Use static kernel singleton pointer instead of per-class instance pointer
[clinton/Smoothieware.git] / src / libs / USBDevice / USBSerial / USBSerial.cpp
CommitLineData
47b0bbd2
MM
1/* Copyright (c) 2010-2011 mbed.org, MIT License\r
2*\r
3* Permission is hereby granted, free of charge, to any person obtaining a copy of this software\r
4* and associated documentation files (the "Software"), to deal in the Software without\r
5* restriction, including without limitation the rights to use, copy, modify, merge, publish,\r
6* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the\r
7* Software is furnished to do so, subject to the following conditions:\r
8*\r
9* The above copyright notice and this permission notice shall be included in all copies or\r
10* substantial portions of the Software.\r
11*\r
12* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING\r
13* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
14* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\r
15* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
16* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
17*/\r
18\r
19#include <cstdint>\r
20#include <cstdio>\r
21\r
22#include "USBSerial.h"\r
23\r
ca5e9a57
MM
24#include "libs/Kernel.h"\r
25#include "libs/SerialMessage.h"\r
26\r
47b0bbd2
MM
27// extern void setled(int, bool);\r
28#define setled(a, b) do {} while (0)\r
29\r
1e9a12f7 30#define iprintf(...) do { } while (0)\r
47b0bbd2 31\r
8fbff64b 32USBSerial::USBSerial(USB *u): USBCDC(u), rxbuf(256 + 8), txbuf(128 + 8)\r
47b0bbd2
MM
33{\r
34 usb = u;\r
ca5e9a57 35 nl_in_rx = 0;\r
654286ea 36 attach = attached = false;\r
0e91ff91 37 flush_to_nl = false;\r
47b0bbd2
MM
38}\r
39\r
6cf52cc2
MM
40void USBSerial::ensure_tx_space(int space)\r
41{\r
42 while (txbuf.free() < space)\r
43 {\r
44 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
45 usb->usbisr();\r
46 }\r
47}\r
48\r
47b0bbd2
MM
49int USBSerial::_putc(int c)\r
50{\r
654286ea
MM
51 if (!attached)\r
52 return 1;\r
6cf52cc2
MM
53 ensure_tx_space(1);\r
54 txbuf.queue(c);\r
47b0bbd2
MM
55\r
56 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
47b0bbd2
MM
57 return 1;\r
58}\r
59\r
60int USBSerial::_getc()\r
61{\r
654286ea
MM
62 if (!attached)\r
63 return 0;\r
ca5e9a57 64 uint8_t c = 0;\r
47b0bbd2
MM
65 setled(4, 1); while (rxbuf.isEmpty()); setled(4, 0);\r
66 rxbuf.dequeue(&c);\r
67 if (rxbuf.free() == MAX_PACKET_SIZE_EPBULK)\r
68 {\r
69 usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r
70 iprintf("rxbuf has room for another packet, interrupt enabled\n");\r
47b0bbd2 71 }\r
d56ad249
MM
72 else if ((rxbuf.free() < MAX_PACKET_SIZE_EPBULK) && (nl_in_rx == 0))\r
73 {\r
74 // handle potential deadlock where a short line, and the beginning of a very long line are bundled in one usb packet\r
75 rxbuf.flush();\r
76 flush_to_nl = true;\r
77\r
78 usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r
79 iprintf("rxbuf has room for another packet, interrupt enabled\n");\r
80 }\r
6de6340d 81 if (nl_in_rx > 0)\r
9afe7d79 82 if (c == '\n' || c == '\r')\r
6de6340d 83 nl_in_rx--;\r
ca5e9a57 84\r
47b0bbd2
MM
85 return c;\r
86}\r
87\r
99b9ed8a
MM
88int USBSerial::puts(const char *str)\r
89{\r
654286ea
MM
90 if (!attached)\r
91 return strlen(str);\r
99b9ed8a
MM
92 int i = 0;\r
93 while (*str)\r
94 {\r
6cf52cc2 95 ensure_tx_space(1);\r
ed4ab0dd 96 txbuf.queue(*str);\r
77b8129c
MM
97 if ((txbuf.available() % 64) == 0)\r
98 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
99b9ed8a
MM
99 i++;\r
100 str++;\r
101 }\r
aff8aca7 102 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
99b9ed8a
MM
103 return i;\r
104}\r
105\r
9bba3e2d 106uint16_t USBSerial::writeBlock(const uint8_t * buf, uint16_t size)\r
47b0bbd2 107{\r
654286ea
MM
108 if (!attached)\r
109 return size;\r
47b0bbd2
MM
110 if (size > txbuf.free())\r
111 {\r
112 size = txbuf.free();\r
113 }\r
114 if (size > 0)\r
115 {\r
116 for (uint8_t i = 0; i < size; i++)\r
117 {\r
118 txbuf.queue(buf[i]);\r
119 }\r
120 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r
121 }\r
122 return size;\r
123}\r
124\r
125bool USBSerial::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus)\r
126{\r
127 /*\r
128 * Called in ISR context\r
129 */\r
130\r
131// static bool needToSendNull = false;\r
132\r
133 bool r = true;\r
134\r
135 if (bEP != CDC_BulkIn.bEndpointAddress)\r
136 return false;\r
137\r
138 iprintf("USBSerial:EpIn: 0x%02X\n", bEPStatus);\r
139\r
140 uint8_t b[MAX_PACKET_SIZE_EPBULK];\r
141\r
142 int l = txbuf.available();\r
143 iprintf("%d bytes queued\n", l);\r
144 if (l > 0)\r
145 {\r
146 if (l > MAX_PACKET_SIZE_EPBULK)\r
147 l = MAX_PACKET_SIZE_EPBULK;\r
148 iprintf("Sending %d bytes:\n\t", l);\r
149 int i;\r
150 for (i = 0; i < l; i++) {\r
151 txbuf.dequeue(&b[i]);\r
152 if (b[i] >= 32 && b[i] < 128)\r
153 iprintf("%c", b[i]);\r
154 else {\r
155 iprintf("\\x%02X", b[i]);\r
156 }\r
157 }\r
158 iprintf("\nSending...\n");\r
159 send(b, l);\r
160 iprintf("Sent\n");\r
47b0bbd2
MM
161 if (txbuf.available() == 0)\r
162 r = false;\r
163 }\r
164 else\r
165 {\r
f455ce87 166 r = false;\r
47b0bbd2
MM
167 }\r
168 iprintf("USBSerial:EpIn Complete\n");\r
169 return r;\r
170}\r
171\r
172bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)\r
173{\r
174 /*\r
175 * Called in ISR context\r
176 */\r
177\r
178 bool r = true;\r
179\r
180 iprintf("USBSerial:EpOut\n");\r
181 if (bEP != CDC_BulkOut.bEndpointAddress)\r
182 return false;\r
183\r
184 if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)\r
185 {\r
186// usb->endpointSetInterrupt(bEP, false);\r
187 return false;\r
188 }\r
189\r
190 uint8_t c[MAX_PACKET_SIZE_EPBULK];\r
191 uint32_t size = 64;\r
192\r
193 //we read the packet received and put it on the circular buffer\r
194 readEP(c, &size);\r
195 iprintf("Read %ld bytes:\n\t", size);\r
196 for (uint8_t i = 0; i < size; i++) {\r
0e91ff91
MM
197\r
198 if (flush_to_nl == false)\r
199 rxbuf.queue(c[i]);\r
200\r
47b0bbd2 201 if (c[i] >= 32 && c[i] < 128)\r
ca5e9a57 202 {\r
47b0bbd2 203 iprintf("%c", c[i]);\r
ca5e9a57
MM
204 }\r
205 else\r
206 {\r
47b0bbd2
MM
207 iprintf("\\x%02X", c[i]);\r
208 }\r
0e91ff91 209\r
9afe7d79 210 if (c[i] == '\n' || c[i] == '\r')\r
0e91ff91
MM
211 {\r
212 if (flush_to_nl)\r
213 flush_to_nl = false;\r
214 else\r
215 nl_in_rx++;\r
216 }\r
217 else if (rxbuf.isFull() && (nl_in_rx == 0))\r
218 {\r
219 // to avoid a deadlock with very long lines, we must dump the buffer\r
220 // and continue flushing to the next newline\r
221 rxbuf.flush();\r
222 flush_to_nl = true;\r
223 }\r
47b0bbd2
MM
224 }\r
225 iprintf("\nQueued, %d empty\n", rxbuf.free());\r
226\r
227 if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)\r
228 {\r
0e91ff91 229 // if buffer is full, stall endpoint, do not accept more data\r
47b0bbd2 230 r = false;\r
0e91ff91
MM
231\r
232 if (nl_in_rx == 0)\r
233 {\r
234 // we have to check for long line deadlock here too\r
235 flush_to_nl = true;\r
236 rxbuf.flush();\r
237\r
238 // and since our buffer is empty, we can accept more data\r
239 r = true;\r
240 }\r
47b0bbd2
MM
241 }\r
242\r
243 usb->readStart(CDC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);\r
244 iprintf("USBSerial:EpOut Complete\n");\r
245 return r;\r
246}\r
247\r
ca5e9a57
MM
248uint8_t USBSerial::available()\r
249{\r
47b0bbd2
MM
250 return rxbuf.available();\r
251}\r
ca5e9a57
MM
252\r
253void USBSerial::on_module_loaded()\r
254{\r
824de637 255 this->register_for_event(ON_MAIN_LOOP);\r
ca5e9a57
MM
256}\r
257\r
824de637 258void USBSerial::on_main_loop(void *argument)\r
ca5e9a57 259{\r
8922c191
MM
260 // apparently some OSes don't assert DTR when a program opens the port\r
261 if (available() && !attach)\r
262 attach = true;\r
263\r
654286ea
MM
264 if (attach != attached)\r
265 {\r
266 if (attach)\r
267 {\r
268 attached = true;\r
269 kernel->streams->append_stream(this);\r
9bba3e2d 270 writeBlock((const uint8_t *) "Smoothie\nok\n", 12);\r
654286ea
MM
271 }\r
272 else\r
273 {\r
274 attached = false;\r
275 kernel->streams->remove_stream(this);\r
276 txbuf.flush();\r
277 rxbuf.flush();\r
278 nl_in_rx = 0;\r
279 }\r
280 }\r
ca5e9a57
MM
281 if (nl_in_rx)\r
282 {\r
283 string received;\r
284 while (available())\r
285 {\r
286 char c = _getc();\r
9afe7d79 287 if( c == '\n' || c == '\r')\r
ca5e9a57
MM
288 {\r
289 struct SerialMessage message;\r
290 message.message = received;\r
291 message.stream = this;\r
99b9ed8a 292 iprintf("USBSerial Received: %s\n", message.message.c_str());\r
314ab8f7 293 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );\r
ca5e9a57
MM
294 return;\r
295 }\r
296 else\r
297 {\r
298 received += c;\r
299 }\r
300 }\r
301 }\r
302}\r
303\r
304void USBSerial::on_attach()\r
305{\r
654286ea 306 attach = true;\r
ca5e9a57
MM
307}\r
308\r
309void USBSerial::on_detach()\r
310{\r
654286ea 311 attach = false;\r
ca5e9a57 312}\r