[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:
Nikolay Minaylov 2022-04-29 15:02:17 +03:00 committed by GitHub
parent 76221021ec
commit 0eac917f91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 348 additions and 243 deletions

View File

@ -1,9 +1,11 @@
#include "cli_i.h"
#include "cli_commands.h"
#include "cli_vcp.h"
#include <furi_hal_version.h>
#include <loader/loader.h>
#define TAG "CliSrv"
Cli* cli_alloc() {
Cli* cli = malloc(sizeof(Cli));
@ -12,55 +14,78 @@ Cli* cli_alloc() {
string_init(cli->last_line);
string_init(cli->line);
cli->session = NULL;
cli->mutex = osMutexNew(NULL);
furi_check(cli->mutex);
cli->idle_sem = osSemaphoreNew(1, 0, NULL);
return cli;
}
void cli_free(Cli* cli) {
void cli_putc(Cli* cli, char c) {
furi_assert(cli);
string_clear(cli->last_line);
string_clear(cli->line);
CliCommandTree_clear(cli->commands);
free(cli);
}
void cli_putc(char c) {
furi_hal_vcp_tx((uint8_t*)&c, 1);
if(cli->session != NULL) {
cli->session->tx((uint8_t*)&c, 1);
}
}
char cli_getc(Cli* cli) {
furi_assert(cli);
char c;
if(furi_hal_vcp_rx((uint8_t*)&c, 1) == 0) {
char c = 0;
if(cli->session != NULL) {
if(cli->session->rx((uint8_t*)&c, 1, osWaitForever) == 0) {
cli_reset(cli);
}
} else {
cli_reset(cli);
}
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) {
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) {
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) {
furi_assert(cli);
char c = '\0';
if(furi_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 0) == 1) {
return c == CliSymbolAsciiETX;
} else {
return false;
if(cli->session != NULL) {
if(cli->session->rx((uint8_t*)&c, 1, 0) == 1) {
return c == CliSymbolAsciiETX;
}
}
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) {
@ -139,7 +164,7 @@ static void cli_handle_backspace(Cli* cli) {
cli->cursor_position--;
} else {
cli_putc(CliSymbolAsciiBell);
cli_putc(cli, CliSymbolAsciiBell);
}
}
@ -210,7 +235,7 @@ static void cli_handle_enter(Cli* cli) {
printf(
"`%s` command not found, use `help` or `?` to list all available commands",
string_get_cstr(command));
cli_putc(CliSymbolAsciiBell);
cli_putc(cli, CliSymbolAsciiBell);
}
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) {
char c = cli_getc(cli);
size_t r;
char in_chr = cli_getc(cli);
size_t rx_len;
if(c == CliSymbolAsciiTab) {
if(in_chr == CliSymbolAsciiTab) {
cli_handle_autocomplete(cli);
} else if(c == CliSymbolAsciiSOH) {
} else if(in_chr == CliSymbolAsciiSOH) {
osDelay(33); // We are too fast, Minicom is not ready yet
cli_motd();
cli_prompt(cli);
} else if(c == CliSymbolAsciiETX) {
} else if(in_chr == CliSymbolAsciiETX) {
cli_reset(cli);
cli_prompt(cli);
} else if(c == CliSymbolAsciiEOT) {
} else if(in_chr == CliSymbolAsciiEOT) {
cli_reset(cli);
} else if(c == CliSymbolAsciiEsc) {
r = furi_hal_vcp_rx((uint8_t*)&c, 1);
if(r && c == '[') {
furi_hal_vcp_rx((uint8_t*)&c, 1);
cli_handle_escape(cli, c);
} else if(in_chr == CliSymbolAsciiEsc) {
rx_len = cli_read(cli, (uint8_t*)&in_chr, 1);
if((rx_len > 0) && (in_chr == '[')) {
cli_read(cli, (uint8_t*)&in_chr, 1);
cli_handle_escape(cli, in_chr);
} 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);
} else if(c == CliSymbolAsciiCR) {
} else if(in_chr == CliSymbolAsciiCR) {
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)) {
string_push_back(cli->line, c);
cli_putc(c);
string_push_back(cli->line, in_chr);
cli_putc(cli, in_chr);
} else {
// ToDo: better way?
string_t temp;
string_init(temp);
string_reserve(temp, string_size(cli->line) + 1);
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);
// 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
// Print character in replace mode
printf("\e[4h%c\e[4l", c);
printf("\e[4h%c\e[4l", in_chr);
fflush(stdout);
}
cli->cursor_position++;
} 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);
}
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) {
Cli* cli = cli_alloc();
furi_hal_vcp_init();
// Init basic cli commands
cli_commands_init(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) {
cli_process_input(cli);
if(cli->session != NULL) {
cli_process_input(cli);
} else {
furi_check(osSemaphoreAcquire(cli->idle_sem, osWaitForever) == osOK);
}
}
return 0;

View File

@ -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);
/** Read from terminal Do it only from inside of cli call.
/** Read from terminal
*
* @param cli Cli instance
* @param buffer pointer to buffer
* @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);
/** 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
*
@ -111,6 +122,12 @@ char cli_getc(Cli* cli);
*/
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
}
#endif

View File

@ -18,6 +18,17 @@ typedef struct {
uint32_t flags;
} 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(
CliCommandTree,
CLI_COMMANDS_TREE_RANK,
@ -31,18 +42,18 @@ BPTREE_DEF2(
struct Cli {
CliCommandTree_t commands;
osMutexId_t mutex;
osSemaphoreId_t idle_sem;
string_t last_line;
string_t line;
CliSession* session;
size_t cursor_position;
};
Cli* cli_alloc();
void cli_free(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);

View File

@ -2,8 +2,9 @@
#include <furi_hal.h>
#include <furi.h>
#include <stream_buffer.h>
#include "cli_i.h"
#define TAG "FuriHalVcp"
#define TAG "CliVcp"
#define USB_CDC_PKT_LEN CDC_DATA_SZ
#define VCP_RX_BUF_SIZE (USB_CDC_PKT_LEN * 3)
@ -12,19 +13,18 @@
#define VCP_IF_NUM 0
typedef enum {
VcpEvtEnable = (1 << 0),
VcpEvtDisable = (1 << 1),
VcpEvtConnect = (1 << 2),
VcpEvtDisconnect = (1 << 3),
VcpEvtStreamRx = (1 << 4),
VcpEvtRx = (1 << 5),
VcpEvtStreamTx = (1 << 6),
VcpEvtTx = (1 << 7),
VcpEvtStop = (1 << 0),
VcpEvtConnect = (1 << 1),
VcpEvtDisconnect = (1 << 2),
VcpEvtStreamRx = (1 << 3),
VcpEvtRx = (1 << 4),
VcpEvtStreamTx = (1 << 5),
VcpEvtTx = (1 << 6),
} WorkerEvtFlags;
#define VCP_THREAD_FLAG_ALL \
(VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | \
VcpEvtStreamRx | VcpEvtStreamTx)
#define VCP_THREAD_FLAG_ALL \
(VcpEvtStop | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | \
VcpEvtStreamTx)
typedef struct {
FuriThread* thread;
@ -33,9 +33,12 @@ typedef struct {
StreamBufferHandle_t rx_stream;
volatile bool connected;
volatile bool running;
FuriHalUsbInterface* usb_if_prev;
uint8_t data_buffer[USB_CDC_PKT_LEN];
} FuriHalVcp;
} CliVcp;
static int32_t vcp_worker(void* context);
static void vcp_on_cdc_tx_complete(void* context);
@ -51,25 +54,23 @@ static CdcCallbacks cdc_cb = {
NULL,
};
static FuriHalVcp* vcp = NULL;
static CliVcp* vcp = NULL;
static const uint8_t ascii_soh = 0x01;
static const uint8_t ascii_eot = 0x04;
void furi_hal_vcp_init() {
vcp = malloc(sizeof(FuriHalVcp));
static void cli_vcp_init() {
if(vcp == NULL) {
vcp = malloc(sizeof(CliVcp));
vcp->tx_stream = xStreamBufferCreate(VCP_TX_BUF_SIZE, 1);
vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1);
}
furi_assert(vcp->thread == NULL);
vcp->connected = false;
vcp->tx_stream = xStreamBufferCreate(VCP_TX_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;
}
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_callback(vcp->thread, vcp_worker);
furi_thread_start(vcp->thread);
@ -77,48 +78,35 @@ void furi_hal_vcp_init() {
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) {
bool enabled = true;
bool tx_idle = false;
bool tx_idle = true;
size_t missed_rx = 0;
uint8_t last_tx_pkt_len = 0;
furi_hal_usb_set_config(&usb_cdc_single, NULL);
// 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_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL);
FURI_LOG_D(TAG, "Start");
vcp->running = true;
while(1) {
uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever);
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
if((flags & VcpEvtConnect) && enabled) {
#ifdef FURI_HAL_USB_VCP_DEBUG
if(flags & VcpEvtConnect) {
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "Connect");
#endif
if(vcp->connected == false) {
@ -128,8 +116,8 @@ static int32_t vcp_worker(void* context) {
}
// VCP session closed
if((flags & VcpEvtDisconnect) && enabled) {
#ifdef FURI_HAL_USB_VCP_DEBUG
if(flags & VcpEvtDisconnect) {
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "Disconnect");
#endif
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?
if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) {
#ifdef FURI_HAL_USB_VCP_DEBUG
if((flags & VcpEvtStreamRx) && (missed_rx > 0)) {
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "StreamRx");
#endif
if(xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) {
@ -151,10 +139,10 @@ static int32_t vcp_worker(void* context) {
}
// New data received
if((flags & VcpEvtRx)) {
if(flags & VcpEvtRx) {
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);
#ifdef FURI_HAL_USB_VCP_DEBUG
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "Rx %d", len);
#endif
if(len > 0) {
@ -163,7 +151,7 @@ static int32_t vcp_worker(void* context) {
len);
}
} else {
#ifdef FURI_HAL_USB_VCP_DEBUG
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "Rx missed");
#endif
missed_rx++;
@ -171,8 +159,8 @@ static int32_t vcp_worker(void* context) {
}
// New data in Tx buffer
if((flags & VcpEvtStreamTx) && enabled) {
#ifdef FURI_HAL_USB_VCP_DEBUG
if(flags & VcpEvtStreamTx) {
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "StreamTx");
#endif
if(tx_idle) {
@ -181,10 +169,10 @@ static int32_t vcp_worker(void* context) {
}
// CDC write transfer done
if((flags & VcpEvtTx) && enabled) {
if(flags & VcpEvtTx) {
size_t len =
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);
#endif
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;
}
}
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;
}
void furi_hal_vcp_enable() {
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) {
static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) {
furi_assert(vcp);
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);
#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;
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);
#endif
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;
}
#ifdef FURI_HAL_USB_VCP_DEBUG
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "rx %u end", size);
#endif
return rx_cnt;
}
size_t furi_hal_vcp_rx(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) {
static void cli_vcp_tx(const uint8_t* buffer, size_t size) {
furi_assert(vcp);
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);
#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);
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);
#endif
@ -272,11 +269,15 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
buffer += batch_size;
}
#ifdef FURI_HAL_USB_VCP_DEBUG
#ifdef CLI_VCP_DEBUG
FURI_LOG_D(TAG, "tx %u end", size);
#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) {
if(state == 0) {
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);
}
bool furi_hal_vcp_is_connected(void) {
static bool cli_vcp_is_connected(void) {
furi_assert(vcp);
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,
};

View 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

View File

@ -5,7 +5,7 @@
#include <stdbool.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_MAGIC (0x17)
#define PIN_MAX_LENGTH 12
@ -39,5 +39,6 @@ typedef struct {
typedef struct {
uint16_t favorite;
PinCode pin_code;
uint8_t is_locked;
uint32_t auto_lock_delay_ms;
} DesktopSettings;

View File

@ -8,6 +8,7 @@
#include "../helpers/pin_lock.h"
#include "../desktop_i.h"
#include <cli/cli_vcp.h>
static const NotificationSequence sequence_pin_fail = {
&message_display_on,
@ -61,26 +62,50 @@ uint32_t desktop_pin_lock_get_fail_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_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_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) {
furi_assert(settings);
if(settings->pin_code.length > 0) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
furi_hal_usb_disable();
if(settings->is_locked == 1) {
furi_hal_rtc_set_flag(FuriHalRtcFlagLock);
} else {
if(desktop_pin_lock_is_locked()) {
settings->is_locked = 1;
SAVE_DESKTOP_SETTINGS(settings);
}
}
} else {
furi_hal_rtc_set_pin_fails(0);
furi_hal_rtc_reset_flag(FuriHalRtcFlagLock);
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) {

View File

@ -8,9 +8,9 @@ void desktop_pin_lock_error_notify();
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();

View File

@ -54,7 +54,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
break;
case DesktopLockMenuEventPinLock:
if(desktop->settings.pin_code.length > 0) {
desktop_pin_lock();
desktop_pin_lock(&desktop->settings);
desktop_lock(desktop);
} else {
LoaderStatus status =

View File

@ -126,7 +126,7 @@ bool desktop_scene_pin_input_on_event(void* context, SceneManagerEvent event) {
consumed = true;
break;
case DesktopPinInputEventUnlocked:
desktop_pin_unlock();
desktop_pin_unlock(&desktop->settings);
desktop_unlock(desktop);
consumed = true;
break;

View File

@ -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];
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) {
deed_weight = MIN(xp_to_levelup, deed_weight);
dolphin_state->data.icounter += deed_weight;

View File

@ -3,6 +3,8 @@
#include <stream_buffer.h>
#include <furi_hal_usb_cdc_i.h>
#include "usb_cdc.h"
#include "cli/cli_vcp.h"
#include "cli/cli.h"
#define USB_CDC_PKT_LEN CDC_DATA_SZ
#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
@ -16,17 +18,16 @@ static const GpioPin* flow_pins[][2] = {
};
typedef enum {
WorkerEvtReserved = (1 << 0), // Reserved for StreamBuffer internal event
WorkerEvtStop = (1 << 1),
WorkerEvtRxDone = (1 << 2),
WorkerEvtStop = (1 << 0),
WorkerEvtRxDone = (1 << 1),
WorkerEvtTxStop = (1 << 3),
WorkerEvtCdcRx = (1 << 4),
WorkerEvtTxStop = (1 << 2),
WorkerEvtCdcRx = (1 << 3),
WorkerEvtCfgChange = (1 << 5),
WorkerEvtCfgChange = (1 << 4),
WorkerEvtLineCfgSet = (1 << 6),
WorkerEvtCtrlLineSet = (1 << 7),
WorkerEvtLineCfgSet = (1 << 5),
WorkerEvtCtrlLineSet = (1 << 6),
} 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) {
furi_hal_usb_unlock();
FURI_LOG_I("", "Init %d", vcp_ch);
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_hal_vcp_disable();
} else {
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);
}
static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) {
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) {
@ -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_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_serial_init(usb_uart, usb_uart->cfg.uart_ch);
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_vcp_deinit(usb_uart, usb_uart->cfg.vcp_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) {
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);
@ -265,6 +274,12 @@ static int32_t usb_uart_worker(void* context) {
osMutexDelete(usb_uart->usb_mutex);
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;
}

View File

@ -62,8 +62,8 @@ void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) {
size_t size_received = 0;
while(1) {
size_received = furi_hal_vcp_rx_with_timeout(buffer, CLI_READ_BUFFER_SIZE, 50);
if(!furi_hal_vcp_is_connected() || cli_rpc.session_close_request) {
size_received = cli_read_timeout(cli_rpc.cli, buffer, CLI_READ_BUFFER_SIZE, 50);
if(!cli_is_connected(cli_rpc.cli) || cli_rpc.session_close_request) {
break;
}

View File

@ -12,6 +12,8 @@ struct SubGhzChatWorker {
volatile bool worker_stoping;
osMessageQueueId_t event_queue;
uint32_t last_time_rx_data;
Cli* cli;
};
/** Worker thread
@ -27,7 +29,7 @@ static int32_t subghz_chat_worker_thread(void* context) {
event.event = SubGhzChatEventUserEntrance;
osMessageQueuePut(instance->event_queue, &event, 0, 0);
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.c = c;
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);
}
SubGhzChatWorker* subghz_chat_worker_alloc() {
SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli) {
SubGhzChatWorker* instance = malloc(sizeof(SubGhzChatWorker));
instance->cli = cli;
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "SubGhzChat");
furi_thread_set_stack_size(instance->thread, 2048);

View File

@ -1,5 +1,6 @@
#pragma once
#include "../subghz_i.h"
#include <cli/cli.h>
typedef struct SubGhzChatWorker SubGhzChatWorker;
@ -17,7 +18,7 @@ typedef struct {
char c;
} SubGhzChatEvent;
SubGhzChatWorker* subghz_chat_worker_alloc();
SubGhzChatWorker* subghz_chat_worker_alloc(Cli* cli);
void subghz_chat_worker_free(SubGhzChatWorker* instance);
bool subghz_chat_worker_start(SubGhzChatWorker* instance, uint32_t frequency);
void subghz_chat_worker_stop(SubGhzChatWorker* instance);

View File

@ -525,7 +525,7 @@ static void subghz_cli_command_chat(Cli* cli, string_t args) {
return;
}
SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc();
SubGhzChatWorker* subghz_chat = subghz_chat_worker_alloc(cli);
if(!subghz_chat_worker_start(subghz_chat, frequency)) {
printf("Startup error SubGhzChatWorker\r\n");

View File

@ -1,7 +1,6 @@
#include "furi_hal_version.h"
#include "furi_hal_usb_i.h"
#include "furi_hal_usb.h"
#include "furi_hal_vcp.h"
#include <furi_hal_power.h>
#include <stm32wbxx_ll_pwr.h>
#include <furi.h>

View File

@ -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(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]);
}
}
}

View File

@ -22,7 +22,6 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
#include "furi_hal_light.h"
#include "furi_hal_delay.h"
#include "furi_hal_power.h"
#include "furi_hal_vcp.h"
#include "furi_hal_interrupt.h"
#include "furi_hal_version.h"
#include "furi_hal_bt.h"

View File

@ -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