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