Commit | Line | Data |
---|---|---|
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 | 32 | USBSerial::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 |
40 | void 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 |
49 | int 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 | |
60 | int 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 |
88 | int 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 | 106 | uint16_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 | |
125 | bool 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 | |
172 | bool 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 |
248 | uint8_t USBSerial::available()\r |
249 | {\r | |
47b0bbd2 MM |
250 | return rxbuf.available();\r |
251 | }\r | |
ca5e9a57 MM |
252 | \r |
253 | void USBSerial::on_module_loaded()\r | |
254 | {\r | |
824de637 | 255 | this->register_for_event(ON_MAIN_LOOP);\r |
ca5e9a57 MM |
256 | }\r |
257 | \r | |
824de637 | 258 | void 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 | |
304 | void USBSerial::on_attach()\r | |
305 | {\r | |
654286ea | 306 | attach = true;\r |
ca5e9a57 MM |
307 | }\r |
308 | \r | |
309 | void USBSerial::on_detach()\r | |
310 | {\r | |
654286ea | 311 | attach = false;\r |
ca5e9a57 | 312 | }\r |