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