[FL-2256] USB Service (#998)
* usb service, removed power observer * fix usb restart after disable * remove usb service from apps * Applcations: remove dead extern * New thread naming scheme for drivers * New thread symbol naming scheme for drivers Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									a37f1d0f6f
								
							
						
					
					
						commit
						242241987e
					
				| @ -11,7 +11,6 @@ extern int32_t gui_srv(void* p); | |||||||
| extern int32_t input_srv(void* p); | extern int32_t input_srv(void* p); | ||||||
| extern int32_t loader_srv(void* p); | extern int32_t loader_srv(void* p); | ||||||
| extern int32_t notification_srv(void* p); | extern int32_t notification_srv(void* p); | ||||||
| extern int32_t power_observer_srv(void* p); |  | ||||||
| extern int32_t power_srv(void* p); | extern int32_t power_srv(void* p); | ||||||
| extern int32_t storage_srv(void* p); | extern int32_t storage_srv(void* p); | ||||||
| extern int32_t desktop_srv(void* p); | extern int32_t desktop_srv(void* p); | ||||||
| @ -115,10 +114,6 @@ const FlipperApplication FLIPPER_SERVICES[] = { | |||||||
|     {.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL}, |     {.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_POWER_OBSERVER |  | ||||||
|     {.app = power_observer_srv, .name = "PowerAuditSrv", .stack_size = 1024, .icon = NULL}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL}, |     {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -18,7 +18,6 @@ SRV_INPUT	= 1 | |||||||
| SRV_LOADER	= 1 | SRV_LOADER	= 1 | ||||||
| SRV_NOTIFICATION = 1 | SRV_NOTIFICATION = 1 | ||||||
| SRV_POWER	= 1 | SRV_POWER	= 1 | ||||||
| SRV_POWER_OBSERVER = 1 |  | ||||||
| SRV_RPC = 1 | SRV_RPC = 1 | ||||||
| SRV_STORAGE	= 1 | SRV_STORAGE	= 1 | ||||||
| 
 | 
 | ||||||
| @ -256,13 +255,6 @@ endif | |||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| SRV_POWER_OBSERVER ?= 0 |  | ||||||
| ifeq ($(SRV_POWER_OBSERVER), 1) |  | ||||||
| CFLAGS		+= -DSRV_POWER_OBSERVER |  | ||||||
| SRV_POWER	= 1 |  | ||||||
| endif |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SRV_POWER ?= 0 | SRV_POWER ?= 0 | ||||||
| ifeq ($(SRV_POWER), 1) | ifeq ($(SRV_POWER), 1) | ||||||
| CFLAGS		+= -DSRV_POWER | CFLAGS		+= -DSRV_POWER | ||||||
|  | |||||||
| @ -196,6 +196,11 @@ int32_t power_srv(void* p) { | |||||||
|         // Update battery view port
 |         // Update battery view port
 | ||||||
|         if(need_refresh) view_port_update(power->battery_view_port); |         if(need_refresh) view_port_update(power->battery_view_port); | ||||||
| 
 | 
 | ||||||
|  |         // Check OTG status and disable it in case of fault
 | ||||||
|  |         if(furi_hal_power_is_otg_enabled()) { | ||||||
|  |             furi_hal_power_check_otg_status(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         osDelay(1000); |         osDelay(1000); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,76 +0,0 @@ | |||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <notification/notification_messages.h> |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     osThreadId_t thread; |  | ||||||
| 
 |  | ||||||
| } PowerObserverSrv; |  | ||||||
| 
 |  | ||||||
| const NotificationMessage message_green_110 = { |  | ||||||
|     .type = NotificationMessageTypeLedGreen, |  | ||||||
|     .data.led.value = 110, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static const NotificationSequence sequence_overconsumption = { |  | ||||||
|     &message_green_110, |  | ||||||
|     &message_red_255, |  | ||||||
|     &message_delay_100, |  | ||||||
|     NULL, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     EventReset = (1 << 0), |  | ||||||
|     EventRequest = (1 << 1), |  | ||||||
| } UsbEvent; |  | ||||||
| 
 |  | ||||||
| static void usb_state_callback(FuriHalUsbStateEvent state, void* context) { |  | ||||||
|     PowerObserverSrv* srv = (PowerObserverSrv*)(context); |  | ||||||
|     if(state == FuriHalUsbStateEventReset) { |  | ||||||
|         osThreadFlagsSet(srv->thread, EventReset); |  | ||||||
|     } else if(state == FuriHalUsbStateEventDescriptorRequest) { |  | ||||||
|         osThreadFlagsSet(srv->thread, EventRequest); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int32_t power_observer_srv(void* p) { |  | ||||||
|     NotificationApp* notifications = furi_record_open("notification"); |  | ||||||
|     PowerObserverSrv* srv = furi_alloc(sizeof(PowerObserverSrv)); |  | ||||||
|     srv->thread = osThreadGetId(); |  | ||||||
| 
 |  | ||||||
|     const float overconsumption_limit = 0.03f; |  | ||||||
|     bool usb_request_pending = false; |  | ||||||
|     uint8_t usb_wait_time = 0; |  | ||||||
| 
 |  | ||||||
|     furi_hal_usb_set_state_callback(usb_state_callback, srv); |  | ||||||
| 
 |  | ||||||
|     while(true) { |  | ||||||
|         uint32_t flags = osThreadFlagsWait(EventReset | EventRequest, osFlagsWaitAny, 500); |  | ||||||
|         if((flags & osFlagsError) == 0) { |  | ||||||
|             if(flags & EventReset) { |  | ||||||
|                 usb_request_pending = true; |  | ||||||
|                 usb_wait_time = 0; |  | ||||||
|             } |  | ||||||
|             if(flags & EventRequest) { |  | ||||||
|                 usb_request_pending = false; |  | ||||||
|             } |  | ||||||
|         } else if(usb_request_pending) { |  | ||||||
|             usb_wait_time++; |  | ||||||
|             if(usb_wait_time > 4) { |  | ||||||
|                 furi_hal_usb_reinit(); |  | ||||||
|                 usb_request_pending = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         float current = -furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge); |  | ||||||
|         if(current > overconsumption_limit) { |  | ||||||
|             notification_message_block(notifications, &sequence_overconsumption); |  | ||||||
|         } |  | ||||||
|         if(furi_hal_power_is_otg_enabled()) { |  | ||||||
|             furi_hal_power_check_otg_status(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     free(srv); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
| @ -42,7 +42,7 @@ bool ble_app_init() { | |||||||
|     ble_app->event_flags = osEventFlagsNew(NULL); |     ble_app->event_flags = osEventFlagsNew(NULL); | ||||||
|     // HCI transport layer thread to handle user asynch events
 |     // HCI transport layer thread to handle user asynch events
 | ||||||
|     ble_app->thread = furi_thread_alloc(); |     ble_app->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(ble_app->thread, "BleHciWorker"); |     furi_thread_set_name(ble_app->thread, "BleHciDriver"); | ||||||
|     furi_thread_set_stack_size(ble_app->thread, 1024); |     furi_thread_set_stack_size(ble_app->thread, 1024); | ||||||
|     furi_thread_set_context(ble_app->thread, ble_app); |     furi_thread_set_context(ble_app->thread, ble_app); | ||||||
|     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); |     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ void ble_glue_init() { | |||||||
| 
 | 
 | ||||||
|     // FreeRTOS system task creation
 |     // FreeRTOS system task creation
 | ||||||
|     ble_glue->thread = furi_thread_alloc(); |     ble_glue->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(ble_glue->thread, "BleShciWorker"); |     furi_thread_set_name(ble_glue->thread, "BleShciDriver"); | ||||||
|     furi_thread_set_stack_size(ble_glue->thread, 1024); |     furi_thread_set_stack_size(ble_glue->thread, 1024); | ||||||
|     furi_thread_set_context(ble_glue->thread, ble_glue); |     furi_thread_set_context(ble_glue->thread, ble_glue); | ||||||
|     furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); |     furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); | ||||||
|  | |||||||
| @ -481,7 +481,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { | |||||||
| 
 | 
 | ||||||
|     // Thread configuration
 |     // Thread configuration
 | ||||||
|     gap->thread = furi_thread_alloc(); |     gap->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(gap->thread, "BleGapWorker"); |     furi_thread_set_name(gap->thread, "BleGapDriver"); | ||||||
|     furi_thread_set_stack_size(gap->thread, 1024); |     furi_thread_set_stack_size(gap->thread, 1024); | ||||||
|     furi_thread_set_context(gap->thread, gap); |     furi_thread_set_context(gap->thread, gap); | ||||||
|     furi_thread_set_callback(gap->thread, gap_app); |     furi_thread_set_callback(gap->thread, gap_app); | ||||||
|  | |||||||
| @ -38,7 +38,6 @@ void furi_hal_init() { | |||||||
| 
 | 
 | ||||||
|     // VCP + USB
 |     // VCP + USB
 | ||||||
|     furi_hal_usb_init(); |     furi_hal_usb_init(); | ||||||
|     furi_hal_usb_set_config(&usb_cdc_single); |  | ||||||
|     furi_hal_vcp_init(); |     furi_hal_vcp_init(); | ||||||
|     FURI_LOG_I(TAG, "USB OK"); |     FURI_LOG_I(TAG, "USB OK"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "furi_hal_version.h" | #include "furi_hal_version.h" | ||||||
| #include "furi_hal_usb_i.h" | #include "furi_hal_usb_i.h" | ||||||
| #include "furi_hal_usb.h" | #include "furi_hal_usb.h" | ||||||
|  | #include "furi_hal_vcp.h" | ||||||
| #include <furi_hal_power.h> | #include <furi_hal_power.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| @ -10,29 +11,46 @@ | |||||||
| 
 | 
 | ||||||
| #define USB_RECONNECT_DELAY 500 | #define USB_RECONNECT_DELAY 500 | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbInterface* usb_if_cur; | typedef struct { | ||||||
| static FuriHalUsbInterface* usb_if_next; |     FuriThread* thread; | ||||||
|  |     osTimerId_t tmr; | ||||||
|  |     bool enabled; | ||||||
|  |     bool connected; | ||||||
|  |     FuriHalUsbInterface* if_cur; | ||||||
|  |     FuriHalUsbInterface* if_next; | ||||||
|  |     FuriHalUsbStateCallback callback; | ||||||
|  |     void* cb_ctx; | ||||||
|  | } UsbSrv; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     EventModeChange = (1 << 0), | ||||||
|  |     EventEnable = (1 << 1), | ||||||
|  |     EventDisable = (1 << 2), | ||||||
|  |     EventReinit = (1 << 3), | ||||||
|  | 
 | ||||||
|  |     EventReset = (1 << 4), | ||||||
|  |     EventRequest = (1 << 5), | ||||||
|  | 
 | ||||||
|  |     EventModeChangeStart = (1 << 6), | ||||||
|  | } UsbEvent; | ||||||
|  | 
 | ||||||
|  | #define USB_SRV_ALL_EVENTS                                                                    \ | ||||||
|  |     (EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \ | ||||||
|  |      EventModeChangeStart) | ||||||
|  | 
 | ||||||
|  | static UsbSrv usb; | ||||||
| 
 | 
 | ||||||
| static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | ||||||
| 
 | 
 | ||||||
| static uint32_t ubuf[0x20]; | static uint32_t ubuf[0x20]; | ||||||
| usbd_device udev; | usbd_device udev; | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbStateCallback callback; | static int32_t furi_hal_usb_thread(void* context); | ||||||
| static void* cb_ctx; |  | ||||||
| 
 |  | ||||||
| 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); | ||||||
| static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); | static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); | static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); | static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| 
 | 
 | ||||||
| struct UsbCfg { |  | ||||||
|     osTimerId_t reconnect_tmr; |  | ||||||
|     bool enabled; |  | ||||||
|     bool connected; |  | ||||||
|     bool mode_changing; |  | ||||||
| } usb_config; |  | ||||||
| 
 |  | ||||||
| static void furi_hal_usb_tmr_cb(void* context); | static void furi_hal_usb_tmr_cb(void* context); | ||||||
| 
 | 
 | ||||||
| /* Low-level init */ | /* Low-level init */ | ||||||
| @ -56,79 +74,51 @@ void furi_hal_usb_init(void) { | |||||||
|     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); |     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); | ||||||
|     // Reset callback will be enabled after first mode change to avoid getting false reset events
 |     // Reset callback will be enabled after first mode change to avoid getting false reset events
 | ||||||
| 
 | 
 | ||||||
|     usb_config.enabled = false; |     usb.enabled = false; | ||||||
|     usb_config.reconnect_tmr = NULL; |     usb.if_cur = NULL; | ||||||
|     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); |     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); | ||||||
|     NVIC_EnableIRQ(USB_LP_IRQn); |     NVIC_EnableIRQ(USB_LP_IRQn); | ||||||
| 
 | 
 | ||||||
|  |     usb.thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(usb.thread, "UsbDriver"); | ||||||
|  |     furi_thread_set_stack_size(usb.thread, 1024); | ||||||
|  |     furi_thread_set_callback(usb.thread, furi_hal_usb_thread); | ||||||
|  |     furi_thread_start(usb.thread); | ||||||
|  | 
 | ||||||
|     FURI_LOG_I(TAG, "Init OK"); |     FURI_LOG_I(TAG, "Init OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | ||||||
|     if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
 |     usb.if_next = new_if; | ||||||
|         usb_config.mode_changing = true; |     if(usb.thread == NULL) { | ||||||
|         usb_if_next = new_if; |         // Service thread hasn't started yet, so just save interface mode
 | ||||||
|         if(usb_config.reconnect_tmr == NULL) |         return; | ||||||
|             usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); |  | ||||||
|         furi_hal_usb_disable(); |  | ||||||
|         usb_config.mode_changing = true; |  | ||||||
|         osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); |  | ||||||
|     } else if( |  | ||||||
|         (usb_config.mode_changing) && |  | ||||||
|         (usb_if_next != new_if)) { // Last interface mode change wasn't completed
 |  | ||||||
|         osTimerStop(usb_config.reconnect_tmr); |  | ||||||
|         usb_if_next = new_if; |  | ||||||
|         osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); |  | ||||||
|     } else { // Interface mode change - second stage
 |  | ||||||
|         if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); |  | ||||||
|         if(new_if != NULL) { |  | ||||||
|             new_if->init(&udev, new_if); |  | ||||||
|             usbd_reg_event(&udev, usbd_evt_reset, reset_evt); |  | ||||||
|             FURI_LOG_I(TAG, "USB Mode change done"); |  | ||||||
|             usb_config.enabled = true; |  | ||||||
|             usb_if_cur = new_if; |  | ||||||
|             usb_config.mode_changing = false; |  | ||||||
|     } |     } | ||||||
|     } |     furi_assert(usb.thread); | ||||||
| } |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); | ||||||
| 
 |  | ||||||
| void furi_hal_usb_reinit() { |  | ||||||
|     // Temporary disable callback to avoid getting false reset events
 |  | ||||||
|     usbd_reg_event(&udev, usbd_evt_reset, NULL); |  | ||||||
|     FURI_LOG_I(TAG, "USB Reinit"); |  | ||||||
|     furi_hal_usb_disable(); |  | ||||||
|     usbd_enable(&udev, false); |  | ||||||
|     usbd_enable(&udev, true); |  | ||||||
|     if(usb_config.reconnect_tmr == NULL) |  | ||||||
|         usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); |  | ||||||
|     usb_config.mode_changing = true; |  | ||||||
|     usb_if_next = usb_if_cur; |  | ||||||
|     osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriHalUsbInterface* furi_hal_usb_get_config() { | FuriHalUsbInterface* furi_hal_usb_get_config() { | ||||||
|     return usb_if_cur; |     return usb.if_cur; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_disable() { | void furi_hal_usb_disable() { | ||||||
|     if(usb_config.enabled) { |     furi_assert(usb.thread); | ||||||
|         susp_evt(&udev, 0, 0); |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable); | ||||||
|         usbd_connect(&udev, false); |  | ||||||
|         usb_config.enabled = false; |  | ||||||
|         FURI_LOG_I(TAG, "USB Disable"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_enable() { | void furi_hal_usb_enable() { | ||||||
|     if((!usb_config.enabled) && (usb_if_cur != NULL)) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable); | ||||||
|         usbd_connect(&udev, true); |  | ||||||
|         usb_config.enabled = true; |  | ||||||
|         FURI_LOG_I(TAG, "USB Enable"); |  | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_usb_reinit() { | ||||||
|  |     furi_assert(usb.thread); | ||||||
|  |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReinit); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_usb_tmr_cb(void* context) { | static void furi_hal_usb_tmr_cb(void* context) { | ||||||
|     furi_hal_usb_set_config(usb_if_next); |     furi_assert(usb.thread); | ||||||
|  |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChangeStart); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Get device / configuration descriptors */ | /* Get device / configuration descriptors */ | ||||||
| @ -137,28 +127,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ | |||||||
|     const uint8_t dnumber = req->wValue & 0xFF; |     const uint8_t dnumber = req->wValue & 0xFF; | ||||||
|     const void* desc; |     const void* desc; | ||||||
|     uint16_t len = 0; |     uint16_t len = 0; | ||||||
|     if(usb_if_cur == NULL) return usbd_fail; |     if(usb.if_cur == NULL) return usbd_fail; | ||||||
| 
 | 
 | ||||||
|     switch(dtype) { |     switch(dtype) { | ||||||
|     case USB_DTYPE_DEVICE: |     case USB_DTYPE_DEVICE: | ||||||
|         if(callback != NULL) { |         osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventRequest); | ||||||
|             callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx); |         if(usb.callback != NULL) { | ||||||
|  |             usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); | ||||||
|         } |         } | ||||||
|         desc = usb_if_cur->dev_descr; |         desc = usb.if_cur->dev_descr; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_CONFIGURATION: |     case USB_DTYPE_CONFIGURATION: | ||||||
|         desc = usb_if_cur->cfg_descr; |         desc = usb.if_cur->cfg_descr; | ||||||
|         len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0]; |         len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_STRING: |     case USB_DTYPE_STRING: | ||||||
|         if(dnumber == UsbDevLang) { |         if(dnumber == UsbDevLang) { | ||||||
|             desc = &dev_lang_desc; |             desc = &dev_lang_desc; | ||||||
|         } else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) { |         } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_manuf_descr; |             desc = usb.if_cur->str_manuf_descr; | ||||||
|         } else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) { |         } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_prod_descr; |             desc = usb.if_cur->str_prod_descr; | ||||||
|         } else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) { |         } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_serial_descr; |             desc = usb.if_cur->str_serial_descr; | ||||||
|         } else |         } else | ||||||
|             return usbd_fail; |             return usbd_fail; | ||||||
|         break; |         break; | ||||||
| @ -176,36 +167,122 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { | void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { | ||||||
|     callback = cb; |     usb.callback = cb; | ||||||
|     cb_ctx = ctx; |     usb.cb_ctx = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if(callback != NULL) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReset); | ||||||
|         callback(FuriHalUsbStateEventReset, cb_ctx); |     if(usb.callback != NULL) { | ||||||
|  |         usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == true)) { |     if((usb.if_cur != NULL) && (usb.connected == true)) { | ||||||
|         usb_config.connected = false; |         usb.connected = false; | ||||||
|         usb_if_cur->suspend(&udev); |         usb.if_cur->suspend(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_exit(); |         furi_hal_power_insomnia_exit(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventSuspend, cb_ctx); |         usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == false)) { |     if((usb.if_cur != NULL) && (usb.connected == false)) { | ||||||
|         usb_config.connected = true; |         usb.connected = true; | ||||||
|         usb_if_cur->wakeup(&udev); |         usb.if_cur->wakeup(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_enter(); |         furi_hal_power_insomnia_enter(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventWakeup, cb_ctx); |         usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static int32_t furi_hal_usb_thread(void* context) { | ||||||
|  |     usb.tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     bool usb_request_pending = false; | ||||||
|  |     uint8_t usb_wait_time = 0; | ||||||
|  | 
 | ||||||
|  |     if(usb.if_next != NULL) { | ||||||
|  |         osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(true) { | ||||||
|  |         uint32_t flags = osThreadFlagsWait(USB_SRV_ALL_EVENTS, osFlagsWaitAny, 500); | ||||||
|  |         if((flags & osFlagsError) == 0) { | ||||||
|  |             if(flags & EventModeChange) { | ||||||
|  |                 if(usb.if_next != usb.if_cur) { | ||||||
|  |                     if(usb.enabled) { // Disable current interface
 | ||||||
|  |                         susp_evt(&udev, 0, 0); | ||||||
|  |                         usbd_connect(&udev, false); | ||||||
|  |                         usb.enabled = false; | ||||||
|  |                         osTimerStart(usb.tmr, USB_RECONNECT_DELAY); | ||||||
|  |                     } else { | ||||||
|  |                         flags |= EventModeChangeStart; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventReinit) { | ||||||
|  |                 // Temporary disable callback to avoid getting false reset events
 | ||||||
|  |                 usbd_reg_event(&udev, usbd_evt_reset, NULL); | ||||||
|  |                 FURI_LOG_I(TAG, "USB Reinit"); | ||||||
|  |                 susp_evt(&udev, 0, 0); | ||||||
|  |                 usbd_connect(&udev, false); | ||||||
|  |                 usb.enabled = false; | ||||||
|  | 
 | ||||||
|  |                 usbd_enable(&udev, false); | ||||||
|  |                 usbd_enable(&udev, true); | ||||||
|  | 
 | ||||||
|  |                 usb.if_next = usb.if_cur; | ||||||
|  |                 osTimerStart(usb.tmr, USB_RECONNECT_DELAY); | ||||||
|  |             } | ||||||
|  |             if(flags & EventModeChangeStart) { // Second stage of mode change process
 | ||||||
|  |                 if(usb.if_cur != NULL) { | ||||||
|  |                     usb.if_cur->deinit(&udev); | ||||||
|  |                 } | ||||||
|  |                 if(usb.if_next != NULL) { | ||||||
|  |                     usb.if_next->init(&udev, usb.if_next); | ||||||
|  |                     usbd_reg_event(&udev, usbd_evt_reset, reset_evt); | ||||||
|  |                     FURI_LOG_I(TAG, "USB Mode change done"); | ||||||
|  |                     usb.enabled = true; | ||||||
|  |                     usb.if_cur = usb.if_next; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventEnable) { | ||||||
|  |                 if((!usb.enabled) && (usb.if_cur != NULL)) { | ||||||
|  |                     usbd_connect(&udev, true); | ||||||
|  |                     usb.enabled = true; | ||||||
|  |                     FURI_LOG_I(TAG, "USB Enable"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventDisable) { | ||||||
|  |                 if(usb.enabled) { | ||||||
|  |                     susp_evt(&udev, 0, 0); | ||||||
|  |                     usbd_connect(&udev, false); | ||||||
|  |                     usb.enabled = false; | ||||||
|  |                     usb_request_pending = false; | ||||||
|  |                     FURI_LOG_I(TAG, "USB Disable"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventReset) { | ||||||
|  |                 usb_request_pending = true; | ||||||
|  |                 usb_wait_time = 0; | ||||||
|  |             } | ||||||
|  |             if(flags & EventRequest) { | ||||||
|  |                 usb_request_pending = false; | ||||||
|  |             } | ||||||
|  |         } else if(usb_request_pending) { | ||||||
|  |             usb_wait_time++; | ||||||
|  |             if(usb_wait_time > 4) { | ||||||
|  |                 furi_hal_usb_reinit(); | ||||||
|  |                 usb_request_pending = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| #include <furi_hal_usb_cdc_i.h> | #include <furi_hal_usb_cdc_i.h> | ||||||
| #include <furi_hal_console.h> | #include <furi_hal.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| 
 | 
 | ||||||
| @ -65,7 +65,7 @@ void furi_hal_vcp_init() { | |||||||
|     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); |     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); | ||||||
| 
 | 
 | ||||||
|     vcp->thread = furi_thread_alloc(); |     vcp->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(vcp->thread, "VcpWorker"); |     furi_thread_set_name(vcp->thread, "VcpDriver"); | ||||||
|     furi_thread_set_stack_size(vcp->thread, 1024); |     furi_thread_set_stack_size(vcp->thread, 1024); | ||||||
|     furi_thread_set_callback(vcp->thread, vcp_worker); |     furi_thread_set_callback(vcp->thread, vcp_worker); | ||||||
|     furi_thread_start(vcp->thread); |     furi_thread_start(vcp->thread); | ||||||
| @ -79,6 +79,7 @@ static int32_t vcp_worker(void* context) { | |||||||
|     size_t missed_rx = 0; |     size_t missed_rx = 0; | ||||||
|     uint8_t last_tx_pkt_len = 0; |     uint8_t last_tx_pkt_len = 0; | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_usb_set_config(&usb_cdc_single); | ||||||
|     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); |     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ bool ble_app_init() { | |||||||
|     ble_app->event_flags = osEventFlagsNew(NULL); |     ble_app->event_flags = osEventFlagsNew(NULL); | ||||||
|     // HCI transport layer thread to handle user asynch events
 |     // HCI transport layer thread to handle user asynch events
 | ||||||
|     ble_app->thread = furi_thread_alloc(); |     ble_app->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(ble_app->thread, "BleHciWorker"); |     furi_thread_set_name(ble_app->thread, "BleHciDriver"); | ||||||
|     furi_thread_set_stack_size(ble_app->thread, 1024); |     furi_thread_set_stack_size(ble_app->thread, 1024); | ||||||
|     furi_thread_set_context(ble_app->thread, ble_app); |     furi_thread_set_context(ble_app->thread, ble_app); | ||||||
|     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); |     furi_thread_set_callback(ble_app->thread, ble_app_hci_thread); | ||||||
|  | |||||||
| @ -88,7 +88,7 @@ void ble_glue_init() { | |||||||
| 
 | 
 | ||||||
|     // FreeRTOS system task creation
 |     // FreeRTOS system task creation
 | ||||||
|     ble_glue->thread = furi_thread_alloc(); |     ble_glue->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(ble_glue->thread, "BleShciWorker"); |     furi_thread_set_name(ble_glue->thread, "BleShciDriver"); | ||||||
|     furi_thread_set_stack_size(ble_glue->thread, 1024); |     furi_thread_set_stack_size(ble_glue->thread, 1024); | ||||||
|     furi_thread_set_context(ble_glue->thread, ble_glue); |     furi_thread_set_context(ble_glue->thread, ble_glue); | ||||||
|     furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); |     furi_thread_set_callback(ble_glue->thread, ble_glue_shci_thread); | ||||||
|  | |||||||
| @ -481,7 +481,7 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { | |||||||
| 
 | 
 | ||||||
|     // Thread configuration
 |     // Thread configuration
 | ||||||
|     gap->thread = furi_thread_alloc(); |     gap->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(gap->thread, "BleGapWorker"); |     furi_thread_set_name(gap->thread, "BleGapDriver"); | ||||||
|     furi_thread_set_stack_size(gap->thread, 1024); |     furi_thread_set_stack_size(gap->thread, 1024); | ||||||
|     furi_thread_set_context(gap->thread, gap); |     furi_thread_set_context(gap->thread, gap); | ||||||
|     furi_thread_set_callback(gap->thread, gap_app); |     furi_thread_set_callback(gap->thread, gap_app); | ||||||
|  | |||||||
| @ -38,7 +38,6 @@ void furi_hal_init() { | |||||||
| 
 | 
 | ||||||
|     // VCP + USB
 |     // VCP + USB
 | ||||||
|     furi_hal_usb_init(); |     furi_hal_usb_init(); | ||||||
|     furi_hal_usb_set_config(&usb_cdc_single); |  | ||||||
|     furi_hal_vcp_init(); |     furi_hal_vcp_init(); | ||||||
|     FURI_LOG_I(TAG, "USB OK"); |     FURI_LOG_I(TAG, "USB OK"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include "furi_hal_version.h" | #include "furi_hal_version.h" | ||||||
| #include "furi_hal_usb_i.h" | #include "furi_hal_usb_i.h" | ||||||
| #include "furi_hal_usb.h" | #include "furi_hal_usb.h" | ||||||
|  | #include "furi_hal_vcp.h" | ||||||
| #include <furi_hal_power.h> | #include <furi_hal_power.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| @ -10,29 +11,46 @@ | |||||||
| 
 | 
 | ||||||
| #define USB_RECONNECT_DELAY 500 | #define USB_RECONNECT_DELAY 500 | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbInterface* usb_if_cur; | typedef struct { | ||||||
| static FuriHalUsbInterface* usb_if_next; |     FuriThread* thread; | ||||||
|  |     osTimerId_t tmr; | ||||||
|  |     bool enabled; | ||||||
|  |     bool connected; | ||||||
|  |     FuriHalUsbInterface* if_cur; | ||||||
|  |     FuriHalUsbInterface* if_next; | ||||||
|  |     FuriHalUsbStateCallback callback; | ||||||
|  |     void* cb_ctx; | ||||||
|  | } UsbSrv; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     EventModeChange = (1 << 0), | ||||||
|  |     EventEnable = (1 << 1), | ||||||
|  |     EventDisable = (1 << 2), | ||||||
|  |     EventReinit = (1 << 3), | ||||||
|  | 
 | ||||||
|  |     EventReset = (1 << 4), | ||||||
|  |     EventRequest = (1 << 5), | ||||||
|  | 
 | ||||||
|  |     EventModeChangeStart = (1 << 6), | ||||||
|  | } UsbEvent; | ||||||
|  | 
 | ||||||
|  | #define USB_SRV_ALL_EVENTS                                                                    \ | ||||||
|  |     (EventModeChange | EventEnable | EventDisable | EventReinit | EventReset | EventRequest | \ | ||||||
|  |      EventModeChangeStart) | ||||||
|  | 
 | ||||||
|  | static UsbSrv usb; | ||||||
| 
 | 
 | ||||||
| static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US); | ||||||
| 
 | 
 | ||||||
| static uint32_t ubuf[0x20]; | static uint32_t ubuf[0x20]; | ||||||
| usbd_device udev; | usbd_device udev; | ||||||
| 
 | 
 | ||||||
| static FuriHalUsbStateCallback callback; | static int32_t furi_hal_usb_thread(void* context); | ||||||
| static void* cb_ctx; |  | ||||||
| 
 |  | ||||||
| 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); | ||||||
| static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); | static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); | static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); | static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep); | ||||||
| 
 | 
 | ||||||
| struct UsbCfg { |  | ||||||
|     osTimerId_t reconnect_tmr; |  | ||||||
|     bool enabled; |  | ||||||
|     bool connected; |  | ||||||
|     bool mode_changing; |  | ||||||
| } usb_config; |  | ||||||
| 
 |  | ||||||
| static void furi_hal_usb_tmr_cb(void* context); | static void furi_hal_usb_tmr_cb(void* context); | ||||||
| 
 | 
 | ||||||
| /* Low-level init */ | /* Low-level init */ | ||||||
| @ -56,79 +74,51 @@ void furi_hal_usb_init(void) { | |||||||
|     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); |     usbd_reg_event(&udev, usbd_evt_wkup, wkup_evt); | ||||||
|     // Reset callback will be enabled after first mode change to avoid getting false reset events
 |     // Reset callback will be enabled after first mode change to avoid getting false reset events
 | ||||||
| 
 | 
 | ||||||
|     usb_config.enabled = false; |     usb.enabled = false; | ||||||
|     usb_config.reconnect_tmr = NULL; |     usb.if_cur = NULL; | ||||||
|     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); |     HAL_NVIC_SetPriority(USB_LP_IRQn, 5, 0); | ||||||
|     NVIC_EnableIRQ(USB_LP_IRQn); |     NVIC_EnableIRQ(USB_LP_IRQn); | ||||||
| 
 | 
 | ||||||
|  |     usb.thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(usb.thread, "UsbDriver"); | ||||||
|  |     furi_thread_set_stack_size(usb.thread, 1024); | ||||||
|  |     furi_thread_set_callback(usb.thread, furi_hal_usb_thread); | ||||||
|  |     furi_thread_start(usb.thread); | ||||||
|  | 
 | ||||||
|     FURI_LOG_I(TAG, "Init OK"); |     FURI_LOG_I(TAG, "Init OK"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | void furi_hal_usb_set_config(FuriHalUsbInterface* new_if) { | ||||||
|     if((new_if != usb_if_cur) && (usb_config.enabled)) { // Interface mode change - first stage
 |     usb.if_next = new_if; | ||||||
|         usb_config.mode_changing = true; |     if(usb.thread == NULL) { | ||||||
|         usb_if_next = new_if; |         // Service thread hasn't started yet, so just save interface mode
 | ||||||
|         if(usb_config.reconnect_tmr == NULL) |         return; | ||||||
|             usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); |  | ||||||
|         furi_hal_usb_disable(); |  | ||||||
|         usb_config.mode_changing = true; |  | ||||||
|         osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); |  | ||||||
|     } else if( |  | ||||||
|         (usb_config.mode_changing) && |  | ||||||
|         (usb_if_next != new_if)) { // Last interface mode change wasn't completed
 |  | ||||||
|         osTimerStop(usb_config.reconnect_tmr); |  | ||||||
|         usb_if_next = new_if; |  | ||||||
|         osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); |  | ||||||
|     } else { // Interface mode change - second stage
 |  | ||||||
|         if(usb_if_cur != NULL) usb_if_cur->deinit(&udev); |  | ||||||
|         if(new_if != NULL) { |  | ||||||
|             new_if->init(&udev, new_if); |  | ||||||
|             usbd_reg_event(&udev, usbd_evt_reset, reset_evt); |  | ||||||
|             FURI_LOG_I(TAG, "USB Mode change done"); |  | ||||||
|             usb_config.enabled = true; |  | ||||||
|             usb_if_cur = new_if; |  | ||||||
|             usb_config.mode_changing = false; |  | ||||||
|     } |     } | ||||||
|     } |     furi_assert(usb.thread); | ||||||
| } |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); | ||||||
| 
 |  | ||||||
| void furi_hal_usb_reinit() { |  | ||||||
|     // Temporary disable callback to avoid getting false reset events
 |  | ||||||
|     usbd_reg_event(&udev, usbd_evt_reset, NULL); |  | ||||||
|     FURI_LOG_I(TAG, "USB Reinit"); |  | ||||||
|     furi_hal_usb_disable(); |  | ||||||
|     usbd_enable(&udev, false); |  | ||||||
|     usbd_enable(&udev, true); |  | ||||||
|     if(usb_config.reconnect_tmr == NULL) |  | ||||||
|         usb_config.reconnect_tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); |  | ||||||
|     usb_config.mode_changing = true; |  | ||||||
|     usb_if_next = usb_if_cur; |  | ||||||
|     osTimerStart(usb_config.reconnect_tmr, USB_RECONNECT_DELAY); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| FuriHalUsbInterface* furi_hal_usb_get_config() { | FuriHalUsbInterface* furi_hal_usb_get_config() { | ||||||
|     return usb_if_cur; |     return usb.if_cur; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_disable() { | void furi_hal_usb_disable() { | ||||||
|     if(usb_config.enabled) { |     furi_assert(usb.thread); | ||||||
|         susp_evt(&udev, 0, 0); |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventDisable); | ||||||
|         usbd_connect(&udev, false); |  | ||||||
|         usb_config.enabled = false; |  | ||||||
|         FURI_LOG_I(TAG, "USB Disable"); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_enable() { | void furi_hal_usb_enable() { | ||||||
|     if((!usb_config.enabled) && (usb_if_cur != NULL)) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventEnable); | ||||||
|         usbd_connect(&udev, true); |  | ||||||
|         usb_config.enabled = true; |  | ||||||
|         FURI_LOG_I(TAG, "USB Enable"); |  | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_usb_reinit() { | ||||||
|  |     furi_assert(usb.thread); | ||||||
|  |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReinit); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_usb_tmr_cb(void* context) { | static void furi_hal_usb_tmr_cb(void* context) { | ||||||
|     furi_hal_usb_set_config(usb_if_next); |     furi_assert(usb.thread); | ||||||
|  |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChangeStart); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Get device / configuration descriptors */ | /* Get device / configuration descriptors */ | ||||||
| @ -137,28 +127,29 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ | |||||||
|     const uint8_t dnumber = req->wValue & 0xFF; |     const uint8_t dnumber = req->wValue & 0xFF; | ||||||
|     const void* desc; |     const void* desc; | ||||||
|     uint16_t len = 0; |     uint16_t len = 0; | ||||||
|     if(usb_if_cur == NULL) return usbd_fail; |     if(usb.if_cur == NULL) return usbd_fail; | ||||||
| 
 | 
 | ||||||
|     switch(dtype) { |     switch(dtype) { | ||||||
|     case USB_DTYPE_DEVICE: |     case USB_DTYPE_DEVICE: | ||||||
|         if(callback != NULL) { |         osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventRequest); | ||||||
|             callback(FuriHalUsbStateEventDescriptorRequest, cb_ctx); |         if(usb.callback != NULL) { | ||||||
|  |             usb.callback(FuriHalUsbStateEventDescriptorRequest, usb.cb_ctx); | ||||||
|         } |         } | ||||||
|         desc = usb_if_cur->dev_descr; |         desc = usb.if_cur->dev_descr; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_CONFIGURATION: |     case USB_DTYPE_CONFIGURATION: | ||||||
|         desc = usb_if_cur->cfg_descr; |         desc = usb.if_cur->cfg_descr; | ||||||
|         len = ((struct usb_string_descriptor*)(usb_if_cur->cfg_descr))->wString[0]; |         len = ((struct usb_string_descriptor*)(usb.if_cur->cfg_descr))->wString[0]; | ||||||
|         break; |         break; | ||||||
|     case USB_DTYPE_STRING: |     case USB_DTYPE_STRING: | ||||||
|         if(dnumber == UsbDevLang) { |         if(dnumber == UsbDevLang) { | ||||||
|             desc = &dev_lang_desc; |             desc = &dev_lang_desc; | ||||||
|         } else if((dnumber == UsbDevManuf) && (usb_if_cur->str_manuf_descr != NULL)) { |         } else if((dnumber == UsbDevManuf) && (usb.if_cur->str_manuf_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_manuf_descr; |             desc = usb.if_cur->str_manuf_descr; | ||||||
|         } else if((dnumber == UsbDevProduct) && (usb_if_cur->str_prod_descr != NULL)) { |         } else if((dnumber == UsbDevProduct) && (usb.if_cur->str_prod_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_prod_descr; |             desc = usb.if_cur->str_prod_descr; | ||||||
|         } else if((dnumber == UsbDevSerial) && (usb_if_cur->str_serial_descr != NULL)) { |         } else if((dnumber == UsbDevSerial) && (usb.if_cur->str_serial_descr != NULL)) { | ||||||
|             desc = usb_if_cur->str_serial_descr; |             desc = usb.if_cur->str_serial_descr; | ||||||
|         } else |         } else | ||||||
|             return usbd_fail; |             return usbd_fail; | ||||||
|         break; |         break; | ||||||
| @ -176,36 +167,122 @@ static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { | void furi_hal_usb_set_state_callback(FuriHalUsbStateCallback cb, void* ctx) { | ||||||
|     callback = cb; |     usb.callback = cb; | ||||||
|     cb_ctx = ctx; |     usb.cb_ctx = ctx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if(callback != NULL) { |     osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventReset); | ||||||
|         callback(FuriHalUsbStateEventReset, cb_ctx); |     if(usb.callback != NULL) { | ||||||
|  |         usb.callback(FuriHalUsbStateEventReset, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void susp_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == true)) { |     if((usb.if_cur != NULL) && (usb.connected == true)) { | ||||||
|         usb_config.connected = false; |         usb.connected = false; | ||||||
|         usb_if_cur->suspend(&udev); |         usb.if_cur->suspend(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_exit(); |         furi_hal_power_insomnia_exit(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventSuspend, cb_ctx); |         usb.callback(FuriHalUsbStateEventSuspend, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | static void wkup_evt(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|     if((usb_if_cur != NULL) && (usb_config.connected == false)) { |     if((usb.if_cur != NULL) && (usb.connected == false)) { | ||||||
|         usb_config.connected = true; |         usb.connected = true; | ||||||
|         usb_if_cur->wakeup(&udev); |         usb.if_cur->wakeup(&udev); | ||||||
| 
 | 
 | ||||||
|         furi_hal_power_insomnia_enter(); |         furi_hal_power_insomnia_enter(); | ||||||
|     } |     } | ||||||
|     if(callback != NULL) { |     if(usb.callback != NULL) { | ||||||
|         callback(FuriHalUsbStateEventWakeup, cb_ctx); |         usb.callback(FuriHalUsbStateEventWakeup, usb.cb_ctx); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static int32_t furi_hal_usb_thread(void* context) { | ||||||
|  |     usb.tmr = osTimerNew(furi_hal_usb_tmr_cb, osTimerOnce, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     bool usb_request_pending = false; | ||||||
|  |     uint8_t usb_wait_time = 0; | ||||||
|  | 
 | ||||||
|  |     if(usb.if_next != NULL) { | ||||||
|  |         osThreadFlagsSet(furi_thread_get_thread_id(usb.thread), EventModeChange); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     while(true) { | ||||||
|  |         uint32_t flags = osThreadFlagsWait(USB_SRV_ALL_EVENTS, osFlagsWaitAny, 500); | ||||||
|  |         if((flags & osFlagsError) == 0) { | ||||||
|  |             if(flags & EventModeChange) { | ||||||
|  |                 if(usb.if_next != usb.if_cur) { | ||||||
|  |                     if(usb.enabled) { // Disable current interface
 | ||||||
|  |                         susp_evt(&udev, 0, 0); | ||||||
|  |                         usbd_connect(&udev, false); | ||||||
|  |                         usb.enabled = false; | ||||||
|  |                         osTimerStart(usb.tmr, USB_RECONNECT_DELAY); | ||||||
|  |                     } else { | ||||||
|  |                         flags |= EventModeChangeStart; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventReinit) { | ||||||
|  |                 // Temporary disable callback to avoid getting false reset events
 | ||||||
|  |                 usbd_reg_event(&udev, usbd_evt_reset, NULL); | ||||||
|  |                 FURI_LOG_I(TAG, "USB Reinit"); | ||||||
|  |                 susp_evt(&udev, 0, 0); | ||||||
|  |                 usbd_connect(&udev, false); | ||||||
|  |                 usb.enabled = false; | ||||||
|  | 
 | ||||||
|  |                 usbd_enable(&udev, false); | ||||||
|  |                 usbd_enable(&udev, true); | ||||||
|  | 
 | ||||||
|  |                 usb.if_next = usb.if_cur; | ||||||
|  |                 osTimerStart(usb.tmr, USB_RECONNECT_DELAY); | ||||||
|  |             } | ||||||
|  |             if(flags & EventModeChangeStart) { // Second stage of mode change process
 | ||||||
|  |                 if(usb.if_cur != NULL) { | ||||||
|  |                     usb.if_cur->deinit(&udev); | ||||||
|  |                 } | ||||||
|  |                 if(usb.if_next != NULL) { | ||||||
|  |                     usb.if_next->init(&udev, usb.if_next); | ||||||
|  |                     usbd_reg_event(&udev, usbd_evt_reset, reset_evt); | ||||||
|  |                     FURI_LOG_I(TAG, "USB Mode change done"); | ||||||
|  |                     usb.enabled = true; | ||||||
|  |                     usb.if_cur = usb.if_next; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventEnable) { | ||||||
|  |                 if((!usb.enabled) && (usb.if_cur != NULL)) { | ||||||
|  |                     usbd_connect(&udev, true); | ||||||
|  |                     usb.enabled = true; | ||||||
|  |                     FURI_LOG_I(TAG, "USB Enable"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventDisable) { | ||||||
|  |                 if(usb.enabled) { | ||||||
|  |                     susp_evt(&udev, 0, 0); | ||||||
|  |                     usbd_connect(&udev, false); | ||||||
|  |                     usb.enabled = false; | ||||||
|  |                     usb_request_pending = false; | ||||||
|  |                     FURI_LOG_I(TAG, "USB Disable"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(flags & EventReset) { | ||||||
|  |                 usb_request_pending = true; | ||||||
|  |                 usb_wait_time = 0; | ||||||
|  |             } | ||||||
|  |             if(flags & EventRequest) { | ||||||
|  |                 usb_request_pending = false; | ||||||
|  |             } | ||||||
|  |         } else if(usb_request_pending) { | ||||||
|  |             usb_wait_time++; | ||||||
|  |             if(usb_wait_time > 4) { | ||||||
|  |                 furi_hal_usb_reinit(); | ||||||
|  |                 usb_request_pending = false; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| #include <furi_hal_usb_cdc_i.h> | #include <furi_hal_usb_cdc_i.h> | ||||||
| #include <furi_hal_console.h> | #include <furi_hal.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <stream_buffer.h> | #include <stream_buffer.h> | ||||||
| 
 | 
 | ||||||
| @ -65,7 +65,7 @@ void furi_hal_vcp_init() { | |||||||
|     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); |     vcp->rx_stream = xStreamBufferCreate(VCP_RX_BUF_SIZE, 1); | ||||||
| 
 | 
 | ||||||
|     vcp->thread = furi_thread_alloc(); |     vcp->thread = furi_thread_alloc(); | ||||||
|     furi_thread_set_name(vcp->thread, "VcpWorker"); |     furi_thread_set_name(vcp->thread, "VcpDriver"); | ||||||
|     furi_thread_set_stack_size(vcp->thread, 1024); |     furi_thread_set_stack_size(vcp->thread, 1024); | ||||||
|     furi_thread_set_callback(vcp->thread, vcp_worker); |     furi_thread_set_callback(vcp->thread, vcp_worker); | ||||||
|     furi_thread_start(vcp->thread); |     furi_thread_start(vcp->thread); | ||||||
| @ -79,6 +79,7 @@ static int32_t vcp_worker(void* context) { | |||||||
|     size_t missed_rx = 0; |     size_t missed_rx = 0; | ||||||
|     uint8_t last_tx_pkt_len = 0; |     uint8_t last_tx_pkt_len = 0; | ||||||
| 
 | 
 | ||||||
|  |     furi_hal_usb_set_config(&usb_cdc_single); | ||||||
|     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); |     furi_hal_cdc_set_callbacks(VCP_IF_NUM, &cdc_cb, NULL); | ||||||
| 
 | 
 | ||||||
|     while(1) { |     while(1) { | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
| #include <furi_hal_spi.h> | #include <furi_hal_spi.h> | ||||||
| 
 | 
 | ||||||
| static const osThreadAttr_t platform_irq_thread_attr = { | static const osThreadAttr_t platform_irq_thread_attr = { | ||||||
|     .name = "RfalIrqWorker", |     .name = "RfalIrqDriver", | ||||||
|     .stack_size = 1024, |     .stack_size = 1024, | ||||||
|     .priority = osPriorityRealtime, |     .priority = osPriorityRealtime, | ||||||
| }; | }; | ||||||
| @ -20,7 +20,7 @@ void nfc_isr(void* _ctx) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void platformIrqWorker() { | void platformIrqThread() { | ||||||
|     while(1) { |     while(1) { | ||||||
|         uint32_t flags = osThreadFlagsWait(0x1, osFlagsWaitAny, osWaitForever); |         uint32_t flags = osThreadFlagsWait(0x1, osFlagsWaitAny, osWaitForever); | ||||||
|         if(flags & 0x1) { |         if(flags & 0x1) { | ||||||
| @ -41,7 +41,7 @@ void platformDisableIrqCallback() { | |||||||
| 
 | 
 | ||||||
| void platformSetIrqCallback(PlatformIrqCallback callback) { | void platformSetIrqCallback(PlatformIrqCallback callback) { | ||||||
|     platform_irq_callback = callback; |     platform_irq_callback = callback; | ||||||
|     platform_irq_thread_id = osThreadNew(platformIrqWorker, NULL, &platform_irq_thread_attr); |     platform_irq_thread_id = osThreadNew(platformIrqThread, NULL, &platform_irq_thread_attr); | ||||||
|     hal_gpio_add_int_callback(&pin, nfc_isr, NULL); |     hal_gpio_add_int_callback(&pin, nfc_isr, NULL); | ||||||
|     // Disable interrupt callback as the pin is shared between 2 apps
 |     // Disable interrupt callback as the pin is shared between 2 apps
 | ||||||
|     // It is enabled in rfalLowPowerModeStop()
 |     // It is enabled in rfalLowPowerModeStop()
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Nikolay Minaylov
						Nikolay Minaylov