fix discrepanceis in realtime postion vs last milestone when using ? to report position
[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 #include "StreamOutputPool.h"
27
28 // extern void setled(int, bool);
29 #define setled(a, b) do {} while (0)
30
31 #define iprintf(...) do { } while (0)
32
33 USBSerial::USBSerial(USB *u): USBCDC(u), rxbuf(256 + 8), txbuf(128 + 8)
34 {
35 usb = u;
36 nl_in_rx = 0;
37 attach = attached = false;
38 flush_to_nl = false;
39 halt_flag= false;
40 query_flag= false;
41 last_char_was_dollar= false;
42 }
43
44 void USBSerial::ensure_tx_space(int space)
45 {
46 while (txbuf.free() < space)
47 {
48 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
49 usb->usbisr();
50 }
51 }
52
53 int USBSerial::_putc(int c)
54 {
55 if (!attached)
56 return 1;
57 ensure_tx_space(1);
58 txbuf.queue(c);
59
60 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
61 return 1;
62 }
63
64 int USBSerial::_getc()
65 {
66 if (!attached)
67 return 0;
68 uint8_t c = 0;
69 setled(4, 1); while (rxbuf.isEmpty()); setled(4, 0);
70 rxbuf.dequeue(&c);
71 if (rxbuf.free() == MAX_PACKET_SIZE_EPBULK)
72 {
73 usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);
74 iprintf("rxbuf has room for another packet, interrupt enabled\n");
75 }
76 else if ((rxbuf.free() < MAX_PACKET_SIZE_EPBULK) && (nl_in_rx == 0))
77 {
78 // handle potential deadlock where a short line, and the beginning of a very long line are bundled in one usb packet
79 rxbuf.flush();
80 flush_to_nl = true;
81
82 usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);
83 iprintf("rxbuf has room for another packet, interrupt enabled\n");
84 }
85 if (nl_in_rx > 0)
86 if (c == '\n' || c == '\r')
87 nl_in_rx--;
88
89 return c;
90 }
91
92 int USBSerial::puts(const char *str)
93 {
94 if (!attached)
95 return strlen(str);
96 int i = 0;
97 while (*str)
98 {
99 ensure_tx_space(1);
100 txbuf.queue(*str);
101 if ((txbuf.available() % 64) == 0)
102 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
103 i++;
104 str++;
105 }
106 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
107 return i;
108 }
109
110 uint16_t USBSerial::writeBlock(const uint8_t * buf, uint16_t size)
111 {
112 if (!attached)
113 return size;
114 if (size > txbuf.free())
115 {
116 size = txbuf.free();
117 }
118 if (size > 0)
119 {
120 for (uint8_t i = 0; i < size; i++)
121 {
122 txbuf.queue(buf[i]);
123 }
124 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
125 }
126 return size;
127 }
128
129 bool USBSerial::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus)
130 {
131 /*
132 * Called in ISR context
133 */
134
135 // static bool needToSendNull = false;
136
137 bool r = true;
138
139 if (bEP != CDC_BulkIn.bEndpointAddress)
140 return false;
141
142 iprintf("USBSerial:EpIn: 0x%02X\n", bEPStatus);
143
144 uint8_t b[MAX_PACKET_SIZE_EPBULK];
145
146 int l = txbuf.available();
147 iprintf("%d bytes queued\n", l);
148 if (l > 0)
149 {
150 if (l > MAX_PACKET_SIZE_EPBULK)
151 l = MAX_PACKET_SIZE_EPBULK;
152 iprintf("Sending %d bytes:\n\t", l);
153 int i;
154 for (i = 0; i < l; i++) {
155 txbuf.dequeue(&b[i]);
156 if (b[i] >= 32 && b[i] < 128)
157 iprintf("%c", b[i]);
158 else {
159 iprintf("\\x%02X", b[i]);
160 }
161 }
162 iprintf("\nSending...\n");
163 send(b, l);
164 iprintf("Sent\n");
165 if (txbuf.available() == 0)
166 r = false;
167 }
168 else
169 {
170 r = false;
171 }
172 iprintf("USBSerial:EpIn Complete\n");
173 return r;
174 }
175
176 bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)
177 {
178 /*
179 * Called in ISR context
180 */
181
182 bool r = true;
183
184 iprintf("USBSerial:EpOut\n");
185 if (bEP != CDC_BulkOut.bEndpointAddress)
186 return false;
187
188 if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)
189 {
190 // usb->endpointSetInterrupt(bEP, false);
191 return false;
192 }
193
194 uint8_t c[MAX_PACKET_SIZE_EPBULK];
195 uint32_t size = 64;
196
197 //we read the packet received and put it on the circular buffer
198 readEP(c, &size);
199 iprintf("Read %ld bytes:\n\t", size);
200 for (uint8_t i = 0; i < size; i++) {
201 if(c[i] == 'X'-'A'+1){ // ^X
202 THEKERNEL->set_feed_hold(false); // required to free stuff up
203 halt_flag= true;
204 continue;
205 }
206
207 if(c[i] == '?'){ // ?
208 query_flag= true;
209 continue;
210 }
211
212 if(THEKERNEL->is_grbl_mode()) {
213 if(c[i] == '!'){ // safe pause
214 THEKERNEL->set_feed_hold(true);
215 continue;
216 }
217
218 if(c[i] == '~'){ // safe resume
219 THEKERNEL->set_feed_hold(false);
220 continue;
221 }
222 if(last_char_was_dollar && (c[i] == 'X' || c[i] == 'H')) {
223 // we need to do this otherwise $X/$H won't work if there was a feed hold like when stop is clicked in bCNC
224 THEKERNEL->set_feed_hold(false);
225 }
226 }
227
228 last_char_was_dollar= (c[i] == '$');
229
230 if (flush_to_nl == false)
231 rxbuf.queue(c[i]);
232
233 // if (c[i] >= 32 && c[i] < 128)
234 // {
235 // iprintf("%c", c[i]);
236 // }
237 // else
238 // {
239 // iprintf("\\x%02X", c[i]);
240 // }
241
242 if (c[i] == '\n' || c[i] == '\r')
243 {
244 if (flush_to_nl)
245 flush_to_nl = false;
246 else
247 nl_in_rx++;
248 }
249 else if (rxbuf.isFull() && (nl_in_rx == 0))
250 {
251 // to avoid a deadlock with very long lines, we must dump the buffer
252 // and continue flushing to the next newline
253 rxbuf.flush();
254 flush_to_nl = true;
255 }
256 }
257 iprintf("\nQueued, %d empty\n", rxbuf.free());
258
259 if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)
260 {
261 // if buffer is full, stall endpoint, do not accept more data
262 r = false;
263
264 if (nl_in_rx == 0)
265 {
266 // we have to check for long line deadlock here too
267 flush_to_nl = true;
268 rxbuf.flush();
269
270 // and since our buffer is empty, we can accept more data
271 r = true;
272 }
273 }
274
275 usb->readStart(CDC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);
276 iprintf("USBSerial:EpOut Complete\n");
277 return r;
278 }
279
280 uint8_t USBSerial::available()
281 {
282 return rxbuf.available();
283 }
284
285 bool USBSerial::ready()
286 {
287 return rxbuf.available();
288 }
289
290 void USBSerial::on_module_loaded()
291 {
292 this->register_for_event(ON_MAIN_LOOP);
293 this->register_for_event(ON_IDLE);
294 }
295
296 void USBSerial::on_idle(void *argument)
297 {
298 if(halt_flag) {
299 halt_flag= false;
300 THEKERNEL->call_event(ON_HALT, nullptr);
301 if(THEKERNEL->is_grbl_mode()) {
302 puts("ALARM:Abort during cycle\r\n");
303 }else{
304 puts("HALTED, M999 or $X to exit HALT state\r\n");
305 }
306 }
307
308 if(query_flag) {
309 query_flag= false;
310 puts(THEKERNEL->get_query_string().c_str());
311 }
312
313 }
314
315 void USBSerial::on_main_loop(void *argument)
316 {
317 // apparently some OSes don't assert DTR when a program opens the port
318 if (available() && !attach)
319 attach = true;
320
321 if (attach != attached)
322 {
323 if (attach)
324 {
325 attached = true;
326 THEKERNEL->streams->append_stream(this);
327 puts("Smoothie\r\nok\r\n");
328 }
329 else
330 {
331 attached = false;
332 THEKERNEL->streams->remove_stream(this);
333 txbuf.flush();
334 rxbuf.flush();
335 nl_in_rx = 0;
336 }
337 }
338
339 // if we are in feed hold we do not process anything
340 if(THEKERNEL->get_feed_hold()) return;
341
342 if (nl_in_rx)
343 {
344 string received;
345 while (available())
346 {
347 char c = _getc();
348 if( c == '\n' || c == '\r')
349 {
350 struct SerialMessage message;
351 message.message = received;
352 message.stream = this;
353 iprintf("USBSerial Received: %s\n", message.message.c_str());
354 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
355 return;
356 }
357 else
358 {
359 received += c;
360 }
361 }
362 }
363 }
364
365 void USBSerial::on_attach()
366 {
367 attach = true;
368 }
369
370 void USBSerial::on_detach()
371 {
372 attach = false;
373 }