diff --git a/bricks/_common/arm_none_eabi.mk b/bricks/_common/arm_none_eabi.mk index eab3a9e4f..eeb97eb54 100644 --- a/bricks/_common/arm_none_eabi.mk +++ b/bricks/_common/arm_none_eabi.mk @@ -373,6 +373,7 @@ endif SRC_STM32_USB_DEV += $(addprefix lib/pbio/drv/usb/stm32_usbd/,\ usbd_conf.c \ usbd_desc.c \ + usbd_pybricks.c \ ) NXOS_SRC_C = $(addprefix lib/pbio/platform/nxt/nxos/,\ diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_conf.h b/lib/pbio/drv/usb/stm32_usbd/usbd_conf.h index d74e585bd..bae1dd807 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_conf.h +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_conf.h @@ -10,5 +10,6 @@ #define USBD_MAX_NUM_CONFIGURATION 1 #define USBD_MAX_STR_DESC_SIZ 0x100 #define USBD_SELF_POWERED 1 +#define USBD_CLASS_BOS_ENABLED 1 #endif /* _USBD_CONF_H_ */ diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c index 06ebed708..86532dd00 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c @@ -45,6 +45,7 @@ /* Includes ------------------------------------------------------------------*/ #include "usbd_core.h" #include "usbd_conf.h" +#include "usbd_pybricks.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -61,12 +62,18 @@ #define DEVICE_ID3 (0x1FFF7A18) #define USB_SIZ_STRING_SERIAL 0x1A +#define USB_SIZ_BOS_DESC 33 /* USB Standard Device Descriptor */ __ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { 0x12, /* bLength */ USB_DESC_TYPE_DEVICE, /* bDescriptorType */ +#if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) + 0x01, /* bcdUSB */ /* changed to USB version 2.01 + in order to support BOS Desc */ +#else 0x00, /* bcdUSB */ +#endif /* (USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1) */ 0x02, 0x02, /* bDeviceClass */ 0x02, /* bDeviceSubClass */ @@ -84,6 +91,135 @@ __ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ }; /* USB_DeviceDescriptor */ +/** BOS descriptor. */ +#if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) +#if defined ( __ICCARM__ ) /* IAR Compiler */ + #pragma data_alignment=4 +#endif /* defined ( __ICCARM__ ) */ +__ALIGN_BEGIN uint8_t USBD_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END = +{ + 5, /* bLength */ + USB_DESC_TYPE_BOS, /* bDescriptorType = BOS */ + LOBYTE(USB_SIZ_BOS_DESC), /* wTotalLength */ + HIBYTE(USB_SIZ_BOS_DESC), /* wTotalLength */ + 0x01, /* bNumDeviceCaps = 1 */ + + 28, /* bLength */ + USB_DEVICE_CAPABITY_TYPE, /* bDescriptorType = Device Capability */ + 0x05, /* bDevCapabilityType = Platform */ + 0x00, /* bReserved */ + + /* + * PlatformCapabilityUUID + * Microsoft OS 2.0 descriptor platform capability ID + * D8DD60DF-4589-4CC7-9CD2-659D9E648A9F + */ + 0xDF, 0x60, 0xDD, 0xD8, + 0x89, 0x45, + 0xC7, 0x4C, + 0x9C, 0xD2, + 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion = 0x06030000 for Windows 8.1 Build */ + LOBYTE(USB_SIZ_MS_OS_DSCRPTR_SET), /* wMSOSDescriptorSetTotalLength */ + HIBYTE(USB_SIZ_MS_OS_DSCRPTR_SET), /* wMSOSDescriptorSetTotalLength */ + USB_MS_VENDOR_CODE, /* bMS_VendorCode */ + 0x00 /* bAltEnumCode = Does not support alternate enumeration */ +}; +#endif /* (USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1) */ + +#if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) +#if defined ( __ICCARM__ ) /* IAR Compiler */ + #pragma data_alignment=4 +#endif /* defined ( __ICCARM__ ) */ +__ALIGN_BEGIN uint8_t USBD_OSDescSet[USB_SIZ_MS_OS_DSCRPTR_SET] __ALIGN_END = +{ + 0x0A, 0x00, /* wLength = 10 */ + 0x00, 0x00, /* wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR */ + 0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion = 0x06030000 for Windows 8.1 Build */ + LOBYTE(USB_SIZ_MS_OS_DSCRPTR_SET), /* wTotalLength */ + HIBYTE(USB_SIZ_MS_OS_DSCRPTR_SET), /* wTotalLength (cont.) */ + + 0x14, 0x00, /* wLength = 20 */ + 0x03, 0x00, /* wDescriptorType = MS_OS_20_FEATURE_COMPATBLE_ID */ + 'W', 'I', 'N', 'U', 'S', 'B', /* CompatibleID */ + 0x00, 0x00, /* CompatibleID (cont.) */ + 0x00, 0x00, 0x00, 0x00, /* SubCompatibleID */ + 0x00, 0x00, 0x00, 0x00, /* SubCompatibleID (cont.) */ + + 0x82, 0x00, /* wLength = 130 */ + 0x04, 0x00, /* wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY */ + 0x07, 0x00, /* wStringType = REG_MULTI_SZ */ + /* wPropertyNameLength = 42 */ + 0x2A, 0x00, + /* PropertyName = DeviceInterfaceGUIDs */ + 'D', '\0', + 'e', '\0', + 'v', '\0', + 'i', '\0', + 'c', '\0', + 'e', '\0', + 'I', '\0', + 'n', '\0', + 't', '\0', + 'e', '\0', + 'r', '\0', + 'f', '\0', + 'a', '\0', + 'c', '\0', + 'e', '\0', + 'G', '\0', + 'U', '\0', + 'I', '\0', + 'D', '\0', + 's', '\0', + '\0', '\0', + + /* wPropertyDataLength = 78 */ + 0x4E, 0x00, + /* PropertyData = {A5C44A4C-53D4-4389-9821-AE95051908A1} */ + '{', '\0', + 'A', '\0', + '5', '\0', + 'C', '\0', + '4', '\0', + '4', '\0', + 'A', '\0', + '4', '\0', + 'C', '\0', + '-', '\0', + '5', '\0', + '3', '\0', + 'D', '\0', + '4', '\0', + '-', '\0', + '4', '\0', + '3', '\0', + '8', '\0', + '9', '\0', + '-', '\0', + '9', '\0', + '8', '\0', + '2', '\0', + '1', '\0', + '-', '\0', + 'A', '\0', + 'E', '\0', + '9', '\0', + '5', '\0', + '0', '\0', + '5', '\0', + '1', '\0', + '9', '\0', + '0', '\0', + '8', '\0', + 'A', '\0', + '1', '\0', + '}', '\0', + '\0', '\0' +}; +#endif /* (USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1) */ + /* USB Standard Device Descriptor */ __ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { USB_LEN_LANGID_STR_DESC, @@ -236,6 +372,16 @@ static uint8_t *USBD_Pybricks_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, ui return USBD_StrDesc; } +#if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) +static uint8_t *USBD_Pybricks_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) { + /* Prevent unused argument(s) compilation warning */ + UNUSED(speed); + + *length = sizeof(USBD_BOSDesc); + return (uint8_t *)USBD_BOSDesc; +} +#endif /* (USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1) */ + USBD_DescriptorsTypeDef Pybricks_Desc = { .GetDeviceDescriptor = USBD_Pybricks_DeviceDescriptor, .GetLangIDStrDescriptor = USBD_Pybricks_LangIDStrDescriptor, @@ -244,4 +390,7 @@ USBD_DescriptorsTypeDef Pybricks_Desc = { .GetSerialStrDescriptor = USBD_Pybricks_SerialStrDescriptor, .GetConfigurationStrDescriptor = USBD_Pybricks_ConfigStrDescriptor, .GetInterfaceStrDescriptor = USBD_Pybricks_InterfaceStrDescriptor, +#if ((USBD_LPM_ENABLED == 1) || (USBD_CLASS_BOS_ENABLED == 1)) + .GetBOSDescriptor = USBD_Pybricks_BOSDescriptor, +#endif }; diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c new file mode 100644 index 000000000..677357d9d --- /dev/null +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c @@ -0,0 +1,546 @@ +/** + ****************************************************************************** + * @file usbd_pybricks.c + * @author MCD Application Team + * @brief This file provides the HID core functions. + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2015 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + * @verbatim + * + * =================================================================== + * Pybricks Class Description + * =================================================================== + * + * + * + * + * + * + * @note In HS mode and when the DMA is used, all variables and data structures + * dealing with the DMA during the transaction process should be 32-bit aligned. + * + * + * @endverbatim + * + ****************************************************************************** + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ctlreq.h" +#include "usbd_pybricks.h" + + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + + +/** @defgroup USBD_Pybricks + * @brief usbd core module + * @{ + */ + +/** @defgroup USBD_Pybricks_Private_TypesDefinitions + * @{ + */ +/** + * @} + */ + + +/** @defgroup USBD_Pybricks_Private_Defines + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_Pybricks_Private_Macros + * @{ + */ + +/** + * @} + */ + + +/** @defgroup USBD_Pybricks_Private_FunctionPrototypes + * @{ + */ + +static uint8_t USBD_Pybricks_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_Pybricks_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static uint8_t USBD_Pybricks_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_Pybricks_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_Pybricks_EP0_RxReady(USBD_HandleTypeDef *pdev); +static uint8_t *USBD_Pybricks_GetCfgDesc(uint16_t *length); + +/** + * @} + */ + +/** @defgroup USBD_Pybricks_Private_Variables + * @{ + */ + +USBD_ClassTypeDef USBD_Pybricks_ClassDriver = +{ + .Init = USBD_Pybricks_Init, + .DeInit = USBD_Pybricks_DeInit, + .Setup = USBD_Pybricks_Setup, + .EP0_RxReady = USBD_Pybricks_EP0_RxReady, + .DataIn = USBD_Pybricks_DataIn, + .DataOut = USBD_Pybricks_DataOut, + .GetFSConfigDescriptor = USBD_Pybricks_GetCfgDesc, +}; + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ +#pragma data_alignment=4 +#endif /* __ICCARM__ */ +/* USB Pybricks device Configuration Descriptor */ +__ALIGN_BEGIN static uint8_t USBD_Pybricks_CfgDesc[USB_PYBRICKS_CONFIG_DESC_SIZ] __ALIGN_END = +{ + /* Configuration Descriptor */ + 0x09, /* bLength: Configuration Descriptor size */ + USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ + LOBYTE(USB_PYBRICKS_CONFIG_DESC_SIZ), /* wTotalLength:no of returned bytes */ + HIBYTE(USB_PYBRICKS_CONFIG_DESC_SIZ), + 0x01, /* bNumInterfaces: 1 interface */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ + 0xC0, /* bmAttributes: self powered */ + 0x32, /* MaxPower 0 mA */ + + /*---------------------------------------------------------------------------*/ + + /* Data class interface descriptor */ + 0x09, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ + 0x00, /* bInterfaceNumber: Number of Interface */ + 0x00, /* bAlternateSetting: Alternate setting */ + 0x02, /* bNumEndpoints: Two endpoints used */ + 0xFF, /* bInterfaceClass: vendor */ + 0x00, /* bInterfaceSubClass: */ + 0x00, /* bInterfaceProtocol: */ + 0x00, /* iInterface: */ + + /* Endpoint OUT Descriptor */ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ + PYBRICKS_OUT_EP, /* bEndpointAddress */ + 0x02, /* bmAttributes: Bulk */ + LOBYTE(PYBRICKS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ + HIBYTE(PYBRICKS_MAX_PACKET_SIZE), + 0x00, /* bInterval: ignore for Bulk transfer */ + + /* Endpoint IN Descriptor */ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ + PYBRICKS_IN_EP, /* bEndpointAddress */ + 0x02, /* bmAttributes: Bulk */ + LOBYTE(PYBRICKS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ + HIBYTE(PYBRICKS_MAX_PACKET_SIZE), + 0x00 /* bInterval */ +}; + +/** + * @} + */ + +/** @defgroup USBD_Pybricks_Private_Functions + * @{ + */ + +/** + * @brief USBD_Pybricks_Init + * Initialize the Pybricks interface + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_Pybricks_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + UNUSED(cfgidx); + static USBD_Pybricks_HandleTypeDef hPybricks; + + pdev->pClassData = &hPybricks; + + (void)USBD_LL_OpenEP(pdev, PYBRICKS_IN_EP, USBD_EP_TYPE_INTR, PYBRICKS_MAX_PACKET_SIZE); + pdev->ep_in[PYBRICKS_IN_EP & 0xFU].is_used = 1U; + + (void)USBD_LL_OpenEP(pdev, PYBRICKS_OUT_EP, USBD_EP_TYPE_INTR, PYBRICKS_MAX_PACKET_SIZE); + pdev->ep_out[PYBRICKS_OUT_EP & 0xFU].is_used = 1U; + + /* Init physical Interface components */ + ((USBD_Pybricks_ItfTypeDef *)pdev->pUserData[pdev->classId])->Init(); + + /* Init Xfer states */ + hPybricks.TxState = 0U; + hPybricks.RxState = 0U; + + (void)USBD_LL_PrepareReceive(pdev, PYBRICKS_OUT_EP, hPybricks.RxBuffer, PYBRICKS_MAX_PACKET_SIZE); + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_Pybricks_DeInit + * DeInitialize the Pybricks layer + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_Pybricks_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + UNUSED(cfgidx); + uint8_t ret = 0U; + + /* Close EP IN */ + (void)USBD_LL_CloseEP(pdev, PYBRICKS_IN_EP); + pdev->ep_in[PYBRICKS_IN_EP & 0xFU].is_used = 0U; + + /* Close EP OUT */ + (void)USBD_LL_CloseEP(pdev, PYBRICKS_OUT_EP); + pdev->ep_out[PYBRICKS_OUT_EP & 0xFU].is_used = 0U; + + /* DeInit physical Interface components */ + if (pdev->pClassData != NULL) + { + ((USBD_Pybricks_ItfTypeDef *)pdev->pUserData[pdev->classId])->DeInit(); + pdev->pClassData = NULL; + } + + return ret; +} + +/** + * @brief USBD_Pybricks_Setup + * Handle the Pybricks specific requests + * @param pdev: instance + * @param req: usb requests + * @retval status + */ +static uint8_t USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + uint8_t ifalt = 0U; + uint16_t status_info = 0U; + USBD_StatusTypeDef ret = USBD_OK; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + case USB_REQ_TYPE_CLASS: + if (req->wLength != 0U) + { + if ((req->bmRequest & 0x80U) != 0U) + { + (void)USBD_CtlSendData(pdev, (uint8_t *)hPybricks->data, req->wLength); + } + else + { + hPybricks->CmdOpCode = req->bRequest; + hPybricks->CmdLength = (uint8_t)req->wLength; + + (void)USBD_CtlPrepareRx(pdev, (uint8_t *)hPybricks->data, req->wLength); + } + } + break; + + case USB_REQ_TYPE_VENDOR: + switch (req->bRequest) + { + case USB_MS_VENDOR_CODE: + (void)USBD_CtlSendData(pdev, + (uint8_t *)USBD_OSDescSet, + MIN(sizeof(USBD_OSDescSet), req->wLength)); + break; + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + break; + + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest) + { + case USB_REQ_GET_STATUS: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U); + } + else + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_GET_INTERFACE: + if (pdev->dev_state == USBD_STATE_CONFIGURED) + { + (void)USBD_CtlSendData(pdev, &ifalt, 1U); + } + else + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_SET_INTERFACE: + if (pdev->dev_state != USBD_STATE_CONFIGURED) + { + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + + case USB_REQ_CLEAR_FEATURE: + break; + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + break; + + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + + return (uint8_t)ret; +} + +/** + * @brief USBD_Pybricks_GetCfgDesc + * return configuration descriptor + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +static uint8_t *USBD_Pybricks_GetCfgDesc(uint16_t *length) +{ + *length = (uint16_t)sizeof(USBD_Pybricks_CfgDesc); + return USBD_Pybricks_CfgDesc; +} + +/** + * @brief USBD_Pybricks_DataIn + * Data sent on non-control IN endpoint + * @param pdev: device instance + * @param epnum: endpoint number + * @retval status + */ +static uint8_t USBD_Pybricks_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ + USBD_Pybricks_HandleTypeDef *hPybricks; + PCD_HandleTypeDef *hpcd = pdev->pData; + + if (pdev->pClassData == NULL) + { + return (uint8_t)USBD_FAIL; + } + + hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + + if ((pdev->ep_in[epnum].total_length > 0U) && + ((pdev->ep_in[epnum].total_length % hpcd->IN_ep[epnum].maxpacket) == 0U)) + { + /* Update the packet total length */ + pdev->ep_in[epnum].total_length = 0U; + + /* Send ZLP */ + (void)USBD_LL_Transmit(pdev, epnum, NULL, 0U); + } + else + { + hPybricks->TxState = 0U; + ((USBD_Pybricks_ItfTypeDef *)pdev->pUserData[pdev->classId])->TransmitCplt(hPybricks->TxBuffer, &hPybricks->TxLength, epnum); + } + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_Pybricks_DataOut + * Data received on non-control Out endpoint + * @param pdev: device instance + * @param epnum: endpoint number + * @retval status + */ +static uint8_t USBD_Pybricks_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + + if (pdev->pClassData == NULL) + { + return (uint8_t)USBD_FAIL; + } + + /* Get the received data length */ + hPybricks->RxLength = USBD_LL_GetRxDataSize(pdev, epnum); + + /* USB data will be immediately processed, this allow next USB traffic being + NAKed till the end of the application Xfer */ + + ((USBD_Pybricks_ItfTypeDef *)pdev->pUserData[pdev->classId])->Receive(hPybricks->RxBuffer, &hPybricks->RxLength); + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_Pybricks_EP0_RxReady + * Handle EP0 Rx Ready event + * @param pdev: device instance + * @retval status + */ +static uint8_t USBD_Pybricks_EP0_RxReady(USBD_HandleTypeDef *pdev) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + + if ((pdev->pUserData != NULL) && (pdev->pUserData[pdev->classId] != NULL) && (hPybricks->CmdOpCode != 0xFFU)) + { + hPybricks->CmdOpCode = 0xFFU; + } + + return (uint8_t)USBD_OK; +} + +/** +* @brief USBD_Pybricks_RegisterInterface + * @param pdev: device instance + * @param fops: CD Interface callback + * @retval status + */ +uint8_t USBD_Pybricks_RegisterInterface(USBD_HandleTypeDef *pdev, + USBD_Pybricks_ItfTypeDef *fops) +{ + if (fops == NULL) + { + return (uint8_t)USBD_FAIL; + } + + pdev->pUserData[pdev->classId] = fops; + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_Pybricks_SetTxBuffer + * @param pdev: device instance + * @param pbuff: Tx Buffer + * @retval status + */ +uint8_t USBD_Pybricks_SetTxBuffer(USBD_HandleTypeDef *pdev, + uint8_t *pbuff, uint32_t length) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + + hPybricks->TxBuffer = pbuff; + hPybricks->TxLength = length; + + return (uint8_t)USBD_OK; +} + + +/** + * @brief USBD_Pybricks_SetRxBuffer + * @param pdev: device instance + * @param pbuff: Rx Buffer + * @retval status + */ +uint8_t USBD_Pybricks_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + + hPybricks->RxBuffer = pbuff; + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_Pybricks_TransmitPacket + * Transmit packet on IN endpoint + * @param pdev: device instance + * @retval status + */ +uint8_t USBD_Pybricks_TransmitPacket(USBD_HandleTypeDef *pdev) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + USBD_StatusTypeDef ret = USBD_BUSY; + + if (pdev->pClassData == NULL) + { + return (uint8_t)USBD_FAIL; + } + + if (hPybricks->TxState == 0U) + { + /* Tx Transfer in progress */ + hPybricks->TxState = 1U; + + /* Update the packet total length */ + pdev->ep_in[PYBRICKS_IN_EP & 0xFU].total_length = hPybricks->TxLength; + + /* Transmit next packet */ + (void)USBD_LL_Transmit(pdev, PYBRICKS_IN_EP, hPybricks->TxBuffer, hPybricks->TxLength); + + ret = USBD_OK; + } + + return (uint8_t)ret; +} + + +/** + * @brief USBD_Pybricks_ReceivePacket + * prepare OUT Endpoint for reception + * @param pdev: device instance + * @retval status + */ +uint8_t USBD_Pybricks_ReceivePacket(USBD_HandleTypeDef *pdev) +{ + USBD_Pybricks_HandleTypeDef *hPybricks = (USBD_Pybricks_HandleTypeDef *)pdev->pClassData; + + if (pdev->pClassData == NULL) + { + return (uint8_t)USBD_FAIL; + } + + /* Prepare Out endpoint to receive next packet */ + (void)USBD_LL_PrepareReceive(pdev, PYBRICKS_OUT_EP, hPybricks->RxBuffer, PYBRICKS_MAX_PACKET_SIZE); + + return (uint8_t)USBD_OK; +} + +/** + * @} + */ + + +/** + * @} + */ + + +/** + * @} + */ diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h new file mode 100644 index 000000000..d0594b0d3 --- /dev/null +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h @@ -0,0 +1,133 @@ +/** + ****************************************************************************** + * @file usbd_pybricks.h + * @author MCD Application Team + * @brief Header file for the usbd_pybricks.c file. + ****************************************************************************** + * @attention + * + * Copyright (c) 2015 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USB_PYBRICKS_H +#define __USB_PYBRICKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ioreq.h" + +/** @addtogroup STM32_USB_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_Pybricks + * @brief This file is the header file for usbd_pybricks.c + * @{ + */ + + +/** @defgroup USBD_Pybricks_Exported_Defines + * @{ + */ +#define USB_MS_VENDOR_CODE 0xCC +#define USB_SIZ_MS_OS_DSCRPTR_SET (10 + 20 + 130) + +#define USB_PYBRICKS_CONFIG_DESC_SIZ (9 + 9 + 7 + 7) + +#define PYBRICKS_IN_EP 0x81U /* EP1 for data IN */ +#define PYBRICKS_OUT_EP 0x01U /* EP1 for data OUT */ + +#define PYBRICKS_MAX_PACKET_SIZE 64U + +/** + * @} + */ + + +/** @defgroup USBD_Pybricks_Exported_TypesDefinitions + * @{ + */ + +/** + * @} + */ +typedef struct +{ + int8_t (* Init)(void); + int8_t (* DeInit)(void); + int8_t (* Receive)(uint8_t *Buf, uint32_t *Len); + int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum); +} USBD_Pybricks_ItfTypeDef; + + +typedef struct +{ + uint32_t data[PYBRICKS_MAX_PACKET_SIZE / 4U]; /* Force 32bits alignment */ + uint8_t CmdOpCode; + uint8_t CmdLength; + uint8_t *RxBuffer; + uint8_t *TxBuffer; + uint32_t RxLength; + uint32_t TxLength; + + __IO uint32_t TxState; + __IO uint32_t RxState; +} USBD_Pybricks_HandleTypeDef; + + + +/** @defgroup USBD_Pybricks_Exported_Macros + * @{ + */ + +/** + * @} + */ + +/** @defgroup USBD_Pybricks_Exported_Variables + * @{ + */ + +extern USBD_ClassTypeDef USBD_Pybricks_ClassDriver; + +extern USBD_DescriptorsTypeDef Pybricks_Desc; +extern uint8_t USBD_OSDescSet[USB_SIZ_MS_OS_DSCRPTR_SET]; +/** + * @} + */ + +/** @defgroup USB_Pybricks_Exported_Functions + * @{ + */ +uint8_t USBD_Pybricks_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_Pybricks_ItfTypeDef *fops); +uint8_t USBD_Pybricks_SetTxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint32_t length); +uint8_t USBD_Pybricks_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuf); +uint8_t USBD_Pybricks_ReceivePacket(USBD_HandleTypeDef *pdev); +uint8_t USBD_Pybricks_TransmitPacket(USBD_HandleTypeDef *pdev); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_PYBRICKS_H */ +/** + * @} + */ + +/** + * @} + */ diff --git a/lib/pbio/drv/usb/usb_stm32.c b/lib/pbio/drv/usb/usb_stm32.c index e7ab1fd5b..095209dbb 100644 --- a/lib/pbio/drv/usb/usb_stm32.c +++ b/lib/pbio/drv/usb/usb_stm32.c @@ -13,7 +13,9 @@ #include #include #include +#include +#include #include #include @@ -22,8 +24,22 @@ PROCESS(pbdrv_usb_process, "USB"); +static uint8_t usb_in_buf[PYBRICKS_MAX_PACKET_SIZE]; +static uint8_t usb_out_buf[PYBRICKS_MAX_PACKET_SIZE - 1]; +static volatile bool usb_in_busy; +static volatile bool usb_out_busy; + +static volatile bool usb_connected; + +// size must be power of 2 for ringbuf! also can't be > 255! +static lwrb_t stdout_buf; +static lwrb_t stdin_buf; +static uint8_t stdout_data[128]; +static uint8_t stdin_data[128]; + static USBD_HandleTypeDef husbd; static PCD_HandleTypeDef hpcd; + static volatile bool vbus_active; static pbdrv_usb_bcd_t pbdrv_usb_bcd; @@ -121,6 +137,75 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) { process_poll(&pbdrv_usb_process); } +/** + * @brief Pybricks_Itf_Init + * Initializes the Pybricks media low layer + * @param None + * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL + */ +static int8_t Pybricks_Itf_Init(void) { + lwrb_init(&stdin_buf, stdin_data, (uint8_t)PBIO_ARRAY_SIZE(stdin_data)); + lwrb_init(&stdout_buf, stdout_data, (uint8_t)PBIO_ARRAY_SIZE(stdout_data)); + USBD_Pybricks_SetTxBuffer(&husbd, usb_out_buf, 0); + USBD_Pybricks_SetRxBuffer(&husbd, usb_in_buf); + usb_in_busy = false; + usb_out_busy = false; + usb_connected = false; + + return USBD_OK; +} + +/** + * @brief Pybricks_Itf_DeInit + * DeInitializes the Pybricks media low layer + * @param None + * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL + */ +static int8_t Pybricks_Itf_DeInit(void) { + usb_connected = false; + return USBD_OK; +} + +/** + * @brief Pybricks_Itf_DataRx + * Data received over USB OUT endpoint are sent over CDC interface + * through this function. + * @param Buf: Buffer of data to be transmitted + * @param Len: Number of data received (in bytes) + * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL + */ +static int8_t Pybricks_Itf_Receive(uint8_t *Buf, uint32_t *Len) { + lwrb_write(&stdin_buf, Buf, *Len); + usb_in_busy = false; + // process_poll(&pbdrv_usb_process); + return USBD_OK; +} + +/** + * @brief Pybricks_Itf_TransmitCplt + * Data transmitted callback + * + * @note + * This function is IN transfer complete callback used to inform user that + * the submitted Data is successfully sent over USB. + * + * @param Buf: Buffer of data to be received + * @param Len: Number of data received (in bytes) + * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL + */ +static int8_t Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len, uint8_t epnum) { + usb_out_busy = false; + // process_poll(&pbdrv_usb_process); + return USBD_OK; +} + +static USBD_Pybricks_ItfTypeDef USBD_Pybricks_fops = { + .Init = Pybricks_Itf_Init, + .DeInit = Pybricks_Itf_DeInit, + .Receive = Pybricks_Itf_Receive, + .TransmitCplt = Pybricks_Itf_TransmitCplt, +}; + // Common USB driver implementation. void pbdrv_usb_init(void) { @@ -128,7 +213,11 @@ void pbdrv_usb_init(void) { husbd.pData = &hpcd; hpcd.pData = &husbd; - USBD_Init(&husbd, NULL, 0); + USBD_Init(&husbd, &Pybricks_Desc, 0); + USBD_RegisterClass(&husbd, &USBD_Pybricks_ClassDriver); + USBD_Pybricks_RegisterInterface(&husbd, &USBD_Pybricks_fops); + USBD_Start(&husbd); + process_start(&pbdrv_usb_process); // VBUS may already be active @@ -139,6 +228,65 @@ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void) { return pbdrv_usb_bcd; } +static void pbdrv_usb_serial_transmit(void) { + static uint32_t tx_size = 0; + + if (usb_out_busy) { + return; + } + + // If tx_size > 0 it means we have a pending retry, otherwise we get as + // much as we can from the stdout buffer. + if (tx_size == 0) { + tx_size = lwrb_get_full(&stdout_buf); + if (tx_size > PBIO_ARRAY_SIZE(usb_out_buf)) { + tx_size = PBIO_ARRAY_SIZE(usb_out_buf); + } + if (tx_size > 0) { + lwrb_read(&stdout_buf, usb_out_buf, tx_size); + } + } + + if (tx_size > 0) { + USBD_Pybricks_SetTxBuffer(&husbd, usb_out_buf, tx_size); + if (USBD_Pybricks_TransmitPacket(&husbd) == USBD_OK) { + usb_out_busy = true; + tx_size = 0; + } + } +} + +static void pbdrv_usb_serial_receive(void) { + if (usb_in_busy) { + return; + } + + if (USBD_Pybricks_ReceivePacket(&husbd) == USBD_OK) { + usb_in_busy = true; + } +} + +// TODO: need to multiplex stdin/stdout with Bluetooth or remove USB serial + +pbio_error_t pbsys_usb_put_char(uint8_t c) { + if (!usb_connected) { + // don't lock up print() when USB not connected - data is discarded + return PBIO_SUCCESS; + } + if (lwrb_write(&stdout_buf, &c, 1) == 0) { + return PBIO_SUCCESS; + } + return PBIO_SUCCESS; +} + +pbio_error_t pbsys_usb_get_char(uint8_t *c) { + if (lwrb_get_full(&stdin_buf) == 0) { + return PBIO_ERROR_AGAIN; + } + lwrb_read(&stdin_buf, c, 1); + return PBIO_SUCCESS; +} + // Event loop PROCESS_THREAD(pbdrv_usb_process, ev, data) { @@ -146,6 +294,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { static PBIO_ONESHOT(no_vbus_oneshot); static PBIO_ONESHOT(bcd_oneshot); static bool bcd_busy; + static struct etimer timer; PROCESS_POLLHANDLER({ if (!bcd_busy && pbio_oneshot(!vbus_active, &no_vbus_oneshot)) { @@ -164,12 +313,20 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { PROCESS_BEGIN(); + etimer_set(&timer, 5); + for (;;) { - PROCESS_WAIT_EVENT(); + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_POLL || (ev == PROCESS_EVENT_TIMER && etimer_expired(&timer))); if (bcd_busy) { bcd_busy = PT_SCHEDULE(pbdrv_usb_stm32_bcd_detect(&bcd_pt)); } + + if (etimer_expired(&timer)) { + etimer_reset(&timer); + pbdrv_usb_serial_transmit(); + pbdrv_usb_serial_receive(); + } } PROCESS_END();