blob: e7e611594316fbe465a6af75790fcbd5c4203e97 [file] [log] [blame]
Brian Silverman099196d2017-06-21 23:26:02 -07001/* Teensyduino Core Library
2 * http://www.pjrc.com/teensy/
3 * Copyright (c) 2017 PJRC.COM, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * 1. The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * 2. If the Software is incorporated into a build system that allows
17 * selection among a list of target devices, then similar target
18 * devices manufactured by PJRC.COM must be included in the list of
19 * target devices and selectable in the same manner.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
25 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
26 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 * SOFTWARE.
29 */
30
Brian Silvermanb79af7c2017-06-21 23:48:02 -070031#include "motors/usb/usb_dev.h"
32#include "motors/usb/usb_serial.h"
33#include <string.h>
Brian Silverman099196d2017-06-21 23:26:02 -070034
Brian Silvermanb79af7c2017-06-21 23:48:02 -070035// These are shared between the two serial ports because they're ignored
36// anyways.
Brian Silverman099196d2017-06-21 23:26:02 -070037uint32_t usb_cdc_line_coding[2];
38volatile uint32_t usb_cdc_line_rtsdtr_millis;
39volatile uint8_t usb_cdc_line_rtsdtr=0;
Brian Silverman099196d2017-06-21 23:26:02 -070040
Brian Silvermanb79af7c2017-06-21 23:48:02 -070041typedef struct {
42 usb_packet_t *rx_packet;
43 usb_packet_t *tx_packet;
44
45 uint32_t rx_endpoint;
46 uint32_t tx_endpoint;
47
48 volatile uint8_t tx_noautoflush;
49 volatile uint8_t transmit_flush_timer;
50} State;
51State states[2];
52
53volatile uint8_t *usb_cdc_transmit_flush_timer =
54 &states[0].transmit_flush_timer;
55volatile uint8_t *usb_cdc2_transmit_flush_timer =
56 &states[1].transmit_flush_timer;
Brian Silverman099196d2017-06-21 23:26:02 -070057
58#define TRANSMIT_FLUSH_TIMEOUT 5 /* in milliseconds */
59
Brian Silvermanb79af7c2017-06-21 23:48:02 -070060void usb_serial_init(void) {
61 states[0].rx_endpoint = CDC_RX_ENDPOINT;
62 states[0].tx_endpoint = CDC_TX_ENDPOINT;
63 states[1].rx_endpoint = CDC2_RX_ENDPOINT;
64 states[1].tx_endpoint = CDC2_TX_ENDPOINT;
65}
66
Brian Silverman099196d2017-06-21 23:26:02 -070067// get the next character, or -1 if nothing received
Brian Silvermanb79af7c2017-06-21 23:48:02 -070068int usb_serial_getchar(int port)
Brian Silverman099196d2017-06-21 23:26:02 -070069{
Brian Silvermanb79af7c2017-06-21 23:48:02 -070070 State *const state = &states[port];
Brian Silverman099196d2017-06-21 23:26:02 -070071 unsigned int i;
72 int c;
73
Brian Silvermanb79af7c2017-06-21 23:48:02 -070074 if (!state->rx_packet) {
Brian Silverman099196d2017-06-21 23:26:02 -070075 if (!usb_configuration) return -1;
Brian Silvermanb79af7c2017-06-21 23:48:02 -070076 state->rx_packet = usb_rx(state->rx_endpoint);
77 if (!state->rx_packet) return -1;
Brian Silverman099196d2017-06-21 23:26:02 -070078 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -070079 i = state->rx_packet->index;
80 c = state->rx_packet->buf[i++];
81 if (i >= state->rx_packet->len) {
82 usb_free(state->rx_packet);
83 state->rx_packet = NULL;
Brian Silverman099196d2017-06-21 23:26:02 -070084 } else {
Brian Silvermanb79af7c2017-06-21 23:48:02 -070085 state->rx_packet->index = i;
Brian Silverman099196d2017-06-21 23:26:02 -070086 }
87 return c;
88}
89
90// peek at the next character, or -1 if nothing received
Brian Silvermanb79af7c2017-06-21 23:48:02 -070091int usb_serial_peekchar(int port)
Brian Silverman099196d2017-06-21 23:26:02 -070092{
Brian Silvermanb79af7c2017-06-21 23:48:02 -070093 State *const state = &states[port];
94 if (!state->rx_packet) {
Brian Silverman099196d2017-06-21 23:26:02 -070095 if (!usb_configuration) return -1;
Brian Silvermanb79af7c2017-06-21 23:48:02 -070096 state->rx_packet = usb_rx(state->rx_endpoint);
97 if (!state->rx_packet) return -1;
Brian Silverman099196d2017-06-21 23:26:02 -070098 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -070099 if (!state->rx_packet) return -1;
100 return state->rx_packet->buf[state->rx_packet->index];
Brian Silverman099196d2017-06-21 23:26:02 -0700101}
102
103// read a block of bytes to a buffer
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700104int usb_serial_read(int port, void *buffer, uint32_t size)
Brian Silverman099196d2017-06-21 23:26:02 -0700105{
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700106 State *const state = &states[port];
Brian Silverman099196d2017-06-21 23:26:02 -0700107 uint8_t *p = (uint8_t *)buffer;
108 uint32_t qty, count=0;
109
110 while (size) {
111 if (!usb_configuration) break;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700112 if (!state->rx_packet) {
Brian Silverman099196d2017-06-21 23:26:02 -0700113 rx:
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700114 state->rx_packet = usb_rx(state->rx_endpoint);
115 if (!state->rx_packet) break;
116 if (state->rx_packet->len == 0) {
117 usb_free(state->rx_packet);
Brian Silverman099196d2017-06-21 23:26:02 -0700118 goto rx;
119 }
120 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700121 qty = state->rx_packet->len - state->rx_packet->index;
Brian Silverman099196d2017-06-21 23:26:02 -0700122 if (qty > size) qty = size;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700123 memcpy(p, state->rx_packet->buf + state->rx_packet->index, qty);
Brian Silverman099196d2017-06-21 23:26:02 -0700124 p += qty;
125 count += qty;
126 size -= qty;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700127 state->rx_packet->index += qty;
128 if (state->rx_packet->index >= state->rx_packet->len) {
129 usb_free(state->rx_packet);
130 state->rx_packet = NULL;
Brian Silverman099196d2017-06-21 23:26:02 -0700131 }
132 }
133 return count;
134}
135
136// discard any buffered input
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700137void usb_serial_flush_input(int port)
Brian Silverman099196d2017-06-21 23:26:02 -0700138{
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700139 State *const state = &states[port];
Brian Silverman099196d2017-06-21 23:26:02 -0700140 usb_packet_t *rx;
141
142 if (!usb_configuration) return;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700143 if (state->rx_packet) {
144 usb_free(state->rx_packet);
145 state->rx_packet = NULL;
Brian Silverman099196d2017-06-21 23:26:02 -0700146 }
147 while (1) {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700148 rx = usb_rx(state->rx_endpoint);
Brian Silverman099196d2017-06-21 23:26:02 -0700149 if (!rx) break;
150 usb_free(rx);
151 }
152}
153
154// Maximum number of transmit packets to queue so we don't starve other endpoints for memory
155#define TX_PACKET_LIMIT 8
156
157// When the PC isn't listening, how long do we wait before discarding data? If this is
158// too short, we risk losing data during the stalls that are common with ordinary desktop
159// software. If it's too long, we stall the user's program when no software is running.
160#define TX_TIMEOUT_MSEC 70
161
162#if F_CPU == 240000000
163 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1600)
164#elif F_CPU == 216000000
165 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1440)
166#elif F_CPU == 192000000
167 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1280)
168#elif F_CPU == 180000000
169 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1200)
170#elif F_CPU == 168000000
171 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 1100)
172#elif F_CPU == 144000000
173 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 932)
174#elif F_CPU == 120000000
175 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 764)
176#elif F_CPU == 96000000
177 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 596)
178#elif F_CPU == 72000000
179 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 512)
180#elif F_CPU == 48000000
181 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 428)
182#elif F_CPU == 24000000
183 #define TX_TIMEOUT (TX_TIMEOUT_MSEC * 262)
184#endif
185
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700186int usb_serial_write(int port, const void *buffer, uint32_t size)
Brian Silverman099196d2017-06-21 23:26:02 -0700187{
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700188 State *const state = &states[port];
Brian Silverman099196d2017-06-21 23:26:02 -0700189 uint32_t len;
Brian Silverman099196d2017-06-21 23:26:02 -0700190 const uint8_t *src = (const uint8_t *)buffer;
191 uint8_t *dest;
192
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700193 state->tx_noautoflush = 1;
Brian Silverman099196d2017-06-21 23:26:02 -0700194 while (size > 0) {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700195 if (!state->tx_packet) {
Brian Silverman099196d2017-06-21 23:26:02 -0700196 while (1) {
197 if (!usb_configuration) {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700198 state->tx_noautoflush = 0;
Brian Silverman099196d2017-06-21 23:26:02 -0700199 return -1;
200 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700201 if (usb_tx_packet_count(state->tx_endpoint) < TX_PACKET_LIMIT) {
202 state->tx_noautoflush = 1;
203 state->tx_packet = usb_malloc();
204 if (state->tx_packet) break;
205 state->tx_noautoflush = 0;
Brian Silverman099196d2017-06-21 23:26:02 -0700206 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700207 return -1;
Brian Silverman099196d2017-06-21 23:26:02 -0700208 }
209 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700210 len = CDC_TX_SIZE - state->tx_packet->index;
Brian Silverman099196d2017-06-21 23:26:02 -0700211 if (len > size) len = size;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700212 dest = state->tx_packet->buf + state->tx_packet->index;
213 state->tx_packet->index += len;
Brian Silverman099196d2017-06-21 23:26:02 -0700214 size -= len;
215 while (len-- > 0) *dest++ = *src++;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700216 if (state->tx_packet->index >= CDC_TX_SIZE) {
217 state->tx_packet->len = CDC_TX_SIZE;
218 usb_tx(state->tx_endpoint, state->tx_packet);
219 state->tx_packet = NULL;
Brian Silverman099196d2017-06-21 23:26:02 -0700220 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700221 state->transmit_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
Brian Silverman099196d2017-06-21 23:26:02 -0700222 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700223 state->tx_noautoflush = 0;
224 return size;
Brian Silverman099196d2017-06-21 23:26:02 -0700225}
226
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700227void usb_serial_flush_output(int port)
Brian Silverman099196d2017-06-21 23:26:02 -0700228{
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700229 State *const state = &states[port];
Brian Silverman099196d2017-06-21 23:26:02 -0700230 if (!usb_configuration) return;
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700231 state->tx_noautoflush = 1;
232 if (state->tx_packet) {
233 state->transmit_flush_timer = 0;
234 state->tx_packet->len = state->tx_packet->index;
235 usb_tx(state->tx_endpoint, state->tx_packet);
236 state->tx_packet = NULL;
Brian Silverman099196d2017-06-21 23:26:02 -0700237 } else {
238 usb_packet_t *tx = usb_malloc();
239 if (tx) {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700240 state->transmit_flush_timer = 0;
241 usb_tx(state->tx_endpoint, tx);
Brian Silverman099196d2017-06-21 23:26:02 -0700242 } else {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700243 state->transmit_flush_timer = 1;
Brian Silverman099196d2017-06-21 23:26:02 -0700244 }
245 }
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700246 state->tx_noautoflush = 0;
Brian Silverman099196d2017-06-21 23:26:02 -0700247}
248
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700249void usb_serial_flush_callback(int port)
Brian Silverman099196d2017-06-21 23:26:02 -0700250{
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700251 State *const state = &states[port];
252 if (state->tx_noautoflush) return;
253 if (state->tx_packet) {
254 state->tx_packet->len = state->tx_packet->index;
255 usb_tx(state->tx_endpoint, state->tx_packet);
256 state->tx_packet = NULL;
Brian Silverman099196d2017-06-21 23:26:02 -0700257 } else {
258 usb_packet_t *tx = usb_malloc();
259 if (tx) {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700260 usb_tx(state->tx_endpoint, tx);
Brian Silverman099196d2017-06-21 23:26:02 -0700261 } else {
Brian Silvermanb79af7c2017-06-21 23:48:02 -0700262 state->transmit_flush_timer = 1;
Brian Silverman099196d2017-06-21 23:26:02 -0700263 }
264 }
265}