From a14c469de0f373841cf4caab60339c16a3099bfd Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 1/8] Add new Pybricks device class Adds a new Pybricks device class and provides the correct descriptors so that Windows will use the WinUSB driver. Signed-off-by: Nate Karstens Co-authored-by: David Lechner --- bricks/_common/arm_none_eabi.mk | 1 + lib/pbio/drv/usb/stm32_usbd/usbd_conf.h | 3 +- lib/pbio/drv/usb/stm32_usbd/usbd_desc.c | 137 +++++- lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c | 481 ++++++++++++++++++++ lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h | 128 ++++++ 5 files changed, 748 insertions(+), 2 deletions(-) create mode 100644 lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c create mode 100644 lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h diff --git a/bricks/_common/arm_none_eabi.mk b/bricks/_common/arm_none_eabi.mk index b08ad1719..b99ed7006 100644 --- a/bricks/_common/arm_none_eabi.mk +++ b/bricks/_common/arm_none_eabi.mk @@ -382,6 +382,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..063f10fd9 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_conf.h +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_conf.h @@ -9,6 +9,7 @@ #define USBD_MAX_NUM_INTERFACES 1 #define USBD_MAX_NUM_CONFIGURATION 1 #define USBD_MAX_STR_DESC_SIZ 0x100 -#define USBD_SELF_POWERED 1 +#define USBD_SELF_POWERED 0 +#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 2519506dd..f3d017e4b 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c @@ -51,6 +51,7 @@ #include "usbd_core.h" #include "usbd_conf.h" +#include "usbd_pybricks.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ @@ -62,7 +63,10 @@ #define DEVICE_ID2 (0x1FFF7A14) #define DEVICE_ID3 (0x1FFF7A18) +#define USB_DEV_CAP_TYPE_PLATFORM (5) + #define USB_SIZ_STRING_SERIAL 0x1A +#define USB_SIZ_BOS_DESC 33 /* USB Standard Device Descriptor */ __ALIGN_BEGIN static @@ -72,7 +76,8 @@ const uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { 0x12, /* bLength */ USB_DESC_TYPE_DEVICE, /* bDescriptorType */ - 0x00, /* bcdUSB */ + 0x01, /* bcdUSB */ /* changed to USB version 2.01 + in order to support BOS Desc */ 0x02, PBIO_PYBRICKS_USB_DEVICE_CLASS, /* bDeviceClass */ PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, /* bDeviceSubClass */ @@ -90,6 +95,127 @@ uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = { USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ }; /* USB_DeviceDescriptor */ +/** BOS descriptor. */ +__ALIGN_BEGIN static 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 */ + 1, /* bNumDeviceCaps */ + + 28, /* bLength */ + USB_DEVICE_CAPABITY_TYPE, /* bDescriptorType = Device Capability */ + USB_DEV_CAP_TYPE_PLATFORM, /* bDevCapabilityType */ + 0x00, /* bReserved */ + + /* + * PlatformCapabilityUUID + * Microsoft OS 2.0 descriptor platform capability ID + * D8DD60DF-4589-4CC7-9CD2-659D9E648A9F + * RFC 4122 explains the correct byte ordering + */ + 0xDF, 0x60, 0xDD, 0xD8, /* 32-bit value */ + 0x89, 0x45, /* 16-bit value */ + 0xC7, 0x4C, /* 16-bit value */ + 0x9C, 0xD2, + 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F, + + 0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion = 0x06030000 for Windows 8.1 Build */ + LOBYTE(USBD_SIZ_MS_OS_DSCRPTR_SET), /* wMSOSDescriptorSetTotalLength */ + HIBYTE(USBD_SIZ_MS_OS_DSCRPTR_SET), /* wMSOSDescriptorSetTotalLength */ + USBD_MS_VENDOR_CODE, /* bMS_VendorCode */ + 0x00 /* bAltEnumCode = Does not support alternate enumeration */ +}; + +__ALIGN_BEGIN const uint8_t USBD_OSDescSet[USBD_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(USBD_SIZ_MS_OS_DSCRPTR_SET), /* wTotalLength */ + HIBYTE(USBD_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.) */ + + 0x84, 0x00, /* wLength = 132 */ + 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 = 80 */ + 0x50, 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', + '\0', '\0' +}; + /* USB Standard Device Descriptor */ __ALIGN_BEGIN static const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { USB_LEN_LANGID_STR_DESC, @@ -242,6 +368,14 @@ static uint8_t *USBD_Pybricks_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, ui return USBD_StrDesc; } +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; +} + USBD_DescriptorsTypeDef USBD_Pybricks_Desc = { .GetDeviceDescriptor = USBD_Pybricks_DeviceDescriptor, .GetLangIDStrDescriptor = USBD_Pybricks_LangIDStrDescriptor, @@ -250,6 +384,7 @@ USBD_DescriptorsTypeDef USBD_Pybricks_Desc = { .GetSerialStrDescriptor = USBD_Pybricks_SerialStrDescriptor, .GetConfigurationStrDescriptor = USBD_Pybricks_ConfigStrDescriptor, .GetInterfaceStrDescriptor = USBD_Pybricks_InterfaceStrDescriptor, + .GetBOSDescriptor = USBD_Pybricks_BOSDescriptor, }; void USBD_Pybricks_Desc_Init(void) { 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..e7c896bad --- /dev/null +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c @@ -0,0 +1,481 @@ +/** + ****************************************************************************** + * @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 + +#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 USBD_StatusTypeDef USBD_Pybricks_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static USBD_StatusTypeDef USBD_Pybricks_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static USBD_StatusTypeDef USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static USBD_StatusTypeDef USBD_Pybricks_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); +static USBD_StatusTypeDef USBD_Pybricks_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); +static USBD_StatusTypeDef 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, +}; + +/* USB Pybricks device Configuration Descriptor */ +__ALIGN_BEGIN static uint8_t USBD_Pybricks_CfgDesc[USBD_PYBRICKS_CONFIG_DESC_SIZ] __ALIGN_END = +{ + /* Configuration Descriptor */ + 0x09, /* bLength: Configuration Descriptor size */ + USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ + LOBYTE(USBD_PYBRICKS_CONFIG_DESC_SIZ), /* wTotalLength:no of returned bytes */ + HIBYTE(USBD_PYBRICKS_CONFIG_DESC_SIZ), + 0x01, /* bNumInterfaces: 1 interface */ + 0x01, /* bConfigurationValue: Configuration value */ + 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ + 0x80, /* bmAttributes */ + 250, /* MaxPower 500mA (number of 2mA units) */ + + /*---------------------------------------------------------------------------*/ + + /* 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 */ + PBIO_PYBRICKS_USB_DEVICE_CLASS, /* bInterfaceClass */ + PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, /* bInterfaceSubClass */ + PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, /* bInterfaceProtocol */ + 0x00, /* iInterface: */ + + /* Endpoint OUT Descriptor */ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ + USBD_PYBRICKS_OUT_EP, /* bEndpointAddress */ + USBD_EP_TYPE_BULK, /* bmAttributes */ + LOBYTE(USBD_PYBRICKS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ + HIBYTE(USBD_PYBRICKS_MAX_PACKET_SIZE), + 0x00, /* bInterval: ignore for Bulk transfer */ + + /* Endpoint IN Descriptor */ + 0x07, /* bLength: Endpoint Descriptor size */ + USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ + USBD_PYBRICKS_IN_EP, /* bEndpointAddress */ + USBD_EP_TYPE_BULK, /* bmAttributes */ + LOBYTE(USBD_PYBRICKS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ + HIBYTE(USBD_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 USBD_StatusTypeDef USBD_Pybricks_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { + UNUSED(cfgidx); + static USBD_Pybricks_HandleTypeDef hPybricks; + + pdev->pClassData = &hPybricks; + + (void)USBD_LL_OpenEP(pdev, USBD_PYBRICKS_IN_EP, USBD_EP_TYPE_BULK, USBD_PYBRICKS_MAX_PACKET_SIZE); + pdev->ep_in[USBD_PYBRICKS_IN_EP & 0xFU].is_used = 1U; + + (void)USBD_LL_OpenEP(pdev, USBD_PYBRICKS_OUT_EP, USBD_EP_TYPE_BULK, USBD_PYBRICKS_MAX_PACKET_SIZE); + pdev->ep_out[USBD_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, USBD_PYBRICKS_OUT_EP, hPybricks.RxBuffer, USBD_PYBRICKS_MAX_PACKET_SIZE); + + return USBD_OK; +} + +/** + * @brief USBD_Pybricks_DeInit + * DeInitialize the Pybricks layer + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static USBD_StatusTypeDef USBD_Pybricks_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) { + UNUSED(cfgidx); + + /* Close EP IN */ + (void)USBD_LL_CloseEP(pdev, USBD_PYBRICKS_IN_EP); + pdev->ep_in[USBD_PYBRICKS_IN_EP & 0xFU].is_used = 0U; + + /* Close EP OUT */ + (void)USBD_LL_CloseEP(pdev, USBD_PYBRICKS_OUT_EP); + pdev->ep_out[USBD_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 USBD_OK; +} + +/** + * @brief USBD_Pybricks_Setup + * Handle the Pybricks specific requests + * @param pdev: instance + * @param req: usb requests + * @retval status + */ +static USBD_StatusTypeDef USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, + USBD_SetupReqTypedef *req) { + 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: + break; + + case USB_REQ_TYPE_VENDOR: + switch (req->bRequest) + { + case USBD_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 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 USBD_StatusTypeDef USBD_Pybricks_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) { + USBD_Pybricks_HandleTypeDef *hPybricks = pdev->pClassData; + PCD_HandleTypeDef *hpcd = pdev->pData; + + if (hPybricks == NULL) { + return USBD_FAIL; + } + + 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 USBD_OK; +} + +/** + * @brief USBD_Pybricks_DataOut + * Data received on non-control Out endpoint + * @param pdev: device instance + * @param epnum: endpoint number + * @retval status + */ +static USBD_StatusTypeDef USBD_Pybricks_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) { + USBD_Pybricks_HandleTypeDef *hPybricks = pdev->pClassData; + + if (hPybricks == NULL) { + return 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 USBD_OK; +} + +/** + * @brief USBD_Pybricks_EP0_RxReady + * Handle EP0 Rx Ready event + * @param pdev: device instance + * @retval status + */ +static USBD_StatusTypeDef USBD_Pybricks_EP0_RxReady(USBD_HandleTypeDef *pdev) { + return USBD_OK; +} + +/** +* @brief USBD_Pybricks_RegisterInterface + * @param pdev: device instance + * @param fops: CD Interface callback + * @retval status + */ +USBD_StatusTypeDef USBD_Pybricks_RegisterInterface(USBD_HandleTypeDef *pdev, + USBD_Pybricks_ItfTypeDef *fops) { + if (fops == NULL) { + return USBD_FAIL; + } + + pdev->pUserData[pdev->classId] = fops; + + return USBD_OK; +} + +/** + * @brief USBD_Pybricks_SetRxBuffer + * @param pdev: device instance + * @param pbuff: Rx Buffer + * @retval status + */ +USBD_StatusTypeDef USBD_Pybricks_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff) { + USBD_Pybricks_HandleTypeDef *hPybricks = pdev->pClassData; + + if (hPybricks == NULL) { + return USBD_FAIL; + } + + hPybricks->RxBuffer = pbuff; + + return USBD_OK; +} + +/** + * @brief USBD_Pybricks_TransmitPacket + * Transmit packet on IN endpoint + * @param pdev: device instance + * @retval status + */ +USBD_StatusTypeDef USBD_Pybricks_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint32_t length) { + USBD_Pybricks_HandleTypeDef *hPybricks = pdev->pClassData; + USBD_StatusTypeDef ret = USBD_BUSY; + + if (hPybricks == NULL) { + return USBD_FAIL; + } + + if (hPybricks->TxState == 0U) { + /* Tx Transfer in progress */ + hPybricks->TxState = 1U; + + hPybricks->TxBuffer = pbuf; + hPybricks->TxLength = length; + + /* Update the packet total length */ + pdev->ep_in[USBD_PYBRICKS_IN_EP & 0xFU].total_length = hPybricks->TxLength; + + /* Transmit next packet */ + (void)USBD_LL_Transmit(pdev, USBD_PYBRICKS_IN_EP, hPybricks->TxBuffer, hPybricks->TxLength); + + ret = USBD_OK; + } + + return ret; +} + + +/** + * @brief USBD_Pybricks_ReceivePacket + * prepare OUT Endpoint for reception + * @param pdev: device instance + * @retval status + */ +USBD_StatusTypeDef USBD_Pybricks_ReceivePacket(USBD_HandleTypeDef *pdev) { + USBD_Pybricks_HandleTypeDef *hPybricks = pdev->pClassData; + + if (hPybricks == NULL) { + return USBD_FAIL; + } + + /* Prepare Out endpoint to receive next packet */ + (void)USBD_LL_PrepareReceive(pdev, USBD_PYBRICKS_OUT_EP, hPybricks->RxBuffer, USBD_PYBRICKS_MAX_PACKET_SIZE); + + return 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..1a51044eb --- /dev/null +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h @@ -0,0 +1,128 @@ +/** + ****************************************************************************** + * @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 USBD_MS_VENDOR_CODE 0x01 +#define USBD_SIZ_MS_OS_DSCRPTR_SET (10 + 20 + 132) + +#define USBD_PYBRICKS_CONFIG_DESC_SIZ (9 + 9 + 7 + 7) + +#define USBD_PYBRICKS_IN_EP 0x81U /* EP1 for data IN */ +#define USBD_PYBRICKS_OUT_EP 0x01U /* EP1 for data OUT */ + +#define USBD_PYBRICKS_MAX_PACKET_SIZE 64U + +/** + * @} + */ + + +/** @defgroup USBD_Pybricks_Exported_TypesDefinitions + * @{ + */ + +/** + * @} + */ +typedef struct +{ + USBD_StatusTypeDef (*Init)(void); + USBD_StatusTypeDef (*DeInit)(void); + USBD_StatusTypeDef (*Receive)(uint8_t *Buf, uint32_t *Len); + USBD_StatusTypeDef (*TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum); +} USBD_Pybricks_ItfTypeDef; + + +typedef struct +{ + 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 const uint8_t USBD_OSDescSet[USBD_SIZ_MS_OS_DSCRPTR_SET]; +/** + * @} + */ + +/** @defgroup USB_Pybricks_Exported_Functions + * @{ + */ +USBD_StatusTypeDef USBD_Pybricks_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_Pybricks_ItfTypeDef *fops); +USBD_StatusTypeDef USBD_Pybricks_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuf); +USBD_StatusTypeDef USBD_Pybricks_ReceivePacket(USBD_HandleTypeDef *pdev); +USBD_StatusTypeDef USBD_Pybricks_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint32_t length); +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* __USB_PYBRICKS_H */ +/** + * @} + */ + +/** + * @} + */ From e1b397b831dfd1d42106f21d9815daddb18de508 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 2/8] Add UUID little endian copy function Adds function to copy UUIDs in little endian format. USB is a little endian protocol, so this will be used to encode UUIDs properly on this medium. Signed-off-by: Nate Karstens --- lib/pbio/include/pbio/util.h | 1 + lib/pbio/src/util.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/lib/pbio/include/pbio/util.h b/lib/pbio/include/pbio/util.h index cf9227bfb..0ff8a38f7 100644 --- a/lib/pbio/include/pbio/util.h +++ b/lib/pbio/include/pbio/util.h @@ -118,6 +118,7 @@ void pbio_set_uint32_be(uint8_t *buf, uint32_t value) { buf[3] = value; } +void pbio_uuid128_le_copy(uint8_t *dst, const uint8_t *src); bool pbio_uuid128_reverse_compare(const uint8_t *uuid1, const uint8_t *uuid2); void pbio_uuid128_reverse_copy(uint8_t *dst, const uint8_t *src); diff --git a/lib/pbio/src/util.c b/lib/pbio/src/util.c index 1da0067af..efdd1d3ac 100644 --- a/lib/pbio/src/util.c +++ b/lib/pbio/src/util.c @@ -3,6 +3,35 @@ #include #include +#include + +/** + * Copies a 128-bit UUID from @p src to a buffer @p dst, + * which is a buffer used by a little endian medium. + * + * According to RFC 4122, the UUID is grouped into the following: + * 1) One 32-bit + * 2) Two 16-bit + * 3) Eight 8-bit + * + * @param [in] dst The destination that will receive the + * resulting little-endian-formatted UUID. + * @param [in] src The UUID in host byte order. + */ +void pbio_uuid128_le_copy(uint8_t *dst, const uint8_t *src) { + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + + dst[4] = src[5]; + dst[5] = src[4]; + + dst[6] = src[7]; + dst[7] = src[6]; + + memcpy(&dst[8], &src[8], 8); +} /** * Compares two 128-bit UUIDs with opposite byte ordering for equality. From 2cd791c9e527c7dbbebfaaa6cb5b224dfccab49e Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 3/8] Add additional info to BOS descriptor Adds additional information to the BOS Descriptor by appending dynamically-generated platform descriptors that use the same UUIDs that are used with BLE and contain the following values: * Device Name * Firmware version * Software (protocol) version * Hub capabilities Signed-off-by: Nate Karstens --- lib/pbio/drv/usb/stm32_usbd/usbd_desc.c | 129 +++++++++++++++++++++++- lib/pbio/include/pbio/protocol.h | 1 + lib/pbio/src/protocol/pybricks.c | 3 + 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c index f3d017e4b..58aa51e37 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c @@ -43,11 +43,16 @@ ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ +#include #include +#include #include #include +#include +#include +#include #include "usbd_core.h" #include "usbd_conf.h" @@ -59,6 +64,9 @@ #define USBD_CONFIGURATION_FS_STRING "Pybricks Config" #define USBD_INTERFACE_FS_STRING "Pybricks Interface" +static const char firmware_version[] = PBIO_VERSION_STR; +static const char software_version[] = PBIO_PROTOCOL_VERSION_STR; + #define DEVICE_ID1 (0x1FFF7A10) #define DEVICE_ID2 (0x1FFF7A14) #define DEVICE_ID3 (0x1FFF7A18) @@ -66,7 +74,15 @@ #define USB_DEV_CAP_TYPE_PLATFORM (5) #define USB_SIZ_STRING_SERIAL 0x1A -#define USB_SIZ_BOS_DESC 33 +#define USB_SIZ_BOS_DESC_CONST (5 + 28) +#define USB_SIZ_UUID (128 / 8) +#define USB_SIZ_PLATFORM_HDR (4 + USB_SIZ_UUID) +#define USB_SIZ_HUB_NAME_MAX (16) +#define USB_SIZ_BOS_DESC (USB_SIZ_BOS_DESC_CONST + \ + USB_SIZ_PLATFORM_HDR + USB_SIZ_HUB_NAME_MAX + \ + USB_SIZ_PLATFORM_HDR + sizeof(firmware_version) - 1 + \ + USB_SIZ_PLATFORM_HDR + sizeof(software_version) - 1 + \ + USB_SIZ_PLATFORM_HDR + PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE) /* USB Standard Device Descriptor */ __ALIGN_BEGIN static @@ -128,6 +144,8 @@ __ALIGN_BEGIN static uint8_t USBD_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END = 0x00 /* bAltEnumCode = Does not support alternate enumeration */ }; +static uint16_t USBD_BOSDesc_Len; + __ALIGN_BEGIN const uint8_t USBD_OSDescSet[USBD_SIZ_MS_OS_DSCRPTR_SET] __ALIGN_END = { 0x0A, 0x00, /* wLength = 10 */ @@ -275,6 +293,38 @@ static void Get_SerialNum(void) { } } +/** + * @brief Add the short BLE UUID to the buffer in little-endian format. + * @param dst The destination buffer + * @param dst The short BLE UUID to add + * @retval None + */ +static void add_ble_short_uuid_le(uint8_t *dst, uint16_t short_uuid) { + /* 32-bit */ + dst[0] = LOBYTE(short_uuid); + dst[1] = HIBYTE(short_uuid); + dst[2] = 0x00; + dst[3] = 0x00; + + /* 16-bit */ + dst[4] = 0x00; + dst[5] = 0x00; + + /* 16-bit */ + dst[6] = 0x00; + dst[7] = 0x10; + + /* 8-byte buffer */ + dst[8] = 0x80; + dst[9] = 0x00; + dst[10] = 0x00; + dst[11] = 0x80; + dst[12] = 0x5F; + dst[13] = 0x9B; + dst[14] = 0x34; + dst[15] = 0xFB; +} + /** * @brief Returns the device descriptor. * @param speed: Current device speed @@ -372,7 +422,7 @@ static uint8_t *USBD_Pybricks_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *l /* Prevent unused argument(s) compilation warning */ UNUSED(speed); - *length = sizeof(USBD_BOSDesc); + *length = USBD_BOSDesc_Len; return (uint8_t *)USBD_BOSDesc; } @@ -400,4 +450,79 @@ void USBD_Pybricks_Desc_Init(void) { USBD_DeviceDesc[11] = HIBYTE(PBDRV_CONFIG_USB_PID_1); } #endif + + const char *str; + size_t len; + + uint8_t *ptr = &USBD_BOSDesc[USB_SIZ_BOS_DESC_CONST]; + + /* Add device name */ + str = pbdrv_bluetooth_get_hub_name(); + len = MIN(strlen(str), USB_SIZ_HUB_NAME_MAX); + + *ptr++ = USB_SIZ_PLATFORM_HDR + len; // bLength + *ptr++ = USB_DEVICE_CAPABITY_TYPE; // bDescriptorType + *ptr++ = USB_DEV_CAP_TYPE_PLATFORM; // bDevCapabilityType + *ptr++ = 0x00; // bReserved + + // PlatformCapabilityUUID + add_ble_short_uuid_le(ptr, pbio_gatt_device_name_char_uuid); + ptr += USB_SIZ_UUID; + + // CapabilityData: Device Name + memcpy(ptr, str, len); + ptr += len; + + /* Add firmware version */ + *ptr++ = USB_SIZ_PLATFORM_HDR + sizeof(firmware_version) - 1; // bLength + *ptr++ = USB_DEVICE_CAPABITY_TYPE; // bDescriptorType + *ptr++ = USB_DEV_CAP_TYPE_PLATFORM; // bDevCapabilityType + *ptr++ = 0x00; // bReserved + + // PlatformCapabilityUUID + add_ble_short_uuid_le(ptr, pbio_gatt_firmware_version_char_uuid); + ptr += USB_SIZ_UUID; + + // CapabilityData: Firmware Version + memcpy(ptr, firmware_version, sizeof(firmware_version) - 1); + ptr += sizeof(firmware_version) - 1; + + /* Add software (protocol) version */ + *ptr++ = USB_SIZ_PLATFORM_HDR + sizeof(software_version) - 1; // bLength + *ptr++ = USB_DEVICE_CAPABITY_TYPE; // bDescriptorType + *ptr++ = USB_DEV_CAP_TYPE_PLATFORM; // bDevCapabilityType + *ptr++ = 0x00; // bReserved + + // PlatformCapabilityUUID + add_ble_short_uuid_le(ptr, pbio_gatt_software_version_char_uuid); + ptr += USB_SIZ_UUID; + + // CapabilityData: Software Version + memcpy(ptr, software_version, sizeof(software_version) - 1); + ptr += sizeof(software_version) - 1; + + /* Add hub capabilities */ + *ptr++ = USB_SIZ_PLATFORM_HDR + PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE; // bLength + *ptr++ = USB_DEVICE_CAPABITY_TYPE; // bDescriptorType + *ptr++ = USB_DEV_CAP_TYPE_PLATFORM; // bDevCapabilityType + *ptr++ = 0x00; // bReserved + + // PlatformCapabilityUUID + pbio_uuid128_le_copy(ptr, pbio_pybricks_hub_capabilities_char_uuid); + ptr += USB_SIZ_UUID; + + // CapabilityData: Hub Capabilities + pbio_pybricks_hub_capabilities(ptr, + USBD_PYBRICKS_MAX_PACKET_SIZE - 1, + PBSYS_APP_HUB_FEATURE_FLAGS, + PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE); + ptr += PBIO_PYBRICKS_HUB_CAPABILITIES_VALUE_SIZE; + + /* Update wTotalLength field in BOS Descriptor */ + USBD_BOSDesc_Len = ptr - USBD_BOSDesc; + USBD_BOSDesc[2] = LOBYTE(USBD_BOSDesc_Len); + USBD_BOSDesc[3] = HIBYTE(USBD_BOSDesc_Len); + + /* Update bNumDeviceCaps field in BOS Descriptor */ + USBD_BOSDesc[4] += 4; } diff --git a/lib/pbio/include/pbio/protocol.h b/lib/pbio/include/pbio/protocol.h index 4fa00bca2..cfe27ca67 100644 --- a/lib/pbio/include/pbio/protocol.h +++ b/lib/pbio/include/pbio/protocol.h @@ -300,6 +300,7 @@ extern const uint8_t pbio_pybricks_command_event_char_uuid[]; extern const uint8_t pbio_pybricks_hub_capabilities_char_uuid[]; extern const uint16_t pbio_gatt_device_info_service_uuid; +extern const uint16_t pbio_gatt_device_name_char_uuid; extern const uint16_t pbio_gatt_firmware_version_char_uuid; extern const uint16_t pbio_gatt_software_version_char_uuid; extern const uint16_t pbio_gatt_pnp_id_char_uuid; diff --git a/lib/pbio/src/protocol/pybricks.c b/lib/pbio/src/protocol/pybricks.c index 6bf2ec9e4..7a0916d30 100644 --- a/lib/pbio/src/protocol/pybricks.c +++ b/lib/pbio/src/protocol/pybricks.c @@ -85,6 +85,9 @@ const uint8_t pbio_pybricks_hub_capabilities_char_uuid[] = { /** Bluetooth Device Information Service UUID. */ const uint16_t pbio_gatt_device_info_service_uuid = 0x180A; +/** Bluetooth Device Name Characteristic UUID. */ +const uint16_t pbio_gatt_device_name_char_uuid = 0x2A00; + /** Bluetooth Firmware Version Characteristic UUID. */ const uint16_t pbio_gatt_firmware_version_char_uuid = 0x2A26; From b42f9b239b71ff330e2739668c082cfab91c7306 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 4/8] Configure USB device and process pybricks commands Configures the USB device and processes pybricks commands. Signed-off-by: Nate Karstens --- lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h | 5 + lib/pbio/drv/usb/usb_stm32.c | 112 +++++++++++++++++++- lib/pbio/platform/essential_hub/platform.c | 7 +- lib/pbio/platform/prime_hub/platform.c | 7 +- 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h index 1a51044eb..226f2b9b2 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h @@ -50,6 +50,10 @@ extern "C" { #define USBD_PYBRICKS_MAX_PACKET_SIZE 64U +#define USBD_PYBRICKS_MSG_COMMAND 0x00 +#define USBD_PYBRICKS_MSG_COMMAND_RESPONSE 0x01 +#define USBD_PYBRICKS_MSG_EVENT 0x02 + /** * @} */ @@ -99,6 +103,7 @@ typedef struct extern USBD_ClassTypeDef USBD_Pybricks_ClassDriver; extern const uint8_t USBD_OSDescSet[USBD_SIZ_MS_OS_DSCRPTR_SET]; + /** * @} */ diff --git a/lib/pbio/drv/usb/usb_stm32.c b/lib/pbio/drv/usb/usb_stm32.c index e7ab1fd5b..cd68863fd 100644 --- a/lib/pbio/drv/usb/usb_stm32.c +++ b/lib/pbio/drv/usb/usb_stm32.c @@ -8,22 +8,31 @@ #if PBDRV_CONFIG_USB_STM32F4 #include +#include #include #include #include #include +#include +#include #include #include +#include +#include #include "../charger/charger.h" #include "./usb_stm32.h" PROCESS(pbdrv_usb_process, "USB"); +static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE]; +static volatile uint32_t usb_in_sz; + static USBD_HandleTypeDef husbd; static PCD_HandleTypeDef hpcd; + static volatile bool vbus_active; static pbdrv_usb_bcd_t pbdrv_usb_bcd; @@ -121,6 +130,72 @@ 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 USBD_StatusTypeDef Pybricks_Itf_Init(void) { + USBD_Pybricks_SetRxBuffer(&husbd, usb_in_buf); + usb_in_sz = 0; + + 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 USBD_StatusTypeDef Pybricks_Itf_DeInit(void) { + 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 USBD_StatusTypeDef Pybricks_Itf_Receive(uint8_t *Buf, uint32_t *Len) { + if (*Len > sizeof(usb_in_buf)) { + return USBD_FAIL; + } + + memcpy(usb_in_buf, Buf, *Len); + usb_in_sz = *Len; + 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 USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len, uint8_t epnum) { + 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 +203,12 @@ void pbdrv_usb_init(void) { husbd.pData = &hpcd; hpcd.pData = &husbd; - USBD_Init(&husbd, NULL, 0); + USBD_Pybricks_Desc_Init(); + USBD_Init(&husbd, &USBD_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 @@ -145,6 +225,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { static struct pt bcd_pt; static PBIO_ONESHOT(no_vbus_oneshot); static PBIO_ONESHOT(bcd_oneshot); + static PBIO_ONESHOT(pwrdn_oneshot); static bool bcd_busy; PROCESS_POLLHANDLER({ @@ -164,11 +245,40 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { PROCESS_BEGIN(); + // Prepare to receive the first packet + USBD_Pybricks_ReceivePacket(&husbd); + for (;;) { PROCESS_WAIT_EVENT(); if (bcd_busy) { bcd_busy = PT_SCHEDULE(pbdrv_usb_stm32_bcd_detect(&bcd_pt)); + + if (bcd_busy) { + // All other USB functions are halted if BCD is busy + continue; + } + } + + if (pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN)) { + if (pbio_oneshot(true, &pwrdn_oneshot)) { + USBD_Stop(&husbd); + USBD_DeInit(&husbd); + } + + // USB communication is stopped after a shutdown, but + // the process is still needed to track the BCD state + continue; + } + + if (usb_in_sz) { + if (usb_in_buf[0] == USBD_PYBRICKS_MSG_COMMAND) { + pbsys_command(usb_in_buf + 1, usb_in_sz - 1); + } + + // Prepare to receive the next packet + usb_in_sz = 0; + USBD_Pybricks_ReceivePacket(&husbd); } } diff --git a/lib/pbio/platform/essential_hub/platform.c b/lib/pbio/platform/essential_hub/platform.c index 699043356..e5e4372a8 100644 --- a/lib/pbio/platform/essential_hub/platform.c +++ b/lib/pbio/platform/essential_hub/platform.c @@ -670,8 +670,13 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { } void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) { - HAL_NVIC_DisableIRQ(EXTI9_5_IRQn); HAL_NVIC_DisableIRQ(OTG_FS_IRQn); + + // The VBUS IRQ remains enabled so that it can still + // be triggered if the device is shut down but left + // connected to charge. When the charging cable is + // disconnected, the IRQ will trigger and lead to the + // device fully powering down. } void OTG_FS_IRQHandler(void) { diff --git a/lib/pbio/platform/prime_hub/platform.c b/lib/pbio/platform/prime_hub/platform.c index 1acd3a0b0..982778772 100644 --- a/lib/pbio/platform/prime_hub/platform.c +++ b/lib/pbio/platform/prime_hub/platform.c @@ -970,8 +970,13 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { } void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) { - HAL_NVIC_DisableIRQ(EXTI9_5_IRQn); HAL_NVIC_DisableIRQ(OTG_FS_IRQn); + + // The VBUS IRQ remains enabled so that it can still + // be triggered if the device is shut down but left + // connected to charge. When the charging cable is + // disconnected, the IRQ will trigger and lead to the + // device fully powering down. } void OTG_FS_IRQHandler(void) { From 659ceebc55752e4edbca6cc076d9959123ea0413 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 5/8] Transmit response message Transmit the response to a command. This is used to communicate any errors in handling the command back to the user. Signed-off-by: Nate Karstens --- lib/pbio/drv/usb/usb_stm32.c | 44 ++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/pbio/drv/usb/usb_stm32.c b/lib/pbio/drv/usb/usb_stm32.c index cd68863fd..775101228 100644 --- a/lib/pbio/drv/usb/usb_stm32.c +++ b/lib/pbio/drv/usb/usb_stm32.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -28,7 +29,10 @@ PROCESS(pbdrv_usb_process, "USB"); static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE]; +static uint8_t usb_response_buf[1 + sizeof(uint32_t)]; static volatile uint32_t usb_in_sz; +static volatile uint32_t usb_response_sz; +static volatile bool transmitting; static USBD_HandleTypeDef husbd; static PCD_HandleTypeDef hpcd; @@ -139,6 +143,8 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) { static USBD_StatusTypeDef Pybricks_Itf_Init(void) { USBD_Pybricks_SetRxBuffer(&husbd, usb_in_buf); usb_in_sz = 0; + usb_response_sz = 0; + transmitting = false; return USBD_OK; } @@ -185,8 +191,17 @@ static USBD_StatusTypeDef Pybricks_Itf_Receive(uint8_t *Buf, uint32_t *Len) { * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL */ static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len, uint8_t epnum) { + USBD_StatusTypeDef ret = USBD_OK; + + if (Buf == usb_response_buf) { + usb_response_sz = 0; + } else { + ret = USBD_FAIL; + } + + transmitting = false; process_poll(&pbdrv_usb_process); - return USBD_OK; + return ret; } static USBD_Pybricks_ItfTypeDef USBD_Pybricks_fops = { @@ -227,6 +242,7 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { static PBIO_ONESHOT(bcd_oneshot); static PBIO_ONESHOT(pwrdn_oneshot); static bool bcd_busy; + static pbio_pybricks_error_t result; PROCESS_POLLHANDLER({ if (!bcd_busy && pbio_oneshot(!vbus_active, &no_vbus_oneshot)) { @@ -273,12 +289,30 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { if (usb_in_sz) { if (usb_in_buf[0] == USBD_PYBRICKS_MSG_COMMAND) { - pbsys_command(usb_in_buf + 1, usb_in_sz - 1); + if (usb_response_sz == 0) { + result = pbsys_command(usb_in_buf + 1, usb_in_sz - 1); + usb_response_buf[0] = USBD_PYBRICKS_MSG_COMMAND_RESPONSE; + pbio_set_uint32_le(&usb_response_buf[1], result); + usb_response_sz = sizeof(usb_response_buf); + + // Prepare to receive the next packet + usb_in_sz = 0; + USBD_Pybricks_ReceivePacket(&husbd); + } + } else { + // Ignore unknown packet + usb_in_sz = 0; + USBD_Pybricks_ReceivePacket(&husbd); } + } + + if (transmitting) { + continue; + } - // Prepare to receive the next packet - usb_in_sz = 0; - USBD_Pybricks_ReceivePacket(&husbd); + if (usb_response_sz) { + transmitting = true; + USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz); } } From d756c90128d3975cac59d0c39de8f43a3090e0b7 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 6/8] Periodically send status Periodically sends status flags over the USB connection. Signed-off-by: Nate Karstens --- lib/pbio/drv/usb/usb_stm32.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/pbio/drv/usb/usb_stm32.c b/lib/pbio/drv/usb/usb_stm32.c index 775101228..3c588e82a 100644 --- a/lib/pbio/drv/usb/usb_stm32.c +++ b/lib/pbio/drv/usb/usb_stm32.c @@ -30,8 +30,10 @@ PROCESS(pbdrv_usb_process, "USB"); static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE]; static uint8_t usb_response_buf[1 + sizeof(uint32_t)]; +static uint8_t usb_status_buf[1 + 1 + sizeof(uint32_t)]; static volatile uint32_t usb_in_sz; static volatile uint32_t usb_response_sz; +static volatile uint32_t usb_status_sz; static volatile bool transmitting; static USBD_HandleTypeDef husbd; @@ -144,6 +146,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) { USBD_Pybricks_SetRxBuffer(&husbd, usb_in_buf); usb_in_sz = 0; usb_response_sz = 0; + usb_status_sz = 0; transmitting = false; return USBD_OK; @@ -195,6 +198,8 @@ static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len, if (Buf == usb_response_buf) { usb_response_sz = 0; + } else if (Buf == usb_status_buf) { + usb_status_sz = 0; } else { ret = USBD_FAIL; } @@ -243,6 +248,9 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { static PBIO_ONESHOT(pwrdn_oneshot); static bool bcd_busy; static pbio_pybricks_error_t result; + static struct etimer timer; + static uint32_t prev_status_flags = ~0; + static uint32_t new_status_flags; PROCESS_POLLHANDLER({ if (!bcd_busy && pbio_oneshot(!vbus_active, &no_vbus_oneshot)) { @@ -264,6 +272,8 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { // Prepare to receive the first packet USBD_Pybricks_ReceivePacket(&husbd); + etimer_set(&timer, 500); + for (;;) { PROCESS_WAIT_EVENT(); @@ -310,7 +320,22 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { continue; } - if (usb_response_sz) { + new_status_flags = pbsys_status_get_flags(); + + // Transmit. Give priority to status updates. + if ((new_status_flags != prev_status_flags) || etimer_expired(&timer)) { + + usb_status_buf[0] = USBD_PYBRICKS_MSG_EVENT; + usb_status_sz = 1 + pbio_pybricks_event_status_report(&usb_status_buf[1], new_status_flags); + + etimer_restart(&timer); + prev_status_flags = new_status_flags; + + transmitting = true; + USBD_Pybricks_TransmitPacket(&husbd, usb_status_buf, usb_status_sz); + + } else if (usb_response_sz) { + transmitting = true; USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz); } From dbbf6a0e013e6c21662772cc1bb09f2b3940d453 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 7/8] Add support for transmitting stdout over USB Adds support for transmitting stdout messages over USB. Signed-off-by: Nate Karstens --- bricks/_common_stm32/mphalport.c | 42 ++++++++++++++++++-- lib/pbio/drv/usb/usb_stm32.c | 68 +++++++++++++++++++++++++++++++- lib/pbio/include/pbdrv/usb.h | 23 +++++++++++ 3 files changed, 127 insertions(+), 6 deletions(-) diff --git a/bricks/_common_stm32/mphalport.c b/bricks/_common_stm32/mphalport.c index d2a2fc30c..9f2def9da 100644 --- a/bricks/_common_stm32/mphalport.c +++ b/bricks/_common_stm32/mphalport.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -128,9 +129,40 @@ int mp_hal_stdin_rx_chr(void) { // Send string of given length void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + uint32_t size; + pbio_error_t err; + + #if PBDRV_CONFIG_USB + + const char *usb_ptr = str; + mp_uint_t usb_len = len; + + while (usb_len) { + size = usb_len; + err = pbdrv_usb_stdout_tx((const uint8_t *)usb_ptr, &size); + + if (err == PBIO_SUCCESS) { + usb_ptr += size; + usb_len -= size; + continue; + } + + if (err != PBIO_ERROR_AGAIN) { + // Ignoring error for now. This means + // stdout lost if USB is disconnected. + break; + } + + if (usb_len) { + MICROPY_EVENT_POLL_HOOK + } + } + + #endif // PBDRV_CONFIG_USB + while (len) { - uint32_t size = len; - pbio_error_t err = pbsys_bluetooth_tx((const uint8_t *)str, &size); + size = len; + err = pbsys_bluetooth_tx((const uint8_t *)str, &size); if (err == PBIO_SUCCESS) { str += size; @@ -144,12 +176,14 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { return; } - MICROPY_EVENT_POLL_HOOK + if (len) { + MICROPY_EVENT_POLL_HOOK + } } } void mp_hal_stdout_tx_flush(void) { - while (!pbsys_bluetooth_tx_is_idle()) { + while (!pbsys_bluetooth_tx_is_idle() && !pbdrv_usb_stdout_tx_is_idle()) { MICROPY_EVENT_POLL_HOOK } } diff --git a/lib/pbio/drv/usb/usb_stm32.c b/lib/pbio/drv/usb/usb_stm32.c index 3c588e82a..1773cb478 100644 --- a/lib/pbio/drv/usb/usb_stm32.c +++ b/lib/pbio/drv/usb/usb_stm32.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -31,9 +32,13 @@ PROCESS(pbdrv_usb_process, "USB"); static uint8_t usb_in_buf[USBD_PYBRICKS_MAX_PACKET_SIZE]; static uint8_t usb_response_buf[1 + sizeof(uint32_t)]; static uint8_t usb_status_buf[1 + 1 + sizeof(uint32_t)]; +static uint8_t usb_stdout_buf[USBD_PYBRICKS_MAX_PACKET_SIZE]; static volatile uint32_t usb_in_sz; static volatile uint32_t usb_response_sz; static volatile uint32_t usb_status_sz; +static volatile uint32_t usb_stdout_sz; +static volatile clock_time_t usb_response_tx_time; +static volatile clock_time_t usb_stdout_tx_time; static volatile bool transmitting; static USBD_HandleTypeDef husbd; @@ -136,6 +141,53 @@ void pbdrv_usb_stm32_handle_vbus_irq(bool active) { process_poll(&pbdrv_usb_process); } +/** + * Queues data to be transmitted via USB. + * @param data [in] The data to be sent. + * @param size [in, out] The size of @p data in bytes. After return, @p size + * contains the number of bytes actually written. + * @return ::PBIO_SUCCESS if @p data was queued, ::PBIO_ERROR_AGAIN + * if @p data could not be queued at this time (e.g. buffer + * is full), ::PBIO_ERROR_INVALID_OP if there is not an + * active USB connection or ::PBIO_ERROR_NOT_SUPPORTED + * if this platform does not support USB. + */ +pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) { + uint8_t *ptr = usb_stdout_buf; + uint32_t ptr_len = sizeof(usb_stdout_buf); + + // TODO: return PBIO_ERROR_INVALID_OP if not connected + + if (usb_stdout_sz) { + return PBIO_ERROR_AGAIN; + } + + *ptr = USBD_PYBRICKS_MSG_EVENT; + ptr++; + ptr_len--; + + *ptr = PBIO_PYBRICKS_EVENT_WRITE_STDOUT; + ptr++; + ptr_len--; + + *size = MIN(*size, ptr_len); + memcpy(ptr, data, *size); + + usb_stdout_sz = 1 + 1 + *size; + + process_poll(&pbdrv_usb_process); + + return PBIO_SUCCESS; +} + +/** + * Indicates if there is stdout data waiting to be transmitted over USB. + * @retval false if stdout data is currently being transmitted. + */ +bool pbdrv_usb_stdout_tx_is_idle(void) { + return usb_stdout_sz == 0; +} + /** * @brief Pybricks_Itf_Init * Initializes the Pybricks media low layer @@ -147,6 +199,7 @@ static USBD_StatusTypeDef Pybricks_Itf_Init(void) { usb_in_sz = 0; usb_response_sz = 0; usb_status_sz = 0; + usb_stdout_sz = 0; transmitting = false; return USBD_OK; @@ -200,6 +253,8 @@ static USBD_StatusTypeDef Pybricks_Itf_TransmitCplt(uint8_t *Buf, uint32_t *Len, usb_response_sz = 0; } else if (Buf == usb_status_buf) { usb_status_sz = 0; + } else if (Buf == usb_stdout_buf) { + usb_stdout_sz = 0; } else { ret = USBD_FAIL; } @@ -322,7 +377,8 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { new_status_flags = pbsys_status_get_flags(); - // Transmit. Give priority to status updates. + // Transmit. Give priority to status updates, then + // cycle between response and stdout buffers. if ((new_status_flags != prev_status_flags) || etimer_expired(&timer)) { usb_status_buf[0] = USBD_PYBRICKS_MSG_EVENT; @@ -334,10 +390,18 @@ PROCESS_THREAD(pbdrv_usb_process, ev, data) { transmitting = true; USBD_Pybricks_TransmitPacket(&husbd, usb_status_buf, usb_status_sz); - } else if (usb_response_sz) { + } else if (usb_response_sz && + (!usb_stdout_sz || (usb_response_tx_time < usb_stdout_tx_time))) { transmitting = true; + usb_response_tx_time = clock_time(); USBD_Pybricks_TransmitPacket(&husbd, usb_response_buf, usb_response_sz); + + } else if (usb_stdout_sz) { + + transmitting = true; + usb_stdout_tx_time = clock_time(); + USBD_Pybricks_TransmitPacket(&husbd, usb_stdout_buf, usb_stdout_sz); } } diff --git a/lib/pbio/include/pbdrv/usb.h b/lib/pbio/include/pbdrv/usb.h index 1a4a40515..27b78687f 100644 --- a/lib/pbio/include/pbdrv/usb.h +++ b/lib/pbio/include/pbdrv/usb.h @@ -9,7 +9,10 @@ #ifndef _PBDRV_USB_H_ #define _PBDRV_USB_H_ +#include + #include +#include /** * Indicates battery charging capabilites that were detected on a USB port. @@ -35,12 +38,32 @@ typedef enum { */ pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void); +/** + * Transmits the given buffer over the USB stdout stream. + * @return The result of the operation. + */ +pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size); + +/** + * Indicates if the USB stdout stream is idle. + * @return true if the USB stdout stream is idle. +*/ +bool pbdrv_usb_stdout_tx_is_idle(void); + #else // PBDRV_CONFIG_USB static inline pbdrv_usb_bcd_t pbdrv_usb_get_bcd(void) { return PBDRV_USB_BCD_NONE; } +static inline pbio_error_t pbdrv_usb_stdout_tx(const uint8_t *data, uint32_t *size) { + return PBIO_SUCCESS; +} + +static inline bool pbdrv_usb_stdout_tx_is_idle(void) { + return true; +} + #endif // PBDRV_CONFIG_USB #endif // _PBDRV_USB_H_ From de2b32521dc2dcf97df786fe2e422be0785d9417 Mon Sep 17 00:00:00 2001 From: Nate Karstens Date: Mon, 19 Feb 2024 00:51:19 -0600 Subject: [PATCH 8/8] Add WebUSB platform descriptor Adds a WebUSB platform descriptor to the BOS descriptor and a landing page pointing to https://code.pybricks.com. Signed-off-by: Nate Karstens --- lib/pbio/drv/usb/stm32_usbd/usbd_desc.c | 28 ++++++++++++++++++--- lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c | 20 +++++++++++++++ lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h | 4 +++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c index 58aa51e37..4d5c162b5 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c @@ -74,7 +74,7 @@ static const char software_version[] = PBIO_PROTOCOL_VERSION_STR; #define USB_DEV_CAP_TYPE_PLATFORM (5) #define USB_SIZ_STRING_SERIAL 0x1A -#define USB_SIZ_BOS_DESC_CONST (5 + 28) +#define USB_SIZ_BOS_DESC_CONST (5 + 28 + 24) #define USB_SIZ_UUID (128 / 8) #define USB_SIZ_PLATFORM_HDR (4 + USB_SIZ_UUID) #define USB_SIZ_HUB_NAME_MAX (16) @@ -118,7 +118,7 @@ __ALIGN_BEGIN static uint8_t USBD_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END = USB_DESC_TYPE_BOS, /* bDescriptorType = BOS */ LOBYTE(USB_SIZ_BOS_DESC), /* wTotalLength */ HIBYTE(USB_SIZ_BOS_DESC), /* wTotalLength */ - 1, /* bNumDeviceCaps */ + 2, /* bNumDeviceCaps */ 28, /* bLength */ USB_DEVICE_CAPABITY_TYPE, /* bDescriptorType = Device Capability */ @@ -141,7 +141,29 @@ __ALIGN_BEGIN static uint8_t USBD_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END = LOBYTE(USBD_SIZ_MS_OS_DSCRPTR_SET), /* wMSOSDescriptorSetTotalLength */ HIBYTE(USBD_SIZ_MS_OS_DSCRPTR_SET), /* wMSOSDescriptorSetTotalLength */ USBD_MS_VENDOR_CODE, /* bMS_VendorCode */ - 0x00 /* bAltEnumCode = Does not support alternate enumeration */ + 0x00, /* bAltEnumCode = Does not support alternate enumeration */ + + 24, /* bLength */ + USB_DEVICE_CAPABITY_TYPE, /* bDescriptorType = Device Capability */ + USB_DEV_CAP_TYPE_PLATFORM, /* bDevCapabilityType */ + 0x00, /* bReserved */ + + /* + * PlatformCapabilityUUID + * WebUSB Platform Capability descriptor + * 3408B638-09A9-47A0-8BFD-A0768815B665 + * RFC 4122 explains the correct byte ordering + */ + 0x38, 0xB6, 0x08, 0x34, /* 32-bit value */ + 0xA9, 0x09, /* 16-bit value */ + 0xA0, 0x47, /* 16-bit value */ + 0x8B, 0xFD, + 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65, + + LOBYTE(0x0100), /* bcdVersion */ + HIBYTE(0x0100), /* bcdVersion */ + USBD_WEBUSB_VENDOR_CODE, /* bVendorCode */ + USBD_WEBUSB_LANDING_PAGE_IDX /* iLandingPage */ }; static uint16_t USBD_BOSDesc_Len; diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c index e7c896bad..fdbc4a32c 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c @@ -155,6 +155,18 @@ __ALIGN_BEGIN static uint8_t USBD_Pybricks_CfgDesc[USBD_PYBRICKS_CONFIG_DESC_SIZ 0x00 /* bInterval */ }; +__ALIGN_BEGIN static const uint8_t WebUSB_DescSet[20] __ALIGN_END = +{ + 20, /* bLength */ + 0x03, /* bDescriptorType = URL */ + 0x01, /* bScheme = https:// */ + + /* URL */ + 'c', 'o', 'd', 'e', '.', + 'p', 'y', 'b', 'r', 'i', 'c', 'k', 's', '.', + 'c', 'o', 'm' +}; + /** * @} */ @@ -248,6 +260,14 @@ static USBD_StatusTypeDef USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, MIN(sizeof(USBD_OSDescSet), req->wLength)); break; + case USBD_WEBUSB_VENDOR_CODE: + if ((req->wValue == USBD_WEBUSB_LANDING_PAGE_IDX) && (req->wIndex == 0x02)) { + (void)USBD_CtlSendData(pdev, + (uint8_t *)WebUSB_DescSet, + MIN(sizeof(WebUSB_DescSet), req->wLength)); + } + break; + default: USBD_CtlError(pdev, req); ret = USBD_FAIL; diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h index 226f2b9b2..43af3b167 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h @@ -41,8 +41,12 @@ extern "C" { * @{ */ #define USBD_MS_VENDOR_CODE 0x01 +#define USBD_WEBUSB_VENDOR_CODE 0x02 + #define USBD_SIZ_MS_OS_DSCRPTR_SET (10 + 20 + 132) +#define USBD_WEBUSB_LANDING_PAGE_IDX 1 + #define USBD_PYBRICKS_CONFIG_DESC_SIZ (9 + 9 + 7 + 7) #define USBD_PYBRICKS_IN_EP 0x81U /* EP1 for data IN */