xref: /freebsd/sys/dev/usb/serial/uftdi.c (revision d2b2128a286a00ee53d79cb88b4e59bf42525cf9)
1 /*	$NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Lennart Augustsson (lennart@augustsson.net).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41 
42 /*
43  * NOTE: all function names beginning like "uftdi_cfg_" can only
44  * be called from within the config thread function !
45  */
46 
47 /*
48  * FTDI FT8U100AX serial adapter driver
49  */
50 
51 #include "usbdevs.h"
52 #include <dev/usb/usb.h>
53 #include <dev/usb/usb_mfunc.h>
54 #include <dev/usb/usb_error.h>
55 #include <dev/usb/usb_cdc.h>
56 
57 #define	USB_DEBUG_VAR uftdi_debug
58 
59 #include <dev/usb/usb_core.h>
60 #include <dev/usb/usb_debug.h>
61 #include <dev/usb/usb_process.h>
62 #include <dev/usb/usb_request.h>
63 #include <dev/usb/usb_lookup.h>
64 #include <dev/usb/usb_util.h>
65 #include <dev/usb/usb_busdma.h>
66 
67 #include <dev/usb/serial/usb_serial.h>
68 #include <dev/usb/serial/uftdi_reg.h>
69 
70 #if USB_DEBUG
71 static int uftdi_debug = 0;
72 
73 SYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
74 SYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW,
75     &uftdi_debug, 0, "Debug level");
76 #endif
77 
78 #define	UFTDI_CONFIG_INDEX	0
79 #define	UFTDI_IFACE_INDEX	0
80 
81 #define	UFTDI_IBUFSIZE 64		/* bytes, maximum number of bytes per
82 					 * frame */
83 #define	UFTDI_OBUFSIZE 64		/* bytes, cannot be increased due to
84 					 * do size encoding */
85 
86 enum {
87 	UFTDI_BULK_DT_WR,
88 	UFTDI_BULK_DT_RD,
89 	UFTDI_N_TRANSFER,
90 };
91 
92 struct uftdi_softc {
93 	struct usb2_com_super_softc sc_super_ucom;
94 	struct usb2_com_softc sc_ucom;
95 
96 	struct usb2_device *sc_udev;
97 	struct usb2_xfer *sc_xfer[UFTDI_N_TRANSFER];
98 	device_t sc_dev;
99 	struct mtx sc_mtx;
100 
101 	uint32_t sc_unit;
102 	enum uftdi_type sc_type;
103 
104 	uint16_t sc_last_lcr;
105 
106 	uint8_t	sc_iface_index;
107 	uint8_t	sc_hdrlen;
108 	uint8_t	sc_msr;
109 	uint8_t	sc_lsr;
110 
111 	uint8_t	sc_name[16];
112 };
113 
114 struct uftdi_param_config {
115 	uint16_t rate;
116 	uint16_t lcr;
117 	uint8_t	v_start;
118 	uint8_t	v_stop;
119 	uint8_t	v_flow;
120 };
121 
122 /* prototypes */
123 
124 static device_probe_t uftdi_probe;
125 static device_attach_t uftdi_attach;
126 static device_detach_t uftdi_detach;
127 
128 static usb2_callback_t uftdi_write_callback;
129 static usb2_callback_t uftdi_read_callback;
130 
131 static void	uftdi_cfg_open(struct usb2_com_softc *);
132 static void	uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
133 static void	uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t);
134 static void	uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t);
135 static int	uftdi_set_parm_soft(struct termios *,
136 		    struct uftdi_param_config *, uint8_t);
137 static int	uftdi_pre_param(struct usb2_com_softc *, struct termios *);
138 static void	uftdi_cfg_param(struct usb2_com_softc *, struct termios *);
139 static void	uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *,
140 		    uint8_t *);
141 static void	uftdi_start_read(struct usb2_com_softc *);
142 static void	uftdi_stop_read(struct usb2_com_softc *);
143 static void	uftdi_start_write(struct usb2_com_softc *);
144 static void	uftdi_stop_write(struct usb2_com_softc *);
145 static uint8_t	uftdi_8u232am_getrate(uint32_t, uint16_t *);
146 
147 static const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = {
148 
149 	[UFTDI_BULK_DT_WR] = {
150 		.type = UE_BULK,
151 		.endpoint = UE_ADDR_ANY,
152 		.direction = UE_DIR_OUT,
153 		.mh.bufsize = UFTDI_OBUFSIZE,
154 		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
155 		.mh.callback = &uftdi_write_callback,
156 	},
157 
158 	[UFTDI_BULK_DT_RD] = {
159 		.type = UE_BULK,
160 		.endpoint = UE_ADDR_ANY,
161 		.direction = UE_DIR_IN,
162 		.mh.bufsize = UFTDI_IBUFSIZE,
163 		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
164 		.mh.callback = &uftdi_read_callback,
165 	},
166 };
167 
168 static const struct usb2_com_callback uftdi_callback = {
169 	.usb2_com_cfg_get_status = &uftdi_cfg_get_status,
170 	.usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr,
171 	.usb2_com_cfg_set_rts = &uftdi_cfg_set_rts,
172 	.usb2_com_cfg_set_break = &uftdi_cfg_set_break,
173 	.usb2_com_cfg_param = &uftdi_cfg_param,
174 	.usb2_com_cfg_open = &uftdi_cfg_open,
175 	.usb2_com_pre_param = &uftdi_pre_param,
176 	.usb2_com_start_read = &uftdi_start_read,
177 	.usb2_com_stop_read = &uftdi_stop_read,
178 	.usb2_com_start_write = &uftdi_start_write,
179 	.usb2_com_stop_write = &uftdi_stop_write,
180 };
181 
182 static device_method_t uftdi_methods[] = {
183 	/* Device interface */
184 	DEVMETHOD(device_probe, uftdi_probe),
185 	DEVMETHOD(device_attach, uftdi_attach),
186 	DEVMETHOD(device_detach, uftdi_detach),
187 
188 	{0, 0}
189 };
190 
191 static devclass_t uftdi_devclass;
192 
193 static driver_t uftdi_driver = {
194 	.name = "uftdi",
195 	.methods = uftdi_methods,
196 	.size = sizeof(struct uftdi_softc),
197 };
198 
199 DRIVER_MODULE(uftdi, uhub, uftdi_driver, uftdi_devclass, NULL, 0);
200 MODULE_DEPEND(uftdi, ucom, 1, 1, 1);
201 MODULE_DEPEND(uftdi, usb, 1, 1, 1);
202 
203 static struct usb2_device_id uftdi_devs[] = {
204 	{USB_VPI(USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_STK541, UFTDI_TYPE_8U232AM)},
205 	{USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD, UFTDI_TYPE_8U232AM)},
206 	{USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_WIRELESSHANDHELDTERMINAL, UFTDI_TYPE_8U232AM)},
207 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)},
208 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)},
209 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)},
210 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)},
211 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)},
212 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)},
213 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)},
214 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)},
215 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)},
216 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)},
217 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)},
218 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)},
219 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)},
220 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)},
221 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)},
222 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)},
223 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)},
224 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)},
225 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)},
226 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)},
227 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)},
228 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)},
229 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)},
230 	{USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)},
231 	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)},
232 	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)},
233 	{USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)},
234 	{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)},
235 };
236 
237 static int
238 uftdi_probe(device_t dev)
239 {
240 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
241 
242 	if (uaa->usb2_mode != USB_MODE_HOST) {
243 		return (ENXIO);
244 	}
245 	if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
246 		return (ENXIO);
247 	}
248 	/* attach to all present interfaces */
249 
250 	return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
251 }
252 
253 static int
254 uftdi_attach(device_t dev)
255 {
256 	struct usb2_attach_arg *uaa = device_get_ivars(dev);
257 	struct uftdi_softc *sc = device_get_softc(dev);
258 	int error;
259 
260 	sc->sc_udev = uaa->device;
261 	sc->sc_dev = dev;
262 	sc->sc_unit = device_get_unit(dev);
263 
264 	device_set_usb2_desc(dev);
265 	mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF);
266 
267 	snprintf(sc->sc_name, sizeof(sc->sc_name),
268 	    "%s", device_get_nameunit(dev));
269 
270 	DPRINTF("\n");
271 
272 	sc->sc_iface_index = uaa->info.bIfaceIndex;
273 	sc->sc_type = USB_GET_DRIVER_INFO(uaa);
274 
275 	switch (sc->sc_type) {
276 	case UFTDI_TYPE_SIO:
277 		sc->sc_hdrlen = 1;
278 		break;
279 	case UFTDI_TYPE_8U232AM:
280 	default:
281 		sc->sc_hdrlen = 0;
282 		break;
283 	}
284 
285 	error = usb2_transfer_setup(uaa->device,
286 	    &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
287 	    UFTDI_N_TRANSFER, sc, &sc->sc_mtx);
288 
289 	if (error) {
290 		device_printf(dev, "allocating USB "
291 		    "transfers failed!\n");
292 		goto detach;
293 	}
294 	sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
295 
296 	/* clear stall at first run */
297 	mtx_lock(&sc->sc_mtx);
298 	usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
299 	usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
300 	mtx_unlock(&sc->sc_mtx);
301 
302 	/* set a valid "lcr" value */
303 
304 	sc->sc_last_lcr =
305 	    (FTDI_SIO_SET_DATA_STOP_BITS_2 |
306 	    FTDI_SIO_SET_DATA_PARITY_NONE |
307 	    FTDI_SIO_SET_DATA_BITS(8));
308 
309 	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
310 	    &uftdi_callback, &sc->sc_mtx);
311 	if (error) {
312 		goto detach;
313 	}
314 	return (0);			/* success */
315 
316 detach:
317 	uftdi_detach(dev);
318 	return (ENXIO);
319 }
320 
321 static int
322 uftdi_detach(device_t dev)
323 {
324 	struct uftdi_softc *sc = device_get_softc(dev);
325 
326 	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
327 	usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
328 	mtx_destroy(&sc->sc_mtx);
329 
330 	return (0);
331 }
332 
333 static void
334 uftdi_cfg_open(struct usb2_com_softc *ucom)
335 {
336 	struct uftdi_softc *sc = ucom->sc_parent;
337 	uint16_t wIndex = ucom->sc_portno;
338 	struct usb2_device_request req;
339 
340 	DPRINTF("");
341 
342 	/* perform a full reset on the device */
343 
344 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
345 	req.bRequest = FTDI_SIO_RESET;
346 	USETW(req.wValue, FTDI_SIO_RESET_SIO);
347 	USETW(req.wIndex, wIndex);
348 	USETW(req.wLength, 0);
349 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
350 	    &req, NULL, 0, 1000);
351 
352 	/* turn on RTS/CTS flow control */
353 
354 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
355 	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
356 	USETW(req.wValue, 0);
357 	USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
358 	USETW(req.wLength, 0);
359 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
360 	    &req, NULL, 0, 1000);
361 
362 	/*
363 	 * NOTE: with the new UCOM layer there will always be a
364 	 * "uftdi_cfg_param()" call after "open()", so there is no need for
365 	 * "open()" to configure anything
366 	 */
367 }
368 
369 static void
370 uftdi_write_callback(struct usb2_xfer *xfer)
371 {
372 	struct uftdi_softc *sc = xfer->priv_sc;
373 	uint32_t actlen;
374 	uint8_t buf[1];
375 
376 	switch (USB_GET_STATE(xfer)) {
377 	case USB_ST_SETUP:
378 	case USB_ST_TRANSFERRED:
379 tr_setup:
380 		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers,
381 		    sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
382 		    &actlen)) {
383 
384 			if (sc->sc_hdrlen > 0) {
385 				buf[0] =
386 				    FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
387 				usb2_copy_in(xfer->frbuffers, 0, buf, 1);
388 			}
389 			xfer->frlengths[0] = actlen + sc->sc_hdrlen;
390 			usb2_start_hardware(xfer);
391 		}
392 		return;
393 
394 	default:			/* Error */
395 		if (xfer->error != USB_ERR_CANCELLED) {
396 			/* try to clear stall first */
397 			xfer->flags.stall_pipe = 1;
398 			goto tr_setup;
399 		}
400 		return;
401 	}
402 }
403 
404 static void
405 uftdi_read_callback(struct usb2_xfer *xfer)
406 {
407 	struct uftdi_softc *sc = xfer->priv_sc;
408 	uint8_t buf[2];
409 	uint8_t ftdi_msr;
410 	uint8_t msr;
411 	uint8_t lsr;
412 
413 	switch (USB_GET_STATE(xfer)) {
414 	case USB_ST_TRANSFERRED:
415 
416 		if (xfer->actlen < 2) {
417 			goto tr_setup;
418 		}
419 		usb2_copy_out(xfer->frbuffers, 0, buf, 2);
420 
421 		ftdi_msr = FTDI_GET_MSR(buf);
422 		lsr = FTDI_GET_LSR(buf);
423 
424 		msr = 0;
425 		if (ftdi_msr & FTDI_SIO_CTS_MASK)
426 			msr |= SER_CTS;
427 		if (ftdi_msr & FTDI_SIO_DSR_MASK)
428 			msr |= SER_DSR;
429 		if (ftdi_msr & FTDI_SIO_RI_MASK)
430 			msr |= SER_RI;
431 		if (ftdi_msr & FTDI_SIO_RLSD_MASK)
432 			msr |= SER_DCD;
433 
434 		if ((sc->sc_msr != msr) ||
435 		    ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
436 			DPRINTF("status change msr=0x%02x (0x%02x) "
437 			    "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
438 			    lsr, sc->sc_lsr);
439 
440 			sc->sc_msr = msr;
441 			sc->sc_lsr = lsr;
442 
443 			usb2_com_status_change(&sc->sc_ucom);
444 		}
445 		xfer->actlen -= 2;
446 
447 		if (xfer->actlen > 0) {
448 			usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2,
449 			    xfer->actlen);
450 		}
451 	case USB_ST_SETUP:
452 tr_setup:
453 		xfer->frlengths[0] = xfer->max_data_length;
454 		usb2_start_hardware(xfer);
455 		return;
456 
457 	default:			/* Error */
458 		if (xfer->error != USB_ERR_CANCELLED) {
459 			/* try to clear stall first */
460 			xfer->flags.stall_pipe = 1;
461 			goto tr_setup;
462 		}
463 		return;
464 	}
465 }
466 
467 static void
468 uftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
469 {
470 	struct uftdi_softc *sc = ucom->sc_parent;
471 	uint16_t wIndex = ucom->sc_portno;
472 	uint16_t wValue;
473 	struct usb2_device_request req;
474 
475 	wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
476 
477 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
478 	req.bRequest = FTDI_SIO_MODEM_CTRL;
479 	USETW(req.wValue, wValue);
480 	USETW(req.wIndex, wIndex);
481 	USETW(req.wLength, 0);
482 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
483 	    &req, NULL, 0, 1000);
484 }
485 
486 static void
487 uftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
488 {
489 	struct uftdi_softc *sc = ucom->sc_parent;
490 	uint16_t wIndex = ucom->sc_portno;
491 	uint16_t wValue;
492 	struct usb2_device_request req;
493 
494 	wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
495 
496 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
497 	req.bRequest = FTDI_SIO_MODEM_CTRL;
498 	USETW(req.wValue, wValue);
499 	USETW(req.wIndex, wIndex);
500 	USETW(req.wLength, 0);
501 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
502 	    &req, NULL, 0, 1000);
503 }
504 
505 static void
506 uftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
507 {
508 	struct uftdi_softc *sc = ucom->sc_parent;
509 	uint16_t wIndex = ucom->sc_portno;
510 	uint16_t wValue;
511 	struct usb2_device_request req;
512 
513 	if (onoff) {
514 		sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
515 	} else {
516 		sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
517 	}
518 
519 	wValue = sc->sc_last_lcr;
520 
521 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
522 	req.bRequest = FTDI_SIO_SET_DATA;
523 	USETW(req.wValue, wValue);
524 	USETW(req.wIndex, wIndex);
525 	USETW(req.wLength, 0);
526 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
527 	    &req, NULL, 0, 1000);
528 }
529 
530 static int
531 uftdi_set_parm_soft(struct termios *t,
532     struct uftdi_param_config *cfg, uint8_t type)
533 {
534 	bzero(cfg, sizeof(*cfg));
535 
536 	switch (type) {
537 	case UFTDI_TYPE_SIO:
538 		switch (t->c_ospeed) {
539 		case 300:
540 			cfg->rate = ftdi_sio_b300;
541 			break;
542 		case 600:
543 			cfg->rate = ftdi_sio_b600;
544 			break;
545 		case 1200:
546 			cfg->rate = ftdi_sio_b1200;
547 			break;
548 		case 2400:
549 			cfg->rate = ftdi_sio_b2400;
550 			break;
551 		case 4800:
552 			cfg->rate = ftdi_sio_b4800;
553 			break;
554 		case 9600:
555 			cfg->rate = ftdi_sio_b9600;
556 			break;
557 		case 19200:
558 			cfg->rate = ftdi_sio_b19200;
559 			break;
560 		case 38400:
561 			cfg->rate = ftdi_sio_b38400;
562 			break;
563 		case 57600:
564 			cfg->rate = ftdi_sio_b57600;
565 			break;
566 		case 115200:
567 			cfg->rate = ftdi_sio_b115200;
568 			break;
569 		default:
570 			return (EINVAL);
571 		}
572 		break;
573 
574 	case UFTDI_TYPE_8U232AM:
575 		if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
576 			return (EINVAL);
577 		}
578 		break;
579 	}
580 
581 	if (t->c_cflag & CSTOPB)
582 		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
583 	else
584 		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
585 
586 	if (t->c_cflag & PARENB) {
587 		if (t->c_cflag & PARODD) {
588 			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
589 		} else {
590 			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
591 		}
592 	} else {
593 		cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
594 	}
595 
596 	switch (t->c_cflag & CSIZE) {
597 	case CS5:
598 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
599 		break;
600 
601 	case CS6:
602 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
603 		break;
604 
605 	case CS7:
606 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
607 		break;
608 
609 	case CS8:
610 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
611 		break;
612 	}
613 
614 	if (t->c_cflag & CRTSCTS) {
615 		cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
616 	} else if (t->c_iflag & (IXON | IXOFF)) {
617 		cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
618 		cfg->v_start = t->c_cc[VSTART];
619 		cfg->v_stop = t->c_cc[VSTOP];
620 	} else {
621 		cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
622 	}
623 
624 	return (0);
625 }
626 
627 static int
628 uftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t)
629 {
630 	struct uftdi_softc *sc = ucom->sc_parent;
631 	struct uftdi_param_config cfg;
632 
633 	DPRINTF("\n");
634 
635 	return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
636 }
637 
638 static void
639 uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
640 {
641 	struct uftdi_softc *sc = ucom->sc_parent;
642 	uint16_t wIndex = ucom->sc_portno;
643 	struct uftdi_param_config cfg;
644 	struct usb2_device_request req;
645 
646 	if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
647 		/* should not happen */
648 		return;
649 	}
650 	sc->sc_last_lcr = cfg.lcr;
651 
652 	DPRINTF("\n");
653 
654 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
655 	req.bRequest = FTDI_SIO_SET_BAUD_RATE;
656 	USETW(req.wValue, cfg.rate);
657 	USETW(req.wIndex, wIndex);
658 	USETW(req.wLength, 0);
659 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
660 	    &req, NULL, 0, 1000);
661 
662 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
663 	req.bRequest = FTDI_SIO_SET_DATA;
664 	USETW(req.wValue, cfg.lcr);
665 	USETW(req.wIndex, wIndex);
666 	USETW(req.wLength, 0);
667 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
668 	    &req, NULL, 0, 1000);
669 
670 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
671 	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
672 	USETW2(req.wValue, cfg.v_stop, cfg.v_start);
673 	USETW2(req.wIndex, cfg.v_flow, wIndex);
674 	USETW(req.wLength, 0);
675 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
676 	    &req, NULL, 0, 1000);
677 }
678 
679 static void
680 uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
681 {
682 	struct uftdi_softc *sc = ucom->sc_parent;
683 
684 	DPRINTF("msr=0x%02x lsr=0x%02x\n",
685 	    sc->sc_msr, sc->sc_lsr);
686 
687 	*msr = sc->sc_msr;
688 	*lsr = sc->sc_lsr;
689 }
690 
691 static void
692 uftdi_start_read(struct usb2_com_softc *ucom)
693 {
694 	struct uftdi_softc *sc = ucom->sc_parent;
695 
696 	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
697 }
698 
699 static void
700 uftdi_stop_read(struct usb2_com_softc *ucom)
701 {
702 	struct uftdi_softc *sc = ucom->sc_parent;
703 
704 	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
705 }
706 
707 static void
708 uftdi_start_write(struct usb2_com_softc *ucom)
709 {
710 	struct uftdi_softc *sc = ucom->sc_parent;
711 
712 	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
713 }
714 
715 static void
716 uftdi_stop_write(struct usb2_com_softc *ucom)
717 {
718 	struct uftdi_softc *sc = ucom->sc_parent;
719 
720 	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
721 }
722 
723 /*------------------------------------------------------------------------*
724  *	uftdi_8u232am_getrate
725  *
726  * Return values:
727  *    0: Success
728  * Else: Failure
729  *------------------------------------------------------------------------*/
730 static uint8_t
731 uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
732 {
733 	/* Table of the nearest even powers-of-2 for values 0..15. */
734 	static const uint8_t roundoff[16] = {
735 		0, 2, 2, 4, 4, 4, 8, 8,
736 		8, 8, 8, 8, 16, 16, 16, 16,
737 	};
738 	uint32_t d;
739 	uint32_t freq;
740 	uint16_t result;
741 
742 	if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
743 		return (1);		/* prevent numerical overflow */
744 
745 	/* Special cases for 2M and 3M. */
746 	if ((speed >= ((3000000 * 100) / 103)) &&
747 	    (speed <= ((3000000 * 100) / 97))) {
748 		result = 0;
749 		goto done;
750 	}
751 	if ((speed >= ((2000000 * 100) / 103)) &&
752 	    (speed <= ((2000000 * 100) / 97))) {
753 		result = 1;
754 		goto done;
755 	}
756 	d = (FTDI_8U232AM_FREQ << 4) / speed;
757 	d = (d & ~15) + roundoff[d & 15];
758 
759 	if (d < FTDI_8U232AM_MIN_DIV)
760 		d = FTDI_8U232AM_MIN_DIV;
761 	else if (d > FTDI_8U232AM_MAX_DIV)
762 		d = FTDI_8U232AM_MAX_DIV;
763 
764 	/*
765 	 * Calculate the frequency needed for "d" to exactly divide down to
766 	 * our target "speed", and check that the actual frequency is within
767 	 * 3% of this.
768 	 */
769 	freq = (speed * d);
770 	if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
771 	    (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
772 		return (1);
773 
774 	/*
775 	 * Pack the divisor into the resultant value.  The lower 14-bits
776 	 * hold the integral part, while the upper 2 bits encode the
777 	 * fractional component: either 0, 0.5, 0.25, or 0.125.
778 	 */
779 	result = (d >> 4);
780 	if (d & 8)
781 		result |= 0x4000;
782 	else if (d & 4)
783 		result |= 0x8000;
784 	else if (d & 2)
785 		result |= 0xc000;
786 
787 done:
788 	*rate = result;
789 	return (0);
790 }
791