Merge remote-tracking branch 'origin/dev' into release-candidate

This commit is contained in:
Aleksandr Kutuzov 2022-11-30 20:42:10 +09:00
commit ec218060ac
220 changed files with 5055 additions and 1784 deletions

View File

@ -2,7 +2,7 @@
Thank you for investing your time in contributing to our project! Thank you for investing your time in contributing to our project!
Read our [Code of Coduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable. Read our [Code of Conduct](CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR. In this guide you will get an overview of the contribution workflow from opening an issue, creating a PR, reviewing, and merging the PR.
@ -17,12 +17,12 @@ See the [ReadMe](ReadMe.md) to get an overview of the project. Here are some hel
## Getting started ## Getting started
Before writing code and creating PR make sure that it aligns with our mission and guidlines: Before writing code and creating PR make sure that it aligns with our mission and guidelines:
- All our devices are intended for research and education. - All our devices are intended for research and education.
- PR that contains code intended to commit crimes is not going to be accepted. - PR that contains code intended to commit crimes is not going to be accepted.
- Your PR must comply with our [Coding Style](CODING_STYLE.md) - Your PR must comply with our [Coding Style](CODING_STYLE.md)
- Your PR must contain code compatiable with project [LICENSE](LICENSE). - Your PR must contain code compatible with project [LICENSE](LICENSE).
- PR will only be merged if it pass CI/CD. - PR will only be merged if it pass CI/CD.
- PR will only be merged if it pass review by code owner. - PR will only be merged if it pass review by code owner.

View File

@ -71,7 +71,7 @@ void WIEGAND::end() {
} }
void WIEGAND::ReadD0() { void WIEGAND::ReadD0() {
_bitCount++; // Increament bit count for Interrupt connected to D0 _bitCount++; // Increment bit count for Interrupt connected to D0
if(_bitCount > 31) // If bit count more than 31, process high bits if(_bitCount > 31) // If bit count more than 31, process high bits
{ {
_cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits _cardTempHigh |= ((0x80000000 & _cardTemp) >> 31); // shift value to high bits

View File

@ -98,7 +98,7 @@ void bt_debug_app_free(BtDebugApp* app) {
int32_t bt_debug_app(void* p) { int32_t bt_debug_app(void* p) {
UNUSED(p); UNUSED(p);
if(!furi_hal_bt_is_testing_supported()) { if(!furi_hal_bt_is_testing_supported()) {
FURI_LOG_E(TAG, "Incorrect radio stack: radio testing fetures are absent."); FURI_LOG_E(TAG, "Incorrect radio stack: radio testing features are absent.");
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack"); dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
return 255; return 255;

View File

@ -145,7 +145,7 @@ DisplayTest* display_test_alloc() {
view_set_previous_callback(view, display_test_previous_callback); view_set_previous_callback(view, display_test_previous_callback);
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view); view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
// Configurtion items // Configuration items
VariableItem* item; VariableItem* item;
instance->config_bias = false; instance->config_bias = false;
instance->config_contrast = 32; instance->config_contrast = 32;

View File

@ -0,0 +1,10 @@
App(
appid="rpc_debug",
name="RPC Debug",
apptype=FlipperAppType.DEBUG,
entry_point="rpc_debug_app",
requires=["gui", "rpc_start", "notification"],
stack_size=2 * 1024,
order=10,
fap_category="Debug",
)

View File

@ -0,0 +1,138 @@
#include "rpc_debug_app.h"
#include <core/log.h>
#include <string.h>
static bool rpc_debug_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
RpcDebugApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool rpc_debug_app_back_event_callback(void* context) {
furi_assert(context);
RpcDebugApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void rpc_debug_app_tick_event_callback(void* context) {
furi_assert(context);
RpcDebugApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
static void rpc_debug_app_rpc_command_callback(RpcAppSystemEvent event, void* context) {
furi_assert(context);
RpcDebugApp* app = context;
furi_assert(app->rpc);
if(event == RpcAppEventSessionClose) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
rpc_system_app_set_callback(app->rpc, NULL, NULL);
app->rpc = NULL;
} else if(event == RpcAppEventAppExit) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
rpc_system_app_confirm(app->rpc, RpcAppEventAppExit, true);
} else {
rpc_system_app_confirm(app->rpc, event, false);
}
}
static bool rpc_debug_app_rpc_init_rpc(RpcDebugApp* app, const char* args) {
bool ret = false;
if(args && strlen(args)) {
uint32_t rpc = 0;
if(sscanf(args, "RPC %lX", &rpc) == 1) {
app->rpc = (RpcAppSystem*)rpc;
rpc_system_app_set_callback(app->rpc, rpc_debug_app_rpc_command_callback, app);
rpc_system_app_send_started(app->rpc);
ret = true;
}
}
return ret;
}
static RpcDebugApp* rpc_debug_app_alloc() {
RpcDebugApp* app = malloc(sizeof(RpcDebugApp));
app->gui = furi_record_open(RECORD_GUI);
app->notifications = furi_record_open(RECORD_NOTIFICATION);
app->scene_manager = scene_manager_alloc(&rpc_debug_app_scene_handlers, app);
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, rpc_debug_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, rpc_debug_app_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, rpc_debug_app_tick_event_callback, 100);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_enable_queue(app->view_dispatcher);
app->widget = widget_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewWidget, widget_get_view(app->widget));
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewSubmenu, submenu_get_view(app->submenu));
app->text_box = text_box_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewTextBox, text_box_get_view(app->text_box));
app->text_input = text_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewTextInput, text_input_get_view(app->text_input));
app->byte_input = byte_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher, RpcDebugAppViewByteInput, byte_input_get_view(app->byte_input));
return app;
}
static void rpc_debug_app_free(RpcDebugApp* app) {
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewByteInput);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextInput);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewTextBox);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
view_dispatcher_remove_view(app->view_dispatcher, RpcDebugAppViewWidget);
free(app->byte_input);
free(app->text_input);
free(app->text_box);
free(app->submenu);
free(app->widget);
free(app->scene_manager);
free(app->view_dispatcher);
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
furi_record_close(RECORD_GUI);
app->gui = NULL;
if(app->rpc) {
rpc_system_app_set_callback(app->rpc, NULL, NULL);
rpc_system_app_send_exited(app->rpc);
app->rpc = NULL;
}
free(app);
}
int32_t rpc_debug_app(void* args) {
RpcDebugApp* app = rpc_debug_app_alloc();
if(rpc_debug_app_rpc_init_rpc(app, args)) {
notification_message(app->notifications, &sequence_display_backlight_on);
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStart);
} else {
scene_manager_next_scene(app->scene_manager, RpcDebugAppSceneStartDummy);
}
view_dispatcher_run(app->view_dispatcher);
rpc_debug_app_free(app);
return 0;
}

View File

@ -0,0 +1,54 @@
#pragma once
#include <furi.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/widget.h>
#include <gui/modules/submenu.h>
#include <gui/modules/text_box.h>
#include <gui/modules/text_input.h>
#include <gui/modules/byte_input.h>
#include <rpc/rpc_app.h>
#include <notification/notification_messages.h>
#include "scenes/rpc_debug_app_scene.h"
#define DATA_STORE_SIZE 64U
#define TEXT_STORE_SIZE 64U
typedef struct {
Gui* gui;
RpcAppSystem* rpc;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
NotificationApp* notifications;
Widget* widget;
Submenu* submenu;
TextBox* text_box;
TextInput* text_input;
ByteInput* byte_input;
char text_store[TEXT_STORE_SIZE];
uint8_t data_store[DATA_STORE_SIZE];
} RpcDebugApp;
typedef enum {
RpcDebugAppViewWidget,
RpcDebugAppViewSubmenu,
RpcDebugAppViewTextBox,
RpcDebugAppViewTextInput,
RpcDebugAppViewByteInput,
} RpcDebugAppView;
typedef enum {
// Reserve first 100 events for button types and indexes, starting from 0
RpcDebugAppCustomEventInputErrorCode = 100,
RpcDebugAppCustomEventInputErrorText,
RpcDebugAppCustomEventInputDataExchange,
RpcDebugAppCustomEventRpcDataExchange,
} RpcDebugAppCustomEvent;

View File

@ -0,0 +1,30 @@
#include "rpc_debug_app_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const rpc_debug_app_on_enter_handlers[])(void*) = {
#include "rpc_debug_app_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const rpc_debug_app_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "rpc_debug_app_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const rpc_debug_app_on_exit_handlers[])(void* context) = {
#include "rpc_debug_app_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers rpc_debug_app_scene_handlers = {
.on_enter_handlers = rpc_debug_app_on_enter_handlers,
.on_event_handlers = rpc_debug_app_on_event_handlers,
.on_exit_handlers = rpc_debug_app_on_exit_handlers,
.scene_num = RpcDebugAppSceneNum,
};

View File

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

View File

@ -0,0 +1,8 @@
ADD_SCENE(rpc_debug_app, start, Start)
ADD_SCENE(rpc_debug_app, start_dummy, StartDummy)
ADD_SCENE(rpc_debug_app, test_app_error, TestAppError)
ADD_SCENE(rpc_debug_app, test_data_exchange, TestDataExchange)
ADD_SCENE(rpc_debug_app, input_error_code, InputErrorCode)
ADD_SCENE(rpc_debug_app, input_error_text, InputErrorText)
ADD_SCENE(rpc_debug_app, input_data_exchange, InputDataExchange)
ADD_SCENE(rpc_debug_app, receive_data_exchange, ReceiveDataExchange)

View File

@ -0,0 +1,40 @@
#include "../rpc_debug_app.h"
static void rpc_debug_app_scene_input_data_exchange_result_callback(void* context) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(
app->view_dispatcher, RpcDebugAppCustomEventInputDataExchange);
}
void rpc_debug_app_scene_input_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context;
byte_input_set_header_text(app->byte_input, "Enter data to exchange");
byte_input_set_result_callback(
app->byte_input,
rpc_debug_app_scene_input_data_exchange_result_callback,
NULL,
app,
app->data_store,
DATA_STORE_SIZE);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewByteInput);
}
bool rpc_debug_app_scene_input_data_exchange_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputDataExchange) {
rpc_system_app_exchange_data(app->rpc, app->data_store, DATA_STORE_SIZE);
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_input_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context;
UNUSED(app);
}

View File

@ -0,0 +1,60 @@
#include "../rpc_debug_app.h"
static bool rpc_debug_app_scene_input_error_code_validator_callback(
const char* text,
FuriString* error,
void* context) {
UNUSED(context);
for(; *text; ++text) {
const char c = *text;
if(c < '0' || c > '9') {
furi_string_printf(error, "%s", "Please enter\na number!");
return false;
}
}
return true;
}
static void rpc_debug_app_scene_input_error_code_result_callback(void* context) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorCode);
}
void rpc_debug_app_scene_input_error_code_on_enter(void* context) {
RpcDebugApp* app = context;
strncpy(app->text_store, "666", TEXT_STORE_SIZE);
text_input_set_header_text(app->text_input, "Enter error code");
text_input_set_validator(
app->text_input, rpc_debug_app_scene_input_error_code_validator_callback, NULL);
text_input_set_result_callback(
app->text_input,
rpc_debug_app_scene_input_error_code_result_callback,
app,
app->text_store,
TEXT_STORE_SIZE,
true);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
}
bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputErrorCode) {
rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store));
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_input_error_code_on_exit(void* context) {
RpcDebugApp* app = context;
text_input_reset(app->text_input);
text_input_set_validator(app->text_input, NULL, NULL);
}

View File

@ -0,0 +1,40 @@
#include "../rpc_debug_app.h"
static void rpc_debug_app_scene_input_error_text_result_callback(void* context) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventInputErrorText);
}
void rpc_debug_app_scene_input_error_text_on_enter(void* context) {
RpcDebugApp* app = context;
strncpy(app->text_store, "I'm a scary error message!", TEXT_STORE_SIZE);
text_input_set_header_text(app->text_input, "Enter error text");
text_input_set_result_callback(
app->text_input,
rpc_debug_app_scene_input_error_text_result_callback,
app,
app->text_store,
TEXT_STORE_SIZE,
true);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextInput);
}
bool rpc_debug_app_scene_input_error_text_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventInputErrorText) {
rpc_system_app_set_error_text(app->rpc, app->text_store);
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_input_error_text_on_exit(void* context) {
RpcDebugApp* app = context;
text_input_reset(app->text_input);
}

View File

@ -0,0 +1,70 @@
#include "../rpc_debug_app.h"
static void rpc_debug_app_scene_start_format_hex(
const uint8_t* data,
size_t data_size,
char* buf,
size_t buf_size) {
furi_assert(data);
furi_assert(buf);
const size_t byte_width = 3;
const size_t line_width = 7;
data_size = MIN(data_size, buf_size / (byte_width + 1));
for(size_t i = 0; i < data_size; ++i) {
char* p = buf + (i * byte_width);
char sep = !((i + 1) % line_width) ? '\n' : ' ';
snprintf(p, byte_width + 1, "%02X%c", data[i], sep);
}
buf[buf_size - 1] = '\0';
}
static void rpc_debug_app_scene_receive_data_exchange_callback(
const uint8_t* data,
size_t data_size,
void* context) {
RpcDebugApp* app = context;
if(data) {
rpc_debug_app_scene_start_format_hex(data, data_size, app->text_store, TEXT_STORE_SIZE);
} else {
strncpy(app->text_store, "<Data empty>", TEXT_STORE_SIZE);
}
view_dispatcher_send_custom_event(app->view_dispatcher, RpcDebugAppCustomEventRpcDataExchange);
}
void rpc_debug_app_scene_receive_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context;
strncpy(app->text_store, "Received data will appear here...", TEXT_STORE_SIZE);
text_box_set_text(app->text_box, app->text_store);
text_box_set_font(app->text_box, TextBoxFontHex);
rpc_system_app_set_data_exchange_callback(
app->rpc, rpc_debug_app_scene_receive_data_exchange_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewTextBox);
}
bool rpc_debug_app_scene_receive_data_exchange_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == RpcDebugAppCustomEventRpcDataExchange) {
notification_message(app->notifications, &sequence_blink_cyan_100);
notification_message(app->notifications, &sequence_display_backlight_on);
text_box_set_text(app->text_box, app->text_store);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_receive_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context;
text_box_reset(app->text_box);
rpc_system_app_set_data_exchange_callback(app->rpc, NULL, NULL);
}

View File

@ -0,0 +1,57 @@
#include "../rpc_debug_app.h"
enum SubmenuIndex {
SubmenuIndexTestAppError,
SubmenuIndexTestDataExchange,
};
static void rpc_debug_app_scene_start_submenu_callback(void* context, uint32_t index) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void rpc_debug_app_scene_start_on_enter(void* context) {
RpcDebugApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Test App Error",
SubmenuIndexTestAppError,
rpc_debug_app_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Test Data Exchange",
SubmenuIndexTestDataExchange,
rpc_debug_app_scene_start_submenu_callback,
app);
submenu_set_selected_item(submenu, SubmenuIndexTestAppError);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
}
bool rpc_debug_app_scene_start_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
if(submenu_index == SubmenuIndexTestAppError) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestAppError);
consumed = true;
} else if(submenu_index == SubmenuIndexTestDataExchange) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneTestDataExchange);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_start_on_exit(void* context) {
RpcDebugApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -0,0 +1,30 @@
#include "../rpc_debug_app.h"
void rpc_debug_app_scene_start_dummy_on_enter(void* context) {
RpcDebugApp* app = context;
widget_add_text_box_element(
app->widget,
0,
0,
128,
64,
AlignCenter,
AlignCenter,
"This application\nis meant to be run\nin \e#RPC\e# mode.",
false);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewWidget);
}
bool rpc_debug_app_scene_start_dummy_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
UNUSED(app);
UNUSED(event);
bool consumed = false;
return consumed;
}
void rpc_debug_app_scene_start_dummy_on_exit(void* context) {
RpcDebugApp* app = context;
widget_reset(app->widget);
}

View File

@ -0,0 +1,57 @@
#include "../rpc_debug_app.h"
typedef enum {
SubmenuIndexSetErrorCode,
SubmenuIndexSetErrorText,
} SubmenuIndex;
static void rpc_debug_app_scene_test_app_error_submenu_callback(void* context, uint32_t index) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void rpc_debug_app_scene_test_app_error_on_enter(void* context) {
RpcDebugApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Set Error Code",
SubmenuIndexSetErrorCode,
rpc_debug_app_scene_test_app_error_submenu_callback,
app);
submenu_add_item(
submenu,
"Set Error Text",
SubmenuIndexSetErrorText,
rpc_debug_app_scene_test_app_error_submenu_callback,
app);
submenu_set_selected_item(submenu, SubmenuIndexSetErrorCode);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
}
bool rpc_debug_app_scene_test_app_error_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
if(submenu_index == SubmenuIndexSetErrorCode) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorCode);
consumed = true;
} else if(submenu_index == SubmenuIndexSetErrorText) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputErrorText);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_test_app_error_on_exit(void* context) {
RpcDebugApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -0,0 +1,58 @@
#include "../rpc_debug_app.h"
typedef enum {
SubmenuIndexSendData,
SubmenuIndexReceiveData,
} SubmenuIndex;
static void
rpc_debug_app_scene_test_data_exchange_submenu_callback(void* context, uint32_t index) {
RpcDebugApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void rpc_debug_app_scene_test_data_exchange_on_enter(void* context) {
RpcDebugApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Send Data",
SubmenuIndexSendData,
rpc_debug_app_scene_test_data_exchange_submenu_callback,
app);
submenu_add_item(
submenu,
"Receive Data",
SubmenuIndexReceiveData,
rpc_debug_app_scene_test_data_exchange_submenu_callback,
app);
submenu_set_selected_item(submenu, SubmenuIndexSendData);
view_dispatcher_switch_to_view(app->view_dispatcher, RpcDebugAppViewSubmenu);
}
bool rpc_debug_app_scene_test_data_exchange_on_event(void* context, SceneManagerEvent event) {
RpcDebugApp* app = context;
SceneManager* scene_manager = app->scene_manager;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
const uint32_t submenu_index = event.event;
if(submenu_index == SubmenuIndexSendData) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneInputDataExchange);
consumed = true;
} else if(submenu_index == SubmenuIndexReceiveData) {
scene_manager_next_scene(scene_manager, RpcDebugAppSceneReceiveDataExchange);
consumed = true;
}
}
return consumed;
}
void rpc_debug_app_scene_test_data_exchange_on_exit(void* context) {
RpcDebugApp* app = context;
submenu_reset(app->submenu);
}

View File

@ -220,11 +220,7 @@ static UartEchoApp* uart_echo_app_alloc() {
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200); furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app); furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
app->worker_thread = furi_thread_alloc(); app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
furi_thread_set_name(app->worker_thread, "UsbUartWorker");
furi_thread_set_stack_size(app->worker_thread, 1024);
furi_thread_set_context(app->worker_thread, app);
furi_thread_set_callback(app->worker_thread, uart_echo_worker);
furi_thread_start(app->worker_thread); furi_thread_start(app->worker_thread);
return app; return app;

View File

@ -11,7 +11,7 @@
extern size_t memmgr_get_free_heap(void); extern size_t memmgr_get_free_heap(void);
extern size_t memmgr_get_minimum_free_heap(void); extern size_t memmgr_get_minimum_free_heap(void);
// current heap managment realization consume: // current heap management realization consume:
// X bytes after allocate and 0 bytes after allocate and free, // X bytes after allocate and 0 bytes after allocate and free,
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t // where X = sizeof(void*) + sizeof(size_t), look to BlockLink_t
const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t); const size_t heap_overhead_max_size = sizeof(void*) + sizeof(size_t);
@ -21,7 +21,7 @@ bool heap_equal(size_t heap_size, size_t heap_size_old) {
const size_t heap_low = heap_size_old - heap_overhead_max_size; const size_t heap_low = heap_size_old - heap_overhead_max_size;
const size_t heap_high = heap_size_old + heap_overhead_max_size; const size_t heap_high = heap_size_old + heap_overhead_max_size;
// not extact, so we must test it against bigger numbers than "overhead size" // not exact, so we must test it against bigger numbers than "overhead size"
const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high)); const bool result = ((heap_size >= heap_low) && (heap_size <= heap_high));
// debug allocation info // debug allocation info

View File

@ -136,7 +136,7 @@ static bool nfc_test_digital_signal_test_encode(
ref_timings_sum += ref[i]; ref_timings_sum += ref[i];
if(timings_diff > timing_tolerance) { if(timings_diff > timing_tolerance) {
FURI_LOG_E( FURI_LOG_E(
TAG, "Too big differece in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]); TAG, "Too big difference in %d timings. Ref: %ld, DUT: %ld", i, ref[i], dut[i]);
timing_check_success = false; timing_check_success = false;
break; break;
} }

View File

@ -43,11 +43,8 @@ MU_TEST(storage_file_open_lock) {
File* file = storage_file_alloc(storage); File* file = storage_file_alloc(storage);
// file_locker thread start // file_locker thread start
FuriThread* locker_thread = furi_thread_alloc(); FuriThread* locker_thread =
furi_thread_set_name(locker_thread, "StorageFileLocker"); furi_thread_alloc_ex("StorageFileLocker", 2048, storage_file_locker, semaphore);
furi_thread_set_stack_size(locker_thread, 2048);
furi_thread_set_context(locker_thread, semaphore);
furi_thread_set_callback(locker_thread, storage_file_locker);
furi_thread_start(locker_thread); furi_thread_start(locker_thread);
// wait for file lock // wait for file lock
@ -133,11 +130,8 @@ MU_TEST(storage_dir_open_lock) {
File* file = storage_file_alloc(storage); File* file = storage_file_alloc(storage);
// file_locker thread start // file_locker thread start
FuriThread* locker_thread = furi_thread_alloc(); FuriThread* locker_thread =
furi_thread_set_name(locker_thread, "StorageDirLocker"); furi_thread_alloc_ex("StorageDirLocker", 2048, storage_dir_locker, semaphore);
furi_thread_set_stack_size(locker_thread, 2048);
furi_thread_set_context(locker_thread, semaphore);
furi_thread_set_callback(locker_thread, storage_dir_locker);
furi_thread_start(locker_thread); furi_thread_start(locker_thread);
// wait for dir lock // wait for dir lock

View File

@ -219,21 +219,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) {
mu_check(stream_eof(stream)); mu_check(stream_eof(stream));
mu_assert_int_eq(0, stream_tell(stream)); mu_assert_int_eq(0, stream_tell(stream));
// insert formated string at the end of stream // insert formatted string at the end of stream
// "" -> "dio666" // "" -> "dio666"
mu_check(stream_insert_format(stream, "%s%d", "dio", 666)); mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
mu_assert_int_eq(6, stream_size(stream)); mu_assert_int_eq(6, stream_size(stream));
mu_check(stream_eof(stream)); mu_check(stream_eof(stream));
mu_assert_int_eq(6, stream_tell(stream)); mu_assert_int_eq(6, stream_tell(stream));
// insert formated string at the end of stream // insert formatted string at the end of stream
// "dio666" -> "dio666zlo555" // "dio666" -> "dio666zlo555"
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555)); mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
mu_assert_int_eq(12, stream_size(stream)); mu_assert_int_eq(12, stream_size(stream));
mu_check(stream_eof(stream)); mu_check(stream_eof(stream));
mu_assert_int_eq(12, stream_tell(stream)); mu_assert_int_eq(12, stream_tell(stream));
// insert formated string at the 6 pos // insert formatted string at the 6 pos
// "dio666" -> "dio666baba13zlo555" // "dio666" -> "dio666baba13zlo555"
mu_check(stream_seek(stream, 6, StreamOffsetFromStart)); mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
mu_check(stream_insert_format(stream, "%s%d", "baba", 13)); mu_check(stream_insert_format(stream, "%s%d", "baba", 13));

View File

@ -13,7 +13,7 @@
#define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo")
#define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s")
#define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub")
#define TEST_RANDOM_COUNT_PARSE 232 #define TEST_RANDOM_COUNT_PARSE 244
#define TEST_TIMEOUT 10000 #define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler; static SubGhzEnvironment* environment_handler;
@ -209,6 +209,149 @@ MU_TEST(subghz_keystore_test) {
"Test keystore error"); "Test keystore error");
} }
typedef enum {
SubGhzHalAsyncTxTestTypeNormal,
SubGhzHalAsyncTxTestTypeInvalidStart,
SubGhzHalAsyncTxTestTypeInvalidMid,
SubGhzHalAsyncTxTestTypeInvalidEnd,
SubGhzHalAsyncTxTestTypeResetStart,
SubGhzHalAsyncTxTestTypeResetMid,
SubGhzHalAsyncTxTestTypeResetEnd,
} SubGhzHalAsyncTxTestType;
typedef struct {
SubGhzHalAsyncTxTestType type;
size_t pos;
} SubGhzHalAsyncTxTest;
#define SUBGHZ_HAL_TEST_DURATION 1
static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
SubGhzHalAsyncTxTest* test = context;
bool is_odd = test->pos % 2;
if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) {
if(test->pos == 0) {
test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
test->pos++;
return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) {
if(test->pos == 0) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
test->pos++;
return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
} else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
test->pos++;
return level_duration_reset();
} else {
furi_crash("Yield after reset");
}
} else {
furi_crash("Programming error");
}
}
bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
SubGhzHalAsyncTxTest test = {0};
test.type = type;
furi_hal_subghz_reset();
furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
furi_hal_subghz_set_frequency_and_path(433920000);
furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test);
while(!furi_hal_subghz_is_async_tx_complete()) {
furi_delay_ms(10);
}
furi_hal_subghz_stop_async_tx();
furi_hal_subghz_sleep();
return true;
}
MU_TEST(subghz_hal_async_tx_test) {
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal),
"Test furi_hal_async_tx normal");
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart),
"Test furi_hal_async_tx invalid start");
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid),
"Test furi_hal_async_tx invalid mid");
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd),
"Test furi_hal_async_tx invalid end");
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart),
"Test furi_hal_async_tx reset start");
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid),
"Test furi_hal_async_tx reset mid");
mu_assert(
subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd),
"Test furi_hal_async_tx reset end");
}
//test decoders //test decoders
MU_TEST(subghz_decoder_came_atomo_test) { MU_TEST(subghz_decoder_came_atomo_test) {
mu_assert( mu_assert(
@ -437,6 +580,13 @@ MU_TEST(subghz_decoder_clemsa_test) {
"Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
} }
MU_TEST(subghz_decoder_ansonic_test) {
mu_assert(
subghz_decoder_test(
EXT_PATH("unit_tests/subghz/ansonic_raw.sub"), SUBGHZ_PROTOCOL_ANSONIC_NAME),
"Test decoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
}
//test encoders //test encoders
MU_TEST(subghz_encoder_princeton_test) { MU_TEST(subghz_encoder_princeton_test) {
mu_assert( mu_assert(
@ -558,6 +708,12 @@ MU_TEST(subghz_encoder_clemsa_test) {
"Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); "Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n");
} }
MU_TEST(subghz_encoder_ansonic_test) {
mu_assert(
subghz_encoder_test(EXT_PATH("unit_tests/subghz/ansonic.sub")),
"Test encoder " SUBGHZ_PROTOCOL_ANSONIC_NAME " error\r\n");
}
MU_TEST(subghz_random_test) { MU_TEST(subghz_random_test) {
mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n");
} }
@ -566,6 +722,8 @@ MU_TEST_SUITE(subghz) {
subghz_test_init(); subghz_test_init();
MU_RUN_TEST(subghz_keystore_test); MU_RUN_TEST(subghz_keystore_test);
MU_RUN_TEST(subghz_hal_async_tx_test);
MU_RUN_TEST(subghz_decoder_came_atomo_test); MU_RUN_TEST(subghz_decoder_came_atomo_test);
MU_RUN_TEST(subghz_decoder_came_test); MU_RUN_TEST(subghz_decoder_came_test);
MU_RUN_TEST(subghz_decoder_came_twee_test); MU_RUN_TEST(subghz_decoder_came_twee_test);
@ -598,6 +756,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_decoder_magellan_test); MU_RUN_TEST(subghz_decoder_magellan_test);
MU_RUN_TEST(subghz_decoder_intertechno_v3_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test);
MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_decoder_clemsa_test);
MU_RUN_TEST(subghz_decoder_ansonic_test);
MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_princeton_test);
MU_RUN_TEST(subghz_encoder_came_test); MU_RUN_TEST(subghz_encoder_came_test);
@ -619,6 +778,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_magellan_test); MU_RUN_TEST(subghz_encoder_magellan_test);
MU_RUN_TEST(subghz_encoder_intertechno_v3_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test); MU_RUN_TEST(subghz_encoder_clemsa_test);
MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_random_test); MU_RUN_TEST(subghz_random_test);
subghz_test_deinit(); subghz_test_deinit();

View File

@ -265,8 +265,7 @@ void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {
offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;
} }
if(offset_new > 0) { if(offset_new > 0) {
offset_new = offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0);
CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0);
} else { } else {
offset_new = 0; offset_new = 0;
} }

View File

@ -3,9 +3,9 @@
#include "../archive_i.h" #include "../archive_i.h"
#include <storage/storage.h> #include <storage/storage.h>
#define TAB_RIGHT InputKeyRight // Default tab swith direction #define TAB_RIGHT InputKeyRight // Default tab switch direction
#define TAB_DEFAULT ArchiveTabFavorites // Start tab #define TAB_DEFAULT ArchiveTabFavorites // Start tab
#define FILE_LIST_BUF_LEN 100 #define FILE_LIST_BUF_LEN 50
static const char* tab_default_paths[] = { static const char* tab_default_paths[] = {
[ArchiveTabFavorites] = "/app:favorites", [ArchiveTabFavorites] = "/app:favorites",

View File

@ -657,12 +657,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
bad_usb->st.state = BadUsbStateInit; bad_usb->st.state = BadUsbStateInit;
bad_usb->st.error[0] = '\0'; bad_usb->st.error[0] = '\0';
bad_usb->thread = furi_thread_alloc(); bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
furi_thread_set_stack_size(bad_usb->thread, 2048);
furi_thread_set_context(bad_usb->thread, bad_usb);
furi_thread_set_callback(bad_usb->thread, bad_usb_worker);
furi_thread_start(bad_usb->thread); furi_thread_start(bad_usb->thread);
return bad_usb; return bad_usb;
} }

View File

@ -31,7 +31,7 @@ bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) {
auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key); auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key);
if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) { if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) {
FURI_LOG_W(TAG, "Cant find symbol '%s' (hash %lx)!", name, gnu_sym_hash); FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash);
result = false; result = false;
} else { } else {
result = true; result = true;

View File

@ -29,6 +29,7 @@ struct GpioApp {
GpioTest* gpio_test; GpioTest* gpio_test;
GpioUsbUart* gpio_usb_uart; GpioUsbUart* gpio_usb_uart;
UsbUartBridge* usb_uart_bridge; UsbUartBridge* usb_uart_bridge;
UsbUartConfig* usb_uart_cfg;
}; };
typedef enum { typedef enum {

View File

@ -9,4 +9,5 @@ typedef enum {
GpioCustomEventErrorBack, GpioCustomEventErrorBack,
GpioUsbUartEventConfig, GpioUsbUartEventConfig,
GpioUsbUartEventConfigSet,
} GpioCustomEvent; } GpioCustomEvent;

View File

@ -9,8 +9,6 @@ typedef enum {
UsbUartLineIndexFlow, UsbUartLineIndexFlow,
} LineIndex; } LineIndex;
static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"}; static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"}; static const char* uart_ch[] = {"13,14", "15,16"};
static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"}; static const char* flow_pins[] = {"None", "2,3", "6,7", "16,15"};
@ -28,8 +26,14 @@ static const uint32_t baudrate_list[] = {
}; };
bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) { bool gpio_scene_usb_uart_cfg_on_event(void* context, SceneManagerEvent event) {
UNUSED(context); GpioApp* app = context;
UNUSED(event); furi_assert(app);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GpioUsbUartEventConfigSet) {
usb_uart_set_config(app->usb_uart_bridge, app->usb_uart_cfg);
return true;
}
}
return false; return false;
} }
@ -38,55 +42,59 @@ void line_ensure_flow_invariant(GpioApp* app) {
// selected. This function enforces that invariant by resetting flow_pins // selected. This function enforces that invariant by resetting flow_pins
// to None if it is configured to 16,15 when LPUART is selected. // to None if it is configured to 16,15 when LPUART is selected.
uint8_t available_flow_pins = cfg_set->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4; uint8_t available_flow_pins = app->usb_uart_cfg->uart_ch == FuriHalUartIdLPUART1 ? 3 : 4;
VariableItem* item = app->var_item_flow; VariableItem* item = app->var_item_flow;
variable_item_set_values_count(item, available_flow_pins); variable_item_set_values_count(item, available_flow_pins);
if(cfg_set->flow_pins >= available_flow_pins) { if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {
cfg_set->flow_pins = 0; app->usb_uart_cfg->flow_pins = 0;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
} }
} }
static void line_vcp_cb(VariableItem* item) { static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vcp_ch[index]); variable_item_set_current_value_text(item, vcp_ch[index]);
cfg_set->vcp_ch = index; app->usb_uart_cfg->vcp_ch = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_port_cb(VariableItem* item) { static void line_port_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, uart_ch[index]); variable_item_set_current_value_text(item, uart_ch[index]);
if(index == 0) if(index == 0)
cfg_set->uart_ch = FuriHalUartIdUSART1; app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
else if(index == 1) else if(index == 1)
cfg_set->uart_ch = FuriHalUartIdLPUART1; app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
line_ensure_flow_invariant(app); line_ensure_flow_invariant(app);
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_flow_cb(VariableItem* item) { static void line_flow_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, flow_pins[index]); variable_item_set_current_value_text(item, flow_pins[index]);
cfg_set->flow_pins = index; app->usb_uart_cfg->flow_pins = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
static void line_baudrate_cb(VariableItem* item) { static void line_baudrate_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item); GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
char br_text[8]; char br_text[8];
@ -94,28 +102,29 @@ static void line_baudrate_cb(VariableItem* item) {
if(index > 0) { if(index > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]); snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
variable_item_set_current_value_text(item, br_text); variable_item_set_current_value_text(item, br_text);
cfg_set->baudrate = baudrate_list[index - 1]; app->usb_uart_cfg->baudrate = baudrate_list[index - 1];
} else { } else {
variable_item_set_current_value_text(item, baudrate_mode[index]); variable_item_set_current_value_text(item, baudrate_mode[index]);
cfg_set->baudrate = 0; app->usb_uart_cfg->baudrate = 0;
} }
cfg_set->baudrate_mode = index; app->usb_uart_cfg->baudrate_mode = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set); view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
} }
void gpio_scene_usb_uart_cfg_on_enter(void* context) { void gpio_scene_usb_uart_cfg_on_enter(void* context) {
GpioApp* app = context; GpioApp* app = context;
furi_assert(app);
VariableItemList* var_item_list = app->var_item_list; VariableItemList* var_item_list = app->var_item_list;
cfg_set = malloc(sizeof(UsbUartConfig)); app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));
usb_uart_get_config(app->usb_uart_bridge, cfg_set); usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);
VariableItem* item; VariableItem* item;
char br_text[8]; char br_text[8];
item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app); item = variable_item_list_add(var_item_list, "USB Channel", 2, line_vcp_cb, app);
variable_item_set_current_value_index(item, cfg_set->vcp_ch); variable_item_set_current_value_index(item, app->usb_uart_cfg->vcp_ch);
variable_item_set_current_value_text(item, vcp_ch[cfg_set->vcp_ch]); variable_item_set_current_value_text(item, vcp_ch[app->usb_uart_cfg->vcp_ch]);
item = variable_item_list_add( item = variable_item_list_add(
var_item_list, var_item_list,
@ -123,22 +132,23 @@ void gpio_scene_usb_uart_cfg_on_enter(void* context) {
sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1,
line_baudrate_cb, line_baudrate_cb,
app); app);
variable_item_set_current_value_index(item, cfg_set->baudrate_mode); variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode);
if(cfg_set->baudrate_mode > 0) { if(app->usb_uart_cfg->baudrate_mode > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]); snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);
variable_item_set_current_value_text(item, br_text); variable_item_set_current_value_text(item, br_text);
} else { } else {
variable_item_set_current_value_text(item, baudrate_mode[cfg_set->baudrate_mode]); variable_item_set_current_value_text(
item, baudrate_mode[app->usb_uart_cfg->baudrate_mode]);
} }
item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app); item = variable_item_list_add(var_item_list, "UART Pins", 2, line_port_cb, app);
variable_item_set_current_value_index(item, cfg_set->uart_ch); variable_item_set_current_value_index(item, app->usb_uart_cfg->uart_ch);
variable_item_set_current_value_text(item, uart_ch[cfg_set->uart_ch]); variable_item_set_current_value_text(item, uart_ch[app->usb_uart_cfg->uart_ch]);
item = variable_item_list_add( item = variable_item_list_add(
var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app); var_item_list, "RTS/DTR Pins", COUNT_OF(flow_pins), line_flow_cb, app);
variable_item_set_current_value_index(item, cfg_set->flow_pins); variable_item_set_current_value_index(item, app->usb_uart_cfg->flow_pins);
variable_item_set_current_value_text(item, flow_pins[cfg_set->flow_pins]); variable_item_set_current_value_text(item, flow_pins[app->usb_uart_cfg->flow_pins]);
app->var_item_flow = item; app->var_item_flow = item;
line_ensure_flow_invariant(app); line_ensure_flow_invariant(app);
@ -155,5 +165,5 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) {
GpioAppViewUsbUartCfg, GpioAppViewUsbUartCfg,
variable_item_list_get_selected_item_index(app->var_item_list)); variable_item_list_get_selected_item_index(app->var_item_list));
variable_item_list_reset(app->var_item_list); variable_item_list_reset(app->var_item_list);
free(cfg_set); free(app->usb_uart_cfg);
} }

View File

@ -3,6 +3,7 @@
#include <furi_hal_usb_cdc.h> #include <furi_hal_usb_cdc.h>
#include "usb_cdc.h" #include "usb_cdc.h"
#include "cli/cli_vcp.h" #include "cli/cli_vcp.h"
#include <toolbox/api_lock.h>
#include "cli/cli.h" #include "cli/cli.h"
#define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_CDC_PKT_LEN CDC_DATA_SZ
@ -51,6 +52,8 @@ struct UsbUartBridge {
UsbUartState st; UsbUartState st;
FuriApiLock cfg_lock;
uint8_t rx_buf[USB_CDC_PKT_LEN]; uint8_t rx_buf[USB_CDC_PKT_LEN];
}; };
@ -159,11 +162,8 @@ static int32_t usb_uart_worker(void* context) {
usb_uart->tx_sem = furi_semaphore_alloc(1, 1); usb_uart->tx_sem = furi_semaphore_alloc(1, 1);
usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal); usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
usb_uart->tx_thread = furi_thread_alloc(); usb_uart->tx_thread =
furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker"); furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart);
furi_thread_set_stack_size(usb_uart->tx_thread, 512);
furi_thread_set_context(usb_uart->tx_thread, usb_uart);
furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread);
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch);
@ -247,6 +247,7 @@ static int32_t usb_uart_worker(void* context) {
usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins; usb_uart->cfg.flow_pins = usb_uart->cfg_new.flow_pins;
events |= WorkerEvtCtrlLineSet; events |= WorkerEvtCtrlLineSet;
} }
api_lock_unlock(usb_uart->cfg_lock);
} }
if(events & WorkerEvtLineCfgSet) { if(events & WorkerEvtLineCfgSet) {
if(usb_uart->cfg.baudrate == 0) if(usb_uart->cfg.baudrate == 0)
@ -338,11 +339,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
usb_uart->thread = furi_thread_alloc(); usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart);
furi_thread_set_name(usb_uart->thread, "UsbUartWorker");
furi_thread_set_stack_size(usb_uart->thread, 1024);
furi_thread_set_context(usb_uart->thread, usb_uart);
furi_thread_set_callback(usb_uart->thread, usb_uart_worker);
furi_thread_start(usb_uart->thread); furi_thread_start(usb_uart->thread);
return usb_uart; return usb_uart;
@ -359,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) {
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
furi_assert(usb_uart); furi_assert(usb_uart);
furi_assert(cfg); furi_assert(cfg);
usb_uart->cfg_lock = api_lock_alloc_locked();
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig)); memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange); furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
api_lock_wait_unlock_and_free(usb_uart->cfg_lock);
} }
void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) { void usb_uart_get_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {

View File

@ -24,7 +24,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Power_hvr_25x27, &I_Power_hvr_25x27,
infrared_scene_universal_common_item_callback, infrared_scene_universal_common_item_callback,
context); context);
infrared_brute_force_add_record(brute_force, i++, "POWER"); infrared_brute_force_add_record(brute_force, i++, "Power");
button_panel_add_item( button_panel_add_item(
button_panel, button_panel,
i, i,
@ -36,7 +36,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Mute_hvr_25x27, &I_Mute_hvr_25x27,
infrared_scene_universal_common_item_callback, infrared_scene_universal_common_item_callback,
context); context);
infrared_brute_force_add_record(brute_force, i++, "MUTE"); infrared_brute_force_add_record(brute_force, i++, "Mute");
button_panel_add_item( button_panel_add_item(
button_panel, button_panel,
i, i,
@ -48,7 +48,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Vol_up_hvr_25x27, &I_Vol_up_hvr_25x27,
infrared_scene_universal_common_item_callback, infrared_scene_universal_common_item_callback,
context); context);
infrared_brute_force_add_record(brute_force, i++, "VOL+"); infrared_brute_force_add_record(brute_force, i++, "Vol_up");
button_panel_add_item( button_panel_add_item(
button_panel, button_panel,
i, i,
@ -60,7 +60,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Up_hvr_25x27, &I_Up_hvr_25x27,
infrared_scene_universal_common_item_callback, infrared_scene_universal_common_item_callback,
context); context);
infrared_brute_force_add_record(brute_force, i++, "CH+"); infrared_brute_force_add_record(brute_force, i++, "Ch_next");
button_panel_add_item( button_panel_add_item(
button_panel, button_panel,
i, i,
@ -72,7 +72,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Vol_down_hvr_25x27, &I_Vol_down_hvr_25x27,
infrared_scene_universal_common_item_callback, infrared_scene_universal_common_item_callback,
context); context);
infrared_brute_force_add_record(brute_force, i++, "VOL-"); infrared_brute_force_add_record(brute_force, i++, "Vol_dn");
button_panel_add_item( button_panel_add_item(
button_panel, button_panel,
i, i,
@ -84,7 +84,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Down_hvr_25x27, &I_Down_hvr_25x27,
infrared_scene_universal_common_item_callback, infrared_scene_universal_common_item_callback,
context); context);
infrared_brute_force_add_record(brute_force, i++, "CH-"); infrared_brute_force_add_record(brute_force, i++, "Ch_prev");
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote"); button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol"); button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");

View File

@ -21,6 +21,7 @@ ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate)
ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth) ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth)
ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult) ADD_SCENE(nfc, mf_ultralight_read_auth_result, MfUltralightReadAuthResult)
ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput)
ADD_SCENE(nfc, mf_ultralight_unlock_auto, MfUltralightUnlockAuto)
ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu)
ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn)
ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess) ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess)

View File

@ -4,6 +4,7 @@
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexSave, SubmenuIndexSave,
SubmenuIndexEmulate, SubmenuIndexEmulate,
SubmenuIndexDetectReader,
SubmenuIndexInfo, SubmenuIndexInfo,
}; };
@ -21,6 +22,14 @@ void nfc_scene_mf_classic_menu_on_enter(void* context) {
submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc); submenu, "Save", SubmenuIndexSave, nfc_scene_mf_classic_menu_submenu_callback, nfc);
submenu_add_item( submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc); submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_mf_classic_menu_submenu_callback, nfc);
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
submenu_add_item(
submenu,
"Detect reader",
SubmenuIndexDetectReader,
nfc_scene_mf_classic_menu_submenu_callback,
nfc);
}
submenu_add_item( submenu_add_item(
submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc); submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_classic_menu_submenu_callback, nfc);
@ -35,17 +44,14 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu, event.event);
if(event.event == SubmenuIndexSave) { if(event.event == SubmenuIndexSave) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic; nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
// Clear device name // Clear device name
nfc_device_set_name(nfc->dev, ""); nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexEmulate) { } else if(event.event == SubmenuIndexEmulate) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate); DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
@ -53,9 +59,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
DOLPHIN_DEED(DolphinDeedNfcEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate);
} }
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
consumed = true;
} else if(event.event == SubmenuIndexInfo) { } else if(event.event == SubmenuIndexInfo) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo);
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
consumed = true; consumed = true;
} }

View File

@ -19,10 +19,10 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
Submenu* submenu = nfc->submenu; Submenu* submenu = nfc->submenu;
MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data; MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data;
if(data->data_read != data->data_size) { if(!mf_ul_is_full_capture(data)) {
submenu_add_item( submenu_add_item(
submenu, submenu,
"Unlock With Password", "Unlock",
SubmenuIndexUnlock, SubmenuIndexUnlock,
nfc_scene_mf_ultralight_menu_submenu_callback, nfc_scene_mf_ultralight_menu_submenu_callback,
nfc); nfc);

View File

@ -24,25 +24,29 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
if(curr_state != state) { if(curr_state != state) {
if(state == NfcSceneMfUlReadStateDetecting) { if(state == NfcSceneMfUlReadStateDetecting) {
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_text( popup_set_text(nfc->popup, "Apply the\ntarget card", 97, 24, AlignCenter, AlignTop);
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50); popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
nfc_blink_read_start(nfc);
} else if(state == NfcSceneMfUlReadStateReading) { } else if(state == NfcSceneMfUlReadStateReading) {
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_header( popup_set_header(
nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop); nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 12, 23, &A_Loading_24); popup_set_icon(nfc->popup, 12, 23, &A_Loading_24);
nfc_blink_detect_start(nfc);
} else if(state == NfcSceneMfUlReadStateNotSupportedCard) { } else if(state == NfcSceneMfUlReadStateNotSupportedCard) {
popup_reset(nfc->popup); popup_reset(nfc->popup);
popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop); popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop);
popup_set_text( popup_set_text(
nfc->popup, nfc->popup,
"Only MIFARE\nUltralight & NTAG\n are supported", "Only MIFARE\nUltralight & NTAG\nare supported",
4, 4,
22, 22,
AlignLeft, AlignLeft,
AlignTop); AlignTop);
popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48); popup_set_icon(nfc->popup, 73, 20, &I_DolphinCommon_56x48);
nfc_blink_stop(nfc);
notification_message(nfc->notifications, &sequence_error);
notification_message(nfc->notifications, &sequence_set_red_255);
} }
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state); scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadAuth, state);
} }
@ -62,8 +66,6 @@ void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) {
&nfc->dev->dev_data, &nfc->dev->dev_data,
nfc_scene_mf_ultralight_read_auth_worker_callback, nfc_scene_mf_ultralight_read_auth_worker_callback,
nfc); nfc);
nfc_blink_read_start(nfc);
} }
bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) { bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent event) {
@ -86,8 +88,17 @@ bool nfc_scene_mf_ultralight_read_auth_on_event(void* context, SceneManagerEvent
nfc, NfcSceneMfUlReadStateNotSupportedCard); nfc, NfcSceneMfUlReadStateNotSupportedCard);
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene( MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); NfcScene next_scene;
if(mf_ul_data->auth_method == MfUltralightAuthMethodManual) {
next_scene = NfcSceneMfUltralightKeyInput;
} else if(mf_ul_data->auth_method == MfUltralightAuthMethodAuto) {
next_scene = NfcSceneMfUltralightUnlockAuto;
} else {
next_scene = NfcSceneMfUltralightUnlockMenu;
}
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene);
} }
return consumed; return consumed;
} }

View File

@ -19,16 +19,20 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data; MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data); MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data);
Widget* widget = nfc->widget; Widget* widget = nfc->widget;
const char* title;
FuriString* temp_str; FuriString* temp_str;
temp_str = furi_string_alloc(); temp_str = furi_string_alloc();
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) { if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
widget_add_string_element( if(mf_ul_data->auth_success) {
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!"); title = "All pages are unlocked!";
} else {
title = "All unlocked but failed auth!";
}
} else { } else {
widget_add_string_element( title = "Not all pages unlocked!";
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!");
} }
widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title);
furi_string_set(temp_str, "UID:"); furi_string_set(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; i++) { 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, " %02X", nfc_data->uid[i]);
@ -65,6 +69,7 @@ void nfc_scene_mf_ultralight_read_auth_result_on_enter(void* context) {
nfc); nfc);
furi_string_free(temp_str); furi_string_free(temp_str);
notification_message(nfc->notifications, &sequence_set_green_255);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
} }
@ -81,8 +86,21 @@ bool nfc_scene_mf_ultralight_read_auth_result_on_event(void* context, SceneManag
consumed = true; consumed = true;
} }
} else if(event.type == SceneManagerEventTypeBack) { } else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene( MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); if(mf_ul_data->auth_method == MfUltralightAuthMethodManual ||
mf_ul_data->auth_method == MfUltralightAuthMethodAuto) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
} else {
NfcScene next_scene;
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
next_scene = NfcSceneMfUltralightMenu;
} else {
next_scene = NfcSceneMfUltralightUnlockMenu;
}
consumed =
scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, next_scene);
}
} }
return consumed; return consumed;
@ -93,4 +111,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) {
// Clean views // Clean views
widget_reset(nfc->widget); widget_reset(nfc->widget);
notification_message_block(nfc->notifications, &sequence_reset_green);
} }

View File

@ -0,0 +1,64 @@
#include "../nfc_i.h"
bool nfc_scene_mf_ultralight_unlock_auto_worker_callback(NfcWorkerEvent event, void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
return true;
}
void nfc_scene_mf_ultralight_unlock_auto_on_enter(void* context) {
Nfc* nfc = context;
// Setup view
widget_add_string_multiline_element(
nfc->widget,
54,
30,
AlignLeft,
AlignCenter,
FontPrimary,
"Touch the\nreader to get\npassword...");
widget_add_icon_element(nfc->widget, 0, 15, &I_Modern_reader_18x34);
widget_add_icon_element(nfc->widget, 20, 12, &I_Move_flipper_26x39);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
// Start worker
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfUltralightEmulate,
&nfc->dev->dev_data,
nfc_scene_mf_ultralight_unlock_auto_worker_callback,
nfc);
nfc_blink_read_start(nfc);
}
bool nfc_scene_mf_ultralight_unlock_auto_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if((event.event == NfcWorkerEventMfUltralightPwdAuth)) {
MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth;
memcpy(nfc->byte_input_store, auth->pwd.raw, sizeof(auth->pwd.raw));
nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodAuto;
nfc_worker_stop(nfc->worker);
notification_message(nfc->notifications, &sequence_success);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_ultralight_unlock_auto_on_exit(void* context) {
Nfc* nfc = context;
// Stop worker
nfc_worker_stop(nfc->worker);
// Clear view
widget_reset(nfc->widget);
nfc_blink_stop(nfc);
}

View File

@ -1,9 +1,10 @@
#include "../nfc_i.h" #include "../nfc_i.h"
enum SubmenuIndex { enum SubmenuIndex {
SubmenuIndexMfUlUnlockMenuManual, SubmenuIndexMfUlUnlockMenuAuto,
SubmenuIndexMfUlUnlockMenuAmeebo, SubmenuIndexMfUlUnlockMenuAmeebo,
SubmenuIndexMfUlUnlockMenuXiaomi, SubmenuIndexMfUlUnlockMenuXiaomi,
SubmenuIndexMfUlUnlockMenuManual,
}; };
void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) { void nfc_scene_mf_ultralight_unlock_menu_submenu_callback(void* context, uint32_t index) {
@ -18,12 +19,14 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
uint32_t state = uint32_t state =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
submenu_add_item( if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) {
submenu, submenu_add_item(
"Enter Password Manually", submenu,
SubmenuIndexMfUlUnlockMenuManual, "Unlock With Reader",
nfc_scene_mf_ultralight_unlock_menu_submenu_callback, SubmenuIndexMfUlUnlockMenuAuto,
nfc); nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc);
}
submenu_add_item( submenu_add_item(
submenu, submenu,
"Auth As Ameebo", "Auth As Ameebo",
@ -32,10 +35,16 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
nfc); nfc);
submenu_add_item( submenu_add_item(
submenu, submenu,
"Auth As Xiaomi", "Auth As Xiaomi Air Purifier",
SubmenuIndexMfUlUnlockMenuXiaomi, SubmenuIndexMfUlUnlockMenuXiaomi,
nfc_scene_mf_ultralight_unlock_menu_submenu_callback, nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc); nfc);
submenu_add_item(
submenu,
"Enter Password Manually",
SubmenuIndexMfUlUnlockMenuManual,
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc);
submenu_set_selected_item(submenu, state); submenu_set_selected_item(submenu, state);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
} }
@ -57,8 +66,12 @@ bool nfc_scene_mf_ultralight_unlock_menu_on_event(void* context, SceneManagerEve
nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi; nfc->dev->dev_data.mf_ul_data.auth_method = MfUltralightAuthMethodXiaomi;
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexMfUlUnlockMenuAuto) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
consumed = true;
} }
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu, event.event);
} }
return consumed; return consumed;
} }

View File

@ -10,15 +10,43 @@ void nfc_scene_mf_ultralight_unlock_warn_dialog_callback(DialogExResult result,
void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) { void nfc_scene_mf_ultralight_unlock_warn_on_enter(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
DialogEx* dialog_ex = nfc->dialog_ex; DialogEx* dialog_ex = nfc->dialog_ex;
MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method;
dialog_ex_set_context(dialog_ex, nfc); dialog_ex_set_context(dialog_ex, nfc);
dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback); dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback);
dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop); if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) {
dialog_ex_set_text( // Build dialog text
dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop); MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth;
dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48); FuriString* password_str =
dialog_ex_set_center_button_text(dialog_ex, "OK"); furi_string_alloc_set_str("Try to unlock the card with\npassword: ");
for(size_t i = 0; i < sizeof(auth->pwd.raw); ++i) {
furi_string_cat_printf(password_str, "%02X ", nfc->byte_input_store[i]);
}
furi_string_cat_str(password_str, "?\nCaution, a wrong password\ncan block the card!");
nfc_text_store_set(nfc, furi_string_get_cstr(password_str));
furi_string_free(password_str);
dialog_ex_set_header(
dialog_ex,
auth_method == MfUltralightAuthMethodAuto ? "Password captured!" : "Risky function!",
64,
0,
AlignCenter,
AlignTop);
dialog_ex_set_text(dialog_ex, nfc->text_store, 64, 12, AlignCenter, AlignTop);
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Continue");
if(auth_method == MfUltralightAuthMethodAuto)
notification_message(nfc->notifications, &sequence_set_green_255);
} else {
dialog_ex_set_header(dialog_ex, "Risky function!", 64, 4, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Wrong password\ncan block your\ncard.", 4, 18, AlignLeft, AlignTop);
dialog_ex_set_icon(dialog_ex, 73, 20, &I_DolphinCommon_56x48);
dialog_ex_set_center_button_text(dialog_ex, "OK");
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
} }
@ -28,12 +56,33 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
bool consumed = false; bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method;
if(event.event == DialogExResultCenter) { if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth); if(event.type == SceneManagerEventTypeCustom) {
DOLPHIN_DEED(DolphinDeedNfcRead); if(event.event == DialogExResultRight) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
DOLPHIN_DEED(DolphinDeedNfcRead);
consumed = true;
} else if(event.event == DialogExResultLeft) {
if(auth_method == MfUltralightAuthMethodAuto) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
} else {
consumed = scene_manager_previous_scene(nfc->scene_manager);
}
}
} else if(event.type == SceneManagerEventTypeBack) {
// Cannot press back
consumed = true; consumed = true;
} }
} else {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
DOLPHIN_DEED(DolphinDeedNfcRead);
consumed = true;
}
}
} }
return consumed; return consumed;
@ -43,5 +92,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) {
Nfc* nfc = context; Nfc* nfc = context;
dialog_ex_reset(nfc->dialog_ex); dialog_ex_reset(nfc->dialog_ex);
submenu_reset(nfc->submenu); nfc_text_store_clear(nfc);
notification_message_block(nfc->notifications, &sequence_reset_green);
} }

View File

@ -87,6 +87,20 @@ void nfc_scene_nfc_data_info_on_enter(void* context) {
temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4); temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4);
if(data->data_size > data->data_read) { if(data->data_size > data->data_read) {
furi_string_cat_printf(temp_str, "\nPassword-protected"); furi_string_cat_printf(temp_str, "\nPassword-protected");
} else if(data->auth_success) {
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(data);
furi_string_cat_printf(
temp_str,
"\nPassword: %02X %02X %02X %02X",
config_pages->auth_data.pwd.raw[0],
config_pages->auth_data.pwd.raw[1],
config_pages->auth_data.pwd.raw[2],
config_pages->auth_data.pwd.raw[3]);
furi_string_cat_printf(
temp_str,
"\nPACK: %02X %02X",
config_pages->auth_data.pack.raw[0],
config_pages->auth_data.pack.raw[1]);
} }
} else if(protocol == NfcDeviceProtocolMifareClassic) { } else if(protocol == NfcDeviceProtocolMifareClassic) {
MfClassicData* data = &dev_data->mf_classic_data; MfClassicData* data = &dev_data->mf_classic_data;
@ -115,7 +129,7 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
if(event.type == SceneManagerEventTypeCustom) { if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeRight) { if(event.event == GuiButtonTypeRight) {
if(protocol == NfcDeviceProtocolMifareDesfire) { if(protocol == NfcDeviceProtocolMifareDesfire) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData);
consumed = true; consumed = true;
} else if(protocol == NfcDeviceProtocolMifareUl) { } else if(protocol == NfcDeviceProtocolMifareUl) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);

View File

@ -70,6 +70,8 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
} else if(event.event == NfcWorkerEventReadMfUltralight) { } else if(event.event == NfcWorkerEventReadMfUltralight) {
notification_message(nfc->notifications, &sequence_success); notification_message(nfc->notifications, &sequence_success);
// Set unlock password input to 0xFFFFFFFF only on fresh read
memset(nfc->byte_input_store, 0xFF, 4);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true; consumed = true;

View File

@ -11,6 +11,8 @@ enum SubmenuIndex {
SubmenuIndexDelete, SubmenuIndexDelete,
SubmenuIndexInfo, SubmenuIndexInfo,
SubmenuIndexRestoreOriginal, SubmenuIndexRestoreOriginal,
SubmenuIndexMfUlUnlockByReader,
SubmenuIndexMfUlUnlockByPassword,
}; };
void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) { void nfc_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
@ -69,6 +71,21 @@ void nfc_scene_saved_menu_on_enter(void* context) {
} }
submenu_add_item( submenu_add_item(
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc); submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
if(nfc->dev->format == NfcDeviceSaveFormatMifareUl &&
!mf_ul_is_full_capture(&nfc->dev->dev_data.mf_ul_data)) {
submenu_add_item(
submenu,
"Unlock With Reader",
SubmenuIndexMfUlUnlockByReader,
nfc_scene_saved_menu_submenu_callback,
nfc);
submenu_add_item(
submenu,
"Unlock With Password",
SubmenuIndexMfUlUnlockByPassword,
nfc_scene_saved_menu_submenu_callback,
nfc);
}
if(nfc->dev->shadow_file_exist) { if(nfc->dev->shadow_file_exist) {
submenu_add_item( submenu_add_item(
submenu, submenu,
@ -106,6 +123,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexDetectReader) { } else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexWrite) { } else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite); scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);
@ -141,6 +159,12 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexRestoreOriginal) { } else if(event.event == SubmenuIndexRestoreOriginal) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm); scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);
consumed = true; consumed = true;
} else if(event.event == SubmenuIndexMfUlUnlockByReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockAuto);
consumed = true;
} else if(event.event == SubmenuIndexMfUlUnlockByPassword) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
consumed = true;
} }
} }

View File

@ -59,11 +59,8 @@ SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) {
instance->cli = cli; instance->cli = cli;
instance->thread = furi_thread_alloc(); instance->thread =
furi_thread_set_name(instance->thread, "SubGhzChat"); furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance);
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_callback(instance->thread, subghz_chat_worker_thread);
instance->subghz_txrx = subghz_tx_rx_worker_alloc(); instance->subghz_txrx = subghz_tx_rx_worker_alloc();
instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent)); instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent));
return instance; return instance;

View File

@ -25,7 +25,7 @@ TUPLE_DEF2(
(frequency, uint32_t), (frequency, uint32_t),
(count, uint8_t), (count, uint8_t),
(rssi_max, uint8_t)) (rssi_max, uint8_t))
/* Register globaly the oplist */ /* Register globally the oplist */
#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ #define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \
TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST)

View File

@ -5,8 +5,6 @@
#define TAG "SubghzFrequencyAnalyzerWorker" #define TAG "SubghzFrequencyAnalyzerWorker"
#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -95.0f
static const uint8_t subghz_preset_ook_58khz[][2] = { static const uint8_t subghz_preset_ook_58khz[][2] = {
{CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz {CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz
/* End */ /* End */
@ -71,7 +69,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
.frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0}; .frequency_coarse = 0, .rssi_coarse = 0, .frequency_fine = 0, .rssi_fine = 0};
float rssi = 0; float rssi = 0;
uint32_t frequency = 0; uint32_t frequency = 0;
float rssi_temp = 0; float rssi_temp = -127.0f;
uint32_t frequency_temp = 0; uint32_t frequency_temp = 0;
CC1101Status status; CC1101Status status;
@ -196,7 +194,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine); TAG, "=:%lu:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine);
instance->sample_hold_counter = 20; instance->sample_hold_counter = 20;
rssi_temp = frequency_rssi.rssi_fine; rssi_temp = (rssi_temp + frequency_rssi.rssi_fine) / 2;
frequency_temp = frequency_rssi.frequency_fine; frequency_temp = frequency_rssi.frequency_fine;
if(instance->filVal) { if(instance->filVal) {
@ -207,10 +205,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// Deliver callback // Deliver callback
if(instance->pair_callback) { if(instance->pair_callback) {
instance->pair_callback( instance->pair_callback(
instance->context, instance->context, frequency_rssi.frequency_fine, rssi_temp, true);
frequency_rssi.frequency_fine,
frequency_rssi.rssi_fine,
true);
} }
} else if( // Deliver results coarse } else if( // Deliver results coarse
(frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) && (frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) &&
@ -222,7 +217,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
(double)frequency_rssi.rssi_coarse); (double)frequency_rssi.rssi_coarse);
instance->sample_hold_counter = 20; instance->sample_hold_counter = 20;
rssi_temp = frequency_rssi.rssi_coarse; rssi_temp = (rssi_temp + frequency_rssi.rssi_coarse) / 2;
frequency_temp = frequency_rssi.frequency_coarse; frequency_temp = frequency_rssi.frequency_coarse;
if(instance->filVal) { if(instance->filVal) {
frequency_rssi.frequency_coarse = frequency_rssi.frequency_coarse =
@ -232,15 +227,12 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
// Deliver callback // Deliver callback
if(instance->pair_callback) { if(instance->pair_callback) {
instance->pair_callback( instance->pair_callback(
instance->context, instance->context, frequency_rssi.frequency_coarse, rssi_temp, true);
frequency_rssi.frequency_coarse,
frequency_rssi.rssi_coarse,
true);
} }
} else { } else {
if(instance->sample_hold_counter > 0) { if(instance->sample_hold_counter > 0) {
instance->sample_hold_counter--; instance->sample_hold_counter--;
if(instance->sample_hold_counter == 18) { if(instance->sample_hold_counter == 15) {
if(instance->pair_callback) { if(instance->pair_callback) {
instance->pair_callback( instance->pair_callback(
instance->context, frequency_temp, rssi_temp, false); instance->context, frequency_temp, rssi_temp, false);
@ -248,8 +240,8 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
} }
} else { } else {
instance->filVal = 0; instance->filVal = 0;
if(instance->pair_callback) rssi_temp = -127.0f;
instance->pair_callback(instance->context, 0, 0, false); instance->pair_callback(instance->context, 0, 0, false);
} }
} }
} }
@ -265,12 +257,8 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont
furi_assert(context); furi_assert(context);
SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker)); SubGhzFrequencyAnalyzerWorker* instance = malloc(sizeof(SubGhzFrequencyAnalyzerWorker));
instance->thread = furi_thread_alloc(); instance->thread = furi_thread_alloc_ex(
furi_thread_set_name(instance->thread, "SubGhzFAWorker"); "SubGhzFAWorker", 2048, subghz_frequency_analyzer_worker_thread, instance);
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_callback(instance->thread, subghz_frequency_analyzer_worker_thread);
SubGhz* subghz = context; SubGhz* subghz = context;
instance->setting = subghz->setting; instance->setting = subghz->setting;
return instance; return instance;

View File

@ -3,6 +3,8 @@
#include <furi_hal.h> #include <furi_hal.h>
#include "../subghz_i.h" #include "../subghz_i.h"
#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -93.0f
typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker; typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker;
typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)( typedef void (*SubGhzFrequencyAnalyzerWorkerPairCallback)(

View File

@ -300,7 +300,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
furi_hal_power_suppress_charge_exit(); furi_hal_power_suppress_charge_exit();
printf("\r\nPackets recieved %u\r\n", instance->packet_count); printf("\r\nPackets received %u\r\n", instance->packet_count);
// Cleanup // Cleanup
subghz_receiver_free(receiver); subghz_receiver_free(receiver);
@ -408,7 +408,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
} }
} }
printf("\r\nPackets recieved \033[0;32m%u\033[0m\r\n", instance->packet_count); printf("\r\nPackets received \033[0;32m%u\033[0m\r\n", instance->packet_count);
// Cleanup // Cleanup
subghz_receiver_free(receiver); subghz_receiver_free(receiver);
@ -438,7 +438,7 @@ static void subghz_cli_command_print_usage() {
printf("\r\n"); printf("\r\n");
printf(" debug cmd:\r\n"); printf(" debug cmd:\r\n");
printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n"); printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
printf("\trx_carrier <frequency:in Hz>\t - Receiv carrier\r\n"); printf("\trx_carrier <frequency:in Hz>\t - Receive carrier\r\n");
printf( printf(
"\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n"); "\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
printf( printf(

View File

@ -13,8 +13,6 @@
#include <assets_icons.h> #include <assets_icons.h>
#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem) #define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem)
#define RSSI_OFFSET 74
#define RSSI_MAX 53 // 127 - RSSI_OFFSET
#define SNPRINTF_FREQUENCY(buff, freq) \ #define SNPRINTF_FREQUENCY(buff, freq) \
snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000); snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000);
@ -49,7 +47,7 @@ typedef struct {
} SubGhzFrequencyAnalyzerModel; } SubGhzFrequencyAnalyzerModel;
static inline uint8_t rssi_sanitize(float rssi) { static inline uint8_t rssi_sanitize(float rssi) {
return (rssi * -1.0f) - RSSI_OFFSET; return (rssi ? (uint8_t)(rssi - SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) : 0);
} }
void subghz_frequency_analyzer_set_callback( void subghz_frequency_analyzer_set_callback(
@ -65,12 +63,25 @@ void subghz_frequency_analyzer_set_callback(
void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
uint8_t column_number = 0; uint8_t column_number = 0;
if(rssi) { if(rssi) {
rssi = rssi / 3; rssi = rssi / 3 + 2;
if(rssi > 20) rssi = 20;
for(uint8_t i = 1; i < rssi; i++) { for(uint8_t i = 1; i < rssi; i++) {
if(i > 20) break;
if(i % 4) { if(i % 4) {
column_number++; column_number++;
canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number); canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, column_number);
}
}
}
}
void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) {
uint8_t column_height = 6;
if(rssi) {
//rssi = rssi
if(rssi > 54) rssi = 54;
for(uint8_t i = 1; i < rssi; i++) {
if(i % 5) {
canvas_draw_box(canvas, x + i, y - column_height, 1, column_height);
} }
} }
} }
@ -86,9 +97,9 @@ static void subghz_frequency_analyzer_log_frequency_draw(
const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency);
if(items_count == 0) { if(items_count == 0) {
canvas_draw_rframe(canvas, offset_x + 27u, offset_y - 3u, 73u, 16u, 5u); canvas_draw_rframe(canvas, offset_x + 27, offset_y - 3, 73, 16, 5);
canvas_draw_str_aligned( canvas_draw_str_aligned(
canvas, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records"); canvas, offset_x + 64, offset_y + 8, AlignCenter, AlignBottom, "No records");
return; return;
} else if(items_count > 3) { } else if(items_count > 3) {
elements_scrollbar_pos( elements_scrollbar_pos(
@ -117,7 +128,7 @@ static void subghz_frequency_analyzer_log_frequency_draw(
canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer); canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer);
// Max RSSI // Max RSSI
subghz_frequency_analyzer_draw_rssi( subghz_frequency_analyzer_draw_log_rssi(
canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10)); canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10));
} }
@ -167,25 +178,20 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
} else { } else {
canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");
canvas_draw_str(canvas, 0, 64, "RSSI"); canvas_draw_str(canvas, 0, 64, "RSSI");
subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20u, 64u); subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20, 64);
subghz_frequency_analyzer_history_frequency_draw(canvas, model); subghz_frequency_analyzer_history_frequency_draw(canvas, model);
} }
// Frequency // Frequency
canvas_set_font(canvas, FontBigNumbers); canvas_set_font(canvas, FontBigNumbers);
snprintf( SNPRINTF_FREQUENCY(buffer, model->frequency);
buffer,
sizeof(buffer),
"%03ld.%03ld",
model->frequency / 1000000 % 1000,
model->frequency / 1000 % 1000);
if(model->signal) { if(model->signal) {
canvas_draw_box(canvas, 4, 12, 121, 22); canvas_draw_box(canvas, 4, 11, 121, 22);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
canvas_draw_str(canvas, 8, 30, buffer); canvas_draw_str(canvas, 8, 29, buffer);
canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); canvas_draw_icon(canvas, 96, 18, &I_MHz_25x11);
} }
static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) { static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) {
@ -292,7 +298,7 @@ static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyz
return false; return false;
} }
(*item)->frequency = model->frequency; (*item)->frequency = model->frequency;
(*item)->count = 1u; (*item)->count = 1;
(*item)->rssi_max = model->rssi; (*item)->rssi_max = model->rssi;
(*item)->seq = items_count; (*item)->seq = items_count;
return true; return true;
@ -394,9 +400,9 @@ void subghz_frequency_analyzer_enter(void* context) {
model->frequency = 0; model->frequency = 0;
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;
model->log_frequency_scroll_offset = 0u; model->log_frequency_scroll_offset = 0;
model->history_frequency[0] = model->history_frequency[1] = model->history_frequency[0] = model->history_frequency[1] =
model->history_frequency[2] = 0u; model->history_frequency[2] = 0;
SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency); SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency);
}, },
true); true);
@ -416,13 +422,13 @@ void subghz_frequency_analyzer_exit(void* context) {
instance->view, instance->view,
SubGhzFrequencyAnalyzerModel * model, SubGhzFrequencyAnalyzerModel * model,
{ {
model->rssi = 0u; model->rssi = 0;
model->frequency = 0; model->frequency = 0;
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc;
model->log_frequency_scroll_offset = 0u; model->log_frequency_scroll_offset = 0;
model->history_frequency[0] = model->history_frequency[1] = model->history_frequency[0] = model->history_frequency[1] =
model->history_frequency[2] = 0u; model->history_frequency[2] = 0;
SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency); SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency);
}, },
true); true);

View File

@ -58,13 +58,13 @@ struct U2fHid_packet {
struct U2fHid { struct U2fHid {
FuriThread* thread; FuriThread* thread;
FuriTimer* lock_timer; FuriTimer* lock_timer;
struct U2fHid_packet packet;
uint8_t seq_id_last; uint8_t seq_id_last;
uint16_t req_buf_ptr; uint16_t req_buf_ptr;
uint32_t req_len_left; uint32_t req_len_left;
uint32_t lock_cid; uint32_t lock_cid;
bool lock; bool lock;
U2fData* u2f_instance; U2fData* u2f_instance;
struct U2fHid_packet packet;
}; };
static void u2f_hid_event_callback(HidU2fEvent ev, void* context) { static void u2f_hid_event_callback(HidU2fEvent ev, void* context) {
@ -215,10 +215,21 @@ static int32_t u2f_hid_worker(void* context) {
} }
if(flags & WorkerEvtRequest) { if(flags & WorkerEvtRequest) {
uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf); uint32_t len_cur = furi_hal_hid_u2f_get_request(packet_buf);
if(len_cur > 0) { do {
if(len_cur == 0) {
break;
}
if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) { if((packet_buf[4] & U2F_HID_TYPE_MASK) == U2F_HID_TYPE_INIT) {
if(len_cur < 7) {
u2f_hid->req_len_left = 0;
break; // Wrong chunk len
}
// Init packet // Init packet
u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]); u2f_hid->packet.len = (packet_buf[5] << 8) | (packet_buf[6]);
if(u2f_hid->packet.len > U2F_HID_MAX_PAYLOAD_LEN) {
u2f_hid->req_len_left = 0;
break; // Wrong packet len
}
if(u2f_hid->packet.len > (len_cur - 7)) { if(u2f_hid->packet.len > (len_cur - 7)) {
u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7); u2f_hid->req_len_left = u2f_hid->packet.len - (len_cur - 7);
len_cur = len_cur - 7; len_cur = len_cur - 7;
@ -232,6 +243,10 @@ static int32_t u2f_hid_worker(void* context) {
u2f_hid->req_buf_ptr = len_cur; u2f_hid->req_buf_ptr = len_cur;
if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur); if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);
} else { } else {
if(len_cur < 5) {
u2f_hid->req_len_left = 0;
break; // Wrong chunk len
}
// Continuation packet // Continuation packet
if(u2f_hid->req_len_left > 0) { if(u2f_hid->req_len_left > 0) {
uint32_t cid_temp = 0; uint32_t cid_temp = 0;
@ -260,7 +275,7 @@ static int32_t u2f_hid_worker(void* context) {
u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD); u2f_hid_send_error(u2f_hid, U2F_HID_ERR_INVALID_CMD);
} }
} }
} } while(0);
} }
if(flags & WorkerEvtUnlock) { if(flags & WorkerEvtUnlock) {
u2f_hid->lock = false; u2f_hid->lock = false;
@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) {
u2f_hid->u2f_instance = u2f_inst; u2f_hid->u2f_instance = u2f_inst;
u2f_hid->thread = furi_thread_alloc(); u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid);
furi_thread_set_name(u2f_hid->thread, "U2fHidWorker");
furi_thread_set_stack_size(u2f_hid->thread, 2048);
furi_thread_set_context(u2f_hid->thread, u2f_hid);
furi_thread_set_callback(u2f_hid->thread, u2f_hid_worker);
furi_thread_start(u2f_hid->thread); furi_thread_start(u2f_hid->thread);
return u2f_hid; return u2f_hid;
} }

View File

@ -37,10 +37,10 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) {
} else if(model->display_msg == U2fMsgSuccess) { } else if(model->display_msg == U2fMsgSuccess) {
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31); canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
canvas_draw_str_aligned( canvas_draw_str_aligned(
canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successfull!"); canvas, 128 / 2, 3, AlignCenter, AlignTop, "Authentication successful!");
} else if(model->display_msg == U2fMsgError) { } else if(model->display_msg == U2fMsgError) {
canvas_draw_icon(canvas, 22, 15, &I_Error_62x31); canvas_draw_icon(canvas, 22, 15, &I_Error_62x31);
canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Ceritficate error"); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Certificate error");
} }
} }

View File

@ -1,10 +0,0 @@
App(
appid="bt_hid",
name="Bluetooth Remote",
apptype=FlipperAppType.EXTERNAL,
entry_point="bt_hid_app",
stack_size=1 * 1024,
fap_category="Tools",
fap_icon="bt_remote_10px.png",
fap_icon_assets="assets",
)

View File

@ -1,216 +0,0 @@
#include "bt_hid.h"
#include <furi_hal_bt.h>
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#define TAG "BtHidApp"
enum BtDebugSubmenuIndex {
BtHidSubmenuIndexKeynote,
BtHidSubmenuIndexKeyboard,
BtHidSubmenuIndexMedia,
BtHidSubmenuIndexTikTok,
BtHidSubmenuIndexMouse,
};
void bt_hid_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
BtHid* app = context;
if(index == BtHidSubmenuIndexKeynote) {
app->view_id = BtHidViewKeynote;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote);
} else if(index == BtHidSubmenuIndexKeyboard) {
app->view_id = BtHidViewKeyboard;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeyboard);
} else if(index == BtHidSubmenuIndexMedia) {
app->view_id = BtHidViewMedia;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMedia);
} else if(index == BtHidSubmenuIndexMouse) {
app->view_id = BtHidViewMouse;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewMouse);
} else if(index == BtHidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
}
}
void bt_hid_dialog_callback(DialogExResult result, void* context) {
furi_assert(context);
BtHid* app = context;
if(result == DialogExResultLeft) {
view_dispatcher_stop(app->view_dispatcher);
} else if(result == DialogExResultRight) {
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
} else if(result == DialogExResultCenter) {
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewSubmenu);
}
}
uint32_t bt_hid_exit_confirm_view(void* context) {
UNUSED(context);
return BtHidViewExitConfirm;
}
uint32_t bt_hid_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
furi_assert(context);
BtHid* bt_hid = context;
bool connected = (status == BtStatusConnected);
if(connected) {
notification_internal_message(bt_hid->notifications, &sequence_set_blue_255);
} else {
notification_internal_message(bt_hid->notifications, &sequence_reset_blue);
}
bt_hid_keynote_set_connected_status(bt_hid->bt_hid_keynote, connected);
bt_hid_keyboard_set_connected_status(bt_hid->bt_hid_keyboard, connected);
bt_hid_media_set_connected_status(bt_hid->bt_hid_media, connected);
bt_hid_mouse_set_connected_status(bt_hid->bt_hid_mouse, connected);
bt_hid_tiktok_set_connected_status(bt_hid->bt_hid_tiktok, connected);
}
BtHid* bt_hid_app_alloc() {
BtHid* app = malloc(sizeof(BtHid));
// Gui
app->gui = furi_record_open(RECORD_GUI);
// Bt
app->bt = furi_record_open(RECORD_BT);
// Notifications
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Submenu view
app->submenu = submenu_alloc();
submenu_add_item(
app->submenu, "Keynote", BtHidSubmenuIndexKeynote, bt_hid_submenu_callback, app);
submenu_add_item(
app->submenu, "Keyboard", BtHidSubmenuIndexKeyboard, bt_hid_submenu_callback, app);
submenu_add_item(app->submenu, "Media", BtHidSubmenuIndexMedia, bt_hid_submenu_callback, app);
submenu_add_item(
app->submenu, "TikTok Controller", BtHidSubmenuIndexTikTok, bt_hid_submenu_callback, app);
submenu_add_item(app->submenu, "Mouse", BtHidSubmenuIndexMouse, bt_hid_submenu_callback, app);
view_set_previous_callback(submenu_get_view(app->submenu), bt_hid_exit);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewSubmenu, submenu_get_view(app->submenu));
// Dialog view
app->dialog = dialog_ex_alloc();
dialog_ex_set_result_callback(app->dialog, bt_hid_dialog_callback);
dialog_ex_set_context(app->dialog, app);
dialog_ex_set_left_button_text(app->dialog, "Exit");
dialog_ex_set_right_button_text(app->dialog, "Stay");
dialog_ex_set_center_button_text(app->dialog, "Menu");
dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewExitConfirm, dialog_ex_get_view(app->dialog));
// Keynote view
app->bt_hid_keynote = bt_hid_keynote_alloc();
view_set_previous_callback(
bt_hid_keynote_get_view(app->bt_hid_keynote), bt_hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewKeynote, bt_hid_keynote_get_view(app->bt_hid_keynote));
// Keyboard view
app->bt_hid_keyboard = bt_hid_keyboard_alloc();
view_set_previous_callback(
bt_hid_keyboard_get_view(app->bt_hid_keyboard), bt_hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewKeyboard, bt_hid_keyboard_get_view(app->bt_hid_keyboard));
// Media view
app->bt_hid_media = bt_hid_media_alloc();
view_set_previous_callback(bt_hid_media_get_view(app->bt_hid_media), bt_hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewMedia, bt_hid_media_get_view(app->bt_hid_media));
// TikTok view
app->bt_hid_tiktok = bt_hid_tiktok_alloc();
view_set_previous_callback(
bt_hid_tiktok_get_view(app->bt_hid_tiktok), bt_hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewTikTok, bt_hid_tiktok_get_view(app->bt_hid_tiktok));
// Mouse view
app->bt_hid_mouse = bt_hid_mouse_alloc();
view_set_previous_callback(bt_hid_mouse_get_view(app->bt_hid_mouse), bt_hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewMouse, bt_hid_mouse_get_view(app->bt_hid_mouse));
// TODO switch to menu after Media is done
app->view_id = BtHidViewSubmenu;
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
return app;
}
void bt_hid_app_free(BtHid* app) {
furi_assert(app);
// Reset notification
notification_internal_message(app->notifications, &sequence_reset_blue);
// Free views
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewSubmenu);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewExitConfirm);
dialog_ex_free(app->dialog);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeynote);
bt_hid_keynote_free(app->bt_hid_keynote);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewKeyboard);
bt_hid_keyboard_free(app->bt_hid_keyboard);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMedia);
bt_hid_media_free(app->bt_hid_media);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewMouse);
bt_hid_mouse_free(app->bt_hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
bt_hid_tiktok_free(app->bt_hid_tiktok);
view_dispatcher_free(app->view_dispatcher);
// Close records
furi_record_close(RECORD_GUI);
app->gui = NULL;
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
furi_record_close(RECORD_BT);
app->bt = NULL;
// Free rest
free(app);
}
int32_t bt_hid_app(void* p) {
UNUSED(p);
// Switch profile to Hid
BtHid* app = bt_hid_app_alloc();
bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
// Change profile
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(TAG, "Failed to switch profile");
bt_hid_app_free(app);
return -1;
}
furi_hal_bt_start_advertising();
DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher);
bt_set_status_changed_callback(app->bt, NULL, NULL);
// Change back profile to Serial
bt_set_profile(app->bt, BtProfileSerial);
bt_hid_app_free(app);
return 0;
}

View File

@ -1,41 +0,0 @@
#pragma once
#include <furi.h>
#include <bt/bt_service/bt.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <notification/notification.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include "views/bt_hid_keynote.h"
#include "views/bt_hid_keyboard.h"
#include "views/bt_hid_media.h"
#include "views/bt_hid_mouse.h"
#include "views/bt_hid_tiktok.h"
typedef struct {
Bt* bt;
Gui* gui;
NotificationApp* notifications;
ViewDispatcher* view_dispatcher;
Submenu* submenu;
DialogEx* dialog;
BtHidKeynote* bt_hid_keynote;
BtHidKeyboard* bt_hid_keyboard;
BtHidMedia* bt_hid_media;
BtHidMouse* bt_hid_mouse;
BtHidTikTok* bt_hid_tiktok;
uint32_t view_id;
} BtHid;
typedef enum {
BtHidViewSubmenu,
BtHidViewKeynote,
BtHidViewKeyboard,
BtHidViewMedia,
BtHidViewMouse,
BtHidViewTikTok,
BtHidViewExitConfirm,
} BtHidView;

View File

@ -1,13 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct BtHidKeyboard BtHidKeyboard;
BtHidKeyboard* bt_hid_keyboard_alloc();
void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard);
View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard);
void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected);

View File

@ -1,13 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct BtHidKeynote BtHidKeynote;
BtHidKeynote* bt_hid_keynote_alloc();
void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote);
View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote);
void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected);

View File

@ -1,13 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct BtHidMedia BtHidMedia;
BtHidMedia* bt_hid_media_alloc();
void bt_hid_media_free(BtHidMedia* bt_hid_media);
View* bt_hid_media_get_view(BtHidMedia* bt_hid_media);
void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected);

View File

@ -1,13 +0,0 @@
#pragma once
#include <gui/view.h>
typedef struct BtHidMouse BtHidMouse;
BtHidMouse* bt_hid_mouse_alloc();
void bt_hid_mouse_free(BtHidMouse* bt_hid_mouse);
View* bt_hid_mouse_get_view(BtHidMouse* bt_hid_mouse);
void bt_hid_mouse_set_connected_status(BtHidMouse* bt_hid_mouse, bool connected);

View File

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

View File

@ -247,7 +247,6 @@ static int32_t dap_process(void* p) {
// deinit usb // deinit usb
furi_hal_usb_set_config(usb_config_prev, NULL); furi_hal_usb_set_config(usb_config_prev, NULL);
dap_common_wait_for_deinit();
dap_common_usb_free_name(); dap_common_usb_free_name();
dap_deinit_gpio(swd_pins_prev); dap_deinit_gpio(swd_pins_prev);
return 0; return 0;
@ -441,19 +440,6 @@ static int32_t cdc_process(void* p) {
/******************************* MAIN APP **********************************/ /******************************* MAIN APP **********************************/
/***************************************************************************/ /***************************************************************************/
static FuriThread* furi_thread_alloc_ex(
const char* name,
uint32_t stack_size,
FuriThreadCallback callback,
void* context) {
FuriThread* thread = furi_thread_alloc();
furi_thread_set_name(thread, name);
furi_thread_set_stack_size(thread, stack_size);
furi_thread_set_callback(thread, callback);
furi_thread_set_context(thread, context);
return thread;
}
static DapApp* dap_app_alloc() { static DapApp* dap_app_alloc() {
DapApp* dap_app = malloc(sizeof(DapApp)); DapApp* dap_app = malloc(sizeof(DapApp));
dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app);

View File

@ -618,23 +618,12 @@ static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1);
if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1);
usb_hid.dev_descr->idVendor = DAP_HID_VID;
usb_hid.dev_descr->idProduct = DAP_HID_PID;
usbd_reg_config(dev, hid_ep_config); usbd_reg_config(dev, hid_ep_config);
usbd_reg_control(dev, hid_control); usbd_reg_control(dev, hid_control);
usbd_connect(dev, true); usbd_connect(dev, true);
} }
static bool deinit_flag = false;
void dap_common_wait_for_deinit() {
while(!deinit_flag) {
furi_delay_ms(50);
}
}
static void hid_deinit(usbd_device* dev) { static void hid_deinit(usbd_device* dev) {
dap_state.usb_dev = NULL; dap_state.usb_dev = NULL;
@ -647,12 +636,6 @@ static void hid_deinit(usbd_device* dev) {
usbd_reg_config(dev, NULL); usbd_reg_config(dev, NULL);
usbd_reg_control(dev, NULL); usbd_reg_control(dev, NULL);
free(usb_hid.str_manuf_descr);
free(usb_hid.str_prod_descr);
FURI_SW_MEMBARRIER();
deinit_flag = true;
} }
static void hid_on_wakeup(usbd_device* dev) { static void hid_on_wakeup(usbd_device* dev) {

View File

@ -51,5 +51,3 @@ void dap_common_usb_set_state_callback(DapStateCallback callback);
void dap_common_usb_alloc_name(const char* name); void dap_common_usb_alloc_name(const char* name);
void dap_common_usb_free_name(); void dap_common_usb_free_name();
void dap_common_wait_for_deinit();

View File

@ -0,0 +1,24 @@
App(
appid="hid_usb",
name="USB Remote",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_usb_app",
stack_size=1 * 1024,
fap_category="Tools",
fap_icon="hid_usb_10px.png",
fap_icon_assets="assets",
fap_icon_assets_symbol="hid",
)
App(
appid="hid_ble",
name="Bluetooth Remote",
apptype=FlipperAppType.PLUGIN,
entry_point="hid_ble_app",
stack_size=1 * 1024,
fap_category="Tools",
fap_icon="hid_ble_10px.png",
fap_icon_assets="assets",
fap_icon_assets_symbol="hid",
)

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,365 @@
#include "hid.h"
#include "views.h"
#include <notification/notification_messages.h>
#include <dolphin/dolphin.h>
#define TAG "HidApp"
enum HidDebugSubmenuIndex {
HidSubmenuIndexKeynote,
HidSubmenuIndexKeyboard,
HidSubmenuIndexMedia,
BtHidSubmenuIndexTikTok,
HidSubmenuIndexMouse,
};
typedef enum { ConnTypeSubmenuIndexBluetooth, ConnTypeSubmenuIndexUsb } ConnTypeDebugSubmenuIndex;
static void hid_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
Hid* app = context;
if(index == HidSubmenuIndexKeynote) {
app->view_id = HidViewKeynote;
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);
} else if(index == HidSubmenuIndexMedia) {
app->view_id = HidViewMedia;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia);
} else if(index == HidSubmenuIndexMouse) {
app->view_id = HidViewMouse;
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse);
} else if(index == BtHidSubmenuIndexTikTok) {
app->view_id = BtHidViewTikTok;
view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok);
}
}
static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) {
furi_assert(context);
Hid* hid = context;
bool connected = (status == BtStatusConnected);
if(connected) {
notification_internal_message(hid->notifications, &sequence_set_blue_255);
} else {
notification_internal_message(hid->notifications, &sequence_reset_blue);
}
hid_keynote_set_connected_status(hid->hid_keynote, connected);
hid_keyboard_set_connected_status(hid->hid_keyboard, connected);
hid_media_set_connected_status(hid->hid_media, connected);
hid_mouse_set_connected_status(hid->hid_mouse, connected);
hid_tiktok_set_connected_status(hid->hid_tiktok, connected);
}
static void hid_dialog_callback(DialogExResult result, void* context) {
furi_assert(context);
Hid* app = context;
if(result == DialogExResultLeft) {
view_dispatcher_stop(app->view_dispatcher);
} else if(result == DialogExResultRight) {
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
} else if(result == DialogExResultCenter) {
view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu);
}
}
static uint32_t hid_exit_confirm_view(void* context) {
UNUSED(context);
return HidViewExitConfirm;
}
static uint32_t hid_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
Hid* hid_alloc(HidTransport transport) {
Hid* app = malloc(sizeof(Hid));
app->transport = transport;
// Gui
app->gui = furi_record_open(RECORD_GUI);
// Bt
app->bt = furi_record_open(RECORD_BT);
// Notifications
app->notifications = furi_record_open(RECORD_NOTIFICATION);
// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Device Type Submenu view
app->device_type_submenu = submenu_alloc();
submenu_add_item(
app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app);
submenu_add_item(
app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app);
if(app->transport == HidTransportBle) {
submenu_add_item(
app->device_type_submenu,
"TikTok Controller",
BtHidSubmenuIndexTikTok,
hid_submenu_callback,
app);
}
view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit);
view_dispatcher_add_view(
app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu));
app->view_id = HidViewSubmenu;
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
return app;
}
Hid* hid_app_alloc_view(void* context) {
furi_assert(context);
Hid* app = context;
// Dialog view
app->dialog = dialog_ex_alloc();
dialog_ex_set_result_callback(app->dialog, hid_dialog_callback);
dialog_ex_set_context(app->dialog, app);
dialog_ex_set_left_button_text(app->dialog, "Exit");
dialog_ex_set_right_button_text(app->dialog, "Stay");
dialog_ex_set_center_button_text(app->dialog, "Menu");
dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop);
view_dispatcher_add_view(
app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog));
// Keynote view
app->hid_keynote = hid_keynote_alloc(app);
view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote));
// Keyboard view
app->hid_keyboard = hid_keyboard_alloc(app);
view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard));
// Media view
app->hid_media = hid_media_alloc(app);
view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media));
// TikTok view
app->hid_tiktok = hid_tiktok_alloc(app);
view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok));
// Mouse view
app->hid_mouse = hid_mouse_alloc(app);
view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view);
view_dispatcher_add_view(
app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse));
return app;
}
void hid_free(Hid* app) {
furi_assert(app);
// Reset notification
notification_internal_message(app->notifications, &sequence_reset_blue);
// Free views
view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu);
submenu_free(app->device_type_submenu);
view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm);
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, HidViewKeyboard);
hid_keyboard_free(app->hid_keyboard);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia);
hid_media_free(app->hid_media);
view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse);
hid_mouse_free(app->hid_mouse);
view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok);
hid_tiktok_free(app->hid_tiktok);
view_dispatcher_free(app->view_dispatcher);
// Close records
furi_record_close(RECORD_GUI);
app->gui = NULL;
furi_record_close(RECORD_NOTIFICATION);
app->notifications = NULL;
furi_record_close(RECORD_BT);
app->bt = NULL;
// Free rest
free(app);
}
void hid_hal_keyboard_press(Hid* instance, uint16_t event) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_kb_press(event);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_kb_press(event);
} else {
furi_crash(NULL);
}
}
void hid_hal_keyboard_release(Hid* instance, uint16_t event) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_kb_release(event);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_kb_release(event);
} else {
furi_crash(NULL);
}
}
void hid_hal_keyboard_release_all(Hid* instance) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_kb_release_all();
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_kb_release_all();
} else {
furi_crash(NULL);
}
}
void hid_hal_consumer_key_press(Hid* instance, uint16_t event) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_consumer_key_press(event);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_consumer_key_press(event);
} else {
furi_crash(NULL);
}
}
void hid_hal_consumer_key_release(Hid* instance, uint16_t event) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_consumer_key_release(event);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_consumer_key_release(event);
} else {
furi_crash(NULL);
}
}
void hid_hal_consumer_key_release_all(Hid* instance) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_consumer_key_release_all();
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_kb_release_all();
} else {
furi_crash(NULL);
}
}
void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_mouse_move(dx, dy);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_mouse_move(dx, dy);
} else {
furi_crash(NULL);
}
}
void hid_hal_mouse_scroll(Hid* instance, int8_t delta) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_mouse_scroll(delta);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_mouse_scroll(delta);
} else {
furi_crash(NULL);
}
}
void hid_hal_mouse_press(Hid* instance, uint16_t event) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_mouse_press(event);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_mouse_press(event);
} else {
furi_crash(NULL);
}
}
void hid_hal_mouse_release(Hid* instance, uint16_t event) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_mouse_release(event);
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_mouse_release(event);
} else {
furi_crash(NULL);
}
}
void hid_hal_mouse_release_all(Hid* instance) {
furi_assert(instance);
if(instance->transport == HidTransportBle) {
furi_hal_bt_hid_mouse_release_all();
} else if(instance->transport == HidTransportUsb) {
furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT);
furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT);
} else {
furi_crash(NULL);
}
}
int32_t hid_usb_app(void* p) {
UNUSED(p);
Hid* app = hid_alloc(HidTransportUsb);
app = hid_app_alloc_view(app);
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_unlock();
furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true);
bt_hid_connection_status_changed_callback(BtStatusConnected, app);
DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher);
furi_hal_usb_set_config(usb_mode_prev, NULL);
hid_free(app);
return 0;
}
int32_t hid_ble_app(void* p) {
UNUSED(p);
Hid* app = hid_alloc(HidTransportBle);
app = hid_app_alloc_view(app);
if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) {
FURI_LOG_E(TAG, "Failed to switch profile");
}
furi_hal_bt_start_advertising();
bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app);
DOLPHIN_DEED(DolphinDeedPluginStart);
view_dispatcher_run(app->view_dispatcher);
bt_set_status_changed_callback(app->bt, NULL, NULL);
bt_set_profile(app->bt, BtProfileSerial);
hid_free(app);
return 0;
}

View File

@ -0,0 +1,60 @@
#pragma once
#include <furi.h>
#include <furi_hal_bt.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb.h>
#include <furi_hal_usb_hid.h>
#include <bt/bt_service/bt.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <notification/notification.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include "views/hid_keynote.h"
#include "views/hid_keyboard.h"
#include "views/hid_media.h"
#include "views/hid_mouse.h"
#include "views/hid_tiktok.h"
typedef enum {
HidTransportUsb,
HidTransportBle,
} HidTransport;
typedef struct Hid Hid;
struct Hid {
Bt* bt;
Gui* gui;
NotificationApp* notifications;
ViewDispatcher* view_dispatcher;
Submenu* device_type_submenu;
DialogEx* dialog;
HidKeynote* hid_keynote;
HidKeyboard* hid_keyboard;
HidMedia* hid_media;
HidMouse* hid_mouse;
HidTikTok* hid_tiktok;
HidTransport transport;
uint32_t view_id;
};
void hid_hal_keyboard_press(Hid* instance, uint16_t event);
void hid_hal_keyboard_release(Hid* instance, uint16_t event);
void hid_hal_keyboard_release_all(Hid* instance);
void hid_hal_consumer_key_press(Hid* instance, uint16_t event);
void hid_hal_consumer_key_release(Hid* instance, uint16_t event);
void hid_hal_consumer_key_release_all(Hid* instance);
void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy);
void hid_hal_mouse_scroll(Hid* instance, int8_t delta);
void hid_hal_mouse_press(Hid* instance, uint16_t event);
void hid_hal_mouse_release(Hid* instance, uint16_t event);
void hid_hal_mouse_release_all(Hid* instance);

View File

Before

Width:  |  Height:  |  Size: 151 B

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

View File

@ -0,0 +1,9 @@
typedef enum {
HidViewSubmenu,
HidViewKeynote,
HidViewKeyboard,
HidViewMedia,
HidViewMouse,
BtHidViewTikTok,
HidViewExitConfirm,
} HidView;

View File

@ -1,14 +1,15 @@
#include "bt_hid_keyboard.h" #include "hid_keyboard.h"
#include <furi.h> #include <furi.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include <gui/elements.h> #include <gui/elements.h>
#include <gui/icon_i.h> #include <gui/icon_i.h>
#include "../hid.h"
#include "hid_icons.h"
#include "bt_hid_icons.h" #define TAG "HidKeyboard"
struct BtHidKeyboard { struct HidKeyboard {
View* view; View* view;
Hid* hid;
}; };
typedef struct { typedef struct {
@ -24,7 +25,7 @@ typedef struct {
bool back_pressed; bool back_pressed;
bool connected; bool connected;
char key_string[5]; char key_string[5];
} BtHidKeyboardModel; } HidKeyboardModel;
typedef struct { typedef struct {
uint8_t width; uint8_t width;
@ -32,13 +33,12 @@ typedef struct {
const Icon* icon; const Icon* icon;
char* shift_key; char* shift_key;
uint8_t value; uint8_t value;
} BtHidKeyboardKey; } HidKeyboardKey;
typedef struct { typedef struct {
int8_t x; int8_t x;
int8_t y; int8_t y;
} BtHidKeyboardPoint; } HidKeyboardPoint;
// 4 BY 12 // 4 BY 12
#define MARGIN_TOP 0 #define MARGIN_TOP 0
#define MARGIN_LEFT 4 #define MARGIN_LEFT 4
@ -49,7 +49,7 @@ typedef struct {
#define COLUMN_COUNT 12 #define COLUMN_COUNT 12
// 0 width items are not drawn, but there value is used // 0 width items are not drawn, but there value is used
const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
{ {
{.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1},
{.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2},
@ -112,7 +112,7 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
}, },
{ {
{.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT},
{.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYPAD_COMMA}, {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA},
{.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT},
{.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR},
{.width = 0, .value = HID_KEYBOARD_SPACEBAR}, {.width = 0, .value = HID_KEYBOARD_SPACEBAR},
@ -140,19 +140,19 @@ const BtHidKeyboardKey bt_hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = {
}, },
}; };
static void bt_hid_keyboard_to_upper(char* str) { static void hid_keyboard_to_upper(char* str) {
while(*str) { while(*str) {
*str = toupper((unsigned char)*str); *str = toupper((unsigned char)*str);
str++; str++;
} }
} }
static void bt_hid_keyboard_draw_key( static void hid_keyboard_draw_key(
Canvas* canvas, Canvas* canvas,
BtHidKeyboardModel* model, HidKeyboardModel* model,
uint8_t x, uint8_t x,
uint8_t y, uint8_t y,
BtHidKeyboardKey key, HidKeyboardKey key,
bool selected) { bool selected) {
if(!key.width) return; if(!key.width) return;
@ -190,7 +190,7 @@ static void bt_hid_keyboard_draw_key(
if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) ||
(model->alt && key.value == HID_KEYBOARD_L_ALT) || (model->alt && key.value == HID_KEYBOARD_L_ALT) ||
(model->gui && key.value == HID_KEYBOARD_L_GUI)) { (model->gui && key.value == HID_KEYBOARD_L_GUI)) {
bt_hid_keyboard_to_upper(model->key_string); hid_keyboard_to_upper(model->key_string);
} }
canvas_draw_str_aligned( canvas_draw_str_aligned(
canvas, canvas,
@ -202,9 +202,9 @@ static void bt_hid_keyboard_draw_key(
} }
} }
static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) { static void hid_keyboard_draw_callback(Canvas* canvas, void* context) {
furi_assert(context); furi_assert(context);
BtHidKeyboardModel* model = context; HidKeyboardModel* model = context;
// Header // Header
if(!model->connected) { if(!model->connected) {
@ -225,17 +225,17 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) {
// Start shifting the all keys up if on the next row (Scrolling) // Start shifting the all keys up if on the next row (Scrolling)
uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
for(uint8_t y = initY; y < ROW_COUNT; y++) { for(uint8_t y = initY; y < ROW_COUNT; y++) {
const BtHidKeyboardKey* keyboardKeyRow = bt_hid_keyboard_keyset[y]; const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y];
uint8_t x = 0; uint8_t x = 0;
for(uint8_t i = 0; i < COLUMN_COUNT; i++) { for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
BtHidKeyboardKey key = keyboardKeyRow[i]; HidKeyboardKey key = keyboardKeyRow[i];
// Select when the button is hovered // Select when the button is hovered
// Select if the button is hovered within its width // Select if the button is hovered within its width
// Select if back is clicked and its the backspace key // Select if back is clicked and its the backspace key
// Deselect when the button clicked or not hovered // Deselect when the button clicked or not hovered
bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
bt_hid_keyboard_draw_key( hid_keyboard_draw_key(
canvas, canvas,
model, model,
x, x,
@ -247,8 +247,8 @@ static void bt_hid_keyboard_draw_callback(Canvas* canvas, void* context) {
} }
} }
static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) { static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) {
BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x]; HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x];
// Use upper case if shift is toggled // Use upper case if shift is toggled
bool useUppercase = model->shift; bool useUppercase = model->shift;
// Check if the key has an upper case version // Check if the key has an upper case version
@ -259,34 +259,34 @@ static uint8_t bt_hid_keyboard_get_selected_key(BtHidKeyboardModel* model) {
return key.value; return key.value;
} }
static void bt_hid_keyboard_get_select_key(BtHidKeyboardModel* model, BtHidKeyboardPoint delta) { static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) {
// Keep going until a valid spot is found, this allows for nulls and zero width keys in the map // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map
do { do {
if(((int8_t)model->y) + delta.y < 0) if(((int8_t)model->y) + delta.y < 0)
model->y = ROW_COUNT - 1; model->y = ROW_COUNT - 1;
else else
model->y = (model->y + delta.y) % ROW_COUNT; model->y = (model->y + delta.y) % ROW_COUNT;
} while(delta.y != 0 && bt_hid_keyboard_keyset[model->y][model->x].value == 0); } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0);
do { do {
if(((int8_t)model->x) + delta.x < 0) if(((int8_t)model->x) + delta.x < 0)
model->x = COLUMN_COUNT - 1; model->x = COLUMN_COUNT - 1;
else else
model->x = (model->x + delta.x) % COLUMN_COUNT; model->x = (model->x + delta.x) % COLUMN_COUNT;
} while(delta.x != 0 && bt_hid_keyboard_keyset[model->y][model->x].width == } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width ==
0); // Skip zero width keys, pretend they are one key 0); // Skip zero width keys, pretend they are one key
} }
static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent* event) { static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) {
with_view_model( with_view_model(
bt_hid_keyboard->view, hid_keyboard->view,
BtHidKeyboardModel * model, HidKeyboardModel * model,
{ {
if(event->key == InputKeyOk) { if(event->key == InputKeyOk) {
if(event->type == InputTypePress) { if(event->type == InputTypePress) {
model->ok_pressed = true; model->ok_pressed = true;
} else if(event->type == InputTypeLong || event->type == InputTypeShort) { } else if(event->type == InputTypeLong || event->type == InputTypeShort) {
model->last_key_code = bt_hid_keyboard_get_selected_key(model); model->last_key_code = hid_keyboard_get_selected_key(model);
// Toggle the modifier key when clicked, and click the key // Toggle the modifier key when clicked, and click the key
if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { if(model->last_key_code == HID_KEYBOARD_L_SHIFT) {
@ -314,10 +314,12 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent*
else else
model->modifier_code &= ~KEY_MOD_LEFT_GUI; model->modifier_code &= ~KEY_MOD_LEFT_GUI;
} }
furi_hal_bt_hid_kb_press(model->modifier_code | model->last_key_code); hid_hal_keyboard_press(
hid_keyboard->hid, model->modifier_code | model->last_key_code);
} else if(event->type == InputTypeRelease) { } else if(event->type == InputTypeRelease) {
// Release happens after short and long presses // Release happens after short and long presses
furi_hal_bt_hid_kb_release(model->modifier_code | model->last_key_code); hid_hal_keyboard_release(
hid_keyboard->hid, model->modifier_code | model->last_key_code);
model->ok_pressed = false; model->ok_pressed = false;
} }
} else if(event->key == InputKeyBack) { } else if(event->key == InputKeyBack) {
@ -325,66 +327,67 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent*
if(event->type == InputTypePress) { if(event->type == InputTypePress) {
model->back_pressed = true; model->back_pressed = true;
} else if(event->type == InputTypeShort) { } else if(event->type == InputTypeShort) {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE);
} else if(event->type == InputTypeRelease) { } else if(event->type == InputTypeRelease) {
model->back_pressed = false; model->back_pressed = false;
} }
} else if(event->type == InputTypePress || event->type == InputTypeRepeat) { } else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
// Cycle the selected keys // Cycle the selected keys
if(event->key == InputKeyUp) { if(event->key == InputKeyUp) {
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = -1}); hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1});
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 0, .y = 1}); hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1});
} else if(event->key == InputKeyLeft) { } else if(event->key == InputKeyLeft) {
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = -1, .y = 0}); hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0});
} else if(event->key == InputKeyRight) { } else if(event->key == InputKeyRight) {
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0}); hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0});
} }
} }
}, },
true); true);
} }
static bool bt_hid_keyboard_input_callback(InputEvent* event, void* context) { static bool hid_keyboard_input_callback(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
BtHidKeyboard* bt_hid_keyboard = context; HidKeyboard* hid_keyboard = context;
bool consumed = false; bool consumed = false;
if(event->type == InputTypeLong && event->key == InputKeyBack) { if(event->type == InputTypeLong && event->key == InputKeyBack) {
furi_hal_bt_hid_kb_release_all(); hid_hal_keyboard_release_all(hid_keyboard->hid);
} else { } else {
bt_hid_keyboard_process(bt_hid_keyboard, event); hid_keyboard_process(hid_keyboard, event);
consumed = true; consumed = true;
} }
return consumed; return consumed;
} }
BtHidKeyboard* bt_hid_keyboard_alloc() { HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard)); HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard));
bt_hid_keyboard->view = view_alloc(); hid_keyboard->view = view_alloc();
view_set_context(bt_hid_keyboard->view, bt_hid_keyboard); hid_keyboard->hid = bt_hid;
view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel)); view_set_context(hid_keyboard->view, hid_keyboard);
view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback); view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel));
view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback); view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback);
view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback);
return bt_hid_keyboard; return hid_keyboard;
} }
void bt_hid_keyboard_free(BtHidKeyboard* bt_hid_keyboard) { void hid_keyboard_free(HidKeyboard* hid_keyboard) {
furi_assert(bt_hid_keyboard); furi_assert(hid_keyboard);
view_free(bt_hid_keyboard->view); view_free(hid_keyboard->view);
free(bt_hid_keyboard); free(hid_keyboard);
} }
View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) { View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) {
furi_assert(bt_hid_keyboard); furi_assert(hid_keyboard);
return bt_hid_keyboard->view; return hid_keyboard->view;
} }
void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) { void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) {
furi_assert(bt_hid_keyboard); furi_assert(hid_keyboard);
with_view_model( with_view_model(
bt_hid_keyboard->view, BtHidKeyboardModel * model, { model->connected = connected; }, true); hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true);
} }

View File

@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidKeyboard HidKeyboard;
HidKeyboard* hid_keyboard_alloc(Hid* bt_hid);
void hid_keyboard_free(HidKeyboard* hid_keyboard);
View* hid_keyboard_get_view(HidKeyboard* hid_keyboard);
void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected);

View File

@ -1,13 +1,14 @@
#include "bt_hid_keynote.h" #include "hid_keynote.h"
#include <furi.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "../hid.h"
#include "bt_hid_icons.h" #include "hid_icons.h"
struct BtHidKeynote { #define TAG "HidKeynote"
struct HidKeynote {
View* view; View* view;
Hid* hid;
}; };
typedef struct { typedef struct {
@ -18,9 +19,9 @@ typedef struct {
bool ok_pressed; bool ok_pressed;
bool back_pressed; bool back_pressed;
bool connected; bool connected;
} BtHidKeynoteModel; } HidKeynoteModel;
static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
canvas_draw_triangle(canvas, x, y, 5, 3, dir); canvas_draw_triangle(canvas, x, y, 5, 3, dir);
if(dir == CanvasDirectionBottomToTop) { if(dir == CanvasDirectionBottomToTop) {
canvas_draw_line(canvas, x, y + 6, x, y - 1); canvas_draw_line(canvas, x, y + 6, x, y - 1);
@ -33,9 +34,9 @@ static void bt_hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canv
} }
} }
static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) { static void hid_keynote_draw_callback(Canvas* canvas, void* context) {
furi_assert(context); furi_assert(context);
BtHidKeynoteModel* model = context; HidKeynoteModel* model = context;
// Header // Header
if(model->connected) { if(model->connected) {
@ -56,7 +57,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_slightly_rounded_box(canvas, 24, 26, 13, 13); elements_slightly_rounded_box(canvas, 24, 26, 13, 13);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Down // Down
@ -65,7 +66,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_slightly_rounded_box(canvas, 24, 47, 13, 13); elements_slightly_rounded_box(canvas, 24, 47, 13, 13);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Left // Left
@ -74,7 +75,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_slightly_rounded_box(canvas, 3, 47, 13, 13); elements_slightly_rounded_box(canvas, 3, 47, 13, 13);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Right // Right
@ -83,7 +84,7 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_slightly_rounded_box(canvas, 45, 47, 13, 13); elements_slightly_rounded_box(canvas, 45, 47, 13, 13);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Ok // Ok
@ -106,100 +107,101 @@ static void bt_hid_keynote_draw_callback(Canvas* canvas, void* context) {
elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back");
} }
static void bt_hid_keynote_process(BtHidKeynote* bt_hid_keynote, InputEvent* event) { static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) {
with_view_model( with_view_model(
bt_hid_keynote->view, hid_keynote->view,
BtHidKeynoteModel * model, HidKeynoteModel * model,
{ {
if(event->type == InputTypePress) { if(event->type == InputTypePress) {
if(event->key == InputKeyUp) { if(event->key == InputKeyUp) {
model->up_pressed = true; model->up_pressed = true;
furi_hal_bt_hid_kb_press(HID_KEYBOARD_UP_ARROW); hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW);
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->down_pressed = true; model->down_pressed = true;
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DOWN_ARROW); hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW);
} else if(event->key == InputKeyLeft) { } else if(event->key == InputKeyLeft) {
model->left_pressed = true; model->left_pressed = true;
furi_hal_bt_hid_kb_press(HID_KEYBOARD_LEFT_ARROW); hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW);
} else if(event->key == InputKeyRight) { } else if(event->key == InputKeyRight) {
model->right_pressed = true; model->right_pressed = true;
furi_hal_bt_hid_kb_press(HID_KEYBOARD_RIGHT_ARROW); hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW);
} else if(event->key == InputKeyOk) { } else if(event->key == InputKeyOk) {
model->ok_pressed = true; model->ok_pressed = true;
furi_hal_bt_hid_kb_press(HID_KEYBOARD_SPACEBAR); hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR);
} else if(event->key == InputKeyBack) { } else if(event->key == InputKeyBack) {
model->back_pressed = true; model->back_pressed = true;
} }
} else if(event->type == InputTypeRelease) { } else if(event->type == InputTypeRelease) {
if(event->key == InputKeyUp) { if(event->key == InputKeyUp) {
model->up_pressed = false; model->up_pressed = false;
furi_hal_bt_hid_kb_release(HID_KEYBOARD_UP_ARROW); hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW);
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->down_pressed = false; model->down_pressed = false;
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DOWN_ARROW); hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW);
} else if(event->key == InputKeyLeft) { } else if(event->key == InputKeyLeft) {
model->left_pressed = false; model->left_pressed = false;
furi_hal_bt_hid_kb_release(HID_KEYBOARD_LEFT_ARROW); hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW);
} else if(event->key == InputKeyRight) { } else if(event->key == InputKeyRight) {
model->right_pressed = false; model->right_pressed = false;
furi_hal_bt_hid_kb_release(HID_KEYBOARD_RIGHT_ARROW); hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW);
} else if(event->key == InputKeyOk) { } else if(event->key == InputKeyOk) {
model->ok_pressed = false; model->ok_pressed = false;
furi_hal_bt_hid_kb_release(HID_KEYBOARD_SPACEBAR); hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR);
} else if(event->key == InputKeyBack) { } else if(event->key == InputKeyBack) {
model->back_pressed = false; model->back_pressed = false;
} }
} else if(event->type == InputTypeShort) { } else if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) { if(event->key == InputKeyBack) {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE); hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE); hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE);
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK); hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK);
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK); hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK);
} }
} }
}, },
true); true);
} }
static bool bt_hid_keynote_input_callback(InputEvent* event, void* context) { static bool hid_keynote_input_callback(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
BtHidKeynote* bt_hid_keynote = context; HidKeynote* hid_keynote = context;
bool consumed = false; bool consumed = false;
if(event->type == InputTypeLong && event->key == InputKeyBack) { if(event->type == InputTypeLong && event->key == InputKeyBack) {
furi_hal_bt_hid_kb_release_all(); hid_hal_keyboard_release_all(hid_keynote->hid);
} else { } else {
bt_hid_keynote_process(bt_hid_keynote, event); hid_keynote_process(hid_keynote, event);
consumed = true; consumed = true;
} }
return consumed; return consumed;
} }
BtHidKeynote* bt_hid_keynote_alloc() { HidKeynote* hid_keynote_alloc(Hid* hid) {
BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote)); HidKeynote* hid_keynote = malloc(sizeof(HidKeynote));
bt_hid_keynote->view = view_alloc(); hid_keynote->view = view_alloc();
view_set_context(bt_hid_keynote->view, bt_hid_keynote); hid_keynote->hid = hid;
view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel)); view_set_context(hid_keynote->view, hid_keynote);
view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback); view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel));
view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback); view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback);
view_set_input_callback(hid_keynote->view, hid_keynote_input_callback);
return bt_hid_keynote; return hid_keynote;
} }
void bt_hid_keynote_free(BtHidKeynote* bt_hid_keynote) { void hid_keynote_free(HidKeynote* hid_keynote) {
furi_assert(bt_hid_keynote); furi_assert(hid_keynote);
view_free(bt_hid_keynote->view); view_free(hid_keynote->view);
free(bt_hid_keynote); free(hid_keynote);
} }
View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) { View* hid_keynote_get_view(HidKeynote* hid_keynote) {
furi_assert(bt_hid_keynote); furi_assert(hid_keynote);
return bt_hid_keynote->view; return hid_keynote->view;
} }
void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) { void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {
furi_assert(bt_hid_keynote); furi_assert(hid_keynote);
with_view_model( with_view_model(
bt_hid_keynote->view, BtHidKeynoteModel * model, { model->connected = connected; }, true); hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true);
} }

View File

@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
typedef struct Hid Hid;
typedef struct HidKeynote HidKeynote;
HidKeynote* hid_keynote_alloc(Hid* bt_hid);
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);

View File

@ -1,13 +1,17 @@
#include "bt_hid_media.h" #include "hid_media.h"
#include <furi.h> #include <furi.h>
#include <furi_hal_bt_hid.h> #include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h> #include <furi_hal_usb_hid.h>
#include <gui/elements.h> #include <gui/elements.h>
#include "../hid.h"
#include "bt_hid_icons.h" #include "hid_icons.h"
struct BtHidMedia { #define TAG "HidMedia"
struct HidMedia {
View* view; View* view;
Hid* hid;
}; };
typedef struct { typedef struct {
@ -17,9 +21,9 @@ typedef struct {
bool down_pressed; bool down_pressed;
bool ok_pressed; bool ok_pressed;
bool connected; bool connected;
} BtHidMediaModel; } HidMediaModel;
static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) {
canvas_draw_triangle(canvas, x, y, 5, 3, dir); canvas_draw_triangle(canvas, x, y, 5, 3, dir);
if(dir == CanvasDirectionBottomToTop) { if(dir == CanvasDirectionBottomToTop) {
canvas_draw_dot(canvas, x, y - 1); canvas_draw_dot(canvas, x, y - 1);
@ -32,9 +36,9 @@ static void bt_hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, Canvas
} }
} }
static void bt_hid_media_draw_callback(Canvas* canvas, void* context) { static void hid_media_draw_callback(Canvas* canvas, void* context) {
furi_assert(context); furi_assert(context);
BtHidMediaModel* model = context; HidMediaModel* model = context;
// Header // Header
if(model->connected) { if(model->connected) {
@ -76,8 +80,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
canvas_set_bitmap_mode(canvas, 0); canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Right // Right
@ -87,8 +91,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
canvas_set_bitmap_mode(canvas, 0); canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
bt_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
// Ok // Ok
@ -96,7 +100,7 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13);
canvas_set_color(canvas, ColorWhite); canvas_set_color(canvas, ColorWhite);
} }
bt_hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight);
canvas_draw_line(canvas, 100, 29, 100, 33); canvas_draw_line(canvas, 100, 29, 100, 33);
canvas_draw_line(canvas, 102, 29, 102, 33); canvas_draw_line(canvas, 102, 29, 102, 33);
canvas_set_color(canvas, ColorBlack); canvas_set_color(canvas, ColorBlack);
@ -107,100 +111,101 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit");
} }
static void bt_hid_media_process_press(BtHidMedia* bt_hid_media, InputEvent* event) { static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) {
with_view_model( with_view_model(
bt_hid_media->view, hid_media->view,
BtHidMediaModel * model, HidMediaModel * model,
{ {
if(event->key == InputKeyUp) { if(event->key == InputKeyUp) {
model->up_pressed = true; model->up_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_INCREMENT); hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT);
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->down_pressed = true; model->down_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_VOLUME_DECREMENT); hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT);
} else if(event->key == InputKeyLeft) { } else if(event->key == InputKeyLeft) {
model->left_pressed = true; model->left_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_PREVIOUS_TRACK); hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK);
} else if(event->key == InputKeyRight) { } else if(event->key == InputKeyRight) {
model->right_pressed = true; model->right_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_SCAN_NEXT_TRACK); hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK);
} else if(event->key == InputKeyOk) { } else if(event->key == InputKeyOk) {
model->ok_pressed = true; model->ok_pressed = true;
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_PLAY_PAUSE); hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
} }
}, },
true); true);
} }
static void bt_hid_media_process_release(BtHidMedia* bt_hid_media, InputEvent* event) { static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) {
with_view_model( with_view_model(
bt_hid_media->view, hid_media->view,
BtHidMediaModel * model, HidMediaModel * model,
{ {
if(event->key == InputKeyUp) { if(event->key == InputKeyUp) {
model->up_pressed = false; model->up_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_INCREMENT); hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT);
} else if(event->key == InputKeyDown) { } else if(event->key == InputKeyDown) {
model->down_pressed = false; model->down_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_VOLUME_DECREMENT); hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT);
} else if(event->key == InputKeyLeft) { } else if(event->key == InputKeyLeft) {
model->left_pressed = false; model->left_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_PREVIOUS_TRACK); hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK);
} else if(event->key == InputKeyRight) { } else if(event->key == InputKeyRight) {
model->right_pressed = false; model->right_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_SCAN_NEXT_TRACK); hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK);
} else if(event->key == InputKeyOk) { } else if(event->key == InputKeyOk) {
model->ok_pressed = false; model->ok_pressed = false;
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_PLAY_PAUSE); hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE);
} }
}, },
true); true);
} }
static bool bt_hid_media_input_callback(InputEvent* event, void* context) { static bool hid_media_input_callback(InputEvent* event, void* context) {
furi_assert(context); furi_assert(context);
BtHidMedia* bt_hid_media = context; HidMedia* hid_media = context;
bool consumed = false; bool consumed = false;
if(event->type == InputTypePress) { if(event->type == InputTypePress) {
bt_hid_media_process_press(bt_hid_media, event); hid_media_process_press(hid_media, event);
consumed = true; consumed = true;
} else if(event->type == InputTypeRelease) { } else if(event->type == InputTypeRelease) {
bt_hid_media_process_release(bt_hid_media, event); hid_media_process_release(hid_media, event);
consumed = true; consumed = true;
} else if(event->type == InputTypeShort) { } else if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) { if(event->key == InputKeyBack) {
furi_hal_bt_hid_consumer_key_release_all(); hid_hal_consumer_key_release_all(hid_media->hid);
} }
} }
return consumed; return consumed;
} }
BtHidMedia* bt_hid_media_alloc() { HidMedia* hid_media_alloc(Hid* hid) {
BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia)); HidMedia* hid_media = malloc(sizeof(HidMedia));
bt_hid_media->view = view_alloc(); hid_media->view = view_alloc();
view_set_context(bt_hid_media->view, bt_hid_media); hid_media->hid = hid;
view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel)); view_set_context(hid_media->view, hid_media);
view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback); view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel));
view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback); view_set_draw_callback(hid_media->view, hid_media_draw_callback);
view_set_input_callback(hid_media->view, hid_media_input_callback);
return bt_hid_media; return hid_media;
} }
void bt_hid_media_free(BtHidMedia* bt_hid_media) { void hid_media_free(HidMedia* hid_media) {
furi_assert(bt_hid_media); furi_assert(hid_media);
view_free(bt_hid_media->view); view_free(hid_media->view);
free(bt_hid_media); free(hid_media);
} }
View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) { View* hid_media_get_view(HidMedia* hid_media) {
furi_assert(bt_hid_media); furi_assert(hid_media);
return bt_hid_media->view; return hid_media->view;
} }
void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) { void hid_media_set_connected_status(HidMedia* hid_media, bool connected) {
furi_assert(bt_hid_media); furi_assert(hid_media);
with_view_model( with_view_model(
bt_hid_media->view, BtHidMediaModel * model, { model->connected = connected; }, true); hid_media->view, HidMediaModel * model, { model->connected = connected; }, true);
} }

View File

@ -0,0 +1,13 @@
#pragma once
#include <gui/view.h>
typedef struct HidMedia HidMedia;
HidMedia* hid_media_alloc();
void hid_media_free(HidMedia* hid_media);
View* hid_media_get_view(HidMedia* hid_media);
void hid_media_set_connected_status(HidMedia* hid_media, bool connected);

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