Merge branch 'dev' into release-candidate
This commit is contained in:
commit
57689a1e0f
@ -11,7 +11,6 @@ extern int32_t gui_srv(void* p);
|
||||
extern int32_t input_srv(void* p);
|
||||
extern int32_t loader_srv(void* p);
|
||||
extern int32_t notification_srv(void* p);
|
||||
extern int32_t power_observer_srv(void* p);
|
||||
extern int32_t power_srv(void* p);
|
||||
extern int32_t storage_srv(void* p);
|
||||
extern int32_t desktop_srv(void* p);
|
||||
@ -115,10 +114,6 @@ const FlipperApplication FLIPPER_SERVICES[] = {
|
||||
{.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_POWER_OBSERVER
|
||||
{.app = power_observer_srv, .name = "PowerAuditSrv", .stack_size = 1024, .icon = NULL},
|
||||
#endif
|
||||
|
||||
#ifdef SRV_STORAGE
|
||||
{.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL},
|
||||
#endif
|
||||
|
||||
@ -18,7 +18,6 @@ SRV_INPUT = 1
|
||||
SRV_LOADER = 1
|
||||
SRV_NOTIFICATION = 1
|
||||
SRV_POWER = 1
|
||||
SRV_POWER_OBSERVER = 1
|
||||
SRV_RPC = 1
|
||||
SRV_STORAGE = 1
|
||||
|
||||
@ -256,13 +255,6 @@ endif
|
||||
endif
|
||||
|
||||
|
||||
SRV_POWER_OBSERVER ?= 0
|
||||
ifeq ($(SRV_POWER_OBSERVER), 1)
|
||||
CFLAGS += -DSRV_POWER_OBSERVER
|
||||
SRV_POWER = 1
|
||||
endif
|
||||
|
||||
|
||||
SRV_POWER ?= 0
|
||||
ifeq ($(SRV_POWER), 1)
|
||||
CFLAGS += -DSRV_POWER
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "elements.h"
|
||||
#include "m-core.h"
|
||||
#include <assets_icons.h>
|
||||
#include "furi_hal_resources.h"
|
||||
#include <furi_hal.h>
|
||||
@ -36,30 +37,15 @@ void elements_progress_bar(
|
||||
furi_assert(canvas);
|
||||
furi_assert(total > 0);
|
||||
uint8_t height = 9;
|
||||
uint8_t marker_width = 7;
|
||||
furi_assert(width > marker_width);
|
||||
|
||||
uint8_t progress_length = ((float)progress / total) * (width - marker_width - 2);
|
||||
uint8_t progress_length = roundf(((float)progress / total) * (width - 2));
|
||||
|
||||
// rframe doesnt work if (radius * 2) > any rect side, so write manually
|
||||
uint8_t x_max = x + width - 1;
|
||||
uint8_t y_max = y + height - 1;
|
||||
canvas_draw_line(canvas, x + 3, y, x_max - 3, y);
|
||||
canvas_draw_line(canvas, x_max - 3, y, x_max, y + 3);
|
||||
canvas_draw_line(canvas, x_max, y + 3, x_max, y_max - 3);
|
||||
canvas_draw_line(canvas, x_max, y_max - 3, x_max - 3, y_max);
|
||||
canvas_draw_line(canvas, x_max - 3, y_max, x + 3, y_max);
|
||||
canvas_draw_line(canvas, x + 3, y_max, x, y_max - 3);
|
||||
canvas_draw_line(canvas, x, y_max - 3, x, y + 3);
|
||||
canvas_draw_line(canvas, x, y + 3, x + 3, y);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_rframe(canvas, x, y, width, height, 3);
|
||||
|
||||
canvas_draw_rbox(canvas, x + 1, y + 1, marker_width + progress_length, height - 2, 3);
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_dot(canvas, x + progress_length + 3, y + 2);
|
||||
canvas_draw_dot(canvas, x + progress_length + 4, y + 2);
|
||||
canvas_draw_dot(canvas, x + progress_length + 5, y + 3);
|
||||
canvas_draw_dot(canvas, x + progress_length + 6, y + 4);
|
||||
canvas_invert_color(canvas);
|
||||
canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2);
|
||||
}
|
||||
|
||||
void elements_scrollbar_pos(
|
||||
@ -202,6 +188,48 @@ void elements_button_center(Canvas* canvas, const char* str) {
|
||||
canvas_invert_color(canvas);
|
||||
}
|
||||
|
||||
static size_t
|
||||
elements_get_max_chars_to_fit(Canvas* canvas, Align horizontal, const char* text, uint8_t x) {
|
||||
const char* end = strchr(text, '\n');
|
||||
if(end == NULL) {
|
||||
end = text + strlen(text);
|
||||
}
|
||||
size_t text_size = end - text;
|
||||
string_t str;
|
||||
string_init_set_str(str, text);
|
||||
string_left(str, text_size);
|
||||
size_t result = 0;
|
||||
|
||||
uint16_t len_px = canvas_string_width(canvas, string_get_cstr(str));
|
||||
uint8_t px_left = 0;
|
||||
if(horizontal == AlignCenter) {
|
||||
px_left = canvas_width(canvas) - (x - len_px / 2);
|
||||
} else if(horizontal == AlignLeft) {
|
||||
px_left = canvas_width(canvas) - x;
|
||||
} else if(horizontal == AlignRight) {
|
||||
px_left = x;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
|
||||
if(len_px > px_left) {
|
||||
uint8_t excess_symbols_approximately =
|
||||
((float)len_px - px_left) / ((float)len_px / text_size);
|
||||
// reduce to 5 to be sure dash fit, and next line will be at least 5 symbols long
|
||||
excess_symbols_approximately = MAX(excess_symbols_approximately, 5);
|
||||
if(text_size > (excess_symbols_approximately + 5)) {
|
||||
result = text_size - excess_symbols_approximately - 5;
|
||||
} else {
|
||||
result = text_size - 1;
|
||||
}
|
||||
} else {
|
||||
result = text_size;
|
||||
}
|
||||
|
||||
string_clear(str);
|
||||
return result;
|
||||
}
|
||||
|
||||
void elements_multiline_text_aligned(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
@ -212,64 +240,40 @@ void elements_multiline_text_aligned(
|
||||
furi_assert(canvas);
|
||||
furi_assert(text);
|
||||
|
||||
uint8_t lines_count = 0;
|
||||
uint8_t font_height = canvas_current_font_height(canvas);
|
||||
string_t str;
|
||||
string_init(str);
|
||||
const char* start = text;
|
||||
char* end;
|
||||
string_t line;
|
||||
|
||||
// get lines count
|
||||
uint8_t i, lines_count;
|
||||
for(i = 0, lines_count = 0; text[i]; i++) lines_count += (text[i] == '\n');
|
||||
|
||||
switch(vertical) {
|
||||
case AlignBottom:
|
||||
y -= font_height * lines_count;
|
||||
break;
|
||||
case AlignCenter:
|
||||
y -= (font_height * lines_count) / 2;
|
||||
break;
|
||||
case AlignTop:
|
||||
default:
|
||||
break;
|
||||
/* go through text line by line and count lines */
|
||||
for(const char* start = text; start[0];) {
|
||||
size_t chars_fit = elements_get_max_chars_to_fit(canvas, horizontal, start, x);
|
||||
++lines_count;
|
||||
start += chars_fit;
|
||||
start += start[0] == '\n' ? 1 : 0;
|
||||
}
|
||||
|
||||
do {
|
||||
end = strchr(start, '\n');
|
||||
if(vertical == AlignBottom) {
|
||||
y -= font_height * (lines_count - 1);
|
||||
} else if(vertical == AlignCenter) {
|
||||
y -= (font_height * (lines_count - 1)) / 2;
|
||||
}
|
||||
|
||||
if(end) {
|
||||
string_set_strn(str, start, end - start);
|
||||
/* go through text line by line and print them */
|
||||
for(const char* start = text; start[0];) {
|
||||
size_t chars_fit = elements_get_max_chars_to_fit(canvas, horizontal, start, x);
|
||||
|
||||
if((start[chars_fit] == '\n') || (start[chars_fit] == 0)) {
|
||||
string_init_printf(line, "%.*s", chars_fit, start);
|
||||
} else {
|
||||
string_set_str(str, start);
|
||||
string_init_printf(line, "%.*s-\n", chars_fit, start);
|
||||
}
|
||||
|
||||
uint16_t len_px = canvas_string_width(canvas, string_get_cstr(str));
|
||||
uint8_t px_left =
|
||||
canvas_width(canvas) - (x - (horizontal == AlignCenter ? len_px / 2 : 0));
|
||||
|
||||
// hacky
|
||||
if(len_px > px_left) {
|
||||
string_t buff;
|
||||
string_init_set(buff, str);
|
||||
size_t s_len = string_size(str);
|
||||
uint8_t end_pos = s_len - ((len_px - px_left) / (len_px / s_len) + 5);
|
||||
|
||||
string_left(buff, end_pos);
|
||||
string_cat(buff, "-");
|
||||
string_right(str, end_pos);
|
||||
|
||||
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(buff));
|
||||
string_clear(buff);
|
||||
|
||||
start = end + 1;
|
||||
y += font_height;
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(str));
|
||||
start = end + 1;
|
||||
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line));
|
||||
string_clear(line);
|
||||
y += font_height;
|
||||
} while(end);
|
||||
string_clear(str);
|
||||
|
||||
start += chars_fit;
|
||||
start += start[0] == '\n' ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text) {
|
||||
|
||||
@ -99,8 +99,6 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
furi_assert(_model);
|
||||
|
||||
ButtonMenuModel* model = (ButtonMenuModel*)_model;
|
||||
|
||||
canvas_clear(canvas);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
|
||||
uint8_t item_position = 0;
|
||||
@ -117,11 +115,14 @@ static void button_menu_view_draw_callback(Canvas* canvas, void* _model) {
|
||||
canvas_draw_icon(canvas, 28, 123, &I_IrdaArrowDown_4x8);
|
||||
}
|
||||
|
||||
string_t disp_str;
|
||||
string_init_set_str(disp_str, model->header);
|
||||
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
|
||||
canvas_draw_str_aligned(canvas, 32, 10, AlignCenter, AlignCenter, string_get_cstr(disp_str));
|
||||
string_clear(disp_str);
|
||||
if(model->header) {
|
||||
string_t disp_str;
|
||||
string_init_set_str(disp_str, model->header);
|
||||
elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6);
|
||||
canvas_draw_str_aligned(
|
||||
canvas, 32, 10, AlignCenter, AlignCenter, string_get_cstr(disp_str));
|
||||
string_clear(disp_str);
|
||||
}
|
||||
|
||||
for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it);
|
||||
ButtonMenuItemArray_next(it), ++item_position) {
|
||||
@ -248,6 +249,7 @@ void button_menu_reset(ButtonMenu* button_menu) {
|
||||
button_menu->view, (ButtonMenuModel * model) {
|
||||
ButtonMenuItemArray_reset(model->items);
|
||||
model->position = 0;
|
||||
model->header = NULL;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@ -40,91 +40,6 @@ void input_isr(void* _ctx) {
|
||||
osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR);
|
||||
}
|
||||
|
||||
#ifdef SRV_CLI
|
||||
void input_cli_send(Cli* cli, string_t args, void* context) {
|
||||
InputEvent event;
|
||||
|
||||
// Get first word as key name
|
||||
string_t key_name;
|
||||
string_init(key_name);
|
||||
size_t ws = string_search_char(args, ' ');
|
||||
if(ws == STRING_FAILURE) {
|
||||
printf("Invalid arguments. Use `input_send KEY TYPE`.");
|
||||
string_clear(key_name);
|
||||
return;
|
||||
} else {
|
||||
string_set_n(key_name, args, 0, ws);
|
||||
string_right(args, ws);
|
||||
string_strim(args);
|
||||
}
|
||||
// Check key name and set event key
|
||||
if(!string_cmp(key_name, "up")) {
|
||||
event.key = InputKeyUp;
|
||||
} else if(!string_cmp(key_name, "down")) {
|
||||
event.key = InputKeyDown;
|
||||
} else if(!string_cmp(key_name, "left")) {
|
||||
event.key = InputKeyLeft;
|
||||
} else if(!string_cmp(key_name, "right")) {
|
||||
event.key = InputKeyRight;
|
||||
} else if(!string_cmp(key_name, "ok")) {
|
||||
event.key = InputKeyOk;
|
||||
} else if(!string_cmp(key_name, "back")) {
|
||||
event.key = InputKeyBack;
|
||||
} else {
|
||||
printf("Invalid key name. Valid keys: `up`, `down`, `left`, `right`, `back`, `ok`.");
|
||||
string_clear(key_name);
|
||||
return;
|
||||
}
|
||||
string_clear(key_name);
|
||||
// Check the rest of args string and set event type
|
||||
if(!string_cmp(args, "press")) {
|
||||
event.type = InputTypePress;
|
||||
} else if(!string_cmp(args, "release")) {
|
||||
event.type = InputTypeRelease;
|
||||
} else if(!string_cmp(args, "short")) {
|
||||
event.type = InputTypeShort;
|
||||
} else if(!string_cmp(args, "long")) {
|
||||
event.type = InputTypeLong;
|
||||
} else {
|
||||
printf("Ivalid type. Valid types: `press`, `release`, `short`, `long`.");
|
||||
return;
|
||||
}
|
||||
// Publish input event
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
}
|
||||
|
||||
static void input_cli_dump_events_callback(const void* value, void* ctx) {
|
||||
furi_assert(value);
|
||||
furi_assert(ctx);
|
||||
osMessageQueueId_t input_queue = ctx;
|
||||
osMessageQueuePut(input_queue, value, 0, osWaitForever);
|
||||
}
|
||||
|
||||
static void input_cli_dump(Cli* cli, string_t args, void* context) {
|
||||
osMessageQueueId_t input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
|
||||
FuriPubSubSubscription* input_subscription =
|
||||
furi_pubsub_subscribe(input->event_pubsub, input_cli_dump_events_callback, input_queue);
|
||||
|
||||
bool stop = false;
|
||||
InputEvent input_event;
|
||||
while(!stop) {
|
||||
if(osMessageQueueGet(input_queue, &input_event, NULL, 100) == osOK) {
|
||||
printf(
|
||||
"key: %s type: %s\r\n",
|
||||
input_get_key_name(input_event.key),
|
||||
input_get_type_name(input_event.type));
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) {
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
furi_pubsub_unsubscribe(input->event_pubsub, input_subscription);
|
||||
osMessageQueueDelete(input_queue);
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* input_get_key_name(InputKey key) {
|
||||
for(size_t i = 0; i < input_pins_count; i++) {
|
||||
if(input_pins[i].key == key) {
|
||||
@ -159,10 +74,7 @@ int32_t input_srv() {
|
||||
#ifdef SRV_CLI
|
||||
input->cli = furi_record_open("cli");
|
||||
if(input->cli) {
|
||||
cli_add_command(
|
||||
input->cli, "input_send", CliCommandFlagParallelSafe, input_cli_send, NULL);
|
||||
cli_add_command(
|
||||
input->cli, "input_dump", CliCommandFlagParallelSafe, input_cli_dump, NULL);
|
||||
cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
127
applications/input/input_cli.c
Normal file
127
applications/input/input_cli.c
Normal file
@ -0,0 +1,127 @@
|
||||
#include "input_i.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
static void input_cli_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("input <cmd> <args>\r\n");
|
||||
printf("Cmd list:\r\n");
|
||||
printf("\tdump\t\t\t - dump input events\r\n");
|
||||
printf("\tsend <key> <type>\t - send input event\r\n");
|
||||
}
|
||||
|
||||
static void input_cli_dump_events_callback(const void* value, void* ctx) {
|
||||
furi_assert(value);
|
||||
furi_assert(ctx);
|
||||
osMessageQueueId_t input_queue = ctx;
|
||||
osMessageQueuePut(input_queue, value, 0, osWaitForever);
|
||||
}
|
||||
|
||||
static void input_cli_dump(Cli* cli, string_t args, Input* input) {
|
||||
osMessageQueueId_t input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
|
||||
FuriPubSubSubscription* input_subscription =
|
||||
furi_pubsub_subscribe(input->event_pubsub, input_cli_dump_events_callback, input_queue);
|
||||
|
||||
bool stop = false;
|
||||
InputEvent input_event;
|
||||
while(!stop) {
|
||||
if(osMessageQueueGet(input_queue, &input_event, NULL, 100) == osOK) {
|
||||
printf(
|
||||
"key: %s type: %s\r\n",
|
||||
input_get_key_name(input_event.key),
|
||||
input_get_type_name(input_event.type));
|
||||
}
|
||||
|
||||
if(cli_cmd_interrupt_received(cli)) {
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
|
||||
furi_pubsub_unsubscribe(input->event_pubsub, input_subscription);
|
||||
osMessageQueueDelete(input_queue);
|
||||
}
|
||||
|
||||
static void input_cli_send_print_usage() {
|
||||
printf("Invalid arguments. Usage:\r\n");
|
||||
printf("\tinput send <key> <type>\r\n");
|
||||
printf("\t\t <key>\t - one of 'up', 'down', 'left', 'right', 'back', 'ok'\r\n");
|
||||
printf("\t\t <type>\t - one of 'press', 'release', 'short', 'long'\r\n");
|
||||
}
|
||||
|
||||
void input_cli_send(Cli* cli, string_t args, Input* input) {
|
||||
InputEvent event;
|
||||
string_t key_str;
|
||||
string_init(key_str);
|
||||
bool parsed = false;
|
||||
|
||||
do {
|
||||
// Parse Key
|
||||
if(!args_read_string_and_trim(args, key_str)) {
|
||||
break;
|
||||
}
|
||||
if(!string_cmp(key_str, "up")) {
|
||||
event.key = InputKeyUp;
|
||||
} else if(!string_cmp(key_str, "down")) {
|
||||
event.key = InputKeyDown;
|
||||
} else if(!string_cmp(key_str, "left")) {
|
||||
event.key = InputKeyLeft;
|
||||
} else if(!string_cmp(key_str, "right")) {
|
||||
event.key = InputKeyRight;
|
||||
} else if(!string_cmp(key_str, "ok")) {
|
||||
event.key = InputKeyOk;
|
||||
} else if(!string_cmp(key_str, "back")) {
|
||||
event.key = InputKeyBack;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
// Parse Type
|
||||
if(!string_cmp(args, "press")) {
|
||||
event.type = InputTypePress;
|
||||
} else if(!string_cmp(args, "release")) {
|
||||
event.type = InputTypeRelease;
|
||||
} else if(!string_cmp(args, "short")) {
|
||||
event.type = InputTypeShort;
|
||||
} else if(!string_cmp(args, "long")) {
|
||||
event.type = InputTypeLong;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
parsed = true;
|
||||
} while(false);
|
||||
|
||||
if(parsed) {
|
||||
furi_pubsub_publish(input->event_pubsub, &event);
|
||||
} else {
|
||||
input_cli_send_print_usage();
|
||||
}
|
||||
string_clear(key_str);
|
||||
}
|
||||
|
||||
void input_cli(Cli* cli, string_t args, void* context) {
|
||||
furi_assert(cli);
|
||||
furi_assert(context);
|
||||
Input* input = context;
|
||||
string_t cmd;
|
||||
string_init(cmd);
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
input_cli_usage();
|
||||
break;
|
||||
}
|
||||
if(string_cmp_str(cmd, "dump") == 0) {
|
||||
input_cli_dump(cli, args, input);
|
||||
break;
|
||||
}
|
||||
if(string_cmp_str(cmd, "send") == 0) {
|
||||
input_cli_send(cli, args, input);
|
||||
break;
|
||||
}
|
||||
|
||||
input_cli_usage();
|
||||
} while(false);
|
||||
|
||||
string_clear(cmd);
|
||||
}
|
||||
@ -44,3 +44,6 @@ void input_press_timer_callback(void* arg);
|
||||
|
||||
/** Input interrupt handler */
|
||||
void input_isr(void* _ctx);
|
||||
|
||||
/** Input CLI command handler */
|
||||
void input_cli(Cli* cli, string_t args, void* context);
|
||||
|
||||
@ -13,6 +13,17 @@
|
||||
#include <sys/types.h>
|
||||
#include "../helpers/irda_parser.h"
|
||||
|
||||
static void irda_cli_start_ir_rx(Cli* cli, string_t args);
|
||||
static void irda_cli_start_ir_tx(Cli* cli, string_t args);
|
||||
|
||||
static const struct {
|
||||
const char* cmd;
|
||||
void (*process_function)(Cli* cli, string_t args);
|
||||
} irda_cli_commands[] = {
|
||||
{.cmd = "rx", .process_function = irda_cli_start_ir_rx},
|
||||
{.cmd = "tx", .process_function = irda_cli_start_ir_tx},
|
||||
};
|
||||
|
||||
static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) {
|
||||
furi_assert(received_signal);
|
||||
char buf[100];
|
||||
@ -48,12 +59,7 @@ static void signal_received_callback(void* context, IrdaWorkerSignal* received_s
|
||||
}
|
||||
}
|
||||
|
||||
void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) {
|
||||
if(furi_hal_irda_is_busy()) {
|
||||
printf("IRDA is busy. Exit.");
|
||||
return;
|
||||
}
|
||||
|
||||
static void irda_cli_start_ir_rx(Cli* cli, string_t args) {
|
||||
IrdaWorker* worker = irda_worker_alloc();
|
||||
irda_worker_rx_start(worker);
|
||||
irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli);
|
||||
@ -68,7 +74,9 @@ void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
|
||||
static void irda_cli_print_usage(void) {
|
||||
printf("Usage:\r\n\tir_tx <protocol> <address> <command>\r\n");
|
||||
printf("Usage:\r\n");
|
||||
printf("\tir rx\r\n");
|
||||
printf("\tir tx <protocol> <address> <command>\r\n");
|
||||
printf("\t<command> and <address> are hex-formatted\r\n");
|
||||
printf("\tAvailable protocols:");
|
||||
for(int i = 0; irda_is_protocol_valid((IrdaProtocol)i); ++i) {
|
||||
@ -131,12 +139,7 @@ static bool parse_signal_raw(
|
||||
return irda_parser_is_raw_signal_valid(*frequency, *duty_cycle, *timings_cnt);
|
||||
}
|
||||
|
||||
void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
|
||||
if(furi_hal_irda_is_busy()) {
|
||||
printf("IRDA is busy. Exit.");
|
||||
return;
|
||||
}
|
||||
|
||||
static void irda_cli_start_ir_tx(Cli* cli, string_t args) {
|
||||
IrdaMessage message;
|
||||
const char* str = string_get_cstr(args);
|
||||
uint32_t frequency;
|
||||
@ -156,11 +159,38 @@ void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) {
|
||||
free(timings);
|
||||
}
|
||||
|
||||
static void irda_cli_start_ir(Cli* cli, string_t args, void* context) {
|
||||
if(furi_hal_irda_is_busy()) {
|
||||
printf("IRDA is busy. Exit.");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
for(; i < COUNT_OF(irda_cli_commands); ++i) {
|
||||
size_t size = strlen(irda_cli_commands[i].cmd);
|
||||
bool cmd_found = !strncmp(string_get_cstr(args), irda_cli_commands[i].cmd, size);
|
||||
if(cmd_found) {
|
||||
if(string_size(args) == size) {
|
||||
break;
|
||||
}
|
||||
if(string_get_cstr(args)[size] == ' ') {
|
||||
string_right(args, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(i < COUNT_OF(irda_cli_commands)) {
|
||||
irda_cli_commands[i].process_function(cli, args);
|
||||
} else {
|
||||
irda_cli_print_usage();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void irda_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = (Cli*)furi_record_open("cli");
|
||||
cli_add_command(cli, "ir_rx", CliCommandFlagDefault, irda_cli_start_ir_rx, NULL);
|
||||
cli_add_command(cli, "ir_tx", CliCommandFlagDefault, irda_cli_start_ir_tx, NULL);
|
||||
cli_add_command(cli, "ir", CliCommandFlagDefault, irda_cli_start_ir, NULL);
|
||||
furi_record_close("cli");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -96,26 +96,30 @@ bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
uint32_t address_length = irda_get_protocol_address_length(signal->protocol);
|
||||
uint32_t address_mask = (1LU << address_length) - 1;
|
||||
if(signal->address != (signal->address & address_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Address is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
address_mask,
|
||||
signal->address);
|
||||
result = false;
|
||||
if(result) {
|
||||
uint32_t address_length = irda_get_protocol_address_length(signal->protocol);
|
||||
uint32_t address_mask = (1LU << address_length) - 1;
|
||||
if(signal->address != (signal->address & address_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Address is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
address_mask,
|
||||
signal->address);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t command_length = irda_get_protocol_command_length(signal->protocol);
|
||||
uint32_t command_mask = (1LU << command_length) - 1;
|
||||
if(signal->command != (signal->command & command_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Command is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
command_mask,
|
||||
signal->command);
|
||||
result = false;
|
||||
if(result) {
|
||||
uint32_t command_length = irda_get_protocol_command_length(signal->protocol);
|
||||
uint32_t command_mask = (1LU << command_length) - 1;
|
||||
if(signal->command != (signal->command & command_mask)) {
|
||||
FURI_LOG_E(
|
||||
TAG,
|
||||
"Command is out of range (mask 0x%08lX): 0x%lX\r\n",
|
||||
command_mask,
|
||||
signal->command);
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@ -33,6 +33,7 @@ public:
|
||||
LearnSuccess,
|
||||
LearnEnterName,
|
||||
LearnDone,
|
||||
AskBack,
|
||||
Remote,
|
||||
RemoteList,
|
||||
Edit,
|
||||
@ -123,6 +124,7 @@ private:
|
||||
{Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()},
|
||||
{Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()},
|
||||
{Scene::LearnDone, new IrdaAppSceneLearnDone()},
|
||||
{Scene::AskBack, new IrdaAppSceneAskBack()},
|
||||
{Scene::Remote, new IrdaAppSceneRemote()},
|
||||
{Scene::RemoteList, new IrdaAppSceneRemoteList()},
|
||||
{Scene::Edit, new IrdaAppSceneEdit()},
|
||||
|
||||
@ -110,10 +110,11 @@ std::string IrdaAppRemoteManager::get_button_name(uint32_t index) {
|
||||
}
|
||||
|
||||
std::string IrdaAppRemoteManager::get_remote_name() {
|
||||
return remote ? remote->name : std::string();
|
||||
return remote.get() ? remote->name : std::string();
|
||||
}
|
||||
|
||||
int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) {
|
||||
furi_assert(remote.get() != nullptr);
|
||||
int i = 0;
|
||||
for(const auto& str : strings) {
|
||||
if(!str.compare(remote->name)) {
|
||||
|
||||
@ -70,8 +70,6 @@ IrdaAppSignal::IrdaAppSignal(const IrdaAppSignal& other) {
|
||||
}
|
||||
|
||||
IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) {
|
||||
clear_timings();
|
||||
|
||||
raw_signal = other.raw_signal;
|
||||
if(!raw_signal) {
|
||||
payload.message = other.payload.message;
|
||||
|
||||
@ -89,6 +89,13 @@ private:
|
||||
std::vector<std::string> remote_names;
|
||||
};
|
||||
|
||||
class IrdaAppSceneAskBack : public IrdaAppScene {
|
||||
public:
|
||||
void on_enter(IrdaApp* app) final;
|
||||
bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
|
||||
void on_exit(IrdaApp* app) final;
|
||||
};
|
||||
|
||||
class IrdaAppSceneEdit : public IrdaAppScene {
|
||||
public:
|
||||
void on_enter(IrdaApp* app) final;
|
||||
|
||||
71
applications/irda/scene/irda_app_scene_ask_back.cpp
Normal file
71
applications/irda/scene/irda_app_scene_ask_back.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "../irda_app.h"
|
||||
#include "gui/modules/dialog_ex.h"
|
||||
#include "irda.h"
|
||||
#include "irda/scene/irda_app_scene.h"
|
||||
#include <string>
|
||||
|
||||
static void dialog_result_callback(DialogExResult result, void* context) {
|
||||
auto app = static_cast<IrdaApp*>(context);
|
||||
IrdaAppEvent event;
|
||||
|
||||
event.type = IrdaAppEvent::Type::DialogExSelected;
|
||||
event.payload.dialog_ex_result = result;
|
||||
|
||||
app->get_view_manager()->send_event(&event);
|
||||
}
|
||||
|
||||
void IrdaAppSceneAskBack::on_enter(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
DialogEx* dialog_ex = view_manager->get_dialog_ex();
|
||||
|
||||
if(app->get_learn_new_remote()) {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to Infrared menu?", 64, 0, AlignCenter, AlignTop);
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Exit to remote menu?", 64, 0, AlignCenter, AlignTop);
|
||||
}
|
||||
|
||||
dialog_ex_set_text(
|
||||
dialog_ex, "All unsaved data\nwill be lost", 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Exit");
|
||||
dialog_ex_set_center_button_text(dialog_ex, nullptr);
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Stay");
|
||||
dialog_ex_set_result_callback(dialog_ex, dialog_result_callback);
|
||||
dialog_ex_set_context(dialog_ex, app);
|
||||
|
||||
view_manager->switch_to(IrdaAppViewManager::ViewType::DialogEx);
|
||||
}
|
||||
|
||||
bool IrdaAppSceneAskBack::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
bool consumed = false;
|
||||
|
||||
if(event->type == IrdaAppEvent::Type::DialogExSelected) {
|
||||
switch(event->payload.dialog_ex_result) {
|
||||
case DialogExResultLeft:
|
||||
consumed = true;
|
||||
if(app->get_learn_new_remote()) {
|
||||
app->search_and_switch_to_previous_scene({IrdaApp::Scene::Start});
|
||||
} else {
|
||||
app->search_and_switch_to_previous_scene(
|
||||
{IrdaApp::Scene::Edit, IrdaApp::Scene::Remote});
|
||||
}
|
||||
break;
|
||||
case DialogExResultCenter:
|
||||
furi_assert(0);
|
||||
break;
|
||||
case DialogExResultRight:
|
||||
app->switch_to_previous_scene();
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == IrdaAppEvent::Type::Back) {
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void IrdaAppSceneAskBack::on_exit(IrdaApp* app) {
|
||||
}
|
||||
@ -21,7 +21,7 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) {
|
||||
|
||||
if(app->get_edit_element() == IrdaApp::EditElement::Button) {
|
||||
auto signal = remote_manager->get_button_data(app->get_current_button());
|
||||
dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_header(dialog_ex, "Delete button?", 64, 0, AlignCenter, AlignTop);
|
||||
if(!signal.is_raw()) {
|
||||
auto message = &signal.get_message();
|
||||
app->set_text_store(
|
||||
@ -41,7 +41,7 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) {
|
||||
signal.get_raw_signal().timings_cnt);
|
||||
}
|
||||
} else {
|
||||
dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 6, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_header(dialog_ex, "Delete remote?", 64, 0, AlignCenter, AlignTop);
|
||||
app->set_text_store(
|
||||
0,
|
||||
"%s\n with %lu buttons",
|
||||
@ -49,7 +49,7 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) {
|
||||
remote_manager->get_number_of_buttons());
|
||||
}
|
||||
|
||||
dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 31, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Back");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Delete");
|
||||
|
||||
@ -5,7 +5,7 @@ void IrdaAppSceneEditDeleteDone::on_enter(IrdaApp* app) {
|
||||
Popup* popup = view_manager->get_popup();
|
||||
|
||||
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
|
||||
popup_set_text(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
|
||||
|
||||
popup_set_callback(popup, IrdaApp::popup_callback);
|
||||
popup_set_context(popup, app);
|
||||
@ -32,4 +32,7 @@ bool IrdaAppSceneEditDeleteDone::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
}
|
||||
|
||||
void IrdaAppSceneEditDeleteDone::on_exit(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
|
||||
app,
|
||||
app->get_text_store(0),
|
||||
enter_name_length,
|
||||
true);
|
||||
false);
|
||||
|
||||
view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ void IrdaAppSceneEditRenameDone::on_enter(IrdaApp* app) {
|
||||
|
||||
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
|
||||
|
||||
popup_set_text(popup, "Saved!", 13, 22, AlignLeft, AlignTop);
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
|
||||
popup_set_callback(popup, IrdaApp::popup_callback);
|
||||
popup_set_context(popup, app);
|
||||
|
||||
@ -69,4 +69,7 @@ bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
|
||||
void IrdaAppSceneLearn::on_exit(IrdaApp* app) {
|
||||
irda_worker_rx_stop(app->get_irda_worker());
|
||||
auto view_manager = app->get_view_manager();
|
||||
auto popup = view_manager->get_popup();
|
||||
popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||
}
|
||||
|
||||
@ -9,9 +9,9 @@ void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) {
|
||||
DOLPHIN_DEED(DolphinDeedIrSave);
|
||||
|
||||
if(app->get_learn_new_remote()) {
|
||||
popup_set_text(popup, "New remote\ncreated!", 5, 7, AlignLeft, AlignTop);
|
||||
popup_set_header(popup, "New remote\ncreated!", 0, 0, AlignLeft, AlignTop);
|
||||
} else {
|
||||
popup_set_text(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
popup_set_callback(popup, IrdaApp::popup_callback);
|
||||
@ -35,4 +35,7 @@ bool IrdaAppSceneLearnDone::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
|
||||
void IrdaAppSceneLearnDone::on_exit(IrdaApp* app) {
|
||||
app->set_learn_new_remote(false);
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
Popup* popup = view_manager->get_popup();
|
||||
popup_set_header(popup, nullptr, 0, 0, AlignLeft, AlignTop);
|
||||
}
|
||||
|
||||
@ -91,12 +91,17 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) {
|
||||
}
|
||||
}
|
||||
|
||||
if(event->type == IrdaAppEvent::Type::Back) {
|
||||
app->switch_to_next_scene(IrdaApp::Scene::AskBack);
|
||||
consumed = true;
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) {
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
DialogEx* dialog_ex = view_manager->get_dialog_ex();
|
||||
dialog_ex_set_center_button_text(dialog_ex, nullptr);
|
||||
dialog_ex_reset(dialog_ex);
|
||||
app->notify_green_off();
|
||||
}
|
||||
|
||||
@ -15,6 +15,11 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) {
|
||||
last_selected_remote.size() ? last_selected_remote.c_str() : nullptr;
|
||||
auto filename_ts = std::make_unique<TextStore>(IrdaAppRemoteManager::max_remote_name_length);
|
||||
|
||||
IrdaAppViewManager* view_manager = app->get_view_manager();
|
||||
ButtonMenu* button_menu = view_manager->get_button_menu();
|
||||
button_menu_reset(button_menu);
|
||||
view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu);
|
||||
|
||||
file_select_result = file_worker.file_select(
|
||||
IrdaApp::irda_directory,
|
||||
IrdaApp::irda_extension,
|
||||
|
||||
@ -52,7 +52,7 @@ void popup_brut_draw_callback(Canvas* canvas, void* context) {
|
||||
canvas_draw_line(canvas, x_max - 1, y_max - 2, x_max - 3, y_max - 2);
|
||||
|
||||
elements_progress_bar(
|
||||
canvas, x + 4, y + 19, x_max - 8, popup_brut->progress, popup_brut->progress_max);
|
||||
canvas, x + 4, y + 19, x_max - 7, popup_brut->progress, popup_brut->progress_max);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
canvas_draw_str(canvas, x + 15, y + 12, "Sending ...");
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
#include "nfc_cli.h"
|
||||
#include "nfc_types.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <cli/cli.h>
|
||||
#include <toolbox/args.h>
|
||||
|
||||
void nfc_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open("cli");
|
||||
cli_add_command(cli, "nfc_detect", CliCommandFlagDefault, nfc_cli_detect, NULL);
|
||||
cli_add_command(cli, "nfc_emulate", CliCommandFlagDefault, nfc_cli_emulate, NULL);
|
||||
furi_record_close("cli");
|
||||
#endif
|
||||
#include "nfc_types.h"
|
||||
|
||||
static void nfc_cli_print_usage() {
|
||||
printf("Usage:\r\n");
|
||||
printf("nfc <cmd>\r\n");
|
||||
printf("Cmd list:\r\n");
|
||||
printf("\tdetect\t - detect nfc device\r\n");
|
||||
printf("\temulate\t - emulate predefined nfca card\r\n");
|
||||
}
|
||||
|
||||
void nfc_cli_detect(Cli* cli, string_t args, void* context) {
|
||||
void nfc_cli_detect(Cli* cli, string_t args) {
|
||||
// Check if nfc worker is not busy
|
||||
if(furi_hal_nfc_is_busy()) {
|
||||
printf("Nfc is busy");
|
||||
printf("Nfc is busy\r\n");
|
||||
return;
|
||||
}
|
||||
rfalNfcDevice* dev_list;
|
||||
@ -45,15 +46,15 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) {
|
||||
furi_hal_nfc_deactivate();
|
||||
}
|
||||
|
||||
void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
|
||||
void nfc_cli_emulate(Cli* cli, string_t args) {
|
||||
// Check if nfc worker is not busy
|
||||
if(furi_hal_nfc_is_busy()) {
|
||||
printf("Nfc is busy");
|
||||
printf("Nfc is busy\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
furi_hal_nfc_exit_sleep();
|
||||
printf("Emulating NFC-A Type: T2T UID: CF72D440 SAK: 20 ATQA: 00/04\r\n");
|
||||
printf("Emulating NFC-A Type: T2T UID: 36 9C E7 B1 0A C1 34 SAK: 00 ATQA: 00/44\r\n");
|
||||
printf("Press Ctrl+C to abort\r\n");
|
||||
|
||||
NfcDeviceCommonData params = {
|
||||
@ -74,3 +75,35 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) {
|
||||
}
|
||||
furi_hal_nfc_deactivate();
|
||||
}
|
||||
|
||||
static void nfc_cli(Cli* cli, string_t args, void* context) {
|
||||
string_t cmd;
|
||||
string_init(cmd);
|
||||
|
||||
do {
|
||||
if(!args_read_string_and_trim(args, cmd)) {
|
||||
nfc_cli_print_usage();
|
||||
break;
|
||||
}
|
||||
if(string_cmp_str(cmd, "detect") == 0) {
|
||||
nfc_cli_detect(cli, args);
|
||||
break;
|
||||
}
|
||||
if(string_cmp_str(cmd, "emulate") == 0) {
|
||||
nfc_cli_emulate(cli, args);
|
||||
break;
|
||||
}
|
||||
|
||||
nfc_cli_print_usage();
|
||||
} while(false);
|
||||
|
||||
string_clear(cmd);
|
||||
}
|
||||
|
||||
void nfc_on_system_start() {
|
||||
#ifdef SRV_CLI
|
||||
Cli* cli = furi_record_open("cli");
|
||||
cli_add_command(cli, "nfc", CliCommandFlagDefault, nfc_cli, NULL);
|
||||
furi_record_close("cli");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cli/cli.h>
|
||||
|
||||
void nfc_on_system_start();
|
||||
|
||||
void nfc_cli_detect(Cli* cli, string_t args, void* context);
|
||||
|
||||
void nfc_cli_emulate(Cli* cli, string_t args, void* context);
|
||||
@ -189,14 +189,7 @@ void nfc_scene_device_info_on_exit(void* context) {
|
||||
if(nfc->dev->format == NfcDeviceSaveFormatUid) {
|
||||
// Clear Dialog
|
||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_right_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_result_callback(dialog_ex, NULL);
|
||||
dialog_ex_set_context(dialog_ex, NULL);
|
||||
dialog_ex_reset(dialog_ex);
|
||||
} else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
|
||||
// Clear TextBox
|
||||
text_box_reset(nfc->text_box);
|
||||
|
||||
@ -103,14 +103,7 @@ void nfc_scene_read_mifare_ul_success_on_exit(void* context) {
|
||||
|
||||
// Clean dialog
|
||||
DialogEx* dialog_ex = nfc->dialog_ex;
|
||||
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
|
||||
dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
|
||||
dialog_ex_set_left_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_right_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_center_button_text(dialog_ex, NULL);
|
||||
dialog_ex_set_result_callback(dialog_ex, NULL);
|
||||
dialog_ex_set_context(dialog_ex, NULL);
|
||||
dialog_ex_reset(dialog_ex);
|
||||
|
||||
// Clean TextBox
|
||||
TextBox* text_box = nfc->text_box;
|
||||
|
||||
@ -196,6 +196,11 @@ int32_t power_srv(void* p) {
|
||||
// Update battery view port
|
||||
if(need_refresh) view_port_update(power->battery_view_port);
|
||||
|
||||
// Check OTG status and disable it in case of fault
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_check_otg_status();
|
||||
}
|
||||
|
||||
osDelay(1000);
|
||||
}
|
||||
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
typedef struct {
|
||||
osThreadId_t thread;
|
||||
|
||||
} PowerObserverSrv;
|
||||
|
||||
const NotificationMessage message_green_110 = {
|
||||
.type = NotificationMessageTypeLedGreen,
|
||||
.data.led.value = 110,
|
||||
};
|
||||
|
||||
static const NotificationSequence sequence_overconsumption = {
|
||||
&message_green_110,
|
||||
&message_red_255,
|
||||
&message_delay_100,
|
||||
NULL,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
EventReset = (1 << 0),
|
||||
EventRequest = (1 << 1),
|
||||
} UsbEvent;
|
||||
|
||||
static void usb_state_callback(FuriHalUsbStateEvent state, void* context) {
|
||||
PowerObserverSrv* srv = (PowerObserverSrv*)(context);
|
||||
if(state == FuriHalUsbStateEventReset) {
|
||||
osThreadFlagsSet(srv->thread, EventReset);
|
||||
} else if(state == FuriHalUsbStateEventDescriptorRequest) {
|
||||
osThreadFlagsSet(srv->thread, EventRequest);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t power_observer_srv(void* p) {
|
||||
NotificationApp* notifications = furi_record_open("notification");
|
||||
PowerObserverSrv* srv = furi_alloc(sizeof(PowerObserverSrv));
|
||||
srv->thread = osThreadGetId();
|
||||
|
||||
const float overconsumption_limit = 0.03f;
|
||||
bool usb_request_pending = false;
|
||||
uint8_t usb_wait_time = 0;
|
||||
|
||||
furi_hal_usb_set_state_callback(usb_state_callback, srv);
|
||||
|
||||
while(true) {
|
||||
uint32_t flags = osThreadFlagsWait(EventReset | EventRequest, osFlagsWaitAny, 500);
|
||||
if((flags & osFlagsError) == 0) {
|
||||
if(flags & EventReset) {
|
||||
usb_request_pending = true;
|
||||
usb_wait_time = 0;
|
||||
}
|
||||
if(flags & EventRequest) {
|
||||
usb_request_pending = false;
|
||||
}
|
||||
} else if(usb_request_pending) {
|
||||
usb_wait_time++;
|
||||
if(usb_wait_time > 4) {
|
||||
furi_hal_usb_reinit();
|
||||
usb_request_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
float current = -furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge);
|
||||
if(current > overconsumption_limit) {
|
||||
notification_message_block(notifications, &sequence_overconsumption);
|
||||
}
|
||||
if(furi_hal_power_is_otg_enabled()) {
|
||||
furi_hal_power_check_otg_status();
|
||||
}
|
||||
}
|
||||
|
||||
free(srv);
|
||||
return 0;
|
||||
}
|
||||
@ -42,7 +42,7 @@ bool ble_app_init() {
|
||||
ble_app->event_flags = osEventFlagsNew(NULL);
|
||||
// HCI transport layer thread to handle user asynch events
|
||||
ble_app->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(ble_app->thread, "BleHciWorker");
|
||||
furi_thread_set_name(ble_app->thread, "BleHciDriver");
|
||||
furi_thread_set_stack_size(ble_app->thread, 1024);
|
||||
furi_thread_set_context(ble_app->thread, ble_app);
|
||||
furi_thread_set_callback(ble_app->thread, ble_app_hci_thread);
|
||||
|
||||
@ -88,7 +88,7 @@ void ble_glue_init() {
|
||||
|
||||
// FreeRTOS system task creation
|
||||
ble_glue->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(ble_glue->thread, "BleShciWorker");
|
||||
furi_thread_set_name(ble_glue->thread, "BleShciDriver");
|
||||
furi_thread_set_stack_size(ble_glue->thread, 1024);
|
||||
furi_thread_set_context(ble_glue->thread, ble_glue);
|
||||
furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread);
|
||||
|
||||
@ -481,7 +481,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
||||
|
||||
// Thread configuration
|
||||
gap->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(gap->thread, "BleGapWorker");
|
||||
furi_thread_set_name(gap->thread, "BleGapDriver");
|
||||
furi_thread_set_stack_size(gap->thread, 1024);
|
||||
furi_thread_set_context(gap->thread, gap);
|
||||
furi_thread_set_callback(gap->thread, gap_app);
|
||||
|
||||
@ -38,7 +38,6 @@ void furi_hal_init() {
|
||||
|
||||
// VCP + USB
|
||||
furi_hal_usb_init();
|
||||
furi_hal_usb_set_config(&usb_cdc_single);
|
||||
furi_hal_vcp_init();
|
||||
FURI_LOG_I(TAG, "USB OK");
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "furi_hal_version.h"
|
||||
#include "furi_hal_usb_i.h"
|
||||
#include "furi_hal_usb.h"
|
||||
#include "furi_hal_vcp.h"
|
||||
#include <furi_hal_power.h>
|
||||
#include <furi.h>
|
||||
|
||||
@ -10,29 +11,46 @@
|
||||
|
||||
#define USB_RECONNECT_DELAY 500
|
||||
|
||||
static FuriHalUsbInterface* usb_if_cur;
|
||||
static FuriHalUsbInterface* usb_if_next;
|
||||
typedef struct {
|
||||
FuriThread* thread;
|
||||
osTimerId_t tmr;
|
||||
bool enabled;
|
||||
bool connected;
|
||||
FuriHalUsbInterface* if_cur;
|
||||
FuriHalUsbInterface* if_next;
|
||||
FuriHalUsbStateCallback callback;
|
||||
void* cb_ctx;
|
||||
} UsbSrv;
|
||||
|
||||
typedef enum {
|
||||
EventModeChange = (1 << 0),
|
||||
EventEnable = (1 << 1),
|
||||
EventDisable = (1 << 2),
|
||||
EventReinit = (1 << 3),
|
||||
|
||||
EventReset = (1 << 4),
|
||||
EventRequest = (1 << 5),
|
||||
|
||||
EventModeChangeStart = (1 << 6),
|
||||
} UsbEvent;
|
||||
|
||||
#define USB_SRV_ALL_EVENTS \
|
||||
(EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \
|
||||
EventModeChangeStart)
|
||||
|
||||
static UsbSrv usb;
|
||||
|
||||
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
|
||||
|
||||
static uint32_t ubuf[0x20];
|
||||
usbd_device udev;
|
||||
|
||||
static FuriHalUsbStateCallback callback;
|
||||
static void* cb_ctx;
|
||||
|
||||
static int32_t furi_hal_usb_thread(void* context);
|
||||
static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length);
|
||||
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||
static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||
static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||
|
||||
struct UsbCfg {
|
||||
osTimerId_t reconnect_tmr;
|
||||
bool enabled;
|
||||
bool connected;
|
||||
bool mode_changing;
|
||||
} usb_config;
|
||||
|
||||
static void furi_hal_usb_tmr_cb(void* context);
|
||||
|
||||
/* Low-level init */
|
||||
@ -56,79 +74,51 @@ void furi_hal_usb_init(void) {
|
||||
usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt);
|
||||
// Reset callback will be enabled after first mode change to avoid getting false reset events
|
||||
|
||||
usb_config.enabled = false;
|
||||
usb_config.reconnect_tmr = NULL;
|
||||
usb.enabled = false;
|
||||
usb.if_cur = NULL;
|
||||
HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0);
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
|
||||
usb.thread = furi_thread_alloc();
|
||||
furi_thread_set_name(usb.thread, "UsbDriver");
|
||||
furi_thread_set_stack_size(usb.thread, 1024);
|
||||
furi_thread_set_callback(usb.thread, furi_hal_usb_thread);
|
||||
furi_thread_start(usb.thread);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) {
|
||||
if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
|
||||
usb_config.mode_changing = true;
|
||||
usb_if_next = new_if;
|
||||
if(usb_config.reconnect_tmr == NULL)
|
||||
usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
|
||||
furi_hal_usb_disable();
|
||||
usb_config.mode_changing = true;
|
||||
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
|
||||
} else if(
|
||||
(usb_config.mode_changing) &&
|
||||
(usb_if_next != new_if)) { // Last interface mode change wasn't completed
|
||||
osTimerStop(usb_config.reconnect_tmr);
|
||||
usb_if_next = new_if;
|
||||
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
|
||||
} else { // Interface mode change - second stage
|
||||
if(usb_if_cur != NULL) usb_if_cur->deinit(&udev);
|
||||
if(new_if != NULL) {
|
||||
new_if->init(&udev, new_if);
|
||||
usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
|
||||
FURI_LOG_I(TAG, "USB Mode change done");
|
||||
usb_config.enabled = true;
|
||||
usb_if_cur = new_if;
|
||||
usb_config.mode_changing = false;
|
||||
}
|
||||
usb.if_next = new_if;
|
||||
if(usb.thread == NULL) {
|
||||
// Service thread hasn't started yet, so just save interface mode
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_usb_reinit() {
|
||||
// Temporary disable callback to avoid getting false reset events
|
||||
usbd_reg_event(&udev, usbd_evt_reset, NULL);
|
||||
FURI_LOG_I(TAG, "USB Reinit");
|
||||
furi_hal_usb_disable();
|
||||
usbd_enable(&udev, false);
|
||||
usbd_enable(&udev, true);
|
||||
if(usb_config.reconnect_tmr == NULL)
|
||||
usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
|
||||
usb_config.mode_changing = true;
|
||||
usb_if_next = usb_if_cur;
|
||||
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange);
|
||||
}
|
||||
|
||||
FuriHalUsbInterface* furi_hal_usb_get_config() {
|
||||
return usb_if_cur;
|
||||
return usb.if_cur;
|
||||
}
|
||||
|
||||
void furi_hal_usb_disable() {
|
||||
if(usb_config.enabled) {
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb_config.enabled = false;
|
||||
FURI_LOG_I(TAG, "USB Disable");
|
||||
}
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable);
|
||||
}
|
||||
|
||||
void furi_hal_usb_enable() {
|
||||
if((!usb_config.enabled) && (usb_if_cur != NULL)) {
|
||||
usbd_connect(&udev, true);
|
||||
usb_config.enabled = true;
|
||||
FURI_LOG_I(TAG, "USB Enable");
|
||||
}
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable);
|
||||
}
|
||||
|
||||
void furi_hal_usb_reinit() {
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReinit);
|
||||
}
|
||||
|
||||
static void furi_hal_usb_tmr_cb(void* context) {
|
||||
furi_hal_usb_set_config(usb_if_next);
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChangeStart);
|
||||
}
|
||||
|
||||
/* Get device / configuration descriptors */
|
||||
@ -137,28 +127,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
|
||||
const uint8_t dnumber = req->wValue & 0xFF;
|
||||
const void* desc;
|
||||
uint16_t len = 0;
|
||||
if(usb_if_cur == NULL) return usbd_fail;
|
||||
if(usb.if_cur == NULL) return usbd_fail;
|
||||
|
||||
switch(dtype) {
|
||||
case USB_DTYPE_DEVICE:
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventRequest);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx);
|
||||
}
|
||||
desc = usb_if_cur->dev_descr;
|
||||
desc = usb.if_cur->dev_descr;
|
||||
break;
|
||||
case USB_DTYPE_CONFIGURATION:
|
||||
desc = usb_if_cur->cfg_descr;
|
||||
len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0];
|
||||
desc = usb.if_cur->cfg_descr;
|
||||
len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0];
|
||||
break;
|
||||
case USB_DTYPE_STRING:
|
||||
if(dnumber == UsbDevLang) {
|
||||
desc = &dev_lang_desc;
|
||||
} else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) {
|
||||
desc = usb_if_cur->str_manuf_descr;
|
||||
} else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) {
|
||||
desc = usb_if_cur->str_prod_descr;
|
||||
} else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) {
|
||||
desc = usb_if_cur->str_serial_descr;
|
||||
} else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) {
|
||||
desc = usb.if_cur->str_manuf_descr;
|
||||
} else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) {
|
||||
desc = usb.if_cur->str_prod_descr;
|
||||
} else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) {
|
||||
desc = usb.if_cur->str_serial_descr;
|
||||
} else
|
||||
return usbd_fail;
|
||||
break;
|
||||
@ -176,36 +167,122 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
|
||||
}
|
||||
|
||||
void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) {
|
||||
callback = cb;
|
||||
cb_ctx = ctx;
|
||||
usb.callback = cb;
|
||||
usb.cb_ctx = ctx;
|
||||
}
|
||||
|
||||
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventReset, cb_ctx);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReset);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
if((usb_if_cur != NULL) && (usb_config.connected == true)) {
|
||||
usb_config.connected = false;
|
||||
usb_if_cur->suspend(&udev);
|
||||
if((usb.if_cur != NULL) && (usb.connected == true)) {
|
||||
usb.connected = false;
|
||||
usb.if_cur->suspend(&udev);
|
||||
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventSuspend, cb_ctx);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
if((usb_if_cur != NULL) && (usb_config.connected == false)) {
|
||||
usb_config.connected = true;
|
||||
usb_if_cur->wakeup(&udev);
|
||||
if((usb.if_cur != NULL) && (usb.connected == false)) {
|
||||
usb.connected = true;
|
||||
usb.if_cur->wakeup(&udev);
|
||||
|
||||
furi_hal_power_insomnia_enter();
|
||||
}
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventWakeup, cb_ctx);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t furi_hal_usb_thread(void* context) {
|
||||
usb.tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
|
||||
|
||||
bool usb_request_pending = false;
|
||||
uint8_t usb_wait_time = 0;
|
||||
|
||||
if(usb.if_next != NULL) {
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange);
|
||||
}
|
||||
|
||||
while(true) {
|
||||
uint32_t flags = osThreadFlagsWait(USB_SRV_ALL_EVENTS, osFlagsWaitAny, 500);
|
||||
if((flags & osFlagsError) == 0) {
|
||||
if(flags & EventModeChange) {
|
||||
if(usb.if_next != usb.if_cur) {
|
||||
if(usb.enabled) { // Disable current interface
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb.enabled = false;
|
||||
osTimerStart(usb.tmr, USB_RECONNECT_DELAY);
|
||||
} else {
|
||||
flags |= EventModeChangeStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(flags & EventReinit) {
|
||||
// Temporary disable callback to avoid getting false reset events
|
||||
usbd_reg_event(&udev, usbd_evt_reset, NULL);
|
||||
FURI_LOG_I(TAG, "USB Reinit");
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb.enabled = false;
|
||||
|
||||
usbd_enable(&udev, false);
|
||||
usbd_enable(&udev, true);
|
||||
|
||||
usb.if_next = usb.if_cur;
|
||||
osTimerStart(usb.tmr, USB_RECONNECT_DELAY);
|
||||
}
|
||||
if(flags & EventModeChangeStart) { // Second stage of mode change process
|
||||
if(usb.if_cur != NULL) {
|
||||
usb.if_cur->deinit(&udev);
|
||||
}
|
||||
if(usb.if_next != NULL) {
|
||||
usb.if_next->init(&udev, usb.if_next);
|
||||
usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
|
||||
FURI_LOG_I(TAG, "USB Mode change done");
|
||||
usb.enabled = true;
|
||||
usb.if_cur = usb.if_next;
|
||||
}
|
||||
}
|
||||
if(flags & EventEnable) {
|
||||
if((!usb.enabled) && (usb.if_cur != NULL)) {
|
||||
usbd_connect(&udev, true);
|
||||
usb.enabled = true;
|
||||
FURI_LOG_I(TAG, "USB Enable");
|
||||
}
|
||||
}
|
||||
if(flags & EventDisable) {
|
||||
if(usb.enabled) {
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb.enabled = false;
|
||||
usb_request_pending = false;
|
||||
FURI_LOG_I(TAG, "USB Disable");
|
||||
}
|
||||
}
|
||||
if(flags & EventReset) {
|
||||
usb_request_pending = true;
|
||||
usb_wait_time = 0;
|
||||
}
|
||||
if(flags & EventRequest) {
|
||||
usb_request_pending = false;
|
||||
}
|
||||
} else if(usb_request_pending) {
|
||||
usb_wait_time++;
|
||||
if(usb_wait_time > 4) {
|
||||
furi_hal_usb_reinit();
|
||||
usb_request_pending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include <furi_hal_usb_cdc_i.h>
|
||||
#include <furi_hal_console.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi.h>
|
||||
#include <stream_buffer.h>
|
||||
|
||||
@ -65,7 +65,7 @@ void furi_hal_vcp_init() {
|
||||
vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1);
|
||||
|
||||
vcp->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(vcp->thread, "VcpWorker");
|
||||
furi_thread_set_name(vcp->thread, "VcpDriver");
|
||||
furi_thread_set_stack_size(vcp->thread, 1024);
|
||||
furi_thread_set_callback(vcp->thread, vcp_worker);
|
||||
furi_thread_start(vcp->thread);
|
||||
@ -79,6 +79,7 @@ static int32_t vcp_worker(void* context) {
|
||||
size_t missed_rx = 0;
|
||||
uint8_t last_tx_pkt_len = 0;
|
||||
|
||||
furi_hal_usb_set_config(&usb_cdc_single);
|
||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
|
||||
|
||||
while(1) {
|
||||
|
||||
@ -42,7 +42,7 @@ bool ble_app_init() {
|
||||
ble_app->event_flags = osEventFlagsNew(NULL);
|
||||
// HCI transport layer thread to handle user asynch events
|
||||
ble_app->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(ble_app->thread, "BleHciWorker");
|
||||
furi_thread_set_name(ble_app->thread, "BleHciDriver");
|
||||
furi_thread_set_stack_size(ble_app->thread, 1024);
|
||||
furi_thread_set_context(ble_app->thread, ble_app);
|
||||
furi_thread_set_callback(ble_app->thread, ble_app_hci_thread);
|
||||
|
||||
@ -88,7 +88,7 @@ void ble_glue_init() {
|
||||
|
||||
// FreeRTOS system task creation
|
||||
ble_glue->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(ble_glue->thread, "BleShciWorker");
|
||||
furi_thread_set_name(ble_glue->thread, "BleShciDriver");
|
||||
furi_thread_set_stack_size(ble_glue->thread, 1024);
|
||||
furi_thread_set_context(ble_glue->thread, ble_glue);
|
||||
furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread);
|
||||
|
||||
@ -481,7 +481,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) {
|
||||
|
||||
// Thread configuration
|
||||
gap->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(gap->thread, "BleGapWorker");
|
||||
furi_thread_set_name(gap->thread, "BleGapDriver");
|
||||
furi_thread_set_stack_size(gap->thread, 1024);
|
||||
furi_thread_set_context(gap->thread, gap);
|
||||
furi_thread_set_callback(gap->thread, gap_app);
|
||||
|
||||
@ -38,7 +38,6 @@ void furi_hal_init() {
|
||||
|
||||
// VCP + USB
|
||||
furi_hal_usb_init();
|
||||
furi_hal_usb_set_config(&usb_cdc_single);
|
||||
furi_hal_vcp_init();
|
||||
FURI_LOG_I(TAG, "USB OK");
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "furi_hal_version.h"
|
||||
#include "furi_hal_usb_i.h"
|
||||
#include "furi_hal_usb.h"
|
||||
#include "furi_hal_vcp.h"
|
||||
#include <furi_hal_power.h>
|
||||
#include <furi.h>
|
||||
|
||||
@ -10,29 +11,46 @@
|
||||
|
||||
#define USB_RECONNECT_DELAY 500
|
||||
|
||||
static FuriHalUsbInterface* usb_if_cur;
|
||||
static FuriHalUsbInterface* usb_if_next;
|
||||
typedef struct {
|
||||
FuriThread* thread;
|
||||
osTimerId_t tmr;
|
||||
bool enabled;
|
||||
bool connected;
|
||||
FuriHalUsbInterface* if_cur;
|
||||
FuriHalUsbInterface* if_next;
|
||||
FuriHalUsbStateCallback callback;
|
||||
void* cb_ctx;
|
||||
} UsbSrv;
|
||||
|
||||
typedef enum {
|
||||
EventModeChange = (1 << 0),
|
||||
EventEnable = (1 << 1),
|
||||
EventDisable = (1 << 2),
|
||||
EventReinit = (1 << 3),
|
||||
|
||||
EventReset = (1 << 4),
|
||||
EventRequest = (1 << 5),
|
||||
|
||||
EventModeChangeStart = (1 << 6),
|
||||
} UsbEvent;
|
||||
|
||||
#define USB_SRV_ALL_EVENTS \
|
||||
(EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \
|
||||
EventModeChangeStart)
|
||||
|
||||
static UsbSrv usb;
|
||||
|
||||
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
|
||||
|
||||
static uint32_t ubuf[0x20];
|
||||
usbd_device udev;
|
||||
|
||||
static FuriHalUsbStateCallback callback;
|
||||
static void* cb_ctx;
|
||||
|
||||
static int32_t furi_hal_usb_thread(void* context);
|
||||
static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length);
|
||||
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||
static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||
static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||
|
||||
struct UsbCfg {
|
||||
osTimerId_t reconnect_tmr;
|
||||
bool enabled;
|
||||
bool connected;
|
||||
bool mode_changing;
|
||||
} usb_config;
|
||||
|
||||
static void furi_hal_usb_tmr_cb(void* context);
|
||||
|
||||
/* Low-level init */
|
||||
@ -56,79 +74,51 @@ void furi_hal_usb_init(void) {
|
||||
usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt);
|
||||
// Reset callback will be enabled after first mode change to avoid getting false reset events
|
||||
|
||||
usb_config.enabled = false;
|
||||
usb_config.reconnect_tmr = NULL;
|
||||
usb.enabled = false;
|
||||
usb.if_cur = NULL;
|
||||
HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0);
|
||||
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||
|
||||
usb.thread = furi_thread_alloc();
|
||||
furi_thread_set_name(usb.thread, "UsbDriver");
|
||||
furi_thread_set_stack_size(usb.thread, 1024);
|
||||
furi_thread_set_callback(usb.thread, furi_hal_usb_thread);
|
||||
furi_thread_start(usb.thread);
|
||||
|
||||
FURI_LOG_I(TAG, "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) {
|
||||
if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
|
||||
usb_config.mode_changing = true;
|
||||
usb_if_next = new_if;
|
||||
if(usb_config.reconnect_tmr == NULL)
|
||||
usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
|
||||
furi_hal_usb_disable();
|
||||
usb_config.mode_changing = true;
|
||||
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
|
||||
} else if(
|
||||
(usb_config.mode_changing) &&
|
||||
(usb_if_next != new_if)) { // Last interface mode change wasn't completed
|
||||
osTimerStop(usb_config.reconnect_tmr);
|
||||
usb_if_next = new_if;
|
||||
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
|
||||
} else { // Interface mode change - second stage
|
||||
if(usb_if_cur != NULL) usb_if_cur->deinit(&udev);
|
||||
if(new_if != NULL) {
|
||||
new_if->init(&udev, new_if);
|
||||
usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
|
||||
FURI_LOG_I(TAG, "USB Mode change done");
|
||||
usb_config.enabled = true;
|
||||
usb_if_cur = new_if;
|
||||
usb_config.mode_changing = false;
|
||||
}
|
||||
usb.if_next = new_if;
|
||||
if(usb.thread == NULL) {
|
||||
// Service thread hasn't started yet, so just save interface mode
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_usb_reinit() {
|
||||
// Temporary disable callback to avoid getting false reset events
|
||||
usbd_reg_event(&udev, usbd_evt_reset, NULL);
|
||||
FURI_LOG_I(TAG, "USB Reinit");
|
||||
furi_hal_usb_disable();
|
||||
usbd_enable(&udev, false);
|
||||
usbd_enable(&udev, true);
|
||||
if(usb_config.reconnect_tmr == NULL)
|
||||
usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
|
||||
usb_config.mode_changing = true;
|
||||
usb_if_next = usb_if_cur;
|
||||
osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY);
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange);
|
||||
}
|
||||
|
||||
FuriHalUsbInterface* furi_hal_usb_get_config() {
|
||||
return usb_if_cur;
|
||||
return usb.if_cur;
|
||||
}
|
||||
|
||||
void furi_hal_usb_disable() {
|
||||
if(usb_config.enabled) {
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb_config.enabled = false;
|
||||
FURI_LOG_I(TAG, "USB Disable");
|
||||
}
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable);
|
||||
}
|
||||
|
||||
void furi_hal_usb_enable() {
|
||||
if((!usb_config.enabled) && (usb_if_cur != NULL)) {
|
||||
usbd_connect(&udev, true);
|
||||
usb_config.enabled = true;
|
||||
FURI_LOG_I(TAG, "USB Enable");
|
||||
}
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable);
|
||||
}
|
||||
|
||||
void furi_hal_usb_reinit() {
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReinit);
|
||||
}
|
||||
|
||||
static void furi_hal_usb_tmr_cb(void* context) {
|
||||
furi_hal_usb_set_config(usb_if_next);
|
||||
furi_assert(usb.thread);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChangeStart);
|
||||
}
|
||||
|
||||
/* Get device / configuration descriptors */
|
||||
@ -137,28 +127,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
|
||||
const uint8_t dnumber = req->wValue & 0xFF;
|
||||
const void* desc;
|
||||
uint16_t len = 0;
|
||||
if(usb_if_cur == NULL) return usbd_fail;
|
||||
if(usb.if_cur == NULL) return usbd_fail;
|
||||
|
||||
switch(dtype) {
|
||||
case USB_DTYPE_DEVICE:
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventRequest);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx);
|
||||
}
|
||||
desc = usb_if_cur->dev_descr;
|
||||
desc = usb.if_cur->dev_descr;
|
||||
break;
|
||||
case USB_DTYPE_CONFIGURATION:
|
||||
desc = usb_if_cur->cfg_descr;
|
||||
len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0];
|
||||
desc = usb.if_cur->cfg_descr;
|
||||
len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0];
|
||||
break;
|
||||
case USB_DTYPE_STRING:
|
||||
if(dnumber == UsbDevLang) {
|
||||
desc = &dev_lang_desc;
|
||||
} else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) {
|
||||
desc = usb_if_cur->str_manuf_descr;
|
||||
} else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) {
|
||||
desc = usb_if_cur->str_prod_descr;
|
||||
} else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) {
|
||||
desc = usb_if_cur->str_serial_descr;
|
||||
} else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) {
|
||||
desc = usb.if_cur->str_manuf_descr;
|
||||
} else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) {
|
||||
desc = usb.if_cur->str_prod_descr;
|
||||
} else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) {
|
||||
desc = usb.if_cur->str_serial_descr;
|
||||
} else
|
||||
return usbd_fail;
|
||||
break;
|
||||
@ -176,36 +167,122 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_
|
||||
}
|
||||
|
||||
void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) {
|
||||
callback = cb;
|
||||
cb_ctx = ctx;
|
||||
usb.callback = cb;
|
||||
usb.cb_ctx = ctx;
|
||||
}
|
||||
|
||||
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventReset, cb_ctx);
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReset);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
if((usb_if_cur != NULL) && (usb_config.connected == true)) {
|
||||
usb_config.connected = false;
|
||||
usb_if_cur->suspend(&udev);
|
||||
if((usb.if_cur != NULL) && (usb.connected == true)) {
|
||||
usb.connected = false;
|
||||
usb.if_cur->suspend(&udev);
|
||||
|
||||
furi_hal_power_insomnia_exit();
|
||||
}
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventSuspend, cb_ctx);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) {
|
||||
if((usb_if_cur != NULL) && (usb_config.connected == false)) {
|
||||
usb_config.connected = true;
|
||||
usb_if_cur->wakeup(&udev);
|
||||
if((usb.if_cur != NULL) && (usb.connected == false)) {
|
||||
usb.connected = true;
|
||||
usb.if_cur->wakeup(&udev);
|
||||
|
||||
furi_hal_power_insomnia_enter();
|
||||
}
|
||||
if(callback != NULL) {
|
||||
callback(FuriHalUsbStateEventWakeup, cb_ctx);
|
||||
if(usb.callback != NULL) {
|
||||
usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t furi_hal_usb_thread(void* context) {
|
||||
usb.tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL);
|
||||
|
||||
bool usb_request_pending = false;
|
||||
uint8_t usb_wait_time = 0;
|
||||
|
||||
if(usb.if_next != NULL) {
|
||||
osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange);
|
||||
}
|
||||
|
||||
while(true) {
|
||||
uint32_t flags = osThreadFlagsWait(USB_SRV_ALL_EVENTS, osFlagsWaitAny, 500);
|
||||
if((flags & osFlagsError) == 0) {
|
||||
if(flags & EventModeChange) {
|
||||
if(usb.if_next != usb.if_cur) {
|
||||
if(usb.enabled) { // Disable current interface
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb.enabled = false;
|
||||
osTimerStart(usb.tmr, USB_RECONNECT_DELAY);
|
||||
} else {
|
||||
flags |= EventModeChangeStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(flags & EventReinit) {
|
||||
// Temporary disable callback to avoid getting false reset events
|
||||
usbd_reg_event(&udev, usbd_evt_reset, NULL);
|
||||
FURI_LOG_I(TAG, "USB Reinit");
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb.enabled = false;
|
||||
|
||||
usbd_enable(&udev, false);
|
||||
usbd_enable(&udev, true);
|
||||
|
||||
usb.if_next = usb.if_cur;
|
||||
osTimerStart(usb.tmr, USB_RECONNECT_DELAY);
|
||||
}
|
||||
if(flags & EventModeChangeStart) { // Second stage of mode change process
|
||||
if(usb.if_cur != NULL) {
|
||||
usb.if_cur->deinit(&udev);
|
||||
}
|
||||
if(usb.if_next != NULL) {
|
||||
usb.if_next->init(&udev, usb.if_next);
|
||||
usbd_reg_event(&udev, usbd_evt_reset, reset_evt);
|
||||
FURI_LOG_I(TAG, "USB Mode change done");
|
||||
usb.enabled = true;
|
||||
usb.if_cur = usb.if_next;
|
||||
}
|
||||
}
|
||||
if(flags & EventEnable) {
|
||||
if((!usb.enabled) && (usb.if_cur != NULL)) {
|
||||
usbd_connect(&udev, true);
|
||||
usb.enabled = true;
|
||||
FURI_LOG_I(TAG, "USB Enable");
|
||||
}
|
||||
}
|
||||
if(flags & EventDisable) {
|
||||
if(usb.enabled) {
|
||||
susp_evt(&udev, 0, 0);
|
||||
usbd_connect(&udev, false);
|
||||
usb.enabled = false;
|
||||
usb_request_pending = false;
|
||||
FURI_LOG_I(TAG, "USB Disable");
|
||||
}
|
||||
}
|
||||
if(flags & EventReset) {
|
||||
usb_request_pending = true;
|
||||
usb_wait_time = 0;
|
||||
}
|
||||
if(flags & EventRequest) {
|
||||
usb_request_pending = false;
|
||||
}
|
||||
} else if(usb_request_pending) {
|
||||
usb_wait_time++;
|
||||
if(usb_wait_time > 4) {
|
||||
furi_hal_usb_reinit();
|
||||
usb_request_pending = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#include <furi_hal_usb_cdc_i.h>
|
||||
#include <furi_hal_console.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi.h>
|
||||
#include <stream_buffer.h>
|
||||
|
||||
@ -65,7 +65,7 @@ void furi_hal_vcp_init() {
|
||||
vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1);
|
||||
|
||||
vcp->thread = furi_thread_alloc();
|
||||
furi_thread_set_name(vcp->thread, "VcpWorker");
|
||||
furi_thread_set_name(vcp->thread, "VcpDriver");
|
||||
furi_thread_set_stack_size(vcp->thread, 1024);
|
||||
furi_thread_set_callback(vcp->thread, vcp_worker);
|
||||
furi_thread_start(vcp->thread);
|
||||
@ -79,6 +79,7 @@ static int32_t vcp_worker(void* context) {
|
||||
size_t missed_rx = 0;
|
||||
uint8_t last_tx_pkt_len = 0;
|
||||
|
||||
furi_hal_usb_set_config(&usb_cdc_single);
|
||||
furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
|
||||
|
||||
while(1) {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
#include <furi_hal_spi.h>
|
||||
|
||||
static const osThreadAttr_t platform_irq_thread_attr = {
|
||||
.name = "RfalIrqWorker",
|
||||
.name = "RfalIrqDriver",
|
||||
.stack_size = 1024,
|
||||
.priority = osPriorityRealtime,
|
||||
};
|
||||
@ -20,7 +20,7 @@ void nfc_isr(void* _ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void platformIrqWorker() {
|
||||
void platformIrqThread() {
|
||||
while(1) {
|
||||
uint32_t flags = osThreadFlagsWait(0x1, osFlagsWaitAny, osWaitForever);
|
||||
if(flags & 0x1) {
|
||||
@ -41,7 +41,7 @@ void platformDisableIrqCallback() {
|
||||
|
||||
void platformSetIrqCallback(PlatformIrqCallback callback) {
|
||||
platform_irq_callback = callback;
|
||||
platform_irq_thread_id = osThreadNew(platformIrqWorker, NULL, &platform_irq_thread_attr);
|
||||
platform_irq_thread_id = osThreadNew(platformIrqThread, NULL, &platform_irq_thread_attr);
|
||||
hal_gpio_add_int_callback(&pin, nfc_isr, NULL);
|
||||
// Disable interrupt callback as the pin is shared between 2 apps
|
||||
// It is enabled in rfalLowPowerModeStop()
|
||||
|
||||
@ -268,9 +268,11 @@ IrdaProtocol irda_get_protocol_by_name(const char* protocol_name) {
|
||||
|
||||
static const IrdaProtocolSpecification* irda_get_spec_by_protocol(IrdaProtocol protocol) {
|
||||
int index = irda_find_index_by_protocol(protocol);
|
||||
furi_check(index >= 0);
|
||||
const IrdaProtocolSpecification* spec =
|
||||
irda_encoder_decoder[index].get_protocol_spec(protocol);
|
||||
const IrdaProtocolSpecification* spec = NULL;
|
||||
if(index >= 0) {
|
||||
spec = irda_encoder_decoder[index].get_protocol_spec(protocol);
|
||||
}
|
||||
|
||||
furi_assert(spec);
|
||||
return spec;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user