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

Fork of STM32_USBDevice by Bradley Scott

Files at this revision

API Documentation at this revision

Comitter:
Troels Nilsson
Date:
Wed Jul 25 14:04:48 2018 +0200
Parent:
75:43bf95761b38
Parent:
78:ba3f68a86e6d
Commit message:
Merge feature_WebUSB into default

Changed in this revision

--- a/USBDevice/USBDescriptor.h	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBDescriptor.h	Wed Jul 25 14:04:48 2018 +0200
@@ -23,13 +23,17 @@
 #define INTERFACE_DESCRIPTOR     (4)
 #define ENDPOINT_DESCRIPTOR      (5)
 #define QUALIFIER_DESCRIPTOR     (6)
+#define INTERFACE_ASSOCIATION_DESCRIPTOR (11)
+#define BOS_DESCRIPTOR           (15)
+#define DEVICE_CAPABILITY_DESCRIPTOR (16)
 
 /* Standard descriptor lengths */
 #define DEVICE_DESCRIPTOR_LENGTH        (0x12)
 #define CONFIGURATION_DESCRIPTOR_LENGTH (0x09)
 #define INTERFACE_DESCRIPTOR_LENGTH     (0x09)
 #define ENDPOINT_DESCRIPTOR_LENGTH      (0x07)
-
+#define INTERFACE_ASSOCIATION_DESCRIPTOR_LENGTH (0x08)
+#define BOS_DESCRIPTOR_LENGTH (0x05)
 
 /*string offset*/
 #define STRING_OFFSET_LANGID            (0)
--- a/USBDevice/USBDevice.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBDevice.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -155,6 +155,24 @@
 #endif
             /* TODO: Support is optional, not implemented here */
             break;
+
+        case BOS_DESCRIPTOR:
+#ifdef DEBUG
+            printf("BOS descr\r\n");
+#endif
+            {
+                uint8_t *descriptor = bosDesc();
+                if (descriptor)
+                {
+                    /* Get wTotalLength */
+                    transfer.remaining = descriptor[2] | (descriptor[3] << 8);
+                    transfer.ptr = descriptor;
+                    transfer.direction = DEVICE_TO_HOST;
+                    success = true;
+                }
+            }
+            break;
+
         default:
 #ifdef DEBUG
             printf("ERROR\r\n");
@@ -835,7 +853,7 @@
 }
 
 
-bool USBDevice::write(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize)
+bool USBDevice::write(uint8_t endpoint, const uint8_t * buffer, uint32_t size, uint32_t maxSize)
 {
     EP_STATUS result;
 
@@ -876,7 +894,7 @@
 }
 
 
-bool USBDevice::writeNB(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize)
+bool USBDevice::writeNB(uint8_t endpoint, const uint8_t * buffer, uint32_t size, uint32_t maxSize)
 {
     EP_STATUS result;
 
--- a/USBDevice/USBDevice.h	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBDevice.h	Wed Jul 25 14:04:48 2018 +0200
@@ -103,7 +103,7 @@
     * @param size the number of bytes to write
     * @param maxSize the maximum length that can be written on this endpoint
     */
-    bool write(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize);
+    bool write(uint8_t endpoint, const uint8_t * buffer, uint32_t size, uint32_t maxSize);
 
 
     /*
@@ -116,7 +116,7 @@
     * @param size the number of bytes to write
     * @param maxSize the maximum length that can be written on this endpoint
     */
-    bool writeNB(uint8_t endpoint, uint8_t * buffer, uint32_t size, uint32_t maxSize);
+    bool writeNB(uint8_t endpoint, const uint8_t * buffer, uint32_t size, uint32_t maxSize);
 
 
     /*
@@ -179,6 +179,13 @@
     virtual uint8_t * configurationDesc(){return NULL;};
 
     /*
+    * Get BOS descriptor
+    *
+    * @returns pointer to the BOS descriptor
+    */    
+    virtual uint8_t * bosDesc() { return nullptr; }
+
+    /*
     * Get string lang id descriptor
     *
     * @return pointer to the string lang id descriptor
--- a/USBDevice/USBEndpoints_STM32L1.h	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBEndpoints_STM32L1.h	Wed Jul 25 14:04:48 2018 +0200
@@ -46,22 +46,34 @@
 /* Generic endpoints - intended to be portable accross devices */
 /* and be suitable for simple USB devices. */
 
-/* Bulk endpoint */
+/* Bulk/CDC endpoint */
 #define EPBULK_OUT  (EP2OUT)
 #define EPBULK_IN   (EP2IN)
 #define EPBULK_OUT_callback   EP2_OUT_callback
 #define EPBULK_IN_callback    EP2_IN_callback
+#define EPCDC_OUT  (EP2OUT)
+#define EPCDC_IN   (EP2IN)
+#define EPCDC_OUT_callback   EP2_OUT_callback
+#define EPCDC_IN_callback    EP2_IN_callback
 /* Interrupt endpoint */
 #define EPINT_OUT   (EP1OUT)
 #define EPINT_IN    (EP1IN)
 #define EPINT_OUT_callback    EP1_OUT_callback
 #define EPINT_IN_callback     EP1_IN_callback
-/* Isochronous endpoint */
-#define EPISO_OUT   (EP3OUT)
+/* Isochronous endpoint - not used, replaced by WebUSB endpoint */
+/*#define EPISO_OUT   (EP3OUT)
 #define EPISO_IN    (EP3IN)
 #define EPISO_OUT_callback    EP3_OUT_callback
-#define EPISO_IN_callback     EP3_IN_callback
+#define EPISO_IN_callback     EP3_IN_callback*/
+
+/* WebUSB endpoint (bulk) */
+#define EPWEBUSB_OUT   (EP3OUT)
+#define EPWEBUSB_IN    (EP3IN)
+#define EPWEBUSB_OUT_callback    EP3_OUT_callback
+#define EPWEBUSB_IN_callback     EP3_IN_callback
 
 #define MAX_PACKET_SIZE_EPBULK  (MAX_PACKET_SIZE_EP2)
+#define MAX_PACKET_SIZE_EPCDC  (MAX_PACKET_SIZE_EP2)
 #define MAX_PACKET_SIZE_EPINT   (MAX_PACKET_SIZE_EP1)
 #define MAX_PACKET_SIZE_EPISO   (MAX_PACKET_SIZE_EP3_ISO)
+#define MAX_PACKET_SIZE_EPWEBUSB (MAX_PACKET_SIZE_EP3)
--- a/USBDevice/USBHAL.h	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL.h	Wed Jul 25 14:04:48 2018 +0200
@@ -51,7 +51,7 @@
     /* Other endpoints */
     EP_STATUS endpointRead(uint8_t endpoint, uint32_t maximumSize);
     EP_STATUS endpointReadResult(uint8_t endpoint, uint8_t *data, uint32_t *bytesRead);
-    EP_STATUS endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size);
+    EP_STATUS endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size);
     EP_STATUS endpointWriteResult(uint8_t endpoint);
     void stallEndpoint(uint8_t endpoint);
     void unstallEndpoint(uint8_t endpoint);
--- a/USBDevice/USBHAL_KL25Z.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_KL25Z.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -370,7 +370,7 @@
     return EP_COMPLETED;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size) {
     uint32_t idx, n;
     uint8_t * ep_buf;
 
--- a/USBDevice/USBHAL_LPC11U.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_LPC11U.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -386,7 +386,7 @@
     LPC_USB->DEVCMDSTAT = devCmdStat;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size) {
     uint32_t flags = 0;
     uint32_t bf;
 
--- a/USBDevice/USBHAL_LPC17.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_LPC17.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -472,7 +472,7 @@
     return EP_COMPLETED;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size) {
     if (getEndpointStallState(endpoint)) {
         return EP_STALLED;
     }
--- a/USBDevice/USBHAL_LPC40.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_LPC40.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -477,7 +477,7 @@
     return EP_COMPLETED;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size) {
     if (getEndpointStallState(endpoint)) {
         return EP_STALLED;
     }
--- a/USBDevice/USBHAL_Maxim.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_Maxim.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -305,7 +305,7 @@
     return EP_COMPLETED;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size)
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size)
 {
     uint8_t epnum = EP_NUM(endpoint);
 
--- a/USBDevice/USBHAL_RZ_A1H.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_RZ_A1H.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -1117,7 +1117,7 @@
 
 
 /*************************************************************************/
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size)
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size)
 {
     uint32_t pipe = EP2PIPE(endpoint);
     uint32_t pipe_size;
--- a/USBDevice/USBHAL_STM32F4.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_STM32F4.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -249,7 +249,7 @@
     return EP_COMPLETED;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size) {
     uint32_t epIndex = endpoint >> 1;
     OTG_FS->INEP_REGS[epIndex].DIEPTSIZ = (1 << 19) | // 1 packet
                                           (size << 0); // Size of packet
--- a/USBDevice/USBHAL_STM32L1.cpp	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBDevice/USBHAL_STM32L1.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -255,9 +255,9 @@
     return EP_COMPLETED;
 }
 
-EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
+EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, const uint8_t *data, uint32_t size) {
     core_util_critical_section_enter();
-    HAL_PCD_EP_Transmit(&hpcd_USB_FS, endpoint>>1, data, size);
+    HAL_PCD_EP_Transmit(&hpcd_USB_FS, endpoint>>1, const_cast<uint8_t*>(data), size);
     epComplete &= ~(1 << endpoint);
     core_util_critical_section_exit();
     return EP_PENDING;
--- a/USBSerial/CircBuffer.h	Fri Oct 27 17:22:45 2017 +0200
+++ b/USBSerial/CircBuffer.h	Wed Jul 25 14:04:48 2018 +0200
@@ -53,6 +53,79 @@
         return(!empty);
     };
 
+    void flush()
+    {
+        write = 0;
+        read = 0;
+    }
+
+    // Queue a block of data of blockSize items
+    void queue(const T *block, uint16_t blockSize)
+    {
+        if (blockSize >= size)
+        {
+            // Block is too big to fit in buffer, take the last size-1 items
+            block = &block[blockSize - (size-1)];
+            blockSize = size-1;
+        }
+
+        if (write + blockSize > size)
+        {
+            // Need to wrap around
+            std::memcpy(&buf[write], block, sizeof(T)*(size-write));
+            std::memcpy(buf, &block[size-write], sizeof(T)*(blockSize - (size-write)));
+        }
+        else
+        {
+            std::memcpy(&buf[write], block, sizeof(T)*blockSize);
+        }
+
+        // Update write position
+        uint16_t wasFree = available() - size - 1;
+        write = write + blockSize;
+        write %= size;
+        if (wasFree < blockSize)
+        {
+            // Update read position as well
+            read = write + 1;
+            read %= size;
+        }
+    }
+
+    // Dequeue a block of data of at most blockSize items, writing them into block
+    // Returns the number of items dequeued
+    uint16_t dequeue(T *block, uint16_t blockSize)
+    {
+        if (isEmpty())
+        {
+            return 0;
+        }
+
+        uint16_t isAvailable = available();
+        if (isAvailable < blockSize)
+        {
+            // Only return what we have
+            blockSize = isAvailable;
+        }
+
+        if (read + blockSize > size)
+        {
+            // Need to wrap around
+            std::memcpy(block, &buf[read], sizeof(T)*(size-read));
+            std::memcpy(&block[size-read], buf, sizeof(T)*(blockSize - (size-read)));
+        }
+        else
+        {
+            std::memcpy(block, &buf[read], sizeof(T)*blockSize);
+        }
+
+        // Update read position
+        read = read + blockSize;
+        read %= size;
+
+        return blockSize;
+    }
+
 private:
     volatile uint16_t write;
     volatile uint16_t read;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBSerial/USBWebUSBSerial.cpp	Wed Jul 25 14:04:48 2018 +0200
@@ -0,0 +1,703 @@
+#include "USBWebUSBSerial.h"
+#include "CriticalSectionLock.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();
+}
+
+USBWebUSBSerial::~USBWebUSBSerial()
+{
+    delete[] m_manufacturerStringDesc;
+    delete[] m_productStringDesc;
+    delete[] m_serialStringDesc;
+}
+
+void USBWebUSBSerial::USBCallback_busReset()
+{
+    m_terminalConnected = false;
+    m_pendingWrite = false;
+    setWebUSBMode(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;
+                    if (!m_webUSBMode)
+                    {
+                        m_pendingWrite = 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
+            setWebUSBMode(false);
+        }
+        else
+        {
+            // Enable WebUSB mode
+            setWebUSBMode(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;
+}
+
+void USBWebUSBSerial::writeToActiveEndpoint()
+{
+    CriticalSectionLock lock;
+    if(!configured() || m_outputBuffer.isEmpty())
+    {
+        m_pendingWrite = false;
+        return;
+    }
+
+    uint8_t buffer[MAX_PACKET_SIZE_EPBULK];
+    uint16_t size = m_outputBuffer.dequeue(buffer, MAX_PACKET_SIZE_EPBULK);
+
+    EP_STATUS result = endpointWrite(activeInEndpoint(), buffer, size);
+
+    if (result != EP_PENDING)
+    {
+        return;
+    }
+
+    m_lastWriteTime = osKernelGetTickCount();
+    m_pendingWrite = true;
+}
+
+bool USBWebUSBSerial::readActiveEP()
+{
+    CriticalSectionLock lock;
+    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;
+    }
+
+    m_inputBuffer.queue(buffer, size);
+
+    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::read(uint8_t *buf, uint16_t size)
+{
+    CriticalSectionLock lock;
+    return m_inputBuffer.dequeue(buf, size);
+}
+
+bool USBWebUSBSerial::writeBuffered(const uint8_t * buf, uint16_t size)
+{
+    CriticalSectionLock lock;
+    if(!m_terminalConnected && !m_webUSBMode)
+    {
+        return false;
+    }
+
+    m_outputBuffer.queue(buf, size);
+    if (!m_pendingWrite)
+    {
+        writeToActiveEndpoint();
+    }
+    else if (m_webUSBMode)
+    {
+        // Check if the write has timed out
+        if (timeSinceWrite() > WEBUSB_WRITE_TIMEOUT)
+        {
+            // Host is no longer reading WebUSB endpoint, assume the client is gone and go back to CDC mode
+            setWebUSBMode(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)
+{
+    CriticalSectionLock lock;
+    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;
+}
+
+bool USBWebUSBSerial::EPCDC_IN_callback()
+{
+    CriticalSectionLock lock;
+    if (!m_webUSBMode)
+    {
+        writeToActiveEndpoint();
+    }
+
+    return true;
+}
+
+bool USBWebUSBSerial::EPWEBUSB_IN_callback()
+{
+    CriticalSectionLock lock;
+    if (m_webUSBMode)
+    {
+        writeToActiveEndpoint();
+    }
+
+    return true;
+}
+
+uint8_t USBWebUSBSerial::available()
+{
+    CriticalSectionLock lock;
+    return m_inputBuffer.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;
+}
+
+void USBWebUSBSerial::setWebUSBMode(bool webUSBMode)
+{
+    CriticalSectionLock lock;
+    if (webUSBMode != m_webUSBMode)
+    {
+        m_webUSBMode = webUSBMode;
+        m_pendingWrite = false;
+
+        // Clear buffers to clean out any left over data
+        m_inputBuffer.flush();
+        m_outputBuffer.flush();
+    }
+}
+
+uint32_t USBWebUSBSerial::timeSinceWrite() const
+{
+    uint32_t currentTime = osKernelGetTickCount();
+    if (currentTime < m_lastWriteTime)
+    {
+        // Tick count has wrapped around and started from 0 again
+        return currentTime + (0xFFFFFFFF - m_lastWriteTime);
+    }
+    else
+    {
+        return currentTime - m_lastWriteTime;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBSerial/USBWebUSBSerial.h	Wed Jul 25 14:04:48 2018 +0200
@@ -0,0 +1,258 @@
+/* Copyright (c) 2010-2011 mbed.org, MIT License
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+* and associated documentation files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in all copies or
+* substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef USBWEBUSBSERIAL_H
+#define USBWEBUSBSERIAL_H
+
+#include <string>
+#include "USBDevice.h"
+#include "CircBuffer.h"
+#include "USBEndpoints.h"
+#include "USBDescriptor.h"
+#include "USBDevice_Types.h"
+
+#ifndef USBWEBUSBSERIAL_INPUT_BUFFER_SIZE
+#define USBWEBUSBSERIAL_INPUT_BUFFER_SIZE 128
+#endif
+
+#ifndef USBWEBUSBSERIAL_OUTPUT_BUFFER_SIZE
+#define USBWEBUSBSERIAL_OUTPUT_BUFFER_SIZE 512
+#endif
+
+/**
+* WebUSB/USBSerial composite device class
+* Handles outputting to standard CDC or WebUSB endpoint depending on mode
+*
+*/
+class USBWebUSBSerial: public USBDevice
+{
+public:
+
+    /**
+    *   Constructor
+    *
+    * @param vendor_id Your vendor_id
+    * @param product_id Your product_id
+    * @param product_release Your product_release (default: 0x0001)
+    * @param connect_blocking define if the connection must be blocked if USB not plugged in
+    *
+    */
+    USBWebUSBSerial(uint16_t vendor_id, uint16_t product_id, uint16_t product_release =  0x0001, bool connect_blocking = true);
+    virtual ~USBWebUSBSerial();
+
+    const std::string& manufacturerName() const { return m_manufacturerName; }
+    void setManufacturerName(const std::string &manufacturerName);
+
+    const std::string& productName() const { return m_productName; }
+    void setProductName(const std::string &productName);
+
+    const std::string& serialNumber() const { return m_serialNumber; }
+    void setSerialNumber(const std::string &serialNumber);
+
+    /**
+    * Read up to size bytes from input buffer
+    *
+    * @returns the number of bytes read
+    */
+
+    int read(uint8_t *buf, uint16_t size);
+
+    /**
+    * Check the number of bytes available.
+    *
+    * @returns the number of bytes available
+    */
+    uint8_t available();
+
+    /** Determine if there is a character available to read
+     *
+     *  @returns
+     *    1 if there is a character available to read,
+     *    0 otherwise
+     */
+    int readable() { return available() ? 1 : 0; }
+
+    /** Determine if there is space available to write a character
+     *
+     *  @returns
+     *    1 if there is space to write a character,
+     *    0 otherwise
+     */
+    int writeable() { return 1; } // Always return 1 since we use a circular buffer
+
+    /**
+    * Write data asynchronously using internal output buffer
+    *
+    * @param buf pointer to data 
+    * @param size size of the buffer. The maximum size of data is limited by the size of the internal buffer
+    *
+    * @returns true if successfull
+    */
+    bool writeBuffered(const uint8_t * buf, uint16_t size);
+
+    /**
+     *  Attach a member function to call when a packet is received.
+     *
+     *  @param tptr pointer to the object to call the member function on
+     *  @param mptr pointer to the member function to be called
+     */
+    template<typename T>
+    void attach(T* tptr, void (T::*mptr)(void))
+    {
+        if (mptr && tptr)
+        {
+            m_rx.attach(tptr, mptr);
+        }
+    }
+
+    /**
+     * Attach a callback called when a packet is received
+     *
+     * @param fptr function pointer
+     */
+    void attach(void (*fptr)(void))
+    {
+        if (fptr)
+        {
+            m_rx.attach(fptr);
+        }
+    }
+
+    /**
+     * Attach a callback to call when serial's settings are changed.
+     *
+     * @param fptr function pointer
+     */
+    void attach(void (*fptr)(int baud, int bits, int parity, int stop))
+    {
+        m_settingsChangedCallback = fptr;
+    }
+
+protected:
+
+    /*
+    * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength.
+    *
+    * @returns pointer to the device descriptor
+    */
+    virtual uint8_t * deviceDesc();
+
+    /*
+    * Get string manufacturer descriptor
+    *
+    * @returns pointer to the string manufacturer descriptor
+    */
+    virtual uint8_t * stringImanufacturerDesc();
+
+    /*
+    * Get string product descriptor
+    *
+    * @returns pointer to the string product descriptor
+    */
+    virtual uint8_t * stringIproductDesc();
+
+    /*
+    * Get string serial descriptor
+    *
+    * @returns pointer to the string serial descriptor
+    */
+    virtual uint8_t * stringIserialDesc();
+
+    /*
+    * Get configuration descriptor
+    *
+    * @returns pointer to the configuration descriptor
+    */
+    virtual uint8_t * configurationDesc();
+
+    /*
+    * Get BOS descriptor
+    *
+    * @returns pointer to the BOS descriptor
+    */    
+    virtual uint8_t * bosDesc();
+
+    /*
+    * Write from internal buffer to active endpoint
+    */
+    void writeToActiveEndpoint();
+
+    /*
+    * Read from the active endpoint into the internal buffer (non-blocking)
+    *
+    * @returns true if successful
+    */
+    bool readActiveEP();
+
+    /*
+    * Called by USBCallback_requestCompleted when CDC line coding is changed
+    * Warning: Called in ISR
+    *
+    * @param baud The baud rate
+    * @param bits The number of bits in a word (5-8)
+    * @param parity The parity
+    * @param stop The number of stop bits (1 or 2)
+    */
+    virtual void lineCodingChanged(int baud, int bits, int parity, int stop)
+    {
+        if (m_settingsChangedCallback)
+        {
+            m_settingsChangedCallback(baud, bits, parity, stop);
+        }
+    }
+
+    virtual bool USBCallback_request();
+    virtual void USBCallback_requestCompleted(uint8_t *buf, uint32_t length);
+    virtual bool USBCallback_setConfiguration(uint8_t configuration);
+    virtual void USBCallback_busReset(void);
+
+    virtual bool EPCDC_OUT_callback();
+    virtual bool EPWEBUSB_OUT_callback();
+    virtual bool EPOUTCallbackHandler(uint8_t endpoint);
+
+    virtual bool EPCDC_IN_callback();
+    virtual bool EPWEBUSB_IN_callback();
+
+private:
+    uint8_t activeOutEndpoint() const { return m_webUSBMode ? EPWEBUSB_OUT : EPCDC_OUT; }
+    uint8_t activeInEndpoint() const { return m_webUSBMode ? EPWEBUSB_IN : EPCDC_IN; }
+    void createDynamicDescriptors();
+    uint8_t *createStringDescriptor(const std::string &string) const;
+    void setWebUSBMode(bool webUSBMode);
+    uint32_t timeSinceWrite() const;
+
+    static uint8_t s_MSOS2Descriptor[];
+    volatile bool m_terminalConnected;
+    FunctionPointer m_rx;
+    CircBuffer<uint8_t,USBWEBUSBSERIAL_INPUT_BUFFER_SIZE> m_inputBuffer;
+    CircBuffer<uint8_t,USBWEBUSBSERIAL_OUTPUT_BUFFER_SIZE> m_outputBuffer;
+    void (*m_settingsChangedCallback)(int baud, int bits, int parity, int stop) = nullptr;
+
+    bool m_pendingWrite = false;
+    uint32_t m_lastWriteTime = 0;
+    std::string m_manufacturerName = "mbed.org";
+    uint8_t *m_manufacturerStringDesc = nullptr;
+    std::string m_productName = "CDC/WebUSB Device";
+    uint8_t *m_productStringDesc = nullptr;
+    std::string m_serialNumber = "0123456789";
+    uint8_t *m_serialStringDesc = nullptr;
+
+    bool m_webUSBMode = false;
+};
+
+#endif