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

Fork of STM32_USBDevice by Bradley Scott

USBSerial/USBWebUSBSerial.cpp

Committer:
Troels Nilsson
Date:
2018-07-25
Revision:
79:d28244984385
Parent:
78:ba3f68a86e6d

File content as of revision 79:d28244984385:

#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;
    }
}