USB-UART: New GUI (#826)
* USB-UART: new gui * Furi: use furi_console for logging instead of printf. * CDC: calling open/close callbacks on interface change * fix vcp_tx block on disconnect * USB mode set by struct pointer * FuriHal: proper event sequence on vcp reconnect * disable debug prints * HAL: add context to UART IRQ's * Context usage in UART IRQ and CDC callbacks * USB-UART: geting rid of baudrate limitations * FuriHal: remove struct pollutant in usb api. Co-authored-by: あく <alleteam@gmail.com> Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
This commit is contained in:
		| @@ -1,5 +1,6 @@ | ||||
| #include "furi-hal-version.h" | ||||
| #include "furi-hal-usb_i.h" | ||||
| #include "furi-hal-usb.h" | ||||
| #include "furi-hal-usb-cdc_i.h" | ||||
| #include <furi.h> | ||||
|  | ||||
| @@ -347,7 +348,7 @@ static const struct CdcConfigDescriptorDual cdc_cfg_desc_dual = { | ||||
| static struct usb_cdc_line_coding cdc_config[IF_NUM_MAX] = {}; | ||||
| static uint8_t cdc_ctrl_line_state[IF_NUM_MAX]; | ||||
|  | ||||
| static void cdc_init(usbd_device* dev, struct UsbInterface* intf); | ||||
| static void cdc_init(usbd_device* dev, UsbInterface* intf); | ||||
| static void cdc_deinit(usbd_device *dev); | ||||
| static void cdc_on_wakeup(usbd_device *dev); | ||||
| static void cdc_on_suspend(usbd_device *dev); | ||||
| @@ -355,10 +356,12 @@ static void cdc_on_suspend(usbd_device *dev); | ||||
| 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 UsbInterface* cdc_if_cur = NULL; | ||||
| static bool connected = false; | ||||
| static CdcCallbacks* callbacks[IF_NUM_MAX] = {NULL}; | ||||
| static void* cb_ctx[IF_NUM_MAX]; | ||||
|  | ||||
| struct UsbInterface usb_cdc_single = { | ||||
| UsbInterface usb_cdc_single = { | ||||
|     .init = cdc_init, | ||||
|     .deinit = cdc_deinit, | ||||
|     .wakeup = cdc_on_wakeup, | ||||
| @@ -373,7 +376,7 @@ struct UsbInterface usb_cdc_single = { | ||||
|     .cfg_descr = (void*)&cdc_cfg_desc_single, | ||||
| }; | ||||
|  | ||||
| struct UsbInterface usb_cdc_dual = { | ||||
| UsbInterface usb_cdc_dual = { | ||||
|     .init = cdc_init, | ||||
|     .deinit = cdc_deinit, | ||||
|     .wakeup = cdc_on_wakeup, | ||||
| @@ -388,7 +391,7 @@ struct UsbInterface usb_cdc_dual = { | ||||
|     .cfg_descr = (void*)&cdc_cfg_desc_dual, | ||||
| }; | ||||
|  | ||||
| static void cdc_init(usbd_device* dev, struct UsbInterface* intf) { | ||||
| static void cdc_init(usbd_device* dev, UsbInterface* intf) { | ||||
|     usb_dev = dev; | ||||
|     cdc_if_cur = intf; | ||||
|      | ||||
| @@ -428,21 +431,35 @@ 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; | ||||
| void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context) { | ||||
|     furi_assert(if_num < IF_NUM_MAX); | ||||
|  | ||||
|     if (callbacks[if_num] != NULL) { | ||||
|         if (callbacks[if_num]->state_callback != NULL) { | ||||
|             if (connected == true) | ||||
|                 callbacks[if_num]->state_callback(cb_ctx[if_num], 0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     callbacks[if_num] = cb; | ||||
|     cb_ctx[if_num] = context; | ||||
|  | ||||
|     if (callbacks[if_num] != NULL) { | ||||
|         if (callbacks[if_num]->state_callback != NULL) { | ||||
|             if (connected == true) | ||||
|                 callbacks[if_num]->state_callback(cb_ctx[if_num], 1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| 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; | ||||
|     furi_assert(if_num < IF_NUM_MAX); | ||||
|     return &cdc_config[if_num]; | ||||
| } | ||||
|  | ||||
| uint8_t furi_hal_cdc_get_ctrl_line_state(uint8_t if_num) { | ||||
|     if (if_num < 2) | ||||
|         return cdc_ctrl_line_state[if_num]; | ||||
|     return 0; | ||||
|     furi_assert(if_num < IF_NUM_MAX); | ||||
|     return cdc_ctrl_line_state[if_num]; | ||||
| } | ||||
|  | ||||
| void furi_hal_cdc_send(uint8_t if_num, uint8_t* buf, uint16_t len) { | ||||
| @@ -462,20 +479,22 @@ 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) { | ||||
|     connected = true; | ||||
|     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); | ||||
|                 callbacks[i]->state_callback(cb_ctx[i], 1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void cdc_on_suspend(usbd_device *dev) { | ||||
|     connected = false; | ||||
|     for (uint8_t i = 0; i < IF_NUM_MAX; i++) { | ||||
|         cdc_ctrl_line_state[i] = 0; | ||||
|         if (callbacks[i] != NULL) { | ||||
|             if (callbacks[i]->state_callback != NULL) | ||||
|                 callbacks[i]->state_callback(0); | ||||
|                 callbacks[i]->state_callback(cb_ctx[i], 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -489,7 +508,7 @@ static void cdc_rx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { | ||||
|      | ||||
|     if (callbacks[if_num] != NULL) { | ||||
|         if (callbacks[if_num]->rx_ep_callback != NULL) | ||||
|             callbacks[if_num]->rx_ep_callback(); | ||||
|             callbacks[if_num]->rx_ep_callback(cb_ctx[if_num]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -502,7 +521,7 @@ static void cdc_tx_ep_callback (usbd_device *dev, uint8_t event, uint8_t ep) { | ||||
|      | ||||
|     if (callbacks[if_num] != NULL) { | ||||
|         if (callbacks[if_num]->tx_ep_callback != NULL) | ||||
|             callbacks[if_num]->tx_ep_callback(); | ||||
|             callbacks[if_num]->tx_ep_callback(cb_ctx[if_num]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -590,14 +609,14 @@ static usbd_respond cdc_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_cal | ||||
|             if (callbacks[if_num] != NULL) { | ||||
|                 cdc_ctrl_line_state[if_num] = req->wValue; | ||||
|                 if (callbacks[if_num]->ctrl_line_callback != NULL) | ||||
|                     callbacks[if_num]->ctrl_line_callback(cdc_ctrl_line_state[if_num]); | ||||
|                     callbacks[if_num]->ctrl_line_callback(cb_ctx[if_num], cdc_ctrl_line_state[if_num]); | ||||
|             } | ||||
|             return usbd_ack; | ||||
|         case USB_CDC_SET_LINE_CODING: | ||||
|             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]); | ||||
|                     callbacks[if_num]->config_callback(cb_ctx[if_num], &cdc_config[if_num]); | ||||
|             } | ||||
|             return usbd_ack; | ||||
|         case USB_CDC_GET_LINE_CODING: | ||||
|   | ||||
| @@ -6,14 +6,14 @@ | ||||
| #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); | ||||
|     void (*tx_ep_callback)(void* context); | ||||
|     void (*rx_ep_callback)(void* context); | ||||
|     void (*state_callback)(void* context, uint8_t state); | ||||
|     void (*ctrl_line_callback)(void* context, uint8_t state); | ||||
|     void (*config_callback)(void* context, struct usb_cdc_line_coding* config); | ||||
| } CdcCallbacks; | ||||
|  | ||||
| void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb); | ||||
| void furi_hal_cdc_set_callbacks(uint8_t if_num, CdcCallbacks* cb, void* context); | ||||
|  | ||||
| struct usb_cdc_line_coding* furi_hal_cdc_get_port_settings(uint8_t if_num); | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include "furi-hal-version.h" | ||||
| #include "furi-hal-usb_i.h" | ||||
| #include "furi-hal-usb.h" | ||||
| #include <furi.h> | ||||
|  | ||||
| #include "usb.h" | ||||
| @@ -182,7 +183,7 @@ static struct HidReport { | ||||
|     struct HidReportMouse mouse; | ||||
| } __attribute__((packed)) hid_report; | ||||
|  | ||||
| static void hid_init(usbd_device* dev, struct UsbInterface* intf); | ||||
| static void hid_init(usbd_device* dev, UsbInterface* intf); | ||||
| static void hid_deinit(usbd_device *dev); | ||||
| static void hid_on_wakeup(usbd_device *dev); | ||||
| static void hid_on_suspend(usbd_device *dev); | ||||
| @@ -254,7 +255,7 @@ bool furi_hal_hid_mouse_scroll(int8_t delta) { | ||||
|     return state; | ||||
| } | ||||
|  | ||||
| struct UsbInterface usb_hid = { | ||||
| UsbInterface usb_hid = { | ||||
|     .init = hid_init, | ||||
|     .deinit = hid_deinit, | ||||
|     .wakeup = hid_on_wakeup, | ||||
| @@ -269,7 +270,7 @@ struct UsbInterface usb_hid = { | ||||
|     .cfg_descr = (void*)&hid_cfg_desc, | ||||
| }; | ||||
|  | ||||
| static void hid_init(usbd_device* dev, struct UsbInterface* intf) { | ||||
| static void hid_init(usbd_device* dev, UsbInterface* intf) { | ||||
|     if (hid_semaphore == NULL) | ||||
|         hid_semaphore = osSemaphoreNew(1, 1, NULL); | ||||
|     usb_dev = dev; | ||||
|   | ||||
| @@ -9,17 +9,8 @@ | ||||
|  | ||||
| #define USB_RECONNECT_DELAY 500 | ||||
|  | ||||
| extern struct UsbInterface usb_cdc_single; | ||||
| extern struct UsbInterface usb_cdc_dual; | ||||
| extern struct UsbInterface usb_hid; | ||||
|  | ||||
| static struct UsbInterface* const usb_if_modes[UsbModesNum] = { | ||||
|     NULL, | ||||
|     &usb_cdc_single, | ||||
|     &usb_cdc_dual, | ||||
|     &usb_hid, | ||||
|     NULL,//&usb_hid_u2f, | ||||
| }; | ||||
| static UsbInterface* usb_if_cur; | ||||
| static UsbInterface* usb_if_next; | ||||
|  | ||||
| static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | ||||
|  | ||||
| @@ -32,8 +23,6 @@ static void wkup_evt(usbd_device *dev, uint8_t event, uint8_t ep); | ||||
|  | ||||
| struct UsbCfg{ | ||||
|     osTimerId_t reconnect_tmr; | ||||
|     UsbMode mode_cur; | ||||
|     UsbMode mode_next; | ||||
|     bool enabled; | ||||
|     bool connected; | ||||
| } usb_config; | ||||
| @@ -69,30 +58,30 @@ void furi_hal_usb_init(void) { | ||||
|     FURI_LOG_I(TAG, "Init OK"); | ||||
| } | ||||
|  | ||||
| void furi_hal_usb_set_config(UsbMode new_mode) { | ||||
|     if (new_mode != usb_config.mode_cur) { | ||||
| void furi_hal_usb_set_config(UsbInterface* new_if) { | ||||
|     if (new_if != usb_if_cur) { | ||||
|         if (usb_config.enabled) { | ||||
|             usb_config.mode_next = new_mode; | ||||
|             usb_if_next = new_if; | ||||
|             if (usb_config.reconnect_tmr == NULL) | ||||
|                 usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); | ||||
|             furi_hal_usb_disable(); | ||||
|             osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); | ||||
|         } | ||||
|         else { | ||||
|             if (usb_if_modes[usb_config.mode_cur] != NULL) | ||||
|                 usb_if_modes[usb_config.mode_cur]->deinit(&udev); | ||||
|             if (usb_if_modes[new_mode] != NULL) { | ||||
|                 usb_if_modes[new_mode]->init(&udev, usb_if_modes[new_mode]); | ||||
|                 FURI_LOG_I(TAG, "USB mode change %u -> %u", usb_config.mode_cur, new_mode); | ||||
|             if (usb_if_cur != NULL) | ||||
|                 usb_if_cur->deinit(&udev); | ||||
|             if (new_if != NULL) { | ||||
|                 new_if->init(&udev, new_if); | ||||
|                 FURI_LOG_I(TAG, "USB mode change"); | ||||
|                 usb_config.enabled = true; | ||||
|                 usb_config.mode_cur = new_mode; | ||||
|                 usb_if_cur = new_if; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| UsbMode furi_hal_usb_get_config() { | ||||
|     return usb_config.mode_cur; | ||||
| UsbInterface* furi_hal_usb_get_config() { | ||||
|     return usb_if_cur; | ||||
| } | ||||
|  | ||||
| void furi_hal_usb_disable() { | ||||
| @@ -105,7 +94,7 @@ void furi_hal_usb_disable() { | ||||
| } | ||||
|  | ||||
| void furi_hal_usb_enable() { | ||||
|     if ((!usb_config.enabled) && (usb_if_modes[usb_config.mode_cur] != NULL)) { | ||||
|     if ((!usb_config.enabled) && (usb_if_cur != NULL)) { | ||||
|         usbd_connect(&udev, true); | ||||
|         usb_config.enabled = true; | ||||
|         FURI_LOG_I(TAG, "USB Enable"); | ||||
| @@ -113,35 +102,35 @@ void furi_hal_usb_enable() { | ||||
| } | ||||
|  | ||||
| static void furi_hal_usb_tmr_cb(void* context) { | ||||
|     furi_hal_usb_set_config(usb_config.mode_next); | ||||
|     furi_hal_usb_set_config(usb_if_next); | ||||
| } | ||||
|  | ||||
| /* Get device / configuration descriptors */ | ||||
| static usbd_respond usb_descriptor_get (usbd_ctlreq *req, void **address, uint16_t *length) { | ||||
| static usbd_respond usb_descriptor_get(usbd_ctlreq *req, void **address, uint16_t *length) { | ||||
|     const uint8_t dtype = req->wValue >> 8; | ||||
|     const uint8_t dnumber = req->wValue & 0xFF; | ||||
|     const void* desc; | ||||
|     uint16_t len = 0; | ||||
|     if (usb_if_modes[usb_config.mode_cur] == NULL) | ||||
|     if (usb_if_cur == NULL) | ||||
|         return usbd_fail; | ||||
|  | ||||
|     switch (dtype) { | ||||
|     case USB_DTYPE_DEVICE: | ||||
|         desc = usb_if_modes[usb_config.mode_cur]->dev_descr; | ||||
|         desc = usb_if_cur->dev_descr; | ||||
|         break; | ||||
|     case USB_DTYPE_CONFIGURATION: | ||||
|         desc = usb_if_modes[usb_config.mode_cur]->cfg_descr; | ||||
|         len = ((struct usb_string_descriptor*)(usb_if_modes[usb_config.mode_cur]->cfg_descr))->wString[0]; | ||||
|         desc = usb_if_cur->cfg_descr; | ||||
|         len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0]; | ||||
|         break; | ||||
|     case USB_DTYPE_STRING: | ||||
|         if (dnumber == UsbDevLang) { | ||||
|             desc = &dev_lang_desc; | ||||
|         } else if (dnumber == UsbDevManuf) { | ||||
|             desc = usb_if_modes[usb_config.mode_cur]->str_manuf_descr; | ||||
|             desc = usb_if_cur->str_manuf_descr; | ||||
|         } else if (dnumber == UsbDevProduct) { | ||||
|             desc = usb_if_modes[usb_config.mode_cur]->str_prod_descr; | ||||
|             desc = usb_if_cur->str_prod_descr; | ||||
|         } else if (dnumber == UsbDevSerial) { | ||||
|             desc = usb_if_modes[usb_config.mode_cur]->str_serial_descr; | ||||
|             desc = usb_if_cur->str_serial_descr; | ||||
|         } else  | ||||
|             return usbd_fail; | ||||
|         break; | ||||
| @@ -160,15 +149,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) && (usb_config.connected == true)) { | ||||
|     if ((usb_if_cur != NULL) && (usb_config.connected == true)) { | ||||
|         usb_config.connected = false; | ||||
|         usb_if_modes[usb_config.mode_cur]->suspend(&udev); | ||||
|         usb_if_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) && (usb_config.connected == false)) { | ||||
|     if ((usb_if_cur != NULL) && (usb_config.connected == false)) { | ||||
|         usb_config.connected = true; | ||||
|         usb_if_modes[usb_config.mode_cur]->wakeup(&udev); | ||||
|         usb_if_cur->wakeup(&udev); | ||||
|     }  | ||||
| } | ||||
|   | ||||
| @@ -11,18 +11,3 @@ enum UsbDevDescStr{ | ||||
|     UsbDevProduct   = 2, | ||||
|     UsbDevSerial    = 3, | ||||
| }; | ||||
|  | ||||
| struct UsbInterface { | ||||
|     void (*init)(usbd_device *dev, struct UsbInterface* intf); | ||||
|     void (*deinit)(usbd_device *dev); | ||||
|     void (*wakeup)(usbd_device *dev); | ||||
|     void (*suspend)(usbd_device *dev);     | ||||
|  | ||||
|     struct usb_device_descriptor* dev_descr; | ||||
|  | ||||
|     void* str_manuf_descr; | ||||
|     void* str_prod_descr; | ||||
|     void* str_serial_descr; | ||||
|  | ||||
|     void* cfg_descr; | ||||
| }; | ||||
|   | ||||
| @@ -13,17 +13,17 @@ | ||||
|  | ||||
| typedef enum { | ||||
|     VcpEvtReserved      = (1 << 0), // Reserved for StreamBuffer internal event | ||||
|     VcpEvtConnect       = (1 << 1), | ||||
|     VcpEvtDisconnect    = (1 << 2), | ||||
|     VcpEvtEnable        = (1 << 3), | ||||
|     VcpEvtDisable       = (1 << 4), | ||||
|     VcpEvtRx            = (1 << 5), | ||||
|     VcpEvtTx            = (1 << 6), | ||||
|     VcpEvtStreamRx      = (1 << 7), | ||||
|     VcpEvtStreamTx      = (1 << 8), | ||||
|     VcpEvtEnable        = (1 << 1), | ||||
|     VcpEvtDisable       = (1 << 2), | ||||
|     VcpEvtConnect       = (1 << 3), | ||||
|     VcpEvtDisconnect    = (1 << 4), | ||||
|     VcpEvtStreamRx      = (1 << 5), | ||||
|     VcpEvtRx            = (1 << 6), | ||||
|     VcpEvtStreamTx      = (1 << 7), | ||||
|     VcpEvtTx            = (1 << 8), | ||||
| } WorkerEvtFlags; | ||||
|  | ||||
| #define VCP_THREAD_FLAG_ALL (VcpEvtConnect | VcpEvtDisconnect | VcpEvtEnable | VcpEvtDisable | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | VcpEvtStreamTx) | ||||
| #define VCP_THREAD_FLAG_ALL (VcpEvtEnable | VcpEvtDisable | VcpEvtConnect | VcpEvtDisconnect | VcpEvtRx | VcpEvtTx | VcpEvtStreamRx | VcpEvtStreamTx) | ||||
|  | ||||
| typedef struct { | ||||
|     FuriThread* thread; | ||||
| @@ -37,10 +37,10 @@ typedef struct { | ||||
| } FuriHalVcp; | ||||
|  | ||||
| static int32_t vcp_worker(void* context); | ||||
| 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_cdc_tx_complete(void* context); | ||||
| static void vcp_on_cdc_rx(void* context); | ||||
| static void vcp_state_callback(void* context, uint8_t state); | ||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state); | ||||
|  | ||||
| static CdcCallbacks cdc_cb = { | ||||
|     vcp_on_cdc_tx_complete, | ||||
| @@ -76,93 +76,19 @@ static int32_t vcp_worker(void* context) { | ||||
|     bool tx_idle = false; | ||||
|     size_t missed_rx = 0; | ||||
|  | ||||
|     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); | ||||
|     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||
|  | ||||
|     while (1) { | ||||
|         uint32_t flags = osThreadFlagsWait(VCP_THREAD_FLAG_ALL, osFlagsWaitAny, osWaitForever); | ||||
|         furi_assert((flags & osFlagsError) == 0); | ||||
|  | ||||
|         // New data received | ||||
|         if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_puts("VCP StreamRx\r\n"); | ||||
| #endif | ||||
|             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||
|                 flags |= VcpEvtRx; | ||||
|                 missed_rx--; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Rx buffer was read, maybe there is enough space for new data? | ||||
|         if((flags & VcpEvtRx)) { | ||||
|             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||
|                 int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|                 furi_hal_console_printf("VCP Rx %d\r\n", len); | ||||
| #endif | ||||
|                 if (len > 0) { | ||||
|                     furi_check(xStreamBufferSend(vcp->rx_stream, vcp->data_buffer, len, osWaitForever) == len); | ||||
|                 } | ||||
|             } else { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|                 furi_hal_console_puts("VCP Rx missed\r\n"); | ||||
| #endif | ||||
|                 missed_rx++; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // New data in Tx buffer | ||||
|         if((flags & VcpEvtStreamTx) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_puts("VCP StreamTx\r\n"); | ||||
| #endif | ||||
|             if (tx_idle) { | ||||
|                 flags |= VcpEvtTx; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // CDC write transfer done | ||||
|         if((flags & VcpEvtTx) && enabled) { | ||||
|             size_t len = xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_printf("VCP Tx %d\r\n", len); | ||||
| #endif | ||||
|             if (len > 0) { // Some data left in Tx buffer. Sending it now | ||||
|                 tx_idle = false; | ||||
|                 furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len); | ||||
|             } else { // There is nothing to send. Set flag to start next transfer instantly | ||||
|                 tx_idle = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // VCP session opened | ||||
|         if((flags & VcpEvtConnect) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_puts("VCP Connect\r\n"); | ||||
| #endif | ||||
|             if (vcp->connected == false) { | ||||
|                 vcp->connected = true; | ||||
|                 xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // VCP session closed | ||||
|         if((flags & VcpEvtDisconnect) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_puts("VCP Disconnect\r\n"); | ||||
| #endif | ||||
|             if (vcp->connected == true) { | ||||
|                 vcp->connected = false; | ||||
|                 xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // VCP enabled | ||||
|         if((flags & VcpEvtEnable) && !enabled){ | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_puts("VCP Enable\r\n"); | ||||
| #endif | ||||
|             furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb); | ||||
|             FURI_LOG_D(TAG, "Enable"); | ||||
| #endif             | ||||
|             flags |= VcpEvtTx; | ||||
|             furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||
|             enabled = true; | ||||
|             furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); // flush Rx buffer | ||||
|             if (furi_hal_cdc_get_ctrl_line_state(VCP_IF_NUM) & (1 << 0)) { | ||||
| @@ -173,13 +99,90 @@ static int32_t vcp_worker(void* context) { | ||||
|  | ||||
|         // VCP disabled | ||||
|         if((flags & VcpEvtDisable) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             furi_hal_console_puts("VCP Disable\r\n"); | ||||
| #endif | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG             | ||||
|             FURI_LOG_D(TAG, "Disable"); | ||||
| #endif             | ||||
|             enabled = false; | ||||
|             vcp->connected = false; | ||||
|             xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); | ||||
|             xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); | ||||
|         } | ||||
|  | ||||
|         // VCP session opened | ||||
|         if((flags & VcpEvtConnect) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG             | ||||
|             FURI_LOG_D(TAG, "Connect"); | ||||
| #endif             | ||||
|             if (vcp->connected == false) { | ||||
|                 vcp->connected = true; | ||||
|                 xStreamBufferSend(vcp->rx_stream, &ascii_soh, 1, osWaitForever); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // VCP session closed | ||||
|         if((flags & VcpEvtDisconnect) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG             | ||||
|             FURI_LOG_D(TAG, "Disconnect"); | ||||
| #endif             | ||||
|             if (vcp->connected == true) { | ||||
|                 vcp->connected = false; | ||||
|                 xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); | ||||
|                 xStreamBufferSend(vcp->rx_stream, &ascii_eot, 1, osWaitForever); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Rx buffer was read, maybe there is enough space for new data? | ||||
|         if((flags & VcpEvtStreamRx) && enabled && missed_rx > 0) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|             FURI_LOG_D(TAG, "StreamRx"); | ||||
| #endif | ||||
|             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||
|                 flags |= VcpEvtRx; | ||||
|                 missed_rx--; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // New data received | ||||
|         if((flags & VcpEvtRx)) { | ||||
|             if (xStreamBufferSpacesAvailable(vcp->rx_stream) >= USB_CDC_PKT_LEN) { | ||||
|                 int32_t len = furi_hal_cdc_receive(VCP_IF_NUM, vcp->data_buffer, USB_CDC_PKT_LEN); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG                 | ||||
|                 FURI_LOG_D(TAG, "Rx %d", len); | ||||
| #endif                 | ||||
|                 if (len > 0) { | ||||
|                     furi_check(xStreamBufferSend(vcp->rx_stream, vcp->data_buffer, len, osWaitForever) == len); | ||||
|                 } | ||||
|             } else { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG                 | ||||
|                 FURI_LOG_D(TAG, "Rx missed"); | ||||
| #endif                 | ||||
|                 missed_rx++; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // New data in Tx buffer | ||||
|         if((flags & VcpEvtStreamTx) && enabled) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG             | ||||
|             FURI_LOG_D(TAG, "StreamTx"); | ||||
| #endif             | ||||
|             if (tx_idle) { | ||||
|                 flags |= VcpEvtTx; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // CDC write transfer done | ||||
|         if((flags & VcpEvtTx) && enabled) { | ||||
|             size_t len = xStreamBufferReceive(vcp->tx_stream, vcp->data_buffer, USB_CDC_PKT_LEN, 0); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG             | ||||
|             FURI_LOG_D(TAG, "Tx %d", len); | ||||
| #endif             | ||||
|             if (len > 0) { // Some data left in Tx buffer. Sending it now | ||||
|                 tx_idle = false; | ||||
|                 furi_hal_cdc_send(VCP_IF_NUM, vcp->data_buffer, len); | ||||
|             } else { // There is nothing to send. Set flag to start next transfer instantly | ||||
|                 tx_idle = true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
| @@ -196,6 +199,10 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo | ||||
|     furi_assert(vcp); | ||||
|     furi_assert(buffer); | ||||
|  | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|     FURI_LOG_D(TAG, "rx %u start", size); | ||||
| #endif     | ||||
|  | ||||
|     size_t rx_cnt = 0; | ||||
|  | ||||
|     while (size > 0) { | ||||
| @@ -204,6 +211,9 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo | ||||
|             batch_size = VCP_RX_BUF_SIZE; | ||||
|  | ||||
|         size_t len = xStreamBufferReceive(vcp->rx_stream, buffer, batch_size, timeout); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG         | ||||
|         FURI_LOG_D(TAG, "%u ", batch_size); | ||||
| #endif         | ||||
|         if (len == 0) | ||||
|             break; | ||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamRx); | ||||
| @@ -211,6 +221,10 @@ size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeo | ||||
|         buffer += len; | ||||
|         rx_cnt += len; | ||||
|     } | ||||
|  | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|     FURI_LOG_D(TAG, "rx %u end", size); | ||||
| #endif     | ||||
|     return rx_cnt; | ||||
| } | ||||
|  | ||||
| @@ -223,34 +237,40 @@ void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) { | ||||
|     furi_assert(vcp); | ||||
|     furi_assert(buffer); | ||||
|  | ||||
|     while (size > 0) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|     FURI_LOG_D(TAG, "tx %u start", size); | ||||
| #endif     | ||||
|  | ||||
|     while (size > 0 && vcp->connected) { | ||||
|         size_t batch_size = size; | ||||
|         if (batch_size > VCP_TX_BUF_SIZE) | ||||
|             batch_size = VCP_TX_BUF_SIZE; | ||||
|         if (batch_size > USB_CDC_PKT_LEN) | ||||
|             batch_size = USB_CDC_PKT_LEN; | ||||
|  | ||||
|         xStreamBufferSend(vcp->tx_stream, buffer, batch_size, osWaitForever); | ||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtStreamTx); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|         FURI_LOG_D(TAG, "%u ", batch_size); | ||||
| #endif         | ||||
|  | ||||
|         size -= batch_size; | ||||
|         buffer += batch_size; | ||||
|     } | ||||
|  | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|     FURI_LOG_D(TAG, "tx %u end", size); | ||||
| #endif     | ||||
| } | ||||
|  | ||||
| static void vcp_state_callback(uint8_t state) { | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|     furi_hal_console_puts("VCP State\r\n"); | ||||
| #endif | ||||
| static void vcp_state_callback(void* context, uint8_t state) { | ||||
|     if (state == 0) { | ||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtDisconnect); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void vcp_on_cdc_control_line(uint8_t state) { | ||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state) { | ||||
|     // bit 0: DTR state, bit 1: RTS state | ||||
|     bool dtr = state & (1 << 0); | ||||
| #ifdef FURI_HAL_USB_VCP_DEBUG | ||||
|     furi_hal_console_puts("VCP CtrlLine\r\n"); | ||||
| #endif | ||||
|  | ||||
|     if (dtr == true) { | ||||
|         osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtConnect); | ||||
|     } else { | ||||
| @@ -258,12 +278,12 @@ static void vcp_on_cdc_control_line(uint8_t state) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void vcp_on_cdc_rx() { | ||||
| static void vcp_on_cdc_rx(void* context) { | ||||
|     uint32_t ret = osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtRx); | ||||
|     furi_assert((ret & osFlagsError) == 0); | ||||
| } | ||||
|  | ||||
| static void vcp_on_cdc_tx_complete() { | ||||
| static void vcp_on_cdc_tx_complete(void* context) { | ||||
|     osThreadFlagsSet(furi_thread_get_thread_id(vcp->thread), VcpEvtTx); | ||||
| } | ||||
|  | ||||
| @@ -271,4 +291,3 @@ bool furi_hal_vcp_is_connected(void) { | ||||
|     furi_assert(vcp); | ||||
|     return vcp->connected; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -40,7 +40,7 @@ void furi_hal_init() { | ||||
|  | ||||
|     // VCP + USB | ||||
|     furi_hal_usb_init(); | ||||
|     furi_hal_usb_set_config(UsbModeVcpSingle); | ||||
|     furi_hal_usb_set_config(&usb_cdc_single); | ||||
|     furi_hal_vcp_init(); | ||||
|     FURI_LOG_I(TAG, "USB OK"); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user