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

Fork of STM32_USBDevice by Bradley Scott

Branch:
feature_WebUSB
Revision:
77:a98f786d05d4
Parent:
76:eef92651f52f
Child:
78:ba3f68a86e6d
--- a/USBSerial/USBWebUSBSerial.cpp	Thu Jul 19 12:57:27 2018 +0200
+++ b/USBSerial/USBWebUSBSerial.cpp	Tue Jul 24 13:08:29 2018 +0200
@@ -1,4 +1,5 @@
 #include "USBWebUSBSerial.h"
+#include "CriticalSectionLock.h"
 
 static uint8_t cdc_line_coding[7]= {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08};
 
@@ -115,7 +116,8 @@
 void USBWebUSBSerial::USBCallback_busReset()
 {
     m_terminalConnected = false;
-    m_webUSBMode = false;
+    m_pendingWrite = false;
+    setWebUSBMode(false);
 }
 
 bool USBWebUSBSerial::USBCallback_request()
@@ -146,6 +148,10 @@
                     m_terminalConnected = true;
                 } else {
                     m_terminalConnected = false;
+                    if (!m_webUSBMode)
+                    {
+                        m_pendingWrite = false;
+                    }
                 }
                 success = true;
                 break;
@@ -166,12 +172,12 @@
         if (transfer->setup.wValue == 0)
         {
             // Disable WebUSB mode
-            m_webUSBMode = false;
+            setWebUSBMode(false);
         }
         else
         {
             // Enable WebUSB mode
-            m_webUSBMode = true;
+            setWebUSBMode(true);
         }
         success = true;
     }
@@ -239,49 +245,32 @@
     return true;
 }
 
-bool USBWebUSBSerial::send(const uint8_t * buffer, uint32_t size)
+void USBWebUSBSerial::writeToActiveEndpoint()
 {
-    if (!m_webUSBMode)
-    {
-        return USBDevice::write(EPCDC_IN, buffer, size, MAX_PACKET_SIZE_EPBULK);
-    }
-    else
+    CriticalSectionLock lock;
+    if(!configured() || m_outputBuffer.isEmpty())
     {
-        EP_STATUS result;
-        if (size > MAX_PACKET_SIZE_EPBULK)
-        {
-            return false;
-        }
-
-        if(!configured()) {
-            return false;
-        }
-
-        result = endpointWrite(EPWEBUSB_IN, buffer, size);
+        m_pendingWrite = false;
+        return;
+    }
 
-        if (result != EP_PENDING)
-        {
-            return false;
-        }
+    uint8_t buffer[MAX_PACKET_SIZE_EPBULK];
+    uint16_t size = m_outputBuffer.dequeue(buffer, MAX_PACKET_SIZE_EPBULK);
 
-        // Wait for completion (or until timeout is reached)
-        uint32_t tstart = osKernelGetTickCount();
-        do {
-            result = endpointWriteResult(EPWEBUSB_IN);
+    EP_STATUS result = endpointWrite(activeInEndpoint(), buffer, size);
 
-            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);
+    if (result != EP_PENDING)
+    {
+        return;
+    }
 
-        return (result == EP_COMPLETED);
-    }
+    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))
@@ -293,10 +282,7 @@
         return false;
     }
 
-    for (uint32_t i = 0; i < size; i++)
-    {
-        m_buffer.queue(buffer[i]);
-    }
+    m_inputBuffer.queue(buffer, size);
 
     if (size != 0 && activeOutEndpoint() == EPCDC_OUT)
     {
@@ -527,36 +513,45 @@
 
 int USBWebUSBSerial::_putc(int c)
 {
-    if (!m_terminalConnected && !m_webUSBMode)
+    if (!writeBuffered((uint8_t *)&c, 1))
     {
         return 0;
     }
-    send((uint8_t *)&c, 1);
     return 1;
 }
 
 int USBWebUSBSerial::_getc()
 {
     uint8_t c = 0;
-    while (m_buffer.isEmpty());
-    m_buffer.dequeue(&c);
+    while (!available());
+    CriticalSectionLock lock;
+    m_inputBuffer.dequeue(&c);
     return c;
 }
 
-bool USBWebUSBSerial::writeBlock(const uint8_t * buf, uint16_t size)
+bool USBWebUSBSerial::writeBuffered(const uint8_t * buf, uint16_t size)
 {
+    CriticalSectionLock lock;
     if(!m_terminalConnected && !m_webUSBMode)
     {
         return false;
     }
-    if(size > MAX_PACKET_SIZE_EPBULK)
+
+    m_outputBuffer.queue(buf, size);
+    if (!m_pendingWrite)
     {
-        return false;
+        writeToActiveEndpoint();
     }
-    if(!send(buf, size))
+    else if (m_webUSBMode)
     {
-        return false;
+        // 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;
 }
 
@@ -574,6 +569,7 @@
 
 bool USBWebUSBSerial::EPOUTCallbackHandler(uint8_t endpoint)
 {
+    CriticalSectionLock lock;
     if (endpoint == activeOutEndpoint())
     {
         return readActiveEP();
@@ -601,9 +597,32 @@
     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()
 {
-    return m_buffer.available();
+    CriticalSectionLock lock;
+    return m_inputBuffer.available();
 }
 
 void USBWebUSBSerial::setManufacturerName(const std::string &manufacturerName)
@@ -659,4 +678,32 @@
         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