xref: /freebsd/sys/dev/usb/serial/uftdi.c (revision 195ebc7e9e4b129de810833791a19dfb4349d6a9)
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_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
74 SYSCTL_INT(_hw_usb_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 ucom_super_softc sc_super_ucom;
94 	struct ucom_softc sc_ucom;
95 
96 	struct usb_device *sc_udev;
97 	struct usb_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 usb_callback_t uftdi_write_callback;
129 static usb_callback_t uftdi_read_callback;
130 
131 static void	uftdi_cfg_open(struct ucom_softc *);
132 static void	uftdi_cfg_set_dtr(struct ucom_softc *, uint8_t);
133 static void	uftdi_cfg_set_rts(struct ucom_softc *, uint8_t);
134 static void	uftdi_cfg_set_break(struct ucom_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 ucom_softc *, struct termios *);
138 static void	uftdi_cfg_param(struct ucom_softc *, struct termios *);
139 static void	uftdi_cfg_get_status(struct ucom_softc *, uint8_t *,
140 		    uint8_t *);
141 static void	uftdi_start_read(struct ucom_softc *);
142 static void	uftdi_stop_read(struct ucom_softc *);
143 static void	uftdi_start_write(struct ucom_softc *);
144 static void	uftdi_stop_write(struct ucom_softc *);
145 static uint8_t	uftdi_8u232am_getrate(uint32_t, uint16_t *);
146 
147 static const struct usb_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 		.bufsize = UFTDI_OBUFSIZE,
154 		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
155 		.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 		.bufsize = UFTDI_IBUFSIZE,
163 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
164 		.callback = &uftdi_read_callback,
165 	},
166 };
167 
168 static const struct ucom_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 usb_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_SERIAL_8U232AM4, UFTDI_TYPE_8U232AM)},
211 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)},
212 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)},
213 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)},
214 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)},
215 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)},
216 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)},
217 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)},
218 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)},
219 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)},
220 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)},
221 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)},
222 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)},
223 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)},
224 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)},
225 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)},
226 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)},
227 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)},
228 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)},
229 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)},
230 	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)},
231 	{USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)},
232 	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)},
233 	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)},
234 	{USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)},
235 	{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)},
236 };
237 
238 static int
239 uftdi_probe(device_t dev)
240 {
241 	struct usb_attach_arg *uaa = device_get_ivars(dev);
242 
243 	if (uaa->usb_mode != USB_MODE_HOST) {
244 		return (ENXIO);
245 	}
246 	if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
247 		return (ENXIO);
248 	}
249 	/* attach to all present interfaces */
250 
251 	return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
252 }
253 
254 static int
255 uftdi_attach(device_t dev)
256 {
257 	struct usb_attach_arg *uaa = device_get_ivars(dev);
258 	struct uftdi_softc *sc = device_get_softc(dev);
259 	int error;
260 
261 	sc->sc_udev = uaa->device;
262 	sc->sc_dev = dev;
263 	sc->sc_unit = device_get_unit(dev);
264 
265 	device_set_usb2_desc(dev);
266 	mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF);
267 
268 	snprintf(sc->sc_name, sizeof(sc->sc_name),
269 	    "%s", device_get_nameunit(dev));
270 
271 	DPRINTF("\n");
272 
273 	sc->sc_iface_index = uaa->info.bIfaceIndex;
274 	sc->sc_type = USB_GET_DRIVER_INFO(uaa);
275 
276 	switch (sc->sc_type) {
277 	case UFTDI_TYPE_SIO:
278 		sc->sc_hdrlen = 1;
279 		break;
280 	case UFTDI_TYPE_8U232AM:
281 	default:
282 		sc->sc_hdrlen = 0;
283 		break;
284 	}
285 
286 	error = usb2_transfer_setup(uaa->device,
287 	    &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
288 	    UFTDI_N_TRANSFER, sc, &sc->sc_mtx);
289 
290 	if (error) {
291 		device_printf(dev, "allocating USB "
292 		    "transfers failed!\n");
293 		goto detach;
294 	}
295 	sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
296 
297 	/* clear stall at first run */
298 	mtx_lock(&sc->sc_mtx);
299 	usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
300 	usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
301 	mtx_unlock(&sc->sc_mtx);
302 
303 	/* set a valid "lcr" value */
304 
305 	sc->sc_last_lcr =
306 	    (FTDI_SIO_SET_DATA_STOP_BITS_2 |
307 	    FTDI_SIO_SET_DATA_PARITY_NONE |
308 	    FTDI_SIO_SET_DATA_BITS(8));
309 
310 	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
311 	    &uftdi_callback, &sc->sc_mtx);
312 	if (error) {
313 		goto detach;
314 	}
315 	return (0);			/* success */
316 
317 detach:
318 	uftdi_detach(dev);
319 	return (ENXIO);
320 }
321 
322 static int
323 uftdi_detach(device_t dev)
324 {
325 	struct uftdi_softc *sc = device_get_softc(dev);
326 
327 	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
328 	usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
329 	mtx_destroy(&sc->sc_mtx);
330 
331 	return (0);
332 }
333 
334 static void
335 uftdi_cfg_open(struct ucom_softc *ucom)
336 {
337 	struct uftdi_softc *sc = ucom->sc_parent;
338 	uint16_t wIndex = ucom->sc_portno;
339 	struct usb_device_request req;
340 
341 	DPRINTF("");
342 
343 	/* perform a full reset on the device */
344 
345 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
346 	req.bRequest = FTDI_SIO_RESET;
347 	USETW(req.wValue, FTDI_SIO_RESET_SIO);
348 	USETW(req.wIndex, wIndex);
349 	USETW(req.wLength, 0);
350 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
351 	    &req, NULL, 0, 1000);
352 
353 	/* turn on RTS/CTS flow control */
354 
355 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
356 	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
357 	USETW(req.wValue, 0);
358 	USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
359 	USETW(req.wLength, 0);
360 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
361 	    &req, NULL, 0, 1000);
362 
363 	/*
364 	 * NOTE: with the new UCOM layer there will always be a
365 	 * "uftdi_cfg_param()" call after "open()", so there is no need for
366 	 * "open()" to configure anything
367 	 */
368 }
369 
370 static void
371 uftdi_write_callback(struct usb_xfer *xfer)
372 {
373 	struct uftdi_softc *sc = xfer->priv_sc;
374 	uint32_t actlen;
375 	uint8_t buf[1];
376 
377 	switch (USB_GET_STATE(xfer)) {
378 	case USB_ST_SETUP:
379 	case USB_ST_TRANSFERRED:
380 tr_setup:
381 		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers,
382 		    sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
383 		    &actlen)) {
384 
385 			if (sc->sc_hdrlen > 0) {
386 				buf[0] =
387 				    FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
388 				usb2_copy_in(xfer->frbuffers, 0, buf, 1);
389 			}
390 			xfer->frlengths[0] = actlen + sc->sc_hdrlen;
391 			usb2_start_hardware(xfer);
392 		}
393 		return;
394 
395 	default:			/* Error */
396 		if (xfer->error != USB_ERR_CANCELLED) {
397 			/* try to clear stall first */
398 			xfer->flags.stall_pipe = 1;
399 			goto tr_setup;
400 		}
401 		return;
402 	}
403 }
404 
405 static void
406 uftdi_read_callback(struct usb_xfer *xfer)
407 {
408 	struct uftdi_softc *sc = xfer->priv_sc;
409 	uint8_t buf[2];
410 	uint8_t ftdi_msr;
411 	uint8_t msr;
412 	uint8_t lsr;
413 
414 	switch (USB_GET_STATE(xfer)) {
415 	case USB_ST_TRANSFERRED:
416 
417 		if (xfer->actlen < 2) {
418 			goto tr_setup;
419 		}
420 		usb2_copy_out(xfer->frbuffers, 0, buf, 2);
421 
422 		ftdi_msr = FTDI_GET_MSR(buf);
423 		lsr = FTDI_GET_LSR(buf);
424 
425 		msr = 0;
426 		if (ftdi_msr & FTDI_SIO_CTS_MASK)
427 			msr |= SER_CTS;
428 		if (ftdi_msr & FTDI_SIO_DSR_MASK)
429 			msr |= SER_DSR;
430 		if (ftdi_msr & FTDI_SIO_RI_MASK)
431 			msr |= SER_RI;
432 		if (ftdi_msr & FTDI_SIO_RLSD_MASK)
433 			msr |= SER_DCD;
434 
435 		if ((sc->sc_msr != msr) ||
436 		    ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
437 			DPRINTF("status change msr=0x%02x (0x%02x) "
438 			    "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
439 			    lsr, sc->sc_lsr);
440 
441 			sc->sc_msr = msr;
442 			sc->sc_lsr = lsr;
443 
444 			usb2_com_status_change(&sc->sc_ucom);
445 		}
446 		xfer->actlen -= 2;
447 
448 		if (xfer->actlen > 0) {
449 			usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2,
450 			    xfer->actlen);
451 		}
452 	case USB_ST_SETUP:
453 tr_setup:
454 		xfer->frlengths[0] = xfer->max_data_length;
455 		usb2_start_hardware(xfer);
456 		return;
457 
458 	default:			/* Error */
459 		if (xfer->error != USB_ERR_CANCELLED) {
460 			/* try to clear stall first */
461 			xfer->flags.stall_pipe = 1;
462 			goto tr_setup;
463 		}
464 		return;
465 	}
466 }
467 
468 static void
469 uftdi_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
470 {
471 	struct uftdi_softc *sc = ucom->sc_parent;
472 	uint16_t wIndex = ucom->sc_portno;
473 	uint16_t wValue;
474 	struct usb_device_request req;
475 
476 	wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
477 
478 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
479 	req.bRequest = FTDI_SIO_MODEM_CTRL;
480 	USETW(req.wValue, wValue);
481 	USETW(req.wIndex, wIndex);
482 	USETW(req.wLength, 0);
483 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
484 	    &req, NULL, 0, 1000);
485 }
486 
487 static void
488 uftdi_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
489 {
490 	struct uftdi_softc *sc = ucom->sc_parent;
491 	uint16_t wIndex = ucom->sc_portno;
492 	uint16_t wValue;
493 	struct usb_device_request req;
494 
495 	wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
496 
497 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
498 	req.bRequest = FTDI_SIO_MODEM_CTRL;
499 	USETW(req.wValue, wValue);
500 	USETW(req.wIndex, wIndex);
501 	USETW(req.wLength, 0);
502 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
503 	    &req, NULL, 0, 1000);
504 }
505 
506 static void
507 uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
508 {
509 	struct uftdi_softc *sc = ucom->sc_parent;
510 	uint16_t wIndex = ucom->sc_portno;
511 	uint16_t wValue;
512 	struct usb_device_request req;
513 
514 	if (onoff) {
515 		sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
516 	} else {
517 		sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
518 	}
519 
520 	wValue = sc->sc_last_lcr;
521 
522 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
523 	req.bRequest = FTDI_SIO_SET_DATA;
524 	USETW(req.wValue, wValue);
525 	USETW(req.wIndex, wIndex);
526 	USETW(req.wLength, 0);
527 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
528 	    &req, NULL, 0, 1000);
529 }
530 
531 static int
532 uftdi_set_parm_soft(struct termios *t,
533     struct uftdi_param_config *cfg, uint8_t type)
534 {
535 	bzero(cfg, sizeof(*cfg));
536 
537 	switch (type) {
538 	case UFTDI_TYPE_SIO:
539 		switch (t->c_ospeed) {
540 		case 300:
541 			cfg->rate = ftdi_sio_b300;
542 			break;
543 		case 600:
544 			cfg->rate = ftdi_sio_b600;
545 			break;
546 		case 1200:
547 			cfg->rate = ftdi_sio_b1200;
548 			break;
549 		case 2400:
550 			cfg->rate = ftdi_sio_b2400;
551 			break;
552 		case 4800:
553 			cfg->rate = ftdi_sio_b4800;
554 			break;
555 		case 9600:
556 			cfg->rate = ftdi_sio_b9600;
557 			break;
558 		case 19200:
559 			cfg->rate = ftdi_sio_b19200;
560 			break;
561 		case 38400:
562 			cfg->rate = ftdi_sio_b38400;
563 			break;
564 		case 57600:
565 			cfg->rate = ftdi_sio_b57600;
566 			break;
567 		case 115200:
568 			cfg->rate = ftdi_sio_b115200;
569 			break;
570 		default:
571 			return (EINVAL);
572 		}
573 		break;
574 
575 	case UFTDI_TYPE_8U232AM:
576 		if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
577 			return (EINVAL);
578 		}
579 		break;
580 	}
581 
582 	if (t->c_cflag & CSTOPB)
583 		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
584 	else
585 		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
586 
587 	if (t->c_cflag & PARENB) {
588 		if (t->c_cflag & PARODD) {
589 			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
590 		} else {
591 			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
592 		}
593 	} else {
594 		cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
595 	}
596 
597 	switch (t->c_cflag & CSIZE) {
598 	case CS5:
599 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
600 		break;
601 
602 	case CS6:
603 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
604 		break;
605 
606 	case CS7:
607 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
608 		break;
609 
610 	case CS8:
611 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
612 		break;
613 	}
614 
615 	if (t->c_cflag & CRTSCTS) {
616 		cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
617 	} else if (t->c_iflag & (IXON | IXOFF)) {
618 		cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
619 		cfg->v_start = t->c_cc[VSTART];
620 		cfg->v_stop = t->c_cc[VSTOP];
621 	} else {
622 		cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
623 	}
624 
625 	return (0);
626 }
627 
628 static int
629 uftdi_pre_param(struct ucom_softc *ucom, struct termios *t)
630 {
631 	struct uftdi_softc *sc = ucom->sc_parent;
632 	struct uftdi_param_config cfg;
633 
634 	DPRINTF("\n");
635 
636 	return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
637 }
638 
639 static void
640 uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t)
641 {
642 	struct uftdi_softc *sc = ucom->sc_parent;
643 	uint16_t wIndex = ucom->sc_portno;
644 	struct uftdi_param_config cfg;
645 	struct usb_device_request req;
646 
647 	if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
648 		/* should not happen */
649 		return;
650 	}
651 	sc->sc_last_lcr = cfg.lcr;
652 
653 	DPRINTF("\n");
654 
655 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
656 	req.bRequest = FTDI_SIO_SET_BAUD_RATE;
657 	USETW(req.wValue, cfg.rate);
658 	USETW(req.wIndex, wIndex);
659 	USETW(req.wLength, 0);
660 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
661 	    &req, NULL, 0, 1000);
662 
663 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
664 	req.bRequest = FTDI_SIO_SET_DATA;
665 	USETW(req.wValue, cfg.lcr);
666 	USETW(req.wIndex, wIndex);
667 	USETW(req.wLength, 0);
668 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
669 	    &req, NULL, 0, 1000);
670 
671 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
672 	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
673 	USETW2(req.wValue, cfg.v_stop, cfg.v_start);
674 	USETW2(req.wIndex, cfg.v_flow, wIndex);
675 	USETW(req.wLength, 0);
676 	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
677 	    &req, NULL, 0, 1000);
678 }
679 
680 static void
681 uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
682 {
683 	struct uftdi_softc *sc = ucom->sc_parent;
684 
685 	DPRINTF("msr=0x%02x lsr=0x%02x\n",
686 	    sc->sc_msr, sc->sc_lsr);
687 
688 	*msr = sc->sc_msr;
689 	*lsr = sc->sc_lsr;
690 }
691 
692 static void
693 uftdi_start_read(struct ucom_softc *ucom)
694 {
695 	struct uftdi_softc *sc = ucom->sc_parent;
696 
697 	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
698 }
699 
700 static void
701 uftdi_stop_read(struct ucom_softc *ucom)
702 {
703 	struct uftdi_softc *sc = ucom->sc_parent;
704 
705 	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
706 }
707 
708 static void
709 uftdi_start_write(struct ucom_softc *ucom)
710 {
711 	struct uftdi_softc *sc = ucom->sc_parent;
712 
713 	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
714 }
715 
716 static void
717 uftdi_stop_write(struct ucom_softc *ucom)
718 {
719 	struct uftdi_softc *sc = ucom->sc_parent;
720 
721 	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
722 }
723 
724 /*------------------------------------------------------------------------*
725  *	uftdi_8u232am_getrate
726  *
727  * Return values:
728  *    0: Success
729  * Else: Failure
730  *------------------------------------------------------------------------*/
731 static uint8_t
732 uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
733 {
734 	/* Table of the nearest even powers-of-2 for values 0..15. */
735 	static const uint8_t roundoff[16] = {
736 		0, 2, 2, 4, 4, 4, 8, 8,
737 		8, 8, 8, 8, 16, 16, 16, 16,
738 	};
739 	uint32_t d;
740 	uint32_t freq;
741 	uint16_t result;
742 
743 	if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
744 		return (1);		/* prevent numerical overflow */
745 
746 	/* Special cases for 2M and 3M. */
747 	if ((speed >= ((3000000 * 100) / 103)) &&
748 	    (speed <= ((3000000 * 100) / 97))) {
749 		result = 0;
750 		goto done;
751 	}
752 	if ((speed >= ((2000000 * 100) / 103)) &&
753 	    (speed <= ((2000000 * 100) / 97))) {
754 		result = 1;
755 		goto done;
756 	}
757 	d = (FTDI_8U232AM_FREQ << 4) / speed;
758 	d = (d & ~15) + roundoff[d & 15];
759 
760 	if (d < FTDI_8U232AM_MIN_DIV)
761 		d = FTDI_8U232AM_MIN_DIV;
762 	else if (d > FTDI_8U232AM_MAX_DIV)
763 		d = FTDI_8U232AM_MAX_DIV;
764 
765 	/*
766 	 * Calculate the frequency needed for "d" to exactly divide down to
767 	 * our target "speed", and check that the actual frequency is within
768 	 * 3% of this.
769 	 */
770 	freq = (speed * d);
771 	if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
772 	    (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
773 		return (1);
774 
775 	/*
776 	 * Pack the divisor into the resultant value.  The lower 14-bits
777 	 * hold the integral part, while the upper 2 bits encode the
778 	 * fractional component: either 0, 0.5, 0.25, or 0.125.
779 	 */
780 	result = (d >> 4);
781 	if (d & 8)
782 		result |= 0x4000;
783 	else if (d & 4)
784 		result |= 0x8000;
785 	else if (d & 2)
786 		result |= 0xc000;
787 
788 done:
789 	*rate = result;
790 	return (0);
791 }
792