1 /* MSPDebug - debugging tool for MSP430 MCUs
2  * Copyright (C) 2012 Daniel Beer
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include <stdlib.h>
20 #include <string.h>
21 #include <time.h>
22 
23 #include "ftdi.h"
24 #include "util.h"
25 #include "usbutil.h"
26 #include "output.h"
27 
28 struct ftdi_transport {
29 	struct transport        base;
30 	struct usb_dev_handle   *handle;
31 };
32 
33 #define USB_INTERFACE           0
34 #define USB_CONFIG              1
35 
36 #define EP_IN                   0x81
37 #define EP_OUT                  0x02
38 
39 #define TIMEOUT_S               30
40 #define REQ_TIMEOUT_MS          100
41 
42 #define REQTYPE_HOST_TO_DEVICE  0x40
43 
44 #define FTDI_SIO_RESET			0 /* Reset the port */
45 #define FTDI_SIO_MODEM_CTRL		1 /* Set the modem control register */
46 #define FTDI_SIO_SET_FLOW_CTRL		2 /* Set flow control register */
47 #define FTDI_SIO_SET_BAUD_RATE		3 /* Set baud rate */
48 #define FTDI_SIO_SET_DATA		4 /* Set the data characteristics of
49 					     the port */
50 #define FTDI_SIO_GET_MODEM_STATUS	5 /* Retrieve current value of modem
51 					     status register */
52 #define FTDI_SIO_SET_EVENT_CHAR		6 /* Set the event character */
53 #define FTDI_SIO_SET_ERROR_CHAR		7 /* Set the error character */
54 #define FTDI_SIO_SET_LATENCY_TIMER	9 /* Set the latency timer */
55 #define FTDI_SIO_GET_LATENCY_TIMER	10 /* Get the latency timer */
56 
57 #define FTDI_SIO_RESET_SIO              0
58 #define FTDI_SIO_RESET_PURGE_RX         1
59 #define FTDI_SIO_RESET_PURGE_TX         2
60 
61 #define FTDI_PACKET_SIZE                64
62 
63 #define FTDI_CLOCK			3000000
64 
65 #define FTDI_DTR			0x0001
66 #define FTDI_RTS			0x0002
67 #define FTDI_WRITE_DTR			0x0100
68 #define FTDI_WRITE_RTS			0x0200
69 
do_cfg(struct usb_dev_handle * handle,const char * what,int request,int value)70 static int do_cfg(struct usb_dev_handle *handle, const char *what,
71 		  int request, int value)
72 {
73 	if (usb_control_msg(handle, REQTYPE_HOST_TO_DEVICE,
74 			    request, value, 0,
75 			    NULL, 0, REQ_TIMEOUT_MS)) {
76 		printc_err("ftdi: %s failed: %s\n", what, usb_strerror());
77 		return -1;
78 	}
79 
80 	return 0;
81 }
82 
configure_ftdi(struct usb_dev_handle * h,int baud_rate)83 int configure_ftdi(struct usb_dev_handle *h, int baud_rate)
84 {
85 	if (do_cfg(h, "reset FTDI",
86 		   FTDI_SIO_RESET, FTDI_SIO_RESET_SIO) < 0 ||
87 	    do_cfg(h, "set data characteristics",
88 		   FTDI_SIO_SET_DATA, 8) < 0 ||
89 	    do_cfg(h, "disable flow control",
90 		   FTDI_SIO_SET_FLOW_CTRL, 0) < 0 ||
91 	    do_cfg(h, "set modem control lines",
92 		   FTDI_SIO_MODEM_CTRL, 0x303) < 0 ||
93 	    do_cfg(h, "set baud rate",
94 		   FTDI_SIO_SET_BAUD_RATE, FTDI_CLOCK / baud_rate) < 0 ||
95 	    do_cfg(h, "set latency timer",
96 		   FTDI_SIO_SET_LATENCY_TIMER, 50) < 0 ||
97 	    do_cfg(h, "purge TX",
98 		   FTDI_SIO_RESET, FTDI_SIO_RESET_PURGE_TX) < 0 ||
99 	    do_cfg(h, "purge RX",
100 		   FTDI_SIO_RESET, FTDI_SIO_RESET_PURGE_RX) < 0)
101 		return -1;
102 
103 	return 0;
104 }
105 
open_device(struct ftdi_transport * tr,struct usb_device * dev,int baud_rate)106 static int open_device(struct ftdi_transport *tr, struct usb_device *dev,
107 		       int baud_rate)
108 {
109 #ifdef __linux__
110 	int driver;
111 	char drv_name[128];
112 #endif
113 
114 	printc_dbg("ftdi: trying to open %s\n", dev->filename);
115 	tr->handle = usb_open(dev);
116 	if (!tr->handle) {
117 		printc_err("ftdi: can't open device: %s\n",
118 			   usb_strerror());
119 		return -1;
120 	}
121 
122 #ifdef __linux__
123 	driver = usb_get_driver_np(tr->handle, USB_INTERFACE,
124 				   drv_name, sizeof(drv_name));
125 	if (driver >= 0) {
126 		printc_dbg("Detaching kernel driver \"%s\"\n", drv_name);
127 		if (usb_detach_kernel_driver_np(tr->handle, USB_INTERFACE) < 0)
128 			printc_err("warning: ftdi: can't detach "
129 				   "kernel driver: %s\n", usb_strerror());
130 	}
131 #endif
132 
133 #ifdef __Windows__
134 	if (usb_set_configuration(tr->handle, USB_CONFIG) < 0) {
135 		printc_err("ftdi: can't set configuration: %s\n",
136 			   usb_strerror());
137 		usb_close(tr->handle);
138 		return -1;
139 	}
140 #endif
141 
142 	if (usb_claim_interface(tr->handle, USB_INTERFACE) < 0) {
143 		printc_err("ftdi: can't claim interface: %s\n",
144 			   usb_strerror());
145 		usb_close(tr->handle);
146 		return -1;
147 	}
148 
149 	if (configure_ftdi(tr->handle, baud_rate) < 0) {
150 		printc_err("ftdi: failed to configure device: %s\n",
151 			   usb_strerror());
152 		usb_close(tr->handle);
153 		return -1;
154 	}
155 
156 	return 0;
157 }
158 
tr_destroy(transport_t tr_base)159 static void tr_destroy(transport_t tr_base)
160 {
161 	struct ftdi_transport *tr = (struct ftdi_transport *)tr_base;
162 
163 	usb_close(tr->handle);
164 	free(tr);
165 }
166 
tr_recv(transport_t tr_base,uint8_t * databuf,int max_len)167 static int tr_recv(transport_t tr_base, uint8_t *databuf, int max_len)
168 {
169 	struct ftdi_transport *tr = (struct ftdi_transport *)tr_base;
170 	time_t deadline = time(NULL) + TIMEOUT_S;
171 	char tmpbuf[FTDI_PACKET_SIZE];
172 
173 	if (max_len > FTDI_PACKET_SIZE - 2)
174 		max_len = FTDI_PACKET_SIZE - 2;
175 
176 	while(time(NULL) < deadline) {
177 		int r = usb_bulk_read(tr->handle, EP_IN,
178 				      tmpbuf, max_len + 2,
179 				      TIMEOUT_S * 1000);
180 
181 		if (r <= 0) {
182 			printc_err("ftdi: usb_bulk_read: %s\n",
183 				   usb_strerror());
184 			return -1;
185 		}
186 
187 		if (r > 2) {
188 			memcpy(databuf, tmpbuf + 2, r - 2);
189 #ifdef DEBUG_OLIMEX_ISO
190 			printc_dbg("ftdi: tr_recv: flags = %02x %02x\n",
191 				   tmpbuf[0], tmpbuf[1]);
192 			debug_hexdump("ftdi: tr_recv", databuf, r - 2);
193 #endif
194 			return r - 2;
195 		}
196 	}
197 
198 	printc_err("ftdi: timed out while receiving data\n");
199 	return -1;
200 }
201 
tr_send(transport_t tr_base,const uint8_t * databuf,int len)202 static int tr_send(transport_t tr_base, const uint8_t *databuf, int len)
203 {
204 	struct ftdi_transport *tr = (struct ftdi_transport *)tr_base;
205 
206 #ifdef DEBUG_OLIMEX_ISO
207 	debug_hexdump("ftdi: tr_send", databuf, len);
208 #endif
209 	while (len) {
210 		int r = usb_bulk_write(tr->handle, EP_OUT,
211 				       (char *)databuf, len,
212 				       TIMEOUT_S * 1000);
213 
214 		if (r <= 0) {
215 			printc_err("ftdi: usb_bulk_write: %s\n",
216 				   usb_strerror());
217 			return -1;
218 		}
219 
220 		databuf += r;
221 		len -= r;
222 	}
223 
224 	return 0;
225 }
226 
tr_flush(transport_t tr_base)227 static int tr_flush(transport_t tr_base)
228 {
229 	struct ftdi_transport *tr = malloc(sizeof(*tr));
230 
231 	return do_cfg(tr->handle, "purge RX",
232 		      FTDI_SIO_RESET, FTDI_SIO_RESET_PURGE_RX);
233 }
234 
tr_set_modem(transport_t tr_base,transport_modem_t state)235 static int tr_set_modem(transport_t tr_base, transport_modem_t state)
236 {
237 	struct ftdi_transport *tr = malloc(sizeof(*tr));
238 	int value = FTDI_WRITE_DTR | FTDI_WRITE_RTS;
239 
240 	/* DTR and RTS bits are active-low for this device */
241 	if (!(state & TRANSPORT_MODEM_DTR))
242 		value |= FTDI_DTR;
243 	if (!(state & TRANSPORT_MODEM_RTS))
244 		value |= FTDI_RTS;
245 
246 	return do_cfg(tr->handle, "set modem control lines",
247 		      FTDI_SIO_MODEM_CTRL, value);
248 }
249 
250 static const struct transport_class ftdi_class = {
251 	.destroy	= tr_destroy,
252 	.send		= tr_send,
253 	.recv		= tr_recv,
254 	.flush		= tr_flush,
255 	.set_modem	= tr_set_modem
256 };
257 
ftdi_open(const char * devpath,const char * requested_serial,uint16_t vendor,uint16_t product,int baud_rate)258 transport_t ftdi_open(const char *devpath,
259 		      const char *requested_serial,
260 		      uint16_t vendor, uint16_t product,
261 		      int baud_rate)
262 {
263 	struct ftdi_transport *tr = malloc(sizeof(*tr));
264 	struct usb_device *dev;
265 
266 	if (!tr) {
267 		pr_error("ftdi: can't allocate memory");
268 		return NULL;
269 	}
270 
271 	tr->base.ops = &ftdi_class;
272 
273 	usb_init();
274 	usb_find_busses();
275 	usb_find_devices();
276 
277 	if (devpath)
278 		dev = usbutil_find_by_loc(devpath);
279 	else
280 		dev = usbutil_find_by_id(vendor, product, requested_serial);
281 
282 	if (!dev) {
283 		free(tr);
284 		return NULL;
285 	}
286 
287 	if (open_device(tr, dev, baud_rate) < 0) {
288 		printc_err("ftdi: failed to open device\n");
289 		free(tr);
290 		return NULL;
291 	}
292 
293 	return &tr->base;
294 }
295