Allow TABS in config
[clinton/Smoothieware.git] / src / libs / USBDevice / USBSerial / USBSerial.cpp
index 6520196..f01c151 100644 (file)
 \r
 #include "libs/Kernel.h"\r
 #include "libs/SerialMessage.h"\r
+#include "StreamOutputPool.h"\r
 \r
 // extern void setled(int, bool);\r
 #define setled(a, b) do {} while (0)\r
 \r
 #define iprintf(...) do { } while (0)\r
 \r
-USBSerial::USBSerial(USB *u): USBCDC(u), rxbuf(128), txbuf(128)\r
+USBSerial::USBSerial(USB *u): USBCDC(u), rxbuf(256 + 8), txbuf(128 + 8)\r
 {\r
     usb = u;\r
     nl_in_rx = 0;\r
+    attach = attached = false;\r
+    flush_to_nl = false;\r
+}\r
+\r
+void USBSerial::ensure_tx_space(int space)\r
+{\r
+    while (txbuf.free() < space)\r
+    {\r
+        usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
+        usb->usbisr();\r
+    }\r
 }\r
 \r
 int USBSerial::_putc(int c)\r
 {\r
-//     send((uint8_t *)&c, 1);\r
-    if (c == '\r')\r
+    if (!attached)\r
         return 1;\r
-    if (txbuf.free())\r
-        txbuf.queue(c);\r
+    ensure_tx_space(1);\r
+    txbuf.queue(c);\r
 \r
     usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
-//     usb->endpointTriggerInterrupt(CDC_BulkIn.bEndpointAddress);\r
     return 1;\r
 }\r
 \r
 int USBSerial::_getc()\r
 {\r
+    if (!attached)\r
+        return 0;\r
     uint8_t c = 0;\r
     setled(4, 1); while (rxbuf.isEmpty()); setled(4, 0);\r
     rxbuf.dequeue(&c);\r
@@ -57,10 +69,18 @@ int USBSerial::_getc()
     {\r
         usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r
         iprintf("rxbuf has room for another packet, interrupt enabled\n");\r
-//         usb->endpointTriggerInterrupt(CDC_BulkOut.bEndpointAddress);\r
+    }\r
+    else if ((rxbuf.free() < MAX_PACKET_SIZE_EPBULK) && (nl_in_rx == 0))\r
+    {\r
+        // handle potential deadlock where a short line, and the beginning of a very long line are bundled in one usb packet\r
+        rxbuf.flush();\r
+        flush_to_nl = true;\r
+\r
+        usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r
+        iprintf("rxbuf has room for another packet, interrupt enabled\n");\r
     }\r
     if (nl_in_rx > 0)\r
-        if (c == '\n')\r
+        if (c == '\n' || c == '\r')\r
             nl_in_rx--;\r
 \r
     return c;\r
@@ -68,18 +88,26 @@ int USBSerial::_getc()
 \r
 int USBSerial::puts(const char *str)\r
 {\r
+    if (!attached)\r
+        return strlen(str);\r
     int i = 0;\r
     while (*str)\r
     {\r
-        _putc(*str);\r
+        ensure_tx_space(1);\r
+        txbuf.queue(*str);\r
+        if ((txbuf.available() % 64) == 0)\r
+            usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
         i++;\r
         str++;\r
     }\r
+    usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
     return i;\r
 }\r
 \r
-uint16_t USBSerial::writeBlock(uint8_t * buf, uint16_t size)\r
+uint16_t USBSerial::writeBlock(const uint8_t * buf, uint16_t size)\r
 {\r
+    if (!attached)\r
+        return size;\r
     if (size > txbuf.free())\r
     {\r
         size = txbuf.free();\r
@@ -131,23 +159,12 @@ bool USBSerial::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus)
         iprintf("\nSending...\n");\r
         send(b, l);\r
         iprintf("Sent\n");\r
-//         if (l == 64 && txbuf.available() == 0)\r
-//             needToSendNull = true;\r
         if (txbuf.available() == 0)\r
             r = false;\r
     }\r
     else\r
     {\r
-//         if (needToSendNull)\r
-//         {\r
-//             send(NULL, 0);\r
-//             needToSendNull = false;\r
-//         }\r
-//         else\r
-//         {\r
-            r = false;\r
-//         }\r
-//         usb->endpointSetInterrupt(bEP, false);\r
+        r = false;\r
     }\r
     iprintf("USBSerial:EpIn Complete\n");\r
     return r;\r
@@ -178,7 +195,10 @@ bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)
     readEP(c, &size);\r
     iprintf("Read %ld bytes:\n\t", size);\r
     for (uint8_t i = 0; i < size; i++) {\r
-        rxbuf.queue(c[i]);\r
+\r
+        if (flush_to_nl == false)\r
+            rxbuf.queue(c[i]);\r
+\r
         if (c[i] >= 32 && c[i] < 128)\r
         {\r
             iprintf("%c", c[i]);\r
@@ -187,15 +207,38 @@ bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)
         {\r
             iprintf("\\x%02X", c[i]);\r
         }\r
-        if (c[i] == '\n')\r
-            nl_in_rx++;\r
+\r
+        if (c[i] == '\n' || c[i] == '\r')\r
+        {\r
+            if (flush_to_nl)\r
+                flush_to_nl = false;\r
+            else\r
+                nl_in_rx++;\r
+        }\r
+        else if (rxbuf.isFull() && (nl_in_rx == 0))\r
+        {\r
+            // to avoid a deadlock with very long lines, we must dump the buffer\r
+            // and continue flushing to the next newline\r
+            rxbuf.flush();\r
+            flush_to_nl = true;\r
+        }\r
     }\r
     iprintf("\nQueued, %d empty\n", rxbuf.free());\r
 \r
     if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)\r
     {\r
-//         usb->endpointSetInterrupt(bEP, false);\r
+        // if buffer is full, stall endpoint, do not accept more data\r
         r = false;\r
+\r
+        if (nl_in_rx == 0)\r
+        {\r
+            // we have to check for long line deadlock here too\r
+            flush_to_nl = true;\r
+            rxbuf.flush();\r
+\r
+            // and since our buffer is empty, we can accept more data\r
+            r = true;\r
+        }\r
     }\r
 \r
     usb->readStart(CDC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);\r
@@ -203,28 +246,6 @@ bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)
     return r;\r
 }\r
 \r
-/*\r
-bool USBSerial::EpCallback(uint8_t bEP, uint8_t bEPStatus) {\r
-    if (bEP == CDC_BulkOut.bEndpointAddress) {\r
-        uint8_t c[65];\r
-        uint32_t size = 0;\r
-\r
-        //we read the packet received and put it on the circular buffer\r
-        readEP(c, &size);\r
-        for (uint8_t i = 0; i < size; i++) {\r
-            buf.queue(c[i]);\r
-        }\r
-\r
-        //call a potential handler\r
-        rx.call();\r
-\r
-        // We reactivate the endpoint to receive next characters\r
-        usb->readStart(CDC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);\r
-        return true;\r
-    }\r
-    return false;\r
-}*/\r
-\r
 uint8_t USBSerial::available()\r
 {\r
     return rxbuf.available();\r
@@ -233,25 +254,44 @@ uint8_t USBSerial::available()
 void USBSerial::on_module_loaded()\r
 {\r
     this->register_for_event(ON_MAIN_LOOP);\r
-//     this->kernel->streams->append_stream(this);\r
 }\r
 \r
 void USBSerial::on_main_loop(void *argument)\r
 {\r
-//     this->kernel->streams->printf("!");\r
+    // apparently some OSes don't assert DTR when a program opens the port\r
+    if (available() && !attach)\r
+        attach = true;\r
+\r
+    if (attach != attached)\r
+    {\r
+        if (attach)\r
+        {\r
+            attached = true;\r
+            THEKERNEL->streams->append_stream(this);\r
+            writeBlock((const uint8_t *) "Smoothie\nok\n", 12);\r
+        }\r
+        else\r
+        {\r
+            attached = false;\r
+            THEKERNEL->streams->remove_stream(this);\r
+            txbuf.flush();\r
+            rxbuf.flush();\r
+            nl_in_rx = 0;\r
+        }\r
+    }\r
     if (nl_in_rx)\r
     {\r
         string received;\r
         while (available())\r
         {\r
             char c = _getc();\r
-            if( c == '\n' )\r
+            if( c == '\n' || c == '\r')\r
             {\r
                 struct SerialMessage message;\r
                 message.message = received;\r
                 message.stream = this;\r
                 iprintf("USBSerial Received: %s\n", message.message.c_str());\r
-                this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message );\r
+                THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );\r
                 return;\r
             }\r
             else\r
@@ -264,14 +304,10 @@ void USBSerial::on_main_loop(void *argument)
 \r
 void USBSerial::on_attach()\r
 {\r
-    this->kernel->streams->append_stream(this);\r
-    writeBlock((uint8_t *) "Smoothie\nok\n", 12);\r
+    attach = true;\r
 }\r
 \r
 void USBSerial::on_detach()\r
 {\r
-    this->kernel->streams->remove_stream(this);\r
-    txbuf.flush();\r
-    rxbuf.flush();\r
-    nl_in_rx = 0;\r
+    attach = false;\r
 }\r