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