Fork of https://developer.mbed.org/users/bscott/code/STM32_USBDevice/

Fork of STM32_USBDevice by Bradley Scott

Branch:
feature_WebUSB
Revision:
76:eef92651f52f
Child:
77:a98f786d05d4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBSerial/USBWebUSBSerial.cpp	Thu Jul 19 12:57:27 2018 +0200
@@ -0,0 +1,662 @@
+#include "USBWebUSBSerial.h"
+
+static uint8_t cdc_line_coding[7]= {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08};
+
+#define DEFAULT_CONFIGURATION (1)
+
+#define CDC_SET_LINE_CODING        0x20
+#define CDC_GET_LINE_CODING        0x21
+#define CDC_SET_CONTROL_LINE_STATE 0x22
+
+// USB standard values
+#define USB_CS_INTERFACE 0x24
+#define USB_HEADER_FUNCTION_DESCRIPTOR_TYPE 0x00
+#define USB_CALL_MANAGEMENT_FUNCTION_DESCRIPTOR_TYPE 0x01
+#define USB_ABSTRACT_CONTROL_MANAGEMENT_FUNCTION_DESCRIPTOR_TYPE 0x02
+#define USB_UNION_INTERFACE_FUNCTION_DESCRIPTOR_TYPE 0x06
+#define PLATFORM_DEVICE_CAPABILITY_TYPE 0x05
+
+#define USB_CDC_CLASS 0x02
+#define USB_ACM_SUBCLASS 0x02
+#define USB_CDC_DATA_CLASS 0x0A
+#define USB_CUSTOM_CLASS 0xFF
+
+// MS OS 2.0 descriptor types
+#define MS_OS_20_SET_HEADER_DESCRIPTOR 0x00
+#define MS_OS_20_SUBSET_HEADER_CONFIGURATION 0x01
+#define MS_OS_20_SUBSET_HEADER_FUNCTION 0x02
+#define MS_OS_20_FEATURE_COMPATIBLE_ID 0x03
+#define MS_OS_20_FEATURE_REG_PROPERTY 0x04
+#define MS_OS_20_FEATURE_MIN_RESUME_TIME 0x05
+#define MS_OS_20_FEATURE_MODEL_ID 0x06
+#define MS_OS_20_FEATURE_CCGP_DEVICE 0x07
+
+#define MS_OS_20_DESCRIPTOR_INDEX 0x07
+#define MS_OS_20_DESCRIPTOR_LENGTH 178
+
+// Vendor commands
+#define WINUSB_VENDOR_CODE 0x01
+#define SET_WEBUSB_ACTIVE_VENDOR_CODE 0xAA
+
+// Control Line State bits
+#define CLS_DTR   (1 << 0)
+#define CLS_RTS   (1 << 1)
+
+#ifdef USB_DEVICE_WRITE_BLOCKING_TIMEOUT_MS
+#define WEBUSB_WRITE_TIMEOUT (USB_DEVICE_WRITE_BLOCKING_TIMEOUT_MS)
+#else
+#define WEBUSB_WRITE_TIMEOUT 1000
+#endif
+
+
+uint8_t USBWebUSBSerial::s_MSOS2Descriptor[] =
+{
+    // Microsoft OS 2.0 descriptor set header (table 10)
+    0x0A, 0x00,  // Header size (always 10 bytes)
+    LSB(MS_OS_20_SET_HEADER_DESCRIPTOR),
+    MSB(MS_OS_20_SET_HEADER_DESCRIPTOR),
+    0x00, 0x00, 0x03, 0x06,  // Windows version (8.1) (0x06030000)
+    LSB(MS_OS_20_DESCRIPTOR_LENGTH),
+    MSB(MS_OS_20_DESCRIPTOR_LENGTH),
+
+    // Configuration subset header (table 11)
+    0x08, 0x00, // wLength, always 8
+    LSB(MS_OS_20_SUBSET_HEADER_CONFIGURATION),
+    MSB(MS_OS_20_SUBSET_HEADER_CONFIGURATION),
+    0x00, // "bConfigurationValue" - actually the index of the configuration, see https://social.msdn.microsoft.com/Forums/sqlserver/en-US/ae64282c-3bc3-49af-8391-4d174479d9e7/microsoft-os-20-descriptors-not-working-on-an-interface-of-a-composite-usb-device%3Fforum%3Dwdk+&cd=1&hl=da&ct=clnk&gl=dk&client=firefox-b
+    0x00, // reserved
+    LSB(MS_OS_20_DESCRIPTOR_LENGTH - 10), // wTotalLength (length of subset including this header)
+    MSB(MS_OS_20_DESCRIPTOR_LENGTH - 10),
+
+    // Function subset (table 12)
+    0x08, 0x00, // wLength, always 8
+    LSB(MS_OS_20_SUBSET_HEADER_FUNCTION),
+    MSB(MS_OS_20_SUBSET_HEADER_FUNCTION),
+    0x02, // bFirstInterface
+    0x00, // reserved
+    LSB(MS_OS_20_DESCRIPTOR_LENGTH - 18), // wSubSetLength (length of subset including this header)
+    MSB(MS_OS_20_DESCRIPTOR_LENGTH - 18),
+
+    // Microsoft OS 2.0 compatible ID descriptor (table 13)
+    // Ties the winusb driver to the webusb interface
+    0x14, 0x00,  // wLength, always 20
+    LSB(MS_OS_20_FEATURE_COMPATIBLE_ID),
+    MSB(MS_OS_20_FEATURE_COMPATIBLE_ID),
+    'W',  'I',  'N',  'U',  'S',  'B',  0x00, 0x00, // CompatibleID (string)
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SubCompatibleID (string)
+
+    // Registry property descriptor (table 14)
+    // Sets a registry key named DeviceInterfaceGUIDs with a GUID for the WebUSB interface (apparently required before libusb can enumerate it...)
+    0x84, 0x00,   // wLength (132)
+    LSB(MS_OS_20_FEATURE_REG_PROPERTY),
+    MSB(MS_OS_20_FEATURE_REG_PROPERTY),
+    0x07, 0x00,   // wPropertyDataType (REG_MULTI_SZ) - see table 15
+    0x2A, 0x00,   // wPropertyNameLength (42)
+    // Property name (wide string) : 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,
+    0x50, 0x00,   // wPropertyDataLength (80)
+    // Property data (wide string): {307A82E9-227E-48C3-8C4E-627BE96A7A78} (randomly generated for this device)
+    '{', 0, '3', 0, '0', 0, '7', 0, 'A', 0, '8', 0, '2', 0, 'E', 0, '9', 0, '-', 0,
+    '2', 0, '2', 0, '7', 0, 'E', 0, '-', 0,
+    '4', 0, '8', 0, 'C', 0, '3', 0, '-', 0,
+    '8', 0, 'C', 0, '4', 0, 'E', 0, '-', 0,
+    '6', 0, '2', 0, '7', 0, 'B', 0, 'E', 0, '9', 0, '6', 0, 'A', 0, '7', 0, 'A', 0, '7', 0, '8', 0, '}', 0, 0, 0, 0, 0
+};
+
+
+USBWebUSBSerial::USBWebUSBSerial(uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking)
+    : USBDevice(vendor_id, product_id, product_release)
+{
+    m_terminalConnected = false;
+    USBDevice::connect(connect_blocking);
+    createDynamicDescriptors();
+}
+
+void USBWebUSBSerial::USBCallback_busReset()
+{
+    m_terminalConnected = false;
+    m_webUSBMode = false;
+}
+
+bool USBWebUSBSerial::USBCallback_request()
+{
+    /* Called in ISR context */
+
+    bool success = false;
+    CONTROL_TRANSFER * transfer = getTransferPtr();
+
+    /* Process class-specific requests */
+    if (transfer->setup.bmRequestType.Type == CLASS_TYPE)
+    {
+        switch (transfer->setup.bRequest)
+        {
+            case CDC_GET_LINE_CODING:
+                transfer->remaining = 7;
+                transfer->ptr = cdc_line_coding;
+                transfer->direction = DEVICE_TO_HOST;
+                success = true;
+                break;
+            case CDC_SET_LINE_CODING:
+                transfer->remaining = 7;
+                transfer->notify = true;
+                success = true;
+                break;
+            case CDC_SET_CONTROL_LINE_STATE:
+                if (transfer->setup.wValue & CLS_DTR) {
+                    m_terminalConnected = true;
+                } else {
+                    m_terminalConnected = false;
+                }
+                success = true;
+                break;
+            default:
+                break;
+        }
+    }
+    else if (transfer->setup.bmRequestType.Type == VENDOR_TYPE && transfer->setup.bRequest == WINUSB_VENDOR_CODE && transfer->setup.wIndex == MS_OS_20_DESCRIPTOR_INDEX)
+    {
+        // Request for the MS OS 2.0 descriptor
+        transfer->remaining = MS_OS_20_DESCRIPTOR_LENGTH;
+        transfer->ptr = s_MSOS2Descriptor;
+        transfer->direction = DEVICE_TO_HOST;
+        success = true;
+    }
+    else if (transfer->setup.bmRequestType.Type == VENDOR_TYPE && transfer->setup.bRequest == SET_WEBUSB_ACTIVE_VENDOR_CODE)
+    {
+        if (transfer->setup.wValue == 0)
+        {
+            // Disable WebUSB mode
+            m_webUSBMode = false;
+        }
+        else
+        {
+            // Enable WebUSB mode
+            m_webUSBMode = true;
+        }
+        success = true;
+    }
+
+    return success;
+}
+
+void USBWebUSBSerial::USBCallback_requestCompleted(uint8_t *buf, uint32_t length)
+{
+    // Request of setting line coding has 7 bytes
+    if (length != 7)
+    {
+        return;
+    }
+
+    CONTROL_TRANSFER * transfer = getTransferPtr();
+
+    /* Process class-specific requests */
+    if (transfer->setup.bmRequestType.Type == CLASS_TYPE)
+    {
+        if (transfer->setup.bRequest == CDC_SET_LINE_CODING)
+        {
+            if (memcmp(cdc_line_coding, buf, 7))
+            {
+                memcpy(cdc_line_coding, buf, 7);
+
+                int baud = buf[0] + (buf[1] << 8)
+                         + (buf[2] << 16) + (buf[3] << 24);
+                int stop = buf[4];
+                int bits = buf[6];
+                int parity = buf[5];
+
+                lineCodingChanged(baud, bits, parity, stop);
+            }
+        }
+    }
+}
+
+// Called in ISR context
+// Set configuration. Return false if the
+// configuration is not supported.
+bool USBWebUSBSerial::USBCallback_setConfiguration(uint8_t configuration)
+{
+    if (configuration != DEFAULT_CONFIGURATION)
+    {
+        return false;
+    }
+
+    if (configured())
+    {
+        // Already configured, nothing to do
+        return true;
+    }
+
+    // Configure endpoints > 0
+    addEndpoint(EPINT_IN, MAX_PACKET_SIZE_EPINT);
+    addEndpoint(EPCDC_IN, MAX_PACKET_SIZE_EPBULK);
+    addEndpoint(EPCDC_OUT, MAX_PACKET_SIZE_EPBULK);
+    addEndpoint(EPWEBUSB_IN, MAX_PACKET_SIZE_EPBULK);
+    addEndpoint(EPWEBUSB_OUT, MAX_PACKET_SIZE_EPBULK);
+
+    // We activate the endpoints to be able to receive data
+    readStart(EPCDC_OUT, MAX_PACKET_SIZE_EPBULK);
+    readStart(EPWEBUSB_OUT, MAX_PACKET_SIZE_EPBULK);
+    return true;
+}
+
+bool USBWebUSBSerial::send(const uint8_t * buffer, uint32_t size)
+{
+    if (!m_webUSBMode)
+    {
+        return USBDevice::write(EPCDC_IN, buffer, size, MAX_PACKET_SIZE_EPBULK);
+    }
+    else
+    {
+        EP_STATUS result;
+        if (size > MAX_PACKET_SIZE_EPBULK)
+        {
+            return false;
+        }
+
+        if(!configured()) {
+            return false;
+        }
+
+        result = endpointWrite(EPWEBUSB_IN, buffer, size);
+
+        if (result != EP_PENDING)
+        {
+            return false;
+        }
+
+        // Wait for completion (or until timeout is reached)
+        uint32_t tstart = osKernelGetTickCount();
+        do {
+            result = endpointWriteResult(EPWEBUSB_IN);
+
+            if ((osKernelGetTickCount()-tstart) > (WEBUSB_WRITE_TIMEOUT)) {
+                // Host is not reading from the endpoint, assume the WebUSB client is no longer active and switch back to CDC mode
+                m_webUSBMode = false;
+                break;
+            }
+        } while (result == EP_PENDING);
+
+        return (result == EP_COMPLETED);
+    }
+}
+
+bool USBWebUSBSerial::readActiveEP()
+{
+    uint8_t buffer[MAX_PACKET_SIZE_EPBULK];
+    uint32_t size = 0;
+    if (!USBDevice::readEP_NB(activeOutEndpoint(), buffer, &size, MAX_PACKET_SIZE_EPBULK))
+    {
+        return false;
+    }
+    if (!readStart(activeOutEndpoint(), MAX_PACKET_SIZE_EPBULK))
+    {
+        return false;
+    }
+
+    for (uint32_t i = 0; i < size; i++)
+    {
+        m_buffer.queue(buffer[i]);
+    }
+
+    if (size != 0 && activeOutEndpoint() == EPCDC_OUT)
+    {
+        // terminal must be connected now since we received packets
+        m_terminalConnected = true;
+    }
+
+    // call a potential handler
+    if (m_rx)
+    {
+        m_rx.call();
+    }
+
+    return true;
+}
+
+uint8_t * USBWebUSBSerial::deviceDesc()
+{
+    static uint8_t deviceDescriptor[] = {
+        DEVICE_DESCRIPTOR_LENGTH, // bLength
+        DEVICE_DESCRIPTOR,    // bDescriptorType
+        0x10, 0x02,           // bcdUSB = 2.10
+        0,                    // bDeviceClass: composite (unspecified on device level)
+        0,                    // bDeviceSubClass
+        0,                    // bDeviceProtocol
+        MAX_PACKET_SIZE_EP0,  // bMaxPacketSize0
+        (uint8_t)(LSB(VENDOR_ID)), (uint8_t)(MSB(VENDOR_ID)),  // idVendor
+        (uint8_t)(LSB(PRODUCT_ID)), (uint8_t)(MSB(PRODUCT_ID)),// idProduct
+        0x00, 0x01,           // bcdDevice
+        STRING_OFFSET_IMANUFACTURER, // iManufacturer
+        STRING_OFFSET_IPRODUCT, // iProduct
+        STRING_OFFSET_ISERIAL, // iSerialNumber
+        1                     // bNumConfigurations
+    };
+    return deviceDescriptor;
+}
+
+uint8_t * USBWebUSBSerial::stringImanufacturerDesc()
+{
+    return m_manufacturerStringDesc;
+}
+
+uint8_t * USBWebUSBSerial::stringIproductDesc()
+{
+    return m_productStringDesc;
+}
+
+uint8_t * USBWebUSBSerial::stringIserialDesc()
+{
+    return m_serialStringDesc;
+}
+
+#define CONFIG1_DESC_SIZE (CONFIGURATION_DESCRIPTOR_LENGTH + \
+                           INTERFACE_ASSOCIATION_DESCRIPTOR_LENGTH + \
+                           INTERFACE_DESCRIPTOR_LENGTH + \
+                           5+5+4+5 + \
+                           ENDPOINT_DESCRIPTOR_LENGTH + \
+                           INTERFACE_DESCRIPTOR_LENGTH + \
+                           ENDPOINT_DESCRIPTOR_LENGTH + \
+                           ENDPOINT_DESCRIPTOR_LENGTH + \
+                           INTERFACE_DESCRIPTOR_LENGTH + \
+                           ENDPOINT_DESCRIPTOR_LENGTH + \
+                           ENDPOINT_DESCRIPTOR_LENGTH)
+
+uint8_t * USBWebUSBSerial::configurationDesc()
+{
+    static uint8_t configDescriptor[] = {
+        // configuration descriptor
+        CONFIGURATION_DESCRIPTOR_LENGTH,  // bLength
+        CONFIGURATION_DESCRIPTOR, // bDescriptorType
+        LSB(CONFIG1_DESC_SIZE), // wTotalLength
+        MSB(CONFIG1_DESC_SIZE),
+        3,                      // bNumInterfaces
+        1,                      // bConfigurationValue
+        0,                      // iConfiguration
+        0x80,                   // bmAttributes
+        50,                     // bMaxPower
+
+        // IAD to associate the two CDC interfaces
+        INTERFACE_ASSOCIATION_DESCRIPTOR_LENGTH, // bLength
+        INTERFACE_ASSOCIATION_DESCRIPTOR, // bDescriptorType
+        0x00,                   // bFirstInterface
+        0x02,                   // bInterfaceCount
+        USB_CDC_CLASS,          // bFunctionClass
+        USB_ACM_SUBCLASS,       // bFunctionSubClass
+        0,                      // bFunctionProtocol
+        0,                      // iFunction
+
+        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
+        INTERFACE_DESCRIPTOR_LENGTH, // bLength
+        INTERFACE_DESCRIPTOR,   // bDescriptorType
+        0,                      // bInterfaceNumber
+        0,                      // bAlternateSetting
+        1,                      // bNumEndpoints
+        USB_CDC_CLASS,          // bInterfaceClass
+        USB_ACM_SUBCLASS,       // bInterfaceSubClass
+        0x01,                   // bInterfaceProtocol
+        0,                      // iInterface
+
+        // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26
+        5,                      // bFunctionLength
+        USB_CS_INTERFACE,       // bDescriptorType
+        USB_HEADER_FUNCTION_DESCRIPTOR_TYPE, // bDescriptorSubtype
+        0x10, 0x01,             // bcdCDC
+
+        // Call Management Functional Descriptor, CDC PSTN Spec 5.3.1, Table 3
+        5,                      // bFunctionLength
+        USB_CS_INTERFACE,       // bDescriptorType
+        USB_CALL_MANAGEMENT_FUNCTION_DESCRIPTOR_TYPE, // bDescriptorSubtype
+        0x03,                   // bmCapabilities
+        1,                      // bDataInterface
+
+        // Abstract Control Management Functional Descriptor, CDC PSTN Spec 5.3.2, Table 4
+        4,                      // bFunctionLength
+        USB_CS_INTERFACE,       // bDescriptorType
+        USB_ABSTRACT_CONTROL_MANAGEMENT_FUNCTION_DESCRIPTOR_TYPE, // bDescriptorSubtype
+        0x06,                   // bmCapabilities
+
+        // Union Functional Descriptor, CDC Spec 5.2.3.2, Table 16
+        5,                      // bFunctionLength
+        USB_CS_INTERFACE,       // bDescriptorType
+        USB_UNION_INTERFACE_FUNCTION_DESCRIPTOR_TYPE, // bDescriptorSubtype
+        0,                      // bMasterInterface
+        1,                      // bSlaveInterface0
+
+        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+        ENDPOINT_DESCRIPTOR_LENGTH,     // bLength
+        ENDPOINT_DESCRIPTOR,            // bDescriptorType
+        PHY_TO_DESC(EPINT_IN),          // bEndpointAddress
+        E_INTERRUPT,                    // bmAttributes (0x03=intr)
+        LSB(MAX_PACKET_SIZE_EPINT),     // wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPINT),     // wMaxPacketSize (MSB)
+        16,                             // bInterval
+
+        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
+        INTERFACE_DESCRIPTOR_LENGTH, // bLength
+        INTERFACE_DESCRIPTOR,       // bDescriptorType
+        1,                          // bInterfaceNumber
+        0,                          // bAlternateSetting
+        2,                          // bNumEndpoints
+        USB_CDC_DATA_CLASS,         // bInterfaceClass
+        0x00,                       // bInterfaceSubClass
+        0x00,                       // bInterfaceProtocol
+        0,                          // iInterface
+
+        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+        ENDPOINT_DESCRIPTOR_LENGTH, // bLength
+        ENDPOINT_DESCRIPTOR,        // bDescriptorType
+        PHY_TO_DESC(EPCDC_IN),     // bEndpointAddress
+        E_BULK,                     // bmAttributes (0x02=bulk)
+        LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
+        0,                          // bInterval
+
+        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+        ENDPOINT_DESCRIPTOR_LENGTH, // bLength
+        ENDPOINT_DESCRIPTOR,        // bDescriptorType
+        PHY_TO_DESC(EPCDC_OUT),    // bEndpointAddress
+        E_BULK,                     // bmAttributes (0x02=bulk)
+        LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
+        0,                          // bInterval
+
+        // WebUSB interface
+        INTERFACE_DESCRIPTOR_LENGTH, // bLength
+        INTERFACE_DESCRIPTOR,       // bDescriptorType
+        2,                          // bInterfaceNumber
+        0,                          // bAlternateSetting
+        2,                          // bNumEndpoints
+        USB_CUSTOM_CLASS,         // bInterfaceClass
+        0x00,                       // bInterfaceSubClass
+        0x00,                       // bInterfaceProtocol
+        0,                          // iInterface
+
+        // WebUSB IN endpoint
+        ENDPOINT_DESCRIPTOR_LENGTH, // bLength
+        ENDPOINT_DESCRIPTOR,        // bDescriptorType
+        PHY_TO_DESC(EPWEBUSB_IN),   // bEndpointAddress
+        E_BULK,                     // bmAttributes (0x02=bulk)
+        LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
+        0,                          // bInterval
+
+        // WebUSB OUT endpoint
+        ENDPOINT_DESCRIPTOR_LENGTH, // bLength
+        ENDPOINT_DESCRIPTOR,        // bDescriptorType
+        PHY_TO_DESC(EPWEBUSB_OUT),  // bEndpointAddress
+        E_BULK,                     // bmAttributes (0x02=bulk)
+        LSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (LSB)
+        MSB(MAX_PACKET_SIZE_EPBULK),// wMaxPacketSize (MSB)
+        0                           // bInterval
+    };
+    return configDescriptor;
+}
+
+#define BOS_MSOS2_PLATFORM_DESCRIPTOR_LENGTH 28
+#define BOS_TOTAL_LENGTH (BOS_DESCRIPTOR_LENGTH + BOS_MSOS2_PLATFORM_DESCRIPTOR_LENGTH)
+
+uint8_t * USBWebUSBSerial::bosDesc()
+{
+    static uint8_t bosDescriptor[] = {
+        BOS_DESCRIPTOR_LENGTH, // bLength
+        BOS_DESCRIPTOR,        // bDescriptorType
+        LSB(BOS_TOTAL_LENGTH), // wTotalLength (LSB)
+        MSB(BOS_TOTAL_LENGTH), // wTotalLength (MSB)
+        0x01,                  // bNumDeviceCaps
+
+        // MS OS 2.0 descriptors information
+        BOS_MSOS2_PLATFORM_DESCRIPTOR_LENGTH,
+        DEVICE_CAPABILITY_DESCRIPTOR,
+        PLATFORM_DEVICE_CAPABILITY_TYPE,
+        0x00, // bReserved
+        0xDF, 0x60, 0xDD, 0xD8,         // PlatformCapabilityUUID ({D8DD60DF-4589-4CC7-9CD2-659D9E648A9F})
+        0x89, 0x45,
+        0xC7, 0x4C,
+        0x9C, 0xD2,
+        0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F,
+
+        0x00, 0x00, 0x03, 0x06,    // Windows version (8.1 = 0x06030000)
+        LSB(MS_OS_20_DESCRIPTOR_LENGTH), // wMSOSDecriptorSetTotalLength (LSB)
+        MSB(MS_OS_20_DESCRIPTOR_LENGTH), // wMSOSDecriptorSetTotalLength (MSB)
+        WINUSB_VENDOR_CODE,         // bMS_VendorCode
+        0x00 // bAltEnum
+    };
+    return bosDescriptor;
+}
+
+
+int USBWebUSBSerial::_putc(int c)
+{
+    if (!m_terminalConnected && !m_webUSBMode)
+    {
+        return 0;
+    }
+    send((uint8_t *)&c, 1);
+    return 1;
+}
+
+int USBWebUSBSerial::_getc()
+{
+    uint8_t c = 0;
+    while (m_buffer.isEmpty());
+    m_buffer.dequeue(&c);
+    return c;
+}
+
+bool USBWebUSBSerial::writeBlock(const uint8_t * buf, uint16_t size)
+{
+    if(!m_terminalConnected && !m_webUSBMode)
+    {
+        return false;
+    }
+    if(size > MAX_PACKET_SIZE_EPBULK)
+    {
+        return false;
+    }
+    if(!send(buf, size))
+    {
+        return false;
+    }
+    return true;
+}
+
+bool USBWebUSBSerial::EPCDC_OUT_callback()
+{
+    EPOUTCallbackHandler(EPCDC_OUT);
+    return true;
+}
+
+bool USBWebUSBSerial::EPWEBUSB_OUT_callback()
+{
+    EPOUTCallbackHandler(EPWEBUSB_OUT);
+    return true;    
+}
+
+bool USBWebUSBSerial::EPOUTCallbackHandler(uint8_t endpoint)
+{
+    if (endpoint == activeOutEndpoint())
+    {
+        return readActiveEP();
+    }
+    else
+    {
+        // Read out the data but ignore it since it is on the inactive endpoint
+        uint8_t buffer[MAX_PACKET_SIZE_EPBULK];
+        uint32_t size = 0;
+        if (!USBDevice::readEP_NB(endpoint, buffer, &size, MAX_PACKET_SIZE_EPBULK))
+        {
+            return false;
+        }
+        if (!readStart(endpoint, MAX_PACKET_SIZE_EPBULK))
+        {
+            return false;
+        }
+
+        if (size != 0 && endpoint == EPCDC_OUT)
+        {
+            // terminal must be connected now since we received packets
+            m_terminalConnected = true;
+        }
+    }
+    return true;
+}
+
+uint8_t USBWebUSBSerial::available()
+{
+    return m_buffer.available();
+}
+
+void USBWebUSBSerial::setManufacturerName(const std::string &manufacturerName)
+{
+    m_manufacturerName = manufacturerName;
+    delete m_manufacturerStringDesc;
+    m_manufacturerStringDesc = nullptr;
+    createDynamicDescriptors();
+}
+
+void USBWebUSBSerial::setProductName(const std::string &productName)
+{
+    m_productName = productName;
+    delete m_productStringDesc;
+    m_productStringDesc = nullptr;
+    createDynamicDescriptors();
+}
+
+void USBWebUSBSerial::setSerialNumber(const std::string &serialNumber)
+{
+    m_serialNumber = serialNumber;
+    delete m_serialStringDesc;
+    m_serialStringDesc = nullptr;
+    createDynamicDescriptors();
+}
+
+void USBWebUSBSerial::createDynamicDescriptors()
+{
+    if (!m_manufacturerStringDesc)
+    {
+        m_manufacturerStringDesc = createStringDescriptor(m_manufacturerName);
+    }
+    if (!m_productStringDesc)
+    {
+        m_productStringDesc = createStringDescriptor(m_productName);
+    }
+    if (!m_serialStringDesc)
+    {
+        m_serialStringDesc = createStringDescriptor(m_serialNumber);
+    }
+}
+
+uint8_t *USBWebUSBSerial::createStringDescriptor(const std::string &string) const
+{
+    uint8_t stringSize = string.size()*2;
+    uint8_t descriptorSize = 2 + stringSize;
+    uint8_t *descriptor = new uint8_t[descriptorSize];
+    descriptor[0] = descriptorSize; // bLength
+    descriptor[1] = STRING_DESCRIPTOR; // bDescriptorType
+    for (int i = 0; i < stringSize/2; i++)
+    {
+        descriptor[(i*2)+2] = string[i];
+        descriptor[(i*2)+3] = 0;
+    }
+    return descriptor;
+}
\ No newline at end of file