\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(...)\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 (txbuf.free())\r
- txbuf.queue(c);\r
+ if (!attached)\r
+ return 1;\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
{\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
}\r
\r
-uint16_t USBSerial::writeBlock(uint8_t * buf, uint16_t size)\r
+int USBSerial::puts(const char *str)\r
+{\r
+ if (!attached)\r
+ return strlen(str);\r
+ int i = 0;\r
+ while (*str)\r
+ {\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(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
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
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
-// 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
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
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
- this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message );\r
+ iprintf("USBSerial Received: %s\n", message.message.c_str());\r
+ THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );\r
return;\r
}\r
else\r
\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