diff --git a/Makefile b/Makefile index bc9c5be7..0091c292 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,15 @@ PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x +NPROCS := 1 +OS := $(shell uname -s) + +ifeq ($(OS), Linux) +NPROCS := $(shell grep -c ^processor /proc/cpuinfo) +else ifeq ($(OS), Darwin) +NPROCS := $(shell sysctl -n hw.ncpu) +endif + .PHONY: all all: bootloader_all firmware_all @@ -15,7 +24,7 @@ flash: bootloader_flash firmware_flash .PHONY: debug debug: - $(MAKE) -C firmware -j9 debug + $(MAKE) -C firmware -j$(NPROCS) debug .PHONY: wipe wipe: @@ -24,29 +33,29 @@ wipe: .PHONY: bootloader_all bootloader_all: - $(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 all + $(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) all .PHONY: firmware_all firmware_all: - $(MAKE) -C $(PROJECT_ROOT)/firmware -j9 all + $(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) all .PHONY: bootloader_clean bootloader_clean: - $(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 clean + $(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) clean .PHONY: firmware_clean firmware_clean: - $(MAKE) -C $(PROJECT_ROOT)/firmware -j9 clean + $(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) clean .PHONY: bootloader_flash bootloader_flash: rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true - $(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 flash + $(MAKE) -C $(PROJECT_ROOT)/bootloader -j$(NPROCS) flash .PHONY: firmware_flash firmware_flash: rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true - $(MAKE) -C $(PROJECT_ROOT)/firmware -j9 flash + $(MAKE) -C $(PROJECT_ROOT)/firmware -j$(NPROCS) flash .PHONY: flash_radio flash_radio: diff --git a/applications/debug_tools/bad_usb.c b/applications/debug_tools/bad_usb.c index 1bd35d59..ad20e26e 100644 --- a/applications/debug_tools/bad_usb.c +++ b/applications/debug_tools/bad_usb.c @@ -248,6 +248,8 @@ static void badusb_worker(void* context) { evt.worker.state = WorkerStateDone; osMessageQueuePut(app->event_queue, &evt, 0, osWaitForever); + furi_hal_hid_kb_release_all(); + osThreadExit(); } diff --git a/applications/gpio/gpio_app.c b/applications/gpio/gpio_app.c old mode 100755 new mode 100644 index 32d92af0..8cd27bc1 --- a/applications/gpio/gpio_app.c +++ b/applications/gpio/gpio_app.c @@ -42,6 +42,9 @@ GpioApp* gpio_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); + view_dispatcher_add_view( + app->view_dispatcher, GpioAppViewUsbUart, variable_item_list_get_view(app->var_item_list)); + scene_manager_next_scene(app->scene_manager, GpioSceneStart); return app; @@ -54,10 +57,13 @@ void gpio_app_free(GpioApp* app) { view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewVarItemList); variable_item_list_free(app->var_item_list); view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewGpioTest); + view_dispatcher_remove_view(app->view_dispatcher, GpioAppViewUsbUart); gpio_test_free(app->gpio_test); + // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); + // Close records furi_record_close("gui"); furi_record_close("notification"); diff --git a/applications/gpio/gpio_app_i.h b/applications/gpio/gpio_app_i.h index 590bedc3..bfeab404 100644 --- a/applications/gpio/gpio_app_i.h +++ b/applications/gpio/gpio_app_i.h @@ -7,8 +7,8 @@ #include #include #include +#include #include - #include #include "views/gpio_test.h" @@ -25,4 +25,5 @@ struct GpioApp { typedef enum { GpioAppViewVarItemList, GpioAppViewGpioTest, + GpioAppViewUsbUart, } GpioAppView; diff --git a/applications/gpio/scenes/gpio_scene_config.h b/applications/gpio/scenes/gpio_scene_config.h index 5f7f78ba..263df2d1 100644 --- a/applications/gpio/scenes/gpio_scene_config.h +++ b/applications/gpio/scenes/gpio_scene_config.h @@ -1,2 +1,3 @@ ADD_SCENE(gpio, start, Start) ADD_SCENE(gpio, test, Test) +ADD_SCENE(gpio, usb_uart, UsbUart) diff --git a/applications/gpio/scenes/gpio_scene_start.c b/applications/gpio/scenes/gpio_scene_start.c old mode 100755 new mode 100644 index 946d8e1b..b3ed40fd --- a/applications/gpio/scenes/gpio_scene_start.c +++ b/applications/gpio/scenes/gpio_scene_start.c @@ -4,10 +4,12 @@ #define GPIO_SCENE_START_CUSTOM_EVENT_OTG_OFF (0UL) #define GPIO_SCENE_START_CUSTOM_EVENT_OTG_ON (1UL) #define GPIO_SCENE_START_CUSTOM_EVENT_TEST (2UL) +#define GPIO_SCENE_START_CUSTOM_EVENT_USB_UART (3UL) enum GpioItem { GpioItemOtg, GpioItemTest, + GpioItemUsbUart, }; enum GpioOtg { @@ -27,6 +29,9 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind if(index == GpioItemTest) { view_dispatcher_send_custom_event( app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_TEST); + } else if(index == GpioItemUsbUart) { + view_dispatcher_send_custom_event( + app->view_dispatcher, GPIO_SCENE_START_CUSTOM_EVENT_USB_UART); } } @@ -65,6 +70,7 @@ void gpio_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, gpio_otg_text[GpioOtgOff]); } 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); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewVarItemList); } @@ -80,6 +86,8 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) { furi_hal_power_disable_otg(); } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_TEST) { scene_manager_next_scene(app->scene_manager, GpioSceneTest); + } else if(event.event == GPIO_SCENE_START_CUSTOM_EVENT_USB_UART) { + 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 new file mode 100644 index 00000000..05de8e7b --- /dev/null +++ b/applications/gpio/scenes/gpio_scene_usb_uart.c @@ -0,0 +1,352 @@ +#include "../gpio_app_i.h" +#include "furi-hal.h" +#include +#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) + +typedef enum { + WorkerCmdStop = (1 << 0), + +} WorkerCommandFlags; + +typedef enum { + UsbUartLineIndexVcp, + UsbUartLineIndexUart, + UsbUartLineIndexBaudrate, + UsbUartLineIndexEnable, + UsbUartLineIndexDisable, +} LineIndex; + +typedef enum { + UsbUartPortUSART1 = 0, + UsbUartPortLPUART1 = 1, +} PortIdx; + +typedef struct { + uint8_t vcp_ch; + PortIdx uart_ch; + uint32_t baudrate; +} UsbUartConfig; + +typedef struct { + UsbUartConfig cfg_cur; + UsbUartConfig cfg_set; + char br_text[8]; + + bool running; + osThreadId_t parent_thread; + + osThreadAttr_t thread_attr; + osThreadId_t thread; + + osThreadAttr_t tx_thread_attr; + osThreadId_t tx_thread; + + StreamBufferHandle_t rx_stream; + osSemaphoreId_t rx_done_sem; + osSemaphoreId_t usb_sof_sem; + + StreamBufferHandle_t tx_stream; + + uint8_t rx_buf[USB_PKT_LEN]; + uint8_t tx_buf[USB_PKT_LEN]; +} UsbUartParams; + +static UsbUartParams* usb_uart; + +static const char* vcp_ch[] = {"0 (CLI)", "1"}; +static const char* uart_ch[] = {"USART1", "LPUART1"}; +static const char* baudrate_mode[] = {"Host"}; +static const uint32_t baudrate_list[] = { + 2400, + 9600, + 19200, + 38400, + 57600, + 115200, + 230400, + 460800, + 921600, +}; + +static void vcp_on_cdc_tx_complete(); +static void vcp_on_cdc_rx(); +static void vcp_state_callback(uint8_t state); +static void vcp_on_cdc_control_line(uint8_t state); +static void vcp_on_line_config(struct usb_cdc_line_coding* config); + +static CdcCallbacks cdc_cb = { + vcp_on_cdc_tx_complete, + vcp_on_cdc_rx, + vcp_state_callback, + vcp_on_cdc_control_line, + vcp_on_line_config, +}; + +/* USB UART worker */ + +static void usb_uart_tx_thread(void* context); + +static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + if(ev == UartIrqEventRXNE) { + size_t ret = + xStreamBufferSendFromISR(usb_uart->rx_stream, &data, 1, &xHigherPriorityTaskWoken); + furi_check(ret == 1); + ret = xStreamBufferBytesAvailable(usb_uart->rx_stream); + if(ret > USB_PKT_LEN) osSemaphoreRelease(usb_uart->rx_done_sem); + } else if(ev == UartIrqEventIDLE) { + osSemaphoreRelease(usb_uart->rx_done_sem); + } + + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static void usb_uart_worker(void* context) { + memcpy(&usb_uart->cfg_cur, &usb_uart->cfg_set, sizeof(UsbUartConfig)); + + usb_uart->rx_stream = xStreamBufferCreate(USB_UART_RX_BUF_SIZE, 1); + usb_uart->rx_done_sem = osSemaphoreNew(1, 1, NULL); + usb_uart->usb_sof_sem = osSemaphoreNew(1, 1, NULL); + + usb_uart->tx_stream = xStreamBufferCreate(USB_UART_TX_BUF_SIZE, 1); + + usb_uart->tx_thread = NULL; + usb_uart->tx_thread_attr.name = "usb_uart_tx"; + usb_uart->tx_thread_attr.stack_size = 512; + + UsbMode usb_mode_prev = furi_hal_usb_get_config(); + if(usb_uart->cfg_cur.vcp_ch == 0) { + furi_hal_usb_set_config(UsbModeVcpSingle); + furi_hal_vcp_disable(); + } else { + furi_hal_usb_set_config(UsbModeVcpDual); + } + + if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) { + furi_hal_usart_init(); + furi_hal_usart_set_irq_cb(usb_uart_on_irq_cb); + if(usb_uart->cfg_cur.baudrate != 0) + furi_hal_usart_set_br(usb_uart->cfg_cur.baudrate); + else + vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch)); + } else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) { + furi_hal_lpuart_init(); + furi_hal_lpuart_set_irq_cb(usb_uart_on_irq_cb); + if(usb_uart->cfg_cur.baudrate != 0) + furi_hal_lpuart_set_br(usb_uart->cfg_cur.baudrate); + else + vcp_on_line_config(furi_hal_cdc_get_port_settings(usb_uart->cfg_cur.vcp_ch)); + } + + furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, &cdc_cb); + usb_uart->tx_thread = osThreadNew(usb_uart_tx_thread, NULL, &usb_uart->tx_thread_attr); + + while(1) { + furi_check(osSemaphoreAcquire(usb_uart->rx_done_sem, osWaitForever) == osOK); + if(osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, 0) == WorkerCmdStop) break; + size_t len = 0; + do { + len = xStreamBufferReceive(usb_uart->rx_stream, usb_uart->rx_buf, USB_PKT_LEN, 0); + if(len > 0) { + if(osSemaphoreAcquire(usb_uart->usb_sof_sem, 100) == osOK) + furi_hal_cdc_send(usb_uart->cfg_cur.vcp_ch, usb_uart->rx_buf, len); + else + xStreamBufferReset(usb_uart->rx_stream); + } + } while(len > 0); + } + + osThreadTerminate(usb_uart->tx_thread); + + if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) + furi_hal_usart_deinit(); + else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) + furi_hal_lpuart_deinit(); + + furi_hal_cdc_set_callbacks(usb_uart->cfg_cur.vcp_ch, NULL); + furi_hal_usb_set_config(usb_mode_prev); + if(usb_uart->cfg_cur.vcp_ch == 0) furi_hal_vcp_enable(); + + vStreamBufferDelete(usb_uart->rx_stream); + osSemaphoreDelete(usb_uart->rx_done_sem); + osSemaphoreDelete(usb_uart->usb_sof_sem); + + vStreamBufferDelete(usb_uart->tx_stream); + osThreadFlagsSet(usb_uart->parent_thread, WorkerCmdStop); + osThreadExit(); +} + +static void usb_uart_tx_thread(void* context) { + uint8_t data = 0; + while(1) { + size_t len = xStreamBufferReceive(usb_uart->tx_stream, &data, 1, osWaitForever); + if(len > 0) { + if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) + furi_hal_usart_tx(&data, len); + else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) + furi_hal_lpuart_tx(&data, len); + } + } + osThreadExit(); +} + +/* VCP callbacks */ + +static void vcp_on_cdc_tx_complete() { + osSemaphoreRelease(usb_uart->usb_sof_sem); +} + +static void vcp_on_cdc_rx() { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + uint16_t max_len = xStreamBufferSpacesAvailable(usb_uart->tx_stream); + if(max_len > 0) { + if(max_len > USB_PKT_LEN) max_len = USB_PKT_LEN; + int32_t size = furi_hal_cdc_receive(usb_uart->cfg_cur.vcp_ch, usb_uart->tx_buf, max_len); + + if(size > 0) { + size_t ret = xStreamBufferSendFromISR( + usb_uart->tx_stream, usb_uart->tx_buf, size, &xHigherPriorityTaskWoken); + furi_check(ret == size); + } + } + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static void vcp_state_callback(uint8_t state) { +} + +static void vcp_on_cdc_control_line(uint8_t state) { +} + +static void vcp_on_line_config(struct usb_cdc_line_coding* config) { + if((usb_uart->cfg_cur.baudrate == 0) && (config->dwDTERate != 0)) { + if(usb_uart->cfg_cur.uart_ch == UsbUartPortUSART1) + furi_hal_usart_set_br(config->dwDTERate); + else if(usb_uart->cfg_cur.uart_ch == UsbUartPortLPUART1) + furi_hal_lpuart_set_br(config->dwDTERate); + } +} + +/* USB UART app */ + +static void usb_uart_enable() { + if(usb_uart->running == false) { + usb_uart->thread = NULL; + usb_uart->thread_attr.name = "usb_uart"; + usb_uart->thread_attr.stack_size = 1024; + usb_uart->parent_thread = osThreadGetId(); + usb_uart->running = true; + usb_uart->thread = osThreadNew(usb_uart_worker, NULL, &usb_uart->thread_attr); + } +} + +static void usb_uart_disable() { + if(usb_uart->running == true) { + osThreadFlagsSet(usb_uart->thread, WorkerCmdStop); + osSemaphoreRelease(usb_uart->rx_done_sem); + osThreadFlagsWait(WorkerCmdStop, osFlagsWaitAny, osWaitForever); + usb_uart->running = false; + } +} + +bool gpio_scene_usb_uart_on_event(void* context, SceneManagerEvent event) { + //GpioApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == UsbUartLineIndexEnable) { + usb_uart_enable(); + } else if(event.event == UsbUartLineIndexDisable) { + usb_uart_disable(); + } + consumed = true; + } + return consumed; +} + +/* Scene callbacks */ + +static void line_vcp_cb(VariableItem* item) { + //GpioApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, vcp_ch[index]); + + usb_uart->cfg_set.vcp_ch = index; +} + +static void line_port_cb(VariableItem* item) { + //GpioApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_ch[index]); + + usb_uart->cfg_set.uart_ch = index; +} + +static void line_baudrate_cb(VariableItem* item) { + //GpioApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + if(index > 0) { + snprintf(usb_uart->br_text, 7, "%lu", baudrate_list[index - 1]); + variable_item_set_current_value_text(item, usb_uart->br_text); + usb_uart->cfg_set.baudrate = baudrate_list[index - 1]; + } else { + variable_item_set_current_value_text(item, baudrate_mode[index]); + usb_uart->cfg_set.baudrate = 0; + } +} + +static void gpio_scene_usb_uart_enter_callback(void* context, uint32_t index) { + furi_assert(context); + GpioApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void gpio_scene_usb_uart_on_enter(void* context) { + GpioApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + + usb_uart = furi_alloc(sizeof(UsbUartParams)); + + VariableItem* item; + + variable_item_list_set_enter_callback(var_item_list, gpio_scene_usb_uart_enter_callback, app); + + item = variable_item_list_add(var_item_list, "VCP Channel", 2, line_vcp_cb, app); + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, vcp_ch[0]); + + item = variable_item_list_add(var_item_list, "UART Port", 2, line_port_cb, app); + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, uart_ch[0]); + + item = variable_item_list_add( + var_item_list, + "Baudrate", + sizeof(baudrate_list) / sizeof(baudrate_list[0]) + 1, + line_baudrate_cb, + app); + variable_item_set_current_value_index(item, 0); + variable_item_set_current_value_text(item, baudrate_mode[0]); + + item = variable_item_list_add(var_item_list, "Enable", 0, NULL, NULL); + item = variable_item_list_add(var_item_list, "Disable", 0, NULL, NULL); + + view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewUsbUart); +} + +void gpio_scene_usb_uart_on_exit(void* context) { + GpioApp* app = context; + usb_uart_disable(); + variable_item_list_clean(app->var_item_list); + free(usb_uart); +} \ No newline at end of file diff --git a/applications/gui/modules/variable-item-list.c b/applications/gui/modules/variable-item-list.c index fccfd84a..800e0602 100755 --- a/applications/gui/modules/variable-item-list.c +++ b/applications/gui/modules/variable-item-list.c @@ -259,7 +259,7 @@ void variable_item_list_clean(VariableItemList* variable_item_list) { VariableItemArray_it_t it; for(VariableItemArray_it(it, model->items); !VariableItemArray_end_p(it); VariableItemArray_next(it)) { - string_clean(VariableItemArray_ref(it)->current_value_text); + string_clear(VariableItemArray_ref(it)->current_value_text); } VariableItemArray_clean(model->items); return false; diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.c b/firmware/targets/f6/furi-hal/furi-hal-console.c index b04a17a1..552f9e77 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.c +++ b/firmware/targets/f6/furi-hal/furi-hal-console.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,8 +8,12 @@ #include +#define CONSOLE_BAUDRATE 230400 + volatile bool furi_hal_console_alive = false; +static void (*irq_cb)(uint8_t ev, uint8_t data); + void furi_hal_console_init() { LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7; @@ -21,11 +26,11 @@ void furi_hal_console_init() { LL_USART_InitTypeDef USART_InitStruct = {0}; USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = 230400; + USART_InitStruct.BaudRate = CONSOLE_BAUDRATE; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; USART_InitStruct.StopBits = LL_USART_STOPBITS_1; USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; LL_USART_Init(USART1, &USART_InitStruct); @@ -36,12 +41,41 @@ void furi_hal_console_init() { LL_USART_Enable(USART1); while(!LL_USART_IsActiveFlag_TEACK(USART1)) ; + + LL_USART_EnableIT_RXNE_RXFNE(USART1); + LL_USART_EnableIT_IDLE(USART1); + HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); + furi_hal_console_alive = true; FURI_LOG_I("FuriHalConsole", "Init OK"); } -static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) { +void furi_hal_usart_init() { + furi_hal_console_alive = false; +} + +void furi_hal_usart_set_br(uint32_t baud) { + if (LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while (!LL_USART_IsActiveFlag_TC(USART1)); + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); + LL_USART_Enable(USART1); + } +} + +void furi_hal_usart_deinit() { + while (!LL_USART_IsActiveFlag_TC(USART1)); + furi_hal_usart_set_br(CONSOLE_BAUDRATE); + furi_hal_console_alive = true; +} + +void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) { + if (LL_USART_IsEnabled(USART1) == 0) + return; + while(buffer_size > 0) { while (!LL_USART_IsActiveFlag_TXE(USART1)); @@ -52,12 +86,32 @@ static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) } } +void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { + irq_cb = cb; + if (irq_cb == NULL) + NVIC_DisableIRQ(USART1_IRQn); + else + NVIC_EnableIRQ(USART1_IRQn); +} + +void USART1_IRQHandler(void) { + if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { + uint8_t data = LL_USART_ReceiveData8(USART1); + irq_cb(UartIrqEventRXNE, data); + } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { + irq_cb(UartIrqEventIDLE, 0); + LL_USART_ClearFlag_IDLE(USART1); + } + + //TODO: more events +} + void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { if (!furi_hal_console_alive) return; // Transmit data - furi_hal_console_uart_tx(buffer, buffer_size); + furi_hal_usart_tx(buffer, buffer_size); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } @@ -67,9 +121,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size return; // Transmit data - furi_hal_console_uart_tx(buffer, buffer_size); + furi_hal_usart_tx(buffer, buffer_size); // Transmit new line symbols - furi_hal_console_uart_tx((const uint8_t*)"\r\n", 2); + furi_hal_usart_tx((const uint8_t*)"\r\n", 2); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } diff --git a/firmware/targets/f6/furi-hal/furi-hal-console.h b/firmware/targets/f6/furi-hal/furi-hal-console.h index cd7ccae8..013653ba 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-console.h +++ b/firmware/targets/f6/furi-hal/furi-hal-console.h @@ -7,6 +7,12 @@ extern "C" { #endif +typedef enum { + UartIrqEventRXNE, + UartIrqEventIDLE, + //TODO: more events +} UartIrqEvent; + void furi_hal_console_init(); void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); @@ -23,6 +29,18 @@ void furi_hal_console_printf(const char format[], ...); void furi_hal_console_puts(const char* data); + +void furi_hal_usart_init(); + +void furi_hal_usart_deinit(); + +void furi_hal_usart_set_br(uint32_t baud); + +void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size); + +void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); + + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f6/furi-hal/furi-hal-lpuart.c b/firmware/targets/f6/furi-hal/furi-hal-lpuart.c new file mode 100644 index 00000000..31aa8b86 --- /dev/null +++ b/firmware/targets/f6/furi-hal/furi-hal-lpuart.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include + +static void (*irq_cb)(uint8_t ev, uint8_t data); + +void furi_hal_lpuart_init() { + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + GPIO_InitStruct.Alternate = LL_GPIO_AF_8; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = 115200; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); + + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + LL_LPUART_EnableIT_IDLE(LPUART1); + HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0); + + FURI_LOG_I("FuriHalLpUart", "Init OK"); +} + +void furi_hal_lpuart_set_br(uint32_t baud) { + if (LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1)); + if (uartclk/baud > 4095) { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); + } else { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); + } + + LL_LPUART_Enable(LPUART1); + } +} + +void furi_hal_lpuart_deinit() { + furi_hal_lpuart_set_irq_cb(NULL); + LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG); + LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG); + LL_LPUART_Disable(LPUART1); + LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1); +} + +void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) { + if (LL_LPUART_IsEnabled(LPUART1) == 0) + return; + + while(buffer_size > 0) { + while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } +} + +void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { + irq_cb = cb; + if (irq_cb == NULL) + NVIC_DisableIRQ(LPUART1_IRQn); + else + NVIC_EnableIRQ(LPUART1_IRQn); +} + +void LPUART1_IRQHandler(void) { + if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { + uint8_t data = LL_LPUART_ReceiveData8(LPUART1); + irq_cb(UartIrqEventRXNE, data); + } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { + irq_cb(UartIrqEventIDLE, 0); + LL_LPUART_ClearFlag_IDLE(LPUART1); + } + + //TODO: more events +} diff --git a/firmware/targets/f6/furi-hal/furi-hal-lpuart.h b/firmware/targets/f6/furi-hal/furi-hal-lpuart.h new file mode 100644 index 00000000..118a9a9c --- /dev/null +++ b/firmware/targets/f6/furi-hal/furi-hal-lpuart.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include "furi-hal-console.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +void furi_hal_lpuart_init(); + +void furi_hal_lpuart_deinit(); + +void furi_hal_lpuart_set_br(uint32_t baud); + +void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size); + +void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); + +#ifdef __cplusplus +} +#endif 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 09efbec3..e643fe57 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c +++ b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc.c @@ -1,6 +1,5 @@ #include "furi-hal-version.h" #include "furi-hal-usb_i.h" -#include "furi-hal-vcp_i.h" #include "furi-hal-usb-cdc_i.h" #include @@ -17,6 +16,8 @@ #define CDC_NTF_SZ 0x08 +#define IF_NUM_MAX 2 + struct CdcIadDescriptor { struct usb_iad_descriptor comm_iad; struct usb_interface_descriptor comm; @@ -343,12 +344,8 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = { }, }; -static struct usb_cdc_line_coding cdc_line = { - .dwDTERate = 38400, - .bCharFormat = USB_CDC_1_STOP_BITS, - .bParityType = USB_CDC_NO_PARITY, - .bDataBits = 8, -}; +static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; + static void cdc_init(usbd_device* dev, struct UsbInterface* intf); static void cdc_deinit(usbd_device *dev); static void cdc_on_wakeup(usbd_device *dev); @@ -358,6 +355,7 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg); static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback); static usbd_device* usb_dev; static struct UsbInterface* cdc_if_cur = NULL; +static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; struct UsbInterface usb_cdc_single = { .init = cdc_init, @@ -429,6 +427,17 @@ static void cdc_deinit(usbd_device *dev) { cdc_if_cur = NULL; } +void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb) { + if (if_num < 2) + callbacks[if_num] = cb; +} + +struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num) { + if (if_num < 2) + return &cdc_config[if_num]; + return NULL; +} + void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) { if (if_num == 0) usbd_ep_write(usb_dev, CDC0_TXD_EP, buf, len); @@ -444,25 +453,47 @@ int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) { } static void cdc_on_wakeup(usbd_device *dev) { - furi_hal_vcp_on_usb_resume(); + for (uint8_t i = 0; i < IF_NUM_MAX; i++) { + if (callbacks[i] != NULL) { + if (callbacks[i]->state_callback != NULL) + callbacks[i]->state_callback(1); + } + } } static void cdc_on_suspend(usbd_device *dev) { - furi_hal_vcp_on_usb_suspend(); + for (uint8_t i = 0; i < IF_NUM_MAX; i++) { + if (callbacks[i] != NULL) { + if (callbacks[i]->state_callback != NULL) + callbacks[i]->state_callback(0); + } + } } static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { + uint8_t if_num = 0; if (ep == CDC0_RXD_EP) - furi_hal_vcp_on_cdc_rx(0); + if_num = 0; else - furi_hal_vcp_on_cdc_rx(1); + if_num = 1; + + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->rx_ep_callback != NULL) + callbacks[if_num]->rx_ep_callback(); + } } static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { + uint8_t if_num = 0; if (ep == CDC0_TXD_EP) - furi_hal_vcp_on_cdc_tx_complete(0); + if_num = 0; else - furi_hal_vcp_on_cdc_tx_complete(1); + if_num = 1; + + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->tx_ep_callback != NULL) + callbacks[if_num]->tx_ep_callback(); + } } static void cdc_txrx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { @@ -494,13 +525,15 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) { return usbd_ack; case 1: /* configuring device */ - if ((CDC0_TXD_EP & 0x7F) != (CDC0_RXD_EP & 0x7F)) { + if ((CDC0_TXD_EP & 0x7F) != (CDC0_RXD_EP & 0x7F)) { + // 2x unidirectional endpoint mode with dualbuf usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ); usbd_reg_endpoint(dev, CDC0_RXD_EP, cdc_rx_ep_callback); usbd_reg_endpoint(dev, CDC0_TXD_EP, cdc_tx_ep_callback); - } else { + } else { + // 1x bidirectional endpoint mode usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ); @@ -532,20 +565,33 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) { } /* Control requests handler */ -static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) { +static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { /* CDC control requests */ - if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) - && req->wIndex == 0 ) { - switch (req->bRequest) { + uint8_t if_num = 0; + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) + && (req->wIndex == 0 || req->wIndex == 2)) { + if (req->wIndex == 0) + if_num = 0; + else + if_num = 1; + + switch(req->bRequest) { case USB_CDC_SET_CONTROL_LINE_STATE: - furi_hal_vcp_on_cdc_control_line(req->wValue); + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->ctrl_line_callback != NULL) + callbacks[if_num]->ctrl_line_callback(req->wValue); + } return usbd_ack; case USB_CDC_SET_LINE_CODING: - memcpy(&cdc_line, req->data, sizeof(cdc_line)); + memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0])); + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->config_callback != NULL) + callbacks[if_num]->config_callback(&cdc_config[if_num]); + } return usbd_ack; case USB_CDC_GET_LINE_CODING: - dev->status.data_ptr = &cdc_line; - dev->status.data_count = sizeof(cdc_line); + dev->status.data_ptr = &cdc_config[if_num]; + dev->status.data_count = sizeof(cdc_config[0]); return usbd_ack; default: return usbd_fail; diff --git a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc_i.h b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc_i.h index 636b3c04..3b181582 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-usb-cdc_i.h +++ b/firmware/targets/f6/furi-hal/furi-hal-usb-cdc_i.h @@ -1,6 +1,21 @@ #pragma once -#define CDC_DATA_SZ 0x40 +#include +#include "usb_cdc.h" + +#define CDC_DATA_SZ 64 + +typedef struct { + void (*tx_ep_callback)(void); + void (*rx_ep_callback)(void); + void (*state_callback)(uint8_t state); + void (*ctrl_line_callback)(uint8_t state); + void (*config_callback)(struct usb_cdc_line_coding* config); +} CdcCallbacks; + +void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb); + +struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num); void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len); diff --git a/firmware/targets/f6/furi-hal/furi-hal-usb.c b/firmware/targets/f6/furi-hal/furi-hal-usb.c index 5442975f..8c78eb8b 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-usb.c +++ b/firmware/targets/f6/furi-hal/furi-hal-usb.c @@ -1,7 +1,6 @@ #include "furi-hal-version.h" #include "furi-hal-usb_i.h" #include "furi-hal-usb.h" -#include "furi-hal-vcp_i.h" #include #include "usb.h" @@ -34,6 +33,7 @@ struct UsbCfg{ UsbMode mode_cur; UsbMode mode_next; bool enabled; + bool connected; } usb_config; static void furi_hal_usb_tmr_cb(void* context); @@ -158,11 +158,15 @@ static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16 } static void susp_evt(usbd_device *dev, uint8_t event, uint8_t ep) { - if (usb_if_modes[usb_config.mode_cur] != NULL) + if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == true)) { + usb_config.connected = false; usb_if_modes[usb_config.mode_cur]->suspend(&udev); + } } static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep) { - if (usb_if_modes[usb_config.mode_cur] != NULL) + if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == false)) { + usb_config.connected = true; usb_if_modes[usb_config.mode_cur]->wakeup(&udev); + } } diff --git a/firmware/targets/f6/furi-hal/furi-hal-vcp.c b/firmware/targets/f6/furi-hal/furi-hal-vcp.c index e276c4a3..b975495d 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-vcp.c +++ b/firmware/targets/f6/furi-hal/furi-hal-vcp.c @@ -1,4 +1,3 @@ -#include #include #include @@ -7,6 +6,7 @@ #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 VCP_IF_NUM 0 typedef struct { volatile bool connected; @@ -17,6 +17,19 @@ typedef struct { osSemaphoreId_t tx_semaphore; } FuriHalVcp; +static void vcp_on_cdc_tx_complete(); +static void vcp_on_cdc_rx(); +static void vcp_state_callback(uint8_t state); +static void vcp_on_cdc_control_line(uint8_t state); + +static CdcCallbacks cdc_cb = { + vcp_on_cdc_tx_complete, + vcp_on_cdc_rx, + vcp_state_callback, + vcp_on_cdc_control_line, + NULL, +}; + static FuriHalVcp* furi_hal_vcp = NULL; static const uint8_t ascii_soh = 0x01; @@ -34,9 +47,22 @@ void furi_hal_vcp_init() { furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL); + furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); + FURI_LOG_I("FuriHalVcp", "Init OK"); } +void furi_hal_vcp_enable() { + furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); + furi_hal_vcp->connected = true; +} + +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); @@ -68,24 +94,22 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { batch_size = APP_TX_DATA_SIZE; } - furi_hal_cdc_send(0, (uint8_t*)buffer, batch_size); + furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size); size -= batch_size; buffer += batch_size; } } -void furi_hal_vcp_on_usb_resume() { - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); -} - -void furi_hal_vcp_on_usb_suspend() { - if (furi_hal_vcp->connected) { +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); } } -void furi_hal_vcp_on_cdc_control_line(uint8_t state) { +static void vcp_on_cdc_control_line(uint8_t state) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; // bit 0: DTR state, bit 1: RTS state @@ -110,30 +134,26 @@ void furi_hal_vcp_on_cdc_control_line(uint8_t state) { portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } -void furi_hal_vcp_on_cdc_rx(uint8_t if_num) { +static void vcp_on_cdc_rx() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - if (if_num == 0) { - 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(0, vcp_rx_buf, max_len); + 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; - }; - } + 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); } -void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num) { - if (if_num == 0) - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); +static void vcp_on_cdc_tx_complete() { + osSemaphoreRelease(furi_hal_vcp->tx_semaphore); } - diff --git a/firmware/targets/f6/furi-hal/furi-hal-vcp_i.h b/firmware/targets/f6/furi-hal/furi-hal-vcp_i.h deleted file mode 100644 index 9c8ccd73..00000000 --- a/firmware/targets/f6/furi-hal/furi-hal-vcp_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -void furi_hal_vcp_on_usb_resume(); - -void furi_hal_vcp_on_usb_suspend(); - -void furi_hal_vcp_on_cdc_control_line(uint8_t state); - -void furi_hal_vcp_on_cdc_rx(uint8_t if_num); - -void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num); diff --git a/firmware/targets/f6/furi-hal/furi-hal.c b/firmware/targets/f6/furi-hal/furi-hal.c index 8f680385..bcddc6e0 100644 --- a/firmware/targets/f6/furi-hal/furi-hal.c +++ b/firmware/targets/f6/furi-hal/furi-hal.c @@ -33,9 +33,9 @@ void furi_hal_init() { furi_hal_crypto_init(); // VCP + USB - furi_hal_vcp_init(); furi_hal_usb_init(); furi_hal_usb_set_config(UsbModeVcpSingle); + furi_hal_vcp_init(); FURI_LOG_I("HAL", "USB OK"); furi_hal_i2c_init(); diff --git a/firmware/targets/f6/target.mk b/firmware/targets/f6/target.mk index 298182ef..f942ba0f 100644 --- a/firmware/targets/f6/target.mk +++ b/firmware/targets/f6/target.mk @@ -72,6 +72,7 @@ C_SOURCES += \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_tim.c \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_usart.c \ + $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_lpuart.c \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c # FreeRTOS diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.c b/firmware/targets/f7/furi-hal/furi-hal-console.c index b04a17a1..552f9e77 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.c +++ b/firmware/targets/f7/furi-hal/furi-hal-console.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,8 +8,12 @@ #include +#define CONSOLE_BAUDRATE 230400 + volatile bool furi_hal_console_alive = false; +static void (*irq_cb)(uint8_t ev, uint8_t data); + void furi_hal_console_init() { LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7; @@ -21,11 +26,11 @@ void furi_hal_console_init() { LL_USART_InitTypeDef USART_InitStruct = {0}; USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1; - USART_InitStruct.BaudRate = 230400; + USART_InitStruct.BaudRate = CONSOLE_BAUDRATE; USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B; USART_InitStruct.StopBits = LL_USART_STOPBITS_1; USART_InitStruct.Parity = LL_USART_PARITY_NONE; - USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX; + USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; LL_USART_Init(USART1, &USART_InitStruct); @@ -36,12 +41,41 @@ void furi_hal_console_init() { LL_USART_Enable(USART1); while(!LL_USART_IsActiveFlag_TEACK(USART1)) ; + + LL_USART_EnableIT_RXNE_RXFNE(USART1); + LL_USART_EnableIT_IDLE(USART1); + HAL_NVIC_SetPriority(USART1_IRQn, 5, 0); + furi_hal_console_alive = true; FURI_LOG_I("FuriHalConsole", "Init OK"); } -static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) { +void furi_hal_usart_init() { + furi_hal_console_alive = false; +} + +void furi_hal_usart_set_br(uint32_t baud) { + if (LL_USART_IsEnabled(USART1)) { + // Wait for transfer complete flag + while (!LL_USART_IsActiveFlag_TC(USART1)); + LL_USART_Disable(USART1); + uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE); + LL_USART_SetBaudRate(USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud); + LL_USART_Enable(USART1); + } +} + +void furi_hal_usart_deinit() { + while (!LL_USART_IsActiveFlag_TC(USART1)); + furi_hal_usart_set_br(CONSOLE_BAUDRATE); + furi_hal_console_alive = true; +} + +void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size) { + if (LL_USART_IsEnabled(USART1) == 0) + return; + while(buffer_size > 0) { while (!LL_USART_IsActiveFlag_TXE(USART1)); @@ -52,12 +86,32 @@ static void furi_hal_console_uart_tx(const uint8_t* buffer, size_t buffer_size) } } +void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { + irq_cb = cb; + if (irq_cb == NULL) + NVIC_DisableIRQ(USART1_IRQn); + else + NVIC_EnableIRQ(USART1_IRQn); +} + +void USART1_IRQHandler(void) { + if (LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) { + uint8_t data = LL_USART_ReceiveData8(USART1); + irq_cb(UartIrqEventRXNE, data); + } else if (LL_USART_IsActiveFlag_IDLE(USART1)) { + irq_cb(UartIrqEventIDLE, 0); + LL_USART_ClearFlag_IDLE(USART1); + } + + //TODO: more events +} + void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) { if (!furi_hal_console_alive) return; // Transmit data - furi_hal_console_uart_tx(buffer, buffer_size); + furi_hal_usart_tx(buffer, buffer_size); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } @@ -67,9 +121,9 @@ void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size return; // Transmit data - furi_hal_console_uart_tx(buffer, buffer_size); + furi_hal_usart_tx(buffer, buffer_size); // Transmit new line symbols - furi_hal_console_uart_tx((const uint8_t*)"\r\n", 2); + furi_hal_usart_tx((const uint8_t*)"\r\n", 2); // Wait for TC flag to be raised for last char while (!LL_USART_IsActiveFlag_TC(USART1)); } diff --git a/firmware/targets/f7/furi-hal/furi-hal-console.h b/firmware/targets/f7/furi-hal/furi-hal-console.h index cd7ccae8..013653ba 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-console.h +++ b/firmware/targets/f7/furi-hal/furi-hal-console.h @@ -7,6 +7,12 @@ extern "C" { #endif +typedef enum { + UartIrqEventRXNE, + UartIrqEventIDLE, + //TODO: more events +} UartIrqEvent; + void furi_hal_console_init(); void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size); @@ -23,6 +29,18 @@ void furi_hal_console_printf(const char format[], ...); void furi_hal_console_puts(const char* data); + +void furi_hal_usart_init(); + +void furi_hal_usart_deinit(); + +void furi_hal_usart_set_br(uint32_t baud); + +void furi_hal_usart_tx(const uint8_t* buffer, size_t buffer_size); + +void furi_hal_usart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); + + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi-hal/furi-hal-lpuart.c b/firmware/targets/f7/furi-hal/furi-hal-lpuart.c new file mode 100644 index 00000000..31aa8b86 --- /dev/null +++ b/firmware/targets/f7/furi-hal/furi-hal-lpuart.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include + +#include + +static void (*irq_cb)(uint8_t ev, uint8_t data); + +void furi_hal_lpuart_init() { + LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; + GPIO_InitStruct.Pin = PC0_Pin|PC1_Pin; + GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; + GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; + GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; + GPIO_InitStruct.Alternate = LL_GPIO_AF_8; + LL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1); + LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPUART1); + + LL_LPUART_InitTypeDef LPUART_InitStruct = {0}; + LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1; + LPUART_InitStruct.BaudRate = 115200; + LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; + LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1; + LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE; + LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; + LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; + LL_LPUART_Init(LPUART1, &LPUART_InitStruct); + LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); + LL_LPUART_EnableFIFO(LPUART1); + + LL_LPUART_Enable(LPUART1); + + while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))); + + LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); + LL_LPUART_EnableIT_IDLE(LPUART1); + HAL_NVIC_SetPriority(LPUART1_IRQn, 5, 0); + + FURI_LOG_I("FuriHalLpUart", "Init OK"); +} + +void furi_hal_lpuart_set_br(uint32_t baud) { + if (LL_LPUART_IsEnabled(LPUART1)) { + // Wait for transfer complete flag + while (!LL_LPUART_IsActiveFlag_TC(LPUART1)); + LL_LPUART_Disable(LPUART1); + uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_GetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1)); + if (uartclk/baud > 4095) { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud); + } else { + LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1); + LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud); + } + + LL_LPUART_Enable(LPUART1); + } +} + +void furi_hal_lpuart_deinit() { + furi_hal_lpuart_set_irq_cb(NULL); + LL_GPIO_SetPinMode(GPIOC, PC0_Pin, LL_GPIO_MODE_ANALOG); + LL_GPIO_SetPinMode(GPIOC, PC1_Pin, LL_GPIO_MODE_ANALOG); + LL_LPUART_Disable(LPUART1); + LL_APB1_GRP2_DisableClock(LL_APB1_GRP2_PERIPH_LPUART1); +} + +void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size) { + if (LL_LPUART_IsEnabled(LPUART1) == 0) + return; + + while(buffer_size > 0) { + while (!LL_LPUART_IsActiveFlag_TXE(LPUART1)); + + LL_LPUART_TransmitData8(LPUART1, *buffer); + + buffer++; + buffer_size--; + } +} + +void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)) { + irq_cb = cb; + if (irq_cb == NULL) + NVIC_DisableIRQ(LPUART1_IRQn); + else + NVIC_EnableIRQ(LPUART1_IRQn); +} + +void LPUART1_IRQHandler(void) { + if (LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) { + uint8_t data = LL_LPUART_ReceiveData8(LPUART1); + irq_cb(UartIrqEventRXNE, data); + } else if (LL_LPUART_IsActiveFlag_IDLE(LPUART1)) { + irq_cb(UartIrqEventIDLE, 0); + LL_LPUART_ClearFlag_IDLE(LPUART1); + } + + //TODO: more events +} diff --git a/firmware/targets/f7/furi-hal/furi-hal-lpuart.h b/firmware/targets/f7/furi-hal/furi-hal-lpuart.h new file mode 100644 index 00000000..118a9a9c --- /dev/null +++ b/firmware/targets/f7/furi-hal/furi-hal-lpuart.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include "furi-hal-console.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +void furi_hal_lpuart_init(); + +void furi_hal_lpuart_deinit(); + +void furi_hal_lpuart_set_br(uint32_t baud); + +void furi_hal_lpuart_tx(const uint8_t* buffer, size_t buffer_size); + +void furi_hal_lpuart_set_irq_cb(void (*cb)(UartIrqEvent ev, uint8_t data)); + +#ifdef __cplusplus +} +#endif 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 09efbec3..e643fe57 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c +++ b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc.c @@ -1,6 +1,5 @@ #include "furi-hal-version.h" #include "furi-hal-usb_i.h" -#include "furi-hal-vcp_i.h" #include "furi-hal-usb-cdc_i.h" #include @@ -17,6 +16,8 @@ #define CDC_NTF_SZ 0x08 +#define IF_NUM_MAX 2 + struct CdcIadDescriptor { struct usb_iad_descriptor comm_iad; struct usb_interface_descriptor comm; @@ -343,12 +344,8 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = { }, }; -static struct usb_cdc_line_coding cdc_line = { - .dwDTERate = 38400, - .bCharFormat = USB_CDC_1_STOP_BITS, - .bParityType = USB_CDC_NO_PARITY, - .bDataBits = 8, -}; +static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; + static void cdc_init(usbd_device* dev, struct UsbInterface* intf); static void cdc_deinit(usbd_device *dev); static void cdc_on_wakeup(usbd_device *dev); @@ -358,6 +355,7 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg); static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback); static usbd_device* usb_dev; static struct UsbInterface* cdc_if_cur = NULL; +static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; struct UsbInterface usb_cdc_single = { .init = cdc_init, @@ -429,6 +427,17 @@ static void cdc_deinit(usbd_device *dev) { cdc_if_cur = NULL; } +void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb) { + if (if_num < 2) + callbacks[if_num] = cb; +} + +struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num) { + if (if_num < 2) + return &cdc_config[if_num]; + return NULL; +} + void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) { if (if_num == 0) usbd_ep_write(usb_dev, CDC0_TXD_EP, buf, len); @@ -444,25 +453,47 @@ int32_t furi_hal_cdc_receive(uint8_t if_num, uint8_t* buf, uint16_t max_len) { } static void cdc_on_wakeup(usbd_device *dev) { - furi_hal_vcp_on_usb_resume(); + for (uint8_t i = 0; i < IF_NUM_MAX; i++) { + if (callbacks[i] != NULL) { + if (callbacks[i]->state_callback != NULL) + callbacks[i]->state_callback(1); + } + } } static void cdc_on_suspend(usbd_device *dev) { - furi_hal_vcp_on_usb_suspend(); + for (uint8_t i = 0; i < IF_NUM_MAX; i++) { + if (callbacks[i] != NULL) { + if (callbacks[i]->state_callback != NULL) + callbacks[i]->state_callback(0); + } + } } static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { + uint8_t if_num = 0; if (ep == CDC0_RXD_EP) - furi_hal_vcp_on_cdc_rx(0); + if_num = 0; else - furi_hal_vcp_on_cdc_rx(1); + if_num = 1; + + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->rx_ep_callback != NULL) + callbacks[if_num]->rx_ep_callback(); + } } static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { + uint8_t if_num = 0; if (ep == CDC0_TXD_EP) - furi_hal_vcp_on_cdc_tx_complete(0); + if_num = 0; else - furi_hal_vcp_on_cdc_tx_complete(1); + if_num = 1; + + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->tx_ep_callback != NULL) + callbacks[if_num]->tx_ep_callback(); + } } static void cdc_txrx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { @@ -494,13 +525,15 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) { return usbd_ack; case 1: /* configuring device */ - if ((CDC0_TXD_EP & 0x7F) != (CDC0_RXD_EP & 0x7F)) { + if ((CDC0_TXD_EP & 0x7F) != (CDC0_RXD_EP & 0x7F)) { + // 2x unidirectional endpoint mode with dualbuf usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK | USB_EPTYPE_DBLBUF, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ); usbd_reg_endpoint(dev, CDC0_RXD_EP, cdc_rx_ep_callback); usbd_reg_endpoint(dev, CDC0_TXD_EP, cdc_tx_ep_callback); - } else { + } else { + // 1x bidirectional endpoint mode usbd_ep_config(dev, CDC0_RXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_TXD_EP, USB_EPTYPE_BULK, CDC_DATA_SZ); usbd_ep_config(dev, CDC0_NTF_EP, USB_EPTYPE_INTERRUPT, CDC_NTF_SZ); @@ -532,20 +565,33 @@ static usbd_respond cdc_ep_config (usbd_device *dev, uint8_t cfg) { } /* Control requests handler */ -static usbd_respond cdc_control (usbd_device *dev, usbd_ctlreq *req, usbd_rqc_callback *callback) { +static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { /* CDC control requests */ - if (((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) - && req->wIndex == 0 ) { - switch (req->bRequest) { + uint8_t if_num = 0; + if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == (USB_REQ_INTERFACE | USB_REQ_CLASS) + && (req->wIndex == 0 || req->wIndex == 2)) { + if (req->wIndex == 0) + if_num = 0; + else + if_num = 1; + + switch(req->bRequest) { case USB_CDC_SET_CONTROL_LINE_STATE: - furi_hal_vcp_on_cdc_control_line(req->wValue); + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->ctrl_line_callback != NULL) + callbacks[if_num]->ctrl_line_callback(req->wValue); + } return usbd_ack; case USB_CDC_SET_LINE_CODING: - memcpy(&cdc_line, req->data, sizeof(cdc_line)); + memcpy(&cdc_config[if_num], req->data, sizeof(cdc_config[0])); + if (callbacks[if_num] != NULL) { + if (callbacks[if_num]->config_callback != NULL) + callbacks[if_num]->config_callback(&cdc_config[if_num]); + } return usbd_ack; case USB_CDC_GET_LINE_CODING: - dev->status.data_ptr = &cdc_line; - dev->status.data_count = sizeof(cdc_line); + dev->status.data_ptr = &cdc_config[if_num]; + dev->status.data_count = sizeof(cdc_config[0]); return usbd_ack; default: return usbd_fail; diff --git a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc_i.h b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc_i.h index 636b3c04..3b181582 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-usb-cdc_i.h +++ b/firmware/targets/f7/furi-hal/furi-hal-usb-cdc_i.h @@ -1,6 +1,21 @@ #pragma once -#define CDC_DATA_SZ 0x40 +#include +#include "usb_cdc.h" + +#define CDC_DATA_SZ 64 + +typedef struct { + void (*tx_ep_callback)(void); + void (*rx_ep_callback)(void); + void (*state_callback)(uint8_t state); + void (*ctrl_line_callback)(uint8_t state); + void (*config_callback)(struct usb_cdc_line_coding* config); +} CdcCallbacks; + +void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb); + +struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num); void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len); diff --git a/firmware/targets/f7/furi-hal/furi-hal-usb.c b/firmware/targets/f7/furi-hal/furi-hal-usb.c index 5442975f..8c78eb8b 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-usb.c +++ b/firmware/targets/f7/furi-hal/furi-hal-usb.c @@ -1,7 +1,6 @@ #include "furi-hal-version.h" #include "furi-hal-usb_i.h" #include "furi-hal-usb.h" -#include "furi-hal-vcp_i.h" #include #include "usb.h" @@ -34,6 +33,7 @@ struct UsbCfg{ UsbMode mode_cur; UsbMode mode_next; bool enabled; + bool connected; } usb_config; static void furi_hal_usb_tmr_cb(void* context); @@ -158,11 +158,15 @@ static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16 } static void susp_evt(usbd_device *dev, uint8_t event, uint8_t ep) { - if (usb_if_modes[usb_config.mode_cur] != NULL) + if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == true)) { + usb_config.connected = false; usb_if_modes[usb_config.mode_cur]->suspend(&udev); + } } static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep) { - if (usb_if_modes[usb_config.mode_cur] != NULL) + if ((usb_if_modes[usb_config.mode_cur] != NULL) && (usb_config.connected == false)) { + usb_config.connected = true; usb_if_modes[usb_config.mode_cur]->wakeup(&udev); + } } diff --git a/firmware/targets/f7/furi-hal/furi-hal-vcp.c b/firmware/targets/f7/furi-hal/furi-hal-vcp.c index e276c4a3..b975495d 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-vcp.c +++ b/firmware/targets/f7/furi-hal/furi-hal-vcp.c @@ -1,4 +1,3 @@ -#include #include #include @@ -7,6 +6,7 @@ #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 VCP_IF_NUM 0 typedef struct { volatile bool connected; @@ -17,6 +17,19 @@ typedef struct { osSemaphoreId_t tx_semaphore; } FuriHalVcp; +static void vcp_on_cdc_tx_complete(); +static void vcp_on_cdc_rx(); +static void vcp_state_callback(uint8_t state); +static void vcp_on_cdc_control_line(uint8_t state); + +static CdcCallbacks cdc_cb = { + vcp_on_cdc_tx_complete, + vcp_on_cdc_rx, + vcp_state_callback, + vcp_on_cdc_control_line, + NULL, +}; + static FuriHalVcp* furi_hal_vcp = NULL; static const uint8_t ascii_soh = 0x01; @@ -34,9 +47,22 @@ void furi_hal_vcp_init() { furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL); + furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); + FURI_LOG_I("FuriHalVcp", "Init OK"); } +void furi_hal_vcp_enable() { + furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); + furi_hal_vcp->connected = true; +} + +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); @@ -68,24 +94,22 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { batch_size = APP_TX_DATA_SIZE; } - furi_hal_cdc_send(0, (uint8_t*)buffer, batch_size); + furi_hal_cdc_send(VCP_IF_NUM, (uint8_t*)buffer, batch_size); size -= batch_size; buffer += batch_size; } } -void furi_hal_vcp_on_usb_resume() { - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); -} - -void furi_hal_vcp_on_usb_suspend() { - if (furi_hal_vcp->connected) { +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); } } -void furi_hal_vcp_on_cdc_control_line(uint8_t state) { +static void vcp_on_cdc_control_line(uint8_t state) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; // bit 0: DTR state, bit 1: RTS state @@ -110,30 +134,26 @@ void furi_hal_vcp_on_cdc_control_line(uint8_t state) { portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } -void furi_hal_vcp_on_cdc_rx(uint8_t if_num) { +static void vcp_on_cdc_rx() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - if (if_num == 0) { - 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(0, vcp_rx_buf, max_len); + 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; - }; - } + 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); } -void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num) { - if (if_num == 0) - osSemaphoreRelease(furi_hal_vcp->tx_semaphore); +static void vcp_on_cdc_tx_complete() { + osSemaphoreRelease(furi_hal_vcp->tx_semaphore); } - diff --git a/firmware/targets/f7/furi-hal/furi-hal-vcp_i.h b/firmware/targets/f7/furi-hal/furi-hal-vcp_i.h deleted file mode 100644 index 9c8ccd73..00000000 --- a/firmware/targets/f7/furi-hal/furi-hal-vcp_i.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -void furi_hal_vcp_on_usb_resume(); - -void furi_hal_vcp_on_usb_suspend(); - -void furi_hal_vcp_on_cdc_control_line(uint8_t state); - -void furi_hal_vcp_on_cdc_rx(uint8_t if_num); - -void furi_hal_vcp_on_cdc_tx_complete(uint8_t if_num); diff --git a/firmware/targets/f7/furi-hal/furi-hal.c b/firmware/targets/f7/furi-hal/furi-hal.c index 8f680385..bcddc6e0 100644 --- a/firmware/targets/f7/furi-hal/furi-hal.c +++ b/firmware/targets/f7/furi-hal/furi-hal.c @@ -33,9 +33,9 @@ void furi_hal_init() { furi_hal_crypto_init(); // VCP + USB - furi_hal_vcp_init(); furi_hal_usb_init(); furi_hal_usb_set_config(UsbModeVcpSingle); + furi_hal_vcp_init(); FURI_LOG_I("HAL", "USB OK"); furi_hal_i2c_init(); diff --git a/firmware/targets/f7/target.mk b/firmware/targets/f7/target.mk index c1f2e82d..be91cee9 100644 --- a/firmware/targets/f7/target.mk +++ b/firmware/targets/f7/target.mk @@ -72,6 +72,7 @@ C_SOURCES += \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_spi.c \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_tim.c \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_usart.c \ + $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_lpuart.c \ $(CUBE_DIR)/Drivers/STM32WBxx_HAL_Driver/Src/stm32wbxx_ll_utils.c # FreeRTOS diff --git a/firmware/targets/furi-hal-include/furi-hal-vcp.h b/firmware/targets/furi-hal-include/furi-hal-vcp.h index 7d4803ae..996232cf 100644 --- a/firmware/targets/furi-hal-include/furi-hal-vcp.h +++ b/firmware/targets/furi-hal-include/furi-hal-vcp.h @@ -17,6 +17,14 @@ extern "C" { */ 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 diff --git a/firmware/targets/furi-hal-include/furi-hal.h b/firmware/targets/furi-hal-include/furi-hal.h index 01d38e1e..757dd121 100644 --- a/firmware/targets/furi-hal-include/furi-hal.h +++ b/firmware/targets/furi-hal-include/furi-hal.h @@ -36,6 +36,7 @@ template struct STOP_EXTERNING_ME {}; #include "furi-hal-usb.h" #include "furi-hal-usb-hid.h" #include "furi-hal-compress.h" +#include "furi-hal-lpuart.h" /** Init furi-hal */ void furi_hal_init();