mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-23 21:34:35 +03:00
plugins & badusb
if author want their plugin to be removed - create issue, thanks
This commit is contained in:
parent
ee00ba5269
commit
f4cc9e5de7
@ -114,6 +114,8 @@ 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 char ducky_cmd_layout[] = {"DUCKY_LANG"};
|
||||
|
||||
static const uint8_t numpad_keys[10] = {
|
||||
HID_KEYPAD_0,
|
||||
HID_KEYPAD_1,
|
||||
@ -203,10 +205,10 @@ static bool ducky_altstring(const char* param) {
|
||||
return state;
|
||||
}
|
||||
|
||||
static bool ducky_string(const char* param) {
|
||||
static bool ducky_string(const char* param, const uint16_t layout) {
|
||||
uint32_t i = 0;
|
||||
while(param[i] != '\0') {
|
||||
uint16_t keycode = HID_ASCII_TO_KEY(param[i]);
|
||||
uint16_t keycode = HID_ASCII_TO_KEY(layout, param[i]);
|
||||
if(keycode != HID_KEYBOARD_NONE) {
|
||||
furi_hal_hid_kb_press(keycode);
|
||||
furi_hal_hid_kb_release(keycode);
|
||||
@ -216,7 +218,7 @@ static bool ducky_string(const char* param) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
|
||||
static uint16_t ducky_get_keycode(const char* param, bool accept_chars, const uint16_t layout) {
|
||||
for(uint8_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) {
|
||||
uint8_t key_cmd_len = strlen(ducky_keys[i].name);
|
||||
if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) &&
|
||||
@ -225,12 +227,12 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) {
|
||||
}
|
||||
}
|
||||
if((accept_chars) && (strlen(param) > 0)) {
|
||||
return (HID_ASCII_TO_KEY(param[0]) & 0xFF);
|
||||
return (HID_ASCII_TO_KEY(layout, param[0]) & 0xFF);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) {
|
||||
static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line, const uint16_t layout) {
|
||||
uint32_t line_len = string_size(line);
|
||||
const char* line_tmp = string_get_cstr(line);
|
||||
bool state = false;
|
||||
@ -252,6 +254,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) {
|
||||
} else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
||||
// ID - executed in ducky_script_preload
|
||||
return (0);
|
||||
} else if(strncmp(line_tmp, ducky_cmd_layout, strlen(ducky_cmd_layout)) == 0) {
|
||||
// DUCKY_LANG - executed in ducky_script_preload
|
||||
return (0);
|
||||
} else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
|
||||
// DELAY
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
@ -271,7 +276,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) {
|
||||
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||
// STRING
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
state = ducky_string(line_tmp);
|
||||
state = ducky_string(line_tmp, layout);
|
||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
||||
// ALTCHAR
|
||||
@ -294,12 +299,12 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) {
|
||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||
} else {
|
||||
// Special keys + modifiers
|
||||
uint16_t key = ducky_get_keycode(line_tmp, false);
|
||||
uint16_t key = ducky_get_keycode(line_tmp, false, layout);
|
||||
if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR;
|
||||
if((key & 0xFF00) != 0) {
|
||||
// It's a modifier key
|
||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||
key |= ducky_get_keycode(line_tmp, true);
|
||||
key |= ducky_get_keycode(line_tmp, true, layout);
|
||||
}
|
||||
furi_hal_hid_kb_press(key);
|
||||
furi_hal_hid_kb_release(key);
|
||||
@ -308,6 +313,19 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, string_t line) {
|
||||
return SCRIPT_STATE_ERROR;
|
||||
}
|
||||
|
||||
static uint16_t ducky_get_layout(const char* line) {
|
||||
uint16_t layout = 0;
|
||||
if(strcmp(line, "US") == 0) {
|
||||
layout = 0;
|
||||
} else if(strcmp(line, "DE") == 0) {
|
||||
layout = 1;
|
||||
} else if(strcmp(line, "FR") == 0) {
|
||||
layout = 2;
|
||||
}
|
||||
FURI_LOG_D(WORKER_TAG, "keyboard layout set: %hu", layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
||||
if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) {
|
||||
bad_usb->hid_cfg.manuf[0] = '\0';
|
||||
@ -333,10 +351,12 @@ static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
|
||||
static uint16_t ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
|
||||
uint8_t ret = 0;
|
||||
uint32_t line_len = 0;
|
||||
|
||||
uint16_t layout = 0;
|
||||
|
||||
string_reset(bad_usb->line);
|
||||
|
||||
do {
|
||||
@ -361,9 +381,17 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
|
||||
} while(ret > 0);
|
||||
|
||||
const char* line_tmp = string_get_cstr(bad_usb->line);
|
||||
bool id_set = false; // Looking for ID command at first line
|
||||
bool id_set = false; // Looking for ID or DUCKY_LANG command at first line
|
||||
if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) {
|
||||
id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]);
|
||||
} else if(strncmp(line_tmp, ducky_cmd_layout, strlen(ducky_cmd_layout)) == 0) {
|
||||
layout = ducky_get_layout(&line_tmp[strlen(ducky_cmd_layout) + 1]);
|
||||
}
|
||||
|
||||
// Looking for DUCKY_LANG command at second line
|
||||
const char* line2_tmp = string_get_cstr(bad_usb->line);
|
||||
if(strncmp(line2_tmp, ducky_cmd_layout, strlen(ducky_cmd_layout)) == 0) {
|
||||
layout = ducky_get_layout(&line2_tmp[strlen(ducky_cmd_layout) + 1]);
|
||||
}
|
||||
|
||||
if(id_set) {
|
||||
@ -375,15 +403,16 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) {
|
||||
storage_file_seek(script_file, 0, true);
|
||||
string_reset(bad_usb->line);
|
||||
|
||||
return true;
|
||||
return layout;
|
||||
}
|
||||
|
||||
static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) {
|
||||
static int32_t
|
||||
ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file, const uint16_t layout) {
|
||||
int32_t delay_val = 0;
|
||||
|
||||
if(bad_usb->repeat_cnt > 0) {
|
||||
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, layout);
|
||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||
return 0;
|
||||
} else if(delay_val < 0) { // Script error
|
||||
@ -417,7 +446,7 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
||||
bad_usb->st.line_cur++;
|
||||
bad_usb->buf_len = bad_usb->buf_len + 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, layout);
|
||||
if(delay_val < 0) {
|
||||
bad_usb->st.error_line = bad_usb->st.line_cur;
|
||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %lu", bad_usb->st.line_cur);
|
||||
@ -452,6 +481,8 @@ static int32_t bad_usb_worker(void* context) {
|
||||
BadUsbWorkerState worker_state = BadUsbStateInit;
|
||||
int32_t delay_val = 0;
|
||||
|
||||
uint16_t layout = 0;
|
||||
|
||||
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
|
||||
|
||||
FURI_LOG_I(WORKER_TAG, "Init");
|
||||
@ -468,7 +499,8 @@ static int32_t bad_usb_worker(void* context) {
|
||||
string_get_cstr(bad_usb->file_path),
|
||||
FSAM_READ,
|
||||
FSOM_OPEN_EXISTING)) {
|
||||
if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) {
|
||||
layout = ducky_script_preload(bad_usb, script_file);
|
||||
if(bad_usb->st.line_nb > 0) {
|
||||
if(furi_hal_hid_is_connected()) {
|
||||
worker_state = BadUsbStateIdle; // Ready to run
|
||||
} else {
|
||||
@ -540,7 +572,7 @@ static int32_t bad_usb_worker(void* context) {
|
||||
continue;
|
||||
}
|
||||
bad_usb->st.state = BadUsbStateRunning;
|
||||
delay_val = ducky_script_execute_next(bad_usb, script_file);
|
||||
delay_val = ducky_script_execute_next(bad_usb, script_file, layout);
|
||||
if(delay_val == SCRIPT_STATE_ERROR) { // Script error
|
||||
delay_val = 0;
|
||||
worker_state = BadUsbStateScriptError;
|
||||
|
13
applications/barcode_generator/application.fam
Normal file
13
applications/barcode_generator/application.fam
Normal file
@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="barcode_generator",
|
||||
name="UPC-A Generator",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="barcode_UPCA_generator_app",
|
||||
cdefines=["APP_BARCODE_GEN"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=1 * 1024,
|
||||
order=50,
|
||||
)
|
544
applications/barcode_generator/barcode_generator.c
Normal file
544
applications/barcode_generator/barcode_generator.c
Normal file
@ -0,0 +1,544 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define BARCODE_STARTING_POS 16
|
||||
#define BARCODE_HEIGHT 50
|
||||
#define BARCODE_Y_START 3
|
||||
#define BARCODE_TEXT_OFFSET 9
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
typedef struct {
|
||||
int barcodeNumeral[12]; //The current barcode number
|
||||
int editingIndex; //The index of the editing symbol
|
||||
int menuIndex; //The index of the menu cursor
|
||||
int modeIndex; //Set to 0 for view, 1 for edit, 2 for menu
|
||||
bool doParityCalculation; //Should do parity check?
|
||||
} PluginState;
|
||||
|
||||
void number_0(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #0 on left is OOOIIOI
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "0");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
}
|
||||
void number_1(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #1 on left is OOIIOOI
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "1");
|
||||
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
}
|
||||
void number_2(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #2 on left is OOIOOII
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "2");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 2, BARCODE_HEIGHT); //OO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
}
|
||||
void number_3(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #3 on left is OIIIIOI
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "3");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 4, BARCODE_HEIGHT); //IIII
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
}
|
||||
void number_4(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #4 on left is OIOOOII
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "4");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
}
|
||||
void number_5(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #5 on left is OIIOOOI
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "5");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 6, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
}
|
||||
void number_6(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #6 on left is OIOIIII
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "6");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 2, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 4, BARCODE_HEIGHT); //IIII
|
||||
}
|
||||
void number_7(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #7 on left is OIIIOII
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "7");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 3, BARCODE_HEIGHT); //III
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
}
|
||||
void number_8(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #8 on left is OIIOIII
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "8");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 1, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 3, BARCODE_HEIGHT); //III
|
||||
}
|
||||
void number_9(
|
||||
Canvas* canvas,
|
||||
bool rightHand,
|
||||
int startingPosition) { //UPC Code for #9 on left is OOOIOII
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str(
|
||||
canvas, startingPosition, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, "9");
|
||||
if(rightHand) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
} else {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_box(canvas, startingPosition, BARCODE_Y_START, 3, BARCODE_HEIGHT); //OOO
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 3, BARCODE_Y_START, 1, BARCODE_HEIGHT); //I
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 4, BARCODE_Y_START, 1, BARCODE_HEIGHT); //O
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, startingPosition + 5, BARCODE_Y_START, 2, BARCODE_HEIGHT); //II
|
||||
}
|
||||
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(plugin_state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
//I originally had all of these values being generated at runtime by math, but that kept giving me trouble.
|
||||
int editingMarkerPosition[12] = {
|
||||
19,
|
||||
26,
|
||||
33,
|
||||
40,
|
||||
47,
|
||||
54,
|
||||
66,
|
||||
73,
|
||||
80,
|
||||
87,
|
||||
94,
|
||||
101,
|
||||
};
|
||||
int menuTextLocations[6] = {
|
||||
20,
|
||||
30,
|
||||
40,
|
||||
50,
|
||||
60,
|
||||
70,
|
||||
};
|
||||
|
||||
if(plugin_state->modeIndex == 2) { //if in the menu
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignCenter, "MENU");
|
||||
canvas_draw_frame(canvas, 50, 0, 29, 11); //box around Menu
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, menuTextLocations[0], AlignCenter, AlignCenter, "View");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, menuTextLocations[1], AlignCenter, AlignCenter, "Edit");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, menuTextLocations[2], AlignCenter, AlignCenter, "Parity?");
|
||||
|
||||
canvas_draw_frame(canvas, 81, menuTextLocations[2] - 2, 6, 6);
|
||||
if(plugin_state->doParityCalculation == true) {
|
||||
canvas_draw_box(canvas, 83, menuTextLocations[2], 2, 2);
|
||||
}
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, menuTextLocations[3], AlignCenter, AlignCenter, "TODO");
|
||||
canvas_draw_disc(
|
||||
canvas, 40, menuTextLocations[plugin_state->menuIndex], 2); //draw menu cursor
|
||||
}
|
||||
|
||||
if(plugin_state->modeIndex != 2) { //if not in the menu
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
//canvas_draw_glyph(canvas, 115, BARCODE_Y_START + BARCODE_HEIGHT + BARCODE_TEXT_OFFSET, 'M');
|
||||
canvas_draw_box(canvas, BARCODE_STARTING_POS, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
|
||||
//canvas_draw_box(canvas, BARCODE_STARTING_POS + 1, 1, 1, 50); //left blank on purpose
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
(BARCODE_STARTING_POS + 2),
|
||||
BARCODE_Y_START,
|
||||
1,
|
||||
BARCODE_HEIGHT + 2); //start saftey
|
||||
for(int index = 0; index < 12; index++) {
|
||||
bool isOnRight = false;
|
||||
if(index >= 6) {
|
||||
isOnRight = true;
|
||||
}
|
||||
if((index == 11) && (plugin_state->doParityCalculation)) { //calculate the check digit
|
||||
int checkDigit =
|
||||
plugin_state->barcodeNumeral[0] + plugin_state->barcodeNumeral[2] +
|
||||
plugin_state->barcodeNumeral[4] + plugin_state->barcodeNumeral[6] +
|
||||
plugin_state->barcodeNumeral[8] + plugin_state->barcodeNumeral[10];
|
||||
//add all odd positions Confusing because 0index
|
||||
checkDigit = checkDigit * 3; //times 3
|
||||
checkDigit += plugin_state->barcodeNumeral[1] + plugin_state->barcodeNumeral[3] +
|
||||
plugin_state->barcodeNumeral[5] + plugin_state->barcodeNumeral[7] +
|
||||
plugin_state->barcodeNumeral[9];
|
||||
//add all even positions to above. Confusing because 0index
|
||||
checkDigit = checkDigit % 10; //mod 10
|
||||
//if m - 0 then x12 = 0, otherwise x12 is 10 - m
|
||||
if(checkDigit == 0) {
|
||||
plugin_state->barcodeNumeral[11] = 0;
|
||||
} else {
|
||||
checkDigit = 10 - checkDigit;
|
||||
plugin_state->barcodeNumeral[11] = checkDigit;
|
||||
}
|
||||
}
|
||||
switch(plugin_state->barcodeNumeral[index]) {
|
||||
case 0:
|
||||
number_0(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 1:
|
||||
number_1(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 2:
|
||||
number_2(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 3:
|
||||
number_3(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 4:
|
||||
number_4(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 5:
|
||||
number_5(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 6:
|
||||
number_6(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 7:
|
||||
number_7(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 8:
|
||||
number_8(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
case 9:
|
||||
number_9(canvas, isOnRight, editingMarkerPosition[index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
//canvas_draw_box(canvas, BARCODE_STARTING_POS + 45, BARCODE_Y_START, 1, BARCODE_HEIGHT);
|
||||
canvas_draw_box(canvas, BARCODE_STARTING_POS + 46, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
|
||||
//canvas_draw_box(canvas, BARCODE_STARTING_POS + 47, BARCODE_Y_START, 1, BARCODE_HEIGHT);
|
||||
canvas_draw_box(canvas, BARCODE_STARTING_POS + 48, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
|
||||
//canvas_draw_box(canvas, BARCODE_STARTING_POS + 49, BARCODE_Y_START, 1, BARCODE_HEIGHT);
|
||||
|
||||
if(plugin_state->modeIndex == 1) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
editingMarkerPosition[plugin_state->editingIndex],
|
||||
63,
|
||||
7,
|
||||
1); //draw editing cursor
|
||||
}
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, BARCODE_STARTING_POS + 92, BARCODE_Y_START, 1, BARCODE_HEIGHT + 2);
|
||||
//canvas_draw_box(canvas, 14, 1, 1, 50); //left blank on purpose
|
||||
canvas_draw_box(
|
||||
canvas,
|
||||
(BARCODE_STARTING_POS + 2) + 92,
|
||||
BARCODE_Y_START,
|
||||
1,
|
||||
BARCODE_HEIGHT + 2); //end safety
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, plugin_state);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void barcode_UPCA_generator_state_init(PluginState* const plugin_state) {
|
||||
int i;
|
||||
for(i = 0; i < 12; ++i) {
|
||||
if(i > 9) {
|
||||
plugin_state->barcodeNumeral[i] = i - 10;
|
||||
} else if(i < 10) {
|
||||
plugin_state->barcodeNumeral[i] = i;
|
||||
}
|
||||
}
|
||||
plugin_state->editingIndex = 0;
|
||||
plugin_state->modeIndex = 0;
|
||||
plugin_state->doParityCalculation = true;
|
||||
plugin_state->menuIndex = 0;
|
||||
}
|
||||
|
||||
int32_t barcode_UPCA_generator_app(void* p) {
|
||||
UNUSED(p);
|
||||
//testing
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
|
||||
|
||||
PluginState* plugin_state = malloc(sizeof(PluginState));
|
||||
barcode_UPCA_generator_state_init(plugin_state);
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
||||
FURI_LOG_E("barcode_UPCA_generator", "cannot create mutex\r\n");
|
||||
free(plugin_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// Set system callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
PluginEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
|
||||
int barcodeMaxIndex;
|
||||
if(plugin_state->doParityCalculation == true) {
|
||||
barcodeMaxIndex = 11;
|
||||
}
|
||||
if(plugin_state->doParityCalculation == false) {
|
||||
barcodeMaxIndex = 12;
|
||||
}
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if((event.input.type == InputTypePress) || (event.input.type == InputTypeRepeat)) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
if(plugin_state->modeIndex == 1) { //if edit mode
|
||||
plugin_state->barcodeNumeral[plugin_state->editingIndex]++;
|
||||
}
|
||||
if(plugin_state->barcodeNumeral[plugin_state->editingIndex] > 9) {
|
||||
plugin_state->barcodeNumeral[plugin_state->editingIndex] = 0;
|
||||
}
|
||||
if(plugin_state->modeIndex == 2) { //if menu mode
|
||||
plugin_state->menuIndex--;
|
||||
}
|
||||
if(plugin_state->menuIndex < 0) {
|
||||
plugin_state->menuIndex = 3;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
if(plugin_state->modeIndex == 1) {
|
||||
plugin_state->barcodeNumeral[plugin_state->editingIndex]--;
|
||||
}
|
||||
if(plugin_state->barcodeNumeral[plugin_state->editingIndex] < 0) {
|
||||
plugin_state->barcodeNumeral[plugin_state->editingIndex] = 9;
|
||||
}
|
||||
if(plugin_state->modeIndex == 2) { //if menu mode
|
||||
plugin_state->menuIndex++;
|
||||
}
|
||||
if(plugin_state->menuIndex > 3) {
|
||||
plugin_state->menuIndex = 0;
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if(plugin_state->modeIndex == 1) {
|
||||
plugin_state->editingIndex++;
|
||||
}
|
||||
if(plugin_state->editingIndex >= barcodeMaxIndex) {
|
||||
plugin_state->editingIndex = 0;
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(plugin_state->modeIndex == 1) {
|
||||
plugin_state->editingIndex--;
|
||||
}
|
||||
if(plugin_state->editingIndex < 0) {
|
||||
plugin_state->editingIndex = barcodeMaxIndex - 1;
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if((plugin_state->modeIndex == 0) ||
|
||||
(plugin_state->modeIndex == 1)) { //if normal or edit more, open menu
|
||||
plugin_state->modeIndex = 2;
|
||||
break;
|
||||
} else if(
|
||||
(plugin_state->modeIndex == 2) &&
|
||||
(plugin_state->menuIndex ==
|
||||
1)) { //if hits select in menu, while index is 1. edit mode
|
||||
plugin_state->modeIndex = 1;
|
||||
break;
|
||||
} else if(
|
||||
(plugin_state->modeIndex == 2) &&
|
||||
(plugin_state->menuIndex ==
|
||||
0)) { //if hits select in menu, while index is 0. view mode
|
||||
plugin_state->modeIndex = 0;
|
||||
break;
|
||||
} else if(
|
||||
(plugin_state->modeIndex == 2) &&
|
||||
(plugin_state->menuIndex ==
|
||||
2)) { //if hits select in menu, while index is 2. Parity switch
|
||||
plugin_state->doParityCalculation =
|
||||
!plugin_state->doParityCalculation; //invert bool
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
case InputKeyBack:
|
||||
if(plugin_state->modeIndex == 0) {
|
||||
processing = false;
|
||||
}
|
||||
if(plugin_state->modeIndex == 2) {
|
||||
plugin_state->modeIndex = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D("barcode_UPCA_generator", "osMessageQueue: event timeout");
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, plugin_state);
|
||||
}
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
return 0;
|
||||
}
|
9
applications/hid_analyzer/application.fam
Normal file
9
applications/hid_analyzer/application.fam
Normal file
@ -0,0 +1,9 @@
|
||||
App(
|
||||
appid="hid_analyzer",
|
||||
name="HID Analyzer",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="hid_analyzer_app",
|
||||
cdefines=["APP_HID_ANALYZER"],
|
||||
stack_size=2 * 1024,
|
||||
order=40,
|
||||
)
|
98
applications/hid_analyzer/helpers/decoder_hid.cpp
Normal file
98
applications/hid_analyzer/helpers/decoder_hid.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "decoder_hid.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
constexpr uint32_t clocks_in_us = 64;
|
||||
|
||||
constexpr uint32_t jitter_time_us = 20;
|
||||
constexpr uint32_t min_time_us = 64;
|
||||
constexpr uint32_t max_time_us = 80;
|
||||
|
||||
constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us;
|
||||
constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us;
|
||||
constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us;
|
||||
|
||||
bool DecoderHID::read(uint8_t* data, uint8_t data_size) {
|
||||
bool result = false;
|
||||
furi_assert(data_size >= 3);
|
||||
|
||||
if(ready) {
|
||||
result = true;
|
||||
hid.decode(
|
||||
reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3, data, data_size);
|
||||
ready = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DecoderHID::process_front(bool polarity, uint32_t time) {
|
||||
if(ready) return;
|
||||
|
||||
if(polarity == true) {
|
||||
last_pulse_time = time;
|
||||
} else {
|
||||
last_pulse_time += time;
|
||||
|
||||
if(last_pulse_time > min_time && last_pulse_time < max_time) {
|
||||
bool pulse;
|
||||
|
||||
if(last_pulse_time < mid_time) {
|
||||
// 6 pulses
|
||||
pulse = false;
|
||||
} else {
|
||||
// 5 pulses
|
||||
pulse = true;
|
||||
}
|
||||
|
||||
if(last_pulse == pulse) {
|
||||
pulse_count++;
|
||||
|
||||
if(pulse) {
|
||||
if(pulse_count > 4) {
|
||||
pulse_count = 0;
|
||||
store_data(1);
|
||||
}
|
||||
} else {
|
||||
if(pulse_count > 5) {
|
||||
pulse_count = 0;
|
||||
store_data(0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(last_pulse) {
|
||||
if(pulse_count > 2) {
|
||||
store_data(1);
|
||||
}
|
||||
} else {
|
||||
if(pulse_count > 3) {
|
||||
store_data(0);
|
||||
}
|
||||
}
|
||||
|
||||
pulse_count = 0;
|
||||
last_pulse = pulse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DecoderHID::DecoderHID() {
|
||||
reset_state();
|
||||
}
|
||||
|
||||
void DecoderHID::store_data(bool data) {
|
||||
stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1);
|
||||
stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1);
|
||||
stored_data[2] = (stored_data[2] << 1) | data;
|
||||
|
||||
if(hid.can_be_decoded(reinterpret_cast<const uint8_t*>(&stored_data), sizeof(uint32_t) * 3)) {
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
void DecoderHID::reset_state() {
|
||||
last_pulse = false;
|
||||
pulse_count = 0;
|
||||
ready = false;
|
||||
last_pulse_time = 0;
|
||||
}
|
24
applications/hid_analyzer/helpers/decoder_hid.h
Normal file
24
applications/hid_analyzer/helpers/decoder_hid.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <atomic>
|
||||
#include "protocols/protocol_hid.h"
|
||||
|
||||
class DecoderHID {
|
||||
public:
|
||||
bool read(uint8_t* data, uint8_t data_size);
|
||||
void process_front(bool polarity, uint32_t time);
|
||||
DecoderHID();
|
||||
|
||||
private:
|
||||
uint32_t last_pulse_time = 0;
|
||||
bool last_pulse;
|
||||
uint8_t pulse_count;
|
||||
|
||||
uint32_t stored_data[3] = {0, 0, 0};
|
||||
void store_data(bool data);
|
||||
|
||||
std::atomic<bool> ready;
|
||||
|
||||
void reset_state();
|
||||
ProtocolHID hid;
|
||||
};
|
143
applications/hid_analyzer/helpers/hid_reader.cpp
Normal file
143
applications/hid_analyzer/helpers/hid_reader.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
#include "hid_reader.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
|
||||
/**
|
||||
* @brief private violation assistant for HIDReader
|
||||
*/
|
||||
struct HIDReaderAccessor {
|
||||
static void decode(HIDReader& hid_reader, bool polarity) {
|
||||
hid_reader.decode(polarity);
|
||||
}
|
||||
};
|
||||
|
||||
void HIDReader::decode(bool polarity) {
|
||||
uint32_t current_dwt_value = DWT->CYCCNT;
|
||||
uint32_t period = current_dwt_value - last_dwt_value;
|
||||
last_dwt_value = current_dwt_value;
|
||||
|
||||
decoder_hid.process_front(polarity, period);
|
||||
|
||||
detect_ticks++;
|
||||
}
|
||||
|
||||
bool HIDReader::switch_timer_elapsed() {
|
||||
const uint32_t seconds_to_switch = furi_kernel_get_tick_frequency() * 2.0f;
|
||||
return (furi_get_tick() - switch_os_tick_last) > seconds_to_switch;
|
||||
}
|
||||
|
||||
void HIDReader::switch_timer_reset() {
|
||||
switch_os_tick_last = furi_get_tick();
|
||||
}
|
||||
|
||||
void HIDReader::switch_mode() {
|
||||
switch(type) {
|
||||
case Type::Normal:
|
||||
type = Type::Indala;
|
||||
furi_hal_rfid_change_read_config(62500.0f, 0.25f);
|
||||
break;
|
||||
case Type::Indala:
|
||||
type = Type::Normal;
|
||||
furi_hal_rfid_change_read_config(125000.0f, 0.5f);
|
||||
break;
|
||||
}
|
||||
|
||||
switch_timer_reset();
|
||||
}
|
||||
|
||||
static void comparator_trigger_callback(bool level, void* comp_ctx) {
|
||||
HIDReader* _this = static_cast<HIDReader*>(comp_ctx);
|
||||
|
||||
HIDReaderAccessor::decode(*_this, !level);
|
||||
}
|
||||
|
||||
HIDReader::HIDReader() {
|
||||
}
|
||||
|
||||
void HIDReader::start() {
|
||||
type = Type::Normal;
|
||||
|
||||
furi_hal_rfid_pins_read();
|
||||
furi_hal_rfid_tim_read(125000, 0.5);
|
||||
furi_hal_rfid_tim_read_start();
|
||||
start_comparator();
|
||||
|
||||
switch_timer_reset();
|
||||
last_read_count = 0;
|
||||
}
|
||||
|
||||
void HIDReader::start_forced(HIDReader::Type _type) {
|
||||
start();
|
||||
if(_type == Type::Indala) {
|
||||
switch_mode();
|
||||
}
|
||||
}
|
||||
|
||||
void HIDReader::stop() {
|
||||
furi_hal_rfid_pins_reset();
|
||||
furi_hal_rfid_tim_read_stop();
|
||||
furi_hal_rfid_tim_reset();
|
||||
stop_comparator();
|
||||
}
|
||||
|
||||
bool HIDReader::read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable) {
|
||||
bool result = false;
|
||||
bool something_read = false;
|
||||
|
||||
if(decoder_hid.read(data, data_size)) {
|
||||
*_type = LfrfidKeyType::KeyH10301; // should be an OK temp
|
||||
something_read = true;
|
||||
}
|
||||
|
||||
// validation
|
||||
if(something_read) {
|
||||
switch_timer_reset();
|
||||
|
||||
if(last_read_type == *_type && memcmp(last_read_data, data, data_size) == 0) {
|
||||
last_read_count = last_read_count + 1;
|
||||
|
||||
if(last_read_count > 2) {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
last_read_type = *_type;
|
||||
memcpy(last_read_data, data, data_size);
|
||||
last_read_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// mode switching
|
||||
if(switch_enable && switch_timer_elapsed()) {
|
||||
switch_mode();
|
||||
last_read_count = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HIDReader::detect() {
|
||||
bool detected = false;
|
||||
if(detect_ticks > 10) {
|
||||
detected = true;
|
||||
}
|
||||
detect_ticks = 0;
|
||||
|
||||
return detected;
|
||||
}
|
||||
|
||||
bool HIDReader::any_read() {
|
||||
return last_read_count > 0;
|
||||
}
|
||||
|
||||
void HIDReader::start_comparator(void) {
|
||||
furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this);
|
||||
last_dwt_value = DWT->CYCCNT;
|
||||
|
||||
furi_hal_rfid_comp_start();
|
||||
}
|
||||
|
||||
void HIDReader::stop_comparator(void) {
|
||||
furi_hal_rfid_comp_stop();
|
||||
furi_hal_rfid_comp_set_callback(NULL, NULL);
|
||||
}
|
47
applications/hid_analyzer/helpers/hid_reader.h
Normal file
47
applications/hid_analyzer/helpers/hid_reader.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "decoder_hid.h"
|
||||
#include "key_info.h"
|
||||
|
||||
//#define RFID_GPIO_DEBUG 1
|
||||
|
||||
class HIDReader {
|
||||
public:
|
||||
enum class Type : uint8_t {
|
||||
Normal,
|
||||
Indala,
|
||||
};
|
||||
|
||||
HIDReader();
|
||||
void start();
|
||||
void start_forced(HIDReader::Type type);
|
||||
void stop();
|
||||
bool read(LfrfidKeyType* _type, uint8_t* data, uint8_t data_size, bool switch_enable = true);
|
||||
|
||||
bool detect();
|
||||
bool any_read();
|
||||
|
||||
private:
|
||||
friend struct HIDReaderAccessor;
|
||||
|
||||
DecoderHID decoder_hid;
|
||||
|
||||
uint32_t last_dwt_value;
|
||||
|
||||
void start_comparator(void);
|
||||
void stop_comparator(void);
|
||||
|
||||
void decode(bool polarity);
|
||||
|
||||
uint32_t detect_ticks;
|
||||
|
||||
uint32_t switch_os_tick_last;
|
||||
bool switch_timer_elapsed();
|
||||
void switch_timer_reset();
|
||||
void switch_mode();
|
||||
|
||||
LfrfidKeyType last_read_type;
|
||||
uint8_t last_read_data[LFRFID_KEY_SIZE];
|
||||
uint8_t last_read_count;
|
||||
|
||||
Type type = Type::Normal;
|
||||
};
|
38
applications/hid_analyzer/helpers/hid_worker.cpp
Normal file
38
applications/hid_analyzer/helpers/hid_worker.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "hid_worker.h"
|
||||
|
||||
HIDWorker::HIDWorker() {
|
||||
}
|
||||
|
||||
HIDWorker::~HIDWorker() {
|
||||
}
|
||||
|
||||
void HIDWorker::start_read() {
|
||||
reader.start();
|
||||
}
|
||||
|
||||
bool HIDWorker::read() {
|
||||
static const uint8_t data_size = LFRFID_KEY_SIZE;
|
||||
uint8_t data[data_size] = {0};
|
||||
LfrfidKeyType type;
|
||||
|
||||
bool result = reader.read(&type, data, data_size);
|
||||
|
||||
if(result) {
|
||||
key.set_type(type);
|
||||
key.set_data(data, data_size);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HIDWorker::detect() {
|
||||
return reader.detect();
|
||||
}
|
||||
|
||||
bool HIDWorker::any_read() {
|
||||
return reader.any_read();
|
||||
}
|
||||
|
||||
void HIDWorker::stop_read() {
|
||||
reader.stop();
|
||||
}
|
21
applications/hid_analyzer/helpers/hid_worker.h
Normal file
21
applications/hid_analyzer/helpers/hid_worker.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "key_info.h"
|
||||
#include "rfid_key.h"
|
||||
#include "hid_reader.h"
|
||||
|
||||
class HIDWorker {
|
||||
public:
|
||||
HIDWorker();
|
||||
~HIDWorker();
|
||||
|
||||
void start_read();
|
||||
bool read();
|
||||
bool detect();
|
||||
bool any_read();
|
||||
void stop_read();
|
||||
|
||||
RfidKey key;
|
||||
|
||||
private:
|
||||
HIDReader reader;
|
||||
};
|
16
applications/hid_analyzer/helpers/key_info.h
Normal file
16
applications/hid_analyzer/helpers/key_info.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t LFRFID_KEY_SIZE = 8;
|
||||
static const uint8_t LFRFID_KEY_NAME_SIZE = 22;
|
||||
|
||||
enum class LfrfidKeyType : uint8_t {
|
||||
KeyEM4100,
|
||||
KeyH10301,
|
||||
KeyI40134,
|
||||
};
|
||||
|
||||
const char* lfrfid_key_get_type_string(LfrfidKeyType type);
|
||||
const char* lfrfid_key_get_manufacturer_string(LfrfidKeyType type);
|
||||
bool lfrfid_key_get_string_type(const char* string, LfrfidKeyType* type);
|
||||
uint8_t lfrfid_key_get_type_data_count(LfrfidKeyType type);
|
30
applications/hid_analyzer/helpers/osc_fsk.h
Normal file
30
applications/hid_analyzer/helpers/osc_fsk.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* This code tries to fit the periods into a given number of cycles (phases) by taking cycles from the next cycle of periods.
|
||||
*/
|
||||
class OscFSK {
|
||||
public:
|
||||
/**
|
||||
* Get next period
|
||||
* @param bit bit value
|
||||
* @param period return period
|
||||
* @return bool whether to advance to the next bit
|
||||
*/
|
||||
bool next(bool bit, uint16_t* period);
|
||||
|
||||
/**
|
||||
* FSK ocillator constructor
|
||||
*
|
||||
* @param freq_low bit 0 freq
|
||||
* @param freq_hi bit 1 freq
|
||||
* @param osc_phase_max max oscillator phase
|
||||
*/
|
||||
OscFSK(uint16_t freq_low, uint16_t freq_hi, uint16_t osc_phase_max);
|
||||
|
||||
private:
|
||||
const uint16_t freq[2];
|
||||
const uint16_t osc_phase_max;
|
||||
int32_t osc_phase_current;
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
class ProtocolGeneric {
|
||||
public:
|
||||
/**
|
||||
* @brief Get the encoded data size
|
||||
*
|
||||
* @return uint8_t size of encoded data in bytes
|
||||
*/
|
||||
virtual uint8_t get_encoded_data_size() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the decoded data size
|
||||
*
|
||||
* @return uint8_t size of decoded data in bytes
|
||||
*/
|
||||
virtual uint8_t get_decoded_data_size() = 0;
|
||||
|
||||
/**
|
||||
* @brief encode decoded data
|
||||
*
|
||||
* @param decoded_data
|
||||
* @param decoded_data_size
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
*/
|
||||
virtual void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief decode encoded data
|
||||
*
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
* @param decoded_data
|
||||
* @param decoded_data_size
|
||||
*/
|
||||
virtual void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) = 0;
|
||||
|
||||
/**
|
||||
* @brief fast check that data can be correctly decoded
|
||||
*
|
||||
* @param encoded_data
|
||||
* @param encoded_data_size
|
||||
* @return true - can be correctly decoded
|
||||
* @return false - cannot be correctly decoded
|
||||
*/
|
||||
virtual bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) = 0;
|
||||
|
||||
virtual ~ProtocolGeneric(){};
|
||||
};
|
155
applications/hid_analyzer/helpers/protocols/protocol_hid.cpp
Normal file
155
applications/hid_analyzer/helpers/protocols/protocol_hid.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
#include "protocol_hid.h"
|
||||
#include <furi.h>
|
||||
|
||||
typedef uint32_t HIDCardData;
|
||||
constexpr uint8_t HIDCount = 3;
|
||||
constexpr uint8_t HIDBitSize = sizeof(HIDCardData) * 8;
|
||||
|
||||
uint8_t ProtocolHID::get_encoded_data_size() {
|
||||
return sizeof(HIDCardData) * HIDCount;
|
||||
}
|
||||
|
||||
uint8_t ProtocolHID::get_decoded_data_size() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
void ProtocolHID::encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) {
|
||||
UNUSED(decoded_data);
|
||||
UNUSED(decoded_data_size);
|
||||
UNUSED(encoded_data);
|
||||
UNUSED(encoded_data_size);
|
||||
// bob!
|
||||
}
|
||||
|
||||
void ProtocolHID::decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) {
|
||||
furi_check(decoded_data_size >= get_decoded_data_size());
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
// header check
|
||||
int16_t second1pos = find_second_1(encoded_data);
|
||||
|
||||
if((*(encoded_data + 1) & 0b1100) != 0x08) {
|
||||
*decoded_data = 37;
|
||||
} else {
|
||||
*decoded_data = (36 - (second1pos - 8));
|
||||
}
|
||||
}
|
||||
|
||||
int16_t ProtocolHID::find_second_1(const uint8_t* encoded_data) {
|
||||
if((*(encoded_data + 1) & 0b11) == 0b10) {
|
||||
return 8;
|
||||
} else {
|
||||
for(int8_t i = 3; i >= 0; i--) {
|
||||
if(((*(encoded_data + 0) >> (2 * i)) & 0b11) == 0b10) {
|
||||
return (12 - i);
|
||||
}
|
||||
}
|
||||
for(int8_t i = 3; i >= 0; i--) {
|
||||
if(((*(encoded_data + 7) >> (2 * i)) & 0b11) == 0b10) {
|
||||
return (16 - i);
|
||||
}
|
||||
}
|
||||
for(int8_t i = 3; i >= 2; i--) {
|
||||
if(((*(encoded_data + 6) >> (2 * i)) & 0b11) == 0b10) {
|
||||
return (20 - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ProtocolHID::can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) {
|
||||
furi_check(encoded_data_size >= get_encoded_data_size());
|
||||
|
||||
const HIDCardData* card_data = reinterpret_cast<const HIDCardData*>(encoded_data);
|
||||
|
||||
// header check
|
||||
int16_t second1pos = -1;
|
||||
// packet pre-preamble
|
||||
if(*(encoded_data + 3) != 0x1D) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// packet preamble
|
||||
if(*(encoded_data + 2) != 0x55) { // first four 0s mandatory in preamble
|
||||
return false;
|
||||
}
|
||||
|
||||
if((*(encoded_data + 1) & 0xF0) != 0x50) { // next two 0s mandatory in preamble
|
||||
return false;
|
||||
}
|
||||
|
||||
if((*(encoded_data + 1) & 0b1100) != 0x08) { // if it's not a 1...
|
||||
// either it's a 37-bit or invalid
|
||||
// so just continue with the manchester encoding checks
|
||||
} else { // it is a 1. so it could be anywhere between 26 and 36 bit encoding. or invalid.
|
||||
// we need to find the location of the second 1
|
||||
second1pos = find_second_1(encoded_data);
|
||||
}
|
||||
|
||||
if(second1pos == -1) {
|
||||
// we're 37 bit or invalid
|
||||
}
|
||||
|
||||
// data decoding. ensure all is properly manchester encoded
|
||||
uint32_t result = 0;
|
||||
|
||||
// decode from word 0
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 11; i >= 0; i--) {
|
||||
switch((*(card_data + 0) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 1
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 1) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// decode from word 2
|
||||
// coded with 01 = 0, 10 = 1 transitions
|
||||
for(int8_t i = 15; i >= 0; i--) {
|
||||
switch((*(card_data + 2) >> (2 * i)) & 0b11) {
|
||||
case 0b01:
|
||||
result = (result << 1) | 0;
|
||||
break;
|
||||
case 0b10:
|
||||
result = (result << 1) | 1;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
25
applications/hid_analyzer/helpers/protocols/protocol_hid.h
Normal file
25
applications/hid_analyzer/helpers/protocols/protocol_hid.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "protocol_generic.h"
|
||||
|
||||
class ProtocolHID : public ProtocolGeneric {
|
||||
public:
|
||||
uint8_t get_encoded_data_size() final;
|
||||
uint8_t get_decoded_data_size() final;
|
||||
|
||||
void encode(
|
||||
const uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size,
|
||||
uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size) final;
|
||||
|
||||
void decode(
|
||||
const uint8_t* encoded_data,
|
||||
const uint8_t encoded_data_size,
|
||||
uint8_t* decoded_data,
|
||||
const uint8_t decoded_data_size) final;
|
||||
|
||||
bool can_be_decoded(const uint8_t* encoded_data, const uint8_t encoded_data_size) final;
|
||||
|
||||
private:
|
||||
int16_t find_second_1(const uint8_t* encoded_data);
|
||||
};
|
36
applications/hid_analyzer/helpers/pulse_joiner.h
Normal file
36
applications/hid_analyzer/helpers/pulse_joiner.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
class PulseJoiner {
|
||||
public:
|
||||
/**
|
||||
* @brief Push timer pulse. First negative pulse is ommited.
|
||||
*
|
||||
* @param polarity pulse polarity: true = high2low, false = low2high
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*
|
||||
* @return true - next pulse can and must be popped immediatly
|
||||
*/
|
||||
bool push_pulse(bool polarity, uint16_t period, uint16_t pulse);
|
||||
|
||||
/**
|
||||
* @brief Get the next timer pulse. Call only if push_pulse returns true.
|
||||
*
|
||||
* @param period overall period time in timer clicks
|
||||
* @param pulse pulse time in timer clicks
|
||||
*/
|
||||
void pop_pulse(uint16_t* period, uint16_t* pulse);
|
||||
|
||||
PulseJoiner();
|
||||
|
||||
private:
|
||||
struct Pulse {
|
||||
bool polarity;
|
||||
uint16_t time;
|
||||
};
|
||||
|
||||
uint8_t pulse_index = 0;
|
||||
static const uint8_t pulse_max = 6;
|
||||
Pulse pulses[pulse_max];
|
||||
};
|
27
applications/hid_analyzer/helpers/rfid_key.h
Normal file
27
applications/hid_analyzer/helpers/rfid_key.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "key_info.h"
|
||||
#include <array>
|
||||
|
||||
class RfidKey {
|
||||
public:
|
||||
RfidKey();
|
||||
~RfidKey();
|
||||
|
||||
void set_type(LfrfidKeyType type);
|
||||
void set_data(const uint8_t* data, const uint8_t data_size);
|
||||
void set_name(const char* name);
|
||||
|
||||
LfrfidKeyType get_type();
|
||||
const uint8_t* get_data();
|
||||
const char* get_type_text();
|
||||
uint8_t get_type_data_count() const;
|
||||
char* get_name();
|
||||
uint8_t get_name_length();
|
||||
void clear();
|
||||
RfidKey& operator=(const RfidKey& rhs);
|
||||
|
||||
private:
|
||||
std::array<uint8_t, LFRFID_KEY_SIZE> data;
|
||||
LfrfidKeyType type;
|
||||
char name[LFRFID_KEY_NAME_SIZE + 1];
|
||||
};
|
29
applications/hid_analyzer/helpers/rfid_timer_emulator.h
Normal file
29
applications/hid_analyzer/helpers/rfid_timer_emulator.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include <furi_hal.h>
|
||||
#include "key_info.h"
|
||||
#include "encoder_generic.h"
|
||||
#include "encoder_emmarin.h"
|
||||
#include "encoder_hid_h10301.h"
|
||||
#include "encoder_indala_40134.h"
|
||||
#include "pulse_joiner.h"
|
||||
#include <map>
|
||||
|
||||
class RfidTimerEmulator {
|
||||
public:
|
||||
RfidTimerEmulator();
|
||||
~RfidTimerEmulator();
|
||||
void start(LfrfidKeyType type, const uint8_t* data, uint8_t data_size);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
EncoderGeneric* current_encoder = nullptr;
|
||||
|
||||
std::map<LfrfidKeyType, EncoderGeneric*> encoders = {
|
||||
{LfrfidKeyType::KeyEM4100, new EncoderEM()},
|
||||
{LfrfidKeyType::KeyH10301, new EncoderHID_H10301()},
|
||||
{LfrfidKeyType::KeyI40134, new EncoderIndala_40134()},
|
||||
};
|
||||
|
||||
PulseJoiner pulse_joiner;
|
||||
static void timer_update_callback(void* ctx);
|
||||
};
|
20
applications/hid_analyzer/helpers/rfid_writer.h
Normal file
20
applications/hid_analyzer/helpers/rfid_writer.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
|
||||
class RfidWriter {
|
||||
public:
|
||||
RfidWriter();
|
||||
~RfidWriter();
|
||||
void start();
|
||||
void stop();
|
||||
void write_em(const uint8_t em_data[5]);
|
||||
void write_hid(const uint8_t hid_data[3]);
|
||||
void write_indala(const uint8_t indala_data[3]);
|
||||
|
||||
private:
|
||||
void write_gap(uint32_t gap_time);
|
||||
void write_bit(bool value);
|
||||
void write_byte(uint8_t value);
|
||||
void write_block(uint8_t page, uint8_t block, bool lock_bit, uint32_t data);
|
||||
void write_reset();
|
||||
};
|
25
applications/hid_analyzer/helpers/state_sequencer.h
Normal file
25
applications/hid_analyzer/helpers/state_sequencer.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include "stdint.h"
|
||||
#include <list>
|
||||
#include <functional>
|
||||
|
||||
class TickSequencer {
|
||||
public:
|
||||
TickSequencer();
|
||||
~TickSequencer();
|
||||
|
||||
void tick();
|
||||
void reset();
|
||||
void clear();
|
||||
|
||||
void do_every_tick(uint32_t tick_count, std::function<void(void)> fn);
|
||||
void do_after_tick(uint32_t tick_count, std::function<void(void)> fn);
|
||||
|
||||
private:
|
||||
std::list<std::pair<uint32_t, std::function<void(void)> > > list;
|
||||
std::list<std::pair<uint32_t, std::function<void(void)> > >::iterator list_it;
|
||||
|
||||
uint32_t tick_count;
|
||||
|
||||
void do_nothing();
|
||||
};
|
22
applications/hid_analyzer/hid_analyzer_app.cpp
Normal file
22
applications/hid_analyzer/hid_analyzer_app.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "hid_analyzer_app.h"
|
||||
#include "scene/hid_analyzer_app_scene_read.h"
|
||||
#include "scene/hid_analyzer_app_scene_read_success.h"
|
||||
|
||||
HIDApp::HIDApp()
|
||||
: scene_controller{this}
|
||||
, notification{"notification"}
|
||||
, storage{"storage"}
|
||||
, dialogs{"dialogs"}
|
||||
, text_store(40) {
|
||||
}
|
||||
|
||||
HIDApp::~HIDApp() {
|
||||
}
|
||||
|
||||
void HIDApp::run(void* _args) {
|
||||
UNUSED(_args);
|
||||
|
||||
scene_controller.add_scene(SceneType::Read, new HIDAppSceneRead());
|
||||
scene_controller.add_scene(SceneType::ReadSuccess, new HIDAppSceneReadSuccess());
|
||||
scene_controller.process(100, SceneType::Read);
|
||||
}
|
65
applications/hid_analyzer/hid_analyzer_app.h
Normal file
65
applications/hid_analyzer/hid_analyzer_app.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <generic_scene.hpp>
|
||||
#include <scene_controller.hpp>
|
||||
#include <view_controller.hpp>
|
||||
#include <record_controller.hpp>
|
||||
#include <text_store.h>
|
||||
|
||||
#include <view_modules/submenu_vm.h>
|
||||
#include <view_modules/popup_vm.h>
|
||||
#include <view_modules/dialog_ex_vm.h>
|
||||
#include <view_modules/text_input_vm.h>
|
||||
#include <view_modules/byte_input_vm.h>
|
||||
#include "view/container_vm.h"
|
||||
|
||||
#include <notification/notification_messages.h>
|
||||
#include <storage/storage.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
|
||||
#include "helpers/hid_worker.h"
|
||||
|
||||
class HIDApp {
|
||||
public:
|
||||
enum class EventType : uint8_t {
|
||||
GENERIC_EVENT_ENUM_VALUES,
|
||||
Next,
|
||||
MenuSelected,
|
||||
Stay,
|
||||
Retry,
|
||||
};
|
||||
|
||||
enum class SceneType : uint8_t {
|
||||
GENERIC_SCENE_ENUM_VALUES,
|
||||
Read,
|
||||
ReadSuccess,
|
||||
};
|
||||
|
||||
class Event {
|
||||
public:
|
||||
union {
|
||||
int32_t menu_index;
|
||||
} payload;
|
||||
|
||||
EventType type;
|
||||
};
|
||||
|
||||
HIDApp();
|
||||
~HIDApp();
|
||||
|
||||
void run(void* args);
|
||||
|
||||
// private:
|
||||
SceneController<GenericScene<HIDApp>, HIDApp> scene_controller;
|
||||
ViewController<HIDApp, SubmenuVM, PopupVM, DialogExVM, TextInputVM, ByteInputVM, ContainerVM>
|
||||
view_controller;
|
||||
RecordController<NotificationApp> notification;
|
||||
RecordController<Storage> storage;
|
||||
RecordController<DialogsApp> dialogs;
|
||||
TextStore text_store;
|
||||
|
||||
HIDWorker worker;
|
||||
};
|
10
applications/hid_analyzer/hid_analyzer_app_launcher.cpp
Normal file
10
applications/hid_analyzer/hid_analyzer_app_launcher.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
#include "hid_analyzer_app.h"
|
||||
|
||||
// app enter function
|
||||
extern "C" int32_t hid_analyzer_app(void* args) {
|
||||
HIDApp* app = new HIDApp();
|
||||
app->run(args);
|
||||
delete app;
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
#include "hid_analyzer_app_scene_read.h"
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
void HIDAppSceneRead::on_enter(HIDApp* app, bool /* need_restore */) {
|
||||
auto popup = app->view_controller.get<PopupVM>();
|
||||
|
||||
DOLPHIN_DEED(DolphinDeedRfidRead);
|
||||
popup->set_header("Searching for\nLF HID RFID", 89, 34, AlignCenter, AlignTop);
|
||||
popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61);
|
||||
|
||||
app->view_controller.switch_to<PopupVM>();
|
||||
app->worker.start_read();
|
||||
}
|
||||
|
||||
bool HIDAppSceneRead::on_event(HIDApp* app, HIDApp::Event* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == HIDApp::EventType::Tick) {
|
||||
if(app->worker.read()) {
|
||||
DOLPHIN_DEED(DolphinDeedRfidReadSuccess);
|
||||
notification_message(app->notification, &sequence_success);
|
||||
app->scene_controller.switch_to_next_scene(HIDApp::SceneType::ReadSuccess);
|
||||
} else {
|
||||
if(app->worker.any_read()) {
|
||||
notification_message(app->notification, &sequence_blink_green_10);
|
||||
} else if(app->worker.detect()) {
|
||||
notification_message(app->notification, &sequence_blink_cyan_10);
|
||||
} else {
|
||||
notification_message(app->notification, &sequence_blink_cyan_10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void HIDAppSceneRead::on_exit(HIDApp* app) {
|
||||
app->view_controller.get<PopupVM>()->clean();
|
||||
app->worker.stop_read();
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "../hid_analyzer_app.h"
|
||||
|
||||
class HIDAppSceneRead : public GenericScene<HIDApp> {
|
||||
public:
|
||||
void on_enter(HIDApp* app, bool need_restore) final;
|
||||
bool on_event(HIDApp* app, HIDApp::Event* event) final;
|
||||
void on_exit(HIDApp* app) final;
|
||||
};
|
@ -0,0 +1,78 @@
|
||||
#include "hid_analyzer_app_scene_read_success.h"
|
||||
#include "../view/elements/button_element.h"
|
||||
#include "../view/elements/icon_element.h"
|
||||
#include "../view/elements/string_element.h"
|
||||
|
||||
void HIDAppSceneReadSuccess::on_enter(HIDApp* app, bool /* need_restore */) {
|
||||
string_init(string[0]);
|
||||
string_init(string[1]);
|
||||
string_init(string[2]);
|
||||
|
||||
auto container = app->view_controller.get<ContainerVM>();
|
||||
|
||||
auto button = container->add<ButtonElement>();
|
||||
button->set_type(ButtonElement::Type::Left, "Retry");
|
||||
button->set_callback(app, HIDAppSceneReadSuccess::back_callback);
|
||||
|
||||
auto icon = container->add<IconElement>();
|
||||
icon->set_icon(3, 12, &I_RFIDBigChip_37x36);
|
||||
|
||||
auto header = container->add<StringElement>();
|
||||
header->set_text("HID", 89, 3, 0, AlignCenter);
|
||||
|
||||
// auto line_1_text = container->add<StringElement>();
|
||||
auto line_2_text = container->add<StringElement>();
|
||||
// auto line_3_text = container->add<StringElement>();
|
||||
|
||||
// auto line_1_value = container->add<StringElement>();
|
||||
auto line_2_value = container->add<StringElement>();
|
||||
// auto line_3_value = container->add<StringElement>();
|
||||
|
||||
const uint8_t* data = app->worker.key.get_data();
|
||||
|
||||
// line_1_text->set_text("Hi:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
|
||||
line_2_text->set_text("Bit:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
|
||||
// line_3_text->set_text("Bye:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
|
||||
|
||||
string_printf(string[1], "%u", data[0]);
|
||||
|
||||
// line_1_value->set_text(
|
||||
// string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
|
||||
line_2_value->set_text(
|
||||
string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
|
||||
// line_3_value->set_text(
|
||||
// string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
|
||||
|
||||
app->view_controller.switch_to<ContainerVM>();
|
||||
|
||||
notification_message_block(app->notification, &sequence_set_green_255);
|
||||
}
|
||||
|
||||
bool HIDAppSceneReadSuccess::on_event(HIDApp* app, HIDApp::Event* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == HIDApp::EventType::Retry) {
|
||||
app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read});
|
||||
consumed = true;
|
||||
} else if(event->type == HIDApp::EventType::Back) {
|
||||
app->scene_controller.search_and_switch_to_previous_scene({HIDApp::SceneType::Read});
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void HIDAppSceneReadSuccess::on_exit(HIDApp* app) {
|
||||
notification_message_block(app->notification, &sequence_reset_green);
|
||||
app->view_controller.get<ContainerVM>()->clean();
|
||||
string_clear(string[0]);
|
||||
string_clear(string[1]);
|
||||
string_clear(string[2]);
|
||||
}
|
||||
|
||||
void HIDAppSceneReadSuccess::back_callback(void* context) {
|
||||
HIDApp* app = static_cast<HIDApp*>(context);
|
||||
HIDApp::Event event;
|
||||
event.type = HIDApp::EventType::Retry;
|
||||
app->view_controller.send_event(&event);
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "../hid_analyzer_app.h"
|
||||
|
||||
class HIDAppSceneReadSuccess : public GenericScene<HIDApp> {
|
||||
public:
|
||||
void on_enter(HIDApp* app, bool need_restore) final;
|
||||
bool on_event(HIDApp* app, HIDApp::Event* event) final;
|
||||
void on_exit(HIDApp* app) final;
|
||||
|
||||
private:
|
||||
static void back_callback(void* context);
|
||||
|
||||
string_t string[3];
|
||||
};
|
17
applications/hid_analyzer/view/container_vm.h
Normal file
17
applications/hid_analyzer/view/container_vm.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <view_modules/generic_view_module.h>
|
||||
|
||||
class ContainerVM : public GenericViewModule {
|
||||
public:
|
||||
ContainerVM();
|
||||
~ContainerVM() final;
|
||||
View* get_view() final;
|
||||
void clean() final;
|
||||
|
||||
template <typename T> T* add();
|
||||
|
||||
private:
|
||||
View* view;
|
||||
static void view_draw_callback(Canvas* canvas, void* model);
|
||||
static bool view_input_callback(InputEvent* event, void* context);
|
||||
};
|
28
applications/hid_analyzer/view/elements/button_element.h
Normal file
28
applications/hid_analyzer/view/elements/button_element.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "generic_element.h"
|
||||
|
||||
typedef void (*ButtonElementCallback)(void* context);
|
||||
|
||||
class ButtonElement : public GenericElement {
|
||||
public:
|
||||
ButtonElement();
|
||||
~ButtonElement() final;
|
||||
void draw(Canvas* canvas) final;
|
||||
bool input(InputEvent* event) final;
|
||||
|
||||
enum class Type : uint8_t {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
};
|
||||
|
||||
void set_type(Type type, const char* text);
|
||||
void set_callback(void* context, ButtonElementCallback callback);
|
||||
|
||||
private:
|
||||
Type type = Type::Left;
|
||||
const char* text = nullptr;
|
||||
|
||||
void* context = nullptr;
|
||||
ButtonElementCallback callback = nullptr;
|
||||
};
|
21
applications/hid_analyzer/view/elements/generic_element.h
Normal file
21
applications/hid_analyzer/view/elements/generic_element.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
class GenericElement {
|
||||
public:
|
||||
GenericElement(){};
|
||||
virtual ~GenericElement(){};
|
||||
virtual void draw(Canvas* canvas) = 0;
|
||||
virtual bool input(InputEvent* event) = 0;
|
||||
|
||||
// TODO that must be accessible only to ContainerVMData
|
||||
void set_parent_view(View* view);
|
||||
|
||||
// TODO that must be accessible only to inheritors
|
||||
void lock_model();
|
||||
void unlock_model(bool need_redraw);
|
||||
|
||||
private:
|
||||
View* view = nullptr;
|
||||
};
|
17
applications/hid_analyzer/view/elements/icon_element.h
Normal file
17
applications/hid_analyzer/view/elements/icon_element.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "generic_element.h"
|
||||
|
||||
class IconElement : public GenericElement {
|
||||
public:
|
||||
IconElement();
|
||||
~IconElement() final;
|
||||
void draw(Canvas* canvas) final;
|
||||
bool input(InputEvent* event) final;
|
||||
|
||||
void set_icon(uint8_t x = 0, uint8_t y = 0, const Icon* icon = NULL);
|
||||
|
||||
private:
|
||||
const Icon* icon = NULL;
|
||||
uint8_t x = 0;
|
||||
uint8_t y = 0;
|
||||
};
|
28
applications/hid_analyzer/view/elements/string_element.h
Normal file
28
applications/hid_analyzer/view/elements/string_element.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "generic_element.h"
|
||||
|
||||
class StringElement : public GenericElement {
|
||||
public:
|
||||
StringElement();
|
||||
~StringElement() final;
|
||||
void draw(Canvas* canvas) final;
|
||||
bool input(InputEvent* event) final;
|
||||
|
||||
void set_text(
|
||||
const char* text = NULL,
|
||||
uint8_t x = 0,
|
||||
uint8_t y = 0,
|
||||
uint8_t fit_width = 0,
|
||||
Align horizontal = AlignLeft,
|
||||
Align vertical = AlignTop,
|
||||
Font font = FontPrimary);
|
||||
|
||||
private:
|
||||
const char* text = NULL;
|
||||
uint8_t x = 0;
|
||||
uint8_t y = 0;
|
||||
uint8_t fit_width = 0;
|
||||
Align horizontal = AlignLeft;
|
||||
Align vertical = AlignTop;
|
||||
Font font = FontPrimary;
|
||||
};
|
@ -37,6 +37,12 @@ App(
|
||||
"music_player",
|
||||
"bt_hid",
|
||||
"picopass",
|
||||
"hid_analyzer",
|
||||
"barcode_generator",
|
||||
"mouse_jacker",
|
||||
"nrf_sniff",
|
||||
"sentry_safe",
|
||||
"wifi_marauder",
|
||||
],
|
||||
)
|
||||
|
||||
|
13
applications/mousejacker/application.fam
Normal file
13
applications/mousejacker/application.fam
Normal file
@ -0,0 +1,13 @@
|
||||
App(
|
||||
appid="mouse_jacker",
|
||||
name="[HW] NRF24: Mouse Jacker",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="mousejacker_app",
|
||||
cdefines=["APP_MOUSEJACKER"],
|
||||
requires=[
|
||||
"gui",
|
||||
"dialogs",
|
||||
],
|
||||
stack_size=2 * 1024,
|
||||
order=60,
|
||||
)
|
352
applications/mousejacker/mousejacker.c
Normal file
352
applications/mousejacker/mousejacker.c
Normal file
@ -0,0 +1,352 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <dialogs/dialogs.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_spi.h>
|
||||
#include <furi_hal_interrupt.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <nrf24.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include "mousejacker_ducky.h"
|
||||
|
||||
#define TAG "mousejacker"
|
||||
#define LOGITECH_MAX_CHANNEL 85
|
||||
#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff"
|
||||
#define NRFSNIFF_APP_PATH_EXTENSION ".txt"
|
||||
#define NRFSNIFF_APP_FILENAME "addresses.txt"
|
||||
#define MOUSEJACKER_APP_PATH_FOLDER "/ext/mousejacker"
|
||||
#define MOUSEJACKER_APP_PATH_EXTENSION ".txt"
|
||||
#define MAX_ADDRS 100
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
bool ducky_err;
|
||||
bool addr_err;
|
||||
bool ducky_running;
|
||||
} PluginState;
|
||||
|
||||
uint8_t addrs_count = 0;
|
||||
uint8_t loaded_addrs[MAX_ADDRS][6]; // first byte is rate, the rest are the address
|
||||
|
||||
char target_fmt_text[] = "Target addr: %s";
|
||||
char target_address_str[12] = "None";
|
||||
char target_text[30];
|
||||
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(plugin_state == NULL) {
|
||||
return;
|
||||
}
|
||||
// border around the edge of the screen
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
if(!plugin_state->addr_err && !plugin_state->ducky_err && !plugin_state->ducky_running) {
|
||||
sprintf(target_text, target_fmt_text, target_address_str);
|
||||
canvas_draw_str_aligned(canvas, 7, 10, AlignLeft, AlignBottom, target_text);
|
||||
canvas_draw_str_aligned(canvas, 22, 20, AlignLeft, AlignBottom, "<- select address ->");
|
||||
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, "Press Ok button to ");
|
||||
canvas_draw_str_aligned(canvas, 10, 40, AlignLeft, AlignBottom, "browse for ducky script");
|
||||
} else if(plugin_state->addr_err) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 10, 10, AlignLeft, AlignBottom, "Error: No nrfsniff folder");
|
||||
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, "or addresses.txt file");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 10, 30, AlignLeft, AlignBottom, "loading error / empty file");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 7, 40, AlignLeft, AlignBottom, "Run (NRF24: Sniff) app first!");
|
||||
} else if(plugin_state->ducky_err) {
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 10, 10, AlignLeft, AlignBottom, "Error: No mousejacker folder");
|
||||
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, "or duckyscript file");
|
||||
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, "loading error");
|
||||
} else if(plugin_state->ducky_running) {
|
||||
sprintf(target_text, target_fmt_text, target_address_str);
|
||||
canvas_draw_str_aligned(canvas, 7, 10, AlignLeft, AlignBottom, target_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, "Running duckyscript...");
|
||||
}
|
||||
|
||||
release_mutex((ValueMutex*)ctx, plugin_state);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void mousejacker_state_init(PluginState* const plugin_state) {
|
||||
plugin_state->x = 50;
|
||||
plugin_state->y = 30;
|
||||
}
|
||||
|
||||
static void hexlify(uint8_t* in, uint8_t size, char* out) {
|
||||
memset(out, 0, size * 2);
|
||||
for(int i = 0; i < size; i++) sprintf(out + strlen(out), "%02X", in[i]);
|
||||
}
|
||||
|
||||
static bool open_ducky_script(Stream* stream) {
|
||||
DialogsApp* dialogs = furi_record_open("dialogs");
|
||||
bool result = false;
|
||||
string_t path;
|
||||
string_init(path);
|
||||
string_set_str(path, MOUSEJACKER_APP_PATH_FOLDER);
|
||||
bool ret = dialog_file_browser_show(
|
||||
dialogs, path, path, MOUSEJACKER_APP_PATH_EXTENSION, true, &I_badusb_10px, false);
|
||||
|
||||
furi_record_close("dialogs");
|
||||
if(ret) {
|
||||
if(!file_stream_open(stream, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_I(TAG, "Cannot open file \"%s\"", (path));
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
string_clear(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool open_addrs_file(Stream* stream) {
|
||||
DialogsApp* dialogs = furi_record_open("dialogs");
|
||||
bool result = false;
|
||||
string_t path;
|
||||
string_init(path);
|
||||
string_set_str(path, NRFSNIFF_APP_PATH_FOLDER);
|
||||
bool ret = dialog_file_browser_show(
|
||||
dialogs, path, path, NRFSNIFF_APP_PATH_EXTENSION, true, &I_sub1_10px, false);
|
||||
|
||||
furi_record_close("dialogs");
|
||||
if(ret) {
|
||||
if(!file_stream_open(stream, string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
FURI_LOG_I(TAG, "Cannot open file \"%s\"", (path));
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
string_clear(path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool process_ducky_file(
|
||||
ViewPort* view_port,
|
||||
PluginState* plugin_state,
|
||||
Stream* file_stream,
|
||||
uint8_t* addr,
|
||||
uint8_t addr_size,
|
||||
uint8_t rate) {
|
||||
size_t file_size = 0;
|
||||
size_t bytes_read = 0;
|
||||
uint8_t* file_buf;
|
||||
bool loaded = false;
|
||||
FURI_LOG_I(TAG, "opening ducky script");
|
||||
if(open_ducky_script(file_stream)) {
|
||||
file_size = stream_size(file_stream);
|
||||
if(file_size == (size_t)0) {
|
||||
FURI_LOG_I(TAG, "load failed. file_size: %d", file_size);
|
||||
return loaded;
|
||||
}
|
||||
file_buf = malloc(file_size);
|
||||
memset(file_buf, 0, file_size);
|
||||
bytes_read = stream_read(file_stream, file_buf, file_size);
|
||||
if(bytes_read == file_size) {
|
||||
plugin_state->ducky_running = true;
|
||||
view_port_update(view_port);
|
||||
FURI_LOG_I(TAG, "executing ducky script");
|
||||
mj_process_ducky_script(nrf24_HANDLE, addr, addr_size, rate, (char*)file_buf);
|
||||
FURI_LOG_I(TAG, "finished execution");
|
||||
furi_delay_ms(300);
|
||||
plugin_state->ducky_running = false;
|
||||
loaded = true;
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "load failed. file size: %d", file_size);
|
||||
}
|
||||
free(file_buf);
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static bool load_addrs_file(Stream* file_stream) {
|
||||
size_t file_size = 0;
|
||||
size_t bytes_read = 0;
|
||||
uint8_t* file_buf;
|
||||
char* line_ptr;
|
||||
uint8_t rate;
|
||||
uint8_t addrlen = 0;
|
||||
uint32_t counter = 0;
|
||||
uint8_t addr[5] = {0};
|
||||
uint32_t i_addr_lo = 0;
|
||||
uint32_t i_addr_hi = 0;
|
||||
bool loaded = false;
|
||||
FURI_LOG_I(TAG, "opening addrs file");
|
||||
addrs_count = 0;
|
||||
if(open_addrs_file(file_stream)) {
|
||||
file_size = stream_size(file_stream);
|
||||
if(file_size == (size_t)0) {
|
||||
FURI_LOG_I(TAG, "load failed. file_size: %d", file_size);
|
||||
return loaded;
|
||||
}
|
||||
file_buf = malloc(file_size);
|
||||
memset(file_buf, 0, file_size);
|
||||
bytes_read = stream_read(file_stream, file_buf, file_size);
|
||||
if(bytes_read == file_size) {
|
||||
FURI_LOG_I(TAG, "loading addrs file");
|
||||
char* line = strtok((char*)file_buf, "\n");
|
||||
|
||||
while(line != NULL) {
|
||||
line_ptr = strstr((char*)line, ",");
|
||||
*line_ptr = 0;
|
||||
rate = atoi(line_ptr + 1);
|
||||
addrlen = (uint8_t)(strlen(line) / 2);
|
||||
i_addr_lo = strtoul(line + 2, NULL, 16);
|
||||
line[2] = (char)0;
|
||||
i_addr_hi = strtoul(line, NULL, 16);
|
||||
int32_to_bytes(i_addr_lo, &addr[1], true);
|
||||
addr[0] = (uint8_t)(i_addr_hi & 0xFF);
|
||||
memset(loaded_addrs[counter], rate, 1);
|
||||
memcpy(&loaded_addrs[counter++][1], addr, addrlen);
|
||||
addrs_count++;
|
||||
line = strtok(NULL, "\n");
|
||||
loaded = true;
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_I(TAG, "load failed. file size: %d", file_size);
|
||||
}
|
||||
free(file_buf);
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
int32_t mousejacker_app(void* p) {
|
||||
UNUSED(p);
|
||||
uint8_t addr_idx = 0;
|
||||
bool ducky_ok = false;
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
|
||||
|
||||
PluginState* plugin_state = malloc(sizeof(PluginState));
|
||||
mousejacker_state_init(plugin_state);
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
||||
FURI_LOG_E("mousejacker", "cannot create mutex\r\n");
|
||||
free(plugin_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
// Set system callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
storage_common_mkdir(storage, MOUSEJACKER_APP_PATH_FOLDER);
|
||||
Stream* file_stream = file_stream_alloc(storage);
|
||||
|
||||
// spawn load file dialog to choose sniffed addresses file
|
||||
if(load_addrs_file(file_stream)) {
|
||||
addr_idx = 0;
|
||||
hexlify(&loaded_addrs[addr_idx][1], 5, target_address_str);
|
||||
plugin_state->addr_err = false;
|
||||
} else {
|
||||
plugin_state->addr_err = true;
|
||||
}
|
||||
stream_free(file_stream);
|
||||
nrf24_init();
|
||||
|
||||
PluginEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
break;
|
||||
case InputKeyDown:
|
||||
break;
|
||||
case InputKeyRight:
|
||||
if(!plugin_state->addr_err) {
|
||||
addr_idx++;
|
||||
if(addr_idx > addrs_count) addr_idx = 0;
|
||||
hexlify(loaded_addrs[addr_idx] + 1, 5, target_address_str);
|
||||
}
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
if(!plugin_state->addr_err) {
|
||||
addr_idx--;
|
||||
if(addr_idx == 0) addr_idx = addrs_count - 1;
|
||||
hexlify(loaded_addrs[addr_idx] + 1, 5, target_address_str);
|
||||
}
|
||||
break;
|
||||
case InputKeyOk:
|
||||
if(!plugin_state->addr_err) {
|
||||
file_stream = file_stream_alloc(storage);
|
||||
nrf24_find_channel(
|
||||
nrf24_HANDLE,
|
||||
loaded_addrs[addr_idx] + 1,
|
||||
loaded_addrs[addr_idx] + 1,
|
||||
5,
|
||||
loaded_addrs[addr_idx][0],
|
||||
2,
|
||||
LOGITECH_MAX_CHANNEL,
|
||||
true);
|
||||
ducky_ok = process_ducky_file(
|
||||
view_port,
|
||||
plugin_state,
|
||||
file_stream,
|
||||
loaded_addrs[addr_idx] + 1,
|
||||
5,
|
||||
loaded_addrs[addr_idx][0]);
|
||||
if(!ducky_ok) {
|
||||
plugin_state->ducky_err = true;
|
||||
} else {
|
||||
plugin_state->ducky_err = false;
|
||||
}
|
||||
stream_free(file_stream);
|
||||
}
|
||||
break;
|
||||
case InputKeyBack:
|
||||
processing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D("mousejacker", "furi_message_queue: event timeout");
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, plugin_state);
|
||||
}
|
||||
|
||||
furi_hal_spi_release(nrf24_HANDLE);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
furi_record_close("storage");
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
return 0;
|
||||
}
|
355
applications/mousejacker/mousejacker_ducky.c
Normal file
355
applications/mousejacker/mousejacker_ducky.c
Normal file
@ -0,0 +1,355 @@
|
||||
#include "mousejacker_ducky.h"
|
||||
|
||||
static const char ducky_cmd_comment[] = {"REM"};
|
||||
static const char ducky_cmd_delay[] = {"DELAY "};
|
||||
static const char ducky_cmd_string[] = {"STRING "};
|
||||
static const char ducky_cmd_repeat[] = {"REPEAT "};
|
||||
|
||||
static uint8_t LOGITECH_HID_TEMPLATE[] =
|
||||
{0x00, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static uint8_t LOGITECH_HELLO[] = {0x00, 0x4F, 0x00, 0x04, 0xB0, 0x10, 0x00, 0x00, 0x00, 0xED};
|
||||
static uint8_t LOGITECH_KEEPALIVE[] = {0x00, 0x40, 0x00, 0x55, 0x6B};
|
||||
|
||||
uint8_t prev_hid = 0;
|
||||
|
||||
#define RT_THRESHOLD 50
|
||||
#define LOGITECH_MIN_CHANNEL 2
|
||||
#define LOGITECH_MAX_CHANNEL 83
|
||||
#define LOGITECH_KEEPALIVE_SIZE 5
|
||||
#define LOGITECH_HID_TEMPLATE_SIZE 10
|
||||
#define LOGITECH_HELLO_SIZE 10
|
||||
#define TAG "mousejacker_ducky"
|
||||
|
||||
MJDuckyKey mj_ducky_keys[] = {{" ", 44, 0}, {"!", 30, 2}, {"\"", 52, 2},
|
||||
{"#", 32, 2}, {"$", 33, 2}, {"%", 34, 2},
|
||||
{"&", 36, 2}, {"'", 52, 0}, {"(", 38, 2},
|
||||
{")", 39, 2}, {"*", 37, 2}, {"+", 46, 2},
|
||||
{",", 54, 0}, {"-", 45, 0}, {".", 55, 0},
|
||||
{"/", 56, 0}, {"0", 39, 0}, {"1", 30, 0},
|
||||
{"2", 31, 0}, {"3", 32, 0}, {"4", 33, 0},
|
||||
{"5", 34, 0}, {"6", 35, 0}, {"7", 36, 0},
|
||||
{"8", 37, 0}, {"9", 38, 0}, {":", 51, 2},
|
||||
{";", 51, 0}, {"<", 54, 2}, {"=", 46, 0},
|
||||
{">", 55, 2}, {"?", 56, 2}, {"@", 31, 2},
|
||||
{"A", 4, 2}, {"B", 5, 2}, {"C", 6, 2},
|
||||
{"D", 7, 2}, {"E", 8, 2}, {"F", 9, 2},
|
||||
{"G", 10, 2}, {"H", 11, 2}, {"I", 12, 2},
|
||||
{"J", 13, 2}, {"K", 14, 2}, {"L", 15, 2},
|
||||
{"M", 16, 2}, {"N", 17, 2}, {"O", 18, 2},
|
||||
{"P", 19, 2}, {"Q", 20, 2}, {"R", 21, 2},
|
||||
{"S", 22, 2}, {"T", 23, 2}, {"U", 24, 2},
|
||||
{"V", 25, 2}, {"W", 26, 2}, {"X", 27, 2},
|
||||
{"Y", 28, 2}, {"Z", 29, 2}, {"[", 47, 0},
|
||||
{"\\", 49, 0}, {"]", 48, 0}, {"^", 35, 2},
|
||||
{"_", 45, 2}, {"`", 53, 0}, {"a", 4, 0},
|
||||
{"b", 5, 0}, {"c", 6, 0}, {"d", 7, 0},
|
||||
{"e", 8, 0}, {"f", 9, 0}, {"g", 10, 0},
|
||||
{"h", 11, 0}, {"i", 12, 0}, {"j", 13, 0},
|
||||
{"k", 14, 0}, {"l", 15, 0}, {"m", 16, 0},
|
||||
{"n", 17, 0}, {"o", 18, 0}, {"p", 19, 0},
|
||||
{"q", 20, 0}, {"r", 21, 0}, {"s", 22, 0},
|
||||
{"t", 23, 0}, {"u", 24, 0}, {"v", 25, 0},
|
||||
{"w", 26, 0}, {"x", 27, 0}, {"y", 28, 0},
|
||||
{"z", 29, 0}, {"{", 47, 2}, {"|", 49, 2},
|
||||
{"}", 48, 2}, {"~", 53, 2}, {"BACKSPACE", 42, 0},
|
||||
{"", 0, 0}, {"ALT", 0, 4}, {"SHIFT", 0, 2},
|
||||
{"CTRL", 0, 1}, {"GUI", 0, 8}, {"SCROLLLOCK", 71, 0},
|
||||
{"ENTER", 40, 0}, {"F12", 69, 0}, {"HOME", 74, 0},
|
||||
{"F10", 67, 0}, {"F9", 66, 0}, {"ESCAPE", 41, 0},
|
||||
{"PAGEUP", 75, 0}, {"TAB", 43, 0}, {"PRINTSCREEN", 70, 0},
|
||||
{"F2", 59, 0}, {"CAPSLOCK", 57, 0}, {"F1", 58, 0},
|
||||
{"F4", 61, 0}, {"F6", 63, 0}, {"F8", 65, 0},
|
||||
{"DOWNARROW", 81, 0}, {"DELETE", 42, 0}, {"RIGHT", 79, 0},
|
||||
{"F3", 60, 0}, {"DOWN", 81, 0}, {"DEL", 76, 0},
|
||||
{"END", 77, 0}, {"INSERT", 73, 0}, {"F5", 62, 0},
|
||||
{"LEFTARROW", 80, 0}, {"RIGHTARROW", 79, 0}, {"PAGEDOWN", 78, 0},
|
||||
{"PAUSE", 72, 0}, {"SPACE", 44, 0}, {"UPARROW", 82, 0},
|
||||
{"F11", 68, 0}, {"F7", 64, 0}, {"UP", 82, 0},
|
||||
{"LEFT", 80, 0}};
|
||||
|
||||
/*
|
||||
static bool mj_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 uint32_t mj_ducky_get_command_len(const char* line) {
|
||||
uint32_t len = strlen(line);
|
||||
for(uint32_t i = 0; i < len; i++) {
|
||||
if(line[i] == ' ') return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mj_get_ducky_key(char* key, size_t keylen, MJDuckyKey* dk) {
|
||||
//FURI_LOG_I(TAG, "looking up key %s with length %d", key, keylen);
|
||||
for(uint i = 0; i < sizeof(mj_ducky_keys) / sizeof(MJDuckyKey); i++) {
|
||||
if(!strncmp(mj_ducky_keys[i].name, key, keylen)) {
|
||||
memcpy(dk, &mj_ducky_keys[i], sizeof(MJDuckyKey));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void checksum(uint8_t* payload, uint len) {
|
||||
// This is also from the KeyKeriki paper
|
||||
// Thanks Thorsten and Max!
|
||||
uint8_t cksum = 0xff;
|
||||
for(uint n = 0; n < len - 2; n++) cksum = (cksum - payload[n]) & 0xff;
|
||||
cksum = (cksum + 1) & 0xff;
|
||||
payload[len - 1] = cksum;
|
||||
}
|
||||
|
||||
static void inject_packet(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* addr,
|
||||
uint8_t addr_size,
|
||||
uint8_t rate,
|
||||
uint8_t* payload,
|
||||
size_t payload_size) {
|
||||
uint8_t rt_count = 0;
|
||||
while(1) {
|
||||
if(nrf24_txpacket(handle, payload, payload_size, true)) break;
|
||||
|
||||
rt_count++;
|
||||
// retransmit threshold exceeded, scan for new channel
|
||||
if(rt_count > RT_THRESHOLD) {
|
||||
if(nrf24_find_channel(
|
||||
handle,
|
||||
addr,
|
||||
addr,
|
||||
addr_size,
|
||||
rate,
|
||||
LOGITECH_MIN_CHANNEL,
|
||||
LOGITECH_MAX_CHANNEL,
|
||||
true) > LOGITECH_MAX_CHANNEL)
|
||||
return; // fail
|
||||
|
||||
rt_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void build_hid_packet(uint8_t mod, uint8_t hid, uint8_t* payload) {
|
||||
memcpy(payload, LOGITECH_HID_TEMPLATE, LOGITECH_HID_TEMPLATE_SIZE);
|
||||
payload[2] = mod;
|
||||
payload[3] = hid;
|
||||
checksum(payload, LOGITECH_HID_TEMPLATE_SIZE);
|
||||
}
|
||||
|
||||
static void send_hid_packet(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* addr,
|
||||
uint8_t addr_size,
|
||||
uint8_t rate,
|
||||
uint8_t mod,
|
||||
uint8_t hid) {
|
||||
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
|
||||
build_hid_packet(0, 0, hid_payload);
|
||||
if(hid == prev_hid)
|
||||
inject_packet(
|
||||
handle,
|
||||
addr,
|
||||
addr_size,
|
||||
rate,
|
||||
hid_payload,
|
||||
LOGITECH_HID_TEMPLATE_SIZE); // empty hid packet
|
||||
|
||||
prev_hid = hid;
|
||||
build_hid_packet(mod, hid, hid_payload);
|
||||
inject_packet(handle, addr, addr_size, rate, hid_payload, LOGITECH_HID_TEMPLATE_SIZE);
|
||||
furi_delay_ms(12);
|
||||
}
|
||||
|
||||
// returns false if there was an error processing script line
|
||||
static bool mj_process_ducky_line(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* addr,
|
||||
uint8_t addr_size,
|
||||
uint8_t rate,
|
||||
char* line,
|
||||
char* prev_line) {
|
||||
MJDuckyKey dk;
|
||||
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
|
||||
char* line_tmp = line;
|
||||
uint32_t line_len = strlen(line);
|
||||
for(uint32_t i = 0; i < line_len; i++) {
|
||||
if((line_tmp[i] != ' ') && (line_tmp[i] != '\t') && (line_tmp[i] != '\n')) {
|
||||
line_tmp = &line_tmp[i];
|
||||
break; // Skip spaces and tabs
|
||||
}
|
||||
if(i == line_len - 1) return true; // Skip empty lines
|
||||
}
|
||||
|
||||
FURI_LOG_I(TAG, "line: %s", line_tmp);
|
||||
|
||||
// General commands
|
||||
if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) {
|
||||
// REM - comment line
|
||||
return true;
|
||||
} else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) {
|
||||
// DELAY
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
uint32_t delay_val = 0;
|
||||
delay_val = atoi(line_tmp);
|
||||
if(delay_val > 0) {
|
||||
uint32_t delay_count = delay_val / 10;
|
||||
build_hid_packet(0, 0, hid_payload);
|
||||
inject_packet(
|
||||
handle,
|
||||
addr,
|
||||
addr_size,
|
||||
rate,
|
||||
hid_payload,
|
||||
LOGITECH_HID_TEMPLATE_SIZE); // empty hid packet
|
||||
for(uint32_t i = 0; i < delay_count; i++) {
|
||||
inject_packet(
|
||||
handle, addr, addr_size, rate, LOGITECH_KEEPALIVE, LOGITECH_KEEPALIVE_SIZE);
|
||||
furi_delay_ms(10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||
// STRING
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
for(size_t i = 0; i < strlen(line_tmp); i++) {
|
||||
if(!mj_get_ducky_key(&line_tmp[i], 1, &dk)) return false;
|
||||
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
||||
// REPEAT
|
||||
uint32_t repeat_cnt = 0;
|
||||
if(prev_line == NULL) return false;
|
||||
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
repeat_cnt = atoi(line_tmp);
|
||||
if(repeat_cnt < 2) return false;
|
||||
|
||||
FURI_LOG_I(TAG, "repeating %s %d times", prev_line, repeat_cnt);
|
||||
for(uint32_t i = 0; i < repeat_cnt; i++)
|
||||
mj_process_ducky_line(handle, addr, addr_size, rate, prev_line, NULL);
|
||||
|
||||
return true;
|
||||
} else if(strncmp(line_tmp, "ALT", strlen("ALT")) == 0) {
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "GUI", strlen("GUI")) == 0 ||
|
||||
strncmp(line_tmp, "WINDOWS", strlen("WINDOWS")) == 0 ||
|
||||
strncmp(line_tmp, "COMMAND", strlen("COMMAND")) == 0) {
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 8, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "CTRL-ALT", strlen("CTRL-ALT")) == 0 ||
|
||||
strncmp(line_tmp, "CONTROL-ALT", strlen("CONTROL-ALT")) == 0) {
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 1, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "CTRL-SHIFT", strlen("CTRL-SHIFT")) == 0 ||
|
||||
strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) {
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 2, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 ||
|
||||
strncmp(line_tmp, "CONTROL", strlen("CONTROL")) == 0) {
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1, dk.hid);
|
||||
return true;
|
||||
} else if(strncmp(line_tmp, "SHIFT", strlen("SHIFT")) == 0) {
|
||||
line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1];
|
||||
if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod | 2, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "ESC", strlen("ESC")) == 0 ||
|
||||
strncmp(line_tmp, "APP", strlen("APP")) == 0 ||
|
||||
strncmp(line_tmp, "ESCAPE", strlen("ESCAPE")) == 0) {
|
||||
if(!mj_get_ducky_key("ESCAPE", 6, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
} else if(strncmp(line_tmp, "ENTER", strlen("ENTER")) == 0) {
|
||||
if(!mj_get_ducky_key("ENTER", 5, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "UP", strlen("UP")) == 0 ||
|
||||
strncmp(line_tmp, "UPARROW", strlen("UPARROW")) == 0) {
|
||||
if(!mj_get_ducky_key("UP", 2, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "DOWN", strlen("DOWN")) == 0 ||
|
||||
strncmp(line_tmp, "DOWNARROW", strlen("DOWNARROW")) == 0) {
|
||||
if(!mj_get_ducky_key("DOWN", 4, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "LEFT", strlen("LEFT")) == 0 ||
|
||||
strncmp(line_tmp, "LEFTARROW", strlen("LEFTARROW")) == 0) {
|
||||
if(!mj_get_ducky_key("LEFT", 4, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
} else if(
|
||||
strncmp(line_tmp, "RIGHT", strlen("RIGHT")) == 0 ||
|
||||
strncmp(line_tmp, "RIGHTARROW", strlen("RIGHTARROW")) == 0) {
|
||||
if(!mj_get_ducky_key("RIGHT", 5, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
} else if(strncmp(line_tmp, "SPACE", strlen("SPACE")) == 0) {
|
||||
if(!mj_get_ducky_key("SPACE", 5, &dk)) return false;
|
||||
send_hid_packet(handle, addr, addr_size, rate, dk.mod, dk.hid);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void mj_process_ducky_script(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* addr,
|
||||
uint8_t addr_size,
|
||||
uint8_t rate,
|
||||
char* script) {
|
||||
uint8_t hid_payload[LOGITECH_HID_TEMPLATE_SIZE] = {0};
|
||||
char* prev_line = NULL;
|
||||
|
||||
inject_packet(handle, addr, addr_size, rate, LOGITECH_HELLO, LOGITECH_HELLO_SIZE);
|
||||
char* line = strtok(script, "\n");
|
||||
while(line != NULL) {
|
||||
if(strcmp(&line[strlen(line) - 1], "\r") == 0) line[strlen(line) - 1] = (char)0;
|
||||
|
||||
if(!mj_process_ducky_line(handle, addr, addr_size, rate, line, prev_line))
|
||||
FURI_LOG_I(TAG, "unable to process ducky script line: %s", line);
|
||||
|
||||
prev_line = line;
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
build_hid_packet(0, 0, hid_payload);
|
||||
inject_packet(
|
||||
handle,
|
||||
addr,
|
||||
addr_size,
|
||||
rate,
|
||||
hid_payload,
|
||||
LOGITECH_HID_TEMPLATE_SIZE); // empty hid packet at end
|
||||
}
|
31
applications/mousejacker/mousejacker_ducky.h
Normal file
31
applications/mousejacker/mousejacker_ducky.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <furi_hal_spi.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <nrf24.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* name;
|
||||
uint8_t hid;
|
||||
uint8_t mod;
|
||||
} MJDuckyKey;
|
||||
|
||||
void mj_process_ducky_script(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* addr,
|
||||
uint8_t addr_size,
|
||||
uint8_t rate,
|
||||
char* script);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
10
applications/nrfsniff/application.fam
Normal file
10
applications/nrfsniff/application.fam
Normal file
@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="nrf_sniff",
|
||||
name="[HW] NRF24: Sniffer",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="nrfsniff_app",
|
||||
cdefines=["APP_NRFSNIFF"],
|
||||
requires=["gui"],
|
||||
stack_size=2 * 1024,
|
||||
order=70,
|
||||
)
|
415
applications/nrfsniff/nrfsniff.c
Normal file
415
applications/nrfsniff/nrfsniff.c
Normal file
@ -0,0 +1,415 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
#include <furi_hal_spi.h>
|
||||
#include <furi_hal_interrupt.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <nrf24.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
|
||||
#define LOGITECH_MAX_CHANNEL 85
|
||||
#define COUNT_THRESHOLD 4
|
||||
#define SAMPLE_TIME 20000
|
||||
|
||||
#define NRFSNIFF_APP_PATH_FOLDER "/ext/nrfsniff"
|
||||
#define NRFSNIFF_APP_FILENAME "addresses.txt"
|
||||
#define TAG "nrfsniff"
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} PluginEvent;
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
} PluginState;
|
||||
|
||||
char rate_text_fmt[] = "Transfer rate: %dMbps";
|
||||
char channel_text_fmt[] = "Channel: %d";
|
||||
char preamble_text_fmt[] = "Preamble: %02X";
|
||||
char sniff_text_fmt[] = "Sniffing: %s";
|
||||
char addresses_header_text[] = "Address,rate";
|
||||
char sniffed_address_fmt[] = "%s,%d";
|
||||
char rate_text[46];
|
||||
char channel_text[42];
|
||||
char preamble_text[14];
|
||||
char sniff_text[38];
|
||||
char sniffed_address[14];
|
||||
|
||||
uint8_t target_channel = 0;
|
||||
uint8_t target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps)
|
||||
uint8_t target_preamble[] = {0xAA, 0x00};
|
||||
uint8_t sniffing_state = false;
|
||||
char top_address[12];
|
||||
|
||||
uint8_t candidates[100][5] = {0}; // top 100 recurring addresses
|
||||
uint32_t counts[100];
|
||||
uint8_t total_candidates = 0;
|
||||
uint8_t last_cleanup_idx = 101; // avoid replacing the last replaced addr
|
||||
|
||||
static int get_addr_index(uint8_t* addr, uint8_t addr_size) {
|
||||
for(int i = 0; i < total_candidates; i++) {
|
||||
uint8_t* arr_item = candidates[i];
|
||||
if(!memcmp(arr_item, addr, addr_size)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
static uint32_t get_addr_count(uint8_t* addr, uint8_t addr_size)
|
||||
{
|
||||
return counts[get_addr_index(addr, addr_size)];
|
||||
}
|
||||
*/
|
||||
|
||||
static uint8_t get_lowest_idx() {
|
||||
uint32_t lowest = 10000;
|
||||
uint8_t lowest_idx = 0;
|
||||
for(uint8_t i = 0; i < total_candidates; i++) {
|
||||
if(i == last_cleanup_idx) continue;
|
||||
|
||||
if(counts[i] < lowest) {
|
||||
lowest = counts[i];
|
||||
lowest_idx = i;
|
||||
}
|
||||
}
|
||||
last_cleanup_idx = lowest_idx;
|
||||
return lowest_idx;
|
||||
}
|
||||
|
||||
static uint8_t get_highest_idx() {
|
||||
uint32_t highest = 0;
|
||||
uint8_t highest_idx = 0;
|
||||
for(uint8_t i = 0; i < total_candidates; i++) {
|
||||
if(counts[i] > highest) {
|
||||
highest = counts[i];
|
||||
highest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
return highest_idx;
|
||||
}
|
||||
|
||||
static void insert_addr(uint8_t* addr, uint8_t addr_size) {
|
||||
uint8_t idx = total_candidates;
|
||||
if(total_candidates > 99) {
|
||||
// replace addr with lowest count
|
||||
idx = get_lowest_idx();
|
||||
}
|
||||
memcpy(candidates[idx], addr, addr_size);
|
||||
counts[idx] = 1;
|
||||
if(total_candidates < 100) total_candidates++;
|
||||
}
|
||||
|
||||
static void render_callback(Canvas* const canvas, void* ctx) {
|
||||
uint8_t rate = 2;
|
||||
char sniffing[] = "Yes";
|
||||
|
||||
const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(plugin_state == NULL) {
|
||||
return;
|
||||
}
|
||||
// border around the edge of the screen
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
if(target_rate == 0) rate = 1;
|
||||
|
||||
if(!sniffing_state) strcpy(sniffing, "No");
|
||||
|
||||
sprintf(rate_text, rate_text_fmt, (int)rate);
|
||||
sprintf(channel_text, channel_text_fmt, (int)target_channel);
|
||||
sprintf(preamble_text, preamble_text_fmt, target_preamble[0]);
|
||||
sprintf(sniff_text, sniff_text_fmt, sniffing);
|
||||
sprintf(sniffed_address, sniffed_address_fmt, top_address, (int)rate);
|
||||
canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, rate_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 20, AlignLeft, AlignBottom, channel_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 30, AlignLeft, AlignBottom, preamble_text);
|
||||
canvas_draw_str_aligned(canvas, 10, 40, AlignLeft, AlignBottom, sniff_text);
|
||||
canvas_draw_str_aligned(canvas, 30, 50, AlignLeft, AlignBottom, addresses_header_text);
|
||||
canvas_draw_str_aligned(canvas, 30, 60, AlignLeft, AlignBottom, sniffed_address);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, plugin_state);
|
||||
}
|
||||
|
||||
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
PluginEvent event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
static void hexlify(uint8_t* in, uint8_t size, char* out) {
|
||||
memset(out, 0, size * 2);
|
||||
for(int i = 0; i < size; i++) sprintf(out + strlen(out), "%02X", in[i]);
|
||||
}
|
||||
|
||||
static bool save_addr_to_file(Storage* storage, uint8_t* data, uint8_t size) {
|
||||
size_t file_size = 0;
|
||||
uint8_t linesize = 0;
|
||||
char filepath[42] = {0};
|
||||
char addrline[14] = {0};
|
||||
char ending[4];
|
||||
uint8_t* file_contents;
|
||||
uint8_t rate = 1;
|
||||
Stream* stream = file_stream_alloc(storage);
|
||||
|
||||
if(target_rate == 8) rate = 2;
|
||||
sprintf(ending, ",%d\n", rate);
|
||||
hexlify(data, size, addrline);
|
||||
strcat(addrline, ending);
|
||||
linesize = strlen(addrline);
|
||||
strcpy(filepath, NRFSNIFF_APP_PATH_FOLDER);
|
||||
strcat(filepath, "/");
|
||||
strcat(filepath, NRFSNIFF_APP_FILENAME);
|
||||
stream_seek(stream, 0, StreamOffsetFromStart);
|
||||
|
||||
// check if address already exists in file
|
||||
if(file_stream_open(stream, filepath, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||
bool found = false;
|
||||
file_size = stream_size(stream);
|
||||
stream_seek(stream, 0, StreamOffsetFromStart);
|
||||
if(file_size > 0) {
|
||||
file_contents = malloc(file_size + 1);
|
||||
memset(file_contents, 0, file_size + 1);
|
||||
if(stream_read(stream, file_contents, file_size) > 0) {
|
||||
char* line = strtok((char*)file_contents, "\n");
|
||||
|
||||
while(line != NULL) {
|
||||
if(!memcmp(line, addrline, 12)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
free(file_contents);
|
||||
}
|
||||
stream_free(stream);
|
||||
if(found) return false;
|
||||
|
||||
stream = file_stream_alloc(storage);
|
||||
stream_seek(stream, 0, StreamOffsetFromStart);
|
||||
}
|
||||
|
||||
// save address to file
|
||||
if(!file_stream_open(stream, filepath, FSAM_WRITE, FSOM_OPEN_APPEND))
|
||||
FURI_LOG_I(TAG, "Cannot open file \"%s\"", filepath);
|
||||
if(stream_write(stream, (uint8_t*)addrline, linesize) != linesize)
|
||||
FURI_LOG_I(TAG, "failed to write bytes to file stream");
|
||||
|
||||
FURI_LOG_I(TAG, "save successful");
|
||||
stream_free(stream);
|
||||
return true;
|
||||
}
|
||||
|
||||
void alt_address(uint8_t* addr, uint8_t* altaddr) {
|
||||
uint8_t macmess_hi_b[4];
|
||||
uint32_t macmess_hi;
|
||||
uint8_t macmess_lo;
|
||||
uint8_t preserved;
|
||||
uint8_t tmpaddr[5];
|
||||
|
||||
// swap bytes
|
||||
for(int i = 0; i < 5; i++) tmpaddr[i] = addr[4 - i];
|
||||
|
||||
// get address into 32-bit and 8-bit variables
|
||||
memcpy(macmess_hi_b, tmpaddr, 4);
|
||||
macmess_lo = tmpaddr[4];
|
||||
|
||||
macmess_hi = bytes_to_int32(macmess_hi_b, true);
|
||||
|
||||
//preserve lowest bit from hi to shift to low
|
||||
preserved = macmess_hi & 1;
|
||||
macmess_hi >>= 1;
|
||||
macmess_lo >>= 1;
|
||||
macmess_lo = (preserved << 7) | macmess_lo;
|
||||
int32_to_bytes(macmess_hi, macmess_hi_b, true);
|
||||
memcpy(tmpaddr, macmess_hi_b, 4);
|
||||
tmpaddr[4] = macmess_lo;
|
||||
|
||||
// swap bytes back
|
||||
for(int i = 0; i < 5; i++) altaddr[i] = tmpaddr[4 - i];
|
||||
}
|
||||
|
||||
static void wrap_up(Storage* storage) {
|
||||
uint8_t ch;
|
||||
uint8_t addr[5];
|
||||
uint8_t altaddr[5];
|
||||
char trying[12];
|
||||
uint8_t idx;
|
||||
uint8_t rate = 0;
|
||||
if(target_rate == 8) rate = 2;
|
||||
|
||||
nrf24_set_idle(nrf24_HANDLE);
|
||||
|
||||
while(true) {
|
||||
idx = get_highest_idx();
|
||||
if(counts[idx] < COUNT_THRESHOLD) break;
|
||||
|
||||
counts[idx] = 0;
|
||||
memcpy(addr, candidates[idx], 5);
|
||||
hexlify(addr, 5, trying);
|
||||
FURI_LOG_I(TAG, "trying address %s", trying);
|
||||
ch = nrf24_find_channel(nrf24_HANDLE, addr, addr, 5, rate, 2, LOGITECH_MAX_CHANNEL, false);
|
||||
FURI_LOG_I(TAG, "find_channel returned %d", (int)ch);
|
||||
if(ch > LOGITECH_MAX_CHANNEL) {
|
||||
alt_address(addr, altaddr);
|
||||
hexlify(altaddr, 5, trying);
|
||||
FURI_LOG_I(TAG, "trying alternate address %s", trying);
|
||||
ch = nrf24_find_channel(
|
||||
nrf24_HANDLE, altaddr, altaddr, 5, rate, 2, LOGITECH_MAX_CHANNEL, false);
|
||||
FURI_LOG_I(TAG, "find_channel returned %d", (int)ch);
|
||||
memcpy(addr, altaddr, 5);
|
||||
}
|
||||
|
||||
if(ch <= LOGITECH_MAX_CHANNEL) {
|
||||
hexlify(addr, 5, top_address);
|
||||
save_addr_to_file(storage, addr, 5);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void start_sniffing() {
|
||||
memset(candidates, 0, sizeof(candidates));
|
||||
memset(counts, 0, sizeof(counts));
|
||||
nrf24_init_promisc_mode(nrf24_HANDLE, target_channel, target_rate);
|
||||
}
|
||||
|
||||
int32_t nrfsniff_app(void* p) {
|
||||
UNUSED(p);
|
||||
uint8_t address[5] = {0};
|
||||
uint32_t start = 0;
|
||||
hexlify(address, 5, top_address);
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
|
||||
PluginState* plugin_state = malloc(sizeof(PluginState));
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
||||
FURI_LOG_E(TAG, "cannot create mutex\r\n");
|
||||
free(plugin_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
nrf24_init();
|
||||
|
||||
// Set system callbacks
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
storage_common_mkdir(storage, NRFSNIFF_APP_PATH_FOLDER);
|
||||
|
||||
PluginEvent event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress ||
|
||||
(event.input.type == InputTypeLong && event.input.key == InputKeyBack)) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
// toggle rate 1/2Mbps
|
||||
if(!sniffing_state) {
|
||||
if(target_rate == 0)
|
||||
target_rate = 8;
|
||||
else
|
||||
target_rate = 0;
|
||||
}
|
||||
break;
|
||||
case InputKeyDown:
|
||||
// toggle preamble
|
||||
if(!sniffing_state) {
|
||||
if(target_preamble[0] == 0x55)
|
||||
target_preamble[0] = 0xAA;
|
||||
else
|
||||
target_preamble[0] = 0x55;
|
||||
|
||||
nrf24_set_src_mac(nrf24_HANDLE, target_preamble, 2);
|
||||
}
|
||||
break;
|
||||
case InputKeyRight:
|
||||
// increment channel
|
||||
if(!sniffing_state && target_channel <= LOGITECH_MAX_CHANNEL)
|
||||
target_channel++;
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
// decrement channel
|
||||
if(!sniffing_state && target_channel > 0) target_channel--;
|
||||
break;
|
||||
case InputKeyOk:
|
||||
// toggle sniffing
|
||||
sniffing_state = !sniffing_state;
|
||||
if(sniffing_state) {
|
||||
start_sniffing();
|
||||
start = furi_get_tick();
|
||||
} else
|
||||
wrap_up(storage);
|
||||
break;
|
||||
case InputKeyBack:
|
||||
if(event.input.type == InputTypeLong) processing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "osMessageQueue: event timeout");
|
||||
// event timeout
|
||||
}
|
||||
|
||||
if(sniffing_state) {
|
||||
if(nrf24_sniff_address(nrf24_HANDLE, 5, address)) {
|
||||
int idx;
|
||||
uint8_t* top_addr;
|
||||
idx = get_addr_index(address, 5);
|
||||
if(idx == -1)
|
||||
insert_addr(address, 5);
|
||||
else
|
||||
counts[idx]++;
|
||||
|
||||
top_addr = candidates[get_highest_idx()];
|
||||
hexlify(top_addr, 5, top_address);
|
||||
}
|
||||
|
||||
if(furi_get_tick() - start >= SAMPLE_TIME) {
|
||||
wrap_up(storage);
|
||||
target_channel++;
|
||||
if(target_channel > LOGITECH_MAX_CHANNEL) target_channel = 2;
|
||||
|
||||
start_sniffing();
|
||||
start = furi_get_tick();
|
||||
}
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, plugin_state);
|
||||
}
|
||||
|
||||
furi_hal_spi_release(nrf24_HANDLE);
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
furi_record_close("storage");
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
return 0;
|
||||
}
|
10
applications/sentry_safe/application.fam
Normal file
10
applications/sentry_safe/application.fam
Normal file
@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="sentry_safe",
|
||||
name="[HW] Sentry Safe",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="sentry_safe_app",
|
||||
cdefines=["APP_SENTRY_SAFE"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=80,
|
||||
)
|
166
applications/sentry_safe/sentry_safe.c
Normal file
166
applications/sentry_safe/sentry_safe.c
Normal file
@ -0,0 +1,166 @@
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <furi_hal.h>
|
||||
|
||||
typedef struct {
|
||||
uint8_t status;
|
||||
} SentryState;
|
||||
|
||||
typedef enum {
|
||||
EventTypeTick,
|
||||
EventTypeKey,
|
||||
} EventType;
|
||||
|
||||
typedef struct {
|
||||
EventType type;
|
||||
InputEvent input;
|
||||
} Event;
|
||||
|
||||
const char* status_texts[3] = {"[Press OK to open safe]", "Sending...", "Done !"};
|
||||
|
||||
static void sentry_safe_render_callback(Canvas* const canvas, void* ctx) {
|
||||
const SentryState* sentry_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
if(sentry_state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Before the function is called, the state is set with the canvas_reset(canvas)
|
||||
|
||||
// Frame
|
||||
canvas_draw_frame(canvas, 0, 0, 128, 64);
|
||||
|
||||
// Message
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
canvas_draw_frame(canvas, 22, 4, 84, 24);
|
||||
canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignBottom, "BLACK <-> GND");
|
||||
canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignBottom, "GREEN <-> C1 ");
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 64, 50, AlignCenter, AlignBottom, status_texts[sentry_state->status]);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, sentry_state);
|
||||
}
|
||||
|
||||
static void sentry_safe_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
|
||||
furi_assert(event_queue);
|
||||
|
||||
Event event = {.type = EventTypeKey, .input = *input_event};
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
void send_request(int command, int a, int b, int c, int d, int e) {
|
||||
int checksum = (command + a + b + c + d + e);
|
||||
|
||||
furi_hal_gpio_init_simple(&gpio_ext_pc1, GpioModeOutputPushPull);
|
||||
furi_hal_gpio_write(&gpio_ext_pc1, false);
|
||||
furi_delay_ms(3.4);
|
||||
furi_hal_gpio_write(&gpio_ext_pc1, true);
|
||||
|
||||
furi_hal_uart_init(FuriHalUartIdLPUART1, 4800);
|
||||
//furi_hal_uart_set_br(FuriHalUartIdLPUART1, 4800);
|
||||
//furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, usb_uart_on_irq_cb, usb_uart);
|
||||
|
||||
uint8_t data[8] = {0x0, command, a, b, c, d, e, checksum};
|
||||
furi_hal_uart_tx(FuriHalUartIdLPUART1, data, 8);
|
||||
|
||||
furi_delay_ms(100);
|
||||
|
||||
furi_hal_uart_set_irq_cb(FuriHalUartIdLPUART1, NULL, NULL);
|
||||
furi_hal_uart_deinit(FuriHalUartIdLPUART1);
|
||||
}
|
||||
|
||||
void reset_code(int a, int b, int c, int d, int e) {
|
||||
send_request(0x75, a, b, c, d, e);
|
||||
}
|
||||
|
||||
void try_code(int a, int b, int c, int d, int e) {
|
||||
send_request(0x71, a, b, c, d, e);
|
||||
}
|
||||
|
||||
int32_t sentry_safe_app(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event));
|
||||
|
||||
SentryState* sentry_state = malloc(sizeof(SentryState));
|
||||
|
||||
sentry_state->status = 0;
|
||||
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, sentry_state, sizeof(SentryState))) {
|
||||
FURI_LOG_E("SentrySafe", "cannot create mutex\r\n");
|
||||
free(sentry_state);
|
||||
return 255;
|
||||
}
|
||||
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, sentry_safe_render_callback, &state_mutex);
|
||||
view_port_input_callback_set(view_port, sentry_safe_input_callback, event_queue);
|
||||
|
||||
// Open GUI and register view_port
|
||||
Gui* gui = furi_record_open("gui");
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
Event event;
|
||||
for(bool processing = true; processing;) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
|
||||
SentryState* sentry_state = (SentryState*)acquire_mutex_block(&state_mutex);
|
||||
|
||||
if(event_status == FuriStatusOk) {
|
||||
// press events
|
||||
if(event.type == EventTypeKey) {
|
||||
if(event.input.type == InputTypePress) {
|
||||
switch(event.input.key) {
|
||||
case InputKeyUp:
|
||||
break;
|
||||
case InputKeyDown:
|
||||
break;
|
||||
case InputKeyRight:
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
break;
|
||||
|
||||
case InputKeyOk:
|
||||
|
||||
if(sentry_state->status == 2) {
|
||||
sentry_state->status = 0;
|
||||
|
||||
} else if(sentry_state->status == 0) {
|
||||
sentry_state->status = 1;
|
||||
|
||||
reset_code(1, 2, 3, 4, 5);
|
||||
furi_delay_ms(500);
|
||||
try_code(1, 2, 3, 4, 5);
|
||||
|
||||
sentry_state->status = 2;
|
||||
}
|
||||
|
||||
break;
|
||||
case InputKeyBack:
|
||||
processing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// event timeout
|
||||
}
|
||||
|
||||
view_port_update(view_port);
|
||||
release_mutex(&state_mutex, sentry_state);
|
||||
}
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
furi_record_close("gui");
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
delete_mutex(&state_mutex);
|
||||
free(sentry_state);
|
||||
|
||||
return 0;
|
||||
}
|
10
applications/wifi_marauder_companion/application.fam
Normal file
10
applications/wifi_marauder_companion/application.fam
Normal file
@ -0,0 +1,10 @@
|
||||
App(
|
||||
appid="wifi_marauder",
|
||||
name="[HW] WiFi (Marauder)",
|
||||
apptype=FlipperAppType.PLUGIN,
|
||||
entry_point="wifi_marauder_app",
|
||||
cdefines=["APP_WIFI_MARAUDER"],
|
||||
requires=["gui"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
)
|
@ -0,0 +1,30 @@
|
||||
#include "wifi_marauder_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const wifi_marauder_scene_on_enter_handlers[])(void*) = {
|
||||
#include "wifi_marauder_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 wifi_marauder_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "wifi_marauder_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 wifi_marauder_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "wifi_marauder_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers wifi_marauder_scene_handlers = {
|
||||
.on_enter_handlers = wifi_marauder_scene_on_enter_handlers,
|
||||
.on_event_handlers = wifi_marauder_scene_on_event_handlers,
|
||||
.on_exit_handlers = wifi_marauder_scene_on_exit_handlers,
|
||||
.scene_num = WifiMarauderSceneNum,
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) WifiMarauderScene##id,
|
||||
typedef enum {
|
||||
#include "wifi_marauder_scene_config.h"
|
||||
WifiMarauderSceneNum,
|
||||
} WifiMarauderScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers wifi_marauder_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "wifi_marauder_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 "wifi_marauder_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 "wifi_marauder_scene_config.h"
|
||||
#undef ADD_SCENE
|
@ -0,0 +1,3 @@
|
||||
ADD_SCENE(wifi_marauder, start, Start)
|
||||
ADD_SCENE(wifi_marauder, console_output, ConsoleOutput)
|
||||
ADD_SCENE(wifi_marauder, text_input, TextInput)
|
@ -0,0 +1,77 @@
|
||||
#include "../wifi_marauder_app_i.h"
|
||||
|
||||
void wifi_marauder_console_output_handle_rx_data_cb(uint8_t *buf, size_t len, void* context) {
|
||||
furi_assert(context);
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
// If text box store gets too big, then truncate it
|
||||
app->text_box_store_strlen += len;
|
||||
if (app->text_box_store_strlen >= WIFI_MARAUDER_TEXT_BOX_STORE_SIZE - 1) {
|
||||
string_right(app->text_box_store, app->text_box_store_strlen / 2);
|
||||
app->text_box_store_strlen = string_size(app->text_box_store);
|
||||
}
|
||||
|
||||
// Null-terminate buf and append to text box store
|
||||
buf[len] = '\0';
|
||||
string_cat_printf(app->text_box_store, "%s", buf);
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshConsoleOutput);
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_console_output_on_enter(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
TextBox* text_box = app->text_box;
|
||||
text_box_reset(app->text_box);
|
||||
text_box_set_font(text_box, TextBoxFontText);
|
||||
if (app->focus_console_start) {
|
||||
text_box_set_focus(text_box, TextBoxFocusStart);
|
||||
} else {
|
||||
text_box_set_focus(text_box, TextBoxFocusEnd);
|
||||
}
|
||||
if (app->is_command) {
|
||||
string_reset(app->text_box_store);
|
||||
app->text_box_store_strlen = 0;
|
||||
} else { // "View Log" menu action
|
||||
text_box_set_text(app->text_box, string_get_cstr(app->text_box_store));
|
||||
}
|
||||
|
||||
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneConsoleOutput, 0);
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput);
|
||||
|
||||
// Register callback to receive data
|
||||
wifi_marauder_uart_set_handle_rx_data_cb(app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for rx thread
|
||||
|
||||
// Send command with newline '\n'
|
||||
if (app->is_command && app->selected_tx_string) {
|
||||
wifi_marauder_uart_tx((uint8_t*)(app->selected_tx_string), strlen(app->selected_tx_string));
|
||||
wifi_marauder_uart_tx((uint8_t*)("\n"), 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool wifi_marauder_scene_console_output_on_event(void* context, SceneManagerEvent event) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
text_box_set_text(app->text_box, string_get_cstr(app->text_box_store));
|
||||
consumed = true;
|
||||
} else if(event.type == SceneManagerEventTypeTick) {
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_console_output_on_exit(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
// Unregister rx callback
|
||||
wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL);
|
||||
|
||||
// Automatically stop the scan when exiting view
|
||||
if (app->is_command) {
|
||||
wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n"));
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
#include "../wifi_marauder_app_i.h"
|
||||
|
||||
#define NUM_MENU_ITEMS (29)
|
||||
|
||||
// For each command, define whether additional arguments are needed
|
||||
// (enabling text input to fill them out), and whether the console
|
||||
// text box should focus at the start of the output or the end
|
||||
#define INPUT_ARGS (true)
|
||||
#define NO_ARGS (false)
|
||||
#define FOCUS_CONSOLE_START (true)
|
||||
#define FOCUS_CONSOLE_END (false)
|
||||
struct WifiMarauderItem {
|
||||
const char* item_string;
|
||||
bool needs_keyboard;
|
||||
bool focus_console_start;
|
||||
};
|
||||
|
||||
const struct WifiMarauderItem items[NUM_MENU_ITEMS] = {
|
||||
{ "View Log (start)", NO_ARGS, FOCUS_CONSOLE_START },
|
||||
{ "View Log (end)", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "attack -t beacon -l", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "attack -t beacon -r", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "attack -t beacon -a", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "attack -t deauth", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "attack -t probe", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "attack -t rickroll", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "channel", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "channel -s", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "clearlist -a", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "clearlist -s", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "help", NO_ARGS, FOCUS_CONSOLE_START },
|
||||
{ "list -a", NO_ARGS, FOCUS_CONSOLE_START },
|
||||
{ "list -s", NO_ARGS, FOCUS_CONSOLE_START },
|
||||
{ "reboot", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "scanap", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "select -a", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "select -s", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "sniffbeacon", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "sniffdeauth", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "sniffesp", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "sniffpmkid", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "sniffpmkid -c", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "sniffpwn", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "ssid -a -g", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "ssid -a -n", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "ssid -r", INPUT_ARGS, FOCUS_CONSOLE_END },
|
||||
{ "update -w", NO_ARGS, FOCUS_CONSOLE_END },
|
||||
};
|
||||
|
||||
static void wifi_marauder_scene_start_var_list_enter_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
WifiMarauderApp* app = context;
|
||||
app->selected_tx_string = items[index].item_string;
|
||||
app->is_command = (2 <= index);
|
||||
app->is_custom_tx_string = false;
|
||||
app->selected_menu_index = index;
|
||||
app->focus_console_start = items[index].focus_console_start;
|
||||
if (items[index].needs_keyboard) {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartKeyboard);
|
||||
} else {
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartConsole);
|
||||
}
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_start_on_enter(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
VariableItemList* var_item_list = app->var_item_list;
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, wifi_marauder_scene_start_var_list_enter_callback, app);
|
||||
|
||||
// TODO: organize menu
|
||||
for (int i = 0; i < NUM_MENU_ITEMS; ++i) {
|
||||
variable_item_list_add(var_item_list, items[i].item_string, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
variable_item_list_set_selected_item(
|
||||
var_item_list, scene_manager_get_scene_state(app->scene_manager, WifiMarauderSceneStart));
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewVarItemList);
|
||||
}
|
||||
|
||||
bool wifi_marauder_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
WifiMarauderApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if(event.type == SceneManagerEventTypeCustom) {
|
||||
if (event.event == WifiMarauderEventStartKeyboard) {
|
||||
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
|
||||
scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewTextInput);
|
||||
} else if (event.event == WifiMarauderEventStartConsole) {
|
||||
scene_manager_set_scene_state(app->scene_manager, WifiMarauderSceneStart, app->selected_menu_index);
|
||||
scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_start_on_exit(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
variable_item_list_reset(app->var_item_list);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
#include "../wifi_marauder_app_i.h"
|
||||
|
||||
|
||||
void wifi_marauder_scene_text_input_callback(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventStartConsole);
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_text_input_on_enter(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
if (false == app->is_custom_tx_string) {
|
||||
// Fill text input with selected string so that user can add to it
|
||||
size_t length = strlen(app->selected_tx_string);
|
||||
furi_assert(length < WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE);
|
||||
bzero(app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE);
|
||||
strncpy(app->text_input_store, app->selected_tx_string, length);
|
||||
|
||||
// Add space - because flipper keyboard currently doesn't have a space
|
||||
app->text_input_store[length] = ' ';
|
||||
app->text_input_store[length+1] = '\0';
|
||||
app->is_custom_tx_string = true;
|
||||
}
|
||||
|
||||
// Setup view
|
||||
TextInput* text_input = app->text_input;
|
||||
text_input_set_header_text(text_input, "Add command arguments");
|
||||
text_input_set_result_callback(text_input, wifi_marauder_scene_text_input_callback, app, app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE, false);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, WifiMarauderAppViewTextInput);
|
||||
}
|
||||
|
||||
bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent event) {
|
||||
WifiMarauderApp* app = context;
|
||||
bool consumed = false;
|
||||
|
||||
if (event.type == SceneManagerEventTypeCustom) {
|
||||
if (event.event == WifiMarauderEventStartConsole) {
|
||||
// Point to custom string to send
|
||||
app->selected_tx_string = app->text_input_store;
|
||||
scene_manager_next_scene(app->scene_manager, WifiMarauderAppViewConsoleOutput);
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void wifi_marauder_scene_text_input_on_exit(void* context) {
|
||||
WifiMarauderApp* app = context;
|
||||
|
||||
text_input_reset(app->text_input);
|
||||
}
|
96
applications/wifi_marauder_companion/wifi_marauder_app.c
Normal file
96
applications/wifi_marauder_companion/wifi_marauder_app.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include "wifi_marauder_app_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool wifi_marauder_app_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
WifiMarauderApp* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool wifi_marauder_app_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
WifiMarauderApp* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
static void wifi_marauder_app_tick_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
WifiMarauderApp* app = context;
|
||||
scene_manager_handle_tick_event(app->scene_manager);
|
||||
}
|
||||
|
||||
WifiMarauderApp* wifi_marauder_app_alloc() {
|
||||
WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp));
|
||||
|
||||
app->gui = furi_record_open("gui");
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&wifi_marauder_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, wifi_marauder_app_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, wifi_marauder_app_back_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, wifi_marauder_app_tick_event_callback, 100);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->var_item_list = variable_item_list_alloc();
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
WifiMarauderAppViewVarItemList,
|
||||
variable_item_list_get_view(app->var_item_list));
|
||||
|
||||
app->text_box = text_box_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput, text_box_get_view(app->text_box));
|
||||
string_init(app->text_box_store);
|
||||
string_reserve(app->text_box_store, WIFI_MARAUDER_TEXT_BOX_STORE_SIZE);
|
||||
|
||||
app->text_input = text_input_alloc();
|
||||
view_dispatcher_add_view(app->view_dispatcher, WifiMarauderAppViewTextInput, text_input_get_view(app->text_input));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, WifiMarauderSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void wifi_marauder_app_free(WifiMarauderApp* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewVarItemList);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewConsoleOutput);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, WifiMarauderAppViewTextInput);
|
||||
text_box_free(app->text_box);
|
||||
string_clear(app->text_box_store);
|
||||
text_input_free(app->text_input);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
wifi_marauder_uart_free(app->uart);
|
||||
|
||||
// Close records
|
||||
furi_record_close("gui");
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t wifi_marauder_app(void* p) {
|
||||
UNUSED(p);
|
||||
WifiMarauderApp* wifi_marauder_app = wifi_marauder_app_alloc();
|
||||
|
||||
wifi_marauder_app->uart = wifi_marauder_uart_init(wifi_marauder_app);
|
||||
|
||||
view_dispatcher_run(wifi_marauder_app->view_dispatcher);
|
||||
|
||||
wifi_marauder_app_free(wifi_marauder_app);
|
||||
|
||||
return 0;
|
||||
}
|
11
applications/wifi_marauder_companion/wifi_marauder_app.h
Normal file
11
applications/wifi_marauder_companion/wifi_marauder_app.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct WifiMarauderApp WifiMarauderApp;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
65
applications/wifi_marauder_companion/wifi_marauder_app_i.h
Normal file
65
applications/wifi_marauder_companion/wifi_marauder_app_i.h
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "wifi_marauder_app.h"
|
||||
#include "scenes/wifi_marauder_scene.h"
|
||||
#include "wifi_marauder_custom_event.h"
|
||||
#include "wifi_marauder_uart.h"
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/text_box.h>
|
||||
#include <gui/modules/text_input.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
|
||||
#define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096)
|
||||
#define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512)
|
||||
|
||||
struct WifiMarauderApp {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
|
||||
char text_input_store[WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE + 1];
|
||||
string_t text_box_store;
|
||||
size_t text_box_store_strlen;
|
||||
TextBox* text_box;
|
||||
TextInput* text_input;
|
||||
//Widget* widget;
|
||||
|
||||
VariableItemList* var_item_list;
|
||||
|
||||
WifiMarauderUart* uart;
|
||||
int selected_menu_index;
|
||||
const char* selected_tx_string;
|
||||
bool is_command;
|
||||
bool is_custom_tx_string;
|
||||
bool focus_console_start;
|
||||
};
|
||||
|
||||
// Supported commands:
|
||||
// https://github.com/justcallmekoko/ESP32Marauder/wiki/cli
|
||||
// Scan
|
||||
// -> If list is empty, then start a new scanap. (Tap any button to stop.)
|
||||
// -> If there's a list, provide option to rescan and dump list of targets to select.
|
||||
// -> Press BACK to go back to top-level.
|
||||
// Attack
|
||||
// -> Beacon
|
||||
// -> Deauth
|
||||
// -> Probe
|
||||
// -> Rickroll
|
||||
// Sniff
|
||||
// -> Beacon
|
||||
// -> Deauth
|
||||
// -> ESP
|
||||
// -> PMKID
|
||||
// -> Pwnagotchi
|
||||
// Channel
|
||||
// Update
|
||||
// Reboot
|
||||
|
||||
typedef enum {
|
||||
WifiMarauderAppViewVarItemList,
|
||||
WifiMarauderAppViewConsoleOutput,
|
||||
WifiMarauderAppViewTextInput,
|
||||
} WifiMarauderAppView;
|
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
WifiMarauderEventRefreshConsoleOutput = 0,
|
||||
WifiMarauderEventStartConsole,
|
||||
WifiMarauderEventStartKeyboard,
|
||||
} WifiMarauderCustomEvent;
|
97
applications/wifi_marauder_companion/wifi_marauder_uart.c
Normal file
97
applications/wifi_marauder_companion/wifi_marauder_uart.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include "wifi_marauder_app_i.h"
|
||||
#include "wifi_marauder_uart.h"
|
||||
|
||||
#include <stream_buffer.h>
|
||||
|
||||
#define UART_CH (FuriHalUartIdUSART1)
|
||||
#define BAUDRATE (115200)
|
||||
|
||||
struct WifiMarauderUart {
|
||||
WifiMarauderApp* app;
|
||||
FuriThread* rx_thread;
|
||||
StreamBufferHandle_t rx_stream;
|
||||
uint8_t rx_buf[RX_BUF_SIZE+1];
|
||||
void (*handle_rx_data_cb)(uint8_t *buf, size_t len, void* context);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
WorkerEvtStop = (1 << 0),
|
||||
WorkerEvtRxDone = (1 << 1),
|
||||
} WorkerEvtFlags;
|
||||
|
||||
void wifi_marauder_uart_set_handle_rx_data_cb(WifiMarauderUart* uart, void (*handle_rx_data_cb)(uint8_t *buf, size_t len, void* context)) {
|
||||
furi_assert(uart);
|
||||
uart->handle_rx_data_cb = handle_rx_data_cb;
|
||||
}
|
||||
|
||||
#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
|
||||
|
||||
void wifi_marauder_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
|
||||
WifiMarauderUart* uart = (WifiMarauderUart*)context;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
if(ev == UartIrqEventRXNE) {
|
||||
xStreamBufferSendFromISR(uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
|
||||
furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtRxDone);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t uart_worker(void* context) {
|
||||
WifiMarauderUart* uart = (void*)context;
|
||||
|
||||
uart->rx_stream = xStreamBufferCreate(RX_BUF_SIZE, 1);
|
||||
|
||||
while(1) {
|
||||
uint32_t events =
|
||||
furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
|
||||
furi_check((events & FuriFlagError) == 0);
|
||||
if(events & WorkerEvtStop) break;
|
||||
if(events & WorkerEvtRxDone) {
|
||||
size_t len =
|
||||
xStreamBufferReceive(uart->rx_stream, uart->rx_buf, RX_BUF_SIZE, 0);
|
||||
if(len > 0) {
|
||||
if (uart->handle_rx_data_cb) uart->handle_rx_data_cb(uart->rx_buf, len, uart->app);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vStreamBufferDelete(uart->rx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wifi_marauder_uart_tx(uint8_t *data, size_t len) {
|
||||
furi_hal_uart_tx(UART_CH, data, len);
|
||||
}
|
||||
|
||||
WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app) {
|
||||
WifiMarauderUart *uart = malloc(sizeof(WifiMarauderUart));
|
||||
|
||||
furi_hal_console_disable();
|
||||
furi_hal_uart_set_br(UART_CH, BAUDRATE);
|
||||
furi_hal_uart_set_irq_cb(UART_CH, wifi_marauder_uart_on_irq_cb, uart);
|
||||
|
||||
uart->app = app;
|
||||
uart->rx_thread = furi_thread_alloc();
|
||||
furi_thread_set_name(uart->rx_thread, "WifiMarauderUartRxThread");
|
||||
furi_thread_set_stack_size(uart->rx_thread, 1024);
|
||||
furi_thread_set_context(uart->rx_thread, uart);
|
||||
furi_thread_set_callback(uart->rx_thread, uart_worker);
|
||||
|
||||
furi_thread_start(uart->rx_thread);
|
||||
return uart;
|
||||
}
|
||||
|
||||
void wifi_marauder_uart_free(WifiMarauderUart* uart) {
|
||||
furi_assert(uart);
|
||||
|
||||
furi_thread_flags_set(furi_thread_get_id(uart->rx_thread), WorkerEvtStop);
|
||||
furi_thread_join(uart->rx_thread);
|
||||
furi_thread_free(uart->rx_thread);
|
||||
|
||||
furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL);
|
||||
furi_hal_console_enable();
|
||||
|
||||
free(uart);
|
||||
}
|
12
applications/wifi_marauder_companion/wifi_marauder_uart.h
Normal file
12
applications/wifi_marauder_companion/wifi_marauder_uart.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "furi_hal.h"
|
||||
|
||||
#define RX_BUF_SIZE (320)
|
||||
|
||||
typedef struct WifiMarauderUart WifiMarauderUart;
|
||||
|
||||
void wifi_marauder_uart_set_handle_rx_data_cb(WifiMarauderUart* uart, void (*handle_rx_data_cb)(uint8_t *buf, size_t len, void* context));
|
||||
void wifi_marauder_uart_tx(uint8_t *data, size_t len);
|
||||
WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app);
|
||||
void wifi_marauder_uart_free(WifiMarauderUart* uart);
|
@ -1,5 +1,5 @@
|
||||
V:0
|
||||
T:1658859390
|
||||
T:1659104936
|
||||
D:badusb
|
||||
D:dolphin
|
||||
D:infrared
|
||||
@ -232,6 +232,7 @@ F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc
|
||||
F:12674515290ad9edcabc82f7695350f8:50737:nfc/assets/mf_classic_dict.nfc
|
||||
D:subghz/assets
|
||||
F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo
|
||||
F:20b3d7ea34e38425ce3a3d2a548cc730:138:subghz/assets/dangerous_settings
|
||||
F:111d2b8df83e27fd889fc5e270297865:3231:subghz/assets/keeloq_mfcodes
|
||||
F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user
|
||||
F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s
|
||||
|
2
assets/resources/subghz/assets/dangerous_settings
Normal file
2
assets/resources/subghz/assets/dangerous_settings
Normal file
@ -0,0 +1,2 @@
|
||||
# Do you want to damage your flipper CC1101 radio chip by using Frequencies that outside HW specs?
|
||||
yes_i_want_to_destroy_my_flipper: false
|
15
documentation/BarcodeGenerator.md
Normal file
15
documentation/BarcodeGenerator.md
Normal file
@ -0,0 +1,15 @@
|
||||
This is a UPC-A Barcode generator for the flipper zero hardware.
|
||||
|
||||
## Author: [McAzzaMan](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator)
|
||||
|
||||
<img src=https://i.imgur.com/bMSAiuK.png>
|
||||
|
||||
It will eventually be expanded in to other barcode types. It currently only generates UPC-A type barcodes.
|
||||
|
||||
<img src=https://i.imgur.com/bxTdzuA.png>
|
||||
|
||||
<b> -Controls- </b> </br>
|
||||
Hitting the centre button on the flipper toggles edit mode.
|
||||
When in edit mode, left and right will change the digit to be changed, and up and down will adjust the digit value.
|
||||
|
||||
<img src=https://i.imgur.com/lGbzdwH.png>
|
48
documentation/NRF24.md
Normal file
48
documentation/NRF24.md
Normal file
@ -0,0 +1,48 @@
|
||||
# flipperzero-nrf24
|
||||
|
||||
## Author: [mothball187](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker)
|
||||
|
||||
An [NRF24](https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf) driver for the [Flipper Zero](https://flipperzero.one/) device. The NRF24 is a popular line of 2.4GHz radio transceivers from Nordic Semiconductors. This library is not currently complete, but functional.
|
||||
|
||||
# How to use
|
||||
- Connect NRF24 to flipper using provided pinouts
|
||||
- Open NRF24: Sniffer, and scan channels, switch between modes/channels using buttons
|
||||
- When you got address -> Open NRF24: Mouse Jacker
|
||||
- Select Address and open badusb file
|
||||
- Done
|
||||
|
||||
## Warning
|
||||
These apps are for **educational purposes** only. Please use this code responsibly and only use these apps on your own equipment.
|
||||
|
||||
## Acknowledgments
|
||||
The NRF24 sniffing technique was discovered and shared by Travis Goodspeed in [his blog](http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html).
|
||||
|
||||
The mousejack vulnerabilities were discovered and reported by Marc Newlin, see [the blog](https://www.bastille.net/research/vulnerabilities/mousejack/technical-details) for technical details.
|
||||
|
||||
Much of the driver code was inspired by [RadioHead's Arduino library](https://www.airspayce.com/mikem/arduino/RadioHead/classRH__NRF24.html).
|
||||
Much of the mousejack code was inspired by the [Jackit project](https://github.com/insecurityofthings/jackit).
|
||||
|
||||
|
||||
# Pinout from from NoComp/Frog
|
||||
<img src="https://media.discordapp.net/attachments/937479784726949900/994495234618687509/unknown.png?width=567&height=634">
|
||||
|
||||
# Mousejacker / NRF24 pinout by UberGuidoZ
|
||||
2/A7 on FZ goes to MOSI/6 on nrf24l01<br>
|
||||
3/A6 on FZ goes to MISO/7 on nrf24l01<br>
|
||||
4/A4 on FZ goes to CSN/4 on nrf24l01<br>
|
||||
5/B3 on FZ goes to SCK/5 on nrf24l01<br>
|
||||
6/B2 on FZ goes to CE/3 on nrf24l01<br>
|
||||
8/GND on FZ goes to GND/1 on nrf24l01<br>
|
||||
9/3V3 on FZ goes to VCC/2 on nrf24l01<br>
|
||||
IRQ/8 is left disconnected on nrf24l01<br>
|
||||
|
||||
![NRF_Pins](https://user-images.githubusercontent.com/57457139/178093717-39effd5c-ebe2-4253-b13c-70517d7902f9.png)
|
||||
|
||||
If the nRF module is acting a bit flakey, try adding a capacitor to the vcc/gnd lines!
|
||||
I've not tried the Plus model so it may have a bigger need for a cap.
|
||||
Otherwise, I haven't had any major issues.
|
||||
Anything from a 3.3 uF to 10 uF should do. (Watch your positive/negative placement! Negative to ground.)
|
||||
I learned if you wanna get fancy, include a 0.1 uF cap in parallel.
|
||||
The 3.3 uF to 10 uF will respond to slow freq changes while the 0.1 uF will respond to the high freq switching spikes that the larger one cannot. That said, a single 10 uF will likely suffice for the Mousejack attack. ¯\\\_(ツ)_/¯
|
||||
|
||||
![NRF_Capacitor](https://user-images.githubusercontent.com/57457139/178169959-d030f9a6-d2ac-46af-af8b-470ff092c8a7.jpg)
|
17
documentation/SentrySafe.md
Normal file
17
documentation/SentrySafe.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Sentry Safe plugin
|
||||
|
||||
## Author: [H4ckd4ddy](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin)
|
||||
|
||||
Flipper zero exploiting vulnerability to open any Sentry Safe and Master Lock electronic safe without any pin code.
|
||||
|
||||
[Vulnerability described here](https://github.com/H4ckd4ddy/bypass-sentry-safe)
|
||||
|
||||
### Usage
|
||||
|
||||
- Start "Sentry Safe" plugin
|
||||
- Place wires as described on the plugin screen
|
||||
<br>(Flipper GPIO) 8/GND -> Black wire (Safe)
|
||||
<br>(Flipper GPIO) 15/C1 -> Green wire (Safe)
|
||||
|
||||
- Press enter
|
||||
- Open safe
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <cc1101.h>
|
||||
#include <stdio.h>
|
||||
@ -284,10 +286,35 @@ uint8_t furi_hal_subghz_get_lqi() {
|
||||
return data[0] & 0x7F;
|
||||
}
|
||||
|
||||
/*
|
||||
Modified by @tkerby to the full YARD Stick One extended range of 281-361 MHz, 378-481 MHz, and 749-962 MHz.
|
||||
These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage!
|
||||
*/
|
||||
bool furi_hal_subghz_is_frequency_valid(uint32_t value) {
|
||||
bool is_extended = false;
|
||||
|
||||
// TODO: Move file check to another place
|
||||
Storage* storage = furi_record_open("storage");
|
||||
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
|
||||
|
||||
if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) {
|
||||
flipper_format_read_bool(
|
||||
fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended, 1);
|
||||
}
|
||||
|
||||
flipper_format_free(fff_data_file);
|
||||
furi_record_close("storage");
|
||||
|
||||
if(!(value >= 299999755 && value <= 348000335) &&
|
||||
!(value >= 386999938 && value <= 464000000) &&
|
||||
!(value >= 778999847 && value <= 928000000)) {
|
||||
!(value >= 778999847 && value <= 928000000) && !(is_extended)) {
|
||||
FURI_LOG_I(TAG, "Frequency blocked - outside default range");
|
||||
return false;
|
||||
} else if(
|
||||
!(value >= 281000000 && value <= 361000000) &&
|
||||
!(value >= 378000000 && value <= 481000000) &&
|
||||
!(value >= 749000000 && value <= 962000000) && is_extended) {
|
||||
FURI_LOG_I(TAG, "Frequency blocked - outside dangerous range");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -295,12 +322,13 @@ bool furi_hal_subghz_is_frequency_valid(uint32_t value) {
|
||||
}
|
||||
|
||||
uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) {
|
||||
// Set these values to the extended frequency range only. They dont define if you can transmit but do select the correct RF path
|
||||
value = furi_hal_subghz_set_frequency(value);
|
||||
if(value >= 299999755 && value <= 348000335) {
|
||||
if(value >= 281000000 && value <= 361000000) {
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPath315);
|
||||
} else if(value >= 386999938 && value <= 464000000) {
|
||||
} else if(value >= 378000000 && value <= 481000000) {
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPath433);
|
||||
} else if(value >= 778999847 && value <= 928000000) {
|
||||
} else if(value >= 749000000 && value <= 962000000) {
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPath868);
|
||||
} else {
|
||||
furi_crash("SubGhz: Incorrect frequency during set.");
|
||||
@ -309,13 +337,8 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) {
|
||||
}
|
||||
|
||||
bool furi_hal_subghz_is_tx_allowed(uint32_t value) {
|
||||
UNUSED(value);
|
||||
// Removed region check
|
||||
if(!(value >= 299999755 && value <= 348000335) &&
|
||||
!(value >= 386999938 && value <= 464000000) &&
|
||||
!(value >= 778999847 && value <= 928000000)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ enum HidKeyboardMods {
|
||||
KEY_MOD_RIGHT_GUI = (1 << 15),
|
||||
};
|
||||
|
||||
/** ASCII to keycode conversion table */
|
||||
/** ASCII to keycode conversion table US */
|
||||
static const uint16_t hid_asciimap[] = {
|
||||
HID_KEYBOARD_NONE, // NUL
|
||||
HID_KEYBOARD_NONE, // SOH
|
||||
@ -153,6 +153,480 @@ static const uint16_t hid_asciimap[] = {
|
||||
HID_KEYBOARD_NONE, // DEL
|
||||
};
|
||||
|
||||
/** HID keyboard key codes DE */
|
||||
enum HidKeyboardKeysDE {
|
||||
HID_KEYBOARD_DE_ERROR_ROLLOVER = 0x01,
|
||||
HID_KEYBOARD_DE_POST_FAIL = 0x02,
|
||||
HID_KEYBOARD_DE_ERROR_UNDEFINED = 0x03,
|
||||
|
||||
HID_KEYBOARD_DE_ENTER = 0x28,
|
||||
HID_KEYBOARD_DE_ESC = 0x29,
|
||||
HID_KEYBOARD_DE_BACKSPACE = 0x2A,
|
||||
HID_KEYBOARD_DE_TAB = 0x2B,
|
||||
HID_KEYBOARD_DE_SPACE = 0x2C,
|
||||
|
||||
HID_KEYBOARD_DE_A = 0x04,
|
||||
HID_KEYBOARD_DE_B = 0x05,
|
||||
HID_KEYBOARD_DE_C = 0x06,
|
||||
HID_KEYBOARD_DE_D = 0x07,
|
||||
HID_KEYBOARD_DE_E = 0x08,
|
||||
HID_KEYBOARD_DE_F = 0x09,
|
||||
HID_KEYBOARD_DE_G = 0x0A,
|
||||
HID_KEYBOARD_DE_H = 0x0B,
|
||||
HID_KEYBOARD_DE_I = 0x0C,
|
||||
HID_KEYBOARD_DE_J = 0x0D,
|
||||
HID_KEYBOARD_DE_K = 0x0E,
|
||||
HID_KEYBOARD_DE_L = 0x0F,
|
||||
HID_KEYBOARD_DE_M = 0x10,
|
||||
HID_KEYBOARD_DE_N = 0x11,
|
||||
HID_KEYBOARD_DE_O = 0x12,
|
||||
HID_KEYBOARD_DE_P = 0x13,
|
||||
HID_KEYBOARD_DE_Q = 0x14,
|
||||
HID_KEYBOARD_DE_R = 0x15,
|
||||
HID_KEYBOARD_DE_S = 0x16,
|
||||
HID_KEYBOARD_DE_T = 0x17,
|
||||
HID_KEYBOARD_DE_U = 0x18,
|
||||
HID_KEYBOARD_DE_V = 0x19,
|
||||
HID_KEYBOARD_DE_W = 0x1A,
|
||||
HID_KEYBOARD_DE_X = 0x1B,
|
||||
HID_KEYBOARD_DE_Y = 0x1D,
|
||||
HID_KEYBOARD_DE_Z = 0x1C,
|
||||
|
||||
HID_KEYBOARD_DE_1 = 0x1E,
|
||||
HID_KEYBOARD_DE_2 = 0x1F,
|
||||
HID_KEYBOARD_DE_3 = 0x20,
|
||||
HID_KEYBOARD_DE_4 = 0x21,
|
||||
HID_KEYBOARD_DE_5 = 0x22,
|
||||
HID_KEYBOARD_DE_6 = 0x23,
|
||||
HID_KEYBOARD_DE_7 = 0x24,
|
||||
HID_KEYBOARD_DE_8 = 0x25,
|
||||
HID_KEYBOARD_DE_9 = 0x26,
|
||||
HID_KEYBOARD_DE_0 = 0x27,
|
||||
|
||||
HID_KEYBOARD_DE_EXCLAMATION = 0x1E,
|
||||
HID_KEYBOARD_DE_DOUBLE_QUOTE = 0x1F,
|
||||
HID_KEYBOARD_DE_DOLLAR = 0x21,
|
||||
HID_KEYBOARD_DE_PERCENT = 0x22,
|
||||
HID_KEYBOARD_DE_AND = 0x23,
|
||||
HID_KEYBOARD_DE_SINGLE_QUOTE = 0x31, //tocheck
|
||||
HID_KEYBOARD_DE_LEFT_PARENTHESIS = 0x25,
|
||||
HID_KEYBOARD_DE_RIGHT_PARENTHESIS = 0x26,
|
||||
HID_KEYBOARD_DE_STAR = 0x30,
|
||||
HID_KEYBOARD_DE_EQUAL = 0x27,
|
||||
HID_KEYBOARD_DE_COMMA = 0x36,
|
||||
HID_KEYBOARD_DE_DASH = 0x38,
|
||||
HID_KEYBOARD_DE_SEMI_COLON = 0x36,
|
||||
HID_KEYBOARD_DE_DOUBLE_POINTS = 0x37,
|
||||
HID_KEYBOARD_DE_SMALLER = 0x64, //todo
|
||||
HID_KEYBOARD_DE_UNDERSCORE = 0x38,
|
||||
HID_KEYBOARD_DE_CIRCUMFLEX = 0x35, //tocheck
|
||||
HID_KEYBOARD_DE_BACKTICK = 0x2E,
|
||||
|
||||
HID_KEYBOARD_DE_CAPS_LOCK = 0xC1,
|
||||
HID_KEYBOARD_DE_F1 = 0xC2,
|
||||
HID_KEYBOARD_DE_F2 = 0xC3,
|
||||
HID_KEYBOARD_DE_F3 = 0xC4,
|
||||
HID_KEYBOARD_DE_F4 = 0xC5,
|
||||
HID_KEYBOARD_DE_F5 = 0xC6,
|
||||
HID_KEYBOARD_DE_F6 = 0xC7,
|
||||
HID_KEYBOARD_DE_F7 = 0xC8,
|
||||
HID_KEYBOARD_DE_F8 = 0xC9,
|
||||
HID_KEYBOARD_DE_F9 = 0xCA,
|
||||
HID_KEYBOARD_DE_F10 = 0xCB,
|
||||
HID_KEYBOARD_DE_F11 = 0xCC,
|
||||
HID_KEYBOARD_DE_F12 = 0xCD,
|
||||
HID_KEYBOARD_DE_PRINT = 0x63,
|
||||
HID_KEYBOARD_DE_SCROLL_LOCK = 0x47,
|
||||
HID_KEYBOARD_DE_PAUSE = 0x48,
|
||||
HID_KEYBOARD_DE_INSERT = 0xD1,
|
||||
HID_KEYBOARD_DE_HOME = 0xD2,
|
||||
HID_KEYBOARD_DE_PAGE_UP = 0xD3,
|
||||
HID_KEYBOARD_DE_DELETE = 0xD4,
|
||||
HID_KEYBOARD_DE_END = 0xD5,
|
||||
HID_KEYBOARD_DE_PAGE_DOWN = 0xD6,
|
||||
HID_KEYBOARD_DE_RIGHT_ARROW = 0xD7,
|
||||
HID_KEYBOARD_DE_LEFT_ARROW = 0xD8,
|
||||
HID_KEYBOARD_DE_DOWN_ARROW = 0xD9,
|
||||
HID_KEYBOARD_DE_UP_ARROW = 0xDA,
|
||||
HID_KEYBOARD_DE_NUM_LOCK = 0x53,
|
||||
HID_KEYBOARD_DE_NON_US = 0x64,
|
||||
HID_KEYBOARD_DE_APPLICATION = 0x65,
|
||||
|
||||
HID_KEYBOARD_DE_SHARP_SS = 0x2D,
|
||||
};
|
||||
|
||||
/** ASCII to keycode conversion table DE */
|
||||
static const uint16_t hid_asciimap_de[] = {
|
||||
HID_KEYBOARD_NONE, // NUL
|
||||
HID_KEYBOARD_NONE, // SOH
|
||||
HID_KEYBOARD_NONE, // STX
|
||||
HID_KEYBOARD_NONE, // ETX
|
||||
HID_KEYBOARD_NONE, // EOT
|
||||
HID_KEYBOARD_NONE, // ENQ
|
||||
HID_KEYBOARD_NONE, // ACK
|
||||
HID_KEYBOARD_NONE, // BEL
|
||||
HID_KEYBOARD_DE_BACKSPACE, // BS Backspace
|
||||
HID_KEYBOARD_DE_TAB, // TAB Tab
|
||||
HID_KEYBOARD_DE_ENTER, // LF Enter
|
||||
HID_KEYBOARD_NONE, // VT
|
||||
HID_KEYBOARD_NONE, // FF
|
||||
HID_KEYBOARD_NONE, // CR
|
||||
HID_KEYBOARD_NONE, // SO
|
||||
HID_KEYBOARD_NONE, // SI
|
||||
HID_KEYBOARD_NONE, // DEL
|
||||
HID_KEYBOARD_NONE, // DC1
|
||||
HID_KEYBOARD_NONE, // DC2
|
||||
HID_KEYBOARD_NONE, // DC3
|
||||
HID_KEYBOARD_NONE, // DC4
|
||||
HID_KEYBOARD_NONE, // NAK
|
||||
HID_KEYBOARD_NONE, // SYN
|
||||
HID_KEYBOARD_NONE, // ETB
|
||||
HID_KEYBOARD_NONE, // CAN
|
||||
HID_KEYBOARD_NONE, // EM
|
||||
HID_KEYBOARD_NONE, // SUB
|
||||
HID_KEYBOARD_NONE, // ESC
|
||||
HID_KEYBOARD_NONE, // FS
|
||||
HID_KEYBOARD_NONE, // GS
|
||||
HID_KEYBOARD_NONE, // RS
|
||||
HID_KEYBOARD_NONE, // US
|
||||
HID_KEYBOARD_DE_SPACE, // ' ' Space
|
||||
HID_KEYBOARD_DE_EXCLAMATION | KEY_MOD_LEFT_SHIFT, // !
|
||||
HID_KEYBOARD_DE_DOUBLE_QUOTE | KEY_MOD_LEFT_SHIFT, // "
|
||||
HID_KEYBOARD_DE_SINGLE_QUOTE, // #
|
||||
HID_KEYBOARD_DE_DOLLAR | KEY_MOD_LEFT_SHIFT, // $
|
||||
HID_KEYBOARD_DE_PERCENT | KEY_MOD_LEFT_SHIFT, // %
|
||||
HID_KEYBOARD_DE_AND | KEY_MOD_LEFT_SHIFT, // &
|
||||
HID_KEYBOARD_DE_SINGLE_QUOTE | KEY_MOD_LEFT_SHIFT, // '
|
||||
HID_KEYBOARD_DE_LEFT_PARENTHESIS | KEY_MOD_LEFT_SHIFT, // (
|
||||
HID_KEYBOARD_DE_RIGHT_PARENTHESIS | KEY_MOD_LEFT_SHIFT, // )
|
||||
HID_KEYBOARD_DE_STAR | KEY_MOD_LEFT_SHIFT, // *
|
||||
HID_KEYBOARD_DE_STAR, // +
|
||||
HID_KEYBOARD_DE_COMMA, // ,
|
||||
HID_KEYBOARD_DE_DASH, // -
|
||||
HID_KEYBOARD_DE_DOUBLE_POINTS, // .
|
||||
HID_KEYBOARD_DE_7 | KEY_MOD_LEFT_SHIFT, // /
|
||||
HID_KEYBOARD_DE_0, // 0
|
||||
HID_KEYBOARD_DE_1, // 1
|
||||
HID_KEYBOARD_DE_2, // 2
|
||||
HID_KEYBOARD_DE_3, // 3
|
||||
HID_KEYBOARD_DE_4, // 4
|
||||
HID_KEYBOARD_DE_5, // 5
|
||||
HID_KEYBOARD_DE_6, // 6
|
||||
HID_KEYBOARD_DE_7, // 7
|
||||
HID_KEYBOARD_DE_8, // 8
|
||||
HID_KEYBOARD_DE_9, // 9
|
||||
HID_KEYBOARD_DE_DOUBLE_POINTS | KEY_MOD_LEFT_SHIFT, // :
|
||||
HID_KEYBOARD_DE_SEMI_COLON | KEY_MOD_LEFT_SHIFT, // ;
|
||||
HID_KEYBOARD_DE_SMALLER, // <
|
||||
HID_KEYBOARD_DE_EQUAL | KEY_MOD_LEFT_SHIFT, // =
|
||||
HID_KEYBOARD_DE_SMALLER | KEY_MOD_LEFT_SHIFT, // >
|
||||
HID_KEYBOARD_DE_SHARP_SS | KEY_MOD_LEFT_SHIFT, // ?
|
||||
HID_KEYBOARD_DE_Q | KEY_MOD_RIGHT_ALT, // @
|
||||
HID_KEYBOARD_DE_A | KEY_MOD_LEFT_SHIFT, // A
|
||||
HID_KEYBOARD_DE_B | KEY_MOD_LEFT_SHIFT, // B
|
||||
HID_KEYBOARD_DE_C | KEY_MOD_LEFT_SHIFT, // C
|
||||
HID_KEYBOARD_DE_D | KEY_MOD_LEFT_SHIFT, // D
|
||||
HID_KEYBOARD_DE_E | KEY_MOD_LEFT_SHIFT, // E
|
||||
HID_KEYBOARD_DE_F | KEY_MOD_LEFT_SHIFT, // F
|
||||
HID_KEYBOARD_DE_G | KEY_MOD_LEFT_SHIFT, // G
|
||||
HID_KEYBOARD_DE_H | KEY_MOD_LEFT_SHIFT, // H
|
||||
HID_KEYBOARD_DE_I | KEY_MOD_LEFT_SHIFT, // I
|
||||
HID_KEYBOARD_DE_J | KEY_MOD_LEFT_SHIFT, // J
|
||||
HID_KEYBOARD_DE_K | KEY_MOD_LEFT_SHIFT, // K
|
||||
HID_KEYBOARD_DE_L | KEY_MOD_LEFT_SHIFT, // L
|
||||
HID_KEYBOARD_DE_M | KEY_MOD_LEFT_SHIFT, // M
|
||||
HID_KEYBOARD_DE_N | KEY_MOD_LEFT_SHIFT, // N
|
||||
HID_KEYBOARD_DE_O | KEY_MOD_LEFT_SHIFT, // O
|
||||
HID_KEYBOARD_DE_P | KEY_MOD_LEFT_SHIFT, // P
|
||||
HID_KEYBOARD_DE_Q | KEY_MOD_LEFT_SHIFT, // Q
|
||||
HID_KEYBOARD_DE_R | KEY_MOD_LEFT_SHIFT, // R
|
||||
HID_KEYBOARD_DE_S | KEY_MOD_LEFT_SHIFT, // S
|
||||
HID_KEYBOARD_DE_T | KEY_MOD_LEFT_SHIFT, // T
|
||||
HID_KEYBOARD_DE_U | KEY_MOD_LEFT_SHIFT, // U
|
||||
HID_KEYBOARD_DE_V | KEY_MOD_LEFT_SHIFT, // V
|
||||
HID_KEYBOARD_DE_W | KEY_MOD_LEFT_SHIFT, // W
|
||||
HID_KEYBOARD_DE_X | KEY_MOD_LEFT_SHIFT, // X
|
||||
HID_KEYBOARD_DE_Y | KEY_MOD_LEFT_SHIFT, // Y
|
||||
HID_KEYBOARD_DE_Z | KEY_MOD_LEFT_SHIFT, // Z
|
||||
HID_KEYBOARD_DE_LEFT_PARENTHESIS | KEY_MOD_RIGHT_ALT, // [
|
||||
HID_KEYBOARD_DE_SHARP_SS | KEY_MOD_RIGHT_ALT, // bslash
|
||||
HID_KEYBOARD_DE_RIGHT_PARENTHESIS | KEY_MOD_RIGHT_ALT, // ]
|
||||
HID_KEYBOARD_DE_CIRCUMFLEX, // ^
|
||||
HID_KEYBOARD_DE_UNDERSCORE | KEY_MOD_LEFT_SHIFT, // _
|
||||
HID_KEYBOARD_DE_BACKTICK | KEY_MOD_LEFT_SHIFT, // `
|
||||
HID_KEYBOARD_DE_A, // a
|
||||
HID_KEYBOARD_DE_B, // b
|
||||
HID_KEYBOARD_DE_C, // c
|
||||
HID_KEYBOARD_DE_D, // d
|
||||
HID_KEYBOARD_DE_E, // e
|
||||
HID_KEYBOARD_DE_F, // f
|
||||
HID_KEYBOARD_DE_G, // g
|
||||
HID_KEYBOARD_DE_H, // h
|
||||
HID_KEYBOARD_DE_I, // i
|
||||
HID_KEYBOARD_DE_J, // j
|
||||
HID_KEYBOARD_DE_K, // k
|
||||
HID_KEYBOARD_DE_L, // l
|
||||
HID_KEYBOARD_DE_M, // m
|
||||
HID_KEYBOARD_DE_N, // n
|
||||
HID_KEYBOARD_DE_O, // o
|
||||
HID_KEYBOARD_DE_P, // p
|
||||
HID_KEYBOARD_DE_Q, // q
|
||||
HID_KEYBOARD_DE_R, // r
|
||||
HID_KEYBOARD_DE_S, // s
|
||||
HID_KEYBOARD_DE_T, // t
|
||||
HID_KEYBOARD_DE_U, // u
|
||||
HID_KEYBOARD_DE_V, // v
|
||||
HID_KEYBOARD_DE_W, // w
|
||||
HID_KEYBOARD_DE_X, // x
|
||||
HID_KEYBOARD_DE_Y, // y
|
||||
HID_KEYBOARD_DE_Z, // z
|
||||
HID_KEYBOARD_DE_7 | KEY_MOD_RIGHT_ALT, // {
|
||||
HID_KEYBOARD_DE_SMALLER | KEY_MOD_RIGHT_ALT, // |
|
||||
HID_KEYBOARD_DE_0 | KEY_MOD_RIGHT_ALT, // }
|
||||
HID_KEYBOARD_DE_STAR | KEY_MOD_RIGHT_ALT, // ~
|
||||
HID_KEYBOARD_NONE, // DEL
|
||||
};
|
||||
|
||||
/** HID keyboard key codes FR */
|
||||
enum HidKeyboardKeysFR {
|
||||
HID_KEYBOARD_FR_ERROR_ROLLOVER = 0x01,
|
||||
HID_KEYBOARD_FR_POST_FAIL = 0x02,
|
||||
HID_KEYBOARD_FR_ERROR_UNDEFINED = 0x03,
|
||||
|
||||
HID_KEYBOARD_FR_ENTER = 0x28,
|
||||
HID_KEYBOARD_FR_ESC = 0x29,
|
||||
HID_KEYBOARD_FR_BACKSPACE = 0x2A,
|
||||
HID_KEYBOARD_FR_TAB = 0x2B,
|
||||
HID_KEYBOARD_FR_SPACE = 0x2C,
|
||||
|
||||
HID_KEYBOARD_FR_A = 0x14,
|
||||
HID_KEYBOARD_FR_B = 0x05,
|
||||
HID_KEYBOARD_FR_C = 0x06,
|
||||
HID_KEYBOARD_FR_D = 0x07,
|
||||
HID_KEYBOARD_FR_E = 0x08,
|
||||
HID_KEYBOARD_FR_F = 0x09,
|
||||
HID_KEYBOARD_FR_G = 0x0A,
|
||||
HID_KEYBOARD_FR_H = 0x0B,
|
||||
HID_KEYBOARD_FR_I = 0x0C,
|
||||
HID_KEYBOARD_FR_J = 0x0D,
|
||||
HID_KEYBOARD_FR_K = 0x0E,
|
||||
HID_KEYBOARD_FR_L = 0x0F,
|
||||
HID_KEYBOARD_FR_M = 0x33,
|
||||
HID_KEYBOARD_FR_N = 0x11,
|
||||
HID_KEYBOARD_FR_O = 0x12,
|
||||
HID_KEYBOARD_FR_P = 0x13,
|
||||
HID_KEYBOARD_FR_Q = 0x04,
|
||||
HID_KEYBOARD_FR_R = 0x15,
|
||||
HID_KEYBOARD_FR_S = 0x16,
|
||||
HID_KEYBOARD_FR_T = 0x17,
|
||||
HID_KEYBOARD_FR_U = 0x18,
|
||||
HID_KEYBOARD_FR_V = 0x19,
|
||||
HID_KEYBOARD_FR_W = 0x1D,
|
||||
HID_KEYBOARD_FR_X = 0x1B,
|
||||
HID_KEYBOARD_FR_Y = 0x1C,
|
||||
HID_KEYBOARD_FR_Z = 0x1A,
|
||||
|
||||
HID_KEYBOARD_FR_1 = 0x1E,
|
||||
HID_KEYBOARD_FR_2 = 0x1F,
|
||||
HID_KEYBOARD_FR_3 = 0x20,
|
||||
HID_KEYBOARD_FR_4 = 0x21,
|
||||
HID_KEYBOARD_FR_5 = 0x22,
|
||||
HID_KEYBOARD_FR_6 = 0x23,
|
||||
HID_KEYBOARD_FR_7 = 0x24,
|
||||
HID_KEYBOARD_FR_8 = 0x25,
|
||||
HID_KEYBOARD_FR_9 = 0x26,
|
||||
HID_KEYBOARD_FR_0 = 0x27,
|
||||
|
||||
HID_KEYBOARD_FR_EXCLAMATION = 0x38,
|
||||
HID_KEYBOARD_FR_DOUBLE_QUOTE = 0x20,
|
||||
HID_KEYBOARD_FR_DOLLAR = 0x30,
|
||||
HID_KEYBOARD_FR_U_BACKTICK = 0x34,
|
||||
HID_KEYBOARD_FR_AND = 0x1E,
|
||||
HID_KEYBOARD_FR_SINGLE_QUOTE = 0x21,
|
||||
HID_KEYBOARD_FR_LEFT_PARENTHESIS = 0x22,
|
||||
HID_KEYBOARD_FR_RIGHT_PARENTHESIS = 0x2D,
|
||||
HID_KEYBOARD_FR_STAR = 0x31,
|
||||
HID_KEYBOARD_FR_EQUAL = 0x2E,
|
||||
HID_KEYBOARD_FR_COMMA = 0x10,
|
||||
HID_KEYBOARD_FR_DASH = 0x23,
|
||||
HID_KEYBOARD_FR_SEMI_COLON = 0x36,
|
||||
HID_KEYBOARD_FR_DOUBLE_POINTS = 0x37,
|
||||
HID_KEYBOARD_FR_SMALLER = 0x64,
|
||||
HID_KEYBOARD_FR_UNDERSCORE = 0x25,
|
||||
HID_KEYBOARD_FR_CIRCUMFLEX = 0x2F,
|
||||
HID_KEYBOARD_FR_A_BACKTICK = 0x27,
|
||||
HID_KEYBOARD_FR_E_ACCENT = 0x1F,
|
||||
HID_KEYBOARD_FR_E_BACKTICK = 0x24,
|
||||
HID_KEYBOARD_FR_C_CEDILLE = 0x26,
|
||||
|
||||
HID_KEYBOARD_FR_CAPS_LOCK = 0xC1,
|
||||
HID_KEYBOARD_FR_F1 = 0xC2,
|
||||
HID_KEYBOARD_FR_F2 = 0xC3,
|
||||
HID_KEYBOARD_FR_F3 = 0xC4,
|
||||
HID_KEYBOARD_FR_F4 = 0xC5,
|
||||
HID_KEYBOARD_FR_F5 = 0xC6,
|
||||
HID_KEYBOARD_FR_F6 = 0xC7,
|
||||
HID_KEYBOARD_FR_F7 = 0xC8,
|
||||
HID_KEYBOARD_FR_F8 = 0xC9,
|
||||
HID_KEYBOARD_FR_F9 = 0xCA,
|
||||
HID_KEYBOARD_FR_F10 = 0xCB,
|
||||
HID_KEYBOARD_FR_F11 = 0xCC,
|
||||
HID_KEYBOARD_FR_F12 = 0xCD,
|
||||
HID_KEYBOARD_FR_PRINT = 0x63,
|
||||
HID_KEYBOARD_FR_SCROLL_LOCK = 0x47,
|
||||
HID_KEYBOARD_FR_PAUSE = 0x48,
|
||||
HID_KEYBOARD_FR_INSERT = 0xD1,
|
||||
HID_KEYBOARD_FR_HOME = 0xD2,
|
||||
HID_KEYBOARD_FR_PAGE_UP = 0xD3,
|
||||
HID_KEYBOARD_FR_DELETE = 0xD4,
|
||||
HID_KEYBOARD_FR_END = 0xD5,
|
||||
HID_KEYBOARD_FR_PAGE_DOWN = 0xD6,
|
||||
HID_KEYBOARD_FR_RIGHT_ARROW = 0xD7,
|
||||
HID_KEYBOARD_FR_LEFT_ARROW = 0xD8,
|
||||
HID_KEYBOARD_FR_DOWN_ARROW = 0xD9,
|
||||
HID_KEYBOARD_FR_UP_ARROW = 0xDA,
|
||||
HID_KEYBOARD_FR_NUM_LOCK = 0x53,
|
||||
HID_KEYBOARD_FR_NON_US = 0x64,
|
||||
HID_KEYBOARD_FR_APPLICATION = 0x65,
|
||||
};
|
||||
|
||||
/** ASCII to keycode conversion table FR */
|
||||
static const uint16_t hid_asciimap_fr[] = {
|
||||
HID_KEYBOARD_NONE, // NUL
|
||||
HID_KEYBOARD_NONE, // SOH
|
||||
HID_KEYBOARD_NONE, // STX
|
||||
HID_KEYBOARD_NONE, // ETX
|
||||
HID_KEYBOARD_NONE, // EOT
|
||||
HID_KEYBOARD_NONE, // ENQ
|
||||
HID_KEYBOARD_NONE, // ACK
|
||||
HID_KEYBOARD_NONE, // BEL
|
||||
HID_KEYBOARD_FR_BACKSPACE, // BS Backspace
|
||||
HID_KEYBOARD_FR_TAB, // TAB Tab
|
||||
HID_KEYBOARD_FR_ENTER, // LF Enter
|
||||
HID_KEYBOARD_NONE, // VT
|
||||
HID_KEYBOARD_NONE, // FF
|
||||
HID_KEYBOARD_NONE, // CR
|
||||
HID_KEYBOARD_NONE, // SO
|
||||
HID_KEYBOARD_NONE, // SI
|
||||
HID_KEYBOARD_NONE, // DEL
|
||||
HID_KEYBOARD_NONE, // DC1
|
||||
HID_KEYBOARD_NONE, // DC2
|
||||
HID_KEYBOARD_NONE, // DC3
|
||||
HID_KEYBOARD_NONE, // DC4
|
||||
HID_KEYBOARD_NONE, // NAK
|
||||
HID_KEYBOARD_NONE, // SYN
|
||||
HID_KEYBOARD_NONE, // ETB
|
||||
HID_KEYBOARD_NONE, // CAN
|
||||
HID_KEYBOARD_NONE, // EM
|
||||
HID_KEYBOARD_NONE, // SUB
|
||||
HID_KEYBOARD_NONE, // ESC
|
||||
HID_KEYBOARD_NONE, // FS
|
||||
HID_KEYBOARD_NONE, // GS
|
||||
HID_KEYBOARD_NONE, // RS
|
||||
HID_KEYBOARD_NONE, // US
|
||||
HID_KEYBOARD_FR_SPACE, // ' ' Space
|
||||
HID_KEYBOARD_FR_EXCLAMATION, // !
|
||||
HID_KEYBOARD_FR_DOUBLE_QUOTE, // "
|
||||
HID_KEYBOARD_FR_DOUBLE_QUOTE | KEY_MOD_RIGHT_ALT, // #
|
||||
HID_KEYBOARD_FR_DOLLAR, // $
|
||||
HID_KEYBOARD_FR_U_BACKTICK | KEY_MOD_LEFT_SHIFT, // %
|
||||
HID_KEYBOARD_FR_AND, // &
|
||||
HID_KEYBOARD_FR_SINGLE_QUOTE, // '
|
||||
HID_KEYBOARD_FR_LEFT_PARENTHESIS, // (
|
||||
HID_KEYBOARD_FR_RIGHT_PARENTHESIS, // )
|
||||
HID_KEYBOARD_FR_STAR, // *
|
||||
HID_KEYBOARD_FR_EQUAL | KEY_MOD_LEFT_SHIFT, // +
|
||||
HID_KEYBOARD_FR_COMMA, // ,
|
||||
HID_KEYBOARD_FR_DASH, // -
|
||||
HID_KEYBOARD_FR_SEMI_COLON | KEY_MOD_LEFT_SHIFT, // .
|
||||
HID_KEYBOARD_FR_DOUBLE_POINTS | KEY_MOD_LEFT_SHIFT, // /
|
||||
HID_KEYBOARD_FR_A_BACKTICK | KEY_MOD_LEFT_SHIFT, // 0
|
||||
HID_KEYBOARD_FR_AND | KEY_MOD_LEFT_SHIFT, // 1
|
||||
HID_KEYBOARD_FR_E_ACCENT | KEY_MOD_LEFT_SHIFT, // 2
|
||||
HID_KEYBOARD_FR_DOUBLE_QUOTE | KEY_MOD_LEFT_SHIFT, // 3
|
||||
HID_KEYBOARD_FR_SINGLE_QUOTE | KEY_MOD_LEFT_SHIFT, // 4
|
||||
HID_KEYBOARD_FR_LEFT_PARENTHESIS | KEY_MOD_LEFT_SHIFT, // 5
|
||||
HID_KEYBOARD_FR_DASH | KEY_MOD_LEFT_SHIFT, // 6
|
||||
HID_KEYBOARD_FR_E_BACKTICK | KEY_MOD_LEFT_SHIFT, // 7
|
||||
HID_KEYBOARD_FR_UNDERSCORE | KEY_MOD_LEFT_SHIFT, // 8
|
||||
HID_KEYBOARD_FR_C_CEDILLE | KEY_MOD_LEFT_SHIFT, // 9
|
||||
HID_KEYBOARD_FR_DOUBLE_POINTS, // :
|
||||
HID_KEYBOARD_FR_SEMI_COLON, // ;
|
||||
HID_KEYBOARD_FR_SMALLER, // <
|
||||
HID_KEYBOARD_FR_EQUAL, // =
|
||||
HID_KEYBOARD_FR_SMALLER | KEY_MOD_LEFT_SHIFT, // >
|
||||
HID_KEYBOARD_FR_COMMA | KEY_MOD_LEFT_SHIFT, // ?
|
||||
HID_KEYBOARD_FR_A_BACKTICK | KEY_MOD_RIGHT_ALT, // @
|
||||
HID_KEYBOARD_FR_A | KEY_MOD_LEFT_SHIFT, // A
|
||||
HID_KEYBOARD_FR_B | KEY_MOD_LEFT_SHIFT, // B
|
||||
HID_KEYBOARD_FR_C | KEY_MOD_LEFT_SHIFT, // C
|
||||
HID_KEYBOARD_FR_D | KEY_MOD_LEFT_SHIFT, // D
|
||||
HID_KEYBOARD_FR_E | KEY_MOD_LEFT_SHIFT, // E
|
||||
HID_KEYBOARD_FR_F | KEY_MOD_LEFT_SHIFT, // F
|
||||
HID_KEYBOARD_FR_G | KEY_MOD_LEFT_SHIFT, // G
|
||||
HID_KEYBOARD_FR_H | KEY_MOD_LEFT_SHIFT, // H
|
||||
HID_KEYBOARD_FR_I | KEY_MOD_LEFT_SHIFT, // I
|
||||
HID_KEYBOARD_FR_J | KEY_MOD_LEFT_SHIFT, // J
|
||||
HID_KEYBOARD_FR_K | KEY_MOD_LEFT_SHIFT, // K
|
||||
HID_KEYBOARD_FR_L | KEY_MOD_LEFT_SHIFT, // L
|
||||
HID_KEYBOARD_FR_M | KEY_MOD_LEFT_SHIFT, // M
|
||||
HID_KEYBOARD_FR_N | KEY_MOD_LEFT_SHIFT, // N
|
||||
HID_KEYBOARD_FR_O | KEY_MOD_LEFT_SHIFT, // O
|
||||
HID_KEYBOARD_FR_P | KEY_MOD_LEFT_SHIFT, // P
|
||||
HID_KEYBOARD_FR_Q | KEY_MOD_LEFT_SHIFT, // Q
|
||||
HID_KEYBOARD_FR_R | KEY_MOD_LEFT_SHIFT, // R
|
||||
HID_KEYBOARD_FR_S | KEY_MOD_LEFT_SHIFT, // S
|
||||
HID_KEYBOARD_FR_T | KEY_MOD_LEFT_SHIFT, // T
|
||||
HID_KEYBOARD_FR_U | KEY_MOD_LEFT_SHIFT, // U
|
||||
HID_KEYBOARD_FR_V | KEY_MOD_LEFT_SHIFT, // V
|
||||
HID_KEYBOARD_FR_W | KEY_MOD_LEFT_SHIFT, // W
|
||||
HID_KEYBOARD_FR_X | KEY_MOD_LEFT_SHIFT, // X
|
||||
HID_KEYBOARD_FR_Y | KEY_MOD_LEFT_SHIFT, // Y
|
||||
HID_KEYBOARD_FR_Z | KEY_MOD_LEFT_SHIFT, // Z
|
||||
HID_KEYBOARD_FR_LEFT_PARENTHESIS | KEY_MOD_RIGHT_ALT, // [
|
||||
HID_KEYBOARD_FR_UNDERSCORE | KEY_MOD_RIGHT_ALT, // bslash
|
||||
HID_KEYBOARD_FR_RIGHT_PARENTHESIS | KEY_MOD_RIGHT_ALT, // ]
|
||||
HID_KEYBOARD_FR_CIRCUMFLEX, // ^
|
||||
HID_KEYBOARD_FR_UNDERSCORE, // _
|
||||
HID_KEYBOARD_FR_E_BACKTICK | KEY_MOD_RIGHT_ALT, // `
|
||||
HID_KEYBOARD_FR_A, // a
|
||||
HID_KEYBOARD_FR_B, // b
|
||||
HID_KEYBOARD_FR_C, // c
|
||||
HID_KEYBOARD_FR_D, // d
|
||||
HID_KEYBOARD_FR_E, // e
|
||||
HID_KEYBOARD_FR_F, // f
|
||||
HID_KEYBOARD_FR_G, // g
|
||||
HID_KEYBOARD_FR_H, // h
|
||||
HID_KEYBOARD_FR_I, // i
|
||||
HID_KEYBOARD_FR_J, // j
|
||||
HID_KEYBOARD_FR_K, // k
|
||||
HID_KEYBOARD_FR_L, // l
|
||||
HID_KEYBOARD_FR_M, // m
|
||||
HID_KEYBOARD_FR_N, // n
|
||||
HID_KEYBOARD_FR_O, // o
|
||||
HID_KEYBOARD_FR_P, // p
|
||||
HID_KEYBOARD_FR_Q, // q
|
||||
HID_KEYBOARD_FR_R, // r
|
||||
HID_KEYBOARD_FR_S, // s
|
||||
HID_KEYBOARD_FR_T, // t
|
||||
HID_KEYBOARD_FR_U, // u
|
||||
HID_KEYBOARD_FR_V, // v
|
||||
HID_KEYBOARD_FR_W, // w
|
||||
HID_KEYBOARD_FR_X, // x
|
||||
HID_KEYBOARD_FR_Y, // y
|
||||
HID_KEYBOARD_FR_Z, // z
|
||||
HID_KEYBOARD_FR_SINGLE_QUOTE | KEY_MOD_RIGHT_ALT, // {
|
||||
HID_KEYBOARD_FR_DASH | KEY_MOD_RIGHT_ALT, // |
|
||||
HID_KEYBOARD_FR_EQUAL | KEY_MOD_RIGHT_ALT, // }
|
||||
HID_KEYBOARD_FR_E_ACCENT | KEY_MOD_RIGHT_ALT, // ~
|
||||
HID_KEYBOARD_NONE, // DEL
|
||||
};
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
static const uint16_t* hid_asciimaps[] = {hid_asciimap, hid_asciimap_de, hid_asciimap_fr};
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
typedef struct {
|
||||
uint32_t vid;
|
||||
uint32_t pid;
|
||||
@ -163,7 +637,8 @@ typedef struct {
|
||||
typedef void (*HidStateCallback)(bool state, void* context);
|
||||
|
||||
/** ASCII to keycode conversion macro */
|
||||
#define HID_ASCII_TO_KEY(x) (((uint8_t)x < 128) ? (hid_asciimap[(uint8_t)x]) : HID_KEYBOARD_NONE)
|
||||
#define HID_ASCII_TO_KEY(x, y) \
|
||||
(((uint8_t)y < 128) ? (hid_asciimaps[(uint8_t)x][(uint8_t)y]) : HID_KEYBOARD_NONE)
|
||||
|
||||
/** HID keyboard leds */
|
||||
enum HidKeyboardLeds {
|
||||
@ -250,4 +725,4 @@ bool furi_hal_hid_consumer_key_press(uint16_t button);
|
||||
*
|
||||
* @param button key code
|
||||
*/
|
||||
bool furi_hal_hid_consumer_key_release(uint16_t button);
|
||||
bool furi_hal_hid_consumer_key_release(uint16_t button);
|
513
lib/drivers/nrf24.c
Normal file
513
lib/drivers/nrf24.c
Normal file
@ -0,0 +1,513 @@
|
||||
#include "nrf24.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
void nrf24_init() {
|
||||
furi_hal_spi_bus_handle_init(nrf24_HANDLE);
|
||||
furi_hal_spi_acquire(nrf24_HANDLE);
|
||||
furi_hal_gpio_init(nrf24_CE_PIN, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh);
|
||||
furi_hal_gpio_write(nrf24_CE_PIN, false);
|
||||
}
|
||||
|
||||
void nrf24_spi_trx(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* tx,
|
||||
uint8_t* rx,
|
||||
uint8_t size,
|
||||
uint32_t timeout) {
|
||||
UNUSED(timeout);
|
||||
furi_hal_gpio_write(handle->cs, false);
|
||||
furi_hal_spi_bus_trx(handle, tx, rx, size, nrf24_TIMEOUT);
|
||||
furi_hal_gpio_write(handle->cs, true);
|
||||
}
|
||||
|
||||
uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data) {
|
||||
uint8_t tx[2] = {W_REGISTER | (REGISTER_MASK & reg), data};
|
||||
uint8_t rx[2] = {0};
|
||||
nrf24_spi_trx(handle, tx, rx, 2, nrf24_TIMEOUT);
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
uint8_t
|
||||
nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) {
|
||||
uint8_t tx[size + 1];
|
||||
uint8_t rx[size + 1];
|
||||
memset(rx, 0, size + 1);
|
||||
tx[0] = W_REGISTER | (REGISTER_MASK & reg);
|
||||
memcpy(&tx[1], data, size);
|
||||
nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT);
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size) {
|
||||
uint8_t tx[size + 1];
|
||||
uint8_t rx[size + 1];
|
||||
memset(rx, 0, size + 1);
|
||||
tx[0] = R_REGISTER | (REGISTER_MASK & reg);
|
||||
memset(&tx[1], 0, size);
|
||||
nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT);
|
||||
memcpy(data, &rx[1], size);
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t tx[] = {FLUSH_RX};
|
||||
uint8_t rx[] = {0};
|
||||
nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT);
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t tx[] = {FLUSH_TX};
|
||||
uint8_t rx[] = {0};
|
||||
nrf24_spi_trx(handle, tx, rx, 1, nrf24_TIMEOUT);
|
||||
return rx[0];
|
||||
}
|
||||
|
||||
uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t maclen;
|
||||
nrf24_read_reg(handle, REG_SETUP_AW, &maclen, 1);
|
||||
maclen &= 3;
|
||||
return maclen + 2;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen) {
|
||||
assert(maclen > 1 && maclen < 6);
|
||||
uint8_t status = 0;
|
||||
status = nrf24_write_reg(handle, REG_SETUP_AW, maclen - 2);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_status(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t status;
|
||||
uint8_t tx[] = {R_REGISTER | (REGISTER_MASK & REG_STATUS)};
|
||||
nrf24_spi_trx(handle, tx, &status, 1, nrf24_TIMEOUT);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t setup = 0;
|
||||
uint32_t rate = 0;
|
||||
nrf24_read_reg(handle, REG_RF_SETUP, &setup, 1);
|
||||
setup &= 0x28;
|
||||
if(setup == 0x20)
|
||||
rate = 250000; // 250kbps
|
||||
else if(setup == 0x08)
|
||||
rate = 2000000; // 2Mbps
|
||||
else if(setup == 0x00)
|
||||
rate = 1000000; // 1Mbps
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate) {
|
||||
uint8_t r6 = 0;
|
||||
uint8_t status = 0;
|
||||
if(!rate) rate = 2000000;
|
||||
|
||||
nrf24_read_reg(handle, REG_RF_SETUP, &r6, 1); // RF_SETUP register
|
||||
r6 = r6 & (~0x28); // Clear rate fields.
|
||||
if(rate == 2000000)
|
||||
r6 = r6 | 0x08;
|
||||
else if(rate == 1000000)
|
||||
r6 = r6;
|
||||
else if(rate == 250000)
|
||||
r6 = r6 | 0x20;
|
||||
|
||||
status = nrf24_write_reg(handle, REG_RF_SETUP, r6); // Write new rate.
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t channel = 0;
|
||||
nrf24_read_reg(handle, REG_RF_CH, &channel, 1);
|
||||
return channel;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan) {
|
||||
uint8_t status;
|
||||
status = nrf24_write_reg(handle, REG_RF_CH, chan);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) {
|
||||
uint8_t size = 0;
|
||||
uint8_t status = 0;
|
||||
size = nrf24_get_maclen(handle);
|
||||
status = nrf24_read_reg(handle, REG_RX_ADDR_P0, mac, size);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) {
|
||||
uint8_t status = 0;
|
||||
uint8_t clearmac[] = {0, 0, 0, 0, 0};
|
||||
nrf24_set_maclen(handle, size);
|
||||
nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, clearmac, 5);
|
||||
status = nrf24_write_buf_reg(handle, REG_RX_ADDR_P0, mac, size);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac) {
|
||||
uint8_t size = 0;
|
||||
uint8_t status = 0;
|
||||
size = nrf24_get_maclen(handle);
|
||||
status = nrf24_read_reg(handle, REG_TX_ADDR, mac, size);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size) {
|
||||
uint8_t status = 0;
|
||||
uint8_t clearmac[] = {0, 0, 0, 0, 0};
|
||||
nrf24_set_maclen(handle, size);
|
||||
nrf24_write_buf_reg(handle, REG_TX_ADDR, clearmac, 5);
|
||||
status = nrf24_write_buf_reg(handle, REG_TX_ADDR, mac, size);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t len = 0;
|
||||
nrf24_read_reg(handle, RX_PW_P0, &len, 1);
|
||||
return len;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len) {
|
||||
uint8_t status = 0;
|
||||
status = nrf24_write_reg(handle, RX_PW_P0, len);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full) {
|
||||
uint8_t status = 0;
|
||||
uint8_t size = 0;
|
||||
uint8_t tx_pl_wid[] = {R_RX_PL_WID, 0};
|
||||
uint8_t rx_pl_wid[] = {0, 0};
|
||||
uint8_t tx_cmd[33] = {0}; // 32 max payload size + 1 for command
|
||||
uint8_t tmp_packet[33] = {0};
|
||||
|
||||
status = nrf24_status(handle);
|
||||
|
||||
if(status & 0x40) {
|
||||
if(full)
|
||||
size = nrf24_get_packetlen(handle);
|
||||
else {
|
||||
nrf24_spi_trx(handle, tx_pl_wid, rx_pl_wid, 2, nrf24_TIMEOUT);
|
||||
size = rx_pl_wid[1];
|
||||
}
|
||||
|
||||
tx_cmd[0] = R_RX_PAYLOAD;
|
||||
nrf24_spi_trx(handle, tx_cmd, tmp_packet, size + 1, nrf24_TIMEOUT);
|
||||
nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit.
|
||||
memcpy(packet, &tmp_packet[1], size);
|
||||
} else if(status == 0) {
|
||||
nrf24_flush_rx(handle);
|
||||
nrf24_write_reg(handle, REG_STATUS, 0x40); // clear bit.
|
||||
}
|
||||
|
||||
*packetsize = size;
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack) {
|
||||
uint8_t status = 0;
|
||||
uint8_t tx[size + 1];
|
||||
uint8_t rx[size + 1];
|
||||
memset(tx, 0, size + 1);
|
||||
memset(rx, 0, size + 1);
|
||||
|
||||
if(!ack)
|
||||
tx[0] = W_TX_PAYLOAD_NOACK;
|
||||
else
|
||||
tx[0] = W_TX_PAYLOAD;
|
||||
|
||||
memcpy(&tx[1], payload, size);
|
||||
nrf24_spi_trx(handle, tx, rx, size + 1, nrf24_TIMEOUT);
|
||||
nrf24_set_tx_mode(handle);
|
||||
|
||||
while(!(status & (TX_DS | MAX_RT))) status = nrf24_status(handle);
|
||||
|
||||
if(status & MAX_RT) nrf24_flush_tx(handle);
|
||||
|
||||
nrf24_set_idle(handle);
|
||||
nrf24_write_reg(handle, REG_STATUS, TX_DS | MAX_RT);
|
||||
return status & TX_DS;
|
||||
}
|
||||
|
||||
uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t status = 0;
|
||||
uint8_t cfg = 0;
|
||||
nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
|
||||
cfg = cfg | 2;
|
||||
status = nrf24_write_reg(handle, REG_CONFIG, cfg);
|
||||
furi_delay_ms(5000);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t status = 0;
|
||||
uint8_t cfg = 0;
|
||||
nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
|
||||
cfg &= 0xfc; // clear bottom two bits to power down the radio
|
||||
status = nrf24_write_reg(handle, REG_CONFIG, cfg);
|
||||
//nr204_write_reg(handle, REG_EN_RXADDR, 0x0);
|
||||
furi_hal_gpio_write(nrf24_CE_PIN, false);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t status = 0;
|
||||
uint8_t cfg = 0;
|
||||
//status = nrf24_write_reg(handle, REG_CONFIG, 0x0F); // enable 2-byte CRC, PWR_UP, and PRIM_RX
|
||||
nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
|
||||
cfg |= 0x03; // PWR_UP, and PRIM_RX
|
||||
status = nrf24_write_reg(handle, REG_CONFIG, cfg);
|
||||
//nr204_write_reg(REG_EN_RXADDR, 0x03) // Set RX Pipe 0 and 1
|
||||
furi_hal_gpio_write(nrf24_CE_PIN, true);
|
||||
furi_delay_ms(2000);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle) {
|
||||
uint8_t status = 0;
|
||||
uint8_t cfg = 0;
|
||||
furi_hal_gpio_write(nrf24_CE_PIN, false);
|
||||
nrf24_write_reg(handle, REG_STATUS, 0x30);
|
||||
//status = nrf24_write_reg(handle, REG_CONFIG, 0x0E); // enable 2-byte CRC, PWR_UP
|
||||
nrf24_read_reg(handle, REG_CONFIG, &cfg, 1);
|
||||
cfg &= 0xfe; // disable PRIM_RX
|
||||
cfg |= 0x02; // PWR_UP
|
||||
status = nrf24_write_reg(handle, REG_CONFIG, cfg);
|
||||
furi_hal_gpio_write(nrf24_CE_PIN, true);
|
||||
furi_delay_ms(2);
|
||||
return status;
|
||||
}
|
||||
|
||||
void nrf24_configure(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t rate,
|
||||
uint8_t* srcmac,
|
||||
uint8_t* dstmac,
|
||||
uint8_t maclen,
|
||||
uint8_t channel,
|
||||
bool noack,
|
||||
bool disable_aa) {
|
||||
assert(channel <= 125);
|
||||
assert(rate == 1 || rate == 2);
|
||||
if(rate == 2)
|
||||
rate = 8; // 2Mbps
|
||||
else
|
||||
rate = 0; // 1Mbps
|
||||
|
||||
nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF
|
||||
nrf24_set_idle(handle);
|
||||
nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts
|
||||
if(disable_aa)
|
||||
nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst
|
||||
else
|
||||
nrf24_write_reg(handle, REG_EN_AA, 0x1F); // Enable Shockburst
|
||||
|
||||
nrf24_write_reg(handle, REG_DYNPD, 0x3F); // enable dynamic payload length on all pipes
|
||||
if(noack)
|
||||
nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack
|
||||
else {
|
||||
nrf24_write_reg(handle, REG_CONFIG, 0x0C); // 2 byte CRC
|
||||
nrf24_write_reg(handle, REG_FEATURE, 0x07); // enable dyn payload and ack
|
||||
nrf24_write_reg(
|
||||
handle, REG_SETUP_RETR, 0x1f); // 15 retries for AA, 500us auto retransmit delay
|
||||
}
|
||||
|
||||
nrf24_set_idle(handle);
|
||||
nrf24_flush_rx(handle);
|
||||
nrf24_flush_tx(handle);
|
||||
|
||||
if(maclen) nrf24_set_maclen(handle, maclen);
|
||||
if(srcmac) nrf24_set_src_mac(handle, srcmac, maclen);
|
||||
if(dstmac) nrf24_set_dst_mac(handle, dstmac, maclen);
|
||||
|
||||
nrf24_write_reg(handle, REG_RF_CH, channel);
|
||||
nrf24_write_reg(handle, REG_RF_SETUP, rate);
|
||||
furi_delay_ms(200);
|
||||
}
|
||||
|
||||
void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate) {
|
||||
//uint8_t preamble[] = {0x55, 0x00}; // little endian
|
||||
uint8_t preamble[] = {0xAA, 0x00}; // little endian
|
||||
//uint8_t preamble[] = {0x00, 0x55}; // little endian
|
||||
//uint8_t preamble[] = {0x00, 0xAA}; // little endian
|
||||
nrf24_write_reg(handle, REG_CONFIG, 0x00); // Stop nRF
|
||||
nrf24_write_reg(handle, REG_STATUS, 0x1c); // clear interrupts
|
||||
nrf24_write_reg(handle, REG_DYNPD, 0x0); // disable shockburst
|
||||
nrf24_write_reg(handle, REG_EN_AA, 0x00); // Disable Shockburst
|
||||
nrf24_write_reg(handle, REG_FEATURE, 0x05); // disable payload-with-ack, enable noack
|
||||
nrf24_set_maclen(handle, 2); // shortest address
|
||||
nrf24_set_src_mac(handle, preamble, 2); // set src mac to preamble bits to catch everything
|
||||
nrf24_set_packetlen(handle, 32); // set max packet length
|
||||
nrf24_set_idle(handle);
|
||||
nrf24_flush_rx(handle);
|
||||
nrf24_flush_tx(handle);
|
||||
nrf24_write_reg(handle, REG_RF_CH, channel);
|
||||
nrf24_write_reg(handle, REG_RF_SETUP, rate);
|
||||
furi_delay_ms(200);
|
||||
|
||||
// prime for RX, no checksum
|
||||
nrf24_write_reg(handle, REG_CONFIG, 0x03); // PWR_UP and PRIM_RX, disable AA and CRC
|
||||
furi_hal_gpio_write(nrf24_CE_PIN, true);
|
||||
furi_delay_ms(2000);
|
||||
}
|
||||
|
||||
void hexlify(uint8_t* in, uint8_t size, char* out) {
|
||||
memset(out, 0, size * 2);
|
||||
for(int i = 0; i < size; i++) sprintf(out + strlen(out), "%02X", in[i]);
|
||||
}
|
||||
|
||||
uint64_t bytes_to_int64(uint8_t* bytes, uint8_t size, bool bigendian) {
|
||||
uint64_t ret = 0;
|
||||
for(int i = 0; i < size; i++)
|
||||
if(bigendian)
|
||||
ret |= bytes[i] << ((size - 1 - i) * 8);
|
||||
else
|
||||
ret |= bytes[i] << (i * 8);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
if(bigendian)
|
||||
out[i] = (val >> ((7 - i) * 8)) & 0xff;
|
||||
else
|
||||
out[i] = (val >> (i * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian) {
|
||||
uint32_t ret = 0;
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(bigendian)
|
||||
ret |= bytes[i] << ((3 - i) * 8);
|
||||
else
|
||||
ret |= bytes[i] << (i * 8);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(bigendian)
|
||||
out[i] = (val >> ((3 - i) * 8)) & 0xff;
|
||||
else
|
||||
out[i] = (val >> (i * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t bytes_to_int16(uint8_t* bytes, bool bigendian) {
|
||||
uint16_t ret = 0;
|
||||
for(int i = 0; i < 2; i++)
|
||||
if(bigendian)
|
||||
ret |= bytes[i] << ((1 - i) * 8);
|
||||
else
|
||||
ret |= bytes[i] << (i * 8);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void int16_to_bytes(uint16_t val, uint8_t* out, bool bigendian) {
|
||||
for(int i = 0; i < 2; i++) {
|
||||
if(bigendian)
|
||||
out[i] = (val >> ((1 - i) * 8)) & 0xff;
|
||||
else
|
||||
out[i] = (val >> (i * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
// handle iffyness with preamble processing sometimes being a bit (literally) off
|
||||
void alt_address_old(uint8_t* packet, uint8_t* altaddr) {
|
||||
uint8_t macmess_hi_b[4];
|
||||
uint8_t macmess_lo_b[2];
|
||||
uint32_t macmess_hi;
|
||||
uint16_t macmess_lo;
|
||||
uint8_t preserved;
|
||||
|
||||
// get first 6 bytes into 32-bit and 16-bit variables
|
||||
memcpy(macmess_hi_b, packet, 4);
|
||||
memcpy(macmess_lo_b, packet + 4, 2);
|
||||
|
||||
macmess_hi = bytes_to_int32(macmess_hi_b, true);
|
||||
|
||||
//preserve least 7 bits from hi that will be shifted down to lo
|
||||
preserved = macmess_hi & 0x7f;
|
||||
macmess_hi >>= 7;
|
||||
|
||||
macmess_lo = bytes_to_int16(macmess_lo_b, true);
|
||||
macmess_lo >>= 7;
|
||||
macmess_lo = (preserved << 9) | macmess_lo;
|
||||
int32_to_bytes(macmess_hi, macmess_hi_b, true);
|
||||
int16_to_bytes(macmess_lo, macmess_lo_b, true);
|
||||
memcpy(altaddr, &macmess_hi_b[1], 3);
|
||||
memcpy(altaddr + 3, macmess_lo_b, 2);
|
||||
}
|
||||
|
||||
bool validate_address(uint8_t* addr) {
|
||||
uint8_t bad[][3] = {{0x55, 0x55}, {0xAA, 0xAA}, {0x00, 0x00}, {0xFF, 0xFF}};
|
||||
for(int i = 0; i < 4; i++)
|
||||
for(int j = 0; j < 2; j++)
|
||||
if(!memcmp(addr + j * 2, bad[i], 2)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address) {
|
||||
bool found = false;
|
||||
uint8_t packet[32] = {0};
|
||||
uint8_t packetsize;
|
||||
//char printit[65];
|
||||
uint8_t status = 0;
|
||||
status = nrf24_rxpacket(handle, packet, &packetsize, true);
|
||||
if(status & 0x40) {
|
||||
if(validate_address(packet)) {
|
||||
for(int i = 0; i < maclen; i++) address[i] = packet[maclen - 1 - i];
|
||||
|
||||
/*
|
||||
alt_address(packet, packet);
|
||||
|
||||
for(i = 0; i < maclen; i++)
|
||||
address[i + 5] = packet[maclen - 1 - i];
|
||||
*/
|
||||
|
||||
//memcpy(address, packet, maclen);
|
||||
//hexlify(packet, packetsize, printit);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
uint8_t nrf24_find_channel(
|
||||
FuriHalSpiBusHandle* handle,
|
||||
uint8_t* srcmac,
|
||||
uint8_t* dstmac,
|
||||
uint8_t maclen,
|
||||
uint8_t rate,
|
||||
uint8_t min_channel,
|
||||
uint8_t max_channel,
|
||||
bool autoinit) {
|
||||
uint8_t ping_packet[] = {0x0f, 0x0f, 0x0f, 0x0f}; // this can be anything, we just need an ack
|
||||
uint8_t ch = max_channel + 1; // means fail
|
||||
nrf24_configure(handle, rate, srcmac, dstmac, maclen, 2, false, false);
|
||||
for(ch = min_channel; ch <= max_channel + 1; ch++) {
|
||||
nrf24_write_reg(handle, REG_RF_CH, ch);
|
||||
if(nrf24_txpacket(handle, ping_packet, 4, true)) break;
|
||||
}
|
||||
|
||||
if(autoinit) {
|
||||
FURI_LOG_I("nrf24", "initializing radio for channel %d", ch);
|
||||
nrf24_configure(handle, rate, srcmac, dstmac, maclen, ch, false, false);
|
||||
return ch;
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
368
lib/drivers/nrf24.h
Normal file
368
lib/drivers/nrf24.h
Normal file
@ -0,0 +1,368 @@
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <furi_hal_spi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define R_REGISTER 0x00
|
||||
#define W_REGISTER 0x20
|
||||
#define REGISTER_MASK 0x1F
|
||||
#define ACTIVATE 0x50
|
||||
#define R_RX_PL_WID 0x60
|
||||
#define R_RX_PAYLOAD 0x61
|
||||
#define W_TX_PAYLOAD 0xA0
|
||||
#define W_TX_PAYLOAD_NOACK 0xB0
|
||||
#define W_ACK_PAYLOAD 0xA8
|
||||
#define FLUSH_TX 0xE1
|
||||
#define FLUSH_RX 0xE2
|
||||
#define REUSE_TX_PL 0xE3
|
||||
#define RF24_NOP 0xFF
|
||||
|
||||
#define REG_CONFIG 0x00
|
||||
#define REG_EN_AA 0x01
|
||||
#define REG_EN_RXADDR 0x02
|
||||
#define REG_SETUP_AW 0x03
|
||||
#define REG_SETUP_RETR 0x04
|
||||
#define REG_DYNPD 0x1C
|
||||
#define REG_FEATURE 0x1D
|
||||
#define REG_RF_SETUP 0x06
|
||||
#define REG_STATUS 0x07
|
||||
#define REG_RX_ADDR_P0 0x0A
|
||||
#define REG_RF_CH 0x05
|
||||
#define REG_TX_ADDR 0x10
|
||||
|
||||
#define RX_PW_P0 0x11
|
||||
#define TX_DS 0x20
|
||||
#define MAX_RT 0x10
|
||||
|
||||
#define nrf24_TIMEOUT 500
|
||||
#define nrf24_CE_PIN &gpio_ext_pb2
|
||||
#define nrf24_HANDLE &furi_hal_spi_bus_handle_external
|
||||
|
||||
|
||||
/* Low level API */
|
||||
|
||||
|
||||
/** Write device register
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param reg - register
|
||||
* @param data - data to write
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_write_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t data);
|
||||
|
||||
|
||||
/** Write buffer to device register
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param reg - register
|
||||
* @param data - data to write
|
||||
* @param size - size of data to write
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_write_buf_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size);
|
||||
|
||||
|
||||
/** Read device register
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param reg - register
|
||||
* @param[out] data - pointer to data
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_read_reg(FuriHalSpiBusHandle* handle, uint8_t reg, uint8_t* data, uint8_t size);
|
||||
|
||||
|
||||
/** Power up the radio for operation
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_power_up(FuriHalSpiBusHandle* handle);
|
||||
|
||||
|
||||
/** Power down the radio
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_idle(FuriHalSpiBusHandle* handle);
|
||||
|
||||
|
||||
/** Sets the radio to RX mode
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_rx_mode(FuriHalSpiBusHandle* handle);
|
||||
|
||||
|
||||
/** Sets the radio to TX mode
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_tx_mode(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/*=============================================================================================================*/
|
||||
|
||||
/* High level API */
|
||||
|
||||
/** Must call this before using any other nrf24 API
|
||||
*
|
||||
*/
|
||||
void nrf24_init();
|
||||
|
||||
|
||||
/** Send flush rx command
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_flush_rx(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Send flush tx command
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_flush_tx(FuriHalSpiBusHandle* handle);
|
||||
|
||||
|
||||
/** Gets the RX packet length in data pipe 0
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return packet length in data pipe 0
|
||||
*/
|
||||
uint8_t nrf24_get_packetlen(FuriHalSpiBusHandle* handle);
|
||||
|
||||
|
||||
/** Sets the RX packet length in data pipe 0
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param len - length to set
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_packetlen(FuriHalSpiBusHandle* handle, uint8_t len);
|
||||
|
||||
|
||||
/** Gets configured length of MAC address
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return MAC address length
|
||||
*/
|
||||
uint8_t nrf24_get_maclen(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Sets configured length of MAC address
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param maclen - length to set MAC address to, must be greater than 1 and less than 6
|
||||
*
|
||||
* @return MAC address length
|
||||
*/
|
||||
uint8_t nrf24_set_maclen(FuriHalSpiBusHandle* handle, uint8_t maclen);
|
||||
|
||||
/** Gets the current status flags from the STATUS register
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return status flags
|
||||
*/
|
||||
uint8_t nrf24_status(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Gets the current transfer rate
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return transfer rate in bps
|
||||
*/
|
||||
uint32_t nrf24_get_rate(FuriHalSpiBusHandle* handle);
|
||||
|
||||
/** Sets the transfer rate
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param rate - the transfer rate in bps
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_rate(FuriHalSpiBusHandle* handle, uint32_t rate);
|
||||
|
||||
/** Gets the current channel
|
||||
* In nrf24, the channel number is multiplied times 1MHz and added to 2400MHz to get the frequency
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
*
|
||||
* @return channel
|
||||
*/
|
||||
uint8_t nrf24_get_chan(FuriHalSpiBusHandle* handle);
|
||||
|
||||
|
||||
/** Sets the channel
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param frequency - the frequency in hertz
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_chan(FuriHalSpiBusHandle* handle, uint8_t chan);
|
||||
|
||||
|
||||
/** Gets the source mac address
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param[out] mac - the source mac address
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_get_src_mac(FuriHalSpiBusHandle* handle, uint8_t *mac);
|
||||
|
||||
|
||||
/** Sets the source mac address
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param mac - the mac address to set
|
||||
* @param size - the size of the mac address (2 to 5)
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_src_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size);
|
||||
|
||||
|
||||
/** Gets the dest mac address
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param[out] mac - the source mac address
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_get_dst_mac(FuriHalSpiBusHandle* handle, uint8_t *mac);
|
||||
|
||||
|
||||
/** Sets the dest mac address
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param mac - the mac address to set
|
||||
* @param size - the size of the mac address (2 to 5)
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_set_dst_mac(FuriHalSpiBusHandle* handle, uint8_t* mac, uint8_t size);
|
||||
|
||||
|
||||
/** Reads RX packet
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param[out] packet - the packet contents
|
||||
* @param[out] packetsize - size of the received packet
|
||||
* @param full - boolean set to true, packet length is determined by RX_PW_P0 register, false it is determined by dynamic payload length command
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_rxpacket(FuriHalSpiBusHandle* handle, uint8_t* packet, uint8_t* packetsize, bool full);
|
||||
|
||||
|
||||
/** Sends TX packet
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param packet - the packet contents
|
||||
* @param size - packet size
|
||||
* @param ack - boolean to determine whether an ACK is required for the packet or not
|
||||
*
|
||||
* @return device status
|
||||
*/
|
||||
uint8_t nrf24_txpacket(FuriHalSpiBusHandle* handle, uint8_t* payload, uint8_t size, bool ack);
|
||||
|
||||
|
||||
/** Configure the radio
|
||||
* This is not comprehensive, but covers a lot of the common configuration options that may be changed
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param rate - transfer rate in Mbps (1 or 2)
|
||||
* @param srcmac - source mac address
|
||||
* @param dstmac - destination mac address
|
||||
* @param maclen - length of mac address
|
||||
* @param channel - channel to tune to
|
||||
* @param noack - if true, disable auto-acknowledge
|
||||
* @param disable_aa - if true, disable ShockBurst
|
||||
*
|
||||
*/
|
||||
void nrf24_configure(FuriHalSpiBusHandle* handle, uint8_t rate, uint8_t* srcmac, uint8_t* dstmac, uint8_t maclen, uint8_t channel, bool noack, bool disable_aa);
|
||||
|
||||
/** Configures the radio for "promiscuous mode" and primes it for rx
|
||||
* This is not an actual mode of the nrf24, but this function exploits a few bugs in the chip that allows it to act as if it were.
|
||||
* See http://travisgoodspeed.blogspot.com/2011/02/promiscuity-is-nrf24l01s-duty.html for details.
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param channel - channel to tune to
|
||||
* @param rate - transfer rate in Mbps (1 or 2)
|
||||
*/
|
||||
void nrf24_init_promisc_mode(FuriHalSpiBusHandle* handle, uint8_t channel, uint8_t rate);
|
||||
|
||||
|
||||
/** Listens for a packet and returns first possible address sniffed
|
||||
* Call this only after calling nrf24_init_promisc_mode
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param maclen - length of target mac address
|
||||
* @param[out] addresses - sniffed address
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
bool nrf24_sniff_address(FuriHalSpiBusHandle* handle, uint8_t maclen, uint8_t* address);
|
||||
|
||||
/** Sends ping packet on each channel for designated tx mac looking for ack
|
||||
*
|
||||
* @param handle - pointer to FuriHalSpiHandle
|
||||
* @param srcmac - source address
|
||||
* @param dstmac - destination address
|
||||
* @param maclen - length of address
|
||||
* @param rate - transfer rate in Mbps (1 or 2)
|
||||
* @param min_channel - channel to start with
|
||||
* @param max_channel - channel to end at
|
||||
* @param autoinit - if true, automatically configure radio for this channel
|
||||
*
|
||||
* @return channel that the address is listening on, if this value is above the max_channel param, it failed
|
||||
*/
|
||||
uint8_t nrf24_find_channel(FuriHalSpiBusHandle* handle, uint8_t* srcmac, uint8_t* dstmac, uint8_t maclen, uint8_t rate, uint8_t min_channel, uint8_t max_channel, bool autoinit);
|
||||
|
||||
|
||||
/** Converts 64 bit value into uint8_t array
|
||||
* @param val - 64-bit integer
|
||||
* @param[out] out - bytes out
|
||||
* @param bigendian - if true, convert as big endian, otherwise little endian
|
||||
*/
|
||||
void int64_to_bytes(uint64_t val, uint8_t* out, bool bigendian);
|
||||
|
||||
|
||||
/** Converts 32 bit value into uint8_t array
|
||||
* @param val - 32-bit integer
|
||||
* @param[out] out - bytes out
|
||||
* @param bigendian - if true, convert as big endian, otherwise little endian
|
||||
*/
|
||||
void int32_to_bytes(uint32_t val, uint8_t* out, bool bigendian);
|
||||
|
||||
|
||||
/** Converts uint8_t array into 32 bit value
|
||||
* @param bytes - uint8_t array
|
||||
* @param bigendian - if true, convert as big endian, otherwise little endian
|
||||
*
|
||||
* @return 32-bit value
|
||||
*/
|
||||
uint32_t bytes_to_int32(uint8_t* bytes, bool bigendian);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user