diff --git a/bricks/_common/sources.mk b/bricks/_common/sources.mk index 6503a9f09..421abfde7 100644 --- a/bricks/_common/sources.mk +++ b/bricks/_common/sources.mk @@ -186,6 +186,7 @@ PBIO_SRC_C = $(addprefix lib/pbio/,\ drv/uart/uart_stm32f0.c \ drv/uart/uart_stm32f4_ll_irq.c \ drv/uart/uart_stm32l4_ll_dma.c \ + drv/usb/usb_common_desc.c \ drv/usb/usb_ev3.c \ drv/usb/usb_nxt.c \ drv/usb/usb_stm32.c \ diff --git a/lib/lego/lego/usb.h b/lib/lego/lego/usb.h index 5c57cbf00..9510119be 100644 --- a/lib/lego/lego/usb.h +++ b/lib/lego/lego/usb.h @@ -10,6 +10,8 @@ /** Official LEGO USB Vendor ID. */ #define LEGO_USB_VID 0x0694 +/** Official LEGO USB Product ID for MINDSTORMS NXT. */ +#define LEGO_USB_PID_NXT 0x0002 /** Official LEGO USB Product ID for MINDSTORMS EV3. */ #define LEGO_USB_PID_EV3 0x0005 /** Official LEGO USB Product ID for MINDSTORMS EV3. */ @@ -28,12 +30,14 @@ #define LEGO_USB_PID_ROBOT_INVENTOR_DFU 0x0011 /** Official LEGO USB Manufacturer String. */ -#define LEGO_USB_MFG_STR "LEGO System A/S" +#define LEGO_USB_MFG_STR u"LEGO System A/S" +/** NXT does not officially come with a product string */ +#define LEGO_USB_PROD_STR_NXT u"NXT" /** Official LEGO USB Product String for MINDSTORMS EV3. */ -#define LEGO_USB_PROD_STR_EV3 "LEGO MINDSTORMS EV3" +#define LEGO_USB_PROD_STR_EV3 u"LEGO MINDSTORMS EV3" /** Official LEGO USB Product String for SPIKE Prime and MINDSTORMS Robot Inventor. */ -#define LEGO_USB_PROD_STR_TECHNIC_LARGE_HUB "LEGO Technic Large Hub" +#define LEGO_USB_PROD_STR_TECHNIC_LARGE_HUB u"LEGO Technic Large Hub" /** Official LEGO USB Product String for SPIKE Essential. */ -#define LEGO_USB_PROD_STR_TECHNIC_SMALL_HUB "LEGO Technic Small Hub" +#define LEGO_USB_PROD_STR_TECHNIC_SMALL_HUB u"LEGO Technic Small Hub" #endif // _LEGO_USB_H_ diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c index c03afa3ad..e3355d9aa 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_desc.c @@ -54,11 +54,12 @@ #include "usbd_conf.h" #include "usbd_pybricks.h" +#include "../usb_ch9.h" +#include "../usb_common_desc.h" + /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ #define USBD_LANGID_STRING 0x409 -#define USBD_CONFIGURATION_FS_STRING "Pybricks Config" -#define USBD_INTERFACE_FS_STRING "Pybricks Interface" // STM32 MCU Device ID register addresses // REVISIT: make pbdrv_xxx_get_serial_number() and use that instead @@ -71,197 +72,37 @@ // descriptor sizes #define USB_SIZ_STRING_SERIAL 26 -#define USB_SIZ_BOS_DESC (5 + 28 + 24) /* USB Standard Device Descriptor */ -__ALIGN_BEGIN static +static #if !defined(PBDRV_CONFIG_USB_STM32F4_HUB_VARIANT_ADDR) const #endif -uint8_t USBD_DeviceDesc[] __ALIGN_END = { - 0x12, /* bLength */ - USB_DESC_TYPE_DEVICE, /* bDescriptorType */ - 0x10, 0x02, /* bcdUSB = 2.1.0 (for BOS support) */ - PBIO_PYBRICKS_USB_DEVICE_CLASS, /* bDeviceClass */ - PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, /* bDeviceSubClass */ - PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, /* bDeviceProtocol */ - USB_MAX_EP0_SIZE, /* bMaxPacketSize */ - LOBYTE(PBDRV_CONFIG_USB_VID), /* idVendor */ - HIBYTE(PBDRV_CONFIG_USB_VID), /* idVendor */ - LOBYTE(PBDRV_CONFIG_USB_PID), /* idProduct */ - HIBYTE(PBDRV_CONFIG_USB_PID), /* idProduct */ - 0x00, 0x02, /* bcdDevice rel. 2.0.0 */ - USBD_IDX_MFC_STR, /* Index of manufacturer string */ - USBD_IDX_PRODUCT_STR, /* Index of product string */ - USBD_IDX_SERIAL_STR, /* Index of serial number string */ - USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ +pbdrv_usb_dev_desc_union_t USBD_DeviceDesc = { + .s = { + .bLength = sizeof(pbdrv_usb_dev_desc_t), + .bDescriptorType = DESC_TYPE_DEVICE, + .bcdUSB = 0x0210, /* 2.1.0 (for BOS support) */ + .bDeviceClass = PBIO_PYBRICKS_USB_DEVICE_CLASS, + .bDeviceSubClass = PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, + .bDeviceProtocol = PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, + .bMaxPacketSize0 = USB_MAX_EP0_SIZE, + .idVendor = PBDRV_CONFIG_USB_VID, + .idProduct = PBDRV_CONFIG_USB_PID, + .bcdDevice = 0x0200, /* rel. 2.0.0 */ + .iManufacturer = USBD_IDX_MFC_STR, + .iProduct = USBD_IDX_PRODUCT_STR, + .iSerialNumber = USBD_IDX_SERIAL_STR, + .bNumConfigurations = USBD_MAX_NUM_CONFIGURATION, + } }; /* USB_DeviceDescriptor */ -_Static_assert(USB_LEN_DEV_DESC == sizeof(USBD_DeviceDesc)); - -/** BOS descriptor. */ -__ALIGN_BEGIN static const uint8_t USBD_BOSDesc[] __ALIGN_END = -{ - 5, /* bLength */ - USB_DESC_TYPE_BOS, /* bDescriptorType = BOS */ - LOBYTE(USB_SIZ_BOS_DESC), /* wTotalLength */ - HIBYTE(USB_SIZ_BOS_DESC), /* wTotalLength */ - 2, /* bNumDeviceCaps */ - - // IMPORTANT: The WebUSB descriptor must be first to make Chromium happy. - - 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_VENDOR_CODE_WEBUSB, /* bVendorCode */ - USBD_WEBUSB_LANDING_PAGE_IDX, /* iLandingPage */ - - // IMPORTANT: The MS OS 2.0 descriptor must be last to make Chromium happy. - - 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_VENDOR_CODE_MS, /* bMS_VendorCode */ - 0x00, /* bAltEnumCode = Does not support alternate enumeration */ -}; -_Static_assert(USB_SIZ_BOS_DESC == sizeof(USBD_BOSDesc)); - -__ALIGN_BEGIN const uint8_t USBD_OSDescSet[] __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' -}; -_Static_assert(USBD_SIZ_MS_OS_DSCRPTR_SET == sizeof(USBD_OSDescSet)); /* USB Standard Device Descriptor */ -__ALIGN_BEGIN static const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = { - USB_LEN_LANGID_STR_DESC, - USB_DESC_TYPE_STRING, - LOBYTE(USBD_LANGID_STRING), - HIBYTE(USBD_LANGID_STRING), -}; - __ALIGN_BEGIN static uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] __ALIGN_END = { USB_SIZ_STRING_SERIAL, USB_DESC_TYPE_STRING, }; -__ALIGN_BEGIN static uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; - /** * @brief Convert Hex 32Bits value into char @@ -316,8 +157,8 @@ static uint8_t *USBD_Pybricks_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t /* Prevent unused argument(s) compilation warning */ UNUSED(speed); - *length = sizeof(USBD_DeviceDesc); - return (uint8_t *)USBD_DeviceDesc; + *length = sizeof(USBD_DeviceDesc.s); + return (uint8_t *)&USBD_DeviceDesc; } /** @@ -330,8 +171,8 @@ static uint8_t *USBD_Pybricks_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint1 /* Prevent unused argument(s) compilation warning */ UNUSED(speed); - *length = sizeof(USBD_LangIDDesc); - return (uint8_t *)USBD_LangIDDesc; + *length = sizeof(pbdrv_usb_str_desc_langid.s); + return (uint8_t *)&pbdrv_usb_str_desc_langid; } /** @@ -341,8 +182,11 @@ static uint8_t *USBD_Pybricks_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint1 * @retval Pointer to descriptor buffer */ static uint8_t *USBD_Pybricks_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) { - USBD_GetString((uint8_t *)PBDRV_CONFIG_USB_PROD_STR, USBD_StrDesc, length); - return USBD_StrDesc; + /* Prevent unused argument(s) compilation warning */ + UNUSED(speed); + + *length = sizeof(pbdrv_usb_str_desc_prod.s); + return (uint8_t *)&pbdrv_usb_str_desc_prod; } /** @@ -355,8 +199,8 @@ static uint8_t *USBD_Pybricks_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, /* Prevent unused argument(s) compilation warning */ UNUSED(speed); - USBD_GetString((uint8_t *)PBDRV_CONFIG_USB_MFG_STR, USBD_StrDesc, length); - return USBD_StrDesc; + *length = sizeof(pbdrv_usb_str_desc_mfg.s); + return (uint8_t *)&pbdrv_usb_str_desc_mfg; } /** @@ -377,34 +221,12 @@ static uint8_t *USBD_Pybricks_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint1 return (uint8_t *)USBD_StringSerial; } -/** - * @brief Returns the configuration string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_Pybricks_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) { - USBD_GetString((uint8_t *)USBD_CONFIGURATION_FS_STRING, USBD_StrDesc, length); - return USBD_StrDesc; -} - -/** - * @brief Returns the interface string descriptor. - * @param speed: Current device speed - * @param length: Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -static uint8_t *USBD_Pybricks_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) { - USBD_GetString((uint8_t *)USBD_INTERFACE_FS_STRING, USBD_StrDesc, length); - return USBD_StrDesc; -} - static uint8_t *USBD_Pybricks_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) { /* Prevent unused argument(s) compilation warning */ UNUSED(speed); - *length = USB_SIZ_BOS_DESC; - return (uint8_t *)USBD_BOSDesc; + *length = sizeof(pbdrv_usb_bos_desc_set.s); + return (uint8_t *)&pbdrv_usb_bos_desc_set; } USBD_DescriptorsTypeDef USBD_Pybricks_Desc = { @@ -413,8 +235,6 @@ USBD_DescriptorsTypeDef USBD_Pybricks_Desc = { .GetManufacturerStrDescriptor = USBD_Pybricks_ManufacturerStrDescriptor, .GetProductStrDescriptor = USBD_Pybricks_ProductStrDescriptor, .GetSerialStrDescriptor = USBD_Pybricks_SerialStrDescriptor, - .GetConfigurationStrDescriptor = USBD_Pybricks_ConfigStrDescriptor, - .GetInterfaceStrDescriptor = USBD_Pybricks_InterfaceStrDescriptor, .GetBOSDescriptor = USBD_Pybricks_BOSDescriptor, }; @@ -424,11 +244,9 @@ void USBD_Pybricks_Desc_Init(void) { #ifdef PBDRV_CONFIG_USB_STM32F4_HUB_VARIANT_ADDR #define VARIANT (*(uint32_t *)PBDRV_CONFIG_USB_STM32F4_HUB_VARIANT_ADDR) if (VARIANT == 0) { - USBD_DeviceDesc[10] = LOBYTE(PBDRV_CONFIG_USB_PID_0); - USBD_DeviceDesc[11] = HIBYTE(PBDRV_CONFIG_USB_PID_0); + USBD_DeviceDesc.s.idProduct = PBDRV_CONFIG_USB_PID_0; } else if (VARIANT == 1) { - USBD_DeviceDesc[10] = LOBYTE(PBDRV_CONFIG_USB_PID_1); - USBD_DeviceDesc[11] = HIBYTE(PBDRV_CONFIG_USB_PID_1); + USBD_DeviceDesc.s.idProduct = PBDRV_CONFIG_USB_PID_1; } #endif } diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c index df65c6bf5..f550db0ec 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.c @@ -42,6 +42,9 @@ #include "usbd_ctlreq.h" #include "usbd_pybricks.h" +#include "../usb_ch9.h" +#include "../usb_common_desc.h" + /** @addtogroup STM32_USB_DEVICE_LIBRARY * @{ @@ -111,70 +114,54 @@ USBD_ClassTypeDef USBD_Pybricks_ClassDriver = }; /* 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 */ -}; - -__ALIGN_BEGIN static const uint8_t WebUSB_DescSet[] __ALIGN_END = -{ - #if PBIO_VERSION_LEVEL_HEX == 0xA - 21, /* bLength */ - #else - 20, /* bLength */ - #endif - 0x03, /* bDescriptorType = URL */ - 0x01, /* bScheme = https:// */ - - /* URL */ - #if PBIO_VERSION_LEVEL_HEX == 0xA - 'a', 'l', 'p', 'h', 'a', - #elif PBIO_VERSION_LEVEL_HEX == 0xB - 'b', 'e', 't', 'a', - #else - 'c', 'o', 'd', 'e', - #endif - '.', 'p', 'y', 'b', 'r', 'i', 'c', 'k', 's', '.', 'c', 'o', 'm' +typedef struct PBDRV_PACKED { + pbdrv_usb_conf_desc_t conf_desc; + pbdrv_usb_iface_desc_t iface_desc; + pbdrv_usb_ep_desc_t ep_out; + pbdrv_usb_ep_desc_t ep_in; +} pbdrv_usb_stm32_conf_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_stm32_conf); + +static pbdrv_usb_stm32_conf_union_t USBD_Pybricks_CfgDesc = { + .s = { + .conf_desc = { + .bLength = sizeof(pbdrv_usb_conf_desc_t), + .bDescriptorType = DESC_TYPE_CONFIGURATION, + .wTotalLength = sizeof(pbdrv_usb_stm32_conf_t), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONF_DESC_BM_ATTR_MUST_BE_SET, + .bMaxPower = 250, /* 500mA (number of 2mA units) */ + }, + .iface_desc = { + .bLength = sizeof(pbdrv_usb_iface_desc_t), + .bDescriptorType = DESC_TYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = PBIO_PYBRICKS_USB_DEVICE_CLASS, + .bInterfaceSubClass = PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, + .bInterfaceProtocol = PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, + .iInterface = 0, + }, + .ep_out = { + .bLength = sizeof(pbdrv_usb_ep_desc_t), + .bDescriptorType = DESC_TYPE_ENDPOINT, + .bEndpointAddress = USBD_PYBRICKS_OUT_EP, + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, + .wMaxPacketSize = USBD_PYBRICKS_MAX_PACKET_SIZE, + .bInterval = 0, /* ignore for Bulk transfer */ + }, + .ep_in = { + .bLength = sizeof(pbdrv_usb_ep_desc_t), + .bDescriptorType = DESC_TYPE_ENDPOINT, + .bEndpointAddress = USBD_PYBRICKS_IN_EP, + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, + .wMaxPacketSize = USBD_PYBRICKS_MAX_PACKET_SIZE, + .bInterval = 0, /* ignore for Bulk transfer */ + }, + } }; /** @@ -261,17 +248,17 @@ static USBD_StatusTypeDef USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, case USB_REQ_TYPE_VENDOR: switch (req->bRequest) { - case USBD_VENDOR_CODE_MS: + case PBDRV_USB_VENDOR_REQ_MS_20: (void)USBD_CtlSendData(pdev, - (uint8_t *)USBD_OSDescSet, - MIN(sizeof(USBD_OSDescSet), req->wLength)); + (uint8_t *)&pbdrv_usb_ms_20_desc_set, + MIN(sizeof(pbdrv_usb_ms_20_desc_set.s), req->wLength)); break; - case USBD_VENDOR_CODE_WEBUSB: - if ((req->wValue == USBD_WEBUSB_LANDING_PAGE_IDX) && (req->wIndex == 0x02)) { + case PBDRV_USB_VENDOR_REQ_WEBUSB: + if ((req->wValue == PBDRV_USB_WEBUSB_LANDING_PAGE_URL_IDX) && (req->wIndex == WEBUSB_REQ_GET_URL)) { (void)USBD_CtlSendData(pdev, - (uint8_t *)WebUSB_DescSet, - MIN(sizeof(WebUSB_DescSet), req->wLength)); + (uint8_t *)&pbdrv_usb_webusb_landing_page, + MIN(pbdrv_usb_webusb_landing_page.s.bLength, req->wLength)); } break; @@ -336,8 +323,8 @@ static USBD_StatusTypeDef USBD_Pybricks_Setup(USBD_HandleTypeDef *pdev, * @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; + *length = (uint16_t)sizeof(USBD_Pybricks_CfgDesc.s); + return (uint8_t *)&USBD_Pybricks_CfgDesc; } /** diff --git a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h index c9619cea2..042e89c84 100644 --- a/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h +++ b/lib/pbio/drv/usb/stm32_usbd/usbd_pybricks.h @@ -36,19 +36,6 @@ extern "C" { * @{ */ - -/** @defgroup USBD_Pybricks_Exported_Defines - * @{ - */ -enum { - USBD_VENDOR_CODE_WEBUSB, - USBD_VENDOR_CODE_MS -}; - -#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 */ #define USBD_PYBRICKS_OUT_EP 0x01U /* EP1 for data OUT */ @@ -122,9 +109,6 @@ typedef struct extern USBD_ClassTypeDef USBD_Pybricks_ClassDriver; -#define USBD_SIZ_MS_OS_DSCRPTR_SET (10 + 20 + 132) -extern const uint8_t USBD_OSDescSet[USBD_SIZ_MS_OS_DSCRPTR_SET]; - /** * @} */ diff --git a/lib/pbio/drv/usb/usb_ch9.h b/lib/pbio/drv/usb/usb_ch9.h index eda7e8ad5..dcc50f8de 100644 --- a/lib/pbio/drv/usb/usb_ch9.h +++ b/lib/pbio/drv/usb/usb_ch9.h @@ -80,6 +80,12 @@ PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_setup_packet) #define DESC_TYPE_DEVICE_QUALIFIER 6 #define DESC_TYPE_OTHER_SPEED_CONFIGURATION 7 #define DESC_TYPE_INTERFACE_POWER 8 +// Binary Object Store, originally from the Wireless USB / USB 3.0 spec +// These descriptors have been backported to USB 2 and are available to +// devices which indicate a bcdDevice of at least 2.1. +// They are used for WebUSB and Microsoft WinUSB driver installation. +#define DESC_TYPE_BOS 15 +#define DESC_TYPE_DEVICE_CAPABILITY 16 // Device descriptor typedef struct PBDRV_PACKED { @@ -130,6 +136,10 @@ typedef struct PBDRV_PACKED { uint8_t iInterface; } pbdrv_usb_iface_desc_t; +// This LangID is used for string descriptors +// English (United States) +#define PBDRV_USB_STRING_LANGID_EN_US 0x0409 + // Endpoint descriptor typedef struct PBDRV_PACKED { uint8_t bLength; @@ -141,10 +151,10 @@ typedef struct PBDRV_PACKED { } pbdrv_usb_ep_desc_t; // Endpoint types for bmAttributes -#define EP_TYPE_CTRL 0 -#define EP_TYPE_ISOC 1 -#define EP_TYPE_BULK 2 -#define EP_TYPE_INTR 3 +#define PBDRV_USB_EP_TYPE_CTRL 0 +#define PBDRV_USB_EP_TYPE_ISOC 1 +#define PBDRV_USB_EP_TYPE_BULK 2 +#define PBDRV_USB_EP_TYPE_INTR 3 // Device qualifier descriptor typedef struct PBDRV_PACKED { @@ -160,4 +170,125 @@ typedef struct PBDRV_PACKED { } pbdrv_usb_dev_qualifier_desc_t; PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_dev_qualifier_desc); +// Binary Object Store +typedef struct PBDRV_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} pbdrv_usb_bos_desc_t; + +typedef struct PBDRV_PACKED { + uint32_t u0; + uint16_t u1; + uint16_t u2; + uint8_t u3[8]; +} pbdrv_usb_uuid_t; + +// Platform-specific capabilities +#define USB_DEVICE_CAPABILITY_TYPE_PLATFORM 5 + +typedef struct PBDRV_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + pbdrv_usb_uuid_t uuid; +} pbdrv_usb_platform_caps_common_t; + +typedef struct PBDRV_PACKED { + pbdrv_usb_platform_caps_common_t hdr; + uint16_t bcdVersion; + uint8_t bVendorCode; + uint8_t iLandingPage; +} pbdrv_usb_webusb_capability_t; + +// {3408b638-09a9-47a0-8bfd-a0768815b665} +#define USB_PLATFORM_CAP_GUID_WEBUSB { \ + .u0 = 0x3408b638, \ + .u1 = 0x09a9, \ + .u2 = 0x47a0, \ + .u3 = {0x8b, 0xfd, 0xa0, 0x76, 0x88, 0x15, 0xb6, 0x65}, \ +} + +typedef struct PBDRV_PACKED { + pbdrv_usb_platform_caps_common_t hdr; + uint32_t dwWindowsVersion; + uint16_t wMSOSDescriptorSetTotalLength; + uint8_t bMS_VendorCode; + uint8_t bAltEnumCode; +} pbdrv_usb_microsoft_20_capability_t; + +// {D8DD60DF-4589-4CC7-9CD2-659D9E648A9F} +#define USB_PLATFORM_CAP_GUID_MS_20 { \ + .u0 = 0xD8DD60DF, \ + .u1 = 0x4589, \ + .u2 = 0x4CC7, \ + .u3 = {0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F}, \ +} + +// These are not technically part of USB Chapter 9, but +// we need them and they logically fit with the rest of the structs. + +#define WEBUSB_REQ_GET_URL 2 +#define WEBUSB_DESC_TYPE_URL 3 +#define WEBUSB_URL_SCHEME_HTTP 0 +#define WEBUSB_URL_SCHEME_HTTPS 1 + +typedef struct PBDRV_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + uint8_t url[]; +} pbdrv_usb_webusb_url_desc_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_webusb_url_desc); + +// Microsoft wIndex values +#define MS_OS_20_DESCRIPTOR_INDEX 7 +#define MS_OS_20_SET_ALT_ENUMERATION 8 + +// Microsoft descriptor types +#define MS_OS_20_SET_HEADER_DESCRIPTOR 0 +#define MS_OS_20_SUBSET_HEADER_CONFIGURATION 1 +#define MS_OS_20_SUBSET_HEADER_FUNCTION 2 +#define MS_OS_20_FEATURE_COMPATBLE_ID 3 +#define MS_OS_20_FEATURE_REG_PROPERTY 4 +#define MS_OS_20_FEATURE_MIN_RESUME_TIME 5 +#define MS_OS_20_FEATURE_MODEL_ID 6 +#define MS_OS_20_FEATURE_CCGP_DEVICE 7 +#define MS_OS_20_FEATURE_VENDOR_REVISION 8 + +// We only need (and thus define) the following descriptor types + +typedef struct PBDRV_PACKED { + uint16_t wLength; + uint16_t wDescriptorType; + uint32_t dwWindowsVersion; + uint16_t wTotalLength; +} pbdrv_usb_ms_20_desc_set_header_t; + +typedef struct PBDRV_PACKED { + uint16_t wLength; + uint16_t wDescriptorType; + uint8_t CompatibleID[8]; + uint8_t SubCompatibleID[8]; +} pbdrv_usb_ms_20_compatible_t; + +typedef struct PBDRV_PACKED { + uint16_t wLength; + uint16_t wDescriptorType; + uint16_t wPropertyDataType; +} pbdrv_usb_ms_20_reg_prop_hdr_t; + +#define MS_OS_20_REG_PROP_TYPE_REG_SZ 1 +#define MS_OS_20_REG_PROP_TYPE_REG_EXPAND_SZ 2 +#define MS_OS_20_REG_PROP_TYPE_REG_BINARY 3 +#define MS_OS_20_REG_PROP_TYPE_REG_DWORD_LE 4 +#define MS_OS_20_REG_PROP_TYPE_REG_DWORD_BE 5 +#define MS_OS_20_REG_PROP_TYPE_REG_LINK 6 +#define MS_OS_20_REG_PROP_TYPE_REG_MULTI_SZ 7 + +// This is the minimum Windows version needed to support these descriptors +#define MS_WINDOWS_VERSION_81 0x06030000 + #endif // _INTERNAL_PBDRV_USB_CH9_H_ diff --git a/lib/pbio/drv/usb/usb_common_desc.c b/lib/pbio/drv/usb/usb_common_desc.c new file mode 100644 index 000000000..bacbe97d2 --- /dev/null +++ b/lib/pbio/drv/usb/usb_common_desc.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 The Pybricks Authors + +// Descriptors which are shared across devices +// (i.e. WebUSB, Microsoft OS descriptors) + +#include + +#if PBDRV_CONFIG_USB + +#include "usb_common_desc.h" + +#if PBIO_VERSION_LEVEL_HEX == 0xA +#define PYBRICKS_CODE_URL "labs.pybricks.com" +#elif PBIO_VERSION_LEVEL_HEX == 0xB +#define PYBRICKS_CODE_URL "beta.pybricks.com" +#else +#define PYBRICKS_CODE_URL "code.pybricks.com" +#endif + +const pbdrv_usb_webusb_url_desc_union_t pbdrv_usb_webusb_landing_page = { + .s = { + .bLength = sizeof(pbdrv_usb_webusb_url_desc_t) + sizeof(PYBRICKS_CODE_URL) - 1, + .bDescriptorType = WEBUSB_DESC_TYPE_URL, + .bScheme = WEBUSB_URL_SCHEME_HTTPS, + .url = PYBRICKS_CODE_URL, + }, +}; + +#define MS_20_REGISTRY_DATA_EXTRA_SZ \ + 2 + sizeof(PBDRV_USB_MS_20_REG_PROPERTY_NAME) + \ + 2 + sizeof(PBDRV_USB_MS_20_REG_PROPERTY_VAL) + +const pbdrv_usb_ms_20_desc_set_union_t pbdrv_usb_ms_20_desc_set = { + .s = { + .desc_set_hdr = { + .wLength = sizeof(pbdrv_usb_ms_20_desc_set_header_t), + .wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR, + .dwWindowsVersion = MS_WINDOWS_VERSION_81, + .wTotalLength = sizeof(pbdrv_usb_ms_20_desc_set_t), + }, + .compatible = { + .wLength = sizeof(pbdrv_usb_ms_20_compatible_t), + .wDescriptorType = MS_OS_20_FEATURE_COMPATBLE_ID, + .CompatibleID = "WINUSB", + .SubCompatibleID = "", + }, + .reg_prop_hdr = { + .wLength = sizeof(pbdrv_usb_ms_20_reg_prop_hdr_t) + MS_20_REGISTRY_DATA_EXTRA_SZ, + .wDescriptorType = MS_OS_20_FEATURE_REG_PROPERTY, + .wPropertyDataType = MS_OS_20_REG_PROP_TYPE_REG_MULTI_SZ, + }, + .property_name_len = sizeof(PBDRV_USB_MS_20_REG_PROPERTY_NAME), + .device_interface_guids = PBDRV_USB_MS_20_REG_PROPERTY_NAME, + .property_val_len = sizeof(PBDRV_USB_MS_20_REG_PROPERTY_VAL), + .device_interface_guid_val = PBDRV_USB_MS_20_REG_PROPERTY_VAL, + } +}; + +const pbdrv_usb_bos_desc_set_union_t pbdrv_usb_bos_desc_set = { + .s = { + .bos = { + .bLength = sizeof(pbdrv_usb_bos_desc_t), + .bDescriptorType = DESC_TYPE_BOS, + .wTotalLength = sizeof(pbdrv_usb_bos_desc_set_t), + .bNumDeviceCaps = 2, + }, + .webusb = { + .hdr = { + .bLength = sizeof(pbdrv_usb_webusb_capability_t), + .bDescriptorType = DESC_TYPE_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DEVICE_CAPABILITY_TYPE_PLATFORM, + .bReserved = 0, + .uuid = USB_PLATFORM_CAP_GUID_WEBUSB, + }, + .bcdVersion = 0x0100, + .bVendorCode = PBDRV_USB_VENDOR_REQ_WEBUSB, + .iLandingPage = PBDRV_USB_WEBUSB_LANDING_PAGE_URL_IDX, + }, + .ms_20 = { + .hdr = { + .bLength = sizeof(pbdrv_usb_microsoft_20_capability_t), + .bDescriptorType = DESC_TYPE_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DEVICE_CAPABILITY_TYPE_PLATFORM, + .bReserved = 0, + .uuid = USB_PLATFORM_CAP_GUID_MS_20, + }, + .dwWindowsVersion = MS_WINDOWS_VERSION_81, + .wMSOSDescriptorSetTotalLength = sizeof(pbdrv_usb_ms_20_desc_set_t), + .bMS_VendorCode = PBDRV_USB_VENDOR_REQ_MS_20, + .bAltEnumCode = 0, + } + } +}; + +const pbdrv_usb_langid_union_t pbdrv_usb_str_desc_langid = { + .s = { + .bLength = 4, + .bDescriptorType = DESC_TYPE_STRING, + .langID = {PBDRV_USB_STRING_LANGID_EN_US}, + } +}; + +const pbdrv_usb_str_mfg_union_t pbdrv_usb_str_desc_mfg = { + .s = { + .bLength = sizeof(pbdrv_usb_str_mfg_t), + .bDescriptorType = DESC_TYPE_STRING, + .str = PBDRV_CONFIG_USB_MFG_STR, + } +}; + +const pbdrv_usb_str_prod_union_t pbdrv_usb_str_desc_prod = { + .s = { + .bLength = sizeof(pbdrv_usb_str_prod_t), + .bDescriptorType = DESC_TYPE_STRING, + .str = PBDRV_CONFIG_USB_PROD_STR, + } +}; + +#endif // PBDRV_CONFIG_USB diff --git a/lib/pbio/drv/usb/usb_common_desc.h b/lib/pbio/drv/usb/usb_common_desc.h new file mode 100644 index 000000000..ff77c17e2 --- /dev/null +++ b/lib/pbio/drv/usb/usb_common_desc.h @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2025 The Pybricks Authors + +// Descriptors which are shared across devices +// (i.e. WebUSB, Microsoft OS descriptors) + +#ifndef _INTERNAL_PBDRV_USB_COMMON_DESC_H_ +#define _INTERNAL_PBDRV_USB_COMMON_DESC_H_ + +#include + +#include +#include "pbdrvconfig.h" + +#include "usb_ch9.h" + +/** + * USB vendor requests which will be used to retrieve WebUSB and Microsoft descriptors + */ +enum { + PBDRV_USB_VENDOR_REQ_WEBUSB, + PBDRV_USB_VENDOR_REQ_MS_20, +}; + +// The descriptor index for the WebUSB landing page +#define PBDRV_USB_WEBUSB_LANDING_PAGE_URL_IDX 1 + +#define PBDRV_USB_MS_20_REG_PROPERTY_NAME u"DeviceInterfaceGUIDs" +// This UUID has been generated by Pybricks and identifies specifically +// our devices which use the Pybricks protocol. Windows APIs can match on this. +// An extra null terminator is required to terminate a REG_MULTI_SZ +#define PBDRV_USB_MS_20_REG_PROPERTY_VAL u"{A5C44A4C-53D4-4389-9821-AE95051908A1}\x00" + +// Complete set of Microsoft USB descriptors +typedef struct PBDRV_PACKED { + pbdrv_usb_ms_20_desc_set_header_t desc_set_hdr; + pbdrv_usb_ms_20_compatible_t compatible; + pbdrv_usb_ms_20_reg_prop_hdr_t reg_prop_hdr; + uint16_t property_name_len; + uint16_t device_interface_guids[PBIO_ARRAY_SIZE(PBDRV_USB_MS_20_REG_PROPERTY_NAME)]; + uint16_t property_val_len; + uint16_t device_interface_guid_val[PBIO_ARRAY_SIZE(PBDRV_USB_MS_20_REG_PROPERTY_VAL)]; +} pbdrv_usb_ms_20_desc_set_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_ms_20_desc_set); + +extern const pbdrv_usb_ms_20_desc_set_union_t pbdrv_usb_ms_20_desc_set; + +// WebUSB descriptor +extern const pbdrv_usb_webusb_url_desc_union_t pbdrv_usb_webusb_landing_page; + +// Complete set of USB BOS descriptors +typedef struct PBDRV_PACKED { + pbdrv_usb_bos_desc_t bos; + // WebUSB must occur before Microsoft OS descriptors + pbdrv_usb_webusb_capability_t webusb; + pbdrv_usb_microsoft_20_capability_t ms_20; +} pbdrv_usb_bos_desc_set_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_bos_desc_set); + +extern const pbdrv_usb_bos_desc_set_union_t pbdrv_usb_bos_desc_set; + +// (Human-readable) string descriptors +typedef struct PBDRV_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t langID[1]; +} pbdrv_usb_langid_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_langid); + +extern const pbdrv_usb_langid_union_t pbdrv_usb_str_desc_langid; + +typedef struct PBDRV_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t str[PBIO_ARRAY_SIZE(PBDRV_CONFIG_USB_MFG_STR) - 1]; +} pbdrv_usb_str_mfg_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_str_mfg); + +extern const pbdrv_usb_str_mfg_union_t pbdrv_usb_str_desc_mfg; + +typedef struct PBDRV_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t str[PBIO_ARRAY_SIZE(PBDRV_CONFIG_USB_PROD_STR) - 1]; +} pbdrv_usb_str_prod_t; +PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_str_prod); + +extern const pbdrv_usb_str_prod_union_t pbdrv_usb_str_desc_prod; + +#endif // _INTERNAL_PBDRV_USB_COMMON_DESC_H_ diff --git a/lib/pbio/drv/usb/usb_ev3.c b/lib/pbio/drv/usb/usb_ev3.c index d9789ce3d..27d7926ff 100644 --- a/lib/pbio/drv/usb/usb_ev3.c +++ b/lib/pbio/drv/usb/usb_ev3.c @@ -18,7 +18,6 @@ #include #include -#include "pbdrvconfig.h" #include #include @@ -35,6 +34,7 @@ #include #include "usb_ch9.h" +#include "usb_common_desc.h" // Maximum packet sizes for the USB pipes #define EP0_BUF_SZ 64 @@ -122,7 +122,7 @@ static const pbdrv_usb_ev3_conf_1_union_t configuration_1_desc_hs = { .bLength = sizeof(pbdrv_usb_ep_desc_t), .bDescriptorType = DESC_TYPE_ENDPOINT, .bEndpointAddress = 0x01, - .bmAttributes = EP_TYPE_BULK, + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, .wMaxPacketSize = PYBRICKS_EP_PKT_SZ_HS, .bInterval = 0, }, @@ -130,7 +130,7 @@ static const pbdrv_usb_ev3_conf_1_union_t configuration_1_desc_hs = { .bLength = sizeof(pbdrv_usb_ep_desc_t), .bDescriptorType = DESC_TYPE_ENDPOINT, .bEndpointAddress = 0x81, - .bmAttributes = EP_TYPE_BULK, + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, .wMaxPacketSize = PYBRICKS_EP_PKT_SZ_HS, .bInterval = 0, }, @@ -164,7 +164,7 @@ static const pbdrv_usb_ev3_conf_1_union_t configuration_1_desc_fs = { .bLength = sizeof(pbdrv_usb_ep_desc_t), .bDescriptorType = DESC_TYPE_ENDPOINT, .bEndpointAddress = 0x01, - .bmAttributes = EP_TYPE_BULK, + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, .wMaxPacketSize = PYBRICKS_EP_PKT_SZ_FS, .bInterval = 0, }, @@ -172,29 +172,15 @@ static const pbdrv_usb_ev3_conf_1_union_t configuration_1_desc_fs = { .bLength = sizeof(pbdrv_usb_ep_desc_t), .bDescriptorType = DESC_TYPE_ENDPOINT, .bEndpointAddress = 0x81, - .bmAttributes = EP_TYPE_BULK, + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, .wMaxPacketSize = PYBRICKS_EP_PKT_SZ_FS, .bInterval = 0, }, } }; -typedef struct PBDRV_PACKED { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t langID[1]; -} pbdrv_usb_langid_t; -PBDRV_USB_TYPE_PUNNING_HELPER(pbdrv_usb_langid); - -pbdrv_usb_langid_union_t usb_str_desc_langid = { - .s = { - .bLength = 4, - .bDescriptorType = DESC_TYPE_STRING, - .langID = {0x0409}, // English (United States) - } -}; - -// We generate string descriptors at runtime, so this dynamic buffer is needed +// We generate a serial number string descriptors at runtime +// so this dynamic buffer is needed #define STRING_DESC_MAX_SZ 64 static union { uint8_t b[STRING_DESC_MAX_SZ]; @@ -455,36 +441,18 @@ static bool usb_get_descriptor(uint16_t wValue) { case DESC_TYPE_STRING: switch (desc_idx) { case STRING_DESC_LANGID: - pbdrv_usb_setup_data_to_send = usb_str_desc_langid.u; - pbdrv_usb_setup_data_to_send_sz = sizeof(usb_str_desc_langid); + pbdrv_usb_setup_data_to_send = pbdrv_usb_str_desc_langid.u; + pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_str_desc_langid.s); return true; case STRING_DESC_MFG: - usb_string_desc_buffer.b[1] = DESC_TYPE_STRING; - i = 0; - while (PBDRV_CONFIG_USB_MFG_STR[i]) { - usb_string_desc_buffer.b[2 + 2 * i] = PBDRV_CONFIG_USB_MFG_STR[i]; - usb_string_desc_buffer.b[2 + 2 * i + 1] = 0; - i++; - } - usb_string_desc_buffer.b[0] = 2 * i + 2; - - pbdrv_usb_setup_data_to_send = usb_string_desc_buffer.u; - pbdrv_usb_setup_data_to_send_sz = usb_string_desc_buffer.b[0]; + pbdrv_usb_setup_data_to_send = pbdrv_usb_str_desc_mfg.u; + pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_str_desc_mfg.s); return true; case STRING_DESC_PRODUCT: - usb_string_desc_buffer.b[1] = DESC_TYPE_STRING; - i = 0; - while (PBDRV_CONFIG_USB_PROD_STR[i]) { - usb_string_desc_buffer.b[2 + 2 * i] = PBDRV_CONFIG_USB_PROD_STR[i]; - usb_string_desc_buffer.b[2 + 2 * i + 1] = 0; - i++; - } - usb_string_desc_buffer.b[0] = 2 * i + 2; - - pbdrv_usb_setup_data_to_send = usb_string_desc_buffer.u; - pbdrv_usb_setup_data_to_send_sz = usb_string_desc_buffer.b[0]; + pbdrv_usb_setup_data_to_send = pbdrv_usb_str_desc_prod.u; + pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_str_desc_prod.s); return true; case STRING_DESC_SERIAL: @@ -502,6 +470,11 @@ static bool usb_get_descriptor(uint16_t wValue) { return true; } break; + + case DESC_TYPE_BOS: + pbdrv_usb_setup_data_to_send = pbdrv_usb_bos_desc_set.u; + pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_bos_desc_set.s); + return true; } return false; @@ -563,121 +536,142 @@ static void usb_device_intr(void) { setup_pkt.u[1] = HWREG(USB0_BASE + USB_O_FIFO0); HWREGH(USB0_BASE + USB_O_CSRL0) = USB_CSRL0_RXRDYC; - if ((setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) == BM_REQ_TYPE_STANDARD) { - switch (setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) { - case BM_REQ_RECIP_DEV: - switch (setup_pkt.s.bRequest) { - case SET_ADDRESS: - pbdrv_usb_addr = setup_pkt.s.wValue; - pbdrv_usb_addr_needs_setting = true; - handled = true; - break; + switch (setup_pkt.s.bmRequestType & BM_REQ_TYPE_MASK) { + case BM_REQ_TYPE_STANDARD: + switch (setup_pkt.s.bmRequestType & BM_REQ_RECIP_MASK) { + case BM_REQ_RECIP_DEV: + switch (setup_pkt.s.bRequest) { + case SET_ADDRESS: + pbdrv_usb_addr = setup_pkt.s.wValue; + pbdrv_usb_addr_needs_setting = true; + handled = true; + break; - case SET_CONFIGURATION: - if (setup_pkt.s.wValue <= 1) { - pbdrv_usb_config = setup_pkt.s.wValue; + case SET_CONFIGURATION: + if (setup_pkt.s.wValue <= 1) { + pbdrv_usb_config = setup_pkt.s.wValue; - if (pbdrv_usb_config == 1) { - // configuring + if (pbdrv_usb_config == 1) { + // configuring - // Reset data toggle, clear stall, flush fifo - HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH; - HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH; - } else { - // deconfiguring + // Reset data toggle, clear stall, flush fifo + HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_CLRDT | USB_TXCSRL1_FLUSH; + HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_CLRDT | USB_RXCSRL1_FLUSH; + } else { + // deconfiguring - // Set stall condition - HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL; - HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL; + // Set stall condition + HWREGB(USB0_BASE + USB_O_TXCSRL1) = USB_TXCSRL1_STALL; + HWREGB(USB0_BASE + USB_O_RXCSRL1) = USB_RXCSRL1_STALL; + } + handled = true; } - handled = true; - } - break; - - case GET_CONFIGURATION: - pbdrv_usb_setup_misc_tx_byte = pbdrv_usb_config; - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 1; - handled = true; - break; - - case GET_STATUS: - pbdrv_usb_setup_misc_tx_byte = 1; // self-powered - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 2; - handled = true; - break; - - case GET_DESCRIPTOR: - if (usb_get_descriptor(setup_pkt.s.wValue)) { - handled = true; - } - break; - } - break; + break; - case BM_REQ_RECIP_IF: - if (setup_pkt.s.wIndex == 0) { - switch (setup_pkt.s.bRequest) { - case GET_INTERFACE: - pbdrv_usb_setup_misc_tx_byte = 0; + case GET_CONFIGURATION: + pbdrv_usb_setup_misc_tx_byte = pbdrv_usb_config; pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; pbdrv_usb_setup_data_to_send_sz = 1; handled = true; break; case GET_STATUS: - pbdrv_usb_setup_misc_tx_byte = 0; + pbdrv_usb_setup_misc_tx_byte = 1; // self-powered pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; pbdrv_usb_setup_data_to_send_sz = 2; handled = true; break; - } - } - break; - - case BM_REQ_RECIP_EP: - switch (setup_pkt.s.bRequest) { - case GET_STATUS: - if (setup_pkt.s.wIndex == 1) { - pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL); - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 2; - handled = true; - } else if (setup_pkt.s.wIndex == 0x81) { - pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL); - pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; - pbdrv_usb_setup_data_to_send_sz = 2; - handled = true; - } - break; - case CLEAR_FEATURE: - if (setup_pkt.s.wValue == 0) { - if (setup_pkt.s.wIndex == 1) { - HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL; - handled = true; - } else if (setup_pkt.s.wIndex == 0x81) { - HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL; + case GET_DESCRIPTOR: + if (usb_get_descriptor(setup_pkt.s.wValue)) { handled = true; } + break; + } + break; + + case BM_REQ_RECIP_IF: + if (setup_pkt.s.wIndex == 0) { + switch (setup_pkt.s.bRequest) { + case GET_INTERFACE: + pbdrv_usb_setup_misc_tx_byte = 0; + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 1; + handled = true; + break; + + case GET_STATUS: + pbdrv_usb_setup_misc_tx_byte = 0; + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 2; + handled = true; + break; } - break; + } + break; - case SET_FEATURE: - if (setup_pkt.s.wValue == 0) { + case BM_REQ_RECIP_EP: + switch (setup_pkt.s.bRequest) { + case GET_STATUS: if (setup_pkt.s.wIndex == 1) { - HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL; + pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_RXCSRL1) & USB_RXCSRL1_STALL); + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 2; handled = true; } else if (setup_pkt.s.wIndex == 0x81) { - HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL; + pbdrv_usb_setup_misc_tx_byte = !!(HWREGB(USB0_BASE + USB_O_TXCSRL1) & USB_TXCSRL1_STALL); + pbdrv_usb_setup_data_to_send = &pbdrv_usb_setup_misc_tx_byte; + pbdrv_usb_setup_data_to_send_sz = 2; handled = true; } - } - break; - } - break; - } + break; + + case CLEAR_FEATURE: + if (setup_pkt.s.wValue == 0) { + if (setup_pkt.s.wIndex == 1) { + HWREGB(USB0_BASE + USB_O_RXCSRL1) &= ~USB_RXCSRL1_STALL; + handled = true; + } else if (setup_pkt.s.wIndex == 0x81) { + HWREGB(USB0_BASE + USB_O_TXCSRL1) &= ~USB_TXCSRL1_STALL; + handled = true; + } + } + break; + + case SET_FEATURE: + if (setup_pkt.s.wValue == 0) { + if (setup_pkt.s.wIndex == 1) { + HWREGB(USB0_BASE + USB_O_RXCSRL1) |= USB_RXCSRL1_STALL; + handled = true; + } else if (setup_pkt.s.wIndex == 0x81) { + HWREGB(USB0_BASE + USB_O_TXCSRL1) |= USB_TXCSRL1_STALL; + handled = true; + } + } + break; + } + break; + } + break; + + case BM_REQ_TYPE_VENDOR: + switch (setup_pkt.s.bRequest) { + case PBDRV_USB_VENDOR_REQ_WEBUSB: + if (setup_pkt.s.wIndex == WEBUSB_REQ_GET_URL && setup_pkt.s.wValue == PBDRV_USB_WEBUSB_LANDING_PAGE_URL_IDX) { + pbdrv_usb_setup_data_to_send = pbdrv_usb_webusb_landing_page.u; + pbdrv_usb_setup_data_to_send_sz = pbdrv_usb_webusb_landing_page.s.bLength; + handled = true; + } + break; + + case PBDRV_USB_VENDOR_REQ_MS_20: + if (setup_pkt.s.wIndex == MS_OS_20_DESCRIPTOR_INDEX) { + pbdrv_usb_setup_data_to_send = pbdrv_usb_ms_20_desc_set.u; + pbdrv_usb_setup_data_to_send_sz = sizeof(pbdrv_usb_ms_20_desc_set.s); + handled = true; + } + break; + } } if (!handled) { diff --git a/lib/pbio/drv/usb/usb_nxt.c b/lib/pbio/drv/usb/usb_nxt.c index 5c973fbc0..bc81e5b0e 100644 --- a/lib/pbio/drv/usb/usb_nxt.c +++ b/lib/pbio/drv/usb/usb_nxt.c @@ -27,6 +27,11 @@ #include "nxos/drivers/aic.h" #include "nxos/util.h" +#include + +#include "usb_ch9.h" +#include "usb_common_desc.h" + /* The USB controller supports up to 4 endpoints. */ #define PBDRV_USB_NXT_N_ENDPOINTS 4 @@ -83,6 +88,15 @@ #define USB_WVALUE_INDEX 0xFF +/** + * Indices for string descriptors + */ +enum { + STRING_DESC_LANGID, + STRING_DESC_MFG, + STRING_DESC_PRODUCT, +}; + /* The following definitions are 'raw' USB setup packets. They are all * standard responses to various setup requests by the USB host. These * packets are all constant, and mostly boilerplate. Don't be too @@ -96,178 +110,78 @@ * don't have a vendor ID to use. Therefore, we are currently * piggybacking on Lego's device space, using an unused product ID. */ -static const uint8_t pbdrv_usb_nxt_device_descriptor[] = { - 18, USB_DESC_TYPE_DEVICE, /* Packet size and type. */ - 0x10, 0x02, /* This packet is USB 2.1 (needed for BOS descriptors). */ - PBIO_PYBRICKS_USB_DEVICE_CLASS, /* Class code. */ - PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, /* Sub class code. */ - PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, /* Device protocol. */ - MAX_EP0_SIZE, /* Maximum packet size for EP0 (control endpoint). */ - 0x94, 0x06, /* Vendor ID : LEGO */ - 0x02, 0x00, /* Product ID : NXT */ - 0x00, 0x02, /* Product revision: 2.0.0. */ - 1, /* Index of the vendor string. */ - 2, /* Index of the product string. */ - 0, /* Index of the serial number (none for us). */ - 1, /* The number of possible configurations. */ -}; - -static const uint8_t pbdrv_usb_nxt_dev_qualifier_desc[] = { - 10, USB_DESC_TYPE_DEVICE_QUALIFIER, /* Packet size and type. */ - 0x10, 0x02, /* This packet is USB 2.1. */ - PBIO_PYBRICKS_USB_DEVICE_CLASS, /* Class code */ - PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, /* Sub class code */ - PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, /* Device protocol */ - MAX_EP0_SIZE, /* Maximum packet size for EP0. */ - 1, /* The number of possible configurations. */ - 0, /* Reserved for future use, must be zero. */ -}; - -// These enumerations are specific to the configuration of this device. - -enum { - PBDRV_USB_NXT_VENDOR_CODE_WEBUSB, - PBDRV_USB_NXT_VENDOR_CODE_MS, -}; - -// NB: Chromium seems quite particular about the order of these descriptors. -// The WebUSB descriptor must come first and the MS OS 2.0 descriptor be last. -static const uint8_t pbdrv_usb_nxt_bos_desc[] = { - 5, USB_DESC_TYPE_BOS, /* Descriptor length and type. */ - 0x39, 0x00, /* Total length of the descriptor = 57. */ - 2, /* Number of device capabilities. */ - - 24, /* bLength */ - USB_DEVICE_CAPABILITY_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, - - 0x00, 0x01, /* bcdVersion = 1.00 */ - PBDRV_USB_NXT_VENDOR_CODE_WEBUSB, /* bVendorCode */ - 1, /* iLandingPage */ - - 28, /* bLength */ - USB_DEVICE_CAPABILITY_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 */ - 0xA2, 0x00, /* wMSOSDescriptorSetTotalLength = 162 */ - PBDRV_USB_NXT_VENDOR_CODE_MS, /* bMS_VendorCode */ - 0x00, /* bAltEnumCode = Does not support alternate enumeration */ +static const pbdrv_usb_dev_desc_t pbdrv_usb_nxt_device_descriptor = { + .bLength = sizeof(pbdrv_usb_dev_desc_t), + .bDescriptorType = DESC_TYPE_DEVICE, + .bcdUSB = 0x0210, /* This packet is USB 2.1 (needed for BOS descriptors). */ + .bDeviceClass = PBIO_PYBRICKS_USB_DEVICE_CLASS, + .bDeviceSubClass = PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, + .bDeviceProtocol = PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, + .bMaxPacketSize0 = MAX_EP0_SIZE, + .idVendor = 0x0694, /* Vendor ID : LEGO */ + .idProduct = 0x0002, /* Product ID : NXT */ + .bcdDevice = 0x0200, /* Product revision: 2.0.0. */ + .iManufacturer = STRING_DESC_MFG, + .iProduct = STRING_DESC_PRODUCT, + .iSerialNumber = 0, // TODO: implement a serial number + .bNumConfigurations = 1, }; -static const uint8_t pbdrv_usb_nxt_full_config[] = { - 0x09, USB_DESC_TYPE_CONFIG, /* Descriptor size and type. */ - 0x20, 0x00, /* Total length of the configuration, interface - * description included. - */ - 1, /* The number of interfaces declared by this configuration. */ - 1, /* The ID for this configuration. */ - 0, /* Index of the configuration description string (none). */ - - /* Configuration attributes bitmap. Bit 7 (MSB) must be 1, bit 6 is - * 1 because the NXT is self-powered, bit 5 is 0 because the NXT - * doesn't support remote wakeup, and bits 0-4 are 0 (reserved). - */ - 0xC0, - 0, /* Device power consumption, for non self-powered devices. */ - - /* - * This is the descriptor for the interface associated with the - * configuration. - */ - 0x09, USB_DESC_TYPE_INT, /* Descriptor size and type. */ - 0x00, /* Interface index. */ - 0x00, /* ID for this interface configuration. */ - 0x02, /* The number of endpoints defined by this interface - * (excluding EP0). - */ - PBIO_PYBRICKS_USB_DEVICE_CLASS, /* Interface class ("Vendor specific"). */ - PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, /* Interface subclass (see above). */ - PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, /* Interface protocol (see above). */ - 0x00, /* Index of the string descriptor for this interface (none). */ - +typedef struct PBDRV_PACKED { + pbdrv_usb_conf_desc_t conf_desc; + pbdrv_usb_iface_desc_t iface_desc; + pbdrv_usb_ep_desc_t ep_out; + pbdrv_usb_ep_desc_t ep_in; +} pbdrv_usb_nxt_conf_t; + +static const pbdrv_usb_nxt_conf_t pbdrv_usb_nxt_full_config = { + .conf_desc = { + .bLength = sizeof(pbdrv_usb_conf_desc_t), + .bDescriptorType = DESC_TYPE_CONFIGURATION, + .wTotalLength = sizeof(pbdrv_usb_nxt_conf_t), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + /* Configuration attributes bitmap. Bit 7 (MSB) must be 1, bit 6 is + * 1 because the NXT is self-powered, bit 5 is 0 because the NXT + * doesn't support remote wakeup, and bits 0-4 are 0 (reserved). + */ + .bmAttributes = USB_CONF_DESC_BM_ATTR_MUST_BE_SET | USB_CONF_DESC_BM_ATTR_SELF_POWERED, + .bMaxPower = 0, + }, + .iface_desc = { + .bLength = sizeof(pbdrv_usb_iface_desc_t), + .bDescriptorType = DESC_TYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = PBIO_PYBRICKS_USB_DEVICE_CLASS, + .bInterfaceSubClass = PBIO_PYBRICKS_USB_DEVICE_SUBCLASS, + .bInterfaceProtocol = PBIO_PYBRICKS_USB_DEVICE_PROTOCOL, + .iInterface = 0, + }, /* * Descriptor for EP1. */ - 7, USB_DESC_TYPE_ENDPT, /* Descriptor length and type. */ - 0x1, /* Endpoint number. MSB is zero, meaning this is an OUT EP. */ - 0x2, /* Endpoint type (bulk). */ - MAX_RCV_SIZE, 0x00, /* Maximum packet size (64). */ - 0, /* EP maximum NAK rate (device never NAKs). */ - + .ep_out = { + .bLength = sizeof(pbdrv_usb_ep_desc_t), + .bDescriptorType = DESC_TYPE_ENDPOINT, + .bEndpointAddress = 0x01, /* Endpoint number. MSB is zero, meaning this is an OUT EP. */ + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, + .wMaxPacketSize = MAX_RCV_SIZE, + .bInterval = 0, + }, /* * Descriptor for EP2. */ - 7, USB_DESC_TYPE_ENDPT, /* Descriptor length and type. */ - 0x82, /* Endpoint number. MSB is one, meaning this is an IN EP. */ - 0x2, /* Endpoint type (bulk). */ - MAX_RCV_SIZE, 0x00, /* Maximum packet size (64). */ - 0, /* EP maximum NAK rate (device never NAKs). */ -}; - -static const uint8_t pbdrv_usb_nxt_string_desc[] = { - 4, USB_DESC_TYPE_STR, /* Descriptor length and type. */ - 0x09, 0x04, /* Supported language ID (US English). */ -}; - -static const uint8_t pbdrv_usb_lego_str[] = { - 10, USB_DESC_TYPE_STR, - 'L', 0, - 'E', 0, - 'G', 0, - 'O', 0, -}; - -static const uint8_t pbdrv_usb_nxt_str[] = { - 30, USB_DESC_TYPE_STR, - 'N', 0, - 'X', 0, - 'T', 0, - ' ', 0, - '+', 0, - ' ', 0, - 'P', 0, - 'y', 0, - 'b', 0, - 'r', 0, - 'i', 0, - 'c', 0, - 'k', 0, - 's', 0, -}; - -/* Internal lookup table mapping string descriptors to their indices - * in the USB string descriptor table. - */ -static const uint8_t *pbdrv_usb_nxt_strings[] = { - pbdrv_usb_lego_str, - pbdrv_usb_nxt_str, + .ep_in = { + .bLength = sizeof(pbdrv_usb_ep_desc_t), + .bDescriptorType = DESC_TYPE_ENDPOINT, + .bEndpointAddress = 0x82, /* Endpoint number. MSB is one, meaning this is an IN EP. */ + .bmAttributes = PBDRV_USB_EP_TYPE_BULK, + .wMaxPacketSize = MAX_SND_SIZE, + .bInterval = 0, + }, }; typedef enum { @@ -349,7 +263,8 @@ static void pbdrv_usb_nxt_csr_set_flag(uint8_t endpoint, uint32_t flags) { * single USB packet, the data is split and scheduled to be sent in * several packets. */ -static void pbdrv_usb_nxt_write_data(int endpoint, const uint8_t *ptr, uint32_t length) { +static void pbdrv_usb_nxt_write_data(int endpoint, const void *ptr_, uint32_t length) { + const uint8_t *ptr = ptr_; uint32_t packet_size; int tx; @@ -376,7 +291,14 @@ static void pbdrv_usb_nxt_write_data(int endpoint, const uint8_t *ptr, uint32_t pbdrv_usb_nxt_state.tx_data[tx] = (uint8_t *)(ptr + packet_size); pbdrv_usb_nxt_state.tx_len[tx] = length; } else { - pbdrv_usb_nxt_state.tx_data[tx] = NULL; + if (length == packet_size && endpoint == 0) { + // If we are sending data to the control pipe, we must terminate the data + // with a ZLP. In order to do so, we set the data pointer to non-NULL + // but the length to 0. We do not want to send ZLPs on the Pybricks bulk pipe. + pbdrv_usb_nxt_state.tx_data[tx] = (uint8_t *)(ptr); + } else { + pbdrv_usb_nxt_state.tx_data[tx] = NULL; + } pbdrv_usb_nxt_state.tx_len[tx] = 0; } @@ -453,108 +375,6 @@ static void pbdrv_usb_nxt_send_null(void) { pbdrv_usb_nxt_write_data(0, NULL, 0); } -static const uint8_t pbdrv_usb_desc_set_ms_os[] = { - 0x0A, 0x00, /* wLength = 10 */ - 0x00, 0x00, /* wDescriptorType = MS_OS_20_SET_HEADER_DESCRIPTOR */ - 0x00, 0x00, 0x03, 0x06, /* dwWindowsVersion = 0x06030000 for Windows 8.1 Build */ - 0xA2, 0x00, /* wTotalLength = 162 */ - - 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', -}; - -static const uint8_t pbdrv_usb_desc_set_webusb[] = { - 20, /* bLength */ - 0x03, /* bDescriptorType = URL */ - 0x01, /* bScheme = https:// */ - - /* URL */ - #if PBIO_VERSION_LEVEL_HEX == 0xA - 'a', 'l', 'p', 'h', 'a', - #elif PBIO_VERSION_LEVEL_HEX == 0xB - 'b', 'e', 't', 'a', - #else - 'c', 'o', 'd', 'e', - #endif - '.', 'p', 'y', 'b', 'r', 'i', 'c', 'k', 's', '.', 'c', 'o', 'm', -}; - typedef struct { uint8_t request_attrs; /* Request characteristics. */ uint8_t request; /* Request type. */ @@ -586,7 +406,7 @@ static void pbdrv_usb_handle_std_request(pbdrv_usb_nxt_setup_packet_t *packet) { response = 0; } - pbdrv_usb_nxt_write_data(0, (uint8_t *)&response, 2); + pbdrv_usb_nxt_write_data(0, &response, 2); } break; @@ -625,43 +445,46 @@ static void pbdrv_usb_handle_std_request(pbdrv_usb_nxt_setup_packet_t *packet) { index = (packet->value & USB_WVALUE_INDEX); switch ((packet->value & USB_WVALUE_TYPE) >> 8) { case USB_DESC_TYPE_DEVICE: /* Device descriptor */ - size = pbdrv_usb_nxt_device_descriptor[0]; - pbdrv_usb_nxt_write_data(0, pbdrv_usb_nxt_device_descriptor, + size = sizeof(pbdrv_usb_nxt_device_descriptor); + pbdrv_usb_nxt_write_data(0, &pbdrv_usb_nxt_device_descriptor, MIN(size, packet->length)); break; case USB_DESC_TYPE_CONFIG: /* Configuration descriptor */ - pbdrv_usb_nxt_write_data(0, pbdrv_usb_nxt_full_config, - MIN(pbdrv_usb_nxt_full_config[2], packet->length)); - - /* TODO: Why? This is not specified in the USB specs. */ - if (pbdrv_usb_nxt_full_config[2] < packet->length) { - pbdrv_usb_nxt_send_null(); - } + size = sizeof(pbdrv_usb_nxt_full_config); + pbdrv_usb_nxt_write_data(0, &pbdrv_usb_nxt_full_config, + MIN(size, packet->length)); break; case USB_DESC_TYPE_STR: /* String or language info. */ - if ((packet->value & USB_WVALUE_INDEX) == 0) { - pbdrv_usb_nxt_write_data(0, pbdrv_usb_nxt_string_desc, - MIN(pbdrv_usb_nxt_string_desc[0], packet->length)); - } else { - /* The host wants a specific string. */ - /* TODO: This should check if the requested string exists. */ - pbdrv_usb_nxt_write_data(0, pbdrv_usb_nxt_strings[index - 1], - MIN(pbdrv_usb_nxt_strings[index - 1][0], - packet->length)); + { + const void *desc = 0; + switch (index) { + case STRING_DESC_LANGID: + desc = &pbdrv_usb_str_desc_langid; + size = sizeof(pbdrv_usb_str_desc_langid.s); + break; + case STRING_DESC_MFG: + desc = &pbdrv_usb_str_desc_mfg; + size = sizeof(pbdrv_usb_str_desc_mfg.s); + break; + case STRING_DESC_PRODUCT: + desc = &pbdrv_usb_str_desc_prod; + size = sizeof(pbdrv_usb_str_desc_prod.s); + break; } - break; - case USB_DESC_TYPE_DEVICE_QUALIFIER: /* Device qualifier descriptor. */ - size = pbdrv_usb_nxt_dev_qualifier_desc[0]; - pbdrv_usb_nxt_write_data(0, pbdrv_usb_nxt_dev_qualifier_desc, - MIN(size, packet->length)); - break; + if (desc) { + pbdrv_usb_nxt_write_data(0, desc, MIN(size, packet->length)); + } else { + pbdrv_usb_nxt_send_stall(0); + } + } + break; case USB_DESC_TYPE_BOS: /* BOS descriptor */ - size = pbdrv_usb_nxt_bos_desc[2]; - pbdrv_usb_nxt_write_data(0, pbdrv_usb_nxt_bos_desc, MIN(size, packet->length)); + size = sizeof(pbdrv_usb_bos_desc_set.s); + pbdrv_usb_nxt_write_data(0, &pbdrv_usb_bos_desc_set, MIN(size, packet->length)); break; default: /* Unknown descriptor, tell the host by stalling. */ @@ -730,19 +553,19 @@ static void pbdrv_usb_nxt_handle_class_request(pbdrv_usb_nxt_setup_packet_t *pac switch (packet->value) { case 0x2A00: { // device name const char *name = pbdrv_bluetooth_get_hub_name(); - pbdrv_usb_nxt_write_data(0, (const uint8_t *)name, + pbdrv_usb_nxt_write_data(0, name, MIN(strlen(name), packet->length)); break; } case 0x2A26: { // firmware revision const char *fw = PBIO_VERSION_STR; - pbdrv_usb_nxt_write_data(0, (const uint8_t *)fw, + pbdrv_usb_nxt_write_data(0, fw, MIN(strlen(fw), packet->length)); break; } case 0x2A28: { // software revision const char *sw = PBIO_PROTOCOL_VERSION_STR; - pbdrv_usb_nxt_write_data(0, (const uint8_t *)sw, + pbdrv_usb_nxt_write_data(0, sw, MIN(strlen(sw), packet->length)); break; } @@ -806,15 +629,15 @@ static uint32_t pbdrv_usb_nxt_manage_setup_packet(void) { break; case USB_BMREQUEST_TYPE_VENDOR: switch (packet.request) { - case PBDRV_USB_NXT_VENDOR_CODE_WEBUSB: + case PBDRV_USB_VENDOR_REQ_WEBUSB: // Since there is only one WebUSB descriptor, we ignore the index. - pbdrv_usb_nxt_write_data(0, pbdrv_usb_desc_set_webusb, - MIN(sizeof(pbdrv_usb_desc_set_webusb), packet.length)); + pbdrv_usb_nxt_write_data(0, &pbdrv_usb_webusb_landing_page, + MIN(pbdrv_usb_webusb_landing_page.s.bLength, packet.length)); break; - case PBDRV_USB_NXT_VENDOR_CODE_MS: + case PBDRV_USB_VENDOR_REQ_MS_20: // Since there is only one MS descriptor, we ignore the index. - pbdrv_usb_nxt_write_data(0, pbdrv_usb_desc_set_ms_os, - MIN(sizeof(pbdrv_usb_desc_set_ms_os), packet.length)); + pbdrv_usb_nxt_write_data(0, &pbdrv_usb_ms_20_desc_set, + MIN(sizeof(pbdrv_usb_ms_20_desc_set.s), packet.length)); break; default: pbdrv_usb_nxt_send_stall(0); @@ -955,8 +778,7 @@ static void pbdrv_usb_nxt_isr(void) { } /* and we will send the following data */ - if (pbdrv_usb_nxt_state.tx_len[endpoint] > 0 - && pbdrv_usb_nxt_state.tx_data[endpoint] != NULL) { + if (pbdrv_usb_nxt_state.tx_data[endpoint] != NULL) { pbdrv_usb_nxt_write_data(endpoint, pbdrv_usb_nxt_state.tx_data[endpoint], pbdrv_usb_nxt_state.tx_len[endpoint]); } else { diff --git a/lib/pbio/platform/nxt/pbdrvconfig.h b/lib/pbio/platform/nxt/pbdrvconfig.h index 17f1a36f5..04d0e78ed 100644 --- a/lib/pbio/platform/nxt/pbdrvconfig.h +++ b/lib/pbio/platform/nxt/pbdrvconfig.h @@ -50,3 +50,7 @@ #define PBDRV_CONFIG_USB (1) #define PBDRV_CONFIG_USB_NXT (1) +#define PBDRV_CONFIG_USB_VID LEGO_USB_VID +#define PBDRV_CONFIG_USB_PID LEGO_USB_PID_NXT +#define PBDRV_CONFIG_USB_MFG_STR LEGO_USB_MFG_STR +#define PBDRV_CONFIG_USB_PROD_STR LEGO_USB_PROD_STR_NXT " + Pybricks"