diff --git a/hal/inc/hal_dynalib_usart.h b/hal/inc/hal_dynalib_usart.h index 41878f210e..14ac9aaa00 100644 --- a/hal/inc/hal_dynalib_usart.h +++ b/hal/inc/hal_dynalib_usart.h @@ -61,6 +61,10 @@ DYNALIB_FN(hal_usart,HAL_USART_Flush_Data) DYNALIB_FN(hal_usart,HAL_USART_Is_Enabled) DYNALIB_FN(hal_usart,HAL_USART_Half_Duplex) DYNALIB_FN(hal_usart,HAL_USART_Available_Data_For_Write) +#ifdef USB_CDC_ENABLE +DYNALIB_FN(hal_usart,USB_USART_Available_Data_For_Write) +DYNALIB_FN(hal_usart,USB_USART_Flush_Data) +#endif DYNALIB_END(hal_usart) diff --git a/hal/inc/usb_hal.h b/hal/inc/usb_hal.h index cf4ce98409..75cbe31fa8 100644 --- a/hal/inc/usb_hal.h +++ b/hal/inc/usb_hal.h @@ -81,6 +81,12 @@ void USB_USART_LineCoding_BitRate_Handler(void (*handler)(uint32_t bitRate)); */ uint8_t USB_USART_Available_Data(void); +/** + * Retrieves the number of bytes of data available in the TX buffer. + * @return + */ +int32_t USB_USART_Available_Data_For_Write(void); + /** * Reads data from the input buffer. * @param peek If the data should be peeked reather than fetched. @@ -93,8 +99,16 @@ int32_t USB_USART_Receive_Data(uint8_t peek); /** * Sends data to the USB serial. * @param Data The data to write. + * @return */ void USB_USART_Send_Data(uint8_t Data); + +/** + * Flushes TX buffer + * @return + */ +void USB_USART_Flush_Data(void); + #endif #ifdef USB_HID_ENABLE diff --git a/hal/src/core/usb_hal.c b/hal/src/core/usb_hal.c index 6da82e705d..654676c7b8 100644 --- a/hal/src/core/usb_hal.c +++ b/hal/src/core/usb_hal.c @@ -197,6 +197,20 @@ int32_t USB_USART_Receive_Data(uint8_t peek) return -1; } +/******************************************************************************* + * Function Name : USB_USART_Available_Data_For_Write. + * Description : Return the length of available space in TX buffer + * Input : None. + * Return : Length. + *******************************************************************************/ +int32_t USB_USART_Available_Data_For_Write(void) +{ + if(bDeviceState == CONFIGURED) + return (USART_RX_DATA_SIZE - USART_Rx_ptr_in) % USART_RX_DATA_SIZE; + return -1; +} + + /******************************************************************************* * Function Name : USB_USART_Send_Data. * Description : Send Data from USB_USART to USB Host. @@ -224,6 +238,17 @@ void USB_USART_Send_Data(uint8_t Data) } } } + +/******************************************************************************* + * Function Name : USB_USART_Flush_Data. + * Description : Flushes TX buffer + * Input : None. + * Return : None. + *******************************************************************************/ +void USB_USART_Flush_Data(void) +{ + // Not implemented properly +} #endif #ifdef USB_HID_ENABLE diff --git a/hal/src/gcc/usb_hal.cpp b/hal/src/gcc/usb_hal.cpp index 0e9d5475cd..f2dce7dac3 100644 --- a/hal/src/gcc/usb_hal.cpp +++ b/hal/src/gcc/usb_hal.cpp @@ -79,6 +79,17 @@ int32_t USB_USART_Receive_Data(uint8_t peek) return result; } +/******************************************************************************* + * Function Name : USB_USART_Available_Data_For_Write. + * Description : Return the length of available space in TX buffer + * Input : None. + * Return : Length. + *******************************************************************************/ +int32_t USB_USART_Available_Data_For_Write(void) +{ + return 1; +} + /******************************************************************************* * Function Name : USB_USART_Send_Data. * Description : Send Data from USB_USART to USB Host. @@ -90,6 +101,16 @@ void USB_USART_Send_Data(uint8_t Data) std::cout.write((const char*)&Data, 1); } +/******************************************************************************* + * Function Name : USB_USART_Flush_Data. + * Description : Flushes TX buffer + * Input : None. + * Return : None. + *******************************************************************************/ +void USB_USART_Flush_Data(void) +{ +} + #ifdef USB_HID_ENABLE /******************************************************************************* * Function Name : USB_HID_Send_Report. diff --git a/hal/src/stm32f2xx/core_hal_stm32f2xx.c b/hal/src/stm32f2xx/core_hal_stm32f2xx.c index e824645312..00877536cb 100644 --- a/hal/src/stm32f2xx/core_hal_stm32f2xx.c +++ b/hal/src/stm32f2xx/core_hal_stm32f2xx.c @@ -48,6 +48,8 @@ #include "dct.h" #include "hal_platform.h" #include "malloc.h" +#include "usb_hal.h" +#include "usart_hal.h" #define STOP_MODE_EXIT_CONDITION_PIN 0x01 #define STOP_MODE_EXIT_CONDITION_RTC 0x02 @@ -383,7 +385,21 @@ void HAL_Core_Enter_Stop_Mode(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long if (!((wakeUpPin < TOTAL_PINS) && (wakeUpPin >= 0) && (edgeTriggerMode <= FALLING)) && seconds <= 0) return; - HAL_disable_irq(); + SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; + + // Disable USB Serial (detach) + USB_USART_Init(0); + + // Flush all USARTs + for (int usart = 0; usart < TOTAL_USARTS; usart++) + { + if (HAL_USART_Is_Enabled(usart)) + { + HAL_USART_Flush_Data(usart); + } + } + + int32_t state = HAL_disable_irq(); uint32_t exit_conditions = 0x00; @@ -455,7 +471,11 @@ void HAL_Core_Enter_Stop_Mode(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long HAL_Interrupts_Restore(); // Successfully exited STOP mode - HAL_enable_irq(0); + HAL_enable_irq(state); + + SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; + + USB_USART_Init(9600); } void HAL_Core_Execute_Stop_Mode(void) diff --git a/hal/src/stm32f2xx/usb_hal.c b/hal/src/stm32f2xx/usb_hal.c index 74a0da8a1e..131b87d9c1 100644 --- a/hal/src/stm32f2xx/usb_hal.c +++ b/hal/src/stm32f2xx/usb_hal.c @@ -33,6 +33,7 @@ #include "usb_conf.h" #include "usbd_desc.h" #include "delay_hal.h" +#include "interrupts_hal.h" /* Private typedef -----------------------------------------------------------*/ @@ -54,12 +55,15 @@ extern uint32_t USBD_OTG_EP1OUT_ISR_Handler(USB_OTG_CORE_HANDLE *pdev); extern volatile LINE_CODING linecoding; extern volatile uint8_t USB_DEVICE_CONFIGURED; extern volatile uint8_t USB_Rx_Buffer[]; -extern volatile uint8_t APP_Rx_Buffer[]; -extern volatile uint32_t APP_Rx_ptr_in; -extern volatile uint16_t USB_Rx_length; -extern volatile uint16_t USB_Rx_ptr; +extern volatile uint32_t USB_Rx_Buffer_head; +extern volatile uint32_t USB_Rx_Buffer_tail; +extern volatile uint32_t USB_Rx_Buffer_length; +extern volatile uint8_t USB_Tx_Buffer[]; +extern volatile uint32_t USB_Tx_Buffer_head; +extern volatile uint32_t USB_Tx_Buffer_tail; extern volatile uint8_t USB_Tx_State; extern volatile uint8_t USB_Rx_State; +extern volatile uint8_t USB_Serial_Open; #endif #if defined (USB_CDC_ENABLE) || defined (USB_HID_ENABLE) @@ -99,25 +103,44 @@ void Get_SerialNum(void) /******************************************************************************* * Function Name : USB_USART_Init * Description : Start USB-USART protocol. - * Input : baudRate (0 : disconnect usb else init usb only once). + * Input : baudRate (0 : disconnect usb else init usb). * Return : None. *******************************************************************************/ void USB_USART_Init(uint32_t baudRate) { if (linecoding.bitrate != baudRate) { - if (!baudRate) + if (!baudRate && linecoding.bitrate > 0) { + // Deconfigure CDC class endpoints + USBD_ClrCfg(&USB_OTG_dev, 0); + + // Class callbacks and descriptor should probably be cleared + // to use another USB class, but this causes a hardfault if no descriptor is set + // and detach/attach is performed. + // Leave them be for now. + + // USB_OTG_dev.dev.class_cb = NULL; + // USB_OTG_dev.dev.usr_cb = NULL; + // USB_OTG_dev.dev.usr_device = NULL; + + // Perform detach USB_Cable_Config(DISABLE); + + // Soft reattach + // USB_OTG_dev.regs.DREGS->DCTL |= 0x02; } else if (!linecoding.bitrate) { - //Perform a Detach-Attach operation on USB bus + //Initialize USB device + SPARK_USB_Setup(); + + // Perform a hard Detach-Attach operation on USB bus USB_Cable_Config(DISABLE); USB_Cable_Config(ENABLE); - //Initialize USB device only once (if linecoding.bitrate==0) - SPARK_USB_Setup(); + // Soft reattach + // USB_OTG_dev.regs.DREGS->DCTL |= 0x02; } //linecoding.bitrate will be overwritten by USB Host linecoding.bitrate = baudRate; @@ -134,12 +157,14 @@ void USB_USART_LineCoding_BitRate_Handler(void (*handler)(uint32_t bitRate)) //Init USB Serial first before calling the linecoding handler USB_USART_Init(9600); - HAL_Delay_Milliseconds(1000); - //Set the system defined custom handler SetLineCodingBitRateHandler(handler); } +static inline bool USB_USART_Connected() { + return linecoding.bitrate > 0 && USB_OTG_dev.dev.device_status == USB_OTG_CONFIGURED && USB_Serial_Open; +} + /******************************************************************************* * Function Name : USB_USART_Available_Data. * Description : Return the length of available data received from USB. @@ -148,12 +173,14 @@ void USB_USART_LineCoding_BitRate_Handler(void (*handler)(uint32_t bitRate)) *******************************************************************************/ uint8_t USB_USART_Available_Data(void) { - if(USB_Rx_State == 1) - { - return (USB_Rx_length - USB_Rx_ptr); - } - - return 0; + int32_t available = 0; + int state = HAL_disable_irq(); + if (USB_Rx_Buffer_head >= USB_Rx_Buffer_tail) + available = USB_Rx_Buffer_head - USB_Rx_Buffer_tail; + else + available = USB_Rx_Buffer_length + USB_Rx_Buffer_head - USB_Rx_Buffer_tail; + HAL_enable_irq(state); + return available; } /******************************************************************************* @@ -164,20 +191,36 @@ uint8_t USB_USART_Available_Data(void) *******************************************************************************/ int32_t USB_USART_Receive_Data(uint8_t peek) { - if(USB_Rx_State == 1) + if (USB_USART_Available_Data() > 0) { - if(!peek && ((USB_Rx_length - USB_Rx_ptr) == 1)) - { - USB_Rx_State = 0; - - /* Prepare Out endpoint to receive next packet */ - DCD_EP_PrepareRx(&USB_OTG_dev, - CDC_OUT_EP, - (uint8_t*)(USB_Rx_Buffer), - CDC_DATA_OUT_PACKET_SIZE); + int state = HAL_disable_irq(); + uint8_t data = USB_Rx_Buffer[USB_Rx_Buffer_tail]; + if (!peek) { + USB_Rx_Buffer_tail++; + if (USB_Rx_Buffer_tail == USB_Rx_Buffer_length) + USB_Rx_Buffer_tail = 0; } + HAL_enable_irq(state); + return data; + } + + return -1; +} - return USB_Rx_Buffer[peek ? USB_Rx_ptr : USB_Rx_ptr++]; +/******************************************************************************* + * Function Name : USB_USART_Available_Data_For_Write. + * Description : Return the length of available space in TX buffer + * Input : None. + * Return : Length. + *******************************************************************************/ +int32_t USB_USART_Available_Data_For_Write(void) +{ + if (USB_USART_Connected()) + { + uint32_t tail = USB_Tx_Buffer_tail; + int32_t available = USB_TX_BUFFER_SIZE - (USB_Tx_Buffer_head >= tail ? + USB_Tx_Buffer_head - tail : USB_TX_BUFFER_SIZE + USB_Tx_Buffer_head - tail) - 1; + return available; } return -1; @@ -191,18 +234,33 @@ int32_t USB_USART_Receive_Data(uint8_t peek) *******************************************************************************/ void USB_USART_Send_Data(uint8_t Data) { - APP_Rx_Buffer[APP_Rx_ptr_in] = Data; + int32_t available = 0; + do { + available = USB_USART_Available_Data_For_Write(); + } + while (available < 1 && available != -1); + // Confirm once again that the Host is connected + if (USB_USART_Connected()) + { + uint32_t head = USB_Tx_Buffer_head; - APP_Rx_ptr_in++; + USB_Tx_Buffer[head] = Data; - /* To avoid buffer overflow */ - if(APP_Rx_ptr_in == APP_RX_DATA_SIZE) - { - APP_Rx_ptr_in = 0; + USB_Tx_Buffer_head = ++head % USB_TX_BUFFER_SIZE; } +} - //Delay 100us to avoid losing the data - HAL_Delay_Microseconds(100); +/******************************************************************************* + * Function Name : USB_USART_Flush_Data. + * Description : Flushes TX buffer + * Input : None. + * Return : None. + *******************************************************************************/ +void USB_USART_Flush_Data(void) +{ + while(USB_USART_Connected() && USB_USART_Available_Data_For_Write() != (USB_TX_BUFFER_SIZE - 1)); + // We should also wait for USB_Tx_State to become 0, as hardware might still be busy transmitting data + while(USB_Tx_State == 1); } #endif diff --git a/hal/src/template/usb_hal.cpp b/hal/src/template/usb_hal.cpp index 7460f51826..e523c9255a 100644 --- a/hal/src/template/usb_hal.cpp +++ b/hal/src/template/usb_hal.cpp @@ -62,6 +62,17 @@ int32_t USB_USART_Receive_Data(uint8_t peek) return -1; } +/******************************************************************************* + * Function Name : USB_USART_Available_Data_For_Write. + * Description : Return the length of available space in TX buffer + * Input : None. + * Return : Length. + *******************************************************************************/ +int32_t USB_USART_Available_Data_For_Write(void) +{ + return 0; +} + /******************************************************************************* * Function Name : USB_USART_Send_Data. * Description : Send Data from USB_USART to USB Host. @@ -71,6 +82,17 @@ int32_t USB_USART_Receive_Data(uint8_t peek) void USB_USART_Send_Data(uint8_t Data) { +} + +/******************************************************************************* + * Function Name : USB_USART_Flush_Data. + * Description : Flushes TX buffer + * Input : None. + * Return : None. + *******************************************************************************/ +void USB_USART_Flush_Data(void) +{ + } #endif diff --git a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/usbd_conf.h b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/usbd_conf.h index 981e554e37..a6ed06686f 100644 --- a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/usbd_conf.h +++ b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/usbd_conf.h @@ -85,9 +85,10 @@ #define CDC_DATA_MAX_PACKET_SIZE 64 /* Endpoint IN & OUT Packet size */ #define CDC_CMD_PACKET_SZE 8 /* Control Endpoint Packet size */ -#define CDC_IN_FRAME_INTERVAL 5 /* Number of micro-frames between IN transfers */ -#define APP_RX_DATA_SIZE 256 /* Total size of IN buffer: +#define CDC_IN_FRAME_INTERVAL 1 /* Number of micro-frames between IN transfers */ +#define USB_TX_BUFFER_SIZE 128 /* Total size of IN buffer: APP_RX_DATA_SIZE*8/MAX_BAUDARATE*1000 should be > CDC_IN_FRAME_INTERVAL */ +#define USB_RX_BUFFER_SIZE 256 #define APP_FOPS APP_fops diff --git a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/src/usb_bsp.c b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/src/usb_bsp.c index f8dab9081b..f78350e351 100644 --- a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/src/usb_bsp.c +++ b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/src/usb_bsp.c @@ -90,7 +90,15 @@ void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev) //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //GPIO_Init(GPIOB, &GPIO_InitStructure); - RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_OTG_HS, ENABLE) ; + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_OTG_HS, ENABLE); + + // Do not disable USB clock in sleep mode. + // This fixes the issue of USB Serial not working after exiting STOP mode + // !!! + // Currently this option is not used, as USB is detached before going into + // STOP mode and reattached after exiting. + // RCC_AHB1PeriphClockLPModeCmd(RCC_AHB1Periph_OTG_HS_ULPI, DISABLE); + #endif } diff --git a/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_cdc_core.c b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_cdc_core.c index d4442f4f39..acaa877906 100644 --- a/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_cdc_core.c +++ b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_cdc_core.c @@ -67,6 +67,62 @@ #include "usbd_cdc_core.h" #include "usbd_desc.h" #include "usbd_req.h" +#include "debug.h" + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif +#ifndef MAX +#define MAX(a, b) (a) > (b) ? (a) : (b) +#endif + +/* Wrap up buffer index */ +static inline uint32_t ring_wrap(uint32_t size, uint32_t idx) +{ + return idx >= size ? idx - size : idx; +} + +/* Returns the number of bytes available in buffer */ +static inline uint32_t ring_data_avail(uint32_t size, uint32_t head, uint32_t tail) +{ + if (head >= tail) + return head - tail; + else + return size + head - tail; +} + +/* Returns the amount of free space available in buffer */ +static inline uint32_t ring_space_avail(uint32_t size, uint32_t head, uint32_t tail) +{ + return size - ring_data_avail(size, head, tail) - 1; +} + +/* Returns the number of contiguous data bytes available in buffer */ +static inline uint32_t ring_data_contig(uint32_t size, uint32_t head, uint32_t tail) +{ + if (head >= tail) + return head - tail; + else + return size - tail; +} + +/* Returns the amount of contiguous space available in buffer */ +static inline uint32_t ring_space_contig(uint32_t size, uint32_t head, uint32_t tail) +{ + if (head >= tail) + return (tail ? size : size - 1) - head; + else + return tail - head - 1; +} + +/* Returns the amount of free space available after wrapping up the head */ +static inline uint32_t ring_space_wrapped(uint32_t size, uint32_t head, uint32_t tail) +{ + if (head < tail || !tail) + return 0; + else + return tail - 1; +} /** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY @@ -121,7 +177,6 @@ static uint8_t usbd_cdc_SOF (void *pdev); /********************************************* CDC specific management functions *********************************************/ -static void Handle_USBAsynchXfer (void *pdev); static uint8_t *USBD_cdc_GetCfgDesc (uint8_t speed, uint16_t *length); #ifdef USE_USB_OTG_HS static uint8_t *USBD_cdc_GetOtherCfgDesc (uint8_t speed, uint16_t *length); @@ -162,14 +217,22 @@ __ALIGN_BEGIN static __IO uint32_t usbd_cdc_AltSet __ALIGN_END = 0; #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ -__ALIGN_BEGIN uint8_t USB_Rx_Buffer [CDC_DATA_MAX_PACKET_SIZE] __ALIGN_END ; +__ALIGN_BEGIN uint8_t USB_Rx_Buffer [USB_RX_BUFFER_SIZE] __ALIGN_END ; + +volatile uint32_t USB_Rx_Buffer_head = 0; +volatile uint32_t USB_Rx_Buffer_tail = 0; +volatile uint32_t USB_Rx_Buffer_length = USB_RX_BUFFER_SIZE; #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED #if defined ( __ICCARM__ ) /*!< IAR Compiler */ #pragma data_alignment=4 #endif #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ -__ALIGN_BEGIN uint8_t APP_Rx_Buffer [APP_RX_DATA_SIZE] __ALIGN_END ; +__ALIGN_BEGIN uint8_t USB_Tx_Buffer [USB_TX_BUFFER_SIZE] __ALIGN_END ; + +volatile uint32_t USB_Tx_Buffer_head = 0; +volatile uint32_t USB_Tx_Buffer_tail = 0; +volatile uint32_t USB_Tx_failed_counter = 0; #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED #if defined ( __ICCARM__ ) /*!< IAR Compiler */ @@ -178,19 +241,15 @@ __ALIGN_BEGIN uint8_t APP_Rx_Buffer [APP_RX_DATA_SIZE] __ALIGN_END ; #endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ __ALIGN_BEGIN uint8_t CmdBuff[CDC_CMD_PACKET_SZE] __ALIGN_END ; -volatile uint32_t APP_Rx_ptr_in = 0; -volatile uint32_t APP_Rx_ptr_out = 0; -volatile uint32_t APP_Rx_length = 0; - -volatile uint16_t USB_Rx_length = 0; -volatile uint16_t USB_Rx_ptr = 0; - volatile uint8_t USB_Tx_State = 0; volatile uint8_t USB_Rx_State = 0; +volatile uint8_t USB_Serial_Open = 0; static uint32_t cdcCmd = 0xFF; static uint32_t cdcLen = 0; +static uint8_t cdcConfigured = 0; + /* CDC interface class callbacks structure */ USBD_Class_cb_TypeDef USBD_CDC_cb = { @@ -418,6 +477,24 @@ __ALIGN_BEGIN uint8_t usbd_cdc_OtherCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_EN * @} */ +static inline void usbd_cdc_Change_Open_State(uint8_t state) { + if (state != USB_Serial_Open) { + DEBUG("USB Serial state: %d", state); + if (state) { + USB_Tx_failed_counter = 0; + // Also flush everything in TX buffer + uint32_t USB_Tx_length; + USB_Tx_length = ring_data_contig(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_head, USB_Tx_Buffer_tail); + if (USB_Tx_length) + USB_Tx_Buffer_tail = ring_wrap(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_tail + USB_Tx_length); + + USB_Tx_State = 0; + USB_Rx_State = 1; + } + USB_Serial_Open = state; + } +} + /** @defgroup usbd_cdc_Private_Functions * @{ */ @@ -434,6 +511,8 @@ static uint8_t usbd_cdc_Init (void *pdev, { uint8_t *pbuf; + usbd_cdc_DeInit(pdev, cfgidx); + /* Open EP IN */ DCD_EP_Open(pdev, CDC_IN_EP, @@ -459,6 +538,9 @@ static uint8_t usbd_cdc_Init (void *pdev, /* Initialize the Interface physical components */ APP_FOPS.pIf_Init(); + USB_Rx_State = 1; + cdcConfigured = 1; + /* Prepare Out endpoint to receive next packet */ DCD_EP_PrepareRx(pdev, CDC_OUT_EP, @@ -478,20 +560,39 @@ static uint8_t usbd_cdc_Init (void *pdev, static uint8_t usbd_cdc_DeInit (void *pdev, uint8_t cfgidx) { - /* Open EP IN */ - DCD_EP_Close(pdev, - CDC_IN_EP); + usbd_cdc_Change_Open_State(0); - /* Open EP OUT */ - DCD_EP_Close(pdev, - CDC_OUT_EP); + if (cdcConfigured) { + if (USB_Tx_State) { + DCD_EP_Flush(pdev, CDC_IN_EP); + } - /* Open Command IN EP */ - DCD_EP_Close(pdev, - CDC_CMD_EP); + /* Close EP IN */ + DCD_EP_Close(pdev, + CDC_IN_EP); - /* Restore default state of the Interface physical components */ - APP_FOPS.pIf_DeInit(); + /* Close EP OUT */ + DCD_EP_Close(pdev, + CDC_OUT_EP); + + /* Close Command IN EP */ + DCD_EP_Close(pdev, + CDC_CMD_EP); + + /* Restore default state of the Interface physical components */ + APP_FOPS.pIf_DeInit(); + } + + usbd_cdc_Change_Open_State(0); + + cdcConfigured = 0; + USB_Tx_State = 0; + USB_Rx_State = 0; + USB_Rx_Buffer_head = 0; + USB_Rx_Buffer_tail = 0; + USB_Rx_Buffer_length = USB_RX_BUFFER_SIZE; + USB_Tx_Buffer_tail = 0; + USB_Tx_Buffer_head = 0; return USBD_OK; } @@ -605,6 +706,8 @@ static uint8_t usbd_cdc_Setup (void *pdev, */ static uint8_t usbd_cdc_EP0_RxReady (void *pdev) { + usbd_cdc_Change_Open_State(1); + if (cdcCmd != NO_CMD) { /* Process the data */ @@ -617,6 +720,16 @@ static uint8_t usbd_cdc_EP0_RxReady (void *pdev) return USBD_OK; } +static inline uint32_t usbd_Last_Tx_Packet_size(void *pdev, uint8_t epnum) +{ + return ((USB_OTG_CORE_HANDLE*)pdev)->dev.in_ep[epnum].xfer_len; +} + +static inline uint32_t usbd_Last_Rx_Packet_size(void *pdev, uint8_t epnum) +{ + return ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; +} + /** * @brief usbd_audio_DataIn * Data sent on non-control IN endpoint @@ -626,44 +739,69 @@ static uint8_t usbd_cdc_EP0_RxReady (void *pdev) */ static uint8_t usbd_cdc_DataIn (void *pdev, uint8_t epnum) { - uint16_t USB_Tx_ptr; - uint16_t USB_Tx_length; + uint32_t USB_Tx_length; - if (USB_Tx_State == 1) - { - if (APP_Rx_length == 0) - { - USB_Tx_State = 0; - } - else - { - if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE){ - USB_Tx_ptr = APP_Rx_ptr_out; - USB_Tx_length = CDC_DATA_IN_PACKET_SIZE; + usbd_cdc_Change_Open_State(1); - APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE; - APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE; - } - else - { - USB_Tx_ptr = APP_Rx_ptr_out; - USB_Tx_length = APP_Rx_length; + if (!USB_Tx_State) + return USBD_OK; - APP_Rx_ptr_out += APP_Rx_length; - APP_Rx_length = 0; - } + USB_Tx_length = ring_data_contig(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_head, USB_Tx_Buffer_tail); - /* Prepare the available data buffer to be sent on IN endpoint */ - DCD_EP_Tx (pdev, - CDC_IN_EP, - (uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr], - USB_Tx_length); - } + if (USB_Tx_length) { + USB_Tx_length = MIN(USB_Tx_length, CDC_DATA_IN_PACKET_SIZE); + } else if (usbd_Last_Tx_Packet_size(pdev, epnum) != CDC_DATA_IN_PACKET_SIZE) { + USB_Tx_State = 0; + return USBD_OK; } + /* Prepare the available data buffer to be sent on IN endpoint */ + DCD_EP_Tx (pdev, + CDC_IN_EP, + (uint8_t*)&USB_Tx_Buffer[USB_Tx_Buffer_tail], + USB_Tx_length); + + USB_Tx_Buffer_tail = ring_wrap(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_tail + USB_Tx_length); return USBD_OK; } +static inline int usbd_cdc_Start_Rx(void *pdev) +{ + + /* USB_Rx_Buffer_length is used here to keep track of + * available _contiguous_ buffer space in USB_Rx_Buffer. + */ + uint32_t USB_Rx_length; + if (USB_Rx_Buffer_head >= USB_Rx_Buffer_tail) + USB_Rx_Buffer_length = USB_RX_BUFFER_SIZE; + + USB_Rx_length = ring_space_contig(USB_Rx_Buffer_length, USB_Rx_Buffer_head, USB_Rx_Buffer_tail); + + if (USB_Rx_length < CDC_DATA_OUT_PACKET_SIZE) { + USB_Rx_length = ring_space_wrapped(USB_Rx_Buffer_length, USB_Rx_Buffer_head, USB_Rx_Buffer_tail); + if (USB_Rx_length < CDC_DATA_OUT_PACKET_SIZE) { + if (USB_Rx_State) { + USB_Rx_State = 0; + DCD_SetEPStatus(pdev, CDC_OUT_EP, USB_OTG_EP_TX_NAK); + } + return 0; + } + USB_Rx_Buffer_length = USB_Rx_Buffer_head; + USB_Rx_Buffer_head = 0; + if (USB_Rx_Buffer_tail == USB_Rx_Buffer_length) + USB_Rx_Buffer_tail = 0; + } + if (!USB_Rx_State) { + USB_Rx_State = 1; + DCD_SetEPStatus(pdev, CDC_OUT_EP, USB_OTG_EP_TX_VALID); + } + DCD_EP_PrepareRx(pdev, + CDC_OUT_EP, + USB_Rx_Buffer + USB_Rx_Buffer_head, + CDC_DATA_OUT_PACKET_SIZE); + return 1; +} + /** * @brief usbd_cdc_DataOut * Data received on non-control Out endpoint @@ -673,22 +811,61 @@ static uint8_t usbd_cdc_DataIn (void *pdev, uint8_t epnum) */ static uint8_t usbd_cdc_DataOut (void *pdev, uint8_t epnum) { - USB_Rx_ptr = 0; + uint32_t USB_Rx_Count = usbd_Last_Rx_Packet_size(pdev, epnum); + USB_Rx_Buffer_head = ring_wrap(USB_Rx_Buffer_length, USB_Rx_Buffer_head + USB_Rx_Count); - /* Get the received data buffer and update the counter */ - USB_Rx_length = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; + // Serial port is definitely open + usbd_cdc_Change_Open_State(1); - /* USB data will be immediately processed, this allow next USB traffic being - NAKed till the end of the application Xfer */ - APP_FOPS.pIf_DataRx(USB_Rx_Buffer, USB_Rx_length); - - /* Prepare Out endpoint to receive next packet */ - //DCD_EP_PrepareRx() is now called in USB_USART_Receive_Data() in usb_hal.c - USB_Rx_State = 1; + usbd_cdc_Start_Rx(pdev); return USBD_OK; } +static void usbd_cdc_Schedule_Out(void *pdev) +{ + if (!USB_Rx_State) + usbd_cdc_Start_Rx(pdev); +} + +static void usbd_cdc_Schedule_In(void *pdev) +{ + uint32_t USB_Tx_length; + USB_Tx_length = ring_data_contig(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_head, USB_Tx_Buffer_tail); + + if (USB_Tx_State) { + if (USB_Serial_Open) { + USB_Tx_failed_counter++; + if (USB_Tx_failed_counter >= 500) { + usbd_cdc_Change_Open_State(0); + // Completely flush TX buffer + DCD_EP_Flush(pdev, CDC_IN_EP); + // Send ZLP + DCD_EP_Tx(pdev, CDC_IN_EP, NULL, 0); + if (USB_Tx_length) + USB_Tx_Buffer_tail = ring_wrap(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_tail + USB_Tx_length); + + USB_Tx_State = 0; + } + } + return; + } + + if (!USB_Tx_length) + return; + + USB_Tx_State = 1; + USB_Tx_failed_counter = 0; + + USB_Tx_length = MIN(USB_Tx_length, CDC_DATA_IN_PACKET_SIZE); + DCD_EP_Tx (pdev, + CDC_IN_EP, + (uint8_t*)&USB_Tx_Buffer[USB_Tx_Buffer_tail], + USB_Tx_length); + + USB_Tx_Buffer_tail = ring_wrap(USB_TX_BUFFER_SIZE, USB_Tx_Buffer_tail + USB_Tx_length); +} + /** * @brief usbd_audio_SOF * Start Of Frame event management @@ -705,77 +882,13 @@ static uint8_t usbd_cdc_SOF (void *pdev) /* Reset the frame counter */ FrameCount = 0; - /* Check the data to be sent through IN pipe */ - Handle_USBAsynchXfer(pdev); + usbd_cdc_Schedule_In(pdev); + usbd_cdc_Schedule_Out(pdev); } return USBD_OK; } -/** - * @brief Handle_USBAsynchXfer - * Send data to USB - * @param pdev: instance - * @retval None - */ -static void Handle_USBAsynchXfer (void *pdev) -{ - uint16_t USB_Tx_ptr; - uint16_t USB_Tx_length; - - if(USB_Tx_State != 1) - { - if (APP_Rx_ptr_out == APP_RX_DATA_SIZE) - { - APP_Rx_ptr_out = 0; - } - - if(APP_Rx_ptr_out == APP_Rx_ptr_in) - { - USB_Tx_State = 0; - return; - } - - if(APP_Rx_ptr_out > APP_Rx_ptr_in) /* rollback */ - { - APP_Rx_length = APP_RX_DATA_SIZE - APP_Rx_ptr_out; - - } - else - { - APP_Rx_length = APP_Rx_ptr_in - APP_Rx_ptr_out; - - } -#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED - APP_Rx_length &= ~0x03; -#endif /* USB_OTG_HS_INTERNAL_DMA_ENABLED */ - - if (APP_Rx_length > CDC_DATA_IN_PACKET_SIZE) - { - USB_Tx_ptr = APP_Rx_ptr_out; - USB_Tx_length = CDC_DATA_IN_PACKET_SIZE; - - APP_Rx_ptr_out += CDC_DATA_IN_PACKET_SIZE; - APP_Rx_length -= CDC_DATA_IN_PACKET_SIZE; - } - else - { - USB_Tx_ptr = APP_Rx_ptr_out; - USB_Tx_length = APP_Rx_length; - - APP_Rx_ptr_out += APP_Rx_length; - APP_Rx_length = 0; - } - DCD_EP_Tx (pdev, - CDC_IN_EP, - (uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr], - USB_Tx_length); - USB_Tx_State = 1; - - } - -} - /** * @brief USBD_cdc_GetCfgDesc * Return configuration descriptor diff --git a/user/tests/wiring/api/wiring.cpp b/user/tests/wiring/api/wiring.cpp index aa1ca695ae..a4db668ea6 100644 --- a/user/tests/wiring/api/wiring.cpp +++ b/user/tests/wiring/api/wiring.cpp @@ -67,7 +67,12 @@ test(api_wiring_usartserial) { API_COMPILE(Serial1.blockOnOverrun(true)); API_COMPILE(Serial1.availableForWrite()); +} +test(api_wiring_usbserial) { + API_COMPILE(Serial.blockOnOverrun(false)); + API_COMPILE(Serial.blockOnOverrun(true)); + API_COMPILE(Serial.availableForWrite()); } void TIM3_callback() diff --git a/wiring/inc/spark_wiring_usbserial.h b/wiring/inc/spark_wiring_usbserial.h index c90c8cf8dc..0719b7f1ce 100644 --- a/wiring/inc/spark_wiring_usbserial.h +++ b/wiring/inc/spark_wiring_usbserial.h @@ -47,9 +47,12 @@ class USBSerial : public Stream virtual size_t write(uint8_t byte); virtual int read(); + virtual int availableForWrite(void); virtual int available(); virtual void flush(); + virtual void blockOnOverrun(bool); + #if PLATFORM_THREADING os_mutex_recursive_t get_mutex() { @@ -81,6 +84,9 @@ class USBSerial : public Stream } using Print::write; + +private: + bool _blocking; }; USBSerial& _fetch_global_serial(); diff --git a/wiring/src/spark_wiring_usbserial.cpp b/wiring/src/spark_wiring_usbserial.cpp index 60e383b3c1..3ea7469d33 100644 --- a/wiring/src/spark_wiring_usbserial.cpp +++ b/wiring/src/spark_wiring_usbserial.cpp @@ -31,6 +31,7 @@ // USBSerial::USBSerial() { + _blocking = true; } // @@ -54,6 +55,11 @@ int USBSerial::read() return USB_USART_Receive_Data(false); } +int USBSerial::availableForWrite() +{ + return USB_USART_Available_Data_For_Write(); +} + int USBSerial::available() { return USB_USART_Available_Data(); @@ -61,12 +67,21 @@ int USBSerial::available() size_t USBSerial::write(uint8_t byte) { - USB_USART_Send_Data(byte); - return 1; + if (USB_USART_Available_Data_For_Write() > 0 || _blocking) { + USB_USART_Send_Data(byte); + return 1; + } + return 0; } void USBSerial::flush() { + USB_USART_Flush_Data(); +} + +void USBSerial::blockOnOverrun(bool block) +{ + _blocking = block; } int USBSerial::peek()