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!
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.
@ -17,12 +17,12 @@ See the [ReadMe](ReadMe.md) to get an overview of the project. Here are some hel
## 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.
- 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 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 review by code owner.

View File

@ -71,7 +71,7 @@ void WIEGAND::end() {
}
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
{
_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) {
UNUSED(p);
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);
dialog_message_show_storage_error(dialogs, "Incorrect\nRadioStack");
return 255;

View File

@ -145,7 +145,7 @@ DisplayTest* display_test_alloc() {
view_set_previous_callback(view, display_test_previous_callback);
view_dispatcher_add_view(instance->view_dispatcher, DisplayTestViewConfigure, view);
// Configurtion items
// Configuration items
VariableItem* item;
instance->config_bias = false;
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_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
app->worker_thread = furi_thread_alloc();
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);
app->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 1024, uart_echo_worker, app);
furi_thread_start(app->worker_thread);
return app;

View File

@ -11,7 +11,7 @@
extern size_t memmgr_get_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,
// where X = sizeof(void*) + sizeof(size_t), look to BlockLink_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_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));
// debug allocation info

View File

@ -136,7 +136,7 @@ static bool nfc_test_digital_signal_test_encode(
ref_timings_sum += ref[i];
if(timings_diff > timing_tolerance) {
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;
break;
}

View File

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

View File

@ -219,21 +219,21 @@ MU_TEST_1(stream_composite_subtest, Stream* stream) {
mu_check(stream_eof(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"
mu_check(stream_insert_format(stream, "%s%d", "dio", 666));
mu_assert_int_eq(6, stream_size(stream));
mu_check(stream_eof(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"
mu_check(stream_insert_format(stream, "%s%d", "zlo", 555));
mu_assert_int_eq(12, stream_size(stream));
mu_check(stream_eof(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"
mu_check(stream_seek(stream, 6, StreamOffsetFromStart));
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 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_COUNT_PARSE 232
#define TEST_RANDOM_COUNT_PARSE 244
#define TEST_TIMEOUT 10000
static SubGhzEnvironment* environment_handler;
@ -209,6 +209,149 @@ MU_TEST(subghz_keystore_test) {
"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
MU_TEST(subghz_decoder_came_atomo_test) {
mu_assert(
@ -437,6 +580,13 @@ MU_TEST(subghz_decoder_clemsa_test) {
"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
MU_TEST(subghz_encoder_princeton_test) {
mu_assert(
@ -558,6 +708,12 @@ MU_TEST(subghz_encoder_clemsa_test) {
"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_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();
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_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_intertechno_v3_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_came_test);
@ -619,6 +778,7 @@ MU_TEST_SUITE(subghz) {
MU_RUN_TEST(subghz_encoder_magellan_test);
MU_RUN_TEST(subghz_encoder_intertechno_v3_test);
MU_RUN_TEST(subghz_encoder_clemsa_test);
MU_RUN_TEST(subghz_encoder_ansonic_test);
MU_RUN_TEST(subghz_random_test);
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;
}
if(offset_new > 0) {
offset_new =
CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0);
offset_new = CLAMP(offset_new, (int32_t)model->item_cnt, 0);
} else {
offset_new = 0;
}

View File

@ -3,9 +3,9 @@
#include "../archive_i.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 FILE_LIST_BUF_LEN 100
#define FILE_LIST_BUF_LEN 50
static const char* tab_default_paths[] = {
[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.error[0] = '\0';
bad_usb->thread = furi_thread_alloc();
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);
bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb);
furi_thread_start(bad_usb->thread);
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);
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;
} else {
result = true;

View File

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

View File

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

View File

@ -9,8 +9,6 @@ typedef enum {
UsbUartLineIndexFlow,
} LineIndex;
static UsbUartConfig* cfg_set;
static const char* vcp_ch[] = {"0 (CLI)", "1"};
static const char* uart_ch[] = {"13,14", "15,16"};
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) {
UNUSED(context);
UNUSED(event);
GpioApp* app = context;
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;
}
@ -38,55 +42,59 @@ void line_ensure_flow_invariant(GpioApp* app) {
// selected. This function enforces that invariant by resetting flow_pins
// 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;
variable_item_set_values_count(item, available_flow_pins);
if(cfg_set->flow_pins >= available_flow_pins) {
cfg_set->flow_pins = 0;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
if(app->usb_uart_cfg->flow_pins >= available_flow_pins) {
app->usb_uart_cfg->flow_pins = 0;
variable_item_set_current_value_index(item, cfg_set->flow_pins);
variable_item_set_current_value_text(item, flow_pins[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[app->usb_uart_cfg->flow_pins]);
}
}
static void line_vcp_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vcp_ch[index]);
cfg_set->vcp_ch = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
app->usb_uart_cfg->vcp_ch = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_port_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, uart_ch[index]);
if(index == 0)
cfg_set->uart_ch = FuriHalUartIdUSART1;
app->usb_uart_cfg->uart_ch = FuriHalUartIdUSART1;
else if(index == 1)
cfg_set->uart_ch = FuriHalUartIdLPUART1;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
app->usb_uart_cfg->uart_ch = FuriHalUartIdLPUART1;
line_ensure_flow_invariant(app);
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_flow_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, flow_pins[index]);
cfg_set->flow_pins = index;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
app->usb_uart_cfg->flow_pins = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
static void line_baudrate_cb(VariableItem* item) {
GpioApp* app = variable_item_get_context(item);
furi_assert(app);
uint8_t index = variable_item_get_current_value_index(item);
char br_text[8];
@ -94,28 +102,29 @@ static void line_baudrate_cb(VariableItem* item) {
if(index > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[index - 1]);
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 {
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;
usb_uart_set_config(app->usb_uart_bridge, cfg_set);
app->usb_uart_cfg->baudrate_mode = index;
view_dispatcher_send_custom_event(app->view_dispatcher, GpioUsbUartEventConfigSet);
}
void gpio_scene_usb_uart_cfg_on_enter(void* context) {
GpioApp* app = context;
furi_assert(app);
VariableItemList* var_item_list = app->var_item_list;
cfg_set = malloc(sizeof(UsbUartConfig));
usb_uart_get_config(app->usb_uart_bridge, cfg_set);
app->usb_uart_cfg = malloc(sizeof(UsbUartConfig));
usb_uart_get_config(app->usb_uart_bridge, app->usb_uart_cfg);
VariableItem* item;
char br_text[8];
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_text(item, vcp_ch[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[app->usb_uart_cfg->vcp_ch]);
item = variable_item_list_add(
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,
line_baudrate_cb,
app);
variable_item_set_current_value_index(item, cfg_set->baudrate_mode);
if(cfg_set->baudrate_mode > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[cfg_set->baudrate_mode - 1]);
variable_item_set_current_value_index(item, app->usb_uart_cfg->baudrate_mode);
if(app->usb_uart_cfg->baudrate_mode > 0) {
snprintf(br_text, 7, "%lu", baudrate_list[app->usb_uart_cfg->baudrate_mode - 1]);
variable_item_set_current_value_text(item, br_text);
} 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);
variable_item_set_current_value_index(item, cfg_set->uart_ch);
variable_item_set_current_value_text(item, uart_ch[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[app->usb_uart_cfg->uart_ch]);
item = variable_item_list_add(
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_text(item, flow_pins[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[app->usb_uart_cfg->flow_pins]);
app->var_item_flow = item;
line_ensure_flow_invariant(app);
@ -155,5 +165,5 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) {
GpioAppViewUsbUartCfg,
variable_item_list_get_selected_item_index(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 "usb_cdc.h"
#include "cli/cli_vcp.h"
#include <toolbox/api_lock.h>
#include "cli/cli.h"
#define USB_CDC_PKT_LEN CDC_DATA_SZ
@ -51,6 +52,8 @@ struct UsbUartBridge {
UsbUartState st;
FuriApiLock cfg_lock;
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->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
usb_uart->tx_thread = furi_thread_alloc();
furi_thread_set_name(usb_uart->tx_thread, "UsbUartTxWorker");
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->tx_thread =
furi_thread_alloc_ex("UsbUartTxWorker", 512, usb_uart_tx_thread, usb_uart);
usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_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;
events |= WorkerEvtCtrlLineSet;
}
api_lock_unlock(usb_uart->cfg_lock);
}
if(events & WorkerEvtLineCfgSet) {
if(usb_uart->cfg.baudrate == 0)
@ -338,11 +339,7 @@ UsbUartBridge* usb_uart_enable(UsbUartConfig* cfg) {
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
usb_uart->thread = furi_thread_alloc();
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);
usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart);
furi_thread_start(usb_uart->thread);
return usb_uart;
@ -359,8 +356,10 @@ void usb_uart_disable(UsbUartBridge* usb_uart) {
void usb_uart_set_config(UsbUartBridge* usb_uart, UsbUartConfig* cfg) {
furi_assert(usb_uart);
furi_assert(cfg);
usb_uart->cfg_lock = api_lock_alloc_locked();
memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
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) {

View File

@ -24,7 +24,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Power_hvr_25x27,
infrared_scene_universal_common_item_callback,
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,
i,
@ -36,7 +36,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Mute_hvr_25x27,
infrared_scene_universal_common_item_callback,
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,
i,
@ -48,7 +48,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Vol_up_hvr_25x27,
infrared_scene_universal_common_item_callback,
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,
i,
@ -60,7 +60,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Up_hvr_25x27,
infrared_scene_universal_common_item_callback,
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,
i,
@ -72,7 +72,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Vol_down_hvr_25x27,
infrared_scene_universal_common_item_callback,
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,
i,
@ -84,7 +84,7 @@ void infrared_scene_universal_tv_on_enter(void* context) {
&I_Down_hvr_25x27,
infrared_scene_universal_common_item_callback,
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, 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_result, MfUltralightReadAuthResult)
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_warn, MfUltralightUnlockWarn)
ADD_SCENE(nfc, mf_desfire_read_success, MfDesfireReadSuccess)

View File

@ -4,6 +4,7 @@
enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexEmulate,
SubmenuIndexDetectReader,
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_add_item(
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, "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;
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfClassicMenu, event.event);
if(event.event == SubmenuIndexSave) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexSave);
nfc->dev->format = NfcDeviceSaveFormatMifareClassic;
// Clear device name
nfc_device_set_name(nfc->dev, "");
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
consumed = true;
} else if(event.event == SubmenuIndexEmulate) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexEmulate);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate);
if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) {
DOLPHIN_DEED(DolphinDeedNfcAddEmulate);
@ -53,9 +59,11 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
DOLPHIN_DEED(DolphinDeedNfcEmulate);
}
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) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo);
scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
consumed = true;
}

View File

@ -19,10 +19,10 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
Submenu* submenu = nfc->submenu;
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,
"Unlock With Password",
"Unlock",
SubmenuIndexUnlock,
nfc_scene_mf_ultralight_menu_submenu_callback,
nfc);

View File

@ -24,14 +24,15 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
if(curr_state != state) {
if(state == NfcSceneMfUlReadStateDetecting) {
popup_reset(nfc->popup);
popup_set_text(
nfc->popup, "Apply card to\nFlipper's back", 97, 24, AlignCenter, AlignTop);
popup_set_text(nfc->popup, "Apply the\ntarget card", 97, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
nfc_blink_read_start(nfc);
} else if(state == NfcSceneMfUlReadStateReading) {
popup_reset(nfc->popup);
popup_set_header(
nfc->popup, "Reading card\nDon't move...", 85, 24, AlignCenter, AlignTop);
popup_set_icon(nfc->popup, 12, 23, &A_Loading_24);
nfc_blink_detect_start(nfc);
} else if(state == NfcSceneMfUlReadStateNotSupportedCard) {
popup_reset(nfc->popup);
popup_set_header(nfc->popup, "Wrong type of card!", 64, 3, AlignCenter, AlignTop);
@ -43,6 +44,9 @@ void nfc_scene_mf_ultralight_read_auth_set_state(Nfc* nfc, NfcSceneMfUlReadState
AlignLeft,
AlignTop);
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);
}
@ -62,8 +66,6 @@ void nfc_scene_mf_ultralight_read_auth_on_enter(void* context) {
&nfc->dev->dev_data,
nfc_scene_mf_ultralight_read_auth_worker_callback,
nfc);
nfc_blink_read_start(nfc);
}
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);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
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;
}

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;
MfUltralightConfigPages* config_pages = mf_ultralight_get_config_pages(mf_ul_data);
Widget* widget = nfc->widget;
const char* title;
FuriString* temp_str;
temp_str = furi_string_alloc();
if((mf_ul_data->data_read == mf_ul_data->data_size) && (mf_ul_data->data_read > 0)) {
widget_add_string_element(
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "All pages are unlocked!");
if(mf_ul_data->auth_success) {
title = "All pages are unlocked!";
} else {
widget_add_string_element(
widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Not all pages unlocked!");
title = "All unlocked but failed auth!";
}
} else {
title = "Not all pages unlocked!";
}
widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, title);
furi_string_set(temp_str, "UID:");
for(size_t i = 0; i < nfc_data->uid_len; 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);
furi_string_free(temp_str);
notification_message(nfc->notifications, &sequence_set_green_255);
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;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
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;
@ -93,4 +111,6 @@ void nfc_scene_mf_ultralight_read_auth_result_on_exit(void* context) {
// Clean views
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"
enum SubmenuIndex {
SubmenuIndexMfUlUnlockMenuManual,
SubmenuIndexMfUlUnlockMenuAuto,
SubmenuIndexMfUlUnlockMenuAmeebo,
SubmenuIndexMfUlUnlockMenuXiaomi,
SubmenuIndexMfUlUnlockMenuManual,
};
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 =
scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
if(nfc->dev->dev_data.protocol == NfcDeviceProtocolMifareUl) {
submenu_add_item(
submenu,
"Enter Password Manually",
SubmenuIndexMfUlUnlockMenuManual,
"Unlock With Reader",
SubmenuIndexMfUlUnlockMenuAuto,
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc);
}
submenu_add_item(
submenu,
"Auth As Ameebo",
@ -32,10 +35,16 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) {
nfc);
submenu_add_item(
submenu,
"Auth As Xiaomi",
"Auth As Xiaomi Air Purifier",
SubmenuIndexMfUlUnlockMenuXiaomi,
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc);
submenu_add_item(
submenu,
"Enter Password Manually",
SubmenuIndexMfUlUnlockMenuManual,
nfc_scene_mf_ultralight_unlock_menu_submenu_callback,
nfc);
submenu_set_selected_item(submenu, state);
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;
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockWarn);
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;
}

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) {
Nfc* nfc = context;
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_result_callback(dialog_ex, nfc_scene_mf_ultralight_unlock_warn_dialog_callback);
if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) {
// Build dialog text
MfUltralightAuth* auth = &nfc->dev->dev_data.mf_ul_auth;
FuriString* password_str =
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);
}
@ -28,6 +56,26 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
bool consumed = false;
MfUltralightAuthMethod auth_method = nfc->dev->dev_data.mf_ul_data.auth_method;
if(auth_method == MfUltralightAuthMethodManual || auth_method == MfUltralightAuthMethodAuto) {
if(event.type == SceneManagerEventTypeCustom) {
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;
}
} else {
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultCenter) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadAuth);
@ -35,6 +83,7 @@ bool nfc_scene_mf_ultralight_unlock_warn_on_event(void* context, SceneManagerEve
consumed = true;
}
}
}
return consumed;
}
@ -43,5 +92,7 @@ void nfc_scene_mf_ultralight_unlock_warn_on_exit(void* context) {
Nfc* nfc = context;
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);
if(data->data_size > data->data_read) {
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) {
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.event == GuiButtonTypeRight) {
if(protocol == NfcDeviceProtocolMifareDesfire) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData);
consumed = true;
} else if(protocol == NfcDeviceProtocolMifareUl) {
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;
} else if(event.event == NfcWorkerEventReadMfUltralight) {
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);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
consumed = true;

View File

@ -11,6 +11,8 @@ enum SubmenuIndex {
SubmenuIndexDelete,
SubmenuIndexInfo,
SubmenuIndexRestoreOriginal,
SubmenuIndexMfUlUnlockByReader,
SubmenuIndexMfUlUnlockByPassword,
};
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, "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) {
submenu_add_item(
submenu,
@ -106,6 +123,7 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
consumed = true;
} else if(event.event == SubmenuIndexWrite) {
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) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);
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->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "SubGhzChat");
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->thread =
furi_thread_alloc_ex("SubGhzChat", 2048, subghz_chat_worker_thread, instance);
instance->subghz_txrx = subghz_tx_rx_worker_alloc();
instance->event_queue = furi_message_queue_alloc(80, sizeof(SubGhzChatEvent));
return instance;

View File

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

View File

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

View File

@ -3,6 +3,8 @@
#include <furi_hal.h>
#include "../subghz_i.h"
#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -93.0f
typedef struct SubGhzFrequencyAnalyzerWorker SubGhzFrequencyAnalyzerWorker;
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();
printf("\r\nPackets recieved %u\r\n", instance->packet_count);
printf("\r\nPackets received %u\r\n", instance->packet_count);
// Cleanup
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
subghz_receiver_free(receiver);
@ -438,7 +438,7 @@ static void subghz_cli_command_print_usage() {
printf("\r\n");
printf(" debug cmd:\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(
"\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
printf(

View File

@ -13,8 +13,6 @@
#include <assets_icons.h>
#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) \
snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000);
@ -49,7 +47,7 @@ typedef struct {
} SubGhzFrequencyAnalyzerModel;
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(
@ -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) {
uint8_t column_number = 0;
if(rssi) {
rssi = rssi / 3;
rssi = rssi / 3 + 2;
if(rssi > 20) rssi = 20;
for(uint8_t i = 1; i < rssi; i++) {
if(i > 20) break;
if(i % 4) {
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);
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, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records");
canvas, offset_x + 64, offset_y + 8, AlignCenter, AlignBottom, "No records");
return;
} else if(items_count > 3) {
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);
// 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));
}
@ -167,25 +178,20 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel
} else {
canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");
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);
}
// Frequency
canvas_set_font(canvas, FontBigNumbers);
snprintf(
buffer,
sizeof(buffer),
"%03ld.%03ld",
model->frequency / 1000000 % 1000,
model->frequency / 1000 % 1000);
SNPRINTF_FREQUENCY(buffer, model->frequency);
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_draw_str(canvas, 8, 30, buffer);
canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11);
canvas_draw_str(canvas, 8, 29, buffer);
canvas_draw_icon(canvas, 96, 18, &I_MHz_25x11);
}
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;
}
(*item)->frequency = model->frequency;
(*item)->count = 1u;
(*item)->count = 1;
(*item)->rssi_max = model->rssi;
(*item)->seq = items_count;
return true;
@ -394,9 +400,9 @@ void subghz_frequency_analyzer_enter(void* context) {
model->frequency = 0;
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
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[2] = 0u;
model->history_frequency[2] = 0;
SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency);
},
true);
@ -416,13 +422,13 @@ void subghz_frequency_analyzer_exit(void* context) {
instance->view,
SubGhzFrequencyAnalyzerModel * model,
{
model->rssi = 0u;
model->rssi = 0;
model->frequency = 0;
model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain;
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[2] = 0u;
model->history_frequency[2] = 0;
SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency);
},
true);

View File

@ -58,13 +58,13 @@ struct U2fHid_packet {
struct U2fHid {
FuriThread* thread;
FuriTimer* lock_timer;
struct U2fHid_packet packet;
uint8_t seq_id_last;
uint16_t req_buf_ptr;
uint32_t req_len_left;
uint32_t lock_cid;
bool lock;
U2fData* u2f_instance;
struct U2fHid_packet packet;
};
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) {
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(len_cur < 7) {
u2f_hid->req_len_left = 0;
break; // Wrong chunk len
}
// Init packet
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)) {
u2f_hid->req_len_left = u2f_hid->packet.len - (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;
if(len_cur > 0) memcpy(u2f_hid->packet.payload, &packet_buf[7], len_cur);
} else {
if(len_cur < 5) {
u2f_hid->req_len_left = 0;
break; // Wrong chunk len
}
// Continuation packet
if(u2f_hid->req_len_left > 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);
}
}
}
} while(0);
}
if(flags & WorkerEvtUnlock) {
u2f_hid->lock = false;
@ -282,11 +297,7 @@ U2fHid* u2f_hid_start(U2fData* u2f_inst) {
u2f_hid->u2f_instance = u2f_inst;
u2f_hid->thread = furi_thread_alloc();
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);
u2f_hid->thread = furi_thread_alloc_ex("U2fHidWorker", 2048, u2f_hid_worker, u2f_hid);
furi_thread_start(u2f_hid->thread);
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) {
canvas_draw_icon(canvas, 22, 15, &I_Connected_62x31);
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) {
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
furi_hal_usb_set_config(usb_config_prev, NULL);
dap_common_wait_for_deinit();
dap_common_usb_free_name();
dap_deinit_gpio(swd_pins_prev);
return 0;
@ -441,19 +440,6 @@ static int32_t cdc_process(void* p) {
/******************************* 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() {
DapApp* dap_app = malloc(sizeof(DapApp));
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_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_control(dev, hid_control);
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) {
dap_state.usb_dev = NULL;
@ -647,12 +636,6 @@ static void hid_deinit(usbd_device* dev) {
usbd_reg_config(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) {

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_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_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include <gui/elements.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;
Hid* hid;
};
typedef struct {
@ -24,7 +25,7 @@ typedef struct {
bool back_pressed;
bool connected;
char key_string[5];
} BtHidKeyboardModel;
} HidKeyboardModel;
typedef struct {
uint8_t width;
@ -32,13 +33,12 @@ typedef struct {
const Icon* icon;
char* shift_key;
uint8_t value;
} BtHidKeyboardKey;
} HidKeyboardKey;
typedef struct {
int8_t x;
int8_t y;
} BtHidKeyboardPoint;
} HidKeyboardPoint;
// 4 BY 12
#define MARGIN_TOP 0
#define MARGIN_LEFT 4
@ -49,7 +49,7 @@ typedef struct {
#define COLUMN_COUNT 12
// 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 = "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 = 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 = 4, .icon = NULL, .key = " ", .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) {
*str = toupper((unsigned char)*str);
str++;
}
}
static void bt_hid_keyboard_draw_key(
static void hid_keyboard_draw_key(
Canvas* canvas,
BtHidKeyboardModel* model,
HidKeyboardModel* model,
uint8_t x,
uint8_t y,
BtHidKeyboardKey key,
HidKeyboardKey key,
bool selected) {
if(!key.width) return;
@ -190,7 +190,7 @@ static void bt_hid_keyboard_draw_key(
if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) ||
(model->alt && key.value == HID_KEYBOARD_L_ALT) ||
(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,
@ -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);
BtHidKeyboardModel* model = context;
HidKeyboardModel* model = context;
// Header
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)
uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0;
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;
for(uint8_t i = 0; i < COLUMN_COUNT; i++) {
BtHidKeyboardKey key = keyboardKeyRow[i];
HidKeyboardKey key = keyboardKeyRow[i];
// Select when the button is hovered
// Select if the button is hovered within its width
// Select if back is clicked and its the backspace key
// Deselect when the button clicked or not hovered
bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y;
bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE;
bt_hid_keyboard_draw_key(
hid_keyboard_draw_key(
canvas,
model,
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) {
BtHidKeyboardKey key = bt_hid_keyboard_keyset[model->y][model->x];
static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) {
HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x];
// Use upper case if shift is toggled
bool useUppercase = model->shift;
// 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;
}
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
do {
if(((int8_t)model->y) + delta.y < 0)
model->y = ROW_COUNT - 1;
else
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 {
if(((int8_t)model->x) + delta.x < 0)
model->x = COLUMN_COUNT - 1;
else
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
}
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(
bt_hid_keyboard->view,
BtHidKeyboardModel * model,
hid_keyboard->view,
HidKeyboardModel * model,
{
if(event->key == InputKeyOk) {
if(event->type == InputTypePress) {
model->ok_pressed = true;
} 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
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
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) {
// 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;
}
} else if(event->key == InputKeyBack) {
@ -325,66 +327,67 @@ static void bt_hid_keyboard_process(BtHidKeyboard* bt_hid_keyboard, InputEvent*
if(event->type == InputTypePress) {
model->back_pressed = true;
} else if(event->type == InputTypeShort) {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE);
hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE);
hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE);
} else if(event->type == InputTypeRelease) {
model->back_pressed = false;
}
} else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
// Cycle the selected keys
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) {
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) {
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) {
bt_hid_keyboard_get_select_key(model, (BtHidKeyboardPoint){.x = 1, .y = 0});
hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0});
}
}
},
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);
BtHidKeyboard* bt_hid_keyboard = context;
HidKeyboard* hid_keyboard = context;
bool consumed = false;
if(event->type == InputTypeLong && event->key == InputKeyBack) {
furi_hal_bt_hid_kb_release_all();
hid_hal_keyboard_release_all(hid_keyboard->hid);
} else {
bt_hid_keyboard_process(bt_hid_keyboard, event);
hid_keyboard_process(hid_keyboard, event);
consumed = true;
}
return consumed;
}
BtHidKeyboard* bt_hid_keyboard_alloc() {
BtHidKeyboard* bt_hid_keyboard = malloc(sizeof(BtHidKeyboard));
bt_hid_keyboard->view = view_alloc();
view_set_context(bt_hid_keyboard->view, bt_hid_keyboard);
view_allocate_model(bt_hid_keyboard->view, ViewModelTypeLocking, sizeof(BtHidKeyboardModel));
view_set_draw_callback(bt_hid_keyboard->view, bt_hid_keyboard_draw_callback);
view_set_input_callback(bt_hid_keyboard->view, bt_hid_keyboard_input_callback);
HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) {
HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard));
hid_keyboard->view = view_alloc();
hid_keyboard->hid = bt_hid;
view_set_context(hid_keyboard->view, hid_keyboard);
view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel));
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) {
furi_assert(bt_hid_keyboard);
view_free(bt_hid_keyboard->view);
free(bt_hid_keyboard);
void hid_keyboard_free(HidKeyboard* hid_keyboard) {
furi_assert(hid_keyboard);
view_free(hid_keyboard->view);
free(hid_keyboard);
}
View* bt_hid_keyboard_get_view(BtHidKeyboard* bt_hid_keyboard) {
furi_assert(bt_hid_keyboard);
return bt_hid_keyboard->view;
View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) {
furi_assert(hid_keyboard);
return hid_keyboard->view;
}
void bt_hid_keyboard_set_connected_status(BtHidKeyboard* bt_hid_keyboard, bool connected) {
furi_assert(bt_hid_keyboard);
void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) {
furi_assert(hid_keyboard);
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 <furi.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include "hid_keynote.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;
Hid* hid;
};
typedef struct {
@ -18,9 +19,9 @@ typedef struct {
bool ok_pressed;
bool back_pressed;
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);
if(dir == CanvasDirectionBottomToTop) {
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);
BtHidKeynoteModel* model = context;
HidKeynoteModel* model = context;
// Header
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);
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);
// 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);
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);
// 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);
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);
// 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);
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);
// 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");
}
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(
bt_hid_keynote->view,
BtHidKeynoteModel * model,
hid_keynote->view,
HidKeynoteModel * model,
{
if(event->type == InputTypePress) {
if(event->key == InputKeyUp) {
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) {
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) {
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) {
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) {
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) {
model->back_pressed = true;
}
} else if(event->type == InputTypeRelease) {
if(event->key == InputKeyUp) {
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) {
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) {
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) {
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) {
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) {
model->back_pressed = false;
}
} else if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) {
furi_hal_bt_hid_kb_press(HID_KEYBOARD_DELETE);
furi_hal_bt_hid_kb_release(HID_KEYBOARD_DELETE);
furi_hal_bt_hid_consumer_key_press(HID_CONSUMER_AC_BACK);
furi_hal_bt_hid_consumer_key_release(HID_CONSUMER_AC_BACK);
hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE);
hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE);
hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK);
hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK);
}
}
},
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);
BtHidKeynote* bt_hid_keynote = context;
HidKeynote* hid_keynote = context;
bool consumed = false;
if(event->type == InputTypeLong && event->key == InputKeyBack) {
furi_hal_bt_hid_kb_release_all();
hid_hal_keyboard_release_all(hid_keynote->hid);
} else {
bt_hid_keynote_process(bt_hid_keynote, event);
hid_keynote_process(hid_keynote, event);
consumed = true;
}
return consumed;
}
BtHidKeynote* bt_hid_keynote_alloc() {
BtHidKeynote* bt_hid_keynote = malloc(sizeof(BtHidKeynote));
bt_hid_keynote->view = view_alloc();
view_set_context(bt_hid_keynote->view, bt_hid_keynote);
view_allocate_model(bt_hid_keynote->view, ViewModelTypeLocking, sizeof(BtHidKeynoteModel));
view_set_draw_callback(bt_hid_keynote->view, bt_hid_keynote_draw_callback);
view_set_input_callback(bt_hid_keynote->view, bt_hid_keynote_input_callback);
HidKeynote* hid_keynote_alloc(Hid* hid) {
HidKeynote* hid_keynote = malloc(sizeof(HidKeynote));
hid_keynote->view = view_alloc();
hid_keynote->hid = hid;
view_set_context(hid_keynote->view, hid_keynote);
view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel));
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) {
furi_assert(bt_hid_keynote);
view_free(bt_hid_keynote->view);
free(bt_hid_keynote);
void hid_keynote_free(HidKeynote* hid_keynote) {
furi_assert(hid_keynote);
view_free(hid_keynote->view);
free(hid_keynote);
}
View* bt_hid_keynote_get_view(BtHidKeynote* bt_hid_keynote) {
furi_assert(bt_hid_keynote);
return bt_hid_keynote->view;
View* hid_keynote_get_view(HidKeynote* hid_keynote) {
furi_assert(hid_keynote);
return hid_keynote->view;
}
void bt_hid_keynote_set_connected_status(BtHidKeynote* bt_hid_keynote, bool connected) {
furi_assert(bt_hid_keynote);
void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) {
furi_assert(hid_keynote);
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_hal_bt_hid.h>
#include <furi_hal_usb_hid.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;
Hid* hid;
};
typedef struct {
@ -17,9 +21,9 @@ typedef struct {
bool down_pressed;
bool ok_pressed;
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);
if(dir == CanvasDirectionBottomToTop) {
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);
BtHidMediaModel* model = context;
HidMediaModel* model = context;
// Header
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_color(canvas, ColorWhite);
}
bt_hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
bt_hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft);
hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft);
canvas_set_color(canvas, ColorBlack);
// Right
@ -87,8 +91,8 @@ static void bt_hid_media_draw_callback(Canvas* canvas, void* context) {
canvas_set_bitmap_mode(canvas, 0);
canvas_set_color(canvas, ColorWhite);
}
bt_hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
bt_hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight);
hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight);
canvas_set_color(canvas, ColorBlack);
// 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_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, 102, 29, 102, 33);
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");
}
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(
bt_hid_media->view,
BtHidMediaModel * model,
hid_media->view,
HidMediaModel * model,
{
if(event->key == InputKeyUp) {
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) {
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) {
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) {
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) {
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);
}
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(
bt_hid_media->view,
BtHidMediaModel * model,
hid_media->view,
HidMediaModel * model,
{
if(event->key == InputKeyUp) {
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) {
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) {
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) {
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) {
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);
}
static bool bt_hid_media_input_callback(InputEvent* event, void* context) {
static bool hid_media_input_callback(InputEvent* event, void* context) {
furi_assert(context);
BtHidMedia* bt_hid_media = context;
HidMedia* hid_media = context;
bool consumed = false;
if(event->type == InputTypePress) {
bt_hid_media_process_press(bt_hid_media, event);
hid_media_process_press(hid_media, event);
consumed = true;
} else if(event->type == InputTypeRelease) {
bt_hid_media_process_release(bt_hid_media, event);
hid_media_process_release(hid_media, event);
consumed = true;
} else if(event->type == InputTypeShort) {
if(event->key == InputKeyBack) {
furi_hal_bt_hid_consumer_key_release_all();
hid_hal_consumer_key_release_all(hid_media->hid);
}
}
return consumed;
}
BtHidMedia* bt_hid_media_alloc() {
BtHidMedia* bt_hid_media = malloc(sizeof(BtHidMedia));
bt_hid_media->view = view_alloc();
view_set_context(bt_hid_media->view, bt_hid_media);
view_allocate_model(bt_hid_media->view, ViewModelTypeLocking, sizeof(BtHidMediaModel));
view_set_draw_callback(bt_hid_media->view, bt_hid_media_draw_callback);
view_set_input_callback(bt_hid_media->view, bt_hid_media_input_callback);
HidMedia* hid_media_alloc(Hid* hid) {
HidMedia* hid_media = malloc(sizeof(HidMedia));
hid_media->view = view_alloc();
hid_media->hid = hid;
view_set_context(hid_media->view, hid_media);
view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel));
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) {
furi_assert(bt_hid_media);
view_free(bt_hid_media->view);
free(bt_hid_media);
void hid_media_free(HidMedia* hid_media) {
furi_assert(hid_media);
view_free(hid_media->view);
free(hid_media);
}
View* bt_hid_media_get_view(BtHidMedia* bt_hid_media) {
furi_assert(bt_hid_media);
return bt_hid_media->view;
View* hid_media_get_view(HidMedia* hid_media) {
furi_assert(hid_media);
return hid_media->view;
}
void bt_hid_media_set_connected_status(BtHidMedia* bt_hid_media, bool connected) {
furi_assert(bt_hid_media);
void hid_media_set_connected_status(HidMedia* hid_media, bool connected) {
furi_assert(hid_media);
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