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