1 /* MSPDebug - debugging tool for the eZ430
2 * Copyright (C) 2009-2012 Daniel Beer
3 * Copyright (C) 2010 Peter Jansen
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #if !defined(__Windows__) || defined(__MINGW32__)
25 #include <usb.h>
26 #else
27 #include <lusb0_usb.h>
28 #endif
29
30 #include <time.h>
31
32 #include "cp210x.h"
33 #include "util.h"
34 #include "usbutil.h"
35 #include "output.h"
36
37 struct cp210x_transport {
38 struct transport base;
39
40 struct usb_dev_handle *handle;
41 int int_number;
42 };
43
44 /*********************************************************************
45 * USB transport
46 *
47 * These functions handle the details of slicing data over USB
48 * transfers. The interface presented is a continuous byte stream with
49 * no slicing codes.
50 *
51 * Writes are unbuffered -- a single write translates to at least
52 * one transfer.
53 */
54
55 #define CP210X_CLOCK 3500000
56
57 #define V1_INTERFACE_CLASS 255
58 #define V1_IN_EP 0x81
59 #define V1_OUT_EP 0x01
60
61 #define CP210x_REQTYPE_HOST_TO_DEVICE 0x41
62
63 #define CP210X_IFC_ENABLE 0x00
64 #define CP210X_SET_BAUDDIV 0x01
65 #define CP210X_SET_MHS 0x07
66
67 /* CP210X_(SET_MHS|GET_MDMSTS) */
68 #define CP210X_DTR 0x0001
69 #define CP210X_RTS 0x0002
70 #define CP210X_CTS 0x0010
71 #define CP210X_DSR 0x0020
72 #define CP210X_RING 0x0040
73 #define CP210X_DCD 0x0080
74 #define CP210X_WRITE_DTR 0x0100
75 #define CP210X_WRITE_RTS 0x0200
76
77 #define TIMEOUT_S 30
78
configure_port(struct cp210x_transport * tr,int baud_rate)79 static int configure_port(struct cp210x_transport *tr, int baud_rate)
80 {
81 int ret;
82
83 ret = usb_control_msg(tr->handle, CP210x_REQTYPE_HOST_TO_DEVICE,
84 CP210X_IFC_ENABLE, 0x1, 0, NULL, 0, 300);
85 #ifdef DEBUG_CP210X
86 printc("%s: %s: Sending control message "
87 "CP210x_REQTYPE_HOST_TO_DEVICE, ret = %d\n",
88 __FILE__, __FUNCTION__, ret);
89 #endif
90 if (ret < 0) {
91 pr_error(__FILE__": can't enable CP210x UART");
92 return -1;
93 }
94
95 /* Set the baud rate to 500000 bps */
96 ret = usb_control_msg(tr->handle, CP210x_REQTYPE_HOST_TO_DEVICE,
97 CP210X_SET_BAUDDIV, CP210X_CLOCK / baud_rate,
98 0, NULL, 0, 300);
99 #ifdef DEBUG_CP210X
100 printc("%s: %s: Sending control message "
101 "CP210X_SET_BAUDDIV, ret = %d\n",
102 __FILE__, __FUNCTION__, ret);
103 #endif
104 if (ret < 0) {
105 pr_error(__FILE__": can't set baud rate");
106 return -1;
107 }
108
109 /* Set the modem control settings.
110 * Clear RTS, DTR and WRITE_DTR, WRITE_RTS
111 */
112 ret = usb_control_msg(tr->handle, CP210x_REQTYPE_HOST_TO_DEVICE,
113 CP210X_SET_MHS, 0x303, 0, NULL, 0, 300);
114 #ifdef DEBUG_CP210X
115 printc("%s: %s: Sending control message "
116 "CP210X_SET_MHS, ret %d\n",
117 __FILE__, __FUNCTION__, ret);
118 #endif
119 if (ret < 0) {
120 pr_error(__FILE__": can't set modem control");
121 return -1;
122 }
123
124 return 0;
125 }
126
open_interface(struct cp210x_transport * tr,struct usb_device * dev,int ino,int baud_rate)127 static int open_interface(struct cp210x_transport *tr,
128 struct usb_device *dev, int ino,
129 int baud_rate)
130 {
131 #if defined(__linux__)
132 int drv;
133 char drName[256];
134 #endif
135
136 printc_dbg(__FILE__": Trying to open interface %d on %s\n",
137 ino, dev->filename);
138
139 tr->int_number = ino;
140
141 tr->handle = usb_open(dev);
142 if (!tr->handle) {
143 pr_error(__FILE__": can't open device");
144 return -1;
145 }
146
147 #if defined(__linux__)
148 drv = usb_get_driver_np(tr->handle, tr->int_number, drName,
149 sizeof(drName));
150 printc(__FILE__" : driver %d\n", drv);
151 if (drv >= 0) {
152 if (usb_detach_kernel_driver_np(tr->handle,
153 tr->int_number) < 0)
154 pr_error(__FILE__": warning: can't detach "
155 "kernel driver");
156 }
157 #endif
158
159 #ifdef __Windows__
160 if (usb_set_configuration(tr->handle, 1) < 0) {
161 pr_error(__FILE__": can't set configuration 1");
162 usb_close(tr->handle);
163 return -1;
164 }
165 #endif
166
167 if (usb_claim_interface(tr->handle, tr->int_number) < 0) {
168 pr_error(__FILE__": can't claim interface");
169 usb_close(tr->handle);
170 return -1;
171 }
172
173 if (configure_port(tr, baud_rate) < 0) {
174 printc_err("Failed to configure for V1 device\n");
175 usb_close(tr->handle);
176 return -1;
177 }
178
179 return 0;
180 }
181
open_device(struct cp210x_transport * tr,struct usb_device * dev,int baud_rate)182 static int open_device(struct cp210x_transport *tr, struct usb_device *dev,
183 int baud_rate)
184 {
185 struct usb_config_descriptor *c = &dev->config[0];
186 int i;
187
188 for (i = 0; i < c->bNumInterfaces; i++) {
189 struct usb_interface *intf = &c->interface[i];
190 struct usb_interface_descriptor *desc = &intf->altsetting[0];
191
192 if (desc->bInterfaceClass == V1_INTERFACE_CLASS &&
193 !open_interface(tr, dev, desc->bInterfaceNumber,
194 baud_rate))
195 return 0;
196 }
197
198 return -1;
199 }
200
usbtr_send(transport_t tr_base,const uint8_t * data,int len)201 static int usbtr_send(transport_t tr_base, const uint8_t *data, int len)
202 {
203 struct cp210x_transport *tr = (struct cp210x_transport *)tr_base;
204 int sent;
205
206 #ifdef DEBUG_CP210X
207 debug_hexdump(__FILE__ ": USB transfer out", data, len);
208 #endif
209 while (len) {
210 sent = usb_bulk_write(tr->handle, V1_OUT_EP,
211 (char *)data, len, TIMEOUT_S * 1000);
212 if (sent <= 0) {
213 pr_error(__FILE__": can't send data");
214 return -1;
215 }
216
217 data += sent;
218 len -= sent;
219 }
220
221 return 0;
222 }
223
usbtr_recv(transport_t tr_base,uint8_t * databuf,int max_len)224 static int usbtr_recv(transport_t tr_base, uint8_t *databuf, int max_len)
225 {
226 struct cp210x_transport *tr = (struct cp210x_transport *)tr_base;
227 int rlen;
228 time_t deadline = time(NULL) + TIMEOUT_S;
229
230 #ifdef DEBUG_CP210X
231 printc(__FILE__": %s : read max %d\n", __FUNCTION__, max_len);
232 #endif
233
234 while (time(NULL) < deadline) {
235 rlen = usb_bulk_read(tr->handle, V1_IN_EP, (char *)databuf,
236 max_len, TIMEOUT_S * 1000);
237
238 #ifdef DEBUG_CP210X
239 printc(__FILE__": %s : read %d\n", __FUNCTION__, rlen);
240 #endif
241
242 if (rlen < 0) {
243 pr_error(__FILE__": can't receive data");
244 return -1;
245 }
246
247 if (rlen > 0) {
248 #ifdef DEBUG_CP210X
249 debug_hexdump(__FILE__": USB transfer in", databuf, rlen);
250 #endif
251 return rlen;
252 }
253 }
254
255 pr_error(__FILE__": read operation timed out");
256 return -1;
257 }
258
usbtr_destroy(transport_t tr_base)259 static void usbtr_destroy(transport_t tr_base)
260 {
261 struct cp210x_transport *tr = (struct cp210x_transport *)tr_base;
262
263 usb_release_interface(tr->handle, tr->int_number);
264 usb_close(tr->handle);
265 free(tr);
266 }
267
usbtr_flush(transport_t tr_base)268 static int usbtr_flush(transport_t tr_base)
269 {
270 struct cp210x_transport *tr = (struct cp210x_transport *)tr_base;
271 char buf[64];
272
273 /* Flush out lingering data */
274 while (usb_bulk_read(tr->handle, V1_IN_EP,
275 buf, sizeof(buf),
276 100) > 0);
277
278 return 0;
279 }
280
usbtr_set_modem(transport_t tr_base,transport_modem_t state)281 static int usbtr_set_modem(transport_t tr_base, transport_modem_t state)
282 {
283 struct cp210x_transport *tr = (struct cp210x_transport *)tr_base;
284 int value = CP210X_WRITE_DTR | CP210X_WRITE_RTS;
285
286 /* DTR and RTS bits are active-low for this device */
287 if (!(state & TRANSPORT_MODEM_DTR))
288 value |= CP210X_DTR;
289 if (!(state & TRANSPORT_MODEM_RTS))
290 value |= CP210X_RTS;
291
292 if (usb_control_msg(tr->handle, CP210x_REQTYPE_HOST_TO_DEVICE,
293 CP210X_SET_MHS, value, 0, NULL, 0, 300) < 0) {
294 pr_error("cp210x: failed to set modem control lines\n");
295 return -1;
296 }
297
298 return 0;
299 }
300
301 static const struct transport_class cp210x_class = {
302 .destroy = usbtr_destroy,
303 .send = usbtr_send,
304 .recv = usbtr_recv,
305 .flush = usbtr_flush,
306 .set_modem = usbtr_set_modem
307 };
308
cp210x_open(const char * devpath,const char * requested_serial,int baud_rate,uint16_t product,uint16_t vendor)309 transport_t cp210x_open(const char *devpath, const char *requested_serial,
310 int baud_rate, uint16_t product, uint16_t vendor)
311 {
312 struct cp210x_transport *tr = malloc(sizeof(*tr));
313 struct usb_device *dev;
314
315 if (!tr) {
316 pr_error(__FILE__": can't allocate memory");
317 return NULL;
318 }
319
320 tr->base.ops = &cp210x_class;
321
322 usb_init();
323 usb_find_busses();
324 usb_find_devices();
325
326 if (devpath)
327 dev = usbutil_find_by_loc(devpath);
328 else
329 dev = usbutil_find_by_id(product, vendor, requested_serial);
330
331 if (!dev) {
332 free(tr);
333 return NULL;
334 }
335
336 if (open_device(tr, dev, baud_rate) < 0) {
337 printc_err(__FILE__ ": failed to open CP210X device\n");
338 free(tr);
339 return NULL;
340 }
341
342 usbtr_flush(&tr->base);
343
344 return (transport_t)tr;
345 }
346