diff --git a/ion/src/device/usb.cpp b/ion/src/device/usb.cpp index aeee8ee9f..242cfdde2 100644 --- a/ion/src/device/usb.cpp +++ b/ion/src/device/usb.cpp @@ -66,6 +66,46 @@ static const struct InterfaceDescriptor interfaceDescriptor = { .iInterface = 0 }; +static const struct OSStringDescriptor osStringDescriptor = { + .bLength = USB_DT_OS_STRING_SIZE, + .bDescriptorType = USB_DT_OS_STRING, + .qwSignature = {'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0}, + .bMS_VendorCode = USB_OS_STRING_BMS_VENDOR_CODE, + .bPad = USB_OS_STRING_PAD +}; + +static const struct OSExtendedCompatIDHeader osExtendedCompatIDHeader = { + .dwLength = USB_OS_EXTENDED_COMPAT_ID_HEADER_SIZE + USB_OS_EXTENDED_COMPAT_ID_FUNCTION_SIZE, + .bcdVersion = USB_EXTENDED_COMPAT_ID_HEADER_BCD_VERSION, + .wIndex = USB_OS_FEATURE_EXTENDED_COMPAT_ID, + .bCount = 1, + .reserved = {0, 0, 0, 0, 0, 0, 0} +}; + +static const struct OSExtendedCompatIDFunction osExtendedCompatIDFunction = { + .bFirstInterfaceNumber = 0, + .bReserved = 0, + .compatibleID = {'W', 'I', 'N', 'U', 'S', 'B', 0, 0}, + .subCompatibleID = {0, 0, 0, 0, 0, 0, 0, 0}, + .reserved = {0, 0, 0, 0, 0, 0}, +}; + +static const struct OSExtendedPropertiesHeader osExtendedPropertiesHeader = { + .dwLength = 0, + .bcdVersion = USB_EXTENDED_PROPERTIES_HEADER_BCD_VERSION, + .wIndex = USB_OS_FEATURE_EXTENDED_PROPERTIES, + .wCount = 1, +}; + +static const struct OSExtendedPropertiesGUIDFunction osExtendedPropertiesGUIDFunction = { + .dwSize = USB_OS_EXTENDED_PROPERTIES_FUNCTION_SIZE_WITHOUT_DATA + USB_GUID_PROPERTY_NAME_LENGTH + USB_GUID_PROPERTY_DATA_LENGTH, + .dwPropertyDataType = USB_PROPERTY_DATA_TYPE_UTF_16_LITTLE_ENDIAN, + .wPropertyNameLength = USB_GUID_PROPERTY_NAME_LENGTH, + .bPropertyName = {'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, 0, 0}, + .dwPropertyDataLength = USB_GUID_PROPERTY_DATA_LENGTH, + .bPropertyData = {'{', 0, '9', 0, '5', 0, '2', 0, '3', 0, '6', 0, '2', 0, 'C', 0, '1', 0, '-', 0, '3', 0, 'D', 0, '9', 0, '3', 0, '-', 0, '4', 0, 'D', 0, '2', 0, 'A', 0, '-', 0, '9', 0, 'A', 0, '2', 0, '0', 0, '-', 0, '6', 0, '5', 0, '5', 0, '3', 0, '2', 0, '0', 0, '1', 0, '4', 0, '7', 0, 'C', 0, '6', 0, 'F', 0, '}', 0, 0, 0} +}; + SetupData sSetupData; uint16_t sRxPacketSize; /* Buffer used for control requests. */ @@ -334,7 +374,6 @@ int controlSetupGetDescriptor() { return (int) RequestReturnCodes::USBD_REQ_HANDLED; case USB_DT_STRING: struct StringDescriptor * stringDescriptor = (struct StringDescriptor *)sControlBufferInit; - if (descriptorIndex == 0) { /* Send sane Language ID descriptor. */ stringDescriptor->wData[0] = USB_LANGID_ENGLISH_US; @@ -342,6 +381,13 @@ int controlSetupGetDescriptor() { sizeof(stringDescriptor->bDescriptorType) + sizeof(stringDescriptor->wData[0]); sControlBufferLength = MIN(sControlBufferLength, stringDescriptor->bLength); + } else if (descriptorIndex == 0xEE) { + // Windows OS String Descriptor + assert(sSetupData.wIndex == 0); + assert(sSetupData.wLength == 0x12); + sControlBuffer = (uint8_t *)(&osStringDescriptor); + sControlBufferLength = MIN(sControlBufferLength, osStringDescriptor.bLength); + return (int) RequestReturnCodes::USBD_REQ_HANDLED; } else { int arrayIndex = descriptorIndex - 1; /* Check that string index is in range. */ @@ -389,6 +435,13 @@ int controlSetupSetConfiguration() { } int controlRequestDispatch() { + if (bmRequestTypeType(sSetupData.bmRequestType) == RequestTypeType::Vendor) { + return controlCustomSetup(); + } + return controlStandardRequest(); +} + +int controlStandardRequest() { switch (sSetupData.bRequest) { case USB_REQ_GET_STATUS: //TODO Not needed for enumeration? @@ -424,6 +477,26 @@ int controlRequestDispatch() { return 0; } +int controlCustomSetup() { + switch (sSetupData.bRequest) { + case USB_OS_STRING_BMS_VENDOR_CODE: + if (sSetupData.wIndex == USB_OS_FEATURE_EXTENDED_COMPAT_ID) { + sControlBuffer = sControlBufferInit; + sControlBufferLength = buildExtendedCompatIDDescriptor(); + return (int) RequestReturnCodes::USBD_REQ_HANDLED; + } + if (sSetupData.wIndex == USB_OS_FEATURE_EXTENDED_PROPERTIES) { + sControlBuffer = sControlBufferInit; + sControlBufferLength = buildExtendedPropertiesDescriptor(); + return (int) RequestReturnCodes::USBD_REQ_HANDLED; + } + break; + default: + break; + } + return (int) RequestReturnCodes::USBD_REQ_NOTSUPP; +} + void controlOut() { switch (sControlState) { case ControlState::DATA_OUT: @@ -698,6 +771,56 @@ uint16_t buildConfigDescriptor(uint8_t index) { return total; } +uint16_t buildExtendedCompatIDDescriptor() { + uint8_t *tmpbuf = sControlBuffer; + + /* Copy the header */ + uint16_t count = MIN(sControlBufferLength, USB_OS_EXTENDED_COMPAT_ID_HEADER_SIZE); + memcpy(sControlBuffer, &osExtendedCompatIDHeader, count); + sControlBuffer += count; + sControlBufferLength -= count; + uint16_t total = count; + uint16_t totalLength = USB_OS_EXTENDED_COMPAT_ID_HEADER_SIZE; + + /* Copy the function */ + count = MIN(sControlBufferLength, USB_OS_EXTENDED_COMPAT_ID_FUNCTION_SIZE); + memcpy(sControlBuffer, &osExtendedCompatIDFunction, count); + sControlBuffer += count; + sControlBufferLength -= count; + total += count; + totalLength += USB_OS_EXTENDED_COMPAT_ID_FUNCTION_SIZE; + + sControlBuffer = tmpbuf; + + return total; +} + +uint16_t buildExtendedPropertiesDescriptor() { + uint8_t *tmpbuf = sControlBuffer; + + /* Copy the header */ + uint16_t count = MIN(sControlBufferLength, USB_OS_EXTENDED_PROPERTIES_HEADER_SIZE); + memcpy(sControlBuffer, &osExtendedPropertiesHeader, count); + sControlBuffer += count; + sControlBufferLength -= count; + uint16_t total = count; + uint16_t totalLength = USB_OS_EXTENDED_PROPERTIES_HEADER_SIZE; + + /* Copy the GUID Property function */ + count = MIN(sControlBufferLength, osExtendedPropertiesGUIDFunction.dwSize); + memcpy(sControlBuffer, &osExtendedPropertiesGUIDFunction, count); + sControlBuffer += count; + sControlBufferLength -= count; + total += count; + totalLength += osExtendedPropertiesGUIDFunction.dwSize; + + /* Fill in dwLength. */ + *(uint16_t *)(tmpbuf) = totalLength; + sControlBuffer = tmpbuf; + + return total; +} + DataDirection bmRequestTypeDirection(uint8_t bmRequestType) { if (bmRequestType & 0x80) { return DataDirection::In; @@ -705,6 +828,11 @@ DataDirection bmRequestTypeDirection(uint8_t bmRequestType) { return DataDirection::Out; } +RequestTypeType bmRequestTypeType(uint8_t bmRequestType) { + int type = (bmRequestType & 0b01100000) >> 5; + return (RequestTypeType)type; +} + int descriptorIndexFromWValue(uint16_t wValue) { return wValue & 0xFF; } diff --git a/ion/src/device/usb.h b/ion/src/device/usb.h index 75e4a0f2b..c3f4be2c6 100644 --- a/ion/src/device/usb.h +++ b/ion/src/device/usb.h @@ -37,6 +37,12 @@ enum class DataDirection { Out }; +enum class RequestTypeType { + Standard = 0, + Class = 1, + Vendor = 2 +}; + /* USB Standard Request Codes */ #define USB_REQ_GET_STATUS 0 #define USB_REQ_CLEAR_FEATURE 1 @@ -160,6 +166,7 @@ void controlSetupOut(); int controlSetupGetDescriptor(); int controlSetupSetConfiguration(); int controlRequestDispatch(); +int controlStandardRequest(); // Control In and Out void controlOut(); void controlIn(); @@ -186,6 +193,7 @@ void usb_set_address(uint8_t address); // Helpers uint16_t buildConfigDescriptor(uint8_t index); DataDirection bmRequestTypeDirection(uint8_t bmRequestType); +RequestTypeType bmRequestTypeType(uint8_t bmRequestType); int descriptorIndexFromWValue(uint16_t wValue); int descriptorTypeFromWValue(uint16_t wValue); bool zlpIsNeeded(uint16_t dataLength, uint16_t dataExpectedLength, uint8_t endpointMaxPacketSize); @@ -205,6 +213,70 @@ constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9); constexpr static GPIOPin DmPin = GPIOPin(GPIOA, 11); constexpr static GPIOPin DpPin = GPIOPin(GPIOA, 12); +/* Windows OS Descriptor */ + +#define USB_DT_OS_STRING_SIZE 0x12 +#define USB_DT_OS_STRING 3 +#define USB_OS_STRING_BMS_VENDOR_CODE 2 // Arbitrarily chosen +#define USB_OS_STRING_PAD 0 +#define USB_OS_FEATURE_EXTENDED_COMPAT_ID 0x04 +#define USB_OS_FEATURE_EXTENDED_PROPERTIES 0x05 +#define USB_EXTENDED_COMPAT_ID_HEADER_BCD_VERSION 0x0100 +#define USB_EXTENDED_PROPERTIES_HEADER_BCD_VERSION 0x0100 +#define USB_OS_EXTENDED_COMPAT_ID_HEADER_SIZE 16 +#define USB_OS_EXTENDED_COMPAT_ID_FUNCTION_SIZE 24 +#define USB_OS_EXTENDED_PROPERTIES_HEADER_SIZE 10 +#define USB_OS_EXTENDED_PROPERTIES_FUNCTION_SIZE_WITHOUT_DATA 14 +#define USB_PROPERTY_DATA_TYPE_UTF_16_LITTLE_ENDIAN 1 +#define USB_GUID_PROPERTY_NAME_LENGTH 0x28 +#define USB_GUID_PROPERTY_DATA_LENGTH 0x4E + +struct OSStringDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t qwSignature[14]; + uint8_t bMS_VendorCode; + uint8_t bPad; +} __attribute__((packed)); + +struct OSExtendedCompatIDHeader { + uint32_t dwLength; // The length, in bytes, of the complete extended compat ID descriptor + uint16_t bcdVersion; // The descriptor’s version number, in binary coded decimal (BCD) format + uint16_t wIndex; // An index that identifies the particular OS feature descriptor + uint8_t bCount; // The number of custom property sections + uint8_t reserved[7]; // Reserved +} __attribute__((packed)); + +struct OSExtendedCompatIDFunction { + uint8_t bFirstInterfaceNumber; // The interface or function number + uint8_t bReserved; // Reserved + uint8_t compatibleID[8]; // The function’s compatible ID + uint8_t subCompatibleID[8]; // The function’s subcompatible ID + uint8_t reserved[6]; // Reserved +} __attribute__((packed)); + +struct OSExtendedPropertiesHeader { + uint32_t dwLength; // The length, in bytes, of the complete extended properties descriptor + uint16_t bcdVersion; // The descriptor’s version number, in binary coded decimal (BCD) format + uint16_t wIndex; // The index for extended properties OS descriptors + uint16_t wCount; // The number of custom property sections that follow the header section +} __attribute__((packed)); + +struct OSExtendedPropertiesGUIDFunction { + uint32_t dwSize; // The size of this custom properties section + uint32_t dwPropertyDataType; // Property data format + uint16_t wPropertyNameLength; + uint8_t bPropertyName[USB_GUID_PROPERTY_NAME_LENGTH]; // Number of custom property sections that follow the header + uint32_t dwPropertyDataLength; // Length of the buffer holding the property data + uint8_t bPropertyData[USB_GUID_PROPERTY_DATA_LENGTH]; // Format-dependent Property data +} __attribute__((packed)); + + +int controlCustomSetup(); +uint16_t buildExtendedCompatIDDescriptor(); +uint16_t buildExtendedPropertiesDescriptor(); + + } } }