[FL-2163] CLI: Separate session from CLI service (#1130)
* CLI: session refactoring * Added forgotten define * Desktop lock state save * Dolphin: use proper type for value returned by dolphin_state_xp_to_levelup Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									76221021ec
								
							
						
					
					
						commit
						0eac917f91
					
				| @ -1,9 +1,11 @@ | |||||||
| #include "cli_i.h" | #include "cli_i.h" | ||||||
| #include "cli_commands.h" | #include "cli_commands.h" | ||||||
| 
 | #include "cli_vcp.h" | ||||||
| #include <furi_hal_version.h> | #include <furi_hal_version.h> | ||||||
| #include <loader/loader.h> | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
|  | #define TAG "CliSrv" | ||||||
|  | 
 | ||||||
| Cli* cli_alloc() { | Cli* cli_alloc() { | ||||||
|     Cli* cli = malloc(sizeof(Cli)); |     Cli* cli = malloc(sizeof(Cli)); | ||||||
| 
 | 
 | ||||||
| @ -12,55 +14,78 @@ Cli* cli_alloc() { | |||||||
|     string_init(cli->last_line); |     string_init(cli->last_line); | ||||||
|     string_init(cli->line); |     string_init(cli->line); | ||||||
| 
 | 
 | ||||||
|  |     cli->session = NULL; | ||||||
|  | 
 | ||||||
|     cli->mutex = osMutexNew(NULL); |     cli->mutex = osMutexNew(NULL); | ||||||
|     furi_check(cli->mutex); |     furi_check(cli->mutex); | ||||||
| 
 | 
 | ||||||
|  |     cli->idle_sem = osSemaphoreNew(1, 0, NULL); | ||||||
|  | 
 | ||||||
|     return cli; |     return cli; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void cli_free(Cli* cli) { | void cli_putc(Cli* cli, char c) { | ||||||
|     furi_assert(cli); |     furi_assert(cli); | ||||||
| 
 |     if(cli->session != NULL) { | ||||||
|     string_clear(cli->last_line); |         cli->session->tx((uint8_t*)&c, 1); | ||||||
|     string_clear(cli->line); |     } | ||||||
| 
 |  | ||||||
|     CliCommandTree_clear(cli->commands); |  | ||||||
| 
 |  | ||||||
|     free(cli); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void cli_putc(char c) { |  | ||||||
|     furi_hal_vcp_tx((uint8_t*)&c, 1); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| char cli_getc(Cli* cli) { | char cli_getc(Cli* cli) { | ||||||
|     furi_assert(cli); |     furi_assert(cli); | ||||||
|     char c; |     char c = 0; | ||||||
|     if(furi_hal_vcp_rx((uint8_t*)&c, 1) == 0) { |     if(cli->session != NULL) { | ||||||
|  |         if(cli->session->rx((uint8_t*)&c, 1, osWaitForever) == 0) { | ||||||
|  |             cli_reset(cli); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|     } |     } | ||||||
|     return c; |     return c; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void cli_stdout_callback(void* _cookie, const char* data, size_t size) { |  | ||||||
|     furi_hal_vcp_tx((const uint8_t*)data, size); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void cli_write(Cli* cli, const uint8_t* buffer, size_t size) { | void cli_write(Cli* cli, const uint8_t* buffer, size_t size) { | ||||||
|     return furi_hal_vcp_tx(buffer, size); |     furi_assert(cli); | ||||||
|  |     if(cli->session != NULL) { | ||||||
|  |         cli->session->tx(buffer, size); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) { | size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) { | ||||||
|     return furi_hal_vcp_rx(buffer, size); |     furi_assert(cli); | ||||||
|  |     if(cli->session != NULL) { | ||||||
|  |         return cli->session->rx(buffer, size, osWaitForever); | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout) { | ||||||
|  |     furi_assert(cli); | ||||||
|  |     if(cli->session != NULL) { | ||||||
|  |         return cli->session->rx(buffer, size, timeout); | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool cli_cmd_interrupt_received(Cli* cli) { | bool cli_cmd_interrupt_received(Cli* cli) { | ||||||
|  |     furi_assert(cli); | ||||||
|     char c = '\0'; |     char c = '\0'; | ||||||
|     if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) { |     if(cli->session != NULL) { | ||||||
|  |         if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) { | ||||||
|             return c == CliSymbolAsciiETX; |             return c == CliSymbolAsciiETX; | ||||||
|     } else { |  | ||||||
|         return false; |  | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool cli_is_connected(Cli* cli) { | ||||||
|  |     furi_assert(cli); | ||||||
|  |     if(cli->session != NULL) { | ||||||
|  |         return (cli->session->is_connected()); | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void cli_print_usage(const char* cmd, const char* usage, const char* arg) { | void cli_print_usage(const char* cmd, const char* usage, const char* arg) { | ||||||
| @ -139,7 +164,7 @@ static void cli_handle_backspace(Cli* cli) { | |||||||
| 
 | 
 | ||||||
|         cli->cursor_position--; |         cli->cursor_position--; | ||||||
|     } else { |     } else { | ||||||
|         cli_putc(CliSymbolAsciiBell); |         cli_putc(cli, CliSymbolAsciiBell); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -210,7 +235,7 @@ static void cli_handle_enter(Cli* cli) { | |||||||
|         printf( |         printf( | ||||||
|             "`%s` command not found, use `help` or `?` to list all available commands", |             "`%s` command not found, use `help` or `?` to list all available commands", | ||||||
|             string_get_cstr(command)); |             string_get_cstr(command)); | ||||||
|         cli_putc(CliSymbolAsciiBell); |         cli_putc(cli, CliSymbolAsciiBell); | ||||||
|     } |     } | ||||||
|     furi_check(osMutexRelease(cli->mutex) == osOK); |     furi_check(osMutexRelease(cli->mutex) == osOK); | ||||||
| 
 | 
 | ||||||
| @ -301,43 +326,43 @@ static void cli_handle_escape(Cli* cli, char c) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void cli_process_input(Cli* cli) { | void cli_process_input(Cli* cli) { | ||||||
|     char c = cli_getc(cli); |     char in_chr = cli_getc(cli); | ||||||
|     size_t r; |     size_t rx_len; | ||||||
| 
 | 
 | ||||||
|     if(c == CliSymbolAsciiTab) { |     if(in_chr == CliSymbolAsciiTab) { | ||||||
|         cli_handle_autocomplete(cli); |         cli_handle_autocomplete(cli); | ||||||
|     } else if(c == CliSymbolAsciiSOH) { |     } else if(in_chr == CliSymbolAsciiSOH) { | ||||||
|         osDelay(33); // We are too fast, Minicom is not ready yet
 |         osDelay(33); // We are too fast, Minicom is not ready yet
 | ||||||
|         cli_motd(); |         cli_motd(); | ||||||
|         cli_prompt(cli); |         cli_prompt(cli); | ||||||
|     } else if(c == CliSymbolAsciiETX) { |     } else if(in_chr == CliSymbolAsciiETX) { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|         cli_prompt(cli); |         cli_prompt(cli); | ||||||
|     } else if(c == CliSymbolAsciiEOT) { |     } else if(in_chr == CliSymbolAsciiEOT) { | ||||||
|         cli_reset(cli); |         cli_reset(cli); | ||||||
|     } else if(c == CliSymbolAsciiEsc) { |     } else if(in_chr == CliSymbolAsciiEsc) { | ||||||
|         r = furi_hal_vcp_rx((uint8_t*)&c, 1); |         rx_len = cli_read(cli, (uint8_t*)&in_chr, 1); | ||||||
|         if(r && c == '[') { |         if((rx_len > 0) && (in_chr == '[')) { | ||||||
|             furi_hal_vcp_rx((uint8_t*)&c, 1); |             cli_read(cli, (uint8_t*)&in_chr, 1); | ||||||
|             cli_handle_escape(cli, c); |             cli_handle_escape(cli, in_chr); | ||||||
|         } else { |         } else { | ||||||
|             cli_putc(CliSymbolAsciiBell); |             cli_putc(cli, CliSymbolAsciiBell); | ||||||
|         } |         } | ||||||
|     } else if(c == CliSymbolAsciiBackspace || c == CliSymbolAsciiDel) { |     } else if(in_chr == CliSymbolAsciiBackspace || in_chr == CliSymbolAsciiDel) { | ||||||
|         cli_handle_backspace(cli); |         cli_handle_backspace(cli); | ||||||
|     } else if(c == CliSymbolAsciiCR) { |     } else if(in_chr == CliSymbolAsciiCR) { | ||||||
|         cli_handle_enter(cli); |         cli_handle_enter(cli); | ||||||
|     } else if(c >= 0x20 && c < 0x7F) { |     } else if(in_chr >= 0x20 && in_chr < 0x7F) { | ||||||
|         if(cli->cursor_position == string_size(cli->line)) { |         if(cli->cursor_position == string_size(cli->line)) { | ||||||
|             string_push_back(cli->line, c); |             string_push_back(cli->line, in_chr); | ||||||
|             cli_putc(c); |             cli_putc(cli, in_chr); | ||||||
|         } else { |         } else { | ||||||
|             // ToDo: better way?
 |             // ToDo: better way?
 | ||||||
|             string_t temp; |             string_t temp; | ||||||
|             string_init(temp); |             string_init(temp); | ||||||
|             string_reserve(temp, string_size(cli->line) + 1); |             string_reserve(temp, string_size(cli->line) + 1); | ||||||
|             string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position); |             string_set_strn(temp, string_get_cstr(cli->line), cli->cursor_position); | ||||||
|             string_push_back(temp, c); |             string_push_back(temp, in_chr); | ||||||
|             string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); |             string_cat_str(temp, string_get_cstr(cli->line) + cli->cursor_position); | ||||||
| 
 | 
 | ||||||
|             // cli->line is cleared and temp's buffer moved to cli->line
 |             // cli->line is cleared and temp's buffer moved to cli->line
 | ||||||
| @ -345,12 +370,12 @@ void cli_process_input(Cli* cli) { | |||||||
|             // NO MEMORY LEAK, STOP REPORTING IT
 |             // NO MEMORY LEAK, STOP REPORTING IT
 | ||||||
| 
 | 
 | ||||||
|             // Print character in replace mode
 |             // Print character in replace mode
 | ||||||
|             printf("\e[4h%c\e[4l", c); |             printf("\e[4h%c\e[4l", in_chr); | ||||||
|             fflush(stdout); |             fflush(stdout); | ||||||
|         } |         } | ||||||
|         cli->cursor_position++; |         cli->cursor_position++; | ||||||
|     } else { |     } else { | ||||||
|         cli_putc(CliSymbolAsciiBell); |         cli_putc(cli, CliSymbolAsciiBell); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -398,19 +423,59 @@ void cli_delete_command(Cli* cli, const char* name) { | |||||||
|     string_clear(name_str); |     string_clear(name_str); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void cli_session_open(Cli* cli, void* session) { | ||||||
|  |     furi_assert(cli); | ||||||
|  | 
 | ||||||
|  |     furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); | ||||||
|  |     cli->session = session; | ||||||
|  |     if(cli->session != NULL) { | ||||||
|  |         cli->session->init(); | ||||||
|  |         furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); | ||||||
|  |     } else { | ||||||
|  |         furi_stdglue_set_thread_stdout_callback(NULL); | ||||||
|  |     } | ||||||
|  |     osSemaphoreRelease(cli->idle_sem); | ||||||
|  |     furi_check(osMutexRelease(cli->mutex) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void cli_session_close(Cli* cli) { | ||||||
|  |     furi_assert(cli); | ||||||
|  | 
 | ||||||
|  |     furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK); | ||||||
|  |     if(cli->session != NULL) { | ||||||
|  |         cli->session->deinit(); | ||||||
|  |     } | ||||||
|  |     cli->session = NULL; | ||||||
|  |     furi_stdglue_set_thread_stdout_callback(NULL); | ||||||
|  |     furi_check(osMutexRelease(cli->mutex) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int32_t cli_srv(void* p) { | int32_t cli_srv(void* p) { | ||||||
|     Cli* cli = cli_alloc(); |     Cli* cli = cli_alloc(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_vcp_init(); |  | ||||||
| 
 |  | ||||||
|     // Init basic cli commands
 |     // Init basic cli commands
 | ||||||
|     cli_commands_init(cli); |     cli_commands_init(cli); | ||||||
| 
 | 
 | ||||||
|     furi_record_create("cli", cli); |     furi_record_create("cli", cli); | ||||||
| 
 | 
 | ||||||
|     furi_stdglue_set_thread_stdout_callback(cli_stdout_callback); |     if(cli->session != NULL) { | ||||||
|  |         furi_stdglue_set_thread_stdout_callback(cli->session->tx_stdout); | ||||||
|  |     } else { | ||||||
|  |         furi_stdglue_set_thread_stdout_callback(NULL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { | ||||||
|  |         cli_session_open(cli, &cli_vcp); | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_W(TAG, "Skipped CLI session open: device in special startup mode"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|  |         if(cli->session != NULL) { | ||||||
|             cli_process_input(cli); |             cli_process_input(cli); | ||||||
|  |         } else { | ||||||
|  |             furi_check(osSemaphoreAcquire(cli->idle_sem, osWaitForever) == osOK); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -73,17 +73,28 @@ void cli_print_usage(const char* cmd, const char* usage, const char* arg); | |||||||
|  */ |  */ | ||||||
| void cli_delete_command(Cli* cli, const char* name); | void cli_delete_command(Cli* cli, const char* name); | ||||||
| 
 | 
 | ||||||
| /** Read from terminal Do it only from inside of cli call.
 | /** Read from terminal
 | ||||||
|  * |  * | ||||||
|  * @param      cli     Cli instance |  * @param      cli     Cli instance | ||||||
|  * @param      buffer  pointer to buffer |  * @param      buffer  pointer to buffer | ||||||
|  * @param      size    size of buffer in bytes |  * @param      size    size of buffer in bytes | ||||||
|  * |  * | ||||||
|  * @return     bytes written |  * @return     bytes read | ||||||
|  */ |  */ | ||||||
| size_t cli_read(Cli* cli, uint8_t* buffer, size_t size); | size_t cli_read(Cli* cli, uint8_t* buffer, size_t size); | ||||||
| 
 | 
 | ||||||
| /** Not blocking check for interrupt command received
 | /** Non-blocking read from terminal
 | ||||||
|  |  * | ||||||
|  |  * @param      cli     Cli instance | ||||||
|  |  * @param      buffer  pointer to buffer | ||||||
|  |  * @param      size    size of buffer in bytes | ||||||
|  |  * @param      timeout timeout value in ms | ||||||
|  |  * | ||||||
|  |  * @return     bytes read | ||||||
|  |  */ | ||||||
|  | size_t cli_read_timeout(Cli* cli, uint8_t* buffer, size_t size, uint32_t timeout); | ||||||
|  | 
 | ||||||
|  | /** Non-blocking check for interrupt command received
 | ||||||
|  * |  * | ||||||
|  * @param      cli   Cli instance |  * @param      cli   Cli instance | ||||||
|  * |  * | ||||||
| @ -111,6 +122,12 @@ char cli_getc(Cli* cli); | |||||||
|  */ |  */ | ||||||
| void cli_nl(); | void cli_nl(); | ||||||
| 
 | 
 | ||||||
|  | void cli_session_open(Cli* cli, void* session); | ||||||
|  | 
 | ||||||
|  | void cli_session_close(Cli* cli); | ||||||
|  | 
 | ||||||
|  | bool cli_is_connected(Cli* cli); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -18,6 +18,17 @@ typedef struct { | |||||||
|     uint32_t flags; |     uint32_t flags; | ||||||
| } CliCommand; | } CliCommand; | ||||||
| 
 | 
 | ||||||
|  | typedef struct CliSession CliSession; | ||||||
|  | 
 | ||||||
|  | struct CliSession { | ||||||
|  |     void (*init)(void); | ||||||
|  |     void (*deinit)(void); | ||||||
|  |     size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); | ||||||
|  |     void (*tx)(const uint8_t* buffer, size_t size); | ||||||
|  |     void (*tx_stdout)(void* _cookie, const char* data, size_t size); | ||||||
|  |     bool (*is_connected)(void); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| BPTREE_DEF2( | BPTREE_DEF2( | ||||||
|     CliCommandTree, |     CliCommandTree, | ||||||
|     CLI_COMMANDS_TREE_RANK, |     CLI_COMMANDS_TREE_RANK, | ||||||
| @ -31,18 +42,18 @@ BPTREE_DEF2( | |||||||
| struct Cli { | struct Cli { | ||||||
|     CliCommandTree_t commands; |     CliCommandTree_t commands; | ||||||
|     osMutexId_t mutex; |     osMutexId_t mutex; | ||||||
|  |     osSemaphoreId_t idle_sem; | ||||||
|     string_t last_line; |     string_t last_line; | ||||||
|     string_t line; |     string_t line; | ||||||
|  |     CliSession* session; | ||||||
| 
 | 
 | ||||||
|     size_t cursor_position; |     size_t cursor_position; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| Cli* cli_alloc(); | Cli* cli_alloc(); | ||||||
| 
 | 
 | ||||||
| void cli_free(Cli* cli); |  | ||||||
| 
 |  | ||||||
| void cli_reset(Cli* cli); | void cli_reset(Cli* cli); | ||||||
| 
 | 
 | ||||||
| void cli_putc(char c); | void cli_putc(Cli* cli, char c); | ||||||
| 
 | 
 | ||||||
| void cli_stdout_callback(void* _cookie, const char* data, size_t size); | void cli_stdout_callback(void* _cookie, const char* data, size_t size); | ||||||
|  | |||||||
| @ -2,8 +2,9 @@ | |||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
|  | #include "cli_i.h" | ||||||
| 
 | 
 | ||||||
| #define TAG "FuriHalVcp" | #define TAG "CliVcp" | ||||||
| 
 | 
 | ||||||
| #define USB_CDC_PKT_LEN CDC_DATA_SZ | #define USB_CDC_PKT_LEN CDC_DATA_SZ | ||||||
| #define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) | #define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3) | ||||||
| @ -12,19 +13,18 @@ | |||||||
| #define VCP_IF_NUM 0 | #define VCP_IF_NUM 0 | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     VcpEvtEnable = (1 << 0), |     VcpEvtStop = (1 << 0), | ||||||
|     VcpEvtDisable = (1 << 1), |     VcpEvtConnect = (1 << 1), | ||||||
|     VcpEvtConnect = (1 << 2), |     VcpEvtDisconnect = (1 << 2), | ||||||
|     VcpEvtDisconnect = (1 << 3), |     VcpEvtStreamRx = (1 << 3), | ||||||
|     VcpEvtStreamRx = (1 << 4), |     VcpEvtRx = (1 << 4), | ||||||
|     VcpEvtRx = (1 << 5), |     VcpEvtStreamTx = (1 << 5), | ||||||
|     VcpEvtStreamTx = (1 << 6), |     VcpEvtTx = (1 << 6), | ||||||
|     VcpEvtTx = (1 << 7), |  | ||||||
| } WorkerEvtFlags; | } WorkerEvtFlags; | ||||||
| 
 | 
 | ||||||
| #define VCP_THREAD_FLAG_ALL                                                                 \ | #define VCP_THREAD_FLAG_ALL                                                                 \ | ||||||
|     (VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | \ |     (VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \ | ||||||
|      VcpEvtStreamRx | VcpEvtStreamTx) |      VcpEvtStreamTx) | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     FuriThread* thread; |     FuriThread* thread; | ||||||
| @ -33,9 +33,12 @@ typedef struct { | |||||||
|     StreamBufferHandle_t rx_stream; |     StreamBufferHandle_t rx_stream; | ||||||
| 
 | 
 | ||||||
|     volatile bool connected; |     volatile bool connected; | ||||||
|  |     volatile bool running; | ||||||
|  | 
 | ||||||
|  |     FuriHalUsbInterface* usb_if_prev; | ||||||
| 
 | 
 | ||||||
|     uint8_t data_buffer[USB_CDC_PKT_LEN]; |     uint8_t data_buffer[USB_CDC_PKT_LEN]; | ||||||
| } FuriHalVcp; | } CliVcp; | ||||||
| 
 | 
 | ||||||
| static int32_t vcp_worker(void* context); | static int32_t vcp_worker(void* context); | ||||||
| static void vcp_on_cdc_tx_complete(void* context); | static void vcp_on_cdc_tx_complete(void* context); | ||||||
| @ -51,25 +54,23 @@ static CdcCallbacks cdc_cb = { | |||||||
|     NULL, |     NULL, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static FuriHalVcp* vcp = NULL; | static CliVcp* vcp = NULL; | ||||||
| 
 | 
 | ||||||
| static const uint8_t ascii_soh = 0x01; | static const uint8_t ascii_soh = 0x01; | ||||||
| static const uint8_t ascii_eot = 0x04; | static const uint8_t ascii_eot = 0x04; | ||||||
| 
 | 
 | ||||||
| void furi_hal_vcp_init() { | static void cli_vcp_init() { | ||||||
|     vcp = malloc(sizeof(FuriHalVcp)); |     if(vcp == NULL) { | ||||||
|     vcp->connected = false; |         vcp = malloc(sizeof(CliVcp)); | ||||||
| 
 |  | ||||||
|         vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); |         vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1); | ||||||
|         vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); |         vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); | ||||||
| 
 |  | ||||||
|     if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { |  | ||||||
|         FURI_LOG_W(TAG, "Skipped worker init: device in special startup mode"); |  | ||||||
|         return; |  | ||||||
|     } |     } | ||||||
|  |     furi_assert(vcp->thread == NULL); | ||||||
|  | 
 | ||||||
|  |     vcp->connected = false; | ||||||
| 
 | 
 | ||||||
|     vcp->thread = furi_thread_alloc(); |     vcp->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(vcp->thread, "VcpDriver"); |     furi_thread_set_name(vcp->thread, "CliVcpWorker"); | ||||||
|     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); | ||||||
| @ -77,48 +78,35 @@ void furi_hal_vcp_init() { | |||||||
|     FURI_LOG_I(TAG, "Init OK"); |     FURI_LOG_I(TAG, "Init OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void cli_vcp_deinit() { | ||||||
|  |     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStop); | ||||||
|  |     furi_thread_join(vcp->thread); | ||||||
|  |     furi_thread_free(vcp->thread); | ||||||
|  |     vcp->thread = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static int32_t vcp_worker(void* context) { | static int32_t vcp_worker(void* context) { | ||||||
|     bool enabled = true; |     bool tx_idle = true; | ||||||
|     bool tx_idle = false; |  | ||||||
|     size_t missed_rx = 0; |     size_t missed_rx = 0; | ||||||
|     uint8_t last_tx_pkt_len = 0; |     uint8_t last_tx_pkt_len = 0; | ||||||
| 
 | 
 | ||||||
|  |     // Switch USB to VCP mode (if it is not set yet)
 | ||||||
|  |     vcp->usb_if_prev = furi_hal_usb_get_config(); | ||||||
|  |     if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { | ||||||
|         furi_hal_usb_set_config(&usb_cdc_single, NULL); |         furi_hal_usb_set_config(&usb_cdc_single, NULL); | ||||||
|  |     } | ||||||
|     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); |     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||||
| 
 | 
 | ||||||
|  |     FURI_LOG_D(TAG, "Start"); | ||||||
|  |     vcp->running = true; | ||||||
|  | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); |         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); | ||||||
|         furi_assert((flags & osFlagsError) == 0); |         furi_assert((flags & osFlagsError) == 0); | ||||||
| 
 | 
 | ||||||
|         // VCP enabled
 |  | ||||||
|         if((flags & VcpEvtEnable) && !enabled) { |  | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG |  | ||||||
|             FURI_LOG_D(TAG, "Enable"); |  | ||||||
| #endif |  | ||||||
|             flags |= VcpEvtTx; |  | ||||||
|             furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); |  | ||||||
|             enabled = true; |  | ||||||
|             furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer
 |  | ||||||
|             if(furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) { |  | ||||||
|                 vcp->connected = true; |  | ||||||
|                 xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // VCP disabled
 |  | ||||||
|         if((flags & VcpEvtDisable) && enabled) { |  | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG |  | ||||||
|             FURI_LOG_D(TAG, "Disable"); |  | ||||||
| #endif |  | ||||||
|             enabled = false; |  | ||||||
|             vcp->connected = false; |  | ||||||
|             xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); |  | ||||||
|             xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // VCP session opened
 |         // VCP session opened
 | ||||||
|         if((flags & VcpEvtConnect) && enabled) { |         if(flags & VcpEvtConnect) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "Connect"); |             FURI_LOG_D(TAG, "Connect"); | ||||||
| #endif | #endif | ||||||
|             if(vcp->connected == false) { |             if(vcp->connected == false) { | ||||||
| @ -128,8 +116,8 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // VCP session closed
 |         // VCP session closed
 | ||||||
|         if((flags & VcpEvtDisconnect) && enabled) { |         if(flags & VcpEvtDisconnect) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "Disconnect"); |             FURI_LOG_D(TAG, "Disconnect"); | ||||||
| #endif | #endif | ||||||
|             if(vcp->connected == true) { |             if(vcp->connected == true) { | ||||||
| @ -140,8 +128,8 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Rx buffer was read, maybe there is enough space for new data?
 |         // Rx buffer was read, maybe there is enough space for new data?
 | ||||||
|         if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) { |         if((flags & VcpEvtStreamRx) && (missed_rx > 0)) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "StreamRx"); |             FURI_LOG_D(TAG, "StreamRx"); | ||||||
| #endif | #endif | ||||||
|             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { |             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||||
| @ -151,10 +139,10 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // New data received
 |         // New data received
 | ||||||
|         if((flags & VcpEvtRx)) { |         if(flags & VcpEvtRx) { | ||||||
|             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { |             if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||||
|                 int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); |                 int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|                 FURI_LOG_D(TAG, "Rx %d", len); |                 FURI_LOG_D(TAG, "Rx %d", len); | ||||||
| #endif | #endif | ||||||
|                 if(len > 0) { |                 if(len > 0) { | ||||||
| @ -163,7 +151,7 @@ static int32_t vcp_worker(void* context) { | |||||||
|                         len); |                         len); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|                 FURI_LOG_D(TAG, "Rx missed"); |                 FURI_LOG_D(TAG, "Rx missed"); | ||||||
| #endif | #endif | ||||||
|                 missed_rx++; |                 missed_rx++; | ||||||
| @ -171,8 +159,8 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // New data in Tx buffer
 |         // New data in Tx buffer
 | ||||||
|         if((flags & VcpEvtStreamTx) && enabled) { |         if(flags & VcpEvtStreamTx) { | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "StreamTx"); |             FURI_LOG_D(TAG, "StreamTx"); | ||||||
| #endif | #endif | ||||||
|             if(tx_idle) { |             if(tx_idle) { | ||||||
| @ -181,10 +169,10 @@ static int32_t vcp_worker(void* context) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // CDC write transfer done
 |         // CDC write transfer done
 | ||||||
|         if((flags & VcpEvtTx) && enabled) { |         if(flags & VcpEvtTx) { | ||||||
|             size_t len = |             size_t len = | ||||||
|                 xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); |                 xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|             FURI_LOG_D(TAG, "Tx %d", len); |             FURI_LOG_D(TAG, "Tx %d", len); | ||||||
| #endif | #endif | ||||||
|             if(len > 0) { // Some data left in Tx buffer. Sending it now
 |             if(len > 0) { // Some data left in Tx buffer. Sending it now
 | ||||||
| @ -202,23 +190,33 @@ static int32_t vcp_worker(void* context) { | |||||||
|                 last_tx_pkt_len = 0; |                 last_tx_pkt_len = 0; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if(flags & VcpEvtStop) { | ||||||
|  |             vcp->connected = false; | ||||||
|  |             vcp->running = false; | ||||||
|  |             furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL, NULL); | ||||||
|  |             // Restore previous USB mode (if it was set during init)
 | ||||||
|  |             if((vcp->usb_if_prev != &usb_cdc_single) && (vcp->usb_if_prev != &usb_cdc_dual)) { | ||||||
|  |                 furi_hal_usb_set_config(vcp->usb_if_prev, NULL); | ||||||
|             } |             } | ||||||
|  |             xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); | ||||||
|  |             xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     FURI_LOG_D(TAG, "End"); | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_vcp_enable() { | static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtEnable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void furi_hal_vcp_disable() { |  | ||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) { |  | ||||||
|     furi_assert(vcp); |     furi_assert(vcp); | ||||||
|     furi_assert(buffer); |     furi_assert(buffer); | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG |     if(vcp->running == false) { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #ifdef CLI_VCP_DEBUG | ||||||
|     FURI_LOG_D(TAG, "rx %u start", size); |     FURI_LOG_D(TAG, "rx %u start", size); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| @ -229,7 +227,7 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo | |||||||
|         if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; |         if(batch_size > VCP_RX_BUF_SIZE) batch_size = VCP_RX_BUF_SIZE; | ||||||
| 
 | 
 | ||||||
|         size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout); |         size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout); | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|         FURI_LOG_D(TAG, "rx %u ", batch_size); |         FURI_LOG_D(TAG, "rx %u ", batch_size); | ||||||
| #endif | #endif | ||||||
|         if(len == 0) break; |         if(len == 0) break; | ||||||
| @ -239,22 +237,21 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo | |||||||
|         rx_cnt += len; |         rx_cnt += len; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|     FURI_LOG_D(TAG, "rx %u end", size); |     FURI_LOG_D(TAG, "rx %u end", size); | ||||||
| #endif | #endif | ||||||
|     return rx_cnt; |     return rx_cnt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) { | static void cli_vcp_tx(const uint8_t* buffer, size_t size) { | ||||||
|     furi_assert(vcp); |  | ||||||
|     return furi_hal_vcp_rx_with_timeout(buffer, size, osWaitForever); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { |  | ||||||
|     furi_assert(vcp); |     furi_assert(vcp); | ||||||
|     furi_assert(buffer); |     furi_assert(buffer); | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG |     if(vcp->running == false) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | #ifdef CLI_VCP_DEBUG | ||||||
|     FURI_LOG_D(TAG, "tx %u start", size); |     FURI_LOG_D(TAG, "tx %u start", size); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| @ -264,7 +261,7 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { | |||||||
| 
 | 
 | ||||||
|         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); |         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx); |         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx); | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|         FURI_LOG_D(TAG, "tx %u", batch_size); |         FURI_LOG_D(TAG, "tx %u", batch_size); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| @ -272,11 +269,15 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { | |||||||
|         buffer += batch_size; |         buffer += batch_size; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | #ifdef CLI_VCP_DEBUG | ||||||
|     FURI_LOG_D(TAG, "tx %u end", size); |     FURI_LOG_D(TAG, "tx %u end", size); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void cli_vcp_tx_stdout(void* _cookie, const char* data, size_t size) { | ||||||
|  |     cli_vcp_tx((const uint8_t*)data, size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void vcp_state_callback(void* context, uint8_t state) { | static void vcp_state_callback(void* context, uint8_t state) { | ||||||
|     if(state == 0) { |     if(state == 0) { | ||||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); |         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); | ||||||
| @ -303,7 +304,16 @@ static void vcp_on_cdc_tx_complete(void* context) { | |||||||
|     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx); |     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_vcp_is_connected(void) { | static bool cli_vcp_is_connected(void) { | ||||||
|     furi_assert(vcp); |     furi_assert(vcp); | ||||||
|     return vcp->connected; |     return vcp->connected; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | CliSession cli_vcp = { | ||||||
|  |     cli_vcp_init, | ||||||
|  |     cli_vcp_deinit, | ||||||
|  |     cli_vcp_rx, | ||||||
|  |     cli_vcp_tx, | ||||||
|  |     cli_vcp_tx_stdout, | ||||||
|  |     cli_vcp_is_connected, | ||||||
|  | }; | ||||||
							
								
								
									
										18
									
								
								applications/cli/cli_vcp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								applications/cli/cli_vcp.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | /**
 | ||||||
|  |  * @file cli_vcp.h | ||||||
|  |  * VCP HAL API | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "cli_i.h" | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | extern CliSession cli_vcp; | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -5,7 +5,7 @@ | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| 
 | 
 | ||||||
| #define DESKTOP_SETTINGS_VER (2) | #define DESKTOP_SETTINGS_VER (3) | ||||||
| #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" | #define DESKTOP_SETTINGS_PATH "/int/desktop.settings" | ||||||
| #define DESKTOP_SETTINGS_MAGIC (0x17) | #define DESKTOP_SETTINGS_MAGIC (0x17) | ||||||
| #define PIN_MAX_LENGTH 12 | #define PIN_MAX_LENGTH 12 | ||||||
| @ -39,5 +39,6 @@ typedef struct { | |||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t favorite; |     uint16_t favorite; | ||||||
|     PinCode pin_code; |     PinCode pin_code; | ||||||
|  |     uint8_t is_locked; | ||||||
|     uint32_t auto_lock_delay_ms; |     uint32_t auto_lock_delay_ms; | ||||||
| } DesktopSettings; | } DesktopSettings; | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ | |||||||
| 
 | 
 | ||||||
| #include "../helpers/pin_lock.h" | #include "../helpers/pin_lock.h" | ||||||
| #include "../desktop_i.h" | #include "../desktop_i.h" | ||||||
|  | #include <cli/cli_vcp.h> | ||||||
| 
 | 
 | ||||||
| static const NotificationSequence sequence_pin_fail = { | static const NotificationSequence sequence_pin_fail = { | ||||||
|     &message_display_on, |     &message_display_on, | ||||||
| @ -61,26 +62,50 @@ uint32_t desktop_pin_lock_get_fail_timeout() { | |||||||
|     return pin_timeout; |     return pin_timeout; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_pin_lock() { | void desktop_pin_lock(DesktopSettings* settings) { | ||||||
|  |     furi_assert(settings); | ||||||
|  | 
 | ||||||
|     furi_hal_rtc_set_pin_fails(0); |     furi_hal_rtc_set_pin_fails(0); | ||||||
|     furi_hal_rtc_set_flag(FuriHalRtcFlagLock); |     furi_hal_rtc_set_flag(FuriHalRtcFlagLock); | ||||||
|     furi_hal_usb_disable(); |     Cli* cli = furi_record_open("cli"); | ||||||
|  |     cli_session_close(cli); | ||||||
|  |     furi_record_close("cli"); | ||||||
|  |     settings->is_locked = 1; | ||||||
|  |     SAVE_DESKTOP_SETTINGS(settings); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_pin_unlock() { | void desktop_pin_unlock(DesktopSettings* settings) { | ||||||
|  |     furi_assert(settings); | ||||||
|  | 
 | ||||||
|     furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |     furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); | ||||||
|     furi_hal_usb_enable(); |     Cli* cli = furi_record_open("cli"); | ||||||
|  |     cli_session_open(cli, &cli_vcp); | ||||||
|  |     furi_record_close("cli"); | ||||||
|  |     settings->is_locked = 0; | ||||||
|  |     SAVE_DESKTOP_SETTINGS(settings); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void desktop_pin_lock_init(DesktopSettings* settings) { | void desktop_pin_lock_init(DesktopSettings* settings) { | ||||||
|  |     furi_assert(settings); | ||||||
|  | 
 | ||||||
|     if(settings->pin_code.length > 0) { |     if(settings->pin_code.length > 0) { | ||||||
|  |         if(settings->is_locked == 1) { | ||||||
|             furi_hal_rtc_set_flag(FuriHalRtcFlagLock); |             furi_hal_rtc_set_flag(FuriHalRtcFlagLock); | ||||||
|         furi_hal_usb_disable(); |         } else { | ||||||
|  |             if(desktop_pin_lock_is_locked()) { | ||||||
|  |                 settings->is_locked = 1; | ||||||
|  |                 SAVE_DESKTOP_SETTINGS(settings); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } else { |     } else { | ||||||
|         furi_hal_rtc_set_pin_fails(0); |         furi_hal_rtc_set_pin_fails(0); | ||||||
|         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); |         furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); | ||||||
|         furi_hal_usb_enable(); |         furi_hal_usb_enable(); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     if(desktop_pin_lock_is_locked()) { | ||||||
|  |         furi_hal_usb_disable(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) { | bool desktop_pin_lock_verify(const PinCode* pin_set, const PinCode* pin_entered) { | ||||||
|  | |||||||
| @ -8,9 +8,9 @@ void desktop_pin_lock_error_notify(); | |||||||
| 
 | 
 | ||||||
| uint32_t desktop_pin_lock_get_fail_timeout(); | uint32_t desktop_pin_lock_get_fail_timeout(); | ||||||
| 
 | 
 | ||||||
| void desktop_pin_lock(); | void desktop_pin_lock(DesktopSettings* settings); | ||||||
| 
 | 
 | ||||||
| void desktop_pin_unlock(); | void desktop_pin_unlock(DesktopSettings* settings); | ||||||
| 
 | 
 | ||||||
| bool desktop_pin_lock_is_locked(); | bool desktop_pin_lock_is_locked(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|             break; |             break; | ||||||
|         case DesktopLockMenuEventPinLock: |         case DesktopLockMenuEventPinLock: | ||||||
|             if(desktop->settings.pin_code.length > 0) { |             if(desktop->settings.pin_code.length > 0) { | ||||||
|                 desktop_pin_lock(); |                 desktop_pin_lock(&desktop->settings); | ||||||
|                 desktop_lock(desktop); |                 desktop_lock(desktop); | ||||||
|             } else { |             } else { | ||||||
|                 LoaderStatus status = |                 LoaderStatus status = | ||||||
|  | |||||||
| @ -126,7 +126,7 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) { | |||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopPinInputEventUnlocked: |         case DesktopPinInputEventUnlocked: | ||||||
|             desktop_pin_unlock(); |             desktop_pin_unlock(&desktop->settings); | ||||||
|             desktop_unlock(desktop); |             desktop_unlock(desktop); | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -148,7 +148,7 @@ void dolphin_state_on_deed(DolphinState* dolphin_state, DolphinDeed deed) { | |||||||
|         dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app]; |         dolphin_deed_get_app_limit(app) - dolphin_state->data.icounter_daily_limit[app]; | ||||||
|     uint8_t deed_weight = CLAMP(dolphin_deed_get_weight(deed), weight_limit, 0); |     uint8_t deed_weight = CLAMP(dolphin_deed_get_weight(deed), weight_limit, 0); | ||||||
| 
 | 
 | ||||||
|     uint8_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter); |     uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(dolphin_state->data.icounter); | ||||||
|     if(xp_to_levelup) { |     if(xp_to_levelup) { | ||||||
|         deed_weight = MIN(xp_to_levelup, deed_weight); |         deed_weight = MIN(xp_to_levelup, deed_weight); | ||||||
|         dolphin_state->data.icounter += deed_weight; |         dolphin_state->data.icounter += deed_weight; | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ | |||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| #include <furi_hal_usb_cdc_i.h> | #include <furi_hal_usb_cdc_i.h> | ||||||
| #include "usb_cdc.h" | #include "usb_cdc.h" | ||||||
|  | #include "cli/cli_vcp.h" | ||||||
|  | #include "cli/cli.h" | ||||||
| 
 | 
 | ||||||
| #define USB_CDC_PKT_LEN CDC_DATA_SZ | #define USB_CDC_PKT_LEN CDC_DATA_SZ | ||||||
| #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) | #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) | ||||||
| @ -16,17 +18,16 @@ static const GpioPin* flow_pins[][2] = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event
 |     WorkerEvtStop = (1 << 0), | ||||||
|     WorkerEvtStop = (1 << 1), |     WorkerEvtRxDone = (1 << 1), | ||||||
|     WorkerEvtRxDone = (1 << 2), |  | ||||||
| 
 | 
 | ||||||
|     WorkerEvtTxStop = (1 << 3), |     WorkerEvtTxStop = (1 << 2), | ||||||
|     WorkerEvtCdcRx = (1 << 4), |     WorkerEvtCdcRx = (1 << 3), | ||||||
| 
 | 
 | ||||||
|     WorkerEvtCfgChange = (1 << 5), |     WorkerEvtCfgChange = (1 << 4), | ||||||
| 
 | 
 | ||||||
|     WorkerEvtLineCfgSet = (1 << 6), |     WorkerEvtLineCfgSet = (1 << 5), | ||||||
|     WorkerEvtCtrlLineSet = (1 << 7), |     WorkerEvtCtrlLineSet = (1 << 6), | ||||||
| 
 | 
 | ||||||
| } WorkerEvtFlags; | } WorkerEvtFlags; | ||||||
| 
 | 
 | ||||||
| @ -84,18 +85,29 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { | |||||||
| 
 | 
 | ||||||
| static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|     furi_hal_usb_unlock(); |     furi_hal_usb_unlock(); | ||||||
|  |     FURI_LOG_I("", "Init %d", vcp_ch); | ||||||
|     if(vcp_ch == 0) { |     if(vcp_ch == 0) { | ||||||
|  |         Cli* cli = furi_record_open("cli"); | ||||||
|  |         cli_session_close(cli); | ||||||
|  |         furi_record_close("cli"); | ||||||
|         furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); |         furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||||
|         furi_hal_vcp_disable(); |  | ||||||
|     } else { |     } else { | ||||||
|         furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); |         furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); | ||||||
|  |         Cli* cli = furi_record_open("cli"); | ||||||
|  |         cli_session_open(cli, &cli_vcp); | ||||||
|  |         furi_record_close("cli"); | ||||||
|     } |     } | ||||||
|     furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); |     furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { | ||||||
|     furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); |     furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); | ||||||
|     if(vcp_ch == 0) furi_hal_vcp_enable(); |     FURI_LOG_I("", "Deinit %d", vcp_ch); | ||||||
|  |     if(vcp_ch != 0) { | ||||||
|  |         Cli* cli = furi_record_open("cli"); | ||||||
|  |         cli_session_close(cli); | ||||||
|  |         furi_record_close("cli"); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { | static void usb_uart_serial_init(UsbUartBridge* usb_uart, uint8_t uart_ch) { | ||||||
| @ -155,7 +167,6 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|     furi_thread_set_context(usb_uart->tx_thread, usb_uart); |     furi_thread_set_context(usb_uart->tx_thread, usb_uart); | ||||||
|     furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); |     furi_thread_set_callback(usb_uart->tx_thread, usb_uart_tx_thread); | ||||||
| 
 | 
 | ||||||
|     FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); |  | ||||||
|     usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); |     usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch); | ||||||
|     usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); |     usb_uart_serial_init(usb_uart, usb_uart->cfg.uart_ch); | ||||||
|     usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); |     usb_uart_set_baudrate(usb_uart, usb_uart->cfg.baudrate); | ||||||
| @ -247,11 +258,9 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|             usb_uart_update_ctrl_lines(usb_uart); |             usb_uart_update_ctrl_lines(usb_uart); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); |     usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch); | ||||||
|     usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); |     usb_uart_serial_deinit(usb_uart, usb_uart->cfg.uart_ch); | ||||||
|     furi_hal_usb_unlock(); | 
 | ||||||
|     furi_hal_usb_set_config(usb_mode_prev, NULL); |  | ||||||
|     if(usb_uart->cfg.flow_pins != 0) { |     if(usb_uart->cfg.flow_pins != 0) { | ||||||
|         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); |         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][0], GpioModeAnalog); | ||||||
|         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); |         furi_hal_gpio_init_simple(flow_pins[usb_uart->cfg.flow_pins - 1][1], GpioModeAnalog); | ||||||
| @ -265,6 +274,12 @@ static int32_t usb_uart_worker(void* context) { | |||||||
|     osMutexDelete(usb_uart->usb_mutex); |     osMutexDelete(usb_uart->usb_mutex); | ||||||
|     osSemaphoreDelete(usb_uart->tx_sem); |     osSemaphoreDelete(usb_uart->tx_sem); | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_usb_unlock(); | ||||||
|  |     furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||||
|  |     Cli* cli = furi_record_open("cli"); | ||||||
|  |     cli_session_open(cli, &cli_vcp); | ||||||
|  |     furi_record_close("cli"); | ||||||
|  | 
 | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -62,8 +62,8 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { | |||||||
|     size_t size_received = 0; |     size_t size_received = 0; | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|         size_received = furi_hal_vcp_rx_with_timeout(buffer, CLI_READ_BUFFER_SIZE, 50); |         size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50); | ||||||
|         if(!furi_hal_vcp_is_connected() || cli_rpc.session_close_request) { |         if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,6 +12,8 @@ struct SubGhzChatWorker { | |||||||
|     volatile bool worker_stoping; |     volatile bool worker_stoping; | ||||||
|     osMessageQueueId_t event_queue; |     osMessageQueueId_t event_queue; | ||||||
|     uint32_t last_time_rx_data; |     uint32_t last_time_rx_data; | ||||||
|  | 
 | ||||||
|  |     Cli* cli; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** Worker thread
 | /** Worker thread
 | ||||||
| @ -27,7 +29,7 @@ static int32_t subghz_chat_worker_thread(void* context) { | |||||||
|     event.event = SubGhzChatEventUserEntrance; |     event.event = SubGhzChatEventUserEntrance; | ||||||
|     osMessageQueuePut(instance->event_queue, &event, 0, 0); |     osMessageQueuePut(instance->event_queue, &event, 0, 0); | ||||||
|     while(instance->worker_running) { |     while(instance->worker_running) { | ||||||
|         if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1000) == 1) { |         if(cli_read_timeout(instance->cli, (uint8_t*)&c, 1, 1000) == 1) { | ||||||
|             event.event = SubGhzChatEventInputData; |             event.event = SubGhzChatEventInputData; | ||||||
|             event.c = c; |             event.c = c; | ||||||
|             osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); |             osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); | ||||||
| @ -52,9 +54,11 @@ static void subghz_chat_worker_update_rx_event_chat(void* context) { | |||||||
|     osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); |     osMessageQueuePut(instance->event_queue, &event, 0, osWaitForever); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SubGhzChatWorker* subghz_chat_worker_alloc() { | SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) { | ||||||
|     SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); |     SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker)); | ||||||
| 
 | 
 | ||||||
|  |     instance->cli = cli; | ||||||
|  | 
 | ||||||
|     instance->thread = furi_thread_alloc(); |     instance->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(instance->thread, "SubGhzChat"); |     furi_thread_set_name(instance->thread, "SubGhzChat"); | ||||||
|     furi_thread_set_stack_size(instance->thread, 2048); |     furi_thread_set_stack_size(instance->thread, 2048); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include "../subghz_i.h" | #include "../subghz_i.h" | ||||||
|  | #include <cli/cli.h> | ||||||
| 
 | 
 | ||||||
| typedef struct SubGhzChatWorker SubGhzChatWorker; | typedef struct SubGhzChatWorker SubGhzChatWorker; | ||||||
| 
 | 
 | ||||||
| @ -17,7 +18,7 @@ typedef struct { | |||||||
|     char c; |     char c; | ||||||
| } SubGhzChatEvent; | } SubGhzChatEvent; | ||||||
| 
 | 
 | ||||||
| SubGhzChatWorker* subghz_chat_worker_alloc(); | SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli); | ||||||
| void subghz_chat_worker_free(SubGhzChatWorker* instance); | void subghz_chat_worker_free(SubGhzChatWorker* instance); | ||||||
| bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); | bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency); | ||||||
| void subghz_chat_worker_stop(SubGhzChatWorker* instance); | void subghz_chat_worker_stop(SubGhzChatWorker* instance); | ||||||
|  | |||||||
| @ -525,7 +525,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(); |     SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli); | ||||||
|     if(!subghz_chat_worker_start(subghz_chat, frequency)) { |     if(!subghz_chat_worker_start(subghz_chat, frequency)) { | ||||||
|         printf("Startup error SubGhzChatWorker\r\n"); |         printf("Startup error SubGhzChatWorker\r\n"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| #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 <stm32wbxx_ll_pwr.h> | #include <stm32wbxx_ll_pwr.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | |||||||
| @ -482,6 +482,9 @@ void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context) | |||||||
|         if(callbacks[if_num]->state_callback != NULL) { |         if(callbacks[if_num]->state_callback != NULL) { | ||||||
|             if(connected == true) callbacks[if_num]->state_callback(cb_ctx[if_num], 1); |             if(connected == true) callbacks[if_num]->state_callback(cb_ctx[if_num], 1); | ||||||
|         } |         } | ||||||
|  |         if(callbacks[if_num]->ctrl_line_callback != NULL) { | ||||||
|  |             callbacks[if_num]->ctrl_line_callback(cb_ctx[if_num], cdc_ctrl_line_state[if_num]); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,7 +22,6 @@ template <unsigned int N> struct STOP_EXTERNING_ME {}; | |||||||
| #include "furi_hal_light.h" | #include "furi_hal_light.h" | ||||||
| #include "furi_hal_delay.h" | #include "furi_hal_delay.h" | ||||||
| #include "furi_hal_power.h" | #include "furi_hal_power.h" | ||||||
| #include "furi_hal_vcp.h" |  | ||||||
| #include "furi_hal_interrupt.h" | #include "furi_hal_interrupt.h" | ||||||
| #include "furi_hal_version.h" | #include "furi_hal_version.h" | ||||||
| #include "furi_hal_bt.h" | #include "furi_hal_bt.h" | ||||||
|  | |||||||
| @ -1,63 +0,0 @@ | |||||||
| /**
 |  | ||||||
|  * @file furi_hal_vcp.h |  | ||||||
|  * VCP HAL API |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <stdbool.h> |  | ||||||
| #include <stdint.h> |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" { |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| /** Init VCP HAL Allocates ring buffer and initializes state
 |  | ||||||
|  */ |  | ||||||
| void furi_hal_vcp_init(); |  | ||||||
| 
 |  | ||||||
| /** Disable VCP to make CDC interface usable by other application
 |  | ||||||
|  */ |  | ||||||
| void furi_hal_vcp_disable(); |  | ||||||
| 
 |  | ||||||
| /** Enable VCP
 |  | ||||||
|  */ |  | ||||||
| void furi_hal_vcp_enable(); |  | ||||||
| 
 |  | ||||||
| /** Recieve data from VCP Waits till some data arrives, never returns 0
 |  | ||||||
|  * |  | ||||||
|  * @param      buffer  pointer to buffer |  | ||||||
|  * @param      size    buffer size |  | ||||||
|  * |  | ||||||
|  * @return     items copied in buffer, 0 if channel closed |  | ||||||
|  */ |  | ||||||
| size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size); |  | ||||||
| 
 |  | ||||||
| /** Recieve data from VCP with timeout Waits till some data arrives during
 |  | ||||||
|  * timeout |  | ||||||
|  * |  | ||||||
|  * @param      buffer   pointer to buffer |  | ||||||
|  * @param      size     buffer size |  | ||||||
|  * @param      timeout  rx timeout in ms |  | ||||||
|  * |  | ||||||
|  * @return     items copied in buffer, 0 if channel closed or timeout occurs |  | ||||||
|  */ |  | ||||||
| size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout); |  | ||||||
| 
 |  | ||||||
| /** Transmit data to VCP
 |  | ||||||
|  * |  | ||||||
|  * @param      buffer  pointer to buffer |  | ||||||
|  * @param      size    buffer size |  | ||||||
|  */ |  | ||||||
| void furi_hal_vcp_tx(const uint8_t* buffer, size_t size); |  | ||||||
| 
 |  | ||||||
| /** Check whether VCP is connected
 |  | ||||||
|  * |  | ||||||
|  * @return     true if connected |  | ||||||
|  */ |  | ||||||
| bool furi_hal_vcp_is_connected(void); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nikolay Minaylov
						Nikolay Minaylov