USBSerial: allow CR to terminate lines
[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(128 + 8), txbuf(128 + 8)
33 {
34 usb = u;
35 nl_in_rx = 0;
36 attach = attached = false;
37 }
38
39 void USBSerial::ensure_tx_space(int space)
40 {
41 while (txbuf.free() < space)
42 {
43 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
44 usb->usbisr();
45 }
46 }
47
48 int USBSerial::_putc(int c)
49 {
50 if (!attached)
51 return 1;
52 ensure_tx_space(1);
53 txbuf.queue(c);
54
55 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
56 return 1;
57 }
58
59 int USBSerial::_getc()
60 {
61 if (!attached)
62 return 0;
63 uint8_t c = 0;
64 setled(4, 1); while (rxbuf.isEmpty()); setled(4, 0);
65 rxbuf.dequeue(&c);
66 if (rxbuf.free() == MAX_PACKET_SIZE_EPBULK)
67 {
68 usb->endpointSetInterrupt(CDC_BulkOut.bEndpointAddress, true);
69 iprintf("rxbuf has room for another packet, interrupt enabled\n");
70 }
71 if (nl_in_rx > 0)
72 if (c == '\n' || c == '\r')
73 nl_in_rx--;
74
75 return c;
76 }
77
78 int USBSerial::puts(const char *str)
79 {
80 if (!attached)
81 return strlen(str);
82 int i = 0;
83 while (*str)
84 {
85 ensure_tx_space(1);
86 txbuf.queue(*str);
87 if ((txbuf.available() % 64) == 0)
88 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
89 i++;
90 str++;
91 }
92 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
93 return i;
94 }
95
96 uint16_t USBSerial::writeBlock(const uint8_t * buf, uint16_t size)
97 {
98 if (!attached)
99 return size;
100 if (size > txbuf.free())
101 {
102 size = txbuf.free();
103 }
104 if (size > 0)
105 {
106 for (uint8_t i = 0; i < size; i++)
107 {
108 txbuf.queue(buf[i]);
109 }
110 usb->endpointSetInterrupt(CDC_BulkIn.bEndpointAddress, true);
111 }
112 return size;
113 }
114
115 bool USBSerial::USBEvent_EPIn(uint8_t bEP, uint8_t bEPStatus)
116 {
117 /*
118 * Called in ISR context
119 */
120
121 // static bool needToSendNull = false;
122
123 bool r = true;
124
125 if (bEP != CDC_BulkIn.bEndpointAddress)
126 return false;
127
128 iprintf("USBSerial:EpIn: 0x%02X\n", bEPStatus);
129
130 uint8_t b[MAX_PACKET_SIZE_EPBULK];
131
132 int l = txbuf.available();
133 iprintf("%d bytes queued\n", l);
134 if (l > 0)
135 {
136 if (l > MAX_PACKET_SIZE_EPBULK)
137 l = MAX_PACKET_SIZE_EPBULK;
138 iprintf("Sending %d bytes:\n\t", l);
139 int i;
140 for (i = 0; i < l; i++) {
141 txbuf.dequeue(&b[i]);
142 if (b[i] >= 32 && b[i] < 128)
143 iprintf("%c", b[i]);
144 else {
145 iprintf("\\x%02X", b[i]);
146 }
147 }
148 iprintf("\nSending...\n");
149 send(b, l);
150 iprintf("Sent\n");
151 if (txbuf.available() == 0)
152 r = false;
153 }
154 else
155 {
156 r = false;
157 }
158 iprintf("USBSerial:EpIn Complete\n");
159 return r;
160 }
161
162 bool USBSerial::USBEvent_EPOut(uint8_t bEP, uint8_t bEPStatus)
163 {
164 /*
165 * Called in ISR context
166 */
167
168 bool r = true;
169
170 iprintf("USBSerial:EpOut\n");
171 if (bEP != CDC_BulkOut.bEndpointAddress)
172 return false;
173
174 if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)
175 {
176 // usb->endpointSetInterrupt(bEP, false);
177 return false;
178 }
179
180 uint8_t c[MAX_PACKET_SIZE_EPBULK];
181 uint32_t size = 64;
182
183 //we read the packet received and put it on the circular buffer
184 readEP(c, &size);
185 iprintf("Read %ld bytes:\n\t", size);
186 for (uint8_t i = 0; i < size; i++) {
187 rxbuf.queue(c[i]);
188 if (c[i] >= 32 && c[i] < 128)
189 {
190 iprintf("%c", c[i]);
191 }
192 else
193 {
194 iprintf("\\x%02X", c[i]);
195 }
196 if (c[i] == '\n' || c[i] == '\r')
197 nl_in_rx++;
198 }
199 iprintf("\nQueued, %d empty\n", rxbuf.free());
200
201 if (rxbuf.free() < MAX_PACKET_SIZE_EPBULK)
202 {
203 r = false;
204 }
205
206 usb->readStart(CDC_BulkOut.bEndpointAddress, MAX_PACKET_SIZE_EPBULK);
207 iprintf("USBSerial:EpOut Complete\n");
208 return r;
209 }
210
211 uint8_t USBSerial::available()
212 {
213 return rxbuf.available();
214 }
215
216 void USBSerial::on_module_loaded()
217 {
218 this->register_for_event(ON_MAIN_LOOP);
219 }
220
221 void USBSerial::on_main_loop(void *argument)
222 {
223 // apparently some OSes don't assert DTR when a program opens the port
224 if (available() && !attach)
225 attach = true;
226
227 if (attach != attached)
228 {
229 if (attach)
230 {
231 attached = true;
232 kernel->streams->append_stream(this);
233 writeBlock((const uint8_t *) "Smoothie\nok\n", 12);
234 }
235 else
236 {
237 attached = false;
238 kernel->streams->remove_stream(this);
239 txbuf.flush();
240 rxbuf.flush();
241 nl_in_rx = 0;
242 }
243 }
244 if (nl_in_rx)
245 {
246 string received;
247 while (available())
248 {
249 char c = _getc();
250 if( c == '\n' || c == '\r')
251 {
252 struct SerialMessage message;
253 message.message = received;
254 message.stream = this;
255 iprintf("USBSerial Received: %s\n", message.message.c_str());
256 this->kernel->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
257 return;
258 }
259 else
260 {
261 received += c;
262 }
263 }
264 }
265 }
266
267 void USBSerial::on_attach()
268 {
269 attach = true;
270 }
271
272 void USBSerial::on_detach()
273 {
274 attach = false;
275 }