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 | |
61134a65 | 26 | #include "StreamOutputPool.h"\r |
ca5e9a57 | 27 | \r |
47b0bbd2 MM |
28 | // extern void setled(int, bool);\r |
29 | #define setled(a, b) do {} while (0)\r | |
30 | \r | |
1e9a12f7 | 31 | #define iprintf(...) do { } while (0)\r |
47b0bbd2 | 32 | \r |
8fbff64b | 33 | USBSerial::USBSerial(USB *u): USBCDC(u), rxbuf(256 + 8), txbuf(128 + 8)\r |
47b0bbd2 MM |
34 | {\r |
35 | usb = u;\r | |
ca5e9a57 | 36 | nl_in_rx = 0;\r |
654286ea | 37 | attach = attached = false;\r |
0e91ff91 | 38 | flush_to_nl = false;\r |
47b0bbd2 MM |
39 | }\r |
40 | \r | |
6cf52cc2 MM |
41 | void USBSerial::ensure_tx_space(int space)\r |
42 | {\r | |
43 | while (txbuf.free() < space)\r | |
44 | {\r | |
45 | usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r | |
46 | usb->usbisr();\r | |
47 | }\r | |
48 | }\r | |
49 | \r | |
47b0bbd2 MM |
50 | int USBSerial::_putc(int c)\r |
51 | {\r | |
654286ea MM |
52 | if (!attached)\r |
53 | return 1;\r | |
6cf52cc2 MM |
54 | ensure_tx_space(1);\r |
55 | txbuf.queue(c);\r | |
47b0bbd2 MM |
56 | \r |
57 | usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r | |
47b0bbd2 MM |
58 | return 1;\r |
59 | }\r | |
60 | \r | |
61 | int USBSerial::_getc()\r | |
62 | {\r | |
654286ea MM |
63 | if (!attached)\r |
64 | return 0;\r | |
ca5e9a57 | 65 | uint8_t c = 0;\r |
47b0bbd2 MM |
66 | setled(4, 1); while (rxbuf.isEmpty()); setled(4, 0);\r |
67 | rxbuf.dequeue(&c);\r | |
68 | if (rxbuf.free() == MAX_PACKET_SIZE_EPBULK)\r | |
69 | {\r | |
70 | usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r | |
71 | iprintf("rxbuf has room for another packet, interrupt enabled\n");\r | |
47b0bbd2 | 72 | }\r |
d56ad249 MM |
73 | else if ((rxbuf.free() < MAX_PACKET_SIZE_EPBULK) && (nl_in_rx == 0))\r |
74 | {\r | |
75 | // handle potential deadlock where a short line, and the beginning of a very long line are bundled in one usb packet\r | |
76 | rxbuf.flush();\r | |
77 | flush_to_nl = true;\r | |
78 | \r | |
79 | usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);\r | |
80 | iprintf("rxbuf has room for another packet, interrupt enabled\n");\r | |
81 | }\r | |
6de6340d | 82 | if (nl_in_rx > 0)\r |
9afe7d79 | 83 | if (c == '\n' || c == '\r')\r |
6de6340d | 84 | nl_in_rx--;\r |
ca5e9a57 | 85 | \r |
47b0bbd2 MM |
86 | return c;\r |
87 | }\r | |
88 | \r | |
99b9ed8a MM |
89 | int USBSerial::puts(const char *str)\r |
90 | {\r | |
654286ea MM |
91 | if (!attached)\r |
92 | return strlen(str);\r | |
99b9ed8a MM |
93 | int i = 0;\r |
94 | while (*str)\r | |
95 | {\r | |
6cf52cc2 | 96 | ensure_tx_space(1);\r |
ed4ab0dd | 97 | txbuf.queue(*str);\r |
77b8129c MM |
98 | if ((txbuf.available() % 64) == 0)\r |
99 | usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r | |
99b9ed8a MM |
100 | i++;\r |
101 | str++;\r | |
102 | }\r | |
aff8aca7 | 103 | usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r |
99b9ed8a MM |
104 | return i;\r |
105 | }\r | |
106 | \r | |
9bba3e2d | 107 | uint16_t USBSerial::writeBlock(const uint8_t * buf, uint16_t size)\r |
47b0bbd2 | 108 | {\r |
654286ea MM |
109 | if (!attached)\r |
110 | return size;\r | |
47b0bbd2 MM |
111 | if (size > txbuf.free())\r |
112 | {\r | |
113 | size = txbuf.free();\r | |
114 | }\r | |
115 | if (size > 0)\r | |
116 | {\r | |
117 | for (uint8_t i = 0; i < size; i++)\r | |
118 | {\r | |
119 | txbuf.queue(buf[i]);\r | |
120 | }\r | |
121 | usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);\r | |
122 | }\r | |
123 | return size;\r | |
124 | }\r | |
125 | \r | |
126 | bool USBSerial::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus)\r | |
127 | {\r | |
128 | /*\r | |
129 | * Called in ISR context\r | |
130 | */\r | |
131 | \r | |
132 | // static bool needToSendNull = false;\r | |
133 | \r | |
134 | bool r = true;\r | |
135 | \r | |
136 | if (bEP != CDC_BulkIn.bEndpointAddress)\r | |
137 | return false;\r | |
138 | \r | |
139 | iprintf("USBSerial:EpIn: 0x%02X\n", bEPStatus);\r | |
140 | \r | |
141 | uint8_t b[MAX_PACKET_SIZE_EPBULK];\r | |
142 | \r | |
143 | int l = txbuf.available();\r | |
144 | iprintf("%d bytes queued\n", l);\r | |
145 | if (l > 0)\r | |
146 | {\r | |
147 | if (l > MAX_PACKET_SIZE_EPBULK)\r | |
148 | l = MAX_PACKET_SIZE_EPBULK;\r | |
149 | iprintf("Sending %d bytes:\n\t", l);\r | |
150 | int i;\r | |
151 | for (i = 0; i < l; i++) {\r | |
152 | txbuf.dequeue(&b[i]);\r | |
153 | if (b[i] >= 32 && b[i] < 128)\r | |
154 | iprintf("%c", b[i]);\r | |
155 | else {\r | |
156 | iprintf("\\x%02X", b[i]);\r | |
157 | }\r | |
158 | }\r | |
159 | iprintf("\nSending...\n");\r | |
160 | send(b, l);\r | |
161 | iprintf("Sent\n");\r | |
47b0bbd2 MM |
162 | if (txbuf.available() == 0)\r |
163 | r = false;\r | |
164 | }\r | |
165 | else\r | |
166 | {\r | |
f455ce87 | 167 | r = false;\r |
47b0bbd2 MM |
168 | }\r |
169 | iprintf("USBSerial:EpIn Complete\n");\r | |
170 | return r;\r | |
171 | }\r | |
172 | \r | |
173 | bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)\r | |
174 | {\r | |
175 | /*\r | |
176 | * Called in ISR context\r | |
177 | */\r | |
178 | \r | |
179 | bool r = true;\r | |
180 | \r | |
181 | iprintf("USBSerial:EpOut\n");\r | |
182 | if (bEP != CDC_BulkOut.bEndpointAddress)\r | |
183 | return false;\r | |
184 | \r | |
185 | if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)\r | |
186 | {\r | |
187 | // usb->endpointSetInterrupt(bEP, false);\r | |
188 | return false;\r | |
189 | }\r | |
190 | \r | |
191 | uint8_t c[MAX_PACKET_SIZE_EPBULK];\r | |
192 | uint32_t size = 64;\r | |
193 | \r | |
194 | //we read the packet received and put it on the circular buffer\r | |
195 | readEP(c, &size);\r | |
196 | iprintf("Read %ld bytes:\n\t", size);\r | |
197 | for (uint8_t i = 0; i < size; i++) {\r | |
0e91ff91 MM |
198 | \r |
199 | if (flush_to_nl == false)\r | |
200 | rxbuf.queue(c[i]);\r | |
201 | \r | |
47b0bbd2 | 202 | if (c[i] >= 32 && c[i] < 128)\r |
ca5e9a57 | 203 | {\r |
47b0bbd2 | 204 | iprintf("%c", c[i]);\r |
ca5e9a57 MM |
205 | }\r |
206 | else\r | |
207 | {\r | |
47b0bbd2 MM |
208 | iprintf("\\x%02X", c[i]);\r |
209 | }\r | |
0e91ff91 | 210 | \r |
9afe7d79 | 211 | if (c[i] == '\n' || c[i] == '\r')\r |
0e91ff91 MM |
212 | {\r |
213 | if (flush_to_nl)\r | |
214 | flush_to_nl = false;\r | |
215 | else\r | |
216 | nl_in_rx++;\r | |
217 | }\r | |
218 | else if (rxbuf.isFull() && (nl_in_rx == 0))\r | |
219 | {\r | |
220 | // to avoid a deadlock with very long lines, we must dump the buffer\r | |
221 | // and continue flushing to the next newline\r | |
222 | rxbuf.flush();\r | |
223 | flush_to_nl = true;\r | |
224 | }\r | |
47b0bbd2 MM |
225 | }\r |
226 | iprintf("\nQueued, %d empty\n", rxbuf.free());\r | |
227 | \r | |
228 | if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)\r | |
229 | {\r | |
0e91ff91 | 230 | // if buffer is full, stall endpoint, do not accept more data\r |
47b0bbd2 | 231 | r = false;\r |
0e91ff91 MM |
232 | \r |
233 | if (nl_in_rx == 0)\r | |
234 | {\r | |
235 | // we have to check for long line deadlock here too\r | |
236 | flush_to_nl = true;\r | |
237 | rxbuf.flush();\r | |
238 | \r | |
239 | // and since our buffer is empty, we can accept more data\r | |
240 | r = true;\r | |
241 | }\r | |
47b0bbd2 MM |
242 | }\r |
243 | \r | |
244 | usb->readStart(CDC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);\r | |
245 | iprintf("USBSerial:EpOut Complete\n");\r | |
246 | return r;\r | |
247 | }\r | |
248 | \r | |
ca5e9a57 MM |
249 | uint8_t USBSerial::available()\r |
250 | {\r | |
47b0bbd2 MM |
251 | return rxbuf.available();\r |
252 | }\r | |
ca5e9a57 MM |
253 | \r |
254 | void USBSerial::on_module_loaded()\r | |
255 | {\r | |
824de637 | 256 | this->register_for_event(ON_MAIN_LOOP);\r |
ca5e9a57 MM |
257 | }\r |
258 | \r | |
824de637 | 259 | void USBSerial::on_main_loop(void *argument)\r |
ca5e9a57 | 260 | {\r |
8922c191 MM |
261 | // apparently some OSes don't assert DTR when a program opens the port\r |
262 | if (available() && !attach)\r | |
263 | attach = true;\r | |
264 | \r | |
654286ea MM |
265 | if (attach != attached)\r |
266 | {\r | |
267 | if (attach)\r | |
268 | {\r | |
269 | attached = true;\r | |
347854ff | 270 | THEKERNEL->streams->append_stream(this);\r |
08d9afc6 | 271 | puts("Smoothie\r\nok\r\n");\r |
654286ea MM |
272 | }\r |
273 | else\r | |
274 | {\r | |
275 | attached = false;\r | |
347854ff | 276 | THEKERNEL->streams->remove_stream(this);\r |
654286ea MM |
277 | txbuf.flush();\r |
278 | rxbuf.flush();\r | |
279 | nl_in_rx = 0;\r | |
280 | }\r | |
281 | }\r | |
ca5e9a57 MM |
282 | if (nl_in_rx)\r |
283 | {\r | |
284 | string received;\r | |
285 | while (available())\r | |
286 | {\r | |
287 | char c = _getc();\r | |
9afe7d79 | 288 | if( c == '\n' || c == '\r')\r |
ca5e9a57 MM |
289 | {\r |
290 | struct SerialMessage message;\r | |
291 | message.message = received;\r | |
292 | message.stream = this;\r | |
99b9ed8a | 293 | iprintf("USBSerial Received: %s\n", message.message.c_str());\r |
314ab8f7 | 294 | THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );\r |
ca5e9a57 MM |
295 | return;\r |
296 | }\r | |
297 | else\r | |
298 | {\r | |
299 | received += c;\r | |
300 | }\r | |
301 | }\r | |
302 | }\r | |
303 | }\r | |
304 | \r | |
305 | void USBSerial::on_attach()\r | |
306 | {\r | |
654286ea | 307 | attach = true;\r |
ca5e9a57 MM |
308 | }\r |
309 | \r | |
310 | void USBSerial::on_detach()\r | |
311 | {\r | |
654286ea | 312 | attach = false;\r |
ca5e9a57 | 313 | }\r |