\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 + 8), txbuf(128 + 8)\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
if (!attached)\r
return 1;\r
- if (c == '\r')\r
- return 1;\r
ensure_tx_space(1);\r
txbuf.queue(c);\r
\r
usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r
iprintf("rxbuf has room for another packet, interrupt enabled\n");\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
while (*str)\r
{\r
ensure_tx_space(1);\r
- if ((*str != '\r'))\r
- txbuf.queue(*str);\r
+ txbuf.queue(*str);\r
if ((txbuf.available() % 64) == 0)\r
usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
i++;\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
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
{\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
+ // 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
\r
void USBSerial::on_main_loop(void *argument)\r
{\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
- kernel->streams->append_stream(this);\r
- writeBlock((uint8_t *) "Smoothie\nok\n", 12);\r
+ THEKERNEL->streams->append_stream(this);\r
+ writeBlock((const uint8_t *) "Smoothie\nok\n", 12);\r
}\r
else\r
{\r
attached = false;\r
- kernel->streams->remove_stream(this);\r
+ THEKERNEL->streams->remove_stream(this);\r
txbuf.flush();\r
rxbuf.flush();\r
nl_in_rx = 0;\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