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