diff --git a/hal/src/core/usb_settings.h b/hal/src/core/usb_settings.h new file mode 100644 index 0000000000..093565a691 --- /dev/null +++ b/hal/src/core/usb_settings.h @@ -0,0 +1,26 @@ +/** + Copyright (c) 2016 Particle Industries, Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . + ****************************************************************************** + */ + +#ifndef USB_SETTINGS_H_ +#define USB_SETTINGS_H_ + +#define USB_RX_BUFFER_SIZE 256 + +//#define USB_SERIAL_USERSPACE_BUFFERS 0 + +#endif /* USB_SETTINGS_H_ */ \ No newline at end of file diff --git a/hal/src/stm32f2xx/usb_hal.c b/hal/src/stm32f2xx/usb_hal.c index 131b87d9c1..2918e11d80 100644 --- a/hal/src/stm32f2xx/usb_hal.c +++ b/hal/src/stm32f2xx/usb_hal.c @@ -28,12 +28,15 @@ /* Includes ------------------------------------------------------------------*/ #include "usb_hal.h" -#include "usbd_cdc_core.h" +#include "usb_settings.h" +#include "usbd_mcdc.h" #include "usbd_usr.h" #include "usb_conf.h" #include "usbd_desc.h" #include "delay_hal.h" #include "interrupts_hal.h" +#include "ringbuf_helper.h" +#include /* Private typedef -----------------------------------------------------------*/ @@ -52,18 +55,10 @@ extern uint32_t USBD_OTG_EP1OUT_ISR_Handler(USB_OTG_CORE_HANDLE *pdev); /* Extern variables ----------------------------------------------------------*/ #ifdef USB_CDC_ENABLE -extern volatile LINE_CODING linecoding; -extern volatile uint8_t USB_DEVICE_CONFIGURED; -extern volatile uint8_t USB_Rx_Buffer[]; -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; +static void (*LineCoding_BitRate_Handler)(uint32_t bitRate) = NULL; +USBD_MCDC_Instance_Data USBD_MCDC = {{0}}; +__ALIGN_BEGIN static uint8_t USBD_MCDC_Rx_Buffer[USB_RX_BUFFER_SIZE]; +__ALIGN_BEGIN static uint8_t USBD_MCDC_Tx_Buffer[USB_TX_BUFFER_SIZE]; #endif #if defined (USB_CDC_ENABLE) || defined (USB_HID_ENABLE) @@ -82,7 +77,7 @@ void SPARK_USB_Setup(void) USB_OTG_HS_CORE_ID, #endif &USR_desc, - &USBD_CDC_cb, + &USBD_MCDC_cb, NULL); } @@ -100,6 +95,18 @@ void Get_SerialNum(void) #endif #ifdef USB_CDC_ENABLE + +uint16_t USB_USART_Request_Handler(/*USBD_Composite_Class_Data* cls, */uint32_t cmd, uint8_t* buf, uint32_t len) { + if (cmd == SET_LINE_CODING && LineCoding_BitRate_Handler) { + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + if (priv) + LineCoding_BitRate_Handler(priv->linecoding.bitrate); + } + + return 0; +} + /******************************************************************************* * Function Name : USB_USART_Init * Description : Start USB-USART protocol. @@ -108,9 +115,9 @@ void Get_SerialNum(void) *******************************************************************************/ void USB_USART_Init(uint32_t baudRate) { - if (linecoding.bitrate != baudRate) + if (USBD_MCDC.linecoding.bitrate != baudRate) { - if (!baudRate && linecoding.bitrate > 0) + if (!baudRate && USBD_MCDC.linecoding.bitrate > 0) { // Deconfigure CDC class endpoints USBD_ClrCfg(&USB_OTG_dev, 0); @@ -130,8 +137,18 @@ void USB_USART_Init(uint32_t baudRate) // Soft reattach // USB_OTG_dev.regs.DREGS->DCTL |= 0x02; } - else if (!linecoding.bitrate) + else if (!USBD_MCDC.linecoding.bitrate) { + memset((void*)&USBD_MCDC, 0, sizeof(USBD_MCDC)); + USBD_MCDC.ep_in_data = CDC_IN_EP; + USBD_MCDC.ep_in_int = CDC_CMD_EP; + USBD_MCDC.ep_out_data = CDC_OUT_EP; + USBD_MCDC.rx_buffer = USBD_MCDC_Rx_Buffer; + USBD_MCDC.tx_buffer = USBD_MCDC_Tx_Buffer; + USBD_MCDC.rx_buffer_size = USB_RX_BUFFER_SIZE; + USBD_MCDC.tx_buffer_size = USB_TX_BUFFER_SIZE; + USBD_MCDC.name = "Serial"; + USBD_MCDC.req_handler = USB_USART_Request_Handler; //Initialize USB device SPARK_USB_Setup(); @@ -143,26 +160,41 @@ void USB_USART_Init(uint32_t baudRate) // USB_OTG_dev.regs.DREGS->DCTL |= 0x02; } //linecoding.bitrate will be overwritten by USB Host - linecoding.bitrate = baudRate; + USBD_MCDC.linecoding.bitrate = baudRate; } } unsigned USB_USART_Baud_Rate(void) { - return linecoding.bitrate; + return USBD_MCDC.linecoding.bitrate; +} + +static bool USB_WillPreempt() +{ + if (HAL_IsISR()) { +#ifdef USE_USB_OTG_FS + int32_t irq = OTG_FS_IRQn; +#else + int32_t irq = OTG_HS_IRQn; +#endif + if (!HAL_WillPreempt(irq, HAL_ServicedIRQn())) + return false; + } + + return true; } + void USB_USART_LineCoding_BitRate_Handler(void (*handler)(uint32_t bitRate)) { //Init USB Serial first before calling the linecoding handler USB_USART_Init(9600); - //Set the system defined custom handler - SetLineCodingBitRateHandler(handler); + LineCoding_BitRate_Handler = handler; } static inline bool USB_USART_Connected() { - return linecoding.bitrate > 0 && USB_OTG_dev.dev.device_status == USB_OTG_CONFIGURED && USB_Serial_Open; + return USBD_MCDC.linecoding.bitrate > 0 && USB_OTG_dev.dev.device_status == USB_OTG_CONFIGURED && USBD_MCDC.serial_open; } /******************************************************************************* @@ -175,10 +207,9 @@ uint8_t USB_USART_Available_Data(void) { 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; + available = ring_data_avail(USBD_MCDC.rx_buffer_length, + USBD_MCDC.rx_buffer_head, + USBD_MCDC.rx_buffer_tail); HAL_enable_irq(state); return available; } @@ -194,11 +225,9 @@ int32_t USB_USART_Receive_Data(uint8_t peek) if (USB_USART_Available_Data() > 0) { int state = HAL_disable_irq(); - uint8_t data = USB_Rx_Buffer[USB_Rx_Buffer_tail]; + uint8_t data = USBD_MCDC.rx_buffer[USBD_MCDC.rx_buffer_tail]; if (!peek) { - USB_Rx_Buffer_tail++; - if (USB_Rx_Buffer_tail == USB_Rx_Buffer_length) - USB_Rx_Buffer_tail = 0; + USBD_MCDC.rx_buffer_tail = ring_wrap(USBD_MCDC.rx_buffer_length, USBD_MCDC.rx_buffer_tail + 1); } HAL_enable_irq(state); return data; @@ -217,9 +246,11 @@ 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; + int state = HAL_disable_irq(); + int32_t available = ring_space_avail(USBD_MCDC.tx_buffer_size, + USBD_MCDC.tx_buffer_head, + USBD_MCDC.tx_buffer_tail); + HAL_enable_irq(state); return available; } @@ -232,22 +263,26 @@ int32_t USB_USART_Available_Data_For_Write(void) * Input : Data. * Return : None. *******************************************************************************/ -void USB_USART_Send_Data(uint8_t Data) +void USB_USART_Send_Data(uint8_t data) { + int32_t ret = -1; int32_t available = 0; do { available = USB_USART_Available_Data_For_Write(); } - while (available < 1 && available != -1); + while (available < 1 && available != -1 && USB_WillPreempt()); // Confirm once again that the Host is connected - if (USB_USART_Connected()) + int32_t state = HAL_disable_irq(); + if (USB_USART_Connected() && available > 0) { - uint32_t head = USB_Tx_Buffer_head; - - USB_Tx_Buffer[head] = Data; - - USB_Tx_Buffer_head = ++head % USB_TX_BUFFER_SIZE; + USBD_MCDC.tx_buffer[USBD_MCDC.tx_buffer_head] = data; + USBD_MCDC.tx_buffer_head = ring_wrap(USBD_MCDC.tx_buffer_size, USBD_MCDC.tx_buffer_head + 1); + ret = 1; } + HAL_enable_irq(state); + + //return ret; + (void)ret; } /******************************************************************************* @@ -258,9 +293,9 @@ void USB_USART_Send_Data(uint8_t Data) *******************************************************************************/ void USB_USART_Flush_Data(void) { - while(USB_USART_Connected() && USB_USART_Available_Data_For_Write() != (USB_TX_BUFFER_SIZE - 1)); + while(USB_USART_Connected() && USB_USART_Available_Data_For_Write() != (USBD_MCDC.tx_buffer_size - 1) && USB_WillPreempt()); // We should also wait for USB_Tx_State to become 0, as hardware might still be busy transmitting data - while(USB_Tx_State == 1); + while(USBD_MCDC.tx_state == 1); } #endif diff --git a/hal/src/stm32f2xx/usb_settings.h b/hal/src/stm32f2xx/usb_settings.h new file mode 100644 index 0000000000..6f3049f31f --- /dev/null +++ b/hal/src/stm32f2xx/usb_settings.h @@ -0,0 +1,28 @@ +/** + Copyright (c) 2016 Particle Industries, Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . + ****************************************************************************** + */ + +#ifndef USB_SETTINGS_H_ +#define USB_SETTINGS_H_ + +#define USB_TX_BUFFER_SIZE 129 /* 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 USB_SERIAL_USERSPACE_BUFFERS 1 + +#endif /* USB_SETTINGS_H_ */ \ No newline at end of file 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 a6ed06686f..27c7b6f7f9 100644 --- a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/usbd_conf.h +++ b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/usbd_conf.h @@ -86,9 +86,9 @@ #define CDC_CMD_PACKET_SZE 8 /* Control Endpoint Packet size */ #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 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/STM32_USB_Device_Driver/inc/usbd_mcdc.h b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/inc/usbd_mcdc.h new file mode 100644 index 0000000000..410c1dca8f --- /dev/null +++ b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/inc/usbd_mcdc.h @@ -0,0 +1,101 @@ +#ifndef USBD_MCDC_H_ +#define USBD_MCDC_H_ + +#include +#include "usbd_ioreq.h" + +#define USBD_MCDC_CONFIG_DESC_SIZE (67) +#define USBD_MCDC_DESC_SIZE (USBD_MCDC_CONFIG_DESC_SIZE - 9) + +#define CDC_DESCRIPTOR_TYPE 0x21 + +#define DEVICE_CLASS_CDC 0x02 +#define DEVICE_SUBCLASS_CDC 0x00 + +#define USB_DEVICE_DESCRIPTOR_TYPE 0x01 +#define USB_CONFIGURATION_DESCRIPTOR_TYPE 0x02 +#define USB_STRING_DESCRIPTOR_TYPE 0x03 +#define USB_INTERFACE_DESCRIPTOR_TYPE 0x04 +#define USB_ENDPOINT_DESCRIPTOR_TYPE 0x05 + +#define STANDARD_ENDPOINT_DESC_SIZE 0x09 + +#define CDC_DATA_IN_PACKET_SIZE CDC_DATA_MAX_PACKET_SIZE + +#define CDC_DATA_OUT_PACKET_SIZE CDC_DATA_MAX_PACKET_SIZE + +/*---------------------------------------------------------------------*/ +/* CDC definitions */ +/*---------------------------------------------------------------------*/ + +/**************************************************/ +/* CDC Requests */ +/**************************************************/ +#define SEND_ENCAPSULATED_COMMAND 0x00 +#define GET_ENCAPSULATED_RESPONSE 0x01 +#define SET_COMM_FEATURE 0x02 +#define GET_COMM_FEATURE 0x03 +#define CLEAR_COMM_FEATURE 0x04 +#define SET_LINE_CODING 0x20 +#define GET_LINE_CODING 0x21 +#define SET_CONTROL_LINE_STATE 0x22 +#define SEND_BREAK 0x23 +#define NO_CMD 0xFF +#define ACM_SERIAL_STATE 0x20 + +#define CDC_DTR 0x01 +#define CDC_RTS 0x02 + +typedef struct USBD_MCDC_Instance_Data { + // // Back-reference to USBD_Composite_Class_Data + // void* cls; + + LINE_CODING linecoding; + uint8_t ep_in_data; + uint8_t ep_out_data; + uint8_t ep_in_int; + + __ALIGN_BEGIN volatile uint32_t alt_set __ALIGN_END; + + uint8_t* rx_buffer; + uint16_t rx_buffer_size; + volatile uint32_t rx_buffer_head; + volatile uint32_t rx_buffer_tail; + volatile uint32_t rx_buffer_length; + + uint8_t* tx_buffer; + uint16_t tx_buffer_size; + volatile uint32_t tx_buffer_head; + volatile uint32_t tx_buffer_tail; + volatile uint32_t tx_failed_counter; + volatile uint32_t tx_buffer_last; + + __ALIGN_BEGIN uint8_t cmd_buffer[CDC_CMD_PACKET_SZE] __ALIGN_END; + + volatile uint8_t rx_state; + volatile uint8_t tx_state; + volatile uint8_t serial_open; + + uint8_t configured; + + uint32_t cmd; + uint32_t cmd_len; + + uint32_t frame_count; + + uint8_t ctrl_line; + + uint16_t (*req_handler) (/*USBD_Composite_Class_Data* cls, */uint32_t cmd, uint8_t* buf, uint32_t len); + + const char* name; + + #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED + // Temporary aligned buffer + __ALIGN_BEGIN uint8_t descriptor[USBD_MCDC_CONFIG_DESC_SIZE] __ALIGN_END; + #endif +} USBD_MCDC_Instance_Data; + +// extern USBD_Multi_Instance_cb_Typedef USBD_MCDC_cb; +extern USBD_Class_cb_TypeDef USBD_MCDC_cb; + +#endif /* USBD_MCDC_H_ */ diff --git a/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/sources.mk b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/sources.mk index 69ae6b2593..41756b3025 100644 --- a/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/sources.mk +++ b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/sources.mk @@ -30,8 +30,7 @@ CSRC += $(TARGET_USB_FS_SRC_PATH)/usbd_sflash_if.c endif # cdc/usbserial specific files -CSRC += $(TARGET_USB_FS_SRC_PATH)/usbd_cdc_core.c -CSRC += $(TARGET_USB_FS_SRC_PATH)/usbd_cdc_if.c +CSRC += $(TARGET_USB_FS_SRC_PATH)/usbd_mcdc.c # C++ source files included in this build. CPPSRC += diff --git a/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_mcdc.c b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_mcdc.c new file mode 100644 index 0000000000..f2dac9701a --- /dev/null +++ b/platform/MCU/STM32F2xx/STM32_USB_Device_Driver/src/usbd_mcdc.c @@ -0,0 +1,664 @@ +#include +#include "usbd_mcdc.h" +#include "usbd_desc.h" +#include "usbd_req.h" +#include "debug.h" +#include "ringbuf_helper.h" + +// LOG_SOURCE_CATEGORY("usb.mcdc") + +#define USBD_MCDC_USRSTR_BASE 10 + +#ifndef MIN +#define MIN(a, b) (a) < (b) ? (a) : (b) +#endif +#ifndef MAX +#define MAX(a, b) (a) > (b) ? (a) : (b) +#endif + +static uint8_t USBD_MCDC_Init (void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t cfgidx); +static uint8_t USBD_MCDC_DeInit (void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t cfgidx); +static uint8_t USBD_MCDC_Setup (void* pdev/*, USBD_Composite_Class_Data* cls*/, USB_SETUP_REQ *req); +static uint8_t USBD_MCDC_EP0_RxReady (void* pdev/*, USBD_Composite_Class_Data* cls*/); +static uint8_t USBD_MCDC_DataIn (void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t epnum); +static uint8_t USBD_MCDC_DataOut (void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t epnum); +static uint8_t USBD_MCDC_SOF (void* pdev/*, USBD_Composite_Class_Data* cls*/); + +static uint8_t* USBD_MCDC_GetCfgDesc (uint8_t speed/*, USBD_Composite_Class_Data* cls*/, uint16_t *length); +static uint8_t* USBD_MCDC_GetUsrStrDescriptor(uint8_t speed/*, USBD_Composite_Class_Data* cls*/, uint8_t index, uint16_t* length); + +static uint16_t USBD_MCDC_Request_Handler(void* pdev/*, USBD_Composite_Class_Data* cls*/, uint32_t cmd, uint8_t* buf, uint32_t len); +static int USBD_MCDC_Start_Rx(void *pdev, USBD_MCDC_Instance_Data* priv); +static void USBD_MCDC_Schedule_Out(void *pdev, USBD_MCDC_Instance_Data* priv); + +static const uint8_t USBD_MCDC_CfgDesc[USBD_MCDC_CONFIG_DESC_SIZE] __ALIGN_END; + +#ifdef CDC_CMD_EP_SHARED +static uint8_t USBD_MCDC_Cmd_Ep_Refcount = 0; +#endif + +extern USBD_MCDC_Instance_Data USBD_MCDC; + +/* CDC interface class callbacks structure */ +USBD_Class_cb_TypeDef USBD_MCDC_cb = +{ + USBD_MCDC_Init, + USBD_MCDC_DeInit, + USBD_MCDC_Setup, + NULL, /* EP0_TxSent, */ + USBD_MCDC_EP0_RxReady, + USBD_MCDC_DataIn, + USBD_MCDC_DataOut, + USBD_MCDC_SOF, + NULL, + NULL, + USBD_MCDC_GetCfgDesc, +#ifdef USE_USB_OTG_HS + USBD_MCDC_GetCfgDesc, /* use same cobfig as per FS */ +#endif /* USE_USB_OTG_HS */ + USBD_MCDC_GetUsrStrDescriptor, +}; +// USBD_Multi_Instance_cb_Typedef USBD_MCDC_cb = +// { +// USBD_MCDC_Init, +// USBD_MCDC_DeInit, +// USBD_MCDC_Setup, +// NULL, /* EP0_TxSent, */ +// USBD_MCDC_EP0_RxReady, +// USBD_MCDC_DataIn, +// USBD_MCDC_DataOut, +// USBD_MCDC_SOF, +// NULL, +// NULL, +// USBD_MCDC_GetCfgDesc, +// #ifdef USE_USB_OTG_HS +// USBD_MCDC_GetCfgDesc, /* use same cobfig as per FS */ +// #endif /* USE_USB_OTG_HS */ +// USBD_MCDC_GetUsrStrDescriptor, + +// }; + +static const uint8_t USBD_MCDC_CfgDesc[USBD_MCDC_CONFIG_DESC_SIZE] __ALIGN_END = +{ + /*Configuration Descriptor*/ + 0x09, /* bLength: Configuration Descriptor size */ + USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */ + USBD_MCDC_CONFIG_DESC_SIZE, /* wTotalLength:no of returned bytes */ + 0x00, + 0x02, /* bNumInterfaces: 2 interface */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ + 0xC0, /* bmAttributes: self powered */ + 0x32, /* MaxPower 100 mA */ + + /*---------------------------------------------------------------------------*/ + + /*Interface Descriptor */ + 0x09, /* bLength: Interface Descriptor size */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: Interface */ + /* Interface descriptor type */ + 0x00, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x01, /* bNumEndpoints: One endpoints used */ + 0x02, /* bInterfaceClass: Communication Interface Class */ + 0x02, /* bInterfaceSubClass: Abstract Control Model */ + 0x01, /* bInterfaceProtocol: Common AT commands */ + 0x00, /* iInterface: */ + + /*Header Functional Descriptor*/ + 0x05, /* bLength: Endpoint Descriptor size */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x00, /* bDescriptorSubtype: Header Func Desc */ + 0x10, /* bcdCDC: spec release number */ + 0x01, + + /*Call Management Functional Descriptor*/ + 0x05, /* bFunctionLength */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x01, /* bDescriptorSubtype: Call Management Func Desc */ + 0x00, /* bmCapabilities: D0+D1 */ + 0x01, /* bDataInterface: 1 */ + + /*ACM Functional Descriptor*/ + 0x04, /* bFunctionLength */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x02, /* bDescriptorSubtype: Abstract Control Management desc */ + 0x02, /* bmCapabilities */ + + /*Union Functional Descriptor*/ + 0x05, /* bFunctionLength */ + 0x24, /* bDescriptorType: CS_INTERFACE */ + 0x06, /* bDescriptorSubtype: Union func desc */ + 0x00, /* bMasterInterface: Communication class interface */ + 0x01, /* bSlaveInterface0: Data Class Interface */ + + /*Endpoint 2 Descriptor*/ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */ + CDC_CMD_EP, /* bEndpointAddress */ + 0x03, /* bmAttributes: Interrupt */ + LOBYTE(CDC_CMD_PACKET_SZE), /* wMaxPacketSize: */ + HIBYTE(CDC_CMD_PACKET_SZE), +#ifdef USE_USB_OTG_HS + 0x10, /* bInterval: */ +#else + 0xFF, /* bInterval: */ +#endif /* USE_USB_OTG_HS */ + + /*---------------------------------------------------------------------------*/ + + /*Data class interface descriptor*/ + 0x09, /* bLength: Endpoint Descriptor size */ + USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType: */ + 0x01, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x02, /* bNumEndpoints: Two endpoints used */ + 0x0A, /* bInterfaceClass: CDC */ + 0x00, /* bInterfaceSubClass: */ + 0x00, /* bInterfaceProtocol: */ + 0x00, /* iInterface: */ + + /*Endpoint OUT Descriptor*/ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */ + CDC_OUT_EP, /* bEndpointAddress */ + 0x02, /* bmAttributes: Bulk */ + LOBYTE(CDC_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize: */ + HIBYTE(CDC_DATA_MAX_PACKET_SIZE), + 0x00, /* bInterval: ignore for Bulk transfer */ + + /*Endpoint IN Descriptor*/ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: Endpoint */ + CDC_IN_EP, /* bEndpointAddress */ + 0x02, /* bmAttributes: Bulk */ + LOBYTE(CDC_DATA_MAX_PACKET_SIZE), /* wMaxPacketSize: */ + HIBYTE(CDC_DATA_MAX_PACKET_SIZE), + 0x00 /* bInterval: ignore for Bulk transfer */ +}; + +static void USBD_MCDC_Change_Open_State(void* pdev, USBD_MCDC_Instance_Data* priv, uint8_t state) { + if (state != priv->serial_open) { + // USBD_Composite_Class_Data* cls = (USBD_Composite_Class_Data*)priv->cls; + // (void)cls; + //LOG_DEBUG(TRACE, "[%s] USB Serial state: %d", priv->name, state); + if (state) { + priv->tx_failed_counter = 0; + // Also flush everything in TX buffer + priv->tx_buffer_head = priv->tx_buffer_tail = priv->tx_buffer_last = 0; + + priv->tx_state = 0; + USBD_MCDC_Schedule_Out(pdev, priv); + } + priv->serial_open = state; + } +} + +static uint8_t USBD_MCDC_Init(void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t cfgidx) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + USBD_MCDC_DeInit(pdev, /*cls,*/ cfgidx); + +#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED + memcpy(priv->descriptor, /*cls->cfg,*/ USBD_MCDC_CfgDesc, USBD_MCDC_DESC_SIZE); +#endif + + /* Open EP IN */ + DCD_EP_Open(pdev, + priv->ep_in_data, + CDC_DATA_IN_PACKET_SIZE, + USB_OTG_EP_BULK); + + /* Open EP OUT */ + DCD_EP_Open(pdev, + priv->ep_out_data, + CDC_DATA_OUT_PACKET_SIZE, + USB_OTG_EP_BULK); + +#ifdef CDC_CMD_EP_SHARED + if (USBD_MCDC_Cmd_Ep_Refcount++ == 0) { +#endif + if (priv->ep_in_int != priv->ep_in_data) { + /* Open Command IN EP */ + DCD_EP_Open(pdev, + priv->ep_in_int, + CDC_CMD_PACKET_SZE, + USB_OTG_EP_INT); + } +#ifdef CDC_CMD_EP_SHARED + } +#endif + priv->configured = 1; + priv->rx_state = 1; + + USBD_MCDC_Start_Rx(pdev, priv); + + return USBD_OK; +} + +static uint8_t USBD_MCDC_DeInit(void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t cfgidx) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + USBD_MCDC_Change_Open_State(pdev, priv, 0); + + if (priv->configured) { + if (priv->tx_state) { + DCD_EP_Flush(pdev, priv->ep_in_data); + } + + /* Close EP IN */ + DCD_EP_Close(pdev, + priv->ep_in_data); + + /* Close EP OUT */ + DCD_EP_Close(pdev, + priv->ep_out_data); +#ifdef CDC_CMD_EP_SHARED + if (--USBD_MCDC_Cmd_Ep_Refcount == 0) { +#endif + if (priv->ep_in_int != priv->ep_in_data) { + /* Close Command IN EP */ + DCD_EP_Close(pdev, + priv->ep_in_int); + } +#ifdef CDC_CMD_EP_SHARED + } +#endif + } + + USBD_MCDC_Change_Open_State(pdev, priv, 0); + + priv->configured = 0; + priv->tx_state = 0; + priv->rx_state = 0; + priv->rx_buffer_head = 0; + priv->rx_buffer_tail = 0; + priv->rx_buffer_length = priv->rx_buffer_size; + priv->tx_buffer_head = 0; + priv->tx_buffer_tail = 0; + priv->tx_buffer_last = 0; + priv->frame_count = 0; + priv->cmd = NO_CMD; + priv->ctrl_line = 0x00; + + return USBD_OK; +} + +static uint8_t USBD_MCDC_Setup(void* pdev/*, USBD_Composite_Class_Data* cls*/, USB_SETUP_REQ *req) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + uint16_t len = 0; + uint8_t* pbuf = NULL; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + /* CDC Class Requests -------------------------------*/ + case USB_REQ_TYPE_CLASS : + /* Check if the request is a data setup packet */ + if (req->wLength) + { + /* Check if the request is Device-to-Host */ + if (req->bmRequest & 0x80) + { + /* Get the data to be sent to Host from interface layer */ + USBD_MCDC_Request_Handler(pdev, /*cls,*/ req->bRequest, priv->cmd_buffer, req->wLength); + + /* Send the data to the host */ + USBD_CtlSendData (pdev, + priv->cmd_buffer, + req->wLength); + } + else /* Host-to-Device requeset */ + { + /* Set the value of the current command to be processed */ + priv->cmd = req->bRequest; + priv->cmd_len = req->wLength; + + /* Prepare the reception of the buffer over EP0 + Next step: the received data will be managed in usbd_cdc_EP0_TxSent() + function. */ + USBD_CtlPrepareRx (pdev, + priv->cmd_buffer, + req->wLength); + } + } + else /* No Data request */ + { + /* Transfer the command to the interface layer */ + USBD_MCDC_Request_Handler(pdev, /*cls,*/ req->bRequest, (uint8_t*)&req->wValue, sizeof(req->wValue)); + } + + return USBD_OK; + + /* Standard Requests -------------------------------*/ + case USB_REQ_TYPE_STANDARD: { + switch (req->bRequest) + { + case USB_REQ_GET_DESCRIPTOR: + if( (req->wValue >> 8) == CDC_DESCRIPTOR_TYPE) + { + // For CDC this request should never arrive probably + #ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED + pbuf = priv->descriptor; + #else + // pbuf = cls->cfg; + pbuf = (uint8_t*)USBD_MCDC_CfgDesc + 9; + #endif + len = MIN(USBD_MCDC_DESC_SIZE , req->wLength); + } + + USBD_CtlSendData (pdev, + pbuf, + len); + break; + + case USB_REQ_GET_INTERFACE : + USBD_CtlSendData (pdev, + (uint8_t *)&priv->alt_set, + 1); + break; + + case USB_REQ_SET_INTERFACE : + if ((uint8_t)(req->wValue) < USBD_ITF_MAX_NUM) + { + priv->alt_set = (uint8_t)(req->wValue); + } + else + { + /* Call the error management function (command will be nacked */ + USBD_CtlError (pdev, req); + } + break; + default: + USBD_CtlError(pdev, req); + return USBD_OK; + } + break; + } + + default: + USBD_CtlError (pdev, req); + return USBD_FAIL; + } + return USBD_OK; +} + +static uint8_t USBD_MCDC_EP0_RxReady(void* pdev/*, USBD_Composite_Class_Data* cls*/) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + + if (priv->cmd != NO_CMD) + { + // USBD_MCDC_Change_Open_State(priv, 1); + /* Process the data */ + USBD_MCDC_Request_Handler(pdev, /*cls,*/ priv->cmd, priv->cmd_buffer, priv->cmd_len); + + /* Reset the command variable to default value */ + priv->cmd = NO_CMD; + } + + 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; +} + +static uint8_t USBD_MCDC_DataIn(void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t epnum) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + uint32_t USB_Tx_length; + + if ((epnum | 0x80) != (priv->ep_in_data) && (epnum | 0x80) != (priv->ep_in_int)) { + return USBD_FAIL; + } + + USBD_MCDC_Change_Open_State(pdev, priv, 1); + + if (!priv->tx_state) + return USBD_OK; + + priv->tx_failed_counter = 0; + priv->tx_buffer_tail = ring_wrap(priv->tx_buffer_size, priv->tx_buffer_tail + priv->tx_buffer_last); + priv->tx_buffer_last = 0; + + USB_Tx_length = ring_data_contig(priv->tx_buffer_size, priv->tx_buffer_head, priv->tx_buffer_tail); + + 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) { + priv->tx_state = 0; + return USBD_OK; + } + + priv->tx_buffer_last = USB_Tx_length; + + /* Prepare the available data buffer to be sent on IN endpoint */ + DCD_EP_Tx (pdev, + priv->ep_in_data, + (uint8_t*)&priv->tx_buffer[priv->tx_buffer_tail], + USB_Tx_length); + + return USBD_OK; +} + +int USBD_MCDC_Start_Rx(void *pdev, USBD_MCDC_Instance_Data* priv) +{ + + /* 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 (priv->rx_buffer_head >= priv->rx_buffer_tail) + priv->rx_buffer_length = priv->rx_buffer_size; + + USB_Rx_length = ring_space_contig(priv->rx_buffer_length, priv->rx_buffer_head, priv->rx_buffer_tail); + + if (USB_Rx_length < CDC_DATA_OUT_PACKET_SIZE) { + USB_Rx_length = ring_space_wrapped(priv->rx_buffer_length, priv->rx_buffer_head, priv->rx_buffer_tail); + if (USB_Rx_length < CDC_DATA_OUT_PACKET_SIZE) { + if (priv->rx_state) { + priv->rx_state = 0; + DCD_SetEPStatus(pdev, priv->ep_out_data, USB_OTG_EP_RX_NAK); + } + return 0; + } + priv->rx_buffer_length = priv->rx_buffer_head; + priv->rx_buffer_head = 0; + if (priv->rx_buffer_tail == priv->rx_buffer_length) + priv->rx_buffer_tail = 0; + } + if (!priv->rx_state) { + priv->rx_state = 1; + DCD_SetEPStatus(pdev, priv->ep_out_data, USB_OTG_EP_RX_VALID); + } + + DCD_EP_PrepareRx(pdev, + priv->ep_out_data, + priv->rx_buffer + priv->rx_buffer_head, + CDC_DATA_OUT_PACKET_SIZE); + return 1; +} + +static uint8_t USBD_MCDC_DataOut(void* pdev/*, USBD_Composite_Class_Data* cls*/, uint8_t epnum) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + if (epnum != (priv->ep_out_data)) { + return USBD_FAIL; + } + + uint32_t USB_Rx_Count = USBD_Last_Rx_Packet_size(pdev, epnum); + priv->rx_buffer_head = ring_wrap(priv->rx_buffer_length, priv->rx_buffer_head + USB_Rx_Count); + + // Serial port is definitely open + USBD_MCDC_Change_Open_State(pdev, priv, 1); + + USBD_MCDC_Start_Rx(pdev, priv); + + return USBD_OK; +} + +void USBD_MCDC_Schedule_Out(void *pdev, USBD_MCDC_Instance_Data* priv) +{ + if (!priv->rx_state) + USBD_MCDC_Start_Rx(pdev, priv); +} + +static void USBD_MCDC_Schedule_In(void *pdev, USBD_MCDC_Instance_Data* priv) +{ + uint32_t USB_Tx_length; + + if (priv->tx_state) { + if (priv->serial_open) { + priv->tx_failed_counter++; + if (priv->tx_failed_counter >= 1000) { + USBD_MCDC_Change_Open_State(pdev, priv, 0); + } + } + + if (!priv->serial_open) { + // Completely flush TX buffer + DCD_EP_Flush(pdev, priv->ep_in_data); + // Send ZLP + DCD_EP_Tx(pdev, priv->ep_in_data, NULL, 0); + priv->tx_buffer_head = 0; + priv->tx_buffer_tail = 0; + priv->tx_buffer_last = 0; + USB_Tx_length = 0; + + priv->tx_state = 0; + } + return; + } + + priv->tx_buffer_tail = ring_wrap(priv->tx_buffer_size, priv->tx_buffer_tail + priv->tx_buffer_last); + priv->tx_buffer_last = 0; + + USB_Tx_length = ring_data_contig(priv->tx_buffer_size, priv->tx_buffer_head, priv->tx_buffer_tail); + + if (!USB_Tx_length) + return; + + priv->tx_state = 1; + priv->tx_failed_counter = 0; + + USB_Tx_length = MIN(USB_Tx_length, CDC_DATA_IN_PACKET_SIZE); + priv->tx_buffer_last = USB_Tx_length; + + DCD_EP_Tx (pdev, + priv->ep_in_data, + (uint8_t*)&priv->tx_buffer[priv->tx_buffer_tail], + USB_Tx_length); +} + +static uint8_t USBD_MCDC_SOF(void* pdev/*, USBD_Composite_Class_Data* cls*/) +{ + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + + if (priv->configured) { + USBD_MCDC_Schedule_In(pdev, priv); + USBD_MCDC_Schedule_Out(pdev, priv); + } + + return USBD_OK; +} + +static uint8_t* USBD_MCDC_GetCfgDesc(uint8_t speed/*, USBD_Composite_Class_Data* cls, uint8_t* buf*/, uint16_t *length) +{ + *length = sizeof(USBD_MCDC_CfgDesc); + return (uint8_t*)USBD_MCDC_CfgDesc; +} + +static uint16_t USBD_MCDC_Request_Handler(void* pdev/*, USBD_Composite_Class_Data* cls*/, uint32_t cmd, uint8_t* buf, uint32_t len) { + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + switch (cmd) + { + case SEND_ENCAPSULATED_COMMAND: + /* Not needed for this driver */ + break; + + case GET_ENCAPSULATED_RESPONSE: + /* Not needed for this driver */ + break; + + case SET_COMM_FEATURE: + /* Not needed for this driver */ + break; + + case GET_COMM_FEATURE: + /* Not needed for this driver */ + break; + + case CLEAR_COMM_FEATURE: + /* Not needed for this driver */ + break; + + case SET_LINE_CODING: + priv->linecoding.bitrate = (uint32_t)(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); + priv->linecoding.format = buf[4]; + priv->linecoding.paritytype = buf[5]; + priv->linecoding.datatype = buf[6]; + // LOG_DEBUG(TRACE, "[%s] SET_LINE_CODING %d", priv->name, priv->linecoding.bitrate); + break; + + case GET_LINE_CODING: + buf[0] = (uint8_t)(priv->linecoding.bitrate); + buf[1] = (uint8_t)(priv->linecoding.bitrate >> 8); + buf[2] = (uint8_t)(priv->linecoding.bitrate >> 16); + buf[3] = (uint8_t)(priv->linecoding.bitrate >> 24); + buf[4] = priv->linecoding.format; + buf[5] = priv->linecoding.paritytype; + buf[6] = priv->linecoding.datatype; + break; + + case SET_CONTROL_LINE_STATE: + priv->ctrl_line = buf[0]; + if (priv->ctrl_line & CDC_DTR) { + USBD_MCDC_Change_Open_State(pdev, priv, 1); + } else if ((priv->ctrl_line & CDC_DTR) == 0x00) { + USBD_MCDC_Change_Open_State(pdev, priv, 0); + } + // LOG_DEBUG(TRACE, "[%s] SET_CONTROL_LINE_STATE DTR=%d RTS=%d", priv->name, priv->ctrl_line & CDC_DTR ? 1 : 0, priv->ctrl_line & CDC_RTS ? 1 : 0); + break; + + case SEND_BREAK: + /* Not needed for this driver */ + break; + + default: + break; + } + + if (priv->req_handler) { + priv->req_handler(/*cls, */cmd, buf, len); + } + + return USBD_OK; +} + +uint8_t* USBD_MCDC_GetUsrStrDescriptor(uint8_t speed/*, USBD_Composite_Class_Data* cls*/, uint8_t index, uint16_t* length) { + // USBD_MCDC_Instance_Data* priv = (USBD_MCDC_Instance_Data*)cls->priv; + USBD_MCDC_Instance_Data* priv = &USBD_MCDC; + + if (index == (USBD_MCDC_USRSTR_BASE/* + cls->firstInterface*/)) { + USBD_GetString((uint8_t*)priv->name, USBD_StrDesc, length); + return USBD_StrDesc; + } + + *length = 0; + return NULL; +} diff --git a/services/inc/ringbuf_helper.h b/services/inc/ringbuf_helper.h new file mode 100644 index 0000000000..f00a1525f5 --- /dev/null +++ b/services/inc/ringbuf_helper.h @@ -0,0 +1,60 @@ +#ifndef RINGBUF_HELPER_H_ +#define RINGBUF_HELPER_H_ + +#include + +/* 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) +{ + if (size == 0) + return 0; + 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 (size == 0) + return 0; + 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 (size == 0) + return 0; + if (head < tail || !tail) + return 0; + else + return tail - 1; +} + +#endif // RINGBUF_HELPER_H_ diff --git a/user/tests/wiring/usbserial/application.cpp b/user/tests/wiring/usbserial/application.cpp new file mode 100644 index 0000000000..f0199953d6 --- /dev/null +++ b/user/tests/wiring/usbserial/application.cpp @@ -0,0 +1,6 @@ +#include "application.h" +#include "unit-test/unit-test.h" + +UNIT_TEST_APP(); + +SYSTEM_MODE(SEMI_AUTOMATIC); diff --git a/user/tests/wiring/usbserial/usbserial.cpp b/user/tests/wiring/usbserial/usbserial.cpp new file mode 100644 index 0000000000..a3eff08d65 --- /dev/null +++ b/user/tests/wiring/usbserial/usbserial.cpp @@ -0,0 +1,231 @@ +#include "application.h" +#include "unit-test/unit-test.h" +#include "usb_settings.h" +#include "ringbuf_helper.h" + +int randomString(char *buf, int len) { + for (int i = 0; i < len; i++) { + uint8_t d = random(0, 15); + char c = d + 48; + if (57 < c) + c += 7; + buf[i] = c; + } + + return len; +} + +void consume(Stream& serial) +{ + while (serial.available() > 0) { + (void)serial.read(); + } +} + +test(0_USBSERIAL_RingBufferHelperIsSane) { + uint32_t size = 129; + uint32_t head = 0; + uint32_t tail = 0; + + head = 0; + tail = 0; + assertEqual(0, ring_data_avail(size, head, tail)); + assertEqual(128, ring_space_avail(size, head, tail)); + assertEqual(0, ring_data_contig(size, head, tail)); + assertEqual(128, ring_space_contig(size, head, tail)); + assertEqual(0, ring_space_wrapped(size, head, tail)); + + head = 63; + tail = 0; + assertEqual(63, ring_data_avail(size, head, tail)); + assertEqual(65, ring_space_avail(size, head, tail)); + assertEqual(63, ring_data_contig(size, head, tail)); + assertEqual(65, ring_space_contig(size, head, tail)); + assertEqual(0, ring_space_wrapped(size, head, tail)); + + head = 63; + tail = 32; + assertEqual(31, ring_data_avail(size, head, tail)); + assertEqual(97, ring_space_avail(size, head, tail)); + assertEqual(31, ring_data_contig(size, head, tail)); + assertEqual(66, ring_space_contig(size, head, tail)); + assertEqual(31, ring_space_wrapped(size, head, tail)); + + head = 63; + tail = 63; + assertEqual(0, ring_data_avail(size, head, tail)); + assertEqual(128, ring_space_avail(size, head, tail)); + assertEqual(0, ring_data_contig(size, head, tail)); + assertEqual(66, ring_space_contig(size, head, tail)); + assertEqual(62, ring_space_wrapped(size, head, tail)); + + head = 128; + tail = 63; + assertEqual(65, ring_data_avail(size, head, tail)); + assertEqual(63, ring_space_avail(size, head, tail)); + assertEqual(65, ring_data_contig(size, head, tail)); + assertEqual(1, ring_space_contig(size, head, tail)); + assertEqual(62, ring_space_wrapped(size, head, tail)); + + head = 128; + tail = 128; + assertEqual(0, ring_data_avail(size, head, tail)); + assertEqual(128, ring_space_avail(size, head, tail)); + assertEqual(0, ring_data_contig(size, head, tail)); + assertEqual(1, ring_space_contig(size, head, tail)); + assertEqual(127, ring_space_wrapped(size, head, tail)); + + head = 0; + tail = 63; + assertEqual(66, ring_data_avail(size, head, tail)); + assertEqual(62, ring_space_avail(size, head, tail)); + assertEqual(66, ring_data_contig(size, head, tail)); + assertEqual(62, ring_space_contig(size, head, tail)); + assertEqual(0, ring_space_wrapped(size, head, tail)); + + head = 32; + tail = 63; + assertEqual(98, ring_data_avail(size, head, tail)); + assertEqual(30, ring_space_avail(size, head, tail)); + assertEqual(66, ring_data_contig(size, head, tail)); + assertEqual(30, ring_space_contig(size, head, tail)); + assertEqual(0, ring_space_wrapped(size, head, tail)); + + head = 0; + tail = 128; + assertEqual(1, ring_data_avail(size, head, tail)); + assertEqual(127, ring_space_avail(size, head, tail)); + assertEqual(1, ring_data_contig(size, head, tail)); + assertEqual(127, ring_space_contig(size, head, tail)); + assertEqual(0, ring_space_wrapped(size, head, tail)); + + head = 128; + tail = 0; + assertEqual(128, ring_data_avail(size, head, tail)); + assertEqual(0, ring_space_avail(size, head, tail)); + assertEqual(128, ring_data_contig(size, head, tail)); + assertEqual(0, ring_space_contig(size, head, tail)); + assertEqual(0, ring_space_wrapped(size, head, tail)); +} + +test(1_USBSERIAL_ReadWrite) { + //The following code will test all the important USB Serial routines + char test[] = "hello"; + char message[10]; + // when + consume(Serial); + Serial.print("Type the following message and press Enter: "); + Serial.println(test); + serialReadLine(&Serial, message, 9, 10000);//10 sec timeout + Serial.println(""); + // then + assertTrue(strncmp(test, message, 5)==0); +} + +test(2_USBSERIAL_isConnectedWorksCorrectly) { + Serial.println("Please close the USB Serial port and open it again within 60 seconds"); + + uint32_t mil = millis(); + // Wait for 60 seconds maximum for the host to close the USB Serial port + while(Serial.isConnected()) { + assertTrue((millis() - mil) < 60000); + } + // Wait for 60 seconds maximum for the host to open the USB Serial port again + while(!Serial.isConnected()) { + assertTrue((millis() - mil) < 60000); + } + + delay(10); + Serial.println("Glad too see you back!"); + + char test[] = "hello"; + char message[10]; + // when + consume(Serial); + Serial.print("Type the following message and press Enter: "); + Serial.println(test); + serialReadLine(&Serial, message, 9, 10000);//10 sec timeout + Serial.println(""); + // then + assertTrue(strncmp(test, message, 5)==0); +} + +test(3_USBSERIAL_RxBufferFillsCompletely) { + Serial.println("We will now test USB Serial RX buffer"); + Serial.println("Please close the USB Serial port and open it again once the device reattaches"); + + uint32_t mil = millis(); + // Wait for 60 seconds maximum for the host to close the USB Serial port + while(Serial.isConnected()) { + assertTrue((millis() - mil) < 60000); + } + + Serial.end(); + assertEqual(Serial.isEnabled(), false); + Serial.begin(); + assertEqual(Serial.isEnabled(), true); + + // Wait for 60 seconds maximum for the host to open the USB Serial port again + while(!Serial.isConnected()) { + assertTrue((millis() - mil) < 60000); + } + + assertEqual(Serial.available(), 0); + + delay(10); + Serial.println("Glad too see you back!"); + + for (int attmpt = 0; attmpt < 3; attmpt++) { + char randStr[USB_RX_BUFFER_SIZE + 2]; + char rStr[USB_RX_BUFFER_SIZE + 2]; + memset(randStr, 0, USB_RX_BUFFER_SIZE + 2); + memset(rStr, 0, USB_RX_BUFFER_SIZE + 2); + srand(millis()); + randomString(randStr, USB_RX_BUFFER_SIZE - 1); + + Serial.println("Please copy and paste the following string:"); + Serial.println(randStr); + + int32_t avail = 0; + mil = millis(); + uint32_t milsame = 0; + while(Serial.available() != (USB_RX_BUFFER_SIZE - 1)) { + if (avail == Serial.available() && avail != 0) { + if (milsame == 0) { + milsame = millis(); + } else { + if ((millis() - milsame) >= 5000) { + // Depending on the host driver, we might have received (USB_RX_BUFFER_SIZE - 64) + break; + } + } + } else { + avail = Serial.available(); + milsame = 0; + } + assertTrue((millis() - mil) < 120000); + } + avail = Serial.available(); + Serial.printf("OK. Read back %d bytes\r\n", avail); + + assertTrue((avail == (USB_RX_BUFFER_SIZE - 1)) || + (avail >= ((USB_RX_BUFFER_SIZE - 1) / 64 * 64))); + for (int i = 0; i < avail; i++) { + rStr[i] = Serial.read(); + } + + Serial.printf("Data: %s\r\n\r\n", rStr); + + assertTrue(!strncmp(randStr, rStr, avail)); + + delay(500); + while(Serial.available()) { + (void)Serial.read(); + } + + if (!((avail == (USB_RX_BUFFER_SIZE - 1)) || (avail == ((USB_RX_BUFFER_SIZE - 1) / 64 * 64)))) { + // Continue only if we received data in 64-byte blocks + break; + } + } +} \ No newline at end of file diff --git a/wiring/inc/spark_wiring_usbserial.h b/wiring/inc/spark_wiring_usbserial.h index 0719b7f1ce..4b5d7cb5c9 100644 --- a/wiring/inc/spark_wiring_usbserial.h +++ b/wiring/inc/spark_wiring_usbserial.h @@ -31,17 +31,19 @@ #include "usb_hal.h" #include "system_task.h" + class USBSerial : public Stream { public: // public methods USBSerial(); - unsigned int baud() { return USB_USART_Baud_Rate(); } - - operator bool() { return baud()!=0; } + unsigned int baud() { return USB_USART_Baud_Rate(); } + operator bool() { return isEnabled(); } + bool isConnected(); + bool isEnabled() { return baud()!=0; } - void begin(long speed); + void begin(long speed=9600); void end(); int peek(); diff --git a/wiring/src/spark_wiring_usbserial.cpp b/wiring/src/spark_wiring_usbserial.cpp index 3ea7469d33..c7826f217a 100644 --- a/wiring/src/spark_wiring_usbserial.cpp +++ b/wiring/src/spark_wiring_usbserial.cpp @@ -31,7 +31,7 @@ // USBSerial::USBSerial() { - _blocking = true; + _blocking = true; } // @@ -48,6 +48,11 @@ void USBSerial::end() USB_USART_Init(0); } +bool USBSerial::isConnected() +{ + return USB_USART_Available_Data_For_Write() >= 0; +} + // Read data from buffer int USBSerial::read()