This commit is contained in:
Willy-JL
2023-06-26 01:13:44 +01:00
36 changed files with 1642 additions and 781 deletions

View File

@@ -60,7 +60,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
AvrIspWorkerRW* instance = context;
/* start PWM on &gpio_ext_pa4 */
if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
}
FURI_LOG_D(TAG, "Start");
@@ -122,7 +124,9 @@ static int32_t avr_isp_worker_rw_thread(void* context) {
}
FURI_LOG_D(TAG, "Stop");
if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
}
return 0;
}
@@ -136,7 +140,12 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
instance->chip_arr_ind = avr_isp_chip_arr_size + 1;
/* start PWM on &gpio_ext_pa4 */
bool was_pwm_enabled = false;
if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) {
furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50);
} else {
was_pwm_enabled = true;
}
do {
if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) {
@@ -200,7 +209,9 @@ bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) {
} while(0);
if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) {
furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4);
}
if(instance->callback) {
if(instance->chip_arr_ind > avr_isp_chip_arr_size) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -22,10 +22,12 @@ static void hid_submenu_callback(void* context, uint32_t index) {
Hid* app = context;
if(index == HidSubmenuIndexKeynote) {
app->view_id = HidViewKeynote;
hid_keynote_set_orientation(app->hid_keynote, false);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
} else if(index == HidSubmenuIndexKeynoteVertical) {
app->view_id = HidViewKeynoteVertical;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynoteVertical);
app->view_id = HidViewKeynote;
hid_keynote_set_orientation(app->hid_keynote, true);
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote);
} else if(index == HidSubmenuIndexKeyboard) {
app->view_id = HidViewKeyboard;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard);
@@ -62,7 +64,6 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con
}
}
hid_keynote_set_connected_status(hid->hid_keynote, connected);
hid_keynote_vertical_set_connected_status(hid->hid_keynote_vertical, connected);
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_numpad_set_connected_status(hid->hid_numpad, connected);
hid_media_set_connected_status(hid->hid_media, connected);
@@ -177,15 +178,6 @@ Hid* hid_app_alloc_view(void* context) {
view_dispatcher_add_view(
app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
// Keynote Vertical view
app->hid_keynote_vertical = hid_keynote_vertical_alloc(app);
view_set_previous_callback(
hid_keynote_vertical_get_view(app->hid_keynote_vertical), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher,
HidViewKeynoteVertical,
hid_keynote_vertical_get_view(app->hid_keynote_vertical));
// Keyboard view
app->hid_keyboard = hid_keyboard_alloc(app);
view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
@@ -252,8 +244,6 @@ void hid_free(Hid* app) {
dialog_ex_free(app->dialog);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote);
hid_keynote_free(app->hid_keynote);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynoteVertical);
hid_keynote_vertical_free(app->hid_keynote_vertical);
view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard);
hid_keyboard_free(app->hid_keyboard);
view_dispatcher_remove_view(app->view_dispatcher, HidViewNumpad);

View File

@@ -17,7 +17,6 @@
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include "views/hid_keynote.h"
#include "views/hid_keynote_vertical.h"
#include "views/hid_keyboard.h"
#include "views/hid_numpad.h"
#include "views/hid_media.h"
@@ -43,7 +42,6 @@ struct Hid {
Submenu* device_type_submenu;
DialogEx* dialog;
HidKeynote* hid_keynote;
HidKeynoteVertical* hid_keynote_vertical;
HidKeyboard* hid_keyboard;
HidNumpad* hid_numpad;
HidMedia* hid_media;

View File

@@ -1,7 +1,6 @@
typedef enum {
HidViewSubmenu,
HidViewKeynote,
HidViewKeynoteVertical,
HidViewKeyboard,
HidViewNumpad,
HidViewMedia,

View File

@@ -111,6 +111,91 @@ static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
}
static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidKeynoteModel* model = context;
// Header
canvas_set_font(canvas, FontPrimary);
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote");
} else {
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote");
}
canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
const uint8_t x_2 = 23;
const uint8_t x_1 = 2;
const uint8_t x_3 = 44;
const uint8_t y_1 = 44;
const uint8_t y_2 = 65;
// Up
canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18);
if(model->up_pressed) {
elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop);
canvas_set_color(canvas, ColorBlack);
// Down
canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18);
if(model->down_pressed) {
elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom);
canvas_set_color(canvas, ColorBlack);
// Left
canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18);
if(model->left_pressed) {
elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft);
canvas_set_color(canvas, ColorBlack);
// Right
canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18);
if(model->right_pressed) {
elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight);
canvas_set_color(canvas, ColorBlack);
// Ok
canvas_draw_icon(canvas, 2, 86, &I_Space_60x18);
if(model->ok_pressed) {
elements_slightly_rounded_box(canvas, 5, 88, 55, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9);
elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space");
canvas_set_color(canvas, ColorBlack);
// Back
canvas_draw_icon(canvas, 2, 107, &I_Space_60x18);
if(model->back_pressed) {
elements_slightly_rounded_box(canvas, 5, 109, 55, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back");
}
static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) {
with_view_model(
hid_keynote->view,
@@ -212,3 +297,16 @@ void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {
with_view_model(
hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true);
}
void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) {
furi_assert(hid_keynote);
if(vertical) {
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback);
view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip);
} else {
view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
view_set_orientation(hid_keynote->view, ViewOrientationHorizontal);
}
}

View File

@@ -12,3 +12,5 @@ void hid_keynote_free(HidKeynote* hid_keynote);
View* hid_keynote_get_view(HidKeynote* hid_keynote);
void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected);
void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical);

View File

@@ -1,228 +0,0 @@
#include "hid_keynote_vertical.h"
#include <gui/elements.h>
#include "../hid.h"
#include "hid_icons.h"
#define TAG "HidKeynoteVertical"
struct HidKeynoteVertical {
View* view;
Hid* hid;
};
typedef struct {
bool left_pressed;
bool up_pressed;
bool right_pressed;
bool down_pressed;
bool ok_pressed;
bool back_pressed;
bool connected;
HidTransport transport;
} HidKeynoteVerticalModel;
static void
hid_keynote_vertical_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
canvas_draw_triangle(canvas, x, y, 5, 3, dir);
if(dir == CanvasDirectionBottomToTop) {
canvas_draw_line(canvas, x, y + 6, x, y - 1);
} else if(dir == CanvasDirectionTopToBottom) {
canvas_draw_line(canvas, x, y - 6, x, y + 1);
} else if(dir == CanvasDirectionRightToLeft) {
canvas_draw_line(canvas, x + 6, y, x - 1, y);
} else if(dir == CanvasDirectionLeftToRight) {
canvas_draw_line(canvas, x - 6, y, x + 1, y);
}
}
static void hid_keynote_vertical_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidKeynoteVerticalModel* model = context;
// Header
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
}
}
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas, 24, 14, AlignLeft, AlignTop, "Vertical Up --->");
canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit");
// Up
canvas_draw_icon(canvas, 21, 24, &I_Button_18x18);
if(model->up_pressed) {
elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_vertical_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
canvas_set_color(canvas, ColorBlack);
// Down
canvas_draw_icon(canvas, 21, 45, &I_Button_18x18);
if(model->down_pressed) {
elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_vertical_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
canvas_set_color(canvas, ColorBlack);
// Left
canvas_draw_icon(canvas, 0, 35, &I_Button_18x18);
if(model->left_pressed) {
elements_slightly_rounded_box(canvas, 3, 37, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_vertical_draw_arrow(canvas, 7, 43, CanvasDirectionRightToLeft);
canvas_set_color(canvas, ColorBlack);
// Right
canvas_draw_icon(canvas, 42, 35, &I_Button_18x18);
if(model->right_pressed) {
elements_slightly_rounded_box(canvas, 45, 37, 13, 13);
canvas_set_color(canvas, ColorWhite);
}
hid_keynote_vertical_draw_arrow(canvas, 53, 43, CanvasDirectionLeftToRight);
canvas_set_color(canvas, ColorBlack);
// Ok
canvas_draw_icon(canvas, 63, 25, &I_Space_65x18);
if(model->ok_pressed) {
elements_slightly_rounded_box(canvas, 66, 27, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9);
elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space");
canvas_set_color(canvas, ColorBlack);
// Back
canvas_draw_icon(canvas, 63, 45, &I_Space_65x18);
if(model->back_pressed) {
elements_slightly_rounded_box(canvas, 66, 47, 60, 13);
canvas_set_color(canvas, ColorWhite);
}
canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8);
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
}
static void
hid_keynote_vertical_process(HidKeynoteVertical* hid_keynote_vertical, InputEvent* event) {
with_view_model(
hid_keynote_vertical->view,
HidKeynoteVerticalModel * model,
{
if(event->type == InputTypePress) {
if(event->key == InputKeyUp) {
model->up_pressed = true;
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
} else if(event->key == InputKeyDown) {
model->down_pressed = true;
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
} else if(event->key == InputKeyLeft) {
model->left_pressed = true;
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
} else if(event->key == InputKeyRight) {
model->right_pressed = true;
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
} else if(event->key == InputKeyOk) {
model->ok_pressed = true;
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
} else if(event->key == InputKeyBack) {
model->back_pressed = true;
}
} else if(event->type == InputTypeRelease) {
if(event->key == InputKeyUp) {
model->up_pressed = false;
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_LEFT_ARROW);
} else if(event->key == InputKeyDown) {
model->down_pressed = false;
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_RIGHT_ARROW);
} else if(event->key == InputKeyLeft) {
model->left_pressed = false;
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DOWN_ARROW);
} else if(event->key == InputKeyRight) {
model->right_pressed = false;
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_UP_ARROW);
} else if(event->key == InputKeyOk) {
model->ok_pressed = false;
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_SPACEBAR);
} else if(event->key == InputKeyBack) {
model->back_pressed = false;
}
} else if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) {
hid_hal_keyboard_press(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
hid_hal_keyboard_release(hid_keynote_vertical->hid, HID_KEYBOARD_DELETE);
hid_hal_consumer_key_press(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
hid_hal_consumer_key_release(hid_keynote_vertical->hid, HID_CONSUMER_AC_BACK);
}
}
},
true);
}
static bool hid_keynote_vertical_input_callback(InputEvent* event, void* context) {
furi_assert(context);
HidKeynoteVertical* hid_keynote_vertical = context;
bool consumed = false;
if(event->type == InputTypeLong && event->key == InputKeyBack) {
hid_hal_keyboard_release_all(hid_keynote_vertical->hid);
} else {
hid_keynote_vertical_process(hid_keynote_vertical, event);
consumed = true;
}
return consumed;
}
HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* hid) {
HidKeynoteVertical* hid_keynote_vertical = malloc(sizeof(HidKeynoteVertical));
hid_keynote_vertical->view = view_alloc();
hid_keynote_vertical->hid = hid;
view_set_context(hid_keynote_vertical->view, hid_keynote_vertical);
view_allocate_model(
hid_keynote_vertical->view, ViewModelTypeLocking, sizeof(HidKeynoteVerticalModel));
view_set_draw_callback(hid_keynote_vertical->view, hid_keynote_vertical_draw_callback);
view_set_input_callback(hid_keynote_vertical->view, hid_keynote_vertical_input_callback);
with_view_model(
hid_keynote_vertical->view,
HidKeynoteVerticalModel * model,
{ model->transport = hid->transport; },
true);
return hid_keynote_vertical;
}
void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical) {
furi_assert(hid_keynote_vertical);
view_free(hid_keynote_vertical->view);
free(hid_keynote_vertical);
}
View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical) {
furi_assert(hid_keynote_vertical);
return hid_keynote_vertical->view;
}
void hid_keynote_vertical_set_connected_status(
HidKeynoteVertical* hid_keynote_vertical,
bool connected) {
furi_assert(hid_keynote_vertical);
with_view_model(
hid_keynote_vertical->view,
HidKeynoteVerticalModel * model,
{ model->connected = connected; },
true);
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidKeynoteVertical HidKeynoteVertical;
HidKeynoteVertical* hid_keynote_vertical_alloc(Hid* bt_hid);
void hid_keynote_vertical_free(HidKeynoteVertical* hid_keynote_vertical);
View* hid_keynote_vertical_get_view(HidKeynoteVertical* hid_keynote_vertical);
void hid_keynote_vertical_set_connected_status(
HidKeynoteVertical* hid_keynote_vertical,
bool connected);

View File

@@ -39,26 +39,26 @@ typedef struct {
int8_t y;
} HidNumpadPoint;
#define MARGIN_TOP 0
#define MARGIN_LEFT 24
#define MARGIN_TOP 32
#define MARGIN_LEFT 1
#define KEY_WIDTH 20
#define KEY_HEIGHT 15
#define KEY_PADDING 1
#define ROW_COUNT 5
#define COLUMN_COUNT 4
#define ROW_COUNT 6
#define COLUMN_COUNT 3
const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
{
{.width = 1, .height = 1, .icon = NULL, .key = "NL", .value = HID_KEYPAD_NUMLOCK},
{.width = 1, .height = 1, .icon = NULL, .key = "/", .value = HID_KEYPAD_SLASH},
{.width = 1, .height = 1, .icon = NULL, .key = "*", .value = HID_KEYPAD_ASTERISK},
{.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
// {.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "7", .value = HID_KEYPAD_7},
{.width = 1, .height = 1, .icon = NULL, .key = "8", .value = HID_KEYBOARD_8},
{.width = 1, .height = 1, .icon = NULL, .key = "9", .value = HID_KEYBOARD_9},
{.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
// {.width = 1, .height = 2, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "4", .value = HID_KEYPAD_4},
@@ -69,13 +69,18 @@ const HidNumpadKey hid_numpad_keyset[ROW_COUNT][COLUMN_COUNT] = {
{.width = 1, .height = 1, .icon = NULL, .key = "1", .value = HID_KEYPAD_1},
{.width = 1, .height = 1, .icon = NULL, .key = "2", .value = HID_KEYPAD_2},
{.width = 1, .height = 1, .icon = NULL, .key = "3", .value = HID_KEYPAD_3},
{.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
// {.width = 1, .height = 2, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
},
{
{.width = 2, .height = 1, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
{.width = 0, .height = 0, .icon = NULL, .key = "0", .value = HID_KEYBOARD_0},
{.width = 1, .height = 1, .icon = NULL, .key = ".", .value = HID_KEYPAD_DOT},
},
{
{.width = 1, .height = 1, .icon = NULL, .key = "En", .value = HID_KEYPAD_ENTER},
{.width = 1, .height = 1, .icon = NULL, .key = "-", .value = HID_KEYPAD_MINUS},
{.width = 1, .height = 1, .icon = NULL, .key = "+", .value = HID_KEYPAD_PLUS},
},
};
static void hid_numpad_draw_key(
@@ -128,26 +133,36 @@ static void hid_numpad_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
HidNumpadModel* model = context;
if((!model->connected) && (model->transport == HidTransportBle)) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
// Header
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Numpad");
canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit");
if(model->transport == HidTransportBle) {
if(model->connected) {
canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15);
} else {
canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15);
elements_multiline_text_aligned(
canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection...");
canvas, 7, 60, AlignLeft, AlignBottom, "Waiting for\nConnection...");
}
elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Numpad");
} else {
elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Numpad");
}
canvas_draw_icon(canvas, 3, 18, &I_Pin_back_arrow_10x8);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit");
if(!model->connected && (model->transport == HidTransportBle)) {
return;
}
canvas_set_font(canvas, FontKeyboard);
uint8_t initY = model->y == 0 ? 0 : 1;
uint8_t initY = 0; // = model->y == 0 ? 0 : 1;
if(model->y > 5) {
initY = model->y - 4;
}
// if(model->y > ROW_COUNT) {
// initY = model->y - (ROW_COUNT - 1);
// }
for(uint8_t y = initY; y < ROW_COUNT; y++) {
const HidNumpadKey* numpadKeyRow = hid_numpad_keyset[y];
@@ -269,6 +284,7 @@ HidNumpad* hid_numpad_alloc(Hid* bt_hid) {
hid_numpad->hid = bt_hid;
view_set_context(hid_numpad->view, hid_numpad);
view_allocate_model(hid_numpad->view, ViewModelTypeLocking, sizeof(HidNumpadModel));
view_set_orientation(hid_numpad->view, ViewOrientationVertical);
view_set_draw_callback(hid_numpad->view, hid_numpad_draw_callback);
view_set_input_callback(hid_numpad->view, hid_numpad_input_callback);

View File

@@ -33,7 +33,13 @@ void signal_gen_scene_pwm_on_enter(void* context) {
signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app);
signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY);
if(!furi_hal_pwm_is_running(pwm_ch_id[0])) {
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
} else {
furi_hal_pwm_stop(pwm_ch_id[0]);
furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY);
}
}
bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
@@ -46,8 +52,18 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty);
} else if(event.event == SignalGenPwmEventChannelChange) {
consumed = true;
// Stop previous channel PWM
if(furi_hal_pwm_is_running(app->pwm_ch_prev)) {
furi_hal_pwm_stop(app->pwm_ch_prev);
}
// Start PWM and restart if it was starter already
if(furi_hal_pwm_is_running(app->pwm_ch)) {
furi_hal_pwm_stop(app->pwm_ch);
furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
} else {
furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty);
}
}
}
return consumed;
@@ -56,5 +72,8 @@ bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) {
void signal_gen_scene_pwm_on_exit(void* context) {
SignalGenApp* app = context;
variable_item_list_reset(app->var_item_list);
if(furi_hal_pwm_is_running(app->pwm_ch)) {
furi_hal_pwm_stop(app->pwm_ch);
}
}

View File

@@ -52,10 +52,10 @@ void totp_type_code_worker_execute_automation(
while(i < code_buffer_size && (cb_char = code_buffer[i]) != 0) {
uint8_t char_index = CONVERT_CHAR_TO_DIGIT(cb_char);
if(char_index > 9) {
char_index = cb_char - 0x41 + 10;
char_index = cb_char - 'A' + 10;
}
if(char_index > 35) break;
if(char_index >= sizeof(hid_number_keys)) break;
uint16_t hid_kb_key = hid_number_keys[char_index];
if(char_index > 9) {

View File

@@ -79,7 +79,7 @@ static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT
&Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10,
&SHT30, &GXHT30, &LM75, &HDC1080, &BMP180,
&BMP280, &BME280, &BME680, &MAX31855, &MAX6675,
&SCD30};
&SCD30, &SCD40};
const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
if(index > SENSOR_TYPES_COUNT) return NULL;
@@ -624,11 +624,16 @@ UnitempStatus unitemp_sensor_updateData(Sensor* sensor) {
UNITEMP_DEBUG("Sensor %s update status %d", sensor->name, sensor->status);
}
if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) {
if(sensor->status == UT_SENSORSTATUS_OK) {
if(app->settings.heat_index &&
((sensor->type->datatype & (UT_TEMPERATURE | UT_HUMIDITY)) ==
(UT_TEMPERATURE | UT_HUMIDITY))) {
unitemp_calculate_heat_index(sensor);
}
if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT) {
uintemp_celsiumToFarengate(sensor);
}
if(sensor->status == UT_SENSORSTATUS_OK) {
sensor->temp += sensor->temp_offset / 10.f;
if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) {
unitemp_pascalToMmHg(sensor);

View File

@@ -119,6 +119,7 @@ typedef struct Sensor {
char* name;
//Температура
float temp;
float heat_index;
//Относительная влажность
float hum;
//Атмосферное давление
@@ -334,4 +335,5 @@ const GPIO*
#include "./sensors/MAX31855.h"
#include "./sensors/MAX6675.h"
#include "./sensors/SCD30.h"
#include "./sensors/SCD40.h"
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,60 @@
//
// Created by Avilov Vasily on 10.06.2023.
//
#ifndef FLIPPERZERO_FIRMWARE_ENDIANNESS_H
#define FLIPPERZERO_FIRMWARE_ENDIANNESS_H
inline static void store16(uint8_t* b, uint16_t i) {
memcpy(b, &i, 2);
}
inline static void store32(uint8_t* b, uint32_t i) {
memcpy(b, &i, 4);
}
inline static uint16_t load16(uint8_t* b) {
uint16_t x;
memcpy(&x, b, 2);
return x;
}
inline static uint32_t load32(uint8_t* b) {
uint32_t x;
memcpy(&x, b, 4);
return x;
}
#if BYTE_ORDER == BIG_ENDIAN
#define htobe16(x) (x)
#define htobe32(x) (x)
#define htole16(x) __builtin_bswap16(x)
#define htole32(x) __builtin_bswap32(x)
#define be16toh(x) (x)
#define be32toh(x) (x)
#define le16toh(x) __builtin_bswap16(x)
#define le32toh(x) __builtin_bswap32(x)
#elif BYTE_ORDER == LITTLE_ENDIAN
#define htobe16(x) __builtin_bswap16(x)
#define htobe32(x) __builtin_bswap32(x)
#define htole16(x) (x)
#define htole32(x) (x)
#define be16toh(x) __builtin_bswap16(x)
#define be32toh(x) __builtin_bswap32(x)
#define le16toh(x) (x)
#define le32toh(x) (x)
#else
#error "What kind of system is this?"
#endif
#define load16_le(b) (le16toh(load16(b)))
#define load32_le(b) (le32toh(load32(b)))
#define store16_le(b, i) (store16(b, htole16(i)))
#define store32_le(b, i) (store32(b, htole32(i)))
#define load16_be(b) (be16toh(load16(b)))
#define load32_be(b) (be32toh(load32(b)))
#define store16_be(b, i) (store16(b, htobe16(i)))
#define store32_be(b, i) (store32(b, htobe32(i)))
#endif //FLIPPERZERO_FIRMWARE_ENDIANNESS_H

View File

@@ -21,60 +21,9 @@
#include "SCD30.h"
#include "../interfaces/I2CSensor.h"
#include "../interfaces/endianness.h"
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
inline static uint16_t load16(uint8_t* b) {
uint16_t x;
memcpy(&x, b, 2);
return x;
}
inline static uint32_t load32(uint8_t* b) {
uint32_t x;
memcpy(&x, b, 4);
return x;
}
inline static void store16(uint8_t* b, uint16_t i) {
memcpy(b, &i, 2);
}
inline static void store32(uint8_t* b, uint32_t i) {
memcpy(b, &i, 4);
}
#if BYTE_ORDER == BIG_ENDIAN
#define htobe16(x) (x)
#define htobe32(x) (x)
#define htole16(x) __builtin_bswap16(x)
#define htole32(x) __builtin_bswap32(x)
#define be16toh(x) (x)
#define be32toh(x) (x)
#define le16toh(x) __builtin_bswap16(x)
#define le32toh(x) __builtin_bswap32(x)
#elif BYTE_ORDER == LITTLE_ENDIAN
#define htobe16(x) __builtin_bswap16(x)
#define htobe32(x) __builtin_bswap32(x)
#define htole16(x) (x)
#define htole32(x) (x)
#define be16toh(x) __builtin_bswap16(x)
#define be32toh(x) __builtin_bswap32(x)
#define le16toh(x) (x)
#define le32toh(x) (x)
#else
#error "What kind of system is this?"
#endif
#define load16_le(b) (le16toh(load16(b)))
#define load32_le(b) (le32toh(load32(b)))
#define store16_le(b, i) (store16(b, htole16(i)))
#define store32_le(b, i) (store32(b, htole32(i)))
#define load16_be(b) (be16toh(load16(b)))
#define load32_be(b) (be32toh(load32(b)))
#define store16_be(b, i) (store16(b, htobe16(i)))
#define store32_be(b, i) (store32(b, htobe32(i)))
typedef union {
uint16_t array16[2];
uint8_t array8[4];

View File

@@ -0,0 +1,291 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
Contributed by divinebird (https://github.com/divinebird)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
#include "SCD30.h"
#include "../interfaces/I2CSensor.h"
#include "../interfaces/endianness.h"
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
bool unitemp_SCD40_init(Sensor* sensor);
bool unitemp_SCD40_deinit(Sensor* sensor);
UnitempStatus unitemp_SCD40_update(Sensor* sensor);
bool unitemp_SCD40_free(Sensor* sensor);
const SensorType SCD40 = {
.typename = "SCD40",
.interface = &I2C,
.datatype = UT_DATA_TYPE_TEMP_HUM_CO2,
.pollingInterval = 5000,
.allocator = unitemp_SCD40_alloc,
.mem_releaser = unitemp_SCD40_free,
.initializer = unitemp_SCD40_init,
.deinitializer = unitemp_SCD40_deinit,
.updater = unitemp_SCD40_update};
#define SCD40_ID 0x62
#define COMMAND_START_PERIODIC_MEASUREMENT 0X21B1
#define COMMAND_READ_MEASUREMENT 0XEC05
#define COMMAND_STOP_PERIODIC_MEASUREMENT 0X3F86
#define COMMAND_PERSIST_SETTINGS 0X3615
#define COMMAND_GET_SERIAL_NUMBER 0X3682
#define COMMAND_PERFORM_SELF_TEST 0X3639
#define COMMAND_PERFORM_FACTORY_RESET 0X3632
#define COMMAND_REINIT 0X3646
#define COMMAND_SET_TEMPERATURE_OFFSET 0X241D
#define COMMAND_GET_TEMPERATURE_OFFSET 0X2318
#define COMMAND_SET_SENSOR_ALTITUDE 0X2427
#define COMMAND_GET_SENSOR_ALTITUDE 0X2322
#define COMMAND_SET_AMBIENT_PRESSURE 0XE000
#define COMMAND_PERFORM_FORCED_RECALIBRATION 0X362F
#define COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2416
#define COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2313
static bool readMeasurement(Sensor* sensor) __attribute__((unused));
static void reset(Sensor* sensor) __attribute__((unused));
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused));
static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused));
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused));
static float getTemperatureOffset(Sensor* sensor) __attribute__((unused));
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused));
static bool beginMeasuring(Sensor* sensor) __attribute__((unused));
static bool stopMeasurement(Sensor* sensor) __attribute__((unused));
bool unitemp_SCD40_alloc(Sensor* sensor, char* args) {
UNUSED(args);
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
i2c_sensor->minI2CAdr = SCD40_ID << 1;
i2c_sensor->maxI2CAdr = SCD40_ID << 1;
return true;
}
bool unitemp_SCD40_free(Sensor* sensor) {
//Нечего высвобождать, так как ничего не было выделено
UNUSED(sensor);
return true;
}
bool unitemp_SCD40_init(Sensor* sensor) {
return beginMeasuring(sensor);
}
bool unitemp_SCD40_deinit(Sensor* sensor) {
return stopMeasurement(sensor);
}
UnitempStatus unitemp_SCD40_update(Sensor* sensor) {
readMeasurement(sensor);
return UT_SENSORSTATUS_OK;
}
#define CRC8_POLYNOMIAL 0x31
#define CRC8_INIT 0xFF
static uint8_t computeCRC8(uint8_t* message, uint8_t len) {
uint8_t crc = CRC8_INIT; // Init with 0xFF
for(uint8_t x = 0; x < len; x++) {
crc ^= message[x]; // XOR-in the next input byte
for(uint8_t i = 0; i < 8; i++) {
if((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ CRC8_POLYNOMIAL);
else
crc <<= 1;
}
}
return crc; // No output reflection
}
// Sends a command along with arguments and CRC
static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) {
static const uint8_t cmdSize = 5;
uint8_t bytes[cmdSize];
uint8_t* pointer = bytes;
store16_be(pointer, command);
pointer += 2;
uint8_t* argPos = pointer;
store16_be(pointer, arguments);
pointer += 2;
*pointer = computeCRC8(argPos, pointer - argPos);
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
}
// Sends just a command, no arguments, no CRC
static bool sendCommand(Sensor* sensor, uint16_t command) {
static const uint8_t cmdSize = 2;
uint8_t bytes[cmdSize];
store16_be(bytes, command);
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
}
static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) {
static const uint8_t regSize = 2;
if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK
furi_delay_ms(3);
uint8_t bytes[regSize];
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0;
return load16_be(bytes);
}
static bool loadWord(uint8_t* buff, uint16_t* val) {
uint16_t tmp = load16_be(buff);
uint8_t expectedCRC = computeCRC8(buff, 2);
if(buff[2] != expectedCRC) return false;
*val = tmp;
return true;
}
static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) {
static const uint8_t respSize = 3;
if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK
furi_delay_ms(3);
uint8_t bytes[respSize];
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false;
return loadWord(bytes, val);
}
// Get 18 bytes from SCD30
// Updates global variables with floats
// Returns true if success
static bool readMeasurement(Sensor* sensor) {
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
return false; // Sensor did not ACK
}
furi_delay_ms(3);
static const uint8_t respSize = 9;
uint8_t buff[respSize];
uint8_t* bytes = buff;
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
FURI_LOG_E(APP_NAME, "Error while read measures");
return false;
}
uint16_t tmpValue;
bool error = false;
if(loadWord(bytes, &tmpValue)) {
sensor->co2 = tmpValue;
} else {
FURI_LOG_E(APP_NAME, "Error while parsing CO2");
error = true;
}
bytes += 3;
if(loadWord(bytes, &tmpValue)) {
sensor->temp = (float)tmpValue * 175.0f / 65535.0f - 45.0f;
} else {
FURI_LOG_E(APP_NAME, "Error while parsing temp");
error = true;
}
bytes += 3;
if(loadWord(bytes, &tmpValue)) {
sensor->hum = (float)tmpValue * 100.0f / 65535.0f;
} else {
FURI_LOG_E(APP_NAME, "Error while parsing humidity");
error = true;
}
return !error;
}
static void reset(Sensor* sensor) {
sendCommand(sensor, COMMAND_REINIT);
}
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) {
return sendCommandWithCRC(
sensor, COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enable); // Activate continuous ASC
}
// Get the current ASC setting
static bool getAutoSelfCalibration(Sensor* sensor) {
return 1 == readRegister(sensor, COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED);
}
// Unfinished
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) {
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
return false; // Sensor did not ACK
}
static const uint8_t respSize = 9;
uint8_t buff[respSize];
uint8_t* bytes = buff;
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
FURI_LOG_E(APP_NAME, "Error while read measures");
return false;
}
*val = 0;
return true;
}
static bool beginMeasuring(Sensor* sensor) {
return sendCommand(sensor, COMMAND_START_PERIODIC_MEASUREMENT);
}
// Stop continuous measurement
static bool stopMeasurement(Sensor* sensor) {
return sendCommand(sensor, COMMAND_READ_MEASUREMENT);
}
static float getTemperatureOffset(Sensor* sensor) {
uint16_t curOffset;
if(!getSettingValue(sensor, COMMAND_GET_TEMPERATURE_OFFSET, &curOffset)) return 0.0;
return (float)curOffset * 175.0f / 65536.0f;
}
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) {
uint16_t newOffset = tempOffset * 65536.0 / 175.0 + 0.5f;
return sendCommandWithCRC(
sensor, COMMAND_SET_TEMPERATURE_OFFSET, newOffset); // Activate continuous ASC
}

View File

@@ -0,0 +1,59 @@
/*
Unitemp - Universal temperature reader
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
Contributed by divinebird (https://github.com/divinebird)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UNITEMP_SCD40
#define UNITEMP_SCD40
#include "../unitemp.h"
#include "../Sensors.h"
extern const SensorType SCD40;
/**
* @brief Выделение памяти и установка начальных значений датчика SCD40
* @param sensor Указатель на создаваемый датчик
* @return Истина при успехе
*/
bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
/**
* @brief Инициализации датчика SCD40
* @param sensor Указатель на датчик
* @return Истина если инициализация упспешная
*/
bool unitemp_SCD40_init(Sensor* sensor);
/**
* @brief Деинициализация датчика
* @param sensor Указатель на датчик
*/
bool unitemp_SCD40_deinit(Sensor* sensor);
/**
* @brief Обновление значений из датчика
* @param sensor Указатель на датчик
* @return Статус опроса датчика
*/
UnitempStatus unitemp_SCD40_update(Sensor* sensor);
/**
* @brief Высвободить память датчика
* @param sensor Указатель на датчик
*/
bool unitemp_SCD40_free(Sensor* sensor);
#endif

View File

@@ -28,8 +28,31 @@ Unitemp* app;
void uintemp_celsiumToFarengate(Sensor* sensor) {
sensor->temp = sensor->temp * (9.0 / 5.0) + 32;
sensor->heat_index = sensor->heat_index * (9.0 / 5.0) + 32;
}
static float heat_index_consts[9] = {
-42.379f,
2.04901523f,
10.14333127f,
-0.22475541f,
-0.00683783f,
-0.05481717f,
0.00122874f,
0.00085282f,
-0.00000199f};
void unitemp_calculate_heat_index(Sensor* sensor) {
// temp should be in Celsius, heat index will be in Celsius
float temp = sensor->temp * (9.0 / 5.0) + 32.0f;
float hum = sensor->hum;
sensor->heat_index =
(heat_index_consts[0] + heat_index_consts[1] * temp + heat_index_consts[2] * hum +
heat_index_consts[3] * temp * hum + heat_index_consts[4] * temp * temp +
heat_index_consts[5] * hum * hum + heat_index_consts[6] * temp * temp * hum +
heat_index_consts[7] * temp * hum * hum + heat_index_consts[8] * temp * temp * hum * hum -
32.0f) *
(5.0 / 9.0);
}
void unitemp_pascalToMmHg(Sensor* sensor) {
sensor->pressure = sensor->pressure * 0.007500638;
}
@@ -71,6 +94,7 @@ bool unitemp_saveSettings(void) {
app->file_stream, "INFINITY_BACKLIGHT %d\n", app->settings.infinityBacklight);
stream_write_format(app->file_stream, "TEMP_UNIT %d\n", app->settings.temp_unit);
stream_write_format(app->file_stream, "PRESSURE_UNIT %d\n", app->settings.pressure_unit);
stream_write_format(app->file_stream, "HEAT_INDEX %d\n", app->settings.heat_index);
//Закрытие потока и освобождение памяти
file_stream_close(app->file_stream);
@@ -166,6 +190,11 @@ bool unitemp_loadSettings(void) {
int p = 0;
sscanf(((char*)(file_buf + line_end)), "\nPRESSURE_UNIT %d", &p);
app->settings.pressure_unit = p;
} else if(!strcmp(buff, "HEAT_INDEX")) {
//Чтение значения параметра
int p = 0;
sscanf(((char*)(file_buf + line_end)), "\nHEAT_INDEX %d", &p);
app->settings.heat_index = p;
} else {
FURI_LOG_W(APP_NAME, "Unknown settings parameter: %s", buff);
}
@@ -203,6 +232,7 @@ static bool unitemp_alloc(void) {
app->settings.infinityBacklight = true; //Подсветка горит всегда
app->settings.temp_unit = UT_TEMP_CELSIUS; //Единица измерения температуры - градусы Цельсия
app->settings.pressure_unit = UT_PRESSURE_MM_HG; //Единица измерения давления - мм рт. ст.
app->settings.heat_index = false;
app->gui = furi_record_open(RECORD_GUI);
//Диспетчер окон

View File

@@ -40,7 +40,7 @@
//Имя приложения
#define APP_NAME "Unitemp"
//Версия приложения
#define UNITEMP_APP_VER "1.3"
#define UNITEMP_APP_VER "1.4"
//Путь хранения файлов плагина
#define APP_PATH_FOLDER "/ext/unitemp"
//Имя файла с настройками
@@ -80,6 +80,8 @@ typedef struct {
tempMeasureUnit temp_unit;
//Единица измерения давления
pressureMeasureUnit pressure_unit;
// Do calculate and show heat index
bool heat_index;
//Последнее состояние OTG
bool lastOTGState;
} UnitempSettings;
@@ -111,6 +113,13 @@ typedef struct {
/* Объявление прототипов функций */
/**
* @brief Calculates the heat index in Celsius from the temperature and humidity and stores it in the sensor heat_index field
*
* @param sensor The sensor struct, with temperature in Celcius and humidity in percent
*/
void unitemp_calculate_heat_index(Sensor* sensor);
/**
* @brief Перевод значения температуры датчика из Цельсия в Фаренгейты
*

View File

@@ -113,6 +113,33 @@ static void _draw_humidity(Canvas* canvas, Sensor* sensor, const uint8_t pos[2])
canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 4, pos[1] + 10 + 7, "%");
}
static void _draw_heat_index(Canvas* canvas, Sensor* sensor, const uint8_t pos[2]) {
canvas_draw_rframe(canvas, pos[0], pos[1], 54, 20, 3);
canvas_draw_rframe(canvas, pos[0], pos[1], 54, 19, 3);
canvas_draw_icon(canvas, pos[0] + 3, pos[1] + 3, &I_heat_index_11x14);
int16_t heat_index_int = sensor->heat_index;
int8_t heat_index_dec = abs((int16_t)(sensor->heat_index * 10) % 10);
snprintf(app->buff, BUFF_SIZE, "%d", heat_index_int);
canvas_set_font(canvas, FontBigNumbers);
canvas_draw_str_aligned(
canvas,
pos[0] + 27 + ((sensor->heat_index <= -10 || sensor->heat_index > 99) ? 5 : 0),
pos[1] + 10,
AlignCenter,
AlignCenter,
app->buff);
if(heat_index_int <= 99) {
uint8_t int_len = canvas_string_width(canvas, app->buff);
snprintf(app->buff, BUFF_SIZE, ".%d", heat_index_dec);
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, pos[0] + 27 + int_len / 2 + 2, pos[1] + 10 + 7, app->buff);
}
}
static void _draw_pressure(Canvas* canvas, Sensor* sensor) {
const uint8_t x = 29, y = 39;
//Рисование рамки
@@ -320,12 +347,23 @@ static void _draw_carousel_values(Canvas* canvas) {
ColorWhite);
break;
case UT_DATA_TYPE_TEMP_HUM:
if(!app->settings.heat_index) {
_draw_temperature(
canvas,
unitemp_sensor_getActive(generalview_sensor_index),
temp_positions[1][0],
temp_positions[1][1],
ColorWhite);
} else {
_draw_temperature(
canvas,
unitemp_sensor_getActive(generalview_sensor_index),
temp_positions[2][0],
temp_positions[2][1],
ColorWhite);
_draw_heat_index(
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]);
}
_draw_humidity(
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[0]);
break;
@@ -446,8 +484,8 @@ static void _draw_carousel_info(Canvas* canvas) {
->currentI2CAdr >>
1);
canvas_draw_str(canvas, 57, 35, app->buff);
canvas_draw_str(canvas, 54, 46, "15 (C0)");
canvas_draw_str(canvas, 54, 58, "16 (C1)");
canvas_draw_str(canvas, 54, 46, "15 (C1)");
canvas_draw_str(canvas, 54, 58, "16 (C0)");
}
}
static void _draw_view_sensorsCarousel(Canvas* canvas) {

View File

@@ -26,6 +26,7 @@ static VariableItemList* variable_item_list;
static const char states[2][9] = {"Auto", "Infinity"};
static const char temp_units[UT_TEMP_COUNT][3] = {"*C", "*F"};
static const char pressure_units[UT_PRESSURE_COUNT][6] = {"mm Hg", "in Hg", "kPa", "hPA"};
static const char heat_index_bool[2][4] = {"OFF", "ON"};
//Элемент списка - бесконечная подсветка
VariableItem* infinity_backlight_item;
@@ -33,6 +34,8 @@ VariableItem* infinity_backlight_item;
VariableItem* temperature_unit_item;
//Единица измерения давления
VariableItem* pressure_unit_item;
VariableItem* heat_index_item;
#define VIEW_ID UnitempViewSettings
/**
@@ -57,6 +60,7 @@ static uint32_t _exit_callback(void* context) {
(bool)variable_item_get_current_value_index(infinity_backlight_item);
app->settings.temp_unit = variable_item_get_current_value_index(temperature_unit_item);
app->settings.pressure_unit = variable_item_get_current_value_index(pressure_unit_item);
app->settings.heat_index = variable_item_get_current_value_index(heat_index_item);
unitemp_saveSettings();
unitemp_loadSettings();
@@ -90,6 +94,11 @@ static void _setting_change_callback(VariableItem* item) {
pressure_unit_item,
pressure_units[variable_item_get_current_value_index(pressure_unit_item)]);
}
if(item == heat_index_item) {
variable_item_set_current_value_text(
heat_index_item,
heat_index_bool[variable_item_get_current_value_index(heat_index_item)]);
}
}
/**
@@ -106,6 +115,8 @@ void unitemp_Settings_alloc(void) {
variable_item_list_add(variable_item_list, "Temp. unit", 2, _setting_change_callback, app);
pressure_unit_item = variable_item_list_add(
variable_item_list, "Press. unit", UT_PRESSURE_COUNT, _setting_change_callback, app);
heat_index_item = variable_item_list_add(
variable_item_list, "Calc. heat index", 2, _setting_change_callback, app);
//Добавление колбека на нажатие средней кнопки
variable_item_list_set_enter_callback(variable_item_list, _enter_callback, app);
@@ -139,6 +150,10 @@ void unitemp_Settings_switch(void) {
pressure_unit_item,
pressure_units[variable_item_get_current_value_index(pressure_unit_item)]);
variable_item_set_current_value_index(heat_index_item, (uint8_t)app->settings.heat_index);
variable_item_set_current_value_text(
heat_index_item, heat_index_bool[variable_item_get_current_value_index(heat_index_item)]);
view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_ID);
}

View File

@@ -160,6 +160,10 @@ static void nfc_cli_apdu(Cli* cli, FuriString* args) {
break;
}
resp_size = (tx_rx.rx_bits / 8) * 2;
if(!resp_size) {
printf("No response\r\n");
break;
}
resp_buffer = malloc(resp_size);
uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size);
resp_buffer[resp_size] = 0;

View File

@@ -8,6 +8,83 @@ void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType typ
}
}
void nfc_scene_slix_build_string(
FuriString* temp_str,
NfcVData* nfcv_data,
SlixTypeFeatures features,
const char* type) {
furi_string_cat_printf(temp_str, "Type: %s\n", type);
furi_string_cat_printf(temp_str, "Keys:\n");
if(features & SlixFeatureRead) {
furi_string_cat_printf(
temp_str,
" Read %08llX%s\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4),
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyRead) ? "" : " (unset)");
}
if(features & SlixFeatureWrite) {
furi_string_cat_printf(
temp_str,
" Write %08llX%s\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4),
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyWrite) ? "" : " (unset)");
}
if(features & SlixFeaturePrivacy) {
furi_string_cat_printf(
temp_str,
" Privacy %08llX%s\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4),
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyPrivacy) ? "" : " (unset)");
furi_string_cat_printf(
temp_str,
" Privacy mode %s\n",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ENABLED" : "DISABLED");
}
if(features & SlixFeatureDestroy) {
furi_string_cat_printf(
temp_str,
" Destroy %08llX%s\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4),
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyDestroy) ? "" : " (unset)");
}
if(features & SlixFeatureEas) {
furi_string_cat_printf(
temp_str,
" EAS %08llX%s\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4),
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsHasKeyEas) ? "" : " (unset)");
}
if(features & SlixFeatureSignature) {
furi_string_cat_printf(
temp_str,
"Signature %08llX...\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.signature, 4));
}
furi_string_cat_printf(
temp_str,
"DSFID: %02X %s\n",
nfcv_data->dsfid,
(nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : "");
furi_string_cat_printf(
temp_str,
"AFI: %02X %s\n",
nfcv_data->afi,
(nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : "");
furi_string_cat_printf(
temp_str,
"EAS: %s\n",
(nfcv_data->security_status[0] & NfcVLockBitEas) ? "locked" : "not locked");
if(features & SlixFeatureProtection) {
furi_string_cat_printf(
temp_str,
"PPL: %s\n",
(nfcv_data->security_status[0] & NfcVLockBitPpl) ? "locked" : "not locked");
furi_string_cat_printf(temp_str, "Prot.ptr %02X\n", nfcv_data->sub_data.slix.pp_pointer);
furi_string_cat_printf(temp_str, "Prot.con %02X\n", nfcv_data->sub_data.slix.pp_condition);
}
}
void nfc_scene_nfc_data_info_on_enter(void* context) {
Nfc* nfc = context;
Widget* widget = nfc->widget;
@@ -80,95 +157,25 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
}
furi_string_cat_printf(temp_str, "\n");
furi_string_cat_printf(
temp_str,
"DSFID: %02X %s\n",
nfcv_data->dsfid,
(nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : "");
furi_string_cat_printf(
temp_str,
"AFI: %02X %s\n",
nfcv_data->afi,
(nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : "");
furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref);
furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
furi_string_cat_printf(temp_str, "IC Ref: %d\n", nfcv_data->ic_ref);
furi_string_cat_printf(temp_str, "Blocks: %d\n", nfcv_data->block_num);
furi_string_cat_printf(temp_str, "Blocksize: %d\n", nfcv_data->block_size);
switch(dev_data->nfcv_data.sub_type) {
case NfcVTypePlain:
furi_string_cat_printf(temp_str, "Type: Plain\n");
break;
case NfcVTypeSlix:
furi_string_cat_printf(temp_str, "Type: SLIX\n");
furi_string_cat_printf(temp_str, "Keys:\n");
furi_string_cat_printf(
temp_str,
" EAS %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix, "SLIX");
break;
case NfcVTypeSlixS:
furi_string_cat_printf(temp_str, "Type: SLIX-S\n");
furi_string_cat_printf(temp_str, "Keys:\n");
furi_string_cat_printf(
temp_str,
" Read %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4));
furi_string_cat_printf(
temp_str,
" Write %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4));
furi_string_cat_printf(
temp_str,
" Privacy %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
furi_string_cat_printf(
temp_str,
" Destroy %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
furi_string_cat_printf(
temp_str,
" EAS %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixS, "SLIX-S");
break;
case NfcVTypeSlixL:
furi_string_cat_printf(temp_str, "Type: SLIX-L\n");
furi_string_cat_printf(temp_str, "Keys:\n");
furi_string_cat_printf(
temp_str,
" Privacy %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
furi_string_cat_printf(
temp_str,
" Destroy %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
furi_string_cat_printf(
temp_str,
" EAS %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlixL, "SLIX-L");
break;
case NfcVTypeSlix2:
furi_string_cat_printf(temp_str, "Type: SLIX2\n");
furi_string_cat_printf(temp_str, "Keys:\n");
furi_string_cat_printf(
temp_str,
" Read %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4));
furi_string_cat_printf(
temp_str,
" Write %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4));
furi_string_cat_printf(
temp_str,
" Privacy %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4));
furi_string_cat_printf(
temp_str,
" Destroy %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4));
furi_string_cat_printf(
temp_str,
" EAS %08llX\n",
nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4));
nfc_scene_slix_build_string(temp_str, nfcv_data, SlixFeatureSlix2, "SLIX2");
break;
default:
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");

View File

@@ -16,7 +16,6 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) {
Nfc* nfc = context;
NfcDeviceData* dev_data = &nfc->dev->dev_data;
FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data;
// Setup view
Widget* widget = nfc->widget;
widget_add_button_element(
@@ -46,13 +45,12 @@ void nfc_scene_nfcv_read_success_on_enter(void* context) {
furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n");
break;
}
furi_string_cat_printf(temp_str, "UID:");
furi_string_cat_printf(temp_str, "UID:\n");
for(size_t i = 0; i < nfc_data->uid_len; i++) {
furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
}
furi_string_cat_printf(temp_str, "\n");
furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num);
furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size);
furi_string_cat_printf(temp_str, "(see More->Info for details)\n");
widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str));
furi_string_free(temp_str);

View File

@@ -1355,6 +1355,7 @@ Function,+,furi_hal_power_sleep,void,
Function,+,furi_hal_power_sleep_available,_Bool,
Function,+,furi_hal_power_suppress_charge_enter,void,
Function,+,furi_hal_power_suppress_charge_exit,void,
Function,+,furi_hal_pwm_is_running,_Bool,FuriHalPwmOutputId
Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t"
Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t"
Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId
1 entry status name type params
1355 Function + furi_hal_power_sleep_available _Bool
1356 Function + furi_hal_power_suppress_charge_enter void
1357 Function + furi_hal_power_suppress_charge_exit void
1358 Function + furi_hal_pwm_is_running _Bool FuriHalPwmOutputId
1359 Function + furi_hal_pwm_set_params void FuriHalPwmOutputId, uint32_t, uint8_t
1360 Function + furi_hal_pwm_start void FuriHalPwmOutputId, uint32_t, uint8_t
1361 Function + furi_hal_pwm_stop void FuriHalPwmOutputId

View File

@@ -82,6 +82,15 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel) {
}
}
bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel) {
if(channel == FuriHalPwmOutputIdTim1PA7) {
return furi_hal_bus_is_enabled(FuriHalBusTIM1);
} else if(channel == FuriHalPwmOutputIdLptim2PA4) {
return furi_hal_bus_is_enabled(FuriHalBusLPTIM2);
}
return false;
}
void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty) {
furi_assert(freq > 0);
uint32_t freq_div = 64000000LU / freq;

View File

@@ -9,6 +9,7 @@ extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
typedef enum {
FuriHalPwmOutputIdTim1PA7,
@@ -37,6 +38,13 @@ void furi_hal_pwm_stop(FuriHalPwmOutputId channel);
*/
void furi_hal_pwm_set_params(FuriHalPwmOutputId channel, uint32_t freq, uint8_t duty);
/** Is PWM channel running?
*
* @param[in] channel PWM channel (FuriHalPwmOutputId)
* @return bool - true if running
*/
bool furi_hal_pwm_is_running(FuriHalPwmOutputId channel);
#ifdef __cplusplus
}
#endif

View File

@@ -51,8 +51,16 @@ struct DigitalSignalInternals {
#define T_TIM 1562 /* 15.625 ns *100 */
#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */
/* end marker in DMA ringbuffer, will get written into timer register at the end */
#define SEQ_TIMER_MAX 0xFFFFFFFF
/* time to wait in loops before returning */
#define SEQ_LOCK_WAIT_MS 10UL
#define SEQ_LOCK_WAIT_TICKS (SEQ_LOCK_WAIT_MS * 1000 * 64)
/* maximum entry count of the sequence dma ring buffer */
#define SEQUENCE_DMA_RINGBUFFER_SIZE 32
#define RINGBUFFER_SIZE 128
/* maximum number of DigitalSignals in a sequence */
#define SEQUENCE_SIGNALS_SIZE 32
/*
@@ -214,12 +222,12 @@ void digital_signal_prepare_arr(DigitalSignal* signal) {
for(size_t pos = 0; pos < signal->edge_cnt; pos++) {
uint32_t pulse_duration = signal->edge_timings[pos] + internals->reload_reg_remainder;
if(pulse_duration < 10 || pulse_duration > 10000000) {
/*FURI_LOG_D(
FURI_LOG_D(
TAG,
"[prepare] pulse_duration out of range: %lu = %lu * %llu",
pulse_duration,
signal->edge_timings[pos],
internals->factor);*/
internals->factor);
pulse_duration = 100;
}
uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM;
@@ -243,20 +251,16 @@ static void digital_signal_stop_timer() {
LL_TIM_DisableUpdateEvent(TIM2);
LL_TIM_DisableDMAReq_UPDATE(TIM2);
if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) {
furi_hal_bus_disable(FuriHalBusTIM2);
}
}
static void digital_signal_setup_timer() {
if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) {
furi_hal_bus_enable(FuriHalBusTIM2);
}
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
LL_TIM_SetPrescaler(TIM2, 0);
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
LL_TIM_SetAutoReload(TIM2, SEQ_TIMER_MAX);
LL_TIM_SetCounter(TIM2, 0);
}
@@ -339,7 +343,7 @@ DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
sequence->bake = false;
sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer));
sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE;
sequence->dma_buffer->size = RINGBUFFER_SIZE;
sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t));
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
@@ -458,42 +462,26 @@ static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) {
return ret;
}
static void digital_sequence_update_pos(DigitalSequence* sequence) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
}
static const uint32_t wait_ms = 10;
static const uint32_t wait_ticks = wait_ms * 1000 * 64;
static void digital_sequence_finish(DigitalSequence* sequence) {
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
do {
uint32_t last_pos = dma_buffer->read_pos;
digital_sequence_update_pos(sequence);
/* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */
if(dma_buffer->read_pos == end_pos) {
/* we are finished, when the DMA transferred the SEQ_TIMER_MAX marker */
if(TIM2->ARR == SEQ_TIMER_MAX) {
break;
}
if(last_pos != dma_buffer->read_pos) { //-V547
prev_timer = DWT->CYCCNT;
}
if(DWT->CYCCNT - prev_timer > wait_ticks) {
/*FURI_LOG_D(
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
dma_buffer->read_pos =
RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
wait_ms,
SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);*/
dma_buffer->write_pos);
break;
}
} while(1);
@@ -508,34 +496,42 @@ static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t len
if(dma_buffer->dma_active) {
uint32_t prev_timer = DWT->CYCCNT;
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
do {
uint32_t last_pos = dma_buffer->read_pos;
digital_sequence_update_pos(sequence);
dma_buffer->read_pos = RINGBUFFER_SIZE - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
if(dma_buffer->read_pos != end_pos) {
uint32_t free =
(RINGBUFFER_SIZE + dma_buffer->read_pos - dma_buffer->write_pos) % RINGBUFFER_SIZE;
if(free > 2) {
break;
}
if(last_pos != dma_buffer->read_pos) { //-V547
prev_timer = DWT->CYCCNT;
}
if(DWT->CYCCNT - prev_timer > wait_ticks) {
/*FURI_LOG_D(
if(DWT->CYCCNT - prev_timer > SEQ_LOCK_WAIT_TICKS) {
FURI_LOG_D(
TAG,
"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)",
wait_ms,
SEQ_LOCK_WAIT_MS,
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);*/
dma_buffer->write_pos);
break;
}
if(TIM2->ARR == SEQ_TIMER_MAX) {
FURI_LOG_D(
TAG,
"[SEQ] buffer underrun in queue (ARR 0x%08lx, read %lu, write %lu)",
TIM2->ARR,
dma_buffer->read_pos,
dma_buffer->write_pos);
break;
}
} while(1);
}
dma_buffer->buffer[dma_buffer->write_pos] = length;
dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF;
dma_buffer->write_pos++;
dma_buffer->write_pos %= RINGBUFFER_SIZE;
dma_buffer->buffer[dma_buffer->write_pos] = SEQ_TIMER_MAX;
}
bool digital_sequence_send(DigitalSequence* sequence) {
@@ -557,64 +553,65 @@ bool digital_sequence_send(DigitalSequence* sequence) {
return true;
}
int32_t remainder = 0;
bool traded_first = false;
if(!sequence->sequence_used) {
return false;
}
FURI_CRITICAL_ENTER();
int32_t remainder = 0;
uint32_t trade_for_next = 0;
uint32_t seq_pos_next = 1;
dma_buffer->dma_active = false;
dma_buffer->buffer[0] = 0xFFFFFFFF;
dma_buffer->buffer[0] = SEQ_TIMER_MAX;
dma_buffer->read_pos = 0;
dma_buffer->write_pos = 0;
for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) {
uint8_t signal_index = sequence->sequence[seq_pos];
DigitalSignal* sig = sequence->signals[signal_index];
bool last_signal = ((seq_pos + 1) == sequence->sequence_used);
/* all signals are prepared and we can re-use the GPIO buffer from the fist signal */
if(seq_pos == 0) {
/* already prepare the current signal pointer */
DigitalSignal* sig = sequence->signals[sequence->sequence[0]];
DigitalSignal* sig_next = NULL;
/* re-use the GPIO buffer from the first signal */
sequence->gpio_buff = sig->internals->gpio_buff;
FURI_CRITICAL_ENTER();
while(sig) {
bool last_signal = (seq_pos_next >= sequence->sequence_used);
if(!last_signal) {
sig_next = sequence->signals[sequence->sequence[seq_pos_next++]];
}
for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) {
if(traded_first) {
traded_first = false;
continue;
}
uint32_t pulse_length = 0;
bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries);
bool last_pulse = ((pulse_pos + 1) >= sig->internals->reload_reg_entries);
uint32_t pulse_length = sig->reload_reg_buff[pulse_pos] + trade_for_next;
pulse_length = sig->reload_reg_buff[pulse_pos];
trade_for_next = 0;
/* when we are too late more than half a tick, make the first edge temporarily longer */
if(remainder >= T_TIM_DIV2) {
remainder -= T_TIM;
pulse_length += 1;
}
remainder += sig->internals->reload_reg_remainder;
/* last pulse in that signal and have a next signal? */
if(last_pulse) {
if((seq_pos + 1) < sequence->sequence_used) {
DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]];
/* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */
/* beware, we do not want the level after the last edge, but the last level before that edge */
/* last pulse in current signal and have a next signal? */
if(last_pulse && sig_next) {
/* when a signal ends with the same level as the next signal begins, let the next signal generate the whole pulse.
beware, we do not want the level after the last edge, but the last level before that edge */
bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
/* take from the next, add it to the current if they have the same level */
/* if they have the same level, pass the duration to the next pulse(s) */
if(end_level == sig_next->start_level) {
pulse_length += sig_next->reload_reg_buff[0];
traded_first = true;
}
trade_for_next = pulse_length;
}
}
/* if it was decided, that the next signal's first pulse shall also handle our "length", then do not queue here */
if(!trade_for_next) {
digital_sequence_queue_pulse(sequence, pulse_length);
if(!dma_buffer->dma_active) {
/* start transmission when buffer was filled enough */
bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4);
bool start_send = sequence->dma_buffer->write_pos >= (RINGBUFFER_SIZE - 2);
/* or it was the last pulse */
if(last_pulse && last_signal) {
@@ -622,7 +619,7 @@ bool digital_sequence_send(DigitalSequence* sequence) {
}
/* start transmission */
if(start_send && !dma_buffer->dma_active) {
if(start_send) {
digital_sequence_setup_dma(sequence);
digital_signal_setup_timer();
@@ -637,10 +634,16 @@ bool digital_sequence_send(DigitalSequence* sequence) {
}
}
}
}
remainder += sig->internals->reload_reg_remainder;
sig = sig_next;
sig_next = NULL;
}
/* wait until last dma transaction was finished */
digital_sequence_finish(sequence);
FURI_CRITICAL_EXIT();
digital_sequence_finish(sequence);
return true;
}

View File

@@ -660,178 +660,167 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) {
return parsed;
}
static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) {
static bool nfc_device_save_slix_data(
FlipperFormat* file,
NfcDevice* dev,
SlixTypeFeatures features,
const char* type) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
char msg[64];
snprintf(msg, sizeof(msg), "%s specific data", type);
if(!flipper_format_write_comment_cstr(file, msg)) break;
if(!flipper_format_write_comment_cstr(
file, "Passwords are optional. If password is omitted, any password is accepted"))
break;
if(features & SlixFeatureRead) {
if(data->flags & NfcVSlixDataFlagsHasKeyRead) {
if(!flipper_format_write_hex(
file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
}
}
if(features & SlixFeatureWrite) {
if(data->flags & NfcVSlixDataFlagsHasKeyWrite) {
if(!flipper_format_write_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
}
}
if(features & SlixFeaturePrivacy) {
if(data->flags & NfcVSlixDataFlagsHasKeyPrivacy) {
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
}
}
if(features & SlixFeatureDestroy) {
if(data->flags & NfcVSlixDataFlagsHasKeyDestroy) {
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
}
}
if(features & SlixFeatureEas) {
if(data->flags & NfcVSlixDataFlagsHasKeyEas) {
if(!flipper_format_write_hex(
file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
}
}
if(features & SlixFeatureSignature) {
if(!flipper_format_write_comment_cstr(
file,
"This is the card's secp128r1 elliptic curve signature. It can not be calculated without knowing NXP's private key."))
break;
if(!flipper_format_write_hex(
file, "Signature", data->signature, sizeof(data->signature)))
break;
}
if(features & SlixFeaturePrivacy) {
bool privacy = (data->flags & NfcVSlixDataFlagsPrivacy) ? true : false;
if(!flipper_format_write_bool(file, "Privacy Mode", &privacy, 1)) break;
}
if(features & SlixFeatureProtection) {
if(!flipper_format_write_comment_cstr(file, "Protection pointer configuration")) break;
if(!flipper_format_write_hex(file, "Protection pointer", &data->pp_pointer, 1)) break;
if(!flipper_format_write_hex(file, "Protection condition", &data->pp_condition, 1))
break;
}
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) {
bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev, SlixTypeFeatures features) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
data->flags = 0;
parsed = true;
} while(false);
return parsed;
}
static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break;
if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(!flipper_format_write_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(features & SlixFeatureRead) {
if(flipper_format_key_exist(file, "Password Read")) {
if(!flipper_format_read_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
file, "Password Read", data->key_read, sizeof(data->key_read))) {
FURI_LOG_D(TAG, "Failed reading Password Read");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyRead;
}
}
if(features & SlixFeatureWrite) {
if(flipper_format_key_exist(file, "Password Write")) {
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
file, "Password Write", data->key_write, sizeof(data->key_write))) {
FURI_LOG_D(TAG, "Failed reading Password Write");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyWrite;
}
}
if(features & SlixFeaturePrivacy) {
if(flipper_format_key_exist(file, "Password Privacy")) {
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) {
FURI_LOG_D(TAG, "Failed reading Password Privacy");
break;
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
parsed = true;
} while(false);
return parsed;
}
static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break;
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) {
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
}
data->flags |= NfcVSlixDataFlagsHasKeyPrivacy;
}
}
if(features & SlixFeatureDestroy) {
if(flipper_format_key_exist(file, "Password Destroy")) {
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) {
FURI_LOG_D(TAG, "Failed reading Password Destroy");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyDestroy;
}
}
if(features & SlixFeatureEas) {
if(flipper_format_key_exist(file, "Password EAS")) {
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
parsed = true;
} while(false);
return parsed;
}
static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) {
bool saved = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
do {
if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break;
if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
break;
if(!flipper_format_write_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
break;
if(!flipper_format_write_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
break;
if(!flipper_format_write_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
break;
if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
break;
if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break;
saved = true;
} while(false);
return saved;
}
bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524
bool parsed = false;
NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix;
memset(data, 0, sizeof(NfcVSlixData));
do {
if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read)))
file, "Password EAS", data->key_eas, sizeof(data->key_eas))) {
FURI_LOG_D(TAG, "Failed reading Password EAS");
break;
}
data->flags |= NfcVSlixDataFlagsHasKeyEas;
}
}
if(features & SlixFeatureSignature) {
if(!flipper_format_read_hex(
file, "Password Write", data->key_write, sizeof(data->key_write)))
file, "Signature", data->signature, sizeof(data->signature))) {
FURI_LOG_D(TAG, "Failed reading Signature");
break;
if(!flipper_format_read_hex(
file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy)))
}
}
if(features & SlixFeaturePrivacy) {
bool privacy;
if(!flipper_format_read_bool(file, "Privacy Mode", &privacy, 1)) {
FURI_LOG_D(TAG, "Failed reading Privacy Mode");
break;
if(!flipper_format_read_hex(
file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy)))
}
if(privacy) {
data->flags |= NfcVSlixDataFlagsPrivacy;
}
}
if(features & SlixFeatureProtection) {
if(!flipper_format_read_hex(file, "Protection pointer", &(data->pp_pointer), 1)) {
FURI_LOG_D(TAG, "Failed reading Protection pointer");
break;
if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas)))
}
if(!flipper_format_read_hex(file, "Protection condition", &(data->pp_condition), 1)) {
FURI_LOG_D(TAG, "Failed reading Protection condition");
break;
if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break;
}
}
parsed = true;
} while(false);
@@ -862,7 +851,8 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
file, "Data Content", data->data, data->block_num * data->block_size))
break;
if(!flipper_format_write_comment_cstr(
file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info"))
file,
"First byte: DSFID (0x01) / AFI (0x02) / EAS (0x04) / PPL (0x08) lock info, others: block lock info"))
break;
if(!flipper_format_write_hex(
file, "Security Status", data->security_status, 1 + data->block_num))
@@ -880,16 +870,16 @@ static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
saved = true;
break;
case NfcVTypeSlix:
saved = nfc_device_save_slix_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix, "SLIX");
break;
case NfcVTypeSlixS:
saved = nfc_device_save_slix_s_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixS, "SLIX-S");
break;
case NfcVTypeSlixL:
saved = nfc_device_save_slix_l_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlixL, "SLIX-L");
break;
case NfcVTypeSlix2:
saved = nfc_device_save_slix2_data(file, dev);
saved = nfc_device_save_slix_data(file, dev, SlixFeatureSlix2, "SLIX2");
break;
default:
break;
@@ -909,23 +899,45 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
uint32_t temp_uint32 = 0;
uint8_t temp_value = 0;
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break;
if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break;
if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break;
if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break;
data->block_num = temp_uint32;
if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break;
if(!flipper_format_read_hex(
file, "Data Content", data->data, data->block_num * data->block_size))
if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) {
FURI_LOG_D(TAG, "Failed reading DSFID");
break;
}
if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) {
FURI_LOG_D(TAG, "Failed reading AFI");
break;
}
if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) {
FURI_LOG_D(TAG, "Failed reading IC Reference");
break;
}
if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) {
FURI_LOG_D(TAG, "Failed reading Block Count");
break;
}
data->block_num = temp_uint32;
if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) {
FURI_LOG_D(TAG, "Failed reading Block Size");
break;
}
if(!flipper_format_read_hex(
file, "Data Content", data->data, data->block_num * data->block_size)) {
FURI_LOG_D(TAG, "Failed reading Data Content");
break;
}
/* optional, as added later */
if(flipper_format_key_exist(file, "Security Status")) {
if(!flipper_format_read_hex(
file, "Security Status", data->security_status, 1 + data->block_num))
file, "Security Status", data->security_status, 1 + data->block_num)) {
FURI_LOG_D(TAG, "Failed reading Security Status");
break;
}
}
if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) {
FURI_LOG_D(TAG, "Failed reading Subtype");
break;
}
if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break;
data->sub_type = temp_value;
switch(data->sub_type) {
@@ -933,16 +945,16 @@ bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) {
parsed = true;
break;
case NfcVTypeSlix:
parsed = nfc_device_load_slix_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix);
break;
case NfcVTypeSlixS:
parsed = nfc_device_load_slix_s_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixS);
break;
case NfcVTypeSlixL:
parsed = nfc_device_load_slix_l_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlixL);
break;
case NfcVTypeSlix2:
parsed = nfc_device_load_slix2_data(file, dev);
parsed = nfc_device_load_slix_data(file, dev, SlixFeatureSlix2);
break;
default:
break;

View File

@@ -1085,14 +1085,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
deactivated = true;
} else {
// If the key A is marked as found and matches the searching key, invalidate it
uint8_t found_key[6];
memcpy(found_key, data->block[i].value, 6);
MfClassicSectorTrailer* sec_trailer =
mf_classic_get_sector_trailer_by_sector(data, i);
uint8_t current_key[6];
memcpy(current_key, &key, 6);
if(mf_classic_is_key_found(data, i, MfClassicKeyA) &&
memcmp(found_key, current_key, 6) == 0) {
memcmp(sec_trailer->key_a, current_key, 6) == 0) {
mf_classic_set_key_not_found(data, i, MfClassicKeyA);
is_key_a_found = false;
FURI_LOG_D(TAG, "Key %dA not found in attack", i);
@@ -1111,14 +1111,14 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
deactivated = true;
} else {
// If the key B is marked as found and matches the searching key, invalidate it
uint8_t found_key[6];
memcpy(found_key, data->block[i].value + 10, 6);
MfClassicSectorTrailer* sec_trailer =
mf_classic_get_sector_trailer_by_sector(data, i);
uint8_t current_key[6];
memcpy(current_key, &key, 6);
if(mf_classic_is_key_found(data, i, MfClassicKeyB) &&
memcmp(found_key, current_key, 6) == 0) {
memcmp(sec_trailer->key_b, current_key, 6) == 0) {
mf_classic_set_key_not_found(data, i, MfClassicKeyB);
is_key_b_found = false;
FURI_LOG_D(TAG, "Key %dB not found in attack", i);

View File

@@ -149,12 +149,18 @@ bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* n
return false;
}
/* clear all know sub type data before reading them */
memset(&nfcv_data->sub_data, 0x00, sizeof(nfcv_data->sub_data));
if(slix_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX detected");
nfcv_data->sub_type = NfcVTypeSlix;
} else if(slix2_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX2 detected");
nfcv_data->sub_type = NfcVTypeSlix2;
if(slix2_read_custom(nfc_data, nfcv_data) != ERR_NONE) {
return false;
}
} else if(slix_s_check_card_type(nfc_data)) {
FURI_LOG_I(TAG, "NXP SLIX-S detected");
nfcv_data->sub_type = NfcVTypeSlixS;
@@ -612,9 +618,34 @@ void nfcv_emu_handle_packet(
if(ctx->flags & NFCV_REQ_FLAG_AFI) {
uint8_t afi = nfcv_data->frame[ctx->payload_offset];
uint8_t family = (afi & 0xF0);
uint8_t subfamily = (afi & 0x0F);
if(family) {
if(subfamily) {
/* selected family and subfamily only */
if(afi == nfcv_data->afi) {
respond = true;
}
} else {
/* selected family, any subfamily */
if(family == (nfcv_data->afi & 0xf0)) {
respond = true;
}
}
} else {
if(subfamily) {
/* proprietary subfamily only */
if(afi == nfcv_data->afi) {
respond = true;
}
} else {
/* all families and subfamilies */
respond = true;
}
}
} else {
respond = true;
}
@@ -740,13 +771,19 @@ void nfcv_emu_handle_packet(
case NFCV_CMD_READ_MULTI_BLOCK:
case NFCV_CMD_READ_BLOCK: {
uint8_t block = nfcv_data->frame[ctx->payload_offset];
uint8_t blocks = 1;
int blocks = 1;
if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) {
blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1;
}
if(block + blocks <= nfcv_data->block_num) {
/* limit the maximum block count, underflow accepted */
if(block + blocks > nfcv_data->block_num) {
blocks = nfcv_data->block_num - block;
}
/* only respond with the valid blocks, if there are any */
if(blocks > 0) {
uint8_t buffer_pos = 0;
ctx->response_buffer[buffer_pos++] = NFCV_NOERROR;
@@ -773,11 +810,14 @@ void nfcv_emu_handle_packet(
ctx->response_flags,
ctx->send_time);
} else {
/* reply with an error only in addressed or selected mode */
if(ctx->addressed || ctx->selected) {
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time);
}
}
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block);
break;

View File

@@ -139,8 +139,10 @@ typedef enum {
} NfcVErrorcodes;
typedef enum {
NfcVLockBitDsfid = 1,
NfcVLockBitAfi = 2,
NfcVLockBitDsfid = 1 << 0,
NfcVLockBitAfi = 1 << 1,
NfcVLockBitEas = 1 << 2,
NfcVLockBitPpl = 1 << 3,
} NfcVLockBits;
typedef enum {
@@ -168,14 +170,55 @@ typedef enum {
NfcVSendFlagsHighRate = 1 << 4
} NfcVSendFlags;
/* SLIX specific config flags */
typedef enum {
NfcVSlixDataFlagsNone = 0,
NfcVSlixDataFlagsHasKeyRead = 1 << 0,
NfcVSlixDataFlagsHasKeyWrite = 1 << 1,
NfcVSlixDataFlagsHasKeyPrivacy = 1 << 2,
NfcVSlixDataFlagsHasKeyDestroy = 1 << 3,
NfcVSlixDataFlagsHasKeyEas = 1 << 4,
NfcVSlixDataFlagsValidKeyRead = 1 << 8,
NfcVSlixDataFlagsValidKeyWrite = 1 << 9,
NfcVSlixDataFlagsValidKeyPrivacy = 1 << 10,
NfcVSlixDataFlagsValidKeyDestroy = 1 << 11,
NfcVSlixDataFlagsValidKeyEas = 1 << 12,
NfcVSlixDataFlagsPrivacy = 1 << 16,
NfcVSlixDataFlagsDestroyed = 1 << 17
} NfcVSlixDataFlags;
/* abstract the file read/write operations for all SLIX types to reduce duplicated code */
typedef enum {
SlixFeatureRead = 1 << 0,
SlixFeatureWrite = 1 << 1,
SlixFeaturePrivacy = 1 << 2,
SlixFeatureDestroy = 1 << 3,
SlixFeatureEas = 1 << 4,
SlixFeatureSignature = 1 << 5,
SlixFeatureProtection = 1 << 6,
SlixFeatureSlix = SlixFeatureEas,
SlixFeatureSlixS =
(SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy |
SlixFeatureEas),
SlixFeatureSlixL = (SlixFeaturePrivacy | SlixFeatureDestroy | SlixFeatureEas),
SlixFeatureSlix2 =
(SlixFeatureRead | SlixFeatureWrite | SlixFeaturePrivacy | SlixFeatureDestroy |
SlixFeatureEas | SlixFeatureSignature | SlixFeatureProtection),
} SlixTypeFeatures;
typedef struct {
uint32_t flags;
uint8_t key_read[4];
uint8_t key_write[4];
uint8_t key_privacy[4];
uint8_t key_destroy[4];
uint8_t key_eas[4];
uint8_t rand[2];
bool privacy;
uint8_t signature[32];
/* SLIX2 options */
uint8_t pp_pointer;
uint8_t pp_condition;
} NfcVSlixData;
typedef union {

View File

@@ -9,6 +9,120 @@
#define TAG "SLIX"
ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
furi_assert(nfc_data);
furi_assert(nfcv_data);
uint8_t rxBuf[32];
uint16_t received = 0;
ReturnCode ret = ERR_NONE;
FURI_LOG_D(TAG, "Read NXP SYSTEM INFORMATION...");
for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
uint8_t cmd[] = {};
uint8_t uid[NFCV_UID_LENGTH];
/* UID is stored reversed in requests */
for(int pos = 0; pos < nfc_data->uid_len; pos++) {
uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
}
ReturnCode ret = rfalNfcvPollerTransceiveReq(
NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION,
RFAL_NFCV_REQ_FLAG_DEFAULT,
NFCV_MANUFACTURER_NXP,
uid,
cmd,
sizeof(cmd),
rxBuf,
sizeof(rxBuf),
&received);
if(ret == ERR_NONE) {
break;
}
}
if(ret != ERR_NONE || received != 8) {
FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
return ret;
}
FURI_LOG_D(TAG, "Success...");
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
slix->pp_pointer = rxBuf[1];
slix->pp_condition = rxBuf[2];
/* convert NXP's to our internal lock bits format */
nfcv_data->security_status[0] = 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitDsfid) ? NfcVLockBitDsfid : 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitAfi) ? NfcVLockBitAfi : 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitEas) ? NfcVLockBitEas : 0;
nfcv_data->security_status[0] |= (rxBuf[3] & SlixLockBitPpl) ? NfcVLockBitPpl : 0;
return ERR_NONE;
}
ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
furi_assert(nfc_data);
furi_assert(nfcv_data);
uint8_t rxBuf[64];
uint16_t received = 0;
ReturnCode ret = ERR_NONE;
FURI_LOG_D(TAG, "Read SIGNATURE...");
for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) {
uint8_t cmd[] = {};
uint8_t uid[NFCV_UID_LENGTH];
/* UID is stored reversed in requests */
for(int pos = 0; pos < nfc_data->uid_len; pos++) {
uid[pos] = nfc_data->uid[nfc_data->uid_len - 1 - pos];
}
ReturnCode ret = rfalNfcvPollerTransceiveReq(
NFCV_CMD_NXP_READ_SIGNATURE,
RFAL_NFCV_REQ_FLAG_DEFAULT,
NFCV_MANUFACTURER_NXP,
uid,
cmd,
sizeof(cmd),
rxBuf,
sizeof(rxBuf),
&received);
if(ret == ERR_NONE) {
break;
}
}
if(ret != ERR_NONE || received != 33) {
FURI_LOG_D(TAG, "Failed: %d, %d", ret, received);
return ret;
}
FURI_LOG_D(TAG, "Success...");
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
memcpy(slix->signature, &rxBuf[1], 32);
return ERR_NONE;
}
ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) {
ReturnCode ret = ERR_NONE;
ret = slix2_read_nxp_sysinfo(nfc_data, nfcv_data);
if(ret != ERR_NONE) {
return ret;
}
ret = slix2_read_signature(nfc_data, nfcv_data);
return ret;
}
static uint32_t slix_read_be(uint8_t* data, uint32_t length) {
uint32_t value = 0;
@@ -137,6 +251,43 @@ ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) {
return ret;
}
static void slix_generic_pass_infos(
uint8_t password_id,
NfcVSlixData* slix,
uint8_t** password,
uint32_t* flag_valid,
uint32_t* flag_set) {
switch(password_id) {
case SLIX_PASS_READ:
*password = slix->key_read;
*flag_valid = NfcVSlixDataFlagsValidKeyRead;
*flag_set = NfcVSlixDataFlagsHasKeyRead;
break;
case SLIX_PASS_WRITE:
*password = slix->key_write;
*flag_valid = NfcVSlixDataFlagsValidKeyWrite;
*flag_set = NfcVSlixDataFlagsHasKeyWrite;
break;
case SLIX_PASS_PRIVACY:
*password = slix->key_privacy;
*flag_valid = NfcVSlixDataFlagsValidKeyPrivacy;
*flag_set = NfcVSlixDataFlagsHasKeyPrivacy;
break;
case SLIX_PASS_DESTROY:
*password = slix->key_destroy;
*flag_valid = NfcVSlixDataFlagsValidKeyDestroy;
*flag_set = NfcVSlixDataFlagsHasKeyDestroy;
break;
case SLIX_PASS_EASAFI:
*password = slix->key_eas;
*flag_valid = NfcVSlixDataFlagsValidKeyEas;
*flag_set = NfcVSlixDataFlagsHasKeyEas;
break;
default:
break;
}
}
bool slix_generic_protocol_filter(
FuriHalNfcTxRxContext* tx_rx,
FuriHalNfcDevData* nfc_data,
@@ -150,7 +301,8 @@ bool slix_generic_protocol_filter(
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
if((slix->flags & NfcVSlixDataFlagsPrivacy) &&
ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER &&
ctx->command != NFCV_CMD_NXP_SET_PASSWORD) {
snprintf(
nfcv_data->last_command,
@@ -186,66 +338,73 @@ bool slix_generic_protocol_filter(
}
case NFCV_CMD_NXP_SET_PASSWORD: {
/* the password to be set is the first parameter */
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
/* right after that is the XORed password */
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
/* only handle if the password type is supported */
if(!(password_id & password_supported)) {
break;
}
uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1];
/* fetch the last RAND value */
uint8_t* rand = slix->rand;
uint8_t* password = NULL;
/* first calc the password that has been sent */
uint8_t password_rcv[4];
switch(password_id) {
case SLIX_PASS_READ:
password = slix->key_read;
break;
case SLIX_PASS_WRITE:
password = slix->key_write;
break;
case SLIX_PASS_PRIVACY:
password = slix->key_privacy;
break;
case SLIX_PASS_DESTROY:
password = slix->key_destroy;
break;
case SLIX_PASS_EASAFI:
password = slix->key_eas;
break;
default:
break;
for(int pos = 0; pos < 4; pos++) {
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
}
uint32_t pass_received = slix_read_be(password_rcv, 4);
/* then determine the password type (or even update if not set yet) */
uint8_t* password = NULL;
uint32_t flag_valid = 0;
uint32_t flag_set = 0;
slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
/* when the password is not supported, return silently */
if(!password) {
break;
}
for(int pos = 0; pos < 4; pos++) {
password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2];
}
uint32_t pass_expect = slix_read_be(password, 4);
uint32_t pass_received = slix_read_be(password_rcv, 4);
/* check if the password is known */
bool pass_valid = false;
uint32_t pass_expect = 0;
/* if the password is all-zeroes, just accept any password*/
if(!pass_expect || pass_expect == pass_received) {
if(slix->flags & flag_set) {
/* if so, fetch the stored password and compare */
pass_expect = slix_read_be(password, 4);
pass_valid = (pass_expect == pass_received);
} else {
/* if not known, just accept it and store that password */
memcpy(password, password_rcv, 4);
nfcv_data->modified = true;
slix->flags |= flag_set;
pass_valid = true;
}
/* if the pass was valid or accepted for other reasons, continue */
if(pass_valid) {
slix->flags |= flag_valid;
/* handle actions when a correct password was given, aside of setting the flag */
switch(password_id) {
case SLIX_PASS_READ:
break;
case SLIX_PASS_WRITE:
break;
case SLIX_PASS_PRIVACY:
slix->privacy = false;
slix->flags &= ~NfcVSlixDataFlagsPrivacy;
nfcv_data->modified = true;
break;
case SLIX_PASS_DESTROY:
slix->flags |= NfcVSlixDataFlagsDestroyed;
FURI_LOG_D(TAG, "Pooof! Got destroyed");
break;
case SLIX_PASS_EASAFI:
break;
default:
break;
}
ctx->response_buffer[0] = NFCV_NOERROR;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
@@ -268,6 +427,49 @@ bool slix_generic_protocol_filter(
break;
}
case NFCV_CMD_NXP_WRITE_PASSWORD: {
uint8_t password_id = nfcv_data->frame[ctx->payload_offset];
if(!(password_id & password_supported)) {
break;
}
uint8_t* new_password = &nfcv_data->frame[ctx->payload_offset + 1];
uint8_t* password = NULL;
uint32_t flag_valid = 0;
uint32_t flag_set = 0;
slix_generic_pass_infos(password_id, slix, &password, &flag_valid, &flag_set);
/* when the password is not supported, return silently */
if(!password) {
break;
}
bool pass_valid = (slix->flags & flag_valid);
if(!(slix->flags & flag_set)) {
pass_valid = true;
}
if(pass_valid) {
slix->flags |= flag_valid;
slix->flags |= flag_set;
memcpy(password, new_password, 4);
ctx->response_buffer[0] = NFCV_NOERROR;
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time);
snprintf(
nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD OK");
} else {
snprintf(
nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE_PASSWORD FAIL");
}
handled = true;
break;
}
case NFCV_CMD_NXP_ENABLE_PRIVACY: {
ctx->response_buffer[0] = NFCV_NOERROR;
@@ -278,7 +480,7 @@ bool slix_generic_protocol_filter(
sizeof(nfcv_data->last_command),
"NFCV_CMD_NXP_ENABLE_PRIVACY");
slix->privacy = true;
slix->flags |= NfcVSlixDataFlagsPrivacy;
handled = true;
break;
}
@@ -315,7 +517,10 @@ void slix_l_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_l_protocol_filter;
@@ -345,7 +550,10 @@ void slix_s_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_s_protocol_filter;
@@ -375,7 +583,10 @@ void slix_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix_protocol_filter;
@@ -389,6 +600,10 @@ bool slix2_protocol_filter( // -V524
furi_assert(nfc_data);
furi_assert(nfcv_data_in);
NfcVData* nfcv_data = (NfcVData*)nfcv_data_in;
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
NfcVSlixData* slix = &nfcv_data->sub_data.slix;
bool handled = false;
/* many SLIX share some of the functions, place that in a generic handler */
@@ -396,6 +611,157 @@ bool slix2_protocol_filter( // -V524
return true;
}
switch(ctx->command) {
/* override WRITE BLOCK for block 79 (16 bit counter) */
case NFCV_CMD_WRITE_BLOCK:
case NFCV_CMD_WRITE_MULTI_BLOCK: {
uint8_t resp_len = 1;
uint8_t blocks = 1;
uint8_t block = nfcv_data->frame[ctx->payload_offset];
uint8_t data_pos = ctx->payload_offset + 1;
if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
blocks = nfcv_data->frame[data_pos] + 1;
data_pos++;
}
uint8_t* data = &nfcv_data->frame[data_pos];
uint32_t data_len = nfcv_data->block_size * blocks;
if((block + blocks) <= nfcv_data->block_num &&
(data_pos + data_len + 2) == nfcv_data->frame_length) {
ctx->response_buffer[0] = NFCV_NOERROR;
for(int block_num = block; block_num < block + blocks; block_num++) {
/* special case, 16-bit counter */
if(block_num == 79) {
uint32_t dest;
uint32_t ctr_old;
memcpy(&dest, &nfcv_data->frame[data_pos], 4);
memcpy(&ctr_old, &nfcv_data->data[nfcv_data->block_size * block_num], 4);
uint32_t ctr_new = ctr_old;
bool allowed = true;
/* increment counter */
if(dest == 1) {
ctr_new = (ctr_old & 0xFFFF0000) | ((ctr_old + 1) & 0xFFFF);
/* protection flag set? */
if(ctr_old & 0x01000000) {
allowed = nfcv_data->sub_data.slix.flags &
NfcVSlixDataFlagsValidKeyRead;
}
} else {
ctr_new = dest;
allowed = nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsValidKeyWrite;
}
if(allowed) {
memcpy(&nfcv_data->data[nfcv_data->block_size * block_num], &ctr_new, 4);
} else {
/* incorrect read or write password */
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
resp_len = 2;
}
} else {
memcpy(
&nfcv_data->data[nfcv_data->block_size * block_num],
&nfcv_data->frame[data_pos],
nfcv_data->block_size);
}
data_pos += nfcv_data->block_size;
}
nfcv_data->modified = true;
} else {
ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR;
ctx->response_buffer[1] = NFCV_ERROR_GENERIC;
resp_len = 2;
}
bool respond = (ctx->response_buffer[0] == NFCV_NOERROR) ||
(ctx->addressed || ctx->selected);
if(respond) {
nfcv_emu_send(
tx_rx,
nfcv_data,
ctx->response_buffer,
resp_len,
ctx->response_flags,
ctx->send_time);
}
if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"WRITE MULTI BLOCK %d, %d blocks",
block,
blocks);
} else {
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"WRITE BLOCK %d <- %02X %02X %02X %02X",
block,
data[0],
data[1],
data[2],
data[3]);
}
handled = true;
break;
}
case NFCV_CMD_NXP_READ_SIGNATURE: {
uint32_t len = 0;
ctx->response_buffer[len++] = NFCV_NOERROR;
memcpy(&ctx->response_buffer[len], slix->signature, sizeof(slix->signature));
len += sizeof(slix->signature);
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ_SIGNATURE");
handled = true;
break;
}
case NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION: {
uint32_t len = 0;
uint8_t lock_bits = 0;
/* convert our internal lock bits format into NXP's */
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? SlixLockBitDsfid : 0;
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitAfi) ? SlixLockBitAfi : 0;
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitEas) ? SlixLockBitEas : 0;
lock_bits |= (nfcv_data->security_status[0] & NfcVLockBitPpl) ? SlixLockBitPpl : 0;
ctx->response_buffer[len++] = NFCV_NOERROR;
ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_pointer;
ctx->response_buffer[len++] = nfcv_data->sub_data.slix.pp_condition;
ctx->response_buffer[len++] = lock_bits;
ctx->response_buffer[len++] = 0x7F; /* features LSB */
ctx->response_buffer[len++] = 0x35; /* features */
ctx->response_buffer[len++] = 0; /* features */
ctx->response_buffer[len++] = 0; /* features MSB */
nfcv_emu_send(
tx_rx, nfcv_data, ctx->response_buffer, len, ctx->response_flags, ctx->send_time);
snprintf(
nfcv_data->last_command,
sizeof(nfcv_data->last_command),
"GET_NXP_SYSTEM_INFORMATION");
handled = true;
break;
}
}
return handled;
}
@@ -405,7 +771,10 @@ void slix2_prepare(NfcVData* nfcv_data) {
FURI_LOG_D(
TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4));
FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4));
FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF");
FURI_LOG_D(
TAG,
" Privacy mode: %s",
(nfcv_data->sub_data.slix.flags & NfcVSlixDataFlagsPrivacy) ? "ON" : "OFF");
NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx;
ctx->emu_protocol_filter = &slix2_protocol_filter;

View File

@@ -8,19 +8,35 @@
#define NFCV_MANUFACTURER_NXP 0x04
/* ISO15693-3 CUSTOM NXP COMMANDS */
#define NFCV_CMD_NXP_SET_EAS 0xA2
#define NFCV_CMD_NXP_RESET_EAS 0xA3
#define NFCV_CMD_NXP_LOCK_EAS 0xA4
#define NFCV_CMD_NXP_EAS_ALARM 0xA5
#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6
#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0
#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1
#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2
#define NFCV_CMD_NXP_SET_PASSWORD 0xB3
#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4
#define NFCV_CMD_NXP_DESTROY 0xB9
#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA
typedef enum {
NFCV_CMD_NXP_SET_EAS = 0xA2,
NFCV_CMD_NXP_RESET_EAS = 0xA3,
NFCV_CMD_NXP_LOCK_EAS = 0xA4,
NFCV_CMD_NXP_EAS_ALARM = 0xA5,
NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI = 0xA6,
NFCV_CMD_NXP_WRITE_EAS_ID = 0xA7,
NFCV_CMD_NXP_GET_NXP_SYSTEM_INFORMATION = 0xAB,
NFCV_CMD_NXP_INVENTORY_PAGE_READ = 0xB0,
NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST = 0xB1,
NFCV_CMD_NXP_GET_RANDOM_NUMBER = 0xB2,
NFCV_CMD_NXP_SET_PASSWORD = 0xB3,
NFCV_CMD_NXP_WRITE_PASSWORD = 0xB4,
NFCV_CMD_NXP_64_BIT_PASSWORD_PROTECTION = 0xB5,
NFCV_CMD_NXP_PROTECT_PAGE = 0xB6,
NFCV_CMD_NXP_LOCK_PAGE_PROTECTION_CONDITION = 0xB7,
NFCV_CMD_NXP_DESTROY = 0xB9,
NFCV_CMD_NXP_ENABLE_PRIVACY = 0xBA,
NFCV_CMD_NXP_STAY_QUIET_PERSISTENT = 0xBC,
NFCV_CMD_NXP_READ_SIGNATURE = 0xBD
} SlixCommands;
/* lock bit bits used in SLIX's NXP SYSTEM INFORMATION response */
typedef enum {
SlixLockBitAfi = 1 << 0,
SlixLockBitEas = 1 << 1,
SlixLockBitDsfid = 1 << 2,
SlixLockBitPpl = 1 << 3,
} SlixLockBits;
/* available passwords */
#define SLIX_PASS_READ 0x01
@@ -37,6 +53,10 @@ bool slix2_check_card_type(FuriHalNfcDevData* nfc_data);
bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data);
bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data);
ReturnCode slix2_read_custom(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
ReturnCode slix2_read_signature(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
ReturnCode slix2_read_nxp_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data);
ReturnCode slix_get_random(NfcVData* data);
ReturnCode slix_unlock(NfcVData* data, uint32_t password_id);