xref: /dragonfly/sys/bus/u4b/serial/uftdi.c (revision 61c0377f)
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  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * NOTE: all function names beginning like "uftdi_cfg_" can only
34  * be called from within the config thread function !
35  */
36 
37 /*
38  * FTDI FT8U100AX serial adapter driver
39  */
40 
41 #include <sys/stdint.h>
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/types.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/bus.h>
48 #include <sys/module.h>
49 #include <sys/lock.h>
50 #include <sys/condvar.h>
51 #include <sys/sysctl.h>
52 #include <sys/unistd.h>
53 #include <sys/callout.h>
54 #include <sys/malloc.h>
55 #include <sys/priv.h>
56 #include <sys/serial.h>
57 
58 #include <bus/u4b/usb.h>
59 #include <bus/u4b/usbdi.h>
60 #include <bus/u4b/usbdi_util.h>
61 #include "usbdevs.h"
62 
63 #define	USB_DEBUG_VAR uftdi_debug
64 #include <bus/u4b/usb_debug.h>
65 #include <bus/u4b/usb_process.h>
66 
67 #include <bus/u4b/serial/usb_serial.h>
68 #include <bus/u4b/serial/uftdi_reg.h>
69 
70 #ifdef USB_DEBUG
71 static int uftdi_debug = 0;
72 
73 static 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_OBUFSIZE 64		/* bytes, cannot be increased due to
82 					 * do size encoding */
83 
84 enum {
85 	UFTDI_BULK_DT_WR,
86 	UFTDI_BULK_DT_RD,
87 	UFTDI_N_TRANSFER,
88 };
89 
90 struct uftdi_softc {
91 	struct ucom_super_softc sc_super_ucom;
92 	struct ucom_softc sc_ucom;
93 
94 	struct usb_device *sc_udev;
95 	struct usb_xfer *sc_xfer[UFTDI_N_TRANSFER];
96 	device_t sc_dev;
97 	struct lock sc_lock;
98 
99 	uint32_t sc_unit;
100 	enum uftdi_type sc_type;
101 
102 	uint16_t sc_last_lcr;
103 
104 	uint8_t	sc_iface_index;
105 	uint8_t	sc_hdrlen;
106 	uint8_t	sc_msr;
107 	uint8_t	sc_lsr;
108 
109 	uint8_t	sc_name[16];
110 };
111 
112 struct uftdi_param_config {
113 	uint16_t rate;
114 	uint16_t lcr;
115 	uint8_t	v_start;
116 	uint8_t	v_stop;
117 	uint8_t	v_flow;
118 };
119 
120 /* prototypes */
121 
122 static device_probe_t uftdi_probe;
123 static device_attach_t uftdi_attach;
124 static device_detach_t uftdi_detach;
125 
126 static usb_callback_t uftdi_write_callback;
127 static usb_callback_t uftdi_read_callback;
128 
129 static void	uftdi_cfg_open(struct ucom_softc *);
130 static void	uftdi_cfg_set_dtr(struct ucom_softc *, uint8_t);
131 static void	uftdi_cfg_set_rts(struct ucom_softc *, uint8_t);
132 static void	uftdi_cfg_set_break(struct ucom_softc *, uint8_t);
133 static int	uftdi_set_parm_soft(struct termios *,
134 		    struct uftdi_param_config *, uint8_t);
135 static int	uftdi_pre_param(struct ucom_softc *, struct termios *);
136 static void	uftdi_cfg_param(struct ucom_softc *, struct termios *);
137 static void	uftdi_cfg_get_status(struct ucom_softc *, uint8_t *,
138 		    uint8_t *);
139 static void	uftdi_start_read(struct ucom_softc *);
140 static void	uftdi_stop_read(struct ucom_softc *);
141 static void	uftdi_start_write(struct ucom_softc *);
142 static void	uftdi_stop_write(struct ucom_softc *);
143 static uint8_t	uftdi_8u232am_getrate(uint32_t, uint16_t *);
144 static void	uftdi_poll(struct ucom_softc *ucom);
145 
146 static const struct usb_config uftdi_config[UFTDI_N_TRANSFER] = {
147 
148 	[UFTDI_BULK_DT_WR] = {
149 		.type = UE_BULK,
150 		.endpoint = UE_ADDR_ANY,
151 		.direction = UE_DIR_OUT,
152 		.bufsize = UFTDI_OBUFSIZE,
153 		.flags = {.pipe_bof = 1,},
154 		.callback = &uftdi_write_callback,
155 	},
156 
157 	[UFTDI_BULK_DT_RD] = {
158 		.type = UE_BULK,
159 		.endpoint = UE_ADDR_ANY,
160 		.direction = UE_DIR_IN,
161 		.bufsize = 0,		/* use wMaxPacketSize */
162 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
163 		.callback = &uftdi_read_callback,
164 	},
165 };
166 
167 static const struct ucom_callback uftdi_callback = {
168 	.ucom_cfg_get_status = &uftdi_cfg_get_status,
169 	.ucom_cfg_set_dtr = &uftdi_cfg_set_dtr,
170 	.ucom_cfg_set_rts = &uftdi_cfg_set_rts,
171 	.ucom_cfg_set_break = &uftdi_cfg_set_break,
172 	.ucom_cfg_param = &uftdi_cfg_param,
173 	.ucom_cfg_open = &uftdi_cfg_open,
174 	.ucom_pre_param = &uftdi_pre_param,
175 	.ucom_start_read = &uftdi_start_read,
176 	.ucom_stop_read = &uftdi_stop_read,
177 	.ucom_start_write = &uftdi_start_write,
178 	.ucom_stop_write = &uftdi_stop_write,
179 	.ucom_poll = &uftdi_poll,
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 	DEVMETHOD_END
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 MODULE_VERSION(uftdi, 1);
203 
204 static STRUCT_USB_HOST_ID uftdi_devs[] = {
205 #define	UFTDI_DEV(v,p,t) \
206   { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, UFTDI_TYPE_##t) }
207 	UFTDI_DEV(ATMEL, STK541, 8U232AM),
208 	UFTDI_DEV(DRESDENELEKTRONIK, SENSORTERMINALBOARD, 8U232AM),
209 	UFTDI_DEV(DRESDENELEKTRONIK, WIRELESSHANDHELDTERMINAL, 8U232AM),
210 	UFTDI_DEV(FALCOM, TWIST, 8U232AM),
211 	UFTDI_DEV(FTDI, GAMMASCOUT, 8U232AM),
212 	UFTDI_DEV(FTDI, SERIAL_8U100AX, SIO),
213 	UFTDI_DEV(FTDI, SERIAL_2232C, 8U232AM),
214 	UFTDI_DEV(FTDI, SERIAL_2232D, 8U232AM),
215 	UFTDI_DEV(FTDI, BEAGLEBONE, 8U232AM),
216 	UFTDI_DEV(FTDI, SERIAL_4232H, 8U232AM),
217 	UFTDI_DEV(FTDI, SERIAL_8U232AM, 8U232AM),
218 	UFTDI_DEV(FTDI, SERIAL_8U232AM4, 8U232AM),
219 	UFTDI_DEV(FTDI, SEMC_DSS20, 8U232AM),
220 	UFTDI_DEV(FTDI, CFA_631, 8U232AM),
221 	UFTDI_DEV(FTDI, CFA_632, 8U232AM),
222 	UFTDI_DEV(FTDI, CFA_633, 8U232AM),
223 	UFTDI_DEV(FTDI, CFA_634, 8U232AM),
224 	UFTDI_DEV(FTDI, CFA_635, 8U232AM),
225 	UFTDI_DEV(FTDI, USB_UIRT, 8U232AM),
226 	UFTDI_DEV(FTDI, USBSERIAL, 8U232AM),
227 	UFTDI_DEV(FTDI, KBS, 8U232AM),
228 	UFTDI_DEV(FTDI, MX2_3, 8U232AM),
229 	UFTDI_DEV(FTDI, MX4_5, 8U232AM),
230 	UFTDI_DEV(FTDI, LK202, 8U232AM),
231 	UFTDI_DEV(FTDI, LK204, 8U232AM),
232 	UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13M, 8U232AM),
233 	UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13S, 8U232AM),
234 	UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13U, 8U232AM),
235 	UFTDI_DEV(FTDI, EISCOU, 8U232AM),
236 	UFTDI_DEV(FTDI, UOPTBR, 8U232AM),
237 	UFTDI_DEV(FTDI, EMCU2D, 8U232AM),
238 	UFTDI_DEV(FTDI, PCMSFU, 8U232AM),
239 	UFTDI_DEV(FTDI, EMCU2H, 8U232AM),
240 	UFTDI_DEV(FTDI, MAXSTREAM, 8U232AM),
241 	UFTDI_DEV(FTDI, CTI_USB_NANO_485, 8U232AM),
242 	UFTDI_DEV(FTDI, CTI_USB_MINI_485, 8U232AM),
243 	UFTDI_DEV(SIIG2, US2308, 8U232AM),
244 	UFTDI_DEV(INTREPIDCS, VALUECAN, 8U232AM),
245 	UFTDI_DEV(INTREPIDCS, NEOVI, 8U232AM),
246 	UFTDI_DEV(BBELECTRONICS, USOTL4, 8U232AM),
247 	UFTDI_DEV(MATRIXORBITAL, MOUA, 8U232AM),
248 	UFTDI_DEV(MARVELL, SHEEVAPLUG, 8U232AM),
249 	UFTDI_DEV(MELCO, PCOPRS1, 8U232AM),
250 	UFTDI_DEV(RATOC, REXUSB60F, 8U232AM),
251 #undef UFTDI_DEV
252 };
253 
254 static int
255 uftdi_probe(device_t dev)
256 {
257 	struct usb_attach_arg *uaa = device_get_ivars(dev);
258 
259 	if (uaa->usb_mode != USB_MODE_HOST) {
260 		return (ENXIO);
261 	}
262 	if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
263 		return (ENXIO);
264 	}
265 	/* attach to all present interfaces */
266 
267 	return (usbd_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
268 }
269 
270 static int
271 uftdi_attach(device_t dev)
272 {
273 	struct usb_attach_arg *uaa = device_get_ivars(dev);
274 	struct uftdi_softc *sc = device_get_softc(dev);
275 	int error;
276 
277 	sc->sc_udev = uaa->device;
278 	sc->sc_dev = dev;
279 	sc->sc_unit = device_get_unit(dev);
280 
281 	device_set_usb_desc(dev);
282 	lockinit(&sc->sc_lock, "uftdi", 0, LK_CANRECURSE);
283 
284 	ksnprintf(sc->sc_name, sizeof(sc->sc_name),
285 	    "%s", device_get_nameunit(dev));
286 
287 	DPRINTF("\n");
288 
289 	sc->sc_iface_index = uaa->info.bIfaceIndex;
290 	sc->sc_type = USB_GET_DRIVER_INFO(uaa);
291 
292 	switch (sc->sc_type) {
293 	case UFTDI_TYPE_SIO:
294 		sc->sc_hdrlen = 1;
295 		break;
296 	case UFTDI_TYPE_8U232AM:
297 	default:
298 		sc->sc_hdrlen = 0;
299 		break;
300 	}
301 
302 	error = usbd_transfer_setup(uaa->device,
303 	    &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
304 	    UFTDI_N_TRANSFER, sc, &sc->sc_lock);
305 
306 	if (error) {
307 		device_printf(dev, "allocating USB "
308 		    "transfers failed\n");
309 		goto detach;
310 	}
311 	sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
312 
313 	/* clear stall at first run */
314 	lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
315 	usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
316 	usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
317 	lockmgr(&sc->sc_lock, LK_RELEASE);
318 
319 	/* set a valid "lcr" value */
320 
321 	sc->sc_last_lcr =
322 	    (FTDI_SIO_SET_DATA_STOP_BITS_2 |
323 	    FTDI_SIO_SET_DATA_PARITY_NONE |
324 	    FTDI_SIO_SET_DATA_BITS(8));
325 
326 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
327 	    &uftdi_callback, &sc->sc_lock);
328 	if (error) {
329 		goto detach;
330 	}
331 	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
332 
333 	return (0);			/* success */
334 
335 detach:
336 	uftdi_detach(dev);
337 	return (ENXIO);
338 }
339 
340 static int
341 uftdi_detach(device_t dev)
342 {
343 	struct uftdi_softc *sc = device_get_softc(dev);
344 
345 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
346 	usbd_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
347 	lockuninit(&sc->sc_lock);
348 
349 	return (0);
350 }
351 
352 static void
353 uftdi_cfg_open(struct ucom_softc *ucom)
354 {
355 	struct uftdi_softc *sc = ucom->sc_parent;
356 	uint16_t wIndex = ucom->sc_portno;
357 	struct usb_device_request req;
358 
359 	DPRINTF("");
360 
361 	/* perform a full reset on the device */
362 
363 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
364 	req.bRequest = FTDI_SIO_RESET;
365 	USETW(req.wValue, FTDI_SIO_RESET_SIO);
366 	USETW(req.wIndex, wIndex);
367 	USETW(req.wLength, 0);
368 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
369 	    &req, NULL, 0, 1000);
370 
371 	/* turn on RTS/CTS flow control */
372 
373 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
374 	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
375 	USETW(req.wValue, 0);
376 	USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
377 	USETW(req.wLength, 0);
378 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
379 	    &req, NULL, 0, 1000);
380 
381 	/*
382 	 * NOTE: with the new UCOM layer there will always be a
383 	 * "uftdi_cfg_param()" call after "open()", so there is no need for
384 	 * "open()" to configure anything
385 	 */
386 }
387 
388 static void
389 uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error)
390 {
391 	struct uftdi_softc *sc = usbd_xfer_softc(xfer);
392 	struct usb_page_cache *pc;
393 	uint32_t actlen;
394 	uint8_t buf[1];
395 
396 	switch (USB_GET_STATE(xfer)) {
397 	case USB_ST_SETUP:
398 	case USB_ST_TRANSFERRED:
399 tr_setup:
400 		pc = usbd_xfer_get_frame(xfer, 0);
401 		if (ucom_get_data(&sc->sc_ucom, pc,
402 		    sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
403 		    &actlen)) {
404 
405 			if (sc->sc_hdrlen > 0) {
406 				buf[0] =
407 				    FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
408 				usbd_copy_in(pc, 0, buf, 1);
409 			}
410 			usbd_xfer_set_frame_len(xfer, 0, actlen + sc->sc_hdrlen);
411 			usbd_transfer_submit(xfer);
412 		}
413 		return;
414 
415 	default:			/* Error */
416 		if (error != USB_ERR_CANCELLED) {
417 			/* try to clear stall first */
418 			usbd_xfer_set_stall(xfer);
419 			goto tr_setup;
420 		}
421 		return;
422 	}
423 }
424 
425 static void
426 uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error)
427 {
428 	struct uftdi_softc *sc = usbd_xfer_softc(xfer);
429 	struct usb_page_cache *pc;
430 	uint8_t buf[2];
431 	uint8_t ftdi_msr;
432 	uint8_t msr;
433 	uint8_t lsr;
434 	int actlen;
435 
436 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
437 
438 	switch (USB_GET_STATE(xfer)) {
439 	case USB_ST_TRANSFERRED:
440 
441 		if (actlen < 2) {
442 			goto tr_setup;
443 		}
444 		pc = usbd_xfer_get_frame(xfer, 0);
445 		usbd_copy_out(pc, 0, buf, 2);
446 
447 		ftdi_msr = FTDI_GET_MSR(buf);
448 		lsr = FTDI_GET_LSR(buf);
449 
450 		msr = 0;
451 		if (ftdi_msr & FTDI_SIO_CTS_MASK)
452 			msr |= SER_CTS;
453 		if (ftdi_msr & FTDI_SIO_DSR_MASK)
454 			msr |= SER_DSR;
455 		if (ftdi_msr & FTDI_SIO_RI_MASK)
456 			msr |= SER_RI;
457 		if (ftdi_msr & FTDI_SIO_RLSD_MASK)
458 			msr |= SER_DCD;
459 
460 		if ((sc->sc_msr != msr) ||
461 		    ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
462 			DPRINTF("status change msr=0x%02x (0x%02x) "
463 			    "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
464 			    lsr, sc->sc_lsr);
465 
466 			sc->sc_msr = msr;
467 			sc->sc_lsr = lsr;
468 
469 			ucom_status_change(&sc->sc_ucom);
470 		}
471 		actlen -= 2;
472 
473 		if (actlen > 0) {
474 			ucom_put_data(&sc->sc_ucom, pc, 2, actlen);
475 		}
476 	case USB_ST_SETUP:
477 tr_setup:
478 		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
479 		usbd_transfer_submit(xfer);
480 		return;
481 
482 	default:			/* Error */
483 		if (error != USB_ERR_CANCELLED) {
484 			/* try to clear stall first */
485 			usbd_xfer_set_stall(xfer);
486 			goto tr_setup;
487 		}
488 		return;
489 	}
490 }
491 
492 static void
493 uftdi_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
494 {
495 	struct uftdi_softc *sc = ucom->sc_parent;
496 	uint16_t wIndex = ucom->sc_portno;
497 	uint16_t wValue;
498 	struct usb_device_request req;
499 
500 	wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
501 
502 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
503 	req.bRequest = FTDI_SIO_MODEM_CTRL;
504 	USETW(req.wValue, wValue);
505 	USETW(req.wIndex, wIndex);
506 	USETW(req.wLength, 0);
507 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
508 	    &req, NULL, 0, 1000);
509 }
510 
511 static void
512 uftdi_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
513 {
514 	struct uftdi_softc *sc = ucom->sc_parent;
515 	uint16_t wIndex = ucom->sc_portno;
516 	uint16_t wValue;
517 	struct usb_device_request req;
518 
519 	wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
520 
521 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
522 	req.bRequest = FTDI_SIO_MODEM_CTRL;
523 	USETW(req.wValue, wValue);
524 	USETW(req.wIndex, wIndex);
525 	USETW(req.wLength, 0);
526 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
527 	    &req, NULL, 0, 1000);
528 }
529 
530 static void
531 uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
532 {
533 	struct uftdi_softc *sc = ucom->sc_parent;
534 	uint16_t wIndex = ucom->sc_portno;
535 	uint16_t wValue;
536 	struct usb_device_request req;
537 
538 	if (onoff) {
539 		sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
540 	} else {
541 		sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
542 	}
543 
544 	wValue = sc->sc_last_lcr;
545 
546 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
547 	req.bRequest = FTDI_SIO_SET_DATA;
548 	USETW(req.wValue, wValue);
549 	USETW(req.wIndex, wIndex);
550 	USETW(req.wLength, 0);
551 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
552 	    &req, NULL, 0, 1000);
553 }
554 
555 static int
556 uftdi_set_parm_soft(struct termios *t,
557     struct uftdi_param_config *cfg, uint8_t type)
558 {
559 	memset(cfg, 0, sizeof(*cfg));
560 
561 	switch (type) {
562 	case UFTDI_TYPE_SIO:
563 		switch (t->c_ospeed) {
564 		case 300:
565 			cfg->rate = ftdi_sio_b300;
566 			break;
567 		case 600:
568 			cfg->rate = ftdi_sio_b600;
569 			break;
570 		case 1200:
571 			cfg->rate = ftdi_sio_b1200;
572 			break;
573 		case 2400:
574 			cfg->rate = ftdi_sio_b2400;
575 			break;
576 		case 4800:
577 			cfg->rate = ftdi_sio_b4800;
578 			break;
579 		case 9600:
580 			cfg->rate = ftdi_sio_b9600;
581 			break;
582 		case 19200:
583 			cfg->rate = ftdi_sio_b19200;
584 			break;
585 		case 38400:
586 			cfg->rate = ftdi_sio_b38400;
587 			break;
588 		case 57600:
589 			cfg->rate = ftdi_sio_b57600;
590 			break;
591 		case 115200:
592 			cfg->rate = ftdi_sio_b115200;
593 			break;
594 		default:
595 			return (EINVAL);
596 		}
597 		break;
598 
599 	case UFTDI_TYPE_8U232AM:
600 		if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
601 			return (EINVAL);
602 		}
603 		break;
604 	}
605 
606 	if (t->c_cflag & CSTOPB)
607 		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
608 	else
609 		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
610 
611 	if (t->c_cflag & PARENB) {
612 		if (t->c_cflag & PARODD) {
613 			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
614 		} else {
615 			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
616 		}
617 	} else {
618 		cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
619 	}
620 
621 	switch (t->c_cflag & CSIZE) {
622 	case CS5:
623 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
624 		break;
625 
626 	case CS6:
627 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
628 		break;
629 
630 	case CS7:
631 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
632 		break;
633 
634 	case CS8:
635 		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
636 		break;
637 	}
638 
639 	if (t->c_cflag & CRTSCTS) {
640 		cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
641 	} else if (t->c_iflag & (IXON | IXOFF)) {
642 		cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
643 		cfg->v_start = t->c_cc[VSTART];
644 		cfg->v_stop = t->c_cc[VSTOP];
645 	} else {
646 		cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
647 	}
648 
649 	return (0);
650 }
651 
652 static int
653 uftdi_pre_param(struct ucom_softc *ucom, struct termios *t)
654 {
655 	struct uftdi_softc *sc = ucom->sc_parent;
656 	struct uftdi_param_config cfg;
657 
658 	DPRINTF("\n");
659 
660 	return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
661 }
662 
663 static void
664 uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t)
665 {
666 	struct uftdi_softc *sc = ucom->sc_parent;
667 	uint16_t wIndex = ucom->sc_portno;
668 	struct uftdi_param_config cfg;
669 	struct usb_device_request req;
670 
671 	if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
672 		/* should not happen */
673 		return;
674 	}
675 	sc->sc_last_lcr = cfg.lcr;
676 
677 	DPRINTF("\n");
678 
679 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
680 	req.bRequest = FTDI_SIO_SET_BAUD_RATE;
681 	USETW(req.wValue, cfg.rate);
682 	USETW(req.wIndex, wIndex);
683 	USETW(req.wLength, 0);
684 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
685 	    &req, NULL, 0, 1000);
686 
687 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
688 	req.bRequest = FTDI_SIO_SET_DATA;
689 	USETW(req.wValue, cfg.lcr);
690 	USETW(req.wIndex, wIndex);
691 	USETW(req.wLength, 0);
692 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
693 	    &req, NULL, 0, 1000);
694 
695 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
696 	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
697 	USETW2(req.wValue, cfg.v_stop, cfg.v_start);
698 	USETW2(req.wIndex, cfg.v_flow, wIndex);
699 	USETW(req.wLength, 0);
700 	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
701 	    &req, NULL, 0, 1000);
702 }
703 
704 static void
705 uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
706 {
707 	struct uftdi_softc *sc = ucom->sc_parent;
708 
709 	DPRINTF("msr=0x%02x lsr=0x%02x\n",
710 	    sc->sc_msr, sc->sc_lsr);
711 
712 	*msr = sc->sc_msr;
713 	*lsr = sc->sc_lsr;
714 }
715 
716 static void
717 uftdi_start_read(struct ucom_softc *ucom)
718 {
719 	struct uftdi_softc *sc = ucom->sc_parent;
720 
721 	usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
722 }
723 
724 static void
725 uftdi_stop_read(struct ucom_softc *ucom)
726 {
727 	struct uftdi_softc *sc = ucom->sc_parent;
728 
729 	usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
730 }
731 
732 static void
733 uftdi_start_write(struct ucom_softc *ucom)
734 {
735 	struct uftdi_softc *sc = ucom->sc_parent;
736 
737 	usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
738 }
739 
740 static void
741 uftdi_stop_write(struct ucom_softc *ucom)
742 {
743 	struct uftdi_softc *sc = ucom->sc_parent;
744 
745 	usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
746 }
747 
748 /*------------------------------------------------------------------------*
749  *	uftdi_8u232am_getrate
750  *
751  * Return values:
752  *    0: Success
753  * Else: Failure
754  *------------------------------------------------------------------------*/
755 static uint8_t
756 uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
757 {
758 	/* Table of the nearest even powers-of-2 for values 0..15. */
759 	static const uint8_t roundoff[16] = {
760 		0, 2, 2, 4, 4, 4, 8, 8,
761 		8, 8, 8, 8, 16, 16, 16, 16,
762 	};
763 	uint32_t d;
764 	uint32_t freq;
765 	uint16_t result;
766 
767 	if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
768 		return (1);		/* prevent numerical overflow */
769 
770 	/* Special cases for 2M and 3M. */
771 	if ((speed >= ((3000000 * 100) / 103)) &&
772 	    (speed <= ((3000000 * 100) / 97))) {
773 		result = 0;
774 		goto done;
775 	}
776 	if ((speed >= ((2000000 * 100) / 103)) &&
777 	    (speed <= ((2000000 * 100) / 97))) {
778 		result = 1;
779 		goto done;
780 	}
781 	d = (FTDI_8U232AM_FREQ << 4) / speed;
782 	d = (d & ~15) + roundoff[d & 15];
783 
784 	if (d < FTDI_8U232AM_MIN_DIV)
785 		d = FTDI_8U232AM_MIN_DIV;
786 	else if (d > FTDI_8U232AM_MAX_DIV)
787 		d = FTDI_8U232AM_MAX_DIV;
788 
789 	/*
790 	 * Calculate the frequency needed for "d" to exactly divide down to
791 	 * our target "speed", and check that the actual frequency is within
792 	 * 3% of this.
793 	 */
794 	freq = (speed * d);
795 	if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
796 	    (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
797 		return (1);
798 
799 	/*
800 	 * Pack the divisor into the resultant value.  The lower 14-bits
801 	 * hold the integral part, while the upper 2 bits encode the
802 	 * fractional component: either 0, 0.5, 0.25, or 0.125.
803 	 */
804 	result = (d >> 4);
805 	if (d & 8)
806 		result |= 0x4000;
807 	else if (d & 4)
808 		result |= 0x8000;
809 	else if (d & 2)
810 		result |= 0xc000;
811 
812 done:
813 	*rate = result;
814 	return (0);
815 }
816 
817 static void
818 uftdi_poll(struct ucom_softc *ucom)
819 {
820 	struct uftdi_softc *sc = ucom->sc_parent;
821 	usbd_transfer_poll(sc->sc_xfer, UFTDI_N_TRANSFER);
822 }
823