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 input_srv(void* p); | ||||||
| extern int32_t loader_srv(void* p); | extern int32_t loader_srv(void* p); | ||||||
| extern int32_t notification_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 power_srv(void* p); | ||||||
| extern int32_t storage_srv(void* p); | extern int32_t storage_srv(void* p); | ||||||
| extern int32_t desktop_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}, |     {.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_POWER_OBSERVER |  | ||||||
|     {.app = power_observer_srv, .name = "PowerAuditSrv", .stack_size = 1024, .icon = NULL}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL}, |     {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -18,7 +18,6 @@ SRV_INPUT	= 1 | |||||||
| SRV_LOADER	= 1 | SRV_LOADER	= 1 | ||||||
| SRV_NOTIFICATION = 1 | SRV_NOTIFICATION = 1 | ||||||
| SRV_POWER	= 1 | SRV_POWER	= 1 | ||||||
| SRV_POWER_OBSERVER = 1 |  | ||||||
| SRV_RPC = 1 | SRV_RPC = 1 | ||||||
| SRV_STORAGE	= 1 | SRV_STORAGE	= 1 | ||||||
| 
 | 
 | ||||||
| @ -256,13 +255,6 @@ endif | |||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| SRV_POWER_OBSERVER ?= 0 |  | ||||||
| ifeq ($(SRV_POWER_OBSERVER), 1) |  | ||||||
| CFLAGS		+= -DSRV_POWER_OBSERVER |  | ||||||
| SRV_POWER	= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_POWER ?= 0 | SRV_POWER ?= 0 | ||||||
| ifeq ($(SRV_POWER), 1) | ifeq ($(SRV_POWER), 1) | ||||||
| CFLAGS		+= -DSRV_POWER | CFLAGS		+= -DSRV_POWER | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "elements.h" | #include "elements.h" | ||||||
|  | #include "m-core.h" | ||||||
| #include <assets_icons.h> | #include <assets_icons.h> | ||||||
| #include "furi_hal_resources.h" | #include "furi_hal_resources.h" | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| @ -36,30 +37,15 @@ void elements_progress_bar( | |||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     furi_assert(total > 0); |     furi_assert(total > 0); | ||||||
|     uint8_t height = 9; |     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
 |     canvas_set_color(canvas, ColorWhite); | ||||||
|     uint8_t x_max = x + width - 1; |     canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2); | ||||||
|     uint8_t y_max = y + height - 1; |     canvas_set_color(canvas, ColorBlack); | ||||||
|     canvas_draw_line(canvas, x + 3, y, x_max - 3, y); |     canvas_draw_rframe(canvas, x, y, width, height, 3); | ||||||
|     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_draw_rbox(canvas, x + 1, y + 1, marker_width + progress_length, height - 2, 3); |     canvas_draw_box(canvas, x + 1, y + 1, progress_length, height - 2); | ||||||
|     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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void elements_scrollbar_pos( | void elements_scrollbar_pos( | ||||||
| @ -202,6 +188,48 @@ void elements_button_center(Canvas* canvas, const char* str) { | |||||||
|     canvas_invert_color(canvas); |     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( | void elements_multiline_text_aligned( | ||||||
|     Canvas* canvas, |     Canvas* canvas, | ||||||
|     uint8_t x, |     uint8_t x, | ||||||
| @ -212,64 +240,40 @@ void elements_multiline_text_aligned( | |||||||
|     furi_assert(canvas); |     furi_assert(canvas); | ||||||
|     furi_assert(text); |     furi_assert(text); | ||||||
| 
 | 
 | ||||||
|  |     uint8_t lines_count = 0; | ||||||
|     uint8_t font_height = canvas_current_font_height(canvas); |     uint8_t font_height = canvas_current_font_height(canvas); | ||||||
|     string_t str; |     string_t line; | ||||||
|     string_init(str); |  | ||||||
|     const char* start = text; |  | ||||||
|     char* end; |  | ||||||
| 
 | 
 | ||||||
|     // get lines count
 |     /* go through text line by line and count lines */ | ||||||
|     uint8_t i, lines_count; |     for(const char* start = text; start[0];) { | ||||||
|     for(i = 0, lines_count = 0; text[i]; i++) lines_count += (text[i] == '\n'); |         size_t chars_fit = elements_get_max_chars_to_fit(canvas, horizontal, start, x); | ||||||
| 
 |         ++lines_count; | ||||||
|     switch(vertical) { |         start += chars_fit; | ||||||
|     case AlignBottom: |         start += start[0] == '\n' ? 1 : 0; | ||||||
|         y -= font_height * lines_count; |  | ||||||
|         break; |  | ||||||
|     case AlignCenter: |  | ||||||
|         y -= (font_height * lines_count) / 2; |  | ||||||
|         break; |  | ||||||
|     case AlignTop: |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     do { |     if(vertical == AlignBottom) { | ||||||
|         end = strchr(start, '\n'); |         y -= font_height * (lines_count - 1); | ||||||
|  |     } else if(vertical == AlignCenter) { | ||||||
|  |         y -= (font_height * (lines_count - 1)) / 2; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|         if(end) { |     /* go through text line by line and print them */ | ||||||
|             string_set_strn(str, start, end - start); |     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 { |         } else { | ||||||
|             string_set_str(str, start); |             string_init_printf(line, "%.*s-\n", chars_fit, start); | ||||||
|         } |         } | ||||||
| 
 |         canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line)); | ||||||
|         uint16_t len_px = canvas_string_width(canvas, string_get_cstr(str)); |         string_clear(line); | ||||||
|         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; |         y += font_height; | ||||||
|  | 
 | ||||||
|  |         start += chars_fit; | ||||||
|  |         start += start[0] == '\n' ? 1 : 0; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|         canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(str)); |  | ||||||
|         start = end + 1; |  | ||||||
|         y += font_height; |  | ||||||
|     } while(end); |  | ||||||
|     string_clear(str); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void elements_multiline_text(Canvas* canvas, uint8_t x, uint8_t y, const char* text) { | 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); |     furi_assert(_model); | ||||||
| 
 | 
 | ||||||
|     ButtonMenuModel* model = (ButtonMenuModel*)_model; |     ButtonMenuModel* model = (ButtonMenuModel*)_model; | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     canvas_set_font(canvas, FontSecondary); |     canvas_set_font(canvas, FontSecondary); | ||||||
| 
 | 
 | ||||||
|     uint8_t item_position = 0; |     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); |         canvas_draw_icon(canvas, 28, 123, &I_IrdaArrowDown_4x8); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if(model->header) { | ||||||
|         string_t disp_str; |         string_t disp_str; | ||||||
|         string_init_set_str(disp_str, model->header); |         string_init_set_str(disp_str, model->header); | ||||||
|         elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); |         elements_string_fit_width(canvas, disp_str, ITEM_WIDTH - 6); | ||||||
|     canvas_draw_str_aligned(canvas, 32, 10, AlignCenter, AlignCenter, string_get_cstr(disp_str)); |         canvas_draw_str_aligned( | ||||||
|  |             canvas, 32, 10, AlignCenter, AlignCenter, string_get_cstr(disp_str)); | ||||||
|         string_clear(disp_str); |         string_clear(disp_str); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); |     for(ButtonMenuItemArray_it(it, model->items); !ButtonMenuItemArray_end_p(it); | ||||||
|         ButtonMenuItemArray_next(it), ++item_position) { |         ButtonMenuItemArray_next(it), ++item_position) { | ||||||
| @ -248,6 +249,7 @@ void button_menu_reset(ButtonMenu* button_menu) { | |||||||
|         button_menu->view, (ButtonMenuModel * model) { |         button_menu->view, (ButtonMenuModel * model) { | ||||||
|             ButtonMenuItemArray_reset(model->items); |             ButtonMenuItemArray_reset(model->items); | ||||||
|             model->position = 0; |             model->position = 0; | ||||||
|  |             model->header = NULL; | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  | |||||||
| @ -40,91 +40,6 @@ void input_isr(void* _ctx) { | |||||||
|     osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR); |     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) { | const char* input_get_key_name(InputKey key) { | ||||||
|     for(size_t i = 0; i < input_pins_count; i++) { |     for(size_t i = 0; i < input_pins_count; i++) { | ||||||
|         if(input_pins[i].key == key) { |         if(input_pins[i].key == key) { | ||||||
| @ -159,10 +74,7 @@ int32_t input_srv() { | |||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     input->cli = furi_record_open("cli"); |     input->cli = furi_record_open("cli"); | ||||||
|     if(input->cli) { |     if(input->cli) { | ||||||
|         cli_add_command( |         cli_add_command(input->cli, "input", CliCommandFlagParallelSafe, input_cli, input); | ||||||
|             input->cli, "input_send", CliCommandFlagParallelSafe, input_cli_send, NULL); |  | ||||||
|         cli_add_command( |  | ||||||
|             input->cli, "input_dump", CliCommandFlagParallelSafe, input_cli_dump, NULL); |  | ||||||
|     } |     } | ||||||
| #endif | #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 */ | /** Input interrupt handler */ | ||||||
| void input_isr(void* _ctx); | 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 <sys/types.h> | ||||||
| #include "../helpers/irda_parser.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) { | static void signal_received_callback(void* context, IrdaWorkerSignal* received_signal) { | ||||||
|     furi_assert(received_signal); |     furi_assert(received_signal); | ||||||
|     char buf[100]; |     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) { | static void irda_cli_start_ir_rx(Cli* cli, string_t args) { | ||||||
|     if(furi_hal_irda_is_busy()) { |  | ||||||
|         printf("IRDA is busy. Exit."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     IrdaWorker* worker = irda_worker_alloc(); |     IrdaWorker* worker = irda_worker_alloc(); | ||||||
|     irda_worker_rx_start(worker); |     irda_worker_rx_start(worker); | ||||||
|     irda_worker_rx_set_received_signal_callback(worker, signal_received_callback, cli); |     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) { | 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("\t<command> and <address> are hex-formatted\r\n"); | ||||||
|     printf("\tAvailable protocols:"); |     printf("\tAvailable protocols:"); | ||||||
|     for(int i = 0; irda_is_protocol_valid((IrdaProtocol)i); ++i) { |     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); |     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) { | static void irda_cli_start_ir_tx(Cli* cli, string_t args) { | ||||||
|     if(furi_hal_irda_is_busy()) { |  | ||||||
|         printf("IRDA is busy. Exit."); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     IrdaMessage message; |     IrdaMessage message; | ||||||
|     const char* str = string_get_cstr(args); |     const char* str = string_get_cstr(args); | ||||||
|     uint32_t frequency; |     uint32_t frequency; | ||||||
| @ -156,11 +159,38 @@ void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { | |||||||
|     free(timings); |     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() { | extern "C" void irda_on_system_start() { | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     Cli* cli = (Cli*)furi_record_open("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", CliCommandFlagDefault, irda_cli_start_ir, NULL); | ||||||
|     cli_add_command(cli, "ir_tx", CliCommandFlagDefault, irda_cli_start_ir_tx, NULL); |  | ||||||
|     furi_record_close("cli"); |     furi_record_close("cli"); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  | |||||||
| @ -96,6 +96,7 @@ bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) { | |||||||
|         result = false; |         result = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if(result) { | ||||||
|         uint32_t address_length = irda_get_protocol_address_length(signal->protocol); |         uint32_t address_length = irda_get_protocol_address_length(signal->protocol); | ||||||
|         uint32_t address_mask = (1LU << address_length) - 1; |         uint32_t address_mask = (1LU << address_length) - 1; | ||||||
|         if(signal->address != (signal->address & address_mask)) { |         if(signal->address != (signal->address & address_mask)) { | ||||||
| @ -106,7 +107,9 @@ bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) { | |||||||
|                 signal->address); |                 signal->address); | ||||||
|             result = false; |             result = false; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     if(result) { | ||||||
|         uint32_t command_length = irda_get_protocol_command_length(signal->protocol); |         uint32_t command_length = irda_get_protocol_command_length(signal->protocol); | ||||||
|         uint32_t command_mask = (1LU << command_length) - 1; |         uint32_t command_mask = (1LU << command_length) - 1; | ||||||
|         if(signal->command != (signal->command & command_mask)) { |         if(signal->command != (signal->command & command_mask)) { | ||||||
| @ -117,6 +120,7 @@ bool irda_parser_is_parsed_signal_valid(const IrdaMessage* signal) { | |||||||
|                 signal->command); |                 signal->command); | ||||||
|             result = false; |             result = false; | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,6 +33,7 @@ public: | |||||||
|         LearnSuccess, |         LearnSuccess, | ||||||
|         LearnEnterName, |         LearnEnterName, | ||||||
|         LearnDone, |         LearnDone, | ||||||
|  |         AskBack, | ||||||
|         Remote, |         Remote, | ||||||
|         RemoteList, |         RemoteList, | ||||||
|         Edit, |         Edit, | ||||||
| @ -123,6 +124,7 @@ private: | |||||||
|         {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, |         {Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()}, | ||||||
|         {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, |         {Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()}, | ||||||
|         {Scene::LearnDone, new IrdaAppSceneLearnDone()}, |         {Scene::LearnDone, new IrdaAppSceneLearnDone()}, | ||||||
|  |         {Scene::AskBack, new IrdaAppSceneAskBack()}, | ||||||
|         {Scene::Remote, new IrdaAppSceneRemote()}, |         {Scene::Remote, new IrdaAppSceneRemote()}, | ||||||
|         {Scene::RemoteList, new IrdaAppSceneRemoteList()}, |         {Scene::RemoteList, new IrdaAppSceneRemoteList()}, | ||||||
|         {Scene::Edit, new IrdaAppSceneEdit()}, |         {Scene::Edit, new IrdaAppSceneEdit()}, | ||||||
|  | |||||||
| @ -110,10 +110,11 @@ std::string IrdaAppRemoteManager::get_button_name(uint32_t index) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string IrdaAppRemoteManager::get_remote_name() { | 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) { | int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) { | ||||||
|  |     furi_assert(remote.get() != nullptr); | ||||||
|     int i = 0; |     int i = 0; | ||||||
|     for(const auto& str : strings) { |     for(const auto& str : strings) { | ||||||
|         if(!str.compare(remote->name)) { |         if(!str.compare(remote->name)) { | ||||||
|  | |||||||
| @ -70,8 +70,6 @@ IrdaAppSignal::IrdaAppSignal(const IrdaAppSignal& other) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) { | IrdaAppSignal::IrdaAppSignal(IrdaAppSignal&& other) { | ||||||
|     clear_timings(); |  | ||||||
| 
 |  | ||||||
|     raw_signal = other.raw_signal; |     raw_signal = other.raw_signal; | ||||||
|     if(!raw_signal) { |     if(!raw_signal) { | ||||||
|         payload.message = other.payload.message; |         payload.message = other.payload.message; | ||||||
|  | |||||||
| @ -89,6 +89,13 @@ private: | |||||||
|     std::vector<std::string> remote_names; |     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 { | class IrdaAppSceneEdit : public IrdaAppScene { | ||||||
| public: | public: | ||||||
|     void on_enter(IrdaApp* app) final; |     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) { |     if(app->get_edit_element() == IrdaApp::EditElement::Button) { | ||||||
|         auto signal = remote_manager->get_button_data(app->get_current_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()) { |         if(!signal.is_raw()) { | ||||||
|             auto message = &signal.get_message(); |             auto message = &signal.get_message(); | ||||||
|             app->set_text_store( |             app->set_text_store( | ||||||
| @ -41,7 +41,7 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | |||||||
|                 signal.get_raw_signal().timings_cnt); |                 signal.get_raw_signal().timings_cnt); | ||||||
|         } |         } | ||||||
|     } else { |     } 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( |         app->set_text_store( | ||||||
|             0, |             0, | ||||||
|             "%s\n with %lu buttons", |             "%s\n with %lu buttons", | ||||||
| @ -49,7 +49,7 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { | |||||||
|             remote_manager->get_number_of_buttons()); |             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_icon(dialog_ex, 0, 0, NULL); | ||||||
|     dialog_ex_set_left_button_text(dialog_ex, "Back"); |     dialog_ex_set_left_button_text(dialog_ex, "Back"); | ||||||
|     dialog_ex_set_right_button_text(dialog_ex, "Delete"); |     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* popup = view_manager->get_popup(); | ||||||
| 
 | 
 | ||||||
|     popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); |     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_callback(popup, IrdaApp::popup_callback); | ||||||
|     popup_set_context(popup, app); |     popup_set_context(popup, app); | ||||||
| @ -32,4 +32,7 @@ bool IrdaAppSceneEditDeleteDone::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneEditDeleteDone::on_exit(IrdaApp* app) { | 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, | ||||||
|         app->get_text_store(0), |         app->get_text_store(0), | ||||||
|         enter_name_length, |         enter_name_length, | ||||||
|         true); |         false); | ||||||
| 
 | 
 | ||||||
|     view_manager->switch_to(IrdaAppViewManager::ViewType::TextInput); |     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_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_callback(popup, IrdaApp::popup_callback); | ||||||
|     popup_set_context(popup, app); |     popup_set_context(popup, app); | ||||||
|  | |||||||
| @ -69,4 +69,7 @@ bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { | |||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearn::on_exit(IrdaApp* app) { | void IrdaAppSceneLearn::on_exit(IrdaApp* app) { | ||||||
|     irda_worker_rx_stop(app->get_irda_worker()); |     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); |     DOLPHIN_DEED(DolphinDeedIrSave); | ||||||
| 
 | 
 | ||||||
|     if(app->get_learn_new_remote()) { |     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 { |     } 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); |     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) { | void IrdaAppSceneLearnDone::on_exit(IrdaApp* app) { | ||||||
|     app->set_learn_new_remote(false); |     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; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { | void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { | ||||||
|     IrdaAppViewManager* view_manager = app->get_view_manager(); |     IrdaAppViewManager* view_manager = app->get_view_manager(); | ||||||
|     DialogEx* dialog_ex = view_manager->get_dialog_ex(); |     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(); |     app->notify_green_off(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -15,6 +15,11 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { | |||||||
|         last_selected_remote.size() ? last_selected_remote.c_str() : nullptr; |         last_selected_remote.size() ? last_selected_remote.c_str() : nullptr; | ||||||
|     auto filename_ts = std::make_unique<TextStore>(IrdaAppRemoteManager::max_remote_name_length); |     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( |     file_select_result = file_worker.file_select( | ||||||
|         IrdaApp::irda_directory, |         IrdaApp::irda_directory, | ||||||
|         IrdaApp::irda_extension, |         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); |     canvas_draw_line(canvas, x_max - 1, y_max - 2, x_max - 3, y_max - 2); | ||||||
| 
 | 
 | ||||||
|     elements_progress_bar( |     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_set_font(canvas, FontSecondary); | ||||||
|     canvas_draw_str(canvas, x + 15, y + 12, "Sending ..."); |     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.h> | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <toolbox/args.h> | ||||||
| 
 | 
 | ||||||
| void nfc_on_system_start() { | #include "nfc_types.h" | ||||||
| #ifdef SRV_CLI | 
 | ||||||
|     Cli* cli = furi_record_open("cli"); | static void nfc_cli_print_usage() { | ||||||
|     cli_add_command(cli, "nfc_detect", CliCommandFlagDefault, nfc_cli_detect, NULL); |     printf("Usage:\r\n"); | ||||||
|     cli_add_command(cli, "nfc_emulate", CliCommandFlagDefault, nfc_cli_emulate, NULL); |     printf("nfc <cmd>\r\n"); | ||||||
|     furi_record_close("cli"); |     printf("Cmd list:\r\n"); | ||||||
| #endif |     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
 |     // Check if nfc worker is not busy
 | ||||||
|     if(furi_hal_nfc_is_busy()) { |     if(furi_hal_nfc_is_busy()) { | ||||||
|         printf("Nfc is busy"); |         printf("Nfc is busy\r\n"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     rfalNfcDevice* dev_list; |     rfalNfcDevice* dev_list; | ||||||
| @ -45,15 +46,15 @@ void nfc_cli_detect(Cli* cli, string_t args, void* context) { | |||||||
|     furi_hal_nfc_deactivate(); |     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
 |     // Check if nfc worker is not busy
 | ||||||
|     if(furi_hal_nfc_is_busy()) { |     if(furi_hal_nfc_is_busy()) { | ||||||
|         printf("Nfc is busy"); |         printf("Nfc is busy\r\n"); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_hal_nfc_exit_sleep(); |     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"); |     printf("Press Ctrl+C to abort\r\n"); | ||||||
| 
 | 
 | ||||||
|     NfcDeviceCommonData params = { |     NfcDeviceCommonData params = { | ||||||
| @ -74,3 +75,35 @@ void nfc_cli_emulate(Cli* cli, string_t args, void* context) { | |||||||
|     } |     } | ||||||
|     furi_hal_nfc_deactivate(); |     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) { |     if(nfc->dev->format == NfcDeviceSaveFormatUid) { | ||||||
|         // Clear Dialog
 |         // Clear Dialog
 | ||||||
|         DialogEx* dialog_ex = nfc->dialog_ex; |         DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|         dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); |         dialog_ex_reset(dialog_ex); | ||||||
|         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); |  | ||||||
|     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { |     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||||
|         // Clear TextBox
 |         // Clear TextBox
 | ||||||
|         text_box_reset(nfc->text_box); |         text_box_reset(nfc->text_box); | ||||||
|  | |||||||
| @ -103,14 +103,7 @@ void nfc_scene_read_mifare_ul_success_on_exit(void* context) { | |||||||
| 
 | 
 | ||||||
|     // Clean dialog
 |     // Clean dialog
 | ||||||
|     DialogEx* dialog_ex = nfc->dialog_ex; |     DialogEx* dialog_ex = nfc->dialog_ex; | ||||||
|     dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter); |     dialog_ex_reset(dialog_ex); | ||||||
|     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); |  | ||||||
| 
 | 
 | ||||||
|     // Clean TextBox
 |     // Clean TextBox
 | ||||||
|     TextBox* text_box = nfc->text_box; |     TextBox* text_box = nfc->text_box; | ||||||
|  | |||||||
| @ -196,6 +196,11 @@ int32_t power_srv(void* p) { | |||||||
|         // Update battery view port
 |         // Update battery view port
 | ||||||
|         if(need_refresh) view_port_update(power->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); |         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); |     ble_app->event_flags = osEventFlagsNew(NULL); | ||||||
|     // HCI transport layer thread to handle user asynch events
 |     // HCI transport layer thread to handle user asynch events
 | ||||||
|     ble_app->thread = furi_thread_alloc(); |     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_stack_size(ble_app->thread, 1024); | ||||||
|     furi_thread_set_context(ble_app->thread, ble_app); |     furi_thread_set_context(ble_app->thread, ble_app); | ||||||
|     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); |     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ void ble_glue_init() { | |||||||
| 
 | 
 | ||||||
|     // FreeRTOS system task creation
 |     // FreeRTOS system task creation
 | ||||||
|     ble_glue->thread = furi_thread_alloc(); |     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_stack_size(ble_glue->thread, 1024); | ||||||
|     furi_thread_set_context(ble_glue->thread, ble_glue); |     furi_thread_set_context(ble_glue->thread, ble_glue); | ||||||
|     furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); |     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
 |     // Thread configuration
 | ||||||
|     gap->thread = furi_thread_alloc(); |     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_stack_size(gap->thread, 1024); | ||||||
|     furi_thread_set_context(gap->thread, gap); |     furi_thread_set_context(gap->thread, gap); | ||||||
|     furi_thread_set_callback(gap->thread, gap_app); |     furi_thread_set_callback(gap->thread, gap_app); | ||||||
|  | |||||||
| @ -38,7 +38,6 @@ void furi_hal_init() { | |||||||
| 
 | 
 | ||||||
|     // VCP + USB
 |     // VCP + USB
 | ||||||
|     furi_hal_usb_init(); |     furi_hal_usb_init(); | ||||||
|     furi_hal_usb_set_config(&usb_cdc_single); |  | ||||||
|     furi_hal_vcp_init(); |     furi_hal_vcp_init(); | ||||||
|     FURI_LOG_I(TAG, "USB OK"); |     FURI_LOG_I(TAG, "USB OK"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "furi_hal_version.h" | #include "furi_hal_version.h" | ||||||
| #include "furi_hal_usb_i.h" | #include "furi_hal_usb_i.h" | ||||||
| #include "furi_hal_usb.h" | #include "furi_hal_usb.h" | ||||||
|  | #include "furi_hal_vcp.h" | ||||||
| #include <furi_hal_power.h> | #include <furi_hal_power.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| @ -10,29 +11,46 @@ | |||||||
| 
 | 
 | ||||||
| #define USB_RECONNECT_DELAY 500 | #define USB_RECONNECT_DELAY 500 | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbInterface* usb_if_cur; | typedef struct { | ||||||
| static FuriHalUsbInterface* usb_if_next; |     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 const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | ||||||
| 
 | 
 | ||||||
| static uint32_t ubuf[0x20]; | static uint32_t ubuf[0x20]; | ||||||
| usbd_device udev; | usbd_device udev; | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbStateCallback callback; | static int32_t furi_hal_usb_thread(void* context); | ||||||
| static void* cb_ctx; |  | ||||||
| 
 |  | ||||||
| static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length); | 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 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 susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| static void wkup_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); | static void furi_hal_usb_tmr_cb(void* context); | ||||||
| 
 | 
 | ||||||
| /* Low-level init */ | /* Low-level init */ | ||||||
| @ -56,79 +74,51 @@ void furi_hal_usb_init(void) { | |||||||
|     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); |     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); | ||||||
|     // Reset callback will be enabled after first mode change to avoid getting false reset events
 |     // Reset callback will be enabled after first mode change to avoid getting false reset events
 | ||||||
| 
 | 
 | ||||||
|     usb_config.enabled = false; |     usb.enabled = false; | ||||||
|     usb_config.reconnect_tmr = NULL; |     usb.if_cur = NULL; | ||||||
|     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); |     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); | ||||||
|     NVIC_EnableIRQ(USB_LP_IRQn); |     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"); |     FURI_LOG_I(TAG, "Init OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | ||||||
|     if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
 |     usb.if_next = new_if; | ||||||
|         usb_config.mode_changing = true; |     if(usb.thread == NULL) { | ||||||
|         usb_if_next = new_if; |         // Service thread hasn't started yet, so just save interface mode
 | ||||||
|         if(usb_config.reconnect_tmr == NULL) |         return; | ||||||
|             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; |  | ||||||
|     } |     } | ||||||
|     } |     furi_assert(usb.thread); | ||||||
| } |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriHalUsbInterface* furi_hal_usb_get_config() { | FuriHalUsbInterface* furi_hal_usb_get_config() { | ||||||
|     return usb_if_cur; |     return usb.if_cur; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_disable() { | void furi_hal_usb_disable() { | ||||||
|     if(usb_config.enabled) { |     furi_assert(usb.thread); | ||||||
|         susp_evt(&udev, 0, 0); |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable); | ||||||
|         usbd_connect(&udev, false); |  | ||||||
|         usb_config.enabled = false; |  | ||||||
|         FURI_LOG_I(TAG, "USB Disable"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_enable() { | void furi_hal_usb_enable() { | ||||||
|     if((!usb_config.enabled) && (usb_if_cur != NULL)) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable); | ||||||
|         usbd_connect(&udev, true); | } | ||||||
|         usb_config.enabled = true; | 
 | ||||||
|         FURI_LOG_I(TAG, "USB Enable"); | 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) { | 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 */ | /* 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 uint8_t dnumber = req->wValue & 0xFF; | ||||||
|     const void* desc; |     const void* desc; | ||||||
|     uint16_t len = 0; |     uint16_t len = 0; | ||||||
|     if(usb_if_cur == NULL) return usbd_fail; |     if(usb.if_cur == NULL) return usbd_fail; | ||||||
| 
 | 
 | ||||||
|     switch(dtype) { |     switch(dtype) { | ||||||
|     case USB_DTYPE_DEVICE: |     case USB_DTYPE_DEVICE: | ||||||
|         if(callback != NULL) { |         osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventRequest); | ||||||
|             callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx); |         if(usb.callback != NULL) { | ||||||
|  |             usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); | ||||||
|         } |         } | ||||||
|         desc = usb_if_cur->dev_descr; |         desc = usb.if_cur->dev_descr; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_CONFIGURATION: |     case USB_DTYPE_CONFIGURATION: | ||||||
|         desc = usb_if_cur->cfg_descr; |         desc = usb.if_cur->cfg_descr; | ||||||
|         len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0]; |         len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_STRING: |     case USB_DTYPE_STRING: | ||||||
|         if(dnumber == UsbDevLang) { |         if(dnumber == UsbDevLang) { | ||||||
|             desc = &dev_lang_desc; |             desc = &dev_lang_desc; | ||||||
|         } else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) { |         } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_manuf_descr; |             desc = usb.if_cur->str_manuf_descr; | ||||||
|         } else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) { |         } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_prod_descr; |             desc = usb.if_cur->str_prod_descr; | ||||||
|         } else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) { |         } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_serial_descr; |             desc = usb.if_cur->str_serial_descr; | ||||||
|         } else |         } else | ||||||
|             return usbd_fail; |             return usbd_fail; | ||||||
|         break; |         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) { | void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { | ||||||
|     callback = cb; |     usb.callback = cb; | ||||||
|     cb_ctx = ctx; |     usb.cb_ctx = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if(callback != NULL) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReset); | ||||||
|         callback(FuriHalUsbStateEventReset, cb_ctx); |     if(usb.callback != NULL) { | ||||||
|  |         usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == true)) { |     if((usb.if_cur != NULL) && (usb.connected == true)) { | ||||||
|         usb_config.connected = false; |         usb.connected = false; | ||||||
|         usb_if_cur->suspend(&udev); |         usb.if_cur->suspend(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_exit(); |         furi_hal_power_insomnia_exit(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventSuspend, cb_ctx); |         usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == false)) { |     if((usb.if_cur != NULL) && (usb.connected == false)) { | ||||||
|         usb_config.connected = true; |         usb.connected = true; | ||||||
|         usb_if_cur->wakeup(&udev); |         usb.if_cur->wakeup(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_enter(); |         furi_hal_power_insomnia_enter(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventWakeup, cb_ctx); |         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_usb_cdc_i.h> | ||||||
| #include <furi_hal_console.h> | #include <furi_hal.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| 
 | 
 | ||||||
| @ -65,7 +65,7 @@ void furi_hal_vcp_init() { | |||||||
|     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); |     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); | ||||||
| 
 | 
 | ||||||
|     vcp->thread = furi_thread_alloc(); |     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_stack_size(vcp->thread, 1024); | ||||||
|     furi_thread_set_callback(vcp->thread, vcp_worker); |     furi_thread_set_callback(vcp->thread, vcp_worker); | ||||||
|     furi_thread_start(vcp->thread); |     furi_thread_start(vcp->thread); | ||||||
| @ -79,6 +79,7 @@ static int32_t vcp_worker(void* context) { | |||||||
|     size_t missed_rx = 0; |     size_t missed_rx = 0; | ||||||
|     uint8_t last_tx_pkt_len = 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); |     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ bool ble_app_init() { | |||||||
|     ble_app->event_flags = osEventFlagsNew(NULL); |     ble_app->event_flags = osEventFlagsNew(NULL); | ||||||
|     // HCI transport layer thread to handle user asynch events
 |     // HCI transport layer thread to handle user asynch events
 | ||||||
|     ble_app->thread = furi_thread_alloc(); |     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_stack_size(ble_app->thread, 1024); | ||||||
|     furi_thread_set_context(ble_app->thread, ble_app); |     furi_thread_set_context(ble_app->thread, ble_app); | ||||||
|     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); |     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ void ble_glue_init() { | |||||||
| 
 | 
 | ||||||
|     // FreeRTOS system task creation
 |     // FreeRTOS system task creation
 | ||||||
|     ble_glue->thread = furi_thread_alloc(); |     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_stack_size(ble_glue->thread, 1024); | ||||||
|     furi_thread_set_context(ble_glue->thread, ble_glue); |     furi_thread_set_context(ble_glue->thread, ble_glue); | ||||||
|     furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); |     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
 |     // Thread configuration
 | ||||||
|     gap->thread = furi_thread_alloc(); |     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_stack_size(gap->thread, 1024); | ||||||
|     furi_thread_set_context(gap->thread, gap); |     furi_thread_set_context(gap->thread, gap); | ||||||
|     furi_thread_set_callback(gap->thread, gap_app); |     furi_thread_set_callback(gap->thread, gap_app); | ||||||
|  | |||||||
| @ -38,7 +38,6 @@ void furi_hal_init() { | |||||||
| 
 | 
 | ||||||
|     // VCP + USB
 |     // VCP + USB
 | ||||||
|     furi_hal_usb_init(); |     furi_hal_usb_init(); | ||||||
|     furi_hal_usb_set_config(&usb_cdc_single); |  | ||||||
|     furi_hal_vcp_init(); |     furi_hal_vcp_init(); | ||||||
|     FURI_LOG_I(TAG, "USB OK"); |     FURI_LOG_I(TAG, "USB OK"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "furi_hal_version.h" | #include "furi_hal_version.h" | ||||||
| #include "furi_hal_usb_i.h" | #include "furi_hal_usb_i.h" | ||||||
| #include "furi_hal_usb.h" | #include "furi_hal_usb.h" | ||||||
|  | #include "furi_hal_vcp.h" | ||||||
| #include <furi_hal_power.h> | #include <furi_hal_power.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| @ -10,29 +11,46 @@ | |||||||
| 
 | 
 | ||||||
| #define USB_RECONNECT_DELAY 500 | #define USB_RECONNECT_DELAY 500 | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbInterface* usb_if_cur; | typedef struct { | ||||||
| static FuriHalUsbInterface* usb_if_next; |     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 const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | ||||||
| 
 | 
 | ||||||
| static uint32_t ubuf[0x20]; | static uint32_t ubuf[0x20]; | ||||||
| usbd_device udev; | usbd_device udev; | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbStateCallback callback; | static int32_t furi_hal_usb_thread(void* context); | ||||||
| static void* cb_ctx; |  | ||||||
| 
 |  | ||||||
| static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length); | 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 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 susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| static void wkup_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); | static void furi_hal_usb_tmr_cb(void* context); | ||||||
| 
 | 
 | ||||||
| /* Low-level init */ | /* Low-level init */ | ||||||
| @ -56,79 +74,51 @@ void furi_hal_usb_init(void) { | |||||||
|     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); |     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); | ||||||
|     // Reset callback will be enabled after first mode change to avoid getting false reset events
 |     // Reset callback will be enabled after first mode change to avoid getting false reset events
 | ||||||
| 
 | 
 | ||||||
|     usb_config.enabled = false; |     usb.enabled = false; | ||||||
|     usb_config.reconnect_tmr = NULL; |     usb.if_cur = NULL; | ||||||
|     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); |     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); | ||||||
|     NVIC_EnableIRQ(USB_LP_IRQn); |     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"); |     FURI_LOG_I(TAG, "Init OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | ||||||
|     if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
 |     usb.if_next = new_if; | ||||||
|         usb_config.mode_changing = true; |     if(usb.thread == NULL) { | ||||||
|         usb_if_next = new_if; |         // Service thread hasn't started yet, so just save interface mode
 | ||||||
|         if(usb_config.reconnect_tmr == NULL) |         return; | ||||||
|             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; |  | ||||||
|     } |     } | ||||||
|     } |     furi_assert(usb.thread); | ||||||
| } |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriHalUsbInterface* furi_hal_usb_get_config() { | FuriHalUsbInterface* furi_hal_usb_get_config() { | ||||||
|     return usb_if_cur; |     return usb.if_cur; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_disable() { | void furi_hal_usb_disable() { | ||||||
|     if(usb_config.enabled) { |     furi_assert(usb.thread); | ||||||
|         susp_evt(&udev, 0, 0); |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable); | ||||||
|         usbd_connect(&udev, false); |  | ||||||
|         usb_config.enabled = false; |  | ||||||
|         FURI_LOG_I(TAG, "USB Disable"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_enable() { | void furi_hal_usb_enable() { | ||||||
|     if((!usb_config.enabled) && (usb_if_cur != NULL)) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable); | ||||||
|         usbd_connect(&udev, true); | } | ||||||
|         usb_config.enabled = true; | 
 | ||||||
|         FURI_LOG_I(TAG, "USB Enable"); | 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) { | 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 */ | /* 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 uint8_t dnumber = req->wValue & 0xFF; | ||||||
|     const void* desc; |     const void* desc; | ||||||
|     uint16_t len = 0; |     uint16_t len = 0; | ||||||
|     if(usb_if_cur == NULL) return usbd_fail; |     if(usb.if_cur == NULL) return usbd_fail; | ||||||
| 
 | 
 | ||||||
|     switch(dtype) { |     switch(dtype) { | ||||||
|     case USB_DTYPE_DEVICE: |     case USB_DTYPE_DEVICE: | ||||||
|         if(callback != NULL) { |         osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventRequest); | ||||||
|             callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx); |         if(usb.callback != NULL) { | ||||||
|  |             usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); | ||||||
|         } |         } | ||||||
|         desc = usb_if_cur->dev_descr; |         desc = usb.if_cur->dev_descr; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_CONFIGURATION: |     case USB_DTYPE_CONFIGURATION: | ||||||
|         desc = usb_if_cur->cfg_descr; |         desc = usb.if_cur->cfg_descr; | ||||||
|         len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0]; |         len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_STRING: |     case USB_DTYPE_STRING: | ||||||
|         if(dnumber == UsbDevLang) { |         if(dnumber == UsbDevLang) { | ||||||
|             desc = &dev_lang_desc; |             desc = &dev_lang_desc; | ||||||
|         } else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) { |         } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_manuf_descr; |             desc = usb.if_cur->str_manuf_descr; | ||||||
|         } else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) { |         } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_prod_descr; |             desc = usb.if_cur->str_prod_descr; | ||||||
|         } else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) { |         } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_serial_descr; |             desc = usb.if_cur->str_serial_descr; | ||||||
|         } else |         } else | ||||||
|             return usbd_fail; |             return usbd_fail; | ||||||
|         break; |         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) { | void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { | ||||||
|     callback = cb; |     usb.callback = cb; | ||||||
|     cb_ctx = ctx; |     usb.cb_ctx = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if(callback != NULL) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReset); | ||||||
|         callback(FuriHalUsbStateEventReset, cb_ctx); |     if(usb.callback != NULL) { | ||||||
|  |         usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == true)) { |     if((usb.if_cur != NULL) && (usb.connected == true)) { | ||||||
|         usb_config.connected = false; |         usb.connected = false; | ||||||
|         usb_if_cur->suspend(&udev); |         usb.if_cur->suspend(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_exit(); |         furi_hal_power_insomnia_exit(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventSuspend, cb_ctx); |         usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == false)) { |     if((usb.if_cur != NULL) && (usb.connected == false)) { | ||||||
|         usb_config.connected = true; |         usb.connected = true; | ||||||
|         usb_if_cur->wakeup(&udev); |         usb.if_cur->wakeup(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_enter(); |         furi_hal_power_insomnia_enter(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventWakeup, cb_ctx); |         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_usb_cdc_i.h> | ||||||
| #include <furi_hal_console.h> | #include <furi_hal.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| 
 | 
 | ||||||
| @ -65,7 +65,7 @@ void furi_hal_vcp_init() { | |||||||
|     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); |     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); | ||||||
| 
 | 
 | ||||||
|     vcp->thread = furi_thread_alloc(); |     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_stack_size(vcp->thread, 1024); | ||||||
|     furi_thread_set_callback(vcp->thread, vcp_worker); |     furi_thread_set_callback(vcp->thread, vcp_worker); | ||||||
|     furi_thread_start(vcp->thread); |     furi_thread_start(vcp->thread); | ||||||
| @ -79,6 +79,7 @@ static int32_t vcp_worker(void* context) { | |||||||
|     size_t missed_rx = 0; |     size_t missed_rx = 0; | ||||||
|     uint8_t last_tx_pkt_len = 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); |     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| #include <furi_hal_spi.h> | #include <furi_hal_spi.h> | ||||||
| 
 | 
 | ||||||
| static const osThreadAttr_t platform_irq_thread_attr = { | static const osThreadAttr_t platform_irq_thread_attr = { | ||||||
|     .name = "RfalIrqWorker", |     .name = "RfalIrqDriver", | ||||||
|     .stack_size = 1024, |     .stack_size = 1024, | ||||||
|     .priority = osPriorityRealtime, |     .priority = osPriorityRealtime, | ||||||
| }; | }; | ||||||
| @ -20,7 +20,7 @@ void nfc_isr(void* _ctx) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void platformIrqWorker() { | void platformIrqThread() { | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(0x1, osFlagsWaitAny, osWaitForever); |         uint32_t flags = osThreadFlagsWait(0x1, osFlagsWaitAny, osWaitForever); | ||||||
|         if(flags & 0x1) { |         if(flags & 0x1) { | ||||||
| @ -41,7 +41,7 @@ void platformDisableIrqCallback() { | |||||||
| 
 | 
 | ||||||
| void platformSetIrqCallback(PlatformIrqCallback callback) { | void platformSetIrqCallback(PlatformIrqCallback callback) { | ||||||
|     platform_irq_callback = 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); |     hal_gpio_add_int_callback(&pin, nfc_isr, NULL); | ||||||
|     // Disable interrupt callback as the pin is shared between 2 apps
 |     // Disable interrupt callback as the pin is shared between 2 apps
 | ||||||
|     // It is enabled in rfalLowPowerModeStop()
 |     // 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) { | static const IrdaProtocolSpecification* irda_get_spec_by_protocol(IrdaProtocol protocol) { | ||||||
|     int index = irda_find_index_by_protocol(protocol); |     int index = irda_find_index_by_protocol(protocol); | ||||||
|     furi_check(index >= 0); |     const IrdaProtocolSpecification* spec = NULL; | ||||||
|     const IrdaProtocolSpecification* spec = |     if(index >= 0) { | ||||||
|         irda_encoder_decoder[index].get_protocol_spec(protocol); |         spec = irda_encoder_decoder[index].get_protocol_spec(protocol); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     furi_assert(spec); |     furi_assert(spec); | ||||||
|     return spec; |     return spec; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Aleksandr Kutuzov
						Aleksandr Kutuzov