From f8d3cc251c1d8d8a56c50208de78396c80ec6de1 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 4 Nov 2021 22:33:28 +0300 Subject: [PATCH] [FL-1984, FL-2004, FL-2010] USB CDC Fixes (#801) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FL-1984, FL-2004] USB-UART Fixes, test with high timer task priority * added forgotten file * switch from EventFlags to ThreadFlags * [FL-1984, FL-2010] USB-UART and furi-hal-vcp rework * Scripts: modernize string formatting. Co-authored-by: あく --- applications/gpio/scenes/gpio_scene_start.c | 5 + .../gpio/scenes/gpio_scene_usb_uart.c | 7 + applications/gpio/usb_uart_bridge.c | 125 ++++------- applications/gui/modules/variable-item-list.c | 36 +++- applications/gui/modules/variable-item-list.h | 4 + .../targets/f6/furi-hal/furi-hal-console.h | 6 - firmware/targets/f6/furi-hal/furi-hal-uart.h | 6 +- .../targets/f6/furi-hal/furi-hal-usb-cdc.c | 16 +- firmware/targets/f6/furi-hal/furi-hal-vcp.c | 203 +++++++++++------- .../targets/f7/furi-hal/furi-hal-console.h | 6 - firmware/targets/f7/furi-hal/furi-hal-uart.c | 4 + firmware/targets/f7/furi-hal/furi-hal-uart.h | 6 +- .../targets/f7/furi-hal/furi-hal-usb-cdc.c | 16 +- firmware/targets/f7/furi-hal/furi-hal-vcp.c | 203 +++++++++++------- scripts/flipper/storage.py | 17 +- 15 files changed, 385 insertions(+), 275 deletions(-) mode change 100755 => 100644 applications/gui/modules/variable-item-list.c diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/gpio/scenes/gpio_scene_start.c index b7d94fe7..df210882 100644 --- a/applications/gpio/scenes/gpio_scene_start.c +++ b/applications/gpio/scenes/gpio_scene_start.c @@ -67,6 +67,9 @@ void gpio_scene_start_on_enter(void* context) { variable_item_list_add(var_item_list, "GPIO tester", 0, NULL, NULL); variable_item_list_add(var_item_list, "USB-UART bridge", 0, NULL, NULL); + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneStart)); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList); } @@ -80,8 +83,10 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF) { furi_hal_power_disable_otg(); } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, 1); scene_manager_next_scene(app->scene_manager, GpioSceneTest); } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_USB_UART) { + scene_manager_set_scene_state(app->scene_manager, GpioSceneStart, 2); scene_manager_next_scene(app->scene_manager, GpioSceneUsbUart); } consumed = true; diff --git a/applications/gpio/scenes/gpio_scene_usb_uart.c b/applications/gpio/scenes/gpio_scene_usb_uart.c index c8ec0141..b24e4b3e 100644 --- a/applications/gpio/scenes/gpio_scene_usb_uart.c +++ b/applications/gpio/scenes/gpio_scene_usb_uart.c @@ -120,12 +120,19 @@ void gpio_scene_usb_uart_on_enter(void* context) { item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL); item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL); + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, GpioSceneUsbUart)); + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); } void gpio_scene_usb_uart_on_exit(void* context) { GpioApp* app = context; usb_uart_disable(); + scene_manager_set_scene_state( + app->scene_manager, + GpioSceneUsbUart, + variable_item_list_get_selected_item_index(app->var_item_list)); variable_item_list_clean(app->var_item_list); free(cfg_set); } diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index b50cc158..ff787ea7 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -4,23 +4,19 @@ #include #include "usb_cdc.h" -#define USB_PKT_LEN CDC_DATA_SZ -#define USB_UART_RX_BUF_SIZE (USB_PKT_LEN * 3) -#define USB_UART_TX_BUF_SIZE (USB_PKT_LEN * 3) +#define USB_CDC_PKT_LEN CDC_DATA_SZ +#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) typedef enum { WorkerEvtStop = (1 << 0), - WorkerEvtRxReady = (1 << 1), + WorkerEvtRxDone = (1 << 1), WorkerEvtTxStop = (1 << 2), - WorkerEvtTxReady = (1 << 3), - - WorkerEvtSof = (1 << 4), - + WorkerEvtCdcRx = (1 << 3), } WorkerEvtFlags; -#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxReady) -#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtTxReady) +#define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone) +#define WORKER_ALL_TX_EVENTS (WorkerEvtTxStop | WorkerEvtCdcRx) typedef struct { UsbUartConfig cfg; @@ -28,13 +24,13 @@ typedef struct { FuriThread* thread; FuriThread* tx_thread; - osEventFlagsId_t events; - StreamBufferHandle_t rx_stream; - StreamBufferHandle_t tx_stream; - uint8_t rx_buf[USB_PKT_LEN]; - uint8_t tx_buf[USB_PKT_LEN]; + osMutexId_t usb_mutex; + + osSemaphoreId_t tx_sem; + + uint8_t rx_buf[USB_CDC_PKT_LEN]; bool buf_full; } UsbUartParams; @@ -65,21 +61,18 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) { if(ev == UartIrqEventRXNE) { xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); - - size_t ret = xStreamBufferBytesAvailable(usb_uart->rx_stream); - if(ret > USB_PKT_LEN) osEventFlagsSet(usb_uart->events, WorkerEvtRxReady); - } else if(ev == UartIrqEventIDLE) { - osEventFlagsSet(usb_uart->events, WorkerEvtRxReady); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtRxDone); } - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } static int32_t usb_uart_worker(void* context) { memcpy(&usb_uart->cfg, context, sizeof(UsbUartConfig)); usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1); - usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1); + + usb_uart->tx_sem = osSemaphoreNew(1, 1, NULL); + usb_uart->usb_mutex = osMutexNew(NULL); usb_uart->tx_thread = furi_thread_alloc(); furi_thread_set_name(usb_uart->tx_thread, "usb_uart_tx"); @@ -91,10 +84,10 @@ static int32_t usb_uart_worker(void* context) { if(usb_uart->cfg.vcp_ch == 0) { furi_hal_usb_set_config(UsbModeVcpSingle); furi_hal_vcp_disable(); - osEventFlagsSet(usb_uart->events, WorkerEvtSof); } else { furi_hal_usb_set_config(UsbModeVcpDual); } + osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx); if(usb_uart->cfg.uart_ch == FuriHalUartIdUSART1) { furi_hal_console_disable(); @@ -114,26 +107,25 @@ static int32_t usb_uart_worker(void* context) { furi_thread_start(usb_uart->tx_thread); while(1) { - uint32_t events = osEventFlagsWait( - usb_uart->events, WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever); + uint32_t events = osThreadFlagsWait(WORKER_ALL_RX_EVENTS, osFlagsWaitAny, osWaitForever); furi_check((events & osFlagsError) == 0); if(events & WorkerEvtStop) break; - if(events & WorkerEvtRxReady) { - size_t len = 0; - do { - len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0); - if(len > 0) { - if((osEventFlagsWait(usb_uart->events, WorkerEvtSof, osFlagsWaitAny, 100) & - osFlagsError) == 0) - furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len); - else - xStreamBufferReset(usb_uart->rx_stream); + if(events & WorkerEvtRxDone) { + size_t len = + xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_CDC_PKT_LEN, 0); + if(len > 0) { + if(osSemaphoreAcquire(usb_uart->tx_sem, 100) == osOK) { + furi_check(osMutexAcquire(usb_uart->usb_mutex, osWaitForever) == osOK); + furi_hal_cdc_send(usb_uart->cfg.vcp_ch, usb_uart->rx_buf, len); + furi_check(osMutexRelease(usb_uart->usb_mutex) == osOK); + } else { + xStreamBufferReset(usb_uart->rx_stream); } - } while(len > 0); + } } } - osEventFlagsSet(usb_uart->events, WorkerEvtTxStop); + osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtTxStop); furi_thread_join(usb_uart->tx_thread); furi_thread_free(usb_uart->tx_thread); @@ -147,35 +139,26 @@ static int32_t usb_uart_worker(void* context) { if(usb_uart->cfg.vcp_ch == 0) furi_hal_vcp_enable(); vStreamBufferDelete(usb_uart->rx_stream); - vStreamBufferDelete(usb_uart->tx_stream); + osMutexDelete(usb_uart->usb_mutex); + osSemaphoreDelete(usb_uart->tx_sem); return 0; } static int32_t usb_uart_tx_thread(void* context) { - uint8_t data[USB_PKT_LEN]; + uint8_t data[USB_CDC_PKT_LEN]; while(1) { - uint32_t events = osEventFlagsWait( - usb_uart->events, WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever); + uint32_t events = osThreadFlagsWait(WORKER_ALL_TX_EVENTS, osFlagsWaitAny, osWaitForever); furi_check((events & osFlagsError) == 0); if(events & WorkerEvtTxStop) break; - if(events & WorkerEvtTxReady) { - size_t len = 0; - do { - len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, 0); - if(len > 0) { - furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, len); - } - if((usb_uart->buf_full == true) && - (xStreamBufferBytesAvailable(usb_uart->tx_stream) == 0)) { - // Stream buffer was overflown, but now is free. Reading USB buffer to resume USB transfers - usb_uart->buf_full = false; - int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_PKT_LEN); - if(size > 0) { - furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size); - } - } - } while(len > 0); + if(events & WorkerEvtCdcRx) { + furi_check(osMutexAcquire(usb_uart->usb_mutex, osWaitForever) == osOK); + int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, data, USB_CDC_PKT_LEN); + furi_check(osMutexRelease(usb_uart->usb_mutex) == osOK); + + if(size > 0) { + furi_hal_uart_tx(usb_uart->cfg.uart_ch, data, size); + } } } return 0; @@ -184,26 +167,11 @@ static int32_t usb_uart_tx_thread(void* context) { /* VCP callbacks */ static void vcp_on_cdc_tx_complete() { - osEventFlagsSet(usb_uart->events, WorkerEvtSof); + osSemaphoreRelease(usb_uart->tx_sem); } static void vcp_on_cdc_rx() { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream); - if(max_len >= USB_PKT_LEN) { - //if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN; - int32_t size = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, usb_uart->tx_buf, USB_PKT_LEN); - if(size > 0) { - size_t ret = xStreamBufferSendFromISR( - usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken); - furi_check(ret == size); - } - } else { - usb_uart->buf_full = true; - } - osEventFlagsSet(usb_uart->events, WorkerEvtTxReady); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->tx_thread), WorkerEvtCdcRx); } static void vcp_state_callback(uint8_t state) { @@ -228,18 +196,15 @@ void usb_uart_enable(UsbUartConfig* cfg) { furi_thread_set_context(usb_uart->thread, cfg); furi_thread_set_callback(usb_uart->thread, usb_uart_worker); - usb_uart->events = osEventFlagsNew(NULL); - furi_thread_start(usb_uart->thread); } } void usb_uart_disable() { if(running == true) { - osEventFlagsSet(usb_uart->events, WorkerEvtStop); + osThreadFlagsSet(furi_thread_get_thread_id(usb_uart->thread), WorkerEvtStop); furi_thread_join(usb_uart->thread); furi_thread_free(usb_uart->thread); - osEventFlagsDelete(usb_uart->events); free(usb_uart); running = false; } diff --git a/applications/gui/modules/variable-item-list.c b/applications/gui/modules/variable-item-list.c old mode 100755 new mode 100644 index 800e0602..03f04cb6 --- a/applications/gui/modules/variable-item-list.c +++ b/applications/gui/modules/variable-item-list.c @@ -84,6 +84,40 @@ static void variable_item_list_draw_callback(Canvas* canvas, void* _model) { elements_scrollbar(canvas, model->position, VariableItemArray_size(model->items)); } +void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index) { + with_view_model( + variable_item_list->view, (VariableItemListModel * model) { + uint8_t position = index; + if(position >= VariableItemArray_size(model->items)) { + position = 0; + } + + model->position = position; + model->window_position = position; + + if(model->window_position > 0) { + model->window_position -= 1; + } + + if(VariableItemArray_size(model->items) <= 4) { + model->window_position = 0; + } else { + if(model->window_position >= (VariableItemArray_size(model->items) - 4)) { + model->window_position = (VariableItemArray_size(model->items) - 4); + } + } + + return true; + }); +} + +uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list) { + VariableItemListModel* model = view_get_model(variable_item_list->view); + uint8_t idx = model->position; + view_commit_model(variable_item_list->view, false); + return idx; +} + static bool variable_item_list_input_callback(InputEvent* event, void* context) { VariableItemList* variable_item_list = context; furi_assert(variable_item_list); @@ -323,4 +357,4 @@ uint8_t variable_item_get_current_value_index(VariableItem* item) { void* variable_item_get_context(VariableItem* item) { return item->context; -} \ No newline at end of file +} diff --git a/applications/gui/modules/variable-item-list.h b/applications/gui/modules/variable-item-list.h index e1e3cbf7..b67a02e6 100755 --- a/applications/gui/modules/variable-item-list.h +++ b/applications/gui/modules/variable-item-list.h @@ -70,6 +70,10 @@ void variable_item_list_set_enter_callback( VariableItemListEnterCallback callback, void* context); +void variable_item_list_set_selected_item(VariableItemList* variable_item_list, uint8_t index); + +uint8_t variable_item_list_get_selected_item_index(VariableItemList* variable_item_list); + /** Set item current selected index * * @param item VariableItem* instance diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.h b/firmware/targets/f6/furi-hal/furi-hal-console.h index 4c10d81e..637c17f6 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.h +++ b/firmware/targets/f6/furi-hal/furi-hal-console.h @@ -7,12 +7,6 @@ extern "C" { #endif -typedef enum { - UartIrqEventRXNE, - UartIrqEventIDLE, - //TODO: more events -} UartIrqEvent; - void furi_hal_console_init(); void furi_hal_console_enable(); diff --git a/firmware/targets/f6/furi-hal/furi-hal-uart.h b/firmware/targets/f6/furi-hal/furi-hal-uart.h index 6be156b7..731a12a2 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-uart.h +++ b/firmware/targets/f6/furi-hal/furi-hal-uart.h @@ -2,7 +2,6 @@ #include #include -#include "furi-hal-console.h" #ifdef __cplusplus extern "C" { @@ -13,6 +12,11 @@ typedef enum { FuriHalUartIdLPUART1, } FuriHalUartId; +typedef enum { + UartIrqEventRXNE, + UartIrqEventIDLE, + //TODO: more events +} UartIrqEvent; void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud); diff --git a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c index 9386b100..dd82cf16 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c +++ b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c @@ -7,12 +7,12 @@ #include "usb_cdc.h" #define CDC0_RXD_EP 0x01 -#define CDC0_TXD_EP 0x81 -#define CDC0_NTF_EP 0x82 +#define CDC0_TXD_EP 0x82 +#define CDC0_NTF_EP 0x83 -#define CDC1_RXD_EP 0x03 -#define CDC1_TXD_EP 0x83 -#define CDC1_NTF_EP 0x84 +#define CDC1_RXD_EP 0x04 +#define CDC1_TXD_EP 0x85 +#define CDC1_NTF_EP 0x86 #define CDC_NTF_SZ 0x08 @@ -446,10 +446,12 @@ void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) { } int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) { + int32_t len = 0; if (if_num == 0) - return usbd_ep_read(usb_dev, CDC0_RXD_EP, buf, max_len); + len = usbd_ep_read(usb_dev, CDC0_RXD_EP, buf, max_len); else - return usbd_ep_read(usb_dev, CDC1_RXD_EP, buf, max_len); + len = usbd_ep_read(usb_dev, CDC1_RXD_EP, buf, max_len); + return ((len < 0) ? 0 : len); } static void cdc_on_wakeup(usbd_device *dev) { diff --git a/firmware/targets/f6/furi-hal/furi-hal-vcp.c b/firmware/targets/f6/furi-hal/furi-hal-vcp.c index b2f17842..b0b47f46 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-vcp.c +++ b/firmware/targets/f6/furi-hal/furi-hal-vcp.c @@ -3,18 +3,28 @@ #include #include -#define APP_RX_DATA_SIZE CDC_DATA_SZ -#define APP_TX_DATA_SIZE CDC_DATA_SZ -#define FURI_HAL_VCP_RX_BUFFER_SIZE (APP_RX_DATA_SIZE * 16) +#define USB_CDC_PKT_LEN CDC_DATA_SZ #define VCP_IF_NUM 0 +typedef enum { + VcpConnect, + VcpDisconnect, +} VcpEvent; + typedef struct { volatile bool connected; - StreamBufferHandle_t rx_stream; - volatile bool rx_stream_full; + uint8_t rx_buf[USB_CDC_PKT_LEN]; + uint8_t rx_buf_start; + uint8_t rx_buf_len; + + osMessageQueueId_t event_queue; + + osMutexId_t usb_mutex; + + osSemaphoreId_t tx_sem; + osSemaphoreId_t rx_sem; - osSemaphoreId_t tx_semaphore; } FuriHalVcp; static void vcp_on_cdc_tx_complete(); @@ -30,22 +40,21 @@ static CdcCallbacks cdc_cb = { NULL, }; -static FuriHalVcp* furi_hal_vcp = NULL; +static FuriHalVcp* vcp = NULL; static const uint8_t ascii_soh = 0x01; static const uint8_t ascii_eot = 0x04; -static uint8_t* vcp_rx_buf; - void furi_hal_vcp_init() { - furi_hal_vcp = furi_alloc(sizeof(FuriHalVcp)); - vcp_rx_buf = furi_alloc(APP_RX_DATA_SIZE); - furi_hal_vcp->connected = false; - - furi_hal_vcp->rx_stream = xStreamBufferCreate(FURI_HAL_VCP_RX_BUFFER_SIZE, 1); - furi_hal_vcp->rx_stream_full = false; + vcp = furi_alloc(sizeof(FuriHalVcp)); + vcp->connected = false; - furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL); + vcp->usb_mutex = osMutexNew(NULL); + + vcp->tx_sem = osSemaphoreNew(1, 1, NULL); + vcp->rx_sem = osSemaphoreNew(1, 0, NULL); + + vcp->event_queue = osMessageQueueNew(8, sizeof(VcpEvent), NULL); furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); @@ -54,111 +63,149 @@ void furi_hal_vcp_init() { void furi_hal_vcp_enable() { furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); - furi_hal_vcp->connected = true; + VcpEvent evt = VcpConnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + vcp->connected = true; + osSemaphoreRelease(vcp->tx_sem); + osSemaphoreRelease(vcp->rx_sem); } void furi_hal_vcp_disable() { furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL); - furi_hal_vcp->connected = false; - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); -} - -size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) { - furi_assert(furi_hal_vcp); - - size_t received = xStreamBufferReceive(furi_hal_vcp->rx_stream, buffer, size, portMAX_DELAY); - - if(furi_hal_vcp->rx_stream_full - && xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream) >= APP_RX_DATA_SIZE) { - furi_hal_vcp->rx_stream_full = false; - } - - return received; + VcpEvent evt = VcpDisconnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + vcp->connected = false; + osSemaphoreRelease(vcp->tx_sem); + osSemaphoreRelease(vcp->rx_sem); } size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) { - furi_assert(furi_hal_vcp); - return xStreamBufferReceive(furi_hal_vcp->rx_stream, buffer, size, timeout); + furi_assert(vcp); + furi_assert(buffer); + + size_t rx_cnt = 0; + + VcpEvent evt = VcpDisconnect; + + if (vcp->rx_buf_len > 0) { + size_t len = (vcp->rx_buf_len > size) ? (size) : (vcp->rx_buf_len); + memcpy(&buffer[rx_cnt], &vcp->rx_buf[vcp->rx_buf_start], len); + vcp->rx_buf_len -= len; + vcp->rx_buf_start += len; + rx_cnt += len; + } + + while (rx_cnt < size) { + if (osMessageQueueGet(vcp->event_queue, &evt, NULL, 0) == osOK) { + if (evt == VcpConnect) + buffer[rx_cnt] = ascii_soh; + else { + buffer[rx_cnt] = ascii_eot; + vcp->rx_buf_len = 0; + } + rx_cnt++; + return rx_cnt; + } + + if (osSemaphoreAcquire(vcp->rx_sem, timeout) == osErrorTimeout) + return rx_cnt; + + furi_check(osMutexAcquire(vcp->usb_mutex, osWaitForever) == osOK); + size_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->rx_buf, USB_CDC_PKT_LEN); + furi_check(osMutexRelease(vcp->usb_mutex) == osOK); + + vcp->rx_buf_len = len; + vcp->rx_buf_start = 0; + + if (vcp->rx_buf_len > (size - rx_cnt)) { + len = size - rx_cnt; + memcpy(&buffer[rx_cnt], vcp->rx_buf, len); + vcp->rx_buf_len -= len; + vcp->rx_buf_start += len; + } else { + memcpy(&buffer[rx_cnt], vcp->rx_buf, vcp->rx_buf_len); + vcp->rx_buf_len = 0; + } + rx_cnt += len; + } + 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, portMAX_DELAY); } void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { - furi_assert(furi_hal_vcp); + furi_assert(vcp); - while (size > 0 && furi_hal_vcp->connected) { - furi_check(osSemaphoreAcquire(furi_hal_vcp->tx_semaphore, osWaitForever) == osOK); - if (!furi_hal_vcp->connected) + while (size > 0 && vcp->connected) { + furi_check(osSemaphoreAcquire(vcp->tx_sem, osWaitForever) == osOK); + if (!vcp->connected) break; size_t batch_size = size; - if (batch_size > APP_TX_DATA_SIZE) { - batch_size = APP_TX_DATA_SIZE; + if (batch_size > USB_CDC_PKT_LEN) { + batch_size = USB_CDC_PKT_LEN; } + furi_check(osMutexAcquire(vcp->usb_mutex, osWaitForever) == osOK); furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size); + furi_check(osMutexRelease(vcp->usb_mutex) == osOK); + size -= batch_size; buffer += batch_size; } } static void vcp_state_callback(uint8_t state) { - if (state == 1) - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); - else if (furi_hal_vcp->connected) { - furi_hal_vcp->connected = false; - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); + if (state == 1) { + osSemaphoreRelease(vcp->rx_sem); + //osSemaphoreRelease(vcp->tx_sem); + } + else if (vcp->connected) { + vcp->connected = false; + osSemaphoreRelease(vcp->rx_sem); + VcpEvent evt = VcpDisconnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + //osSemaphoreRelease(vcp->tx_sem); } } static void vcp_on_cdc_control_line(uint8_t state) { - - BaseType_t xHigherPriorityTaskWoken = pdFALSE; // bit 0: DTR state, bit 1: RTS state - // bool dtr = state & 0b01; bool dtr = state & 0b1; if (dtr) { - if (!furi_hal_vcp->connected) { - furi_hal_vcp->connected = true; - xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, &ascii_soh, 1, &xHigherPriorityTaskWoken); // SOH - + if (!vcp->connected) { + vcp->connected = true; + VcpEvent evt = VcpConnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); } } else { - if (furi_hal_vcp->connected) { - xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, &ascii_eot, 1, &xHigherPriorityTaskWoken); // EOT - furi_hal_vcp->connected = false; + if (vcp->connected) { + VcpEvent evt = VcpDisconnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + vcp->connected = false; } } - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + osSemaphoreRelease(vcp->tx_sem); + osSemaphoreRelease(vcp->rx_sem); } -static void vcp_on_cdc_rx() { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream); - if (max_len > 0) { - if (max_len > APP_RX_DATA_SIZE) - max_len = APP_RX_DATA_SIZE; - int32_t size = furi_hal_cdc_receive(VCP_IF_NUM, vcp_rx_buf, max_len); - - if (size > 0) { - size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, vcp_rx_buf, size, &xHigherPriorityTaskWoken); - furi_check(ret == size); - } - } else { - furi_hal_vcp->rx_stream_full = true; - }; - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +static void vcp_on_cdc_rx() { + if (vcp->connected == false) + return; + osSemaphoreRelease(vcp->rx_sem); } static void vcp_on_cdc_tx_complete() { - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); + osSemaphoreRelease(vcp->tx_sem); } bool furi_hal_vcp_is_connected(void) { - return furi_hal_vcp->connected; + return vcp->connected; } diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.h b/firmware/targets/f7/furi-hal/furi-hal-console.h index 4c10d81e..637c17f6 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.h +++ b/firmware/targets/f7/furi-hal/furi-hal-console.h @@ -7,12 +7,6 @@ extern "C" { #endif -typedef enum { - UartIrqEventRXNE, - UartIrqEventIDLE, - //TODO: more events -} UartIrqEvent; - void furi_hal_console_init(); void furi_hal_console_enable(); diff --git a/firmware/targets/f7/furi-hal/furi-hal-uart.c b/firmware/targets/f7/furi-hal/furi-hal-uart.c index c6e74101..ff2d94a7 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-uart.c +++ b/firmware/targets/f7/furi-hal/furi-hal-uart.c @@ -182,6 +182,8 @@ void LPUART1_IRQHandler(void) { } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0); LL_LPUART_ClearFlag_IDLE(LPUART1); + } else if (LL_LPUART_IsActiveFlag_ORE(LPUART1)) { + LL_LPUART_ClearFlag_ORE(LPUART1); } //TODO: more events } @@ -193,5 +195,7 @@ void USART1_IRQHandler(void) { } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0); LL_USART_ClearFlag_IDLE(USART1); + } else if (LL_USART_IsActiveFlag_ORE(USART1)) { + LL_USART_ClearFlag_ORE(USART1); } } diff --git a/firmware/targets/f7/furi-hal/furi-hal-uart.h b/firmware/targets/f7/furi-hal/furi-hal-uart.h index 6be156b7..731a12a2 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-uart.h +++ b/firmware/targets/f7/furi-hal/furi-hal-uart.h @@ -2,7 +2,6 @@ #include #include -#include "furi-hal-console.h" #ifdef __cplusplus extern "C" { @@ -13,6 +12,11 @@ typedef enum { FuriHalUartIdLPUART1, } FuriHalUartId; +typedef enum { + UartIrqEventRXNE, + UartIrqEventIDLE, + //TODO: more events +} UartIrqEvent; void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud); diff --git a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c index 9386b100..dd82cf16 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c +++ b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c @@ -7,12 +7,12 @@ #include "usb_cdc.h" #define CDC0_RXD_EP 0x01 -#define CDC0_TXD_EP 0x81 -#define CDC0_NTF_EP 0x82 +#define CDC0_TXD_EP 0x82 +#define CDC0_NTF_EP 0x83 -#define CDC1_RXD_EP 0x03 -#define CDC1_TXD_EP 0x83 -#define CDC1_NTF_EP 0x84 +#define CDC1_RXD_EP 0x04 +#define CDC1_TXD_EP 0x85 +#define CDC1_NTF_EP 0x86 #define CDC_NTF_SZ 0x08 @@ -446,10 +446,12 @@ void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) { } int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) { + int32_t len = 0; if (if_num == 0) - return usbd_ep_read(usb_dev, CDC0_RXD_EP, buf, max_len); + len = usbd_ep_read(usb_dev, CDC0_RXD_EP, buf, max_len); else - return usbd_ep_read(usb_dev, CDC1_RXD_EP, buf, max_len); + len = usbd_ep_read(usb_dev, CDC1_RXD_EP, buf, max_len); + return ((len < 0) ? 0 : len); } static void cdc_on_wakeup(usbd_device *dev) { diff --git a/firmware/targets/f7/furi-hal/furi-hal-vcp.c b/firmware/targets/f7/furi-hal/furi-hal-vcp.c index b2f17842..b0b47f46 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-vcp.c +++ b/firmware/targets/f7/furi-hal/furi-hal-vcp.c @@ -3,18 +3,28 @@ #include #include -#define APP_RX_DATA_SIZE CDC_DATA_SZ -#define APP_TX_DATA_SIZE CDC_DATA_SZ -#define FURI_HAL_VCP_RX_BUFFER_SIZE (APP_RX_DATA_SIZE * 16) +#define USB_CDC_PKT_LEN CDC_DATA_SZ #define VCP_IF_NUM 0 +typedef enum { + VcpConnect, + VcpDisconnect, +} VcpEvent; + typedef struct { volatile bool connected; - StreamBufferHandle_t rx_stream; - volatile bool rx_stream_full; + uint8_t rx_buf[USB_CDC_PKT_LEN]; + uint8_t rx_buf_start; + uint8_t rx_buf_len; + + osMessageQueueId_t event_queue; + + osMutexId_t usb_mutex; + + osSemaphoreId_t tx_sem; + osSemaphoreId_t rx_sem; - osSemaphoreId_t tx_semaphore; } FuriHalVcp; static void vcp_on_cdc_tx_complete(); @@ -30,22 +40,21 @@ static CdcCallbacks cdc_cb = { NULL, }; -static FuriHalVcp* furi_hal_vcp = NULL; +static FuriHalVcp* vcp = NULL; static const uint8_t ascii_soh = 0x01; static const uint8_t ascii_eot = 0x04; -static uint8_t* vcp_rx_buf; - void furi_hal_vcp_init() { - furi_hal_vcp = furi_alloc(sizeof(FuriHalVcp)); - vcp_rx_buf = furi_alloc(APP_RX_DATA_SIZE); - furi_hal_vcp->connected = false; - - furi_hal_vcp->rx_stream = xStreamBufferCreate(FURI_HAL_VCP_RX_BUFFER_SIZE, 1); - furi_hal_vcp->rx_stream_full = false; + vcp = furi_alloc(sizeof(FuriHalVcp)); + vcp->connected = false; - furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL); + vcp->usb_mutex = osMutexNew(NULL); + + vcp->tx_sem = osSemaphoreNew(1, 1, NULL); + vcp->rx_sem = osSemaphoreNew(1, 0, NULL); + + vcp->event_queue = osMessageQueueNew(8, sizeof(VcpEvent), NULL); furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); @@ -54,111 +63,149 @@ void furi_hal_vcp_init() { void furi_hal_vcp_enable() { furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); - furi_hal_vcp->connected = true; + VcpEvent evt = VcpConnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + vcp->connected = true; + osSemaphoreRelease(vcp->tx_sem); + osSemaphoreRelease(vcp->rx_sem); } void furi_hal_vcp_disable() { furi_hal_cdc_set_callbacks(VCP_IF_NUM, NULL); - furi_hal_vcp->connected = false; - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); -} - -size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) { - furi_assert(furi_hal_vcp); - - size_t received = xStreamBufferReceive(furi_hal_vcp->rx_stream, buffer, size, portMAX_DELAY); - - if(furi_hal_vcp->rx_stream_full - && xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream) >= APP_RX_DATA_SIZE) { - furi_hal_vcp->rx_stream_full = false; - } - - return received; + VcpEvent evt = VcpDisconnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + vcp->connected = false; + osSemaphoreRelease(vcp->tx_sem); + osSemaphoreRelease(vcp->rx_sem); } size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) { - furi_assert(furi_hal_vcp); - return xStreamBufferReceive(furi_hal_vcp->rx_stream, buffer, size, timeout); + furi_assert(vcp); + furi_assert(buffer); + + size_t rx_cnt = 0; + + VcpEvent evt = VcpDisconnect; + + if (vcp->rx_buf_len > 0) { + size_t len = (vcp->rx_buf_len > size) ? (size) : (vcp->rx_buf_len); + memcpy(&buffer[rx_cnt], &vcp->rx_buf[vcp->rx_buf_start], len); + vcp->rx_buf_len -= len; + vcp->rx_buf_start += len; + rx_cnt += len; + } + + while (rx_cnt < size) { + if (osMessageQueueGet(vcp->event_queue, &evt, NULL, 0) == osOK) { + if (evt == VcpConnect) + buffer[rx_cnt] = ascii_soh; + else { + buffer[rx_cnt] = ascii_eot; + vcp->rx_buf_len = 0; + } + rx_cnt++; + return rx_cnt; + } + + if (osSemaphoreAcquire(vcp->rx_sem, timeout) == osErrorTimeout) + return rx_cnt; + + furi_check(osMutexAcquire(vcp->usb_mutex, osWaitForever) == osOK); + size_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->rx_buf, USB_CDC_PKT_LEN); + furi_check(osMutexRelease(vcp->usb_mutex) == osOK); + + vcp->rx_buf_len = len; + vcp->rx_buf_start = 0; + + if (vcp->rx_buf_len > (size - rx_cnt)) { + len = size - rx_cnt; + memcpy(&buffer[rx_cnt], vcp->rx_buf, len); + vcp->rx_buf_len -= len; + vcp->rx_buf_start += len; + } else { + memcpy(&buffer[rx_cnt], vcp->rx_buf, vcp->rx_buf_len); + vcp->rx_buf_len = 0; + } + rx_cnt += len; + } + 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, portMAX_DELAY); } void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { - furi_assert(furi_hal_vcp); + furi_assert(vcp); - while (size > 0 && furi_hal_vcp->connected) { - furi_check(osSemaphoreAcquire(furi_hal_vcp->tx_semaphore, osWaitForever) == osOK); - if (!furi_hal_vcp->connected) + while (size > 0 && vcp->connected) { + furi_check(osSemaphoreAcquire(vcp->tx_sem, osWaitForever) == osOK); + if (!vcp->connected) break; size_t batch_size = size; - if (batch_size > APP_TX_DATA_SIZE) { - batch_size = APP_TX_DATA_SIZE; + if (batch_size > USB_CDC_PKT_LEN) { + batch_size = USB_CDC_PKT_LEN; } + furi_check(osMutexAcquire(vcp->usb_mutex, osWaitForever) == osOK); furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size); + furi_check(osMutexRelease(vcp->usb_mutex) == osOK); + size -= batch_size; buffer += batch_size; } } static void vcp_state_callback(uint8_t state) { - if (state == 1) - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); - else if (furi_hal_vcp->connected) { - furi_hal_vcp->connected = false; - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); + if (state == 1) { + osSemaphoreRelease(vcp->rx_sem); + //osSemaphoreRelease(vcp->tx_sem); + } + else if (vcp->connected) { + vcp->connected = false; + osSemaphoreRelease(vcp->rx_sem); + VcpEvent evt = VcpDisconnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + //osSemaphoreRelease(vcp->tx_sem); } } static void vcp_on_cdc_control_line(uint8_t state) { - - BaseType_t xHigherPriorityTaskWoken = pdFALSE; // bit 0: DTR state, bit 1: RTS state - // bool dtr = state & 0b01; bool dtr = state & 0b1; if (dtr) { - if (!furi_hal_vcp->connected) { - furi_hal_vcp->connected = true; - xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, &ascii_soh, 1, &xHigherPriorityTaskWoken); // SOH - + if (!vcp->connected) { + vcp->connected = true; + VcpEvent evt = VcpConnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); } } else { - if (furi_hal_vcp->connected) { - xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, &ascii_eot, 1, &xHigherPriorityTaskWoken); // EOT - furi_hal_vcp->connected = false; + if (vcp->connected) { + VcpEvent evt = VcpDisconnect; + osMessageQueuePut(vcp->event_queue, &evt, 0, 0); + vcp->connected = false; } } - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + osSemaphoreRelease(vcp->tx_sem); + osSemaphoreRelease(vcp->rx_sem); } -static void vcp_on_cdc_rx() { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp->rx_stream); - if (max_len > 0) { - if (max_len > APP_RX_DATA_SIZE) - max_len = APP_RX_DATA_SIZE; - int32_t size = furi_hal_cdc_receive(VCP_IF_NUM, vcp_rx_buf, max_len); - - if (size > 0) { - size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, vcp_rx_buf, size, &xHigherPriorityTaskWoken); - furi_check(ret == size); - } - } else { - furi_hal_vcp->rx_stream_full = true; - }; - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +static void vcp_on_cdc_rx() { + if (vcp->connected == false) + return; + osSemaphoreRelease(vcp->rx_sem); } static void vcp_on_cdc_tx_complete() { - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); + osSemaphoreRelease(vcp->tx_sem); } bool furi_hal_vcp_is_connected(void) { - return furi_hal_vcp->connected; + return vcp->connected; } diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 73799cef..53c01d8c 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -1,4 +1,5 @@ import os +import sys import serial import time import hashlib @@ -143,7 +144,7 @@ class FlipperStorage: walk_dirs = [] path = path.replace("//", "/") - self.send_and_wait_eol('storage list "' + path + '"\r') + self.send_and_wait_eol(f'storage list "{path}"\r') data = self.read.until(self.CLI_PROMPT) lines = data.split(b"\r\n") @@ -198,9 +199,7 @@ class FlipperStorage: if size == 0: break - self.send_and_wait_eol( - 'storage write_chunk "' + filename_to + '" ' + str(size) + "\r" - ) + self.send_and_wait_eol(f'storage write_chunk "{filename_to}" {size}\r') answer = self.read.until(self.CLI_EOL) if self.has_error(answer): self.last_error = self.get_error(answer) @@ -214,9 +213,8 @@ class FlipperStorage: percent = str(math.ceil(file.tell() / filesize * 100)) total_chunks = str(math.ceil(filesize / buffer_size)) current_chunk = str(math.ceil(file.tell() / buffer_size)) - print( - percent + "%, chunk " + current_chunk + " of " + total_chunks, end="\r" - ) + sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + sys.stdout.flush() file.close() print() return True @@ -246,9 +244,8 @@ class FlipperStorage: percent = str(math.ceil(readed_size / size * 100)) total_chunks = str(math.ceil(size / buffer_size)) current_chunk = str(math.ceil(readed_size / buffer_size)) - print( - percent + "%, chunk " + current_chunk + " of " + total_chunks, end="\r" - ) + sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + sys.stdout.flush() print() self.read.until(self.CLI_PROMPT) return filedata