uftdi.c revision 187259
1244197Sgonzo/*	$NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $	*/
2244197Sgonzo
3244197Sgonzo/*-
4244197Sgonzo * Copyright (c) 2000 The NetBSD Foundation, Inc.
5244197Sgonzo * All rights reserved.
6244197Sgonzo *
7244197Sgonzo * This code is derived from software contributed to The NetBSD Foundation
8244197Sgonzo * by Lennart Augustsson (lennart@augustsson.net).
9244197Sgonzo *
10244197Sgonzo * Redistribution and use in source and binary forms, with or without
11244197Sgonzo * modification, are permitted provided that the following conditions
12244197Sgonzo * are met:
13244197Sgonzo * 1. Redistributions of source code must retain the above copyright
14244197Sgonzo *    notice, this list of conditions and the following disclaimer.
15244197Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
16244197Sgonzo *    notice, this list of conditions and the following disclaimer in the
17244197Sgonzo *    documentation and/or other materials provided with the distribution.
18244197Sgonzo * 3. All advertising materials mentioning features or use of this software
19244197Sgonzo *    must display the following acknowledgement:
20244197Sgonzo *        This product includes software developed by the NetBSD
21244197Sgonzo *        Foundation, Inc. and its contributors.
22244197Sgonzo * 4. Neither the name of The NetBSD Foundation nor the names of its
23244197Sgonzo *    contributors may be used to endorse or promote products derived
24244197Sgonzo *    from this software without specific prior written permission.
25244197Sgonzo *
26244197Sgonzo * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27244197Sgonzo * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28244197Sgonzo * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29244197Sgonzo * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30244197Sgonzo * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31244197Sgonzo * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32244197Sgonzo * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33244197Sgonzo * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34244197Sgonzo * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35244197Sgonzo * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36244197Sgonzo * POSSIBILITY OF SUCH DAMAGE.
37244197Sgonzo */
38244197Sgonzo
39244197Sgonzo#include <sys/cdefs.h>
40244197Sgonzo__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uftdi2.c 187259 2009-01-15 02:35:40Z thompsa $");
41244197Sgonzo
42244197Sgonzo/*
43244197Sgonzo * NOTE: all function names beginning like "uftdi_cfg_" can only
44244197Sgonzo * be called from within the config thread function !
45244197Sgonzo */
46244197Sgonzo
47244197Sgonzo/*
48244197Sgonzo * FTDI FT8U100AX serial adapter driver
49244197Sgonzo */
50244197Sgonzo
51244197Sgonzo#include <dev/usb2/include/usb2_devid.h>
52244197Sgonzo#include <dev/usb2/include/usb2_standard.h>
53244197Sgonzo#include <dev/usb2/include/usb2_mfunc.h>
54244197Sgonzo#include <dev/usb2/include/usb2_error.h>
55244197Sgonzo#include <dev/usb2/include/usb2_cdc.h>
56244197Sgonzo
57244197Sgonzo#define	USB_DEBUG_VAR uftdi_debug
58244197Sgonzo
59244197Sgonzo#include <dev/usb2/core/usb2_core.h>
60244197Sgonzo#include <dev/usb2/core/usb2_debug.h>
61244197Sgonzo#include <dev/usb2/core/usb2_process.h>
62244197Sgonzo#include <dev/usb2/core/usb2_request.h>
63244197Sgonzo#include <dev/usb2/core/usb2_lookup.h>
64244197Sgonzo#include <dev/usb2/core/usb2_util.h>
65244197Sgonzo#include <dev/usb2/core/usb2_busdma.h>
66244197Sgonzo
67244197Sgonzo#include <dev/usb2/serial/usb2_serial.h>
68244197Sgonzo#include <dev/usb2/serial/uftdi2_reg.h>
69244197Sgonzo
70244197Sgonzo#if USB_DEBUG
71244197Sgonzostatic int uftdi_debug = 0;
72244197Sgonzo
73244197SgonzoSYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
74244197SgonzoSYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW,
75244197Sgonzo    &uftdi_debug, 0, "Debug level");
76244197Sgonzo#endif
77244197Sgonzo
78244197Sgonzo#define	UFTDI_CONFIG_INDEX	0
79244197Sgonzo#define	UFTDI_IFACE_INDEX	0
80244197Sgonzo
81244197Sgonzo#define	UFTDI_IBUFSIZE 64		/* bytes, maximum number of bytes per
82244197Sgonzo					 * frame */
83244197Sgonzo#define	UFTDI_OBUFSIZE 64		/* bytes, cannot be increased due to
84244197Sgonzo					 * do size encoding */
85244197Sgonzo
86244197Sgonzoenum {
87244197Sgonzo	UFTDI_BULK_DT_WR,
88244197Sgonzo	UFTDI_BULK_DT_RD,
89244197Sgonzo	UFTDI_BULK_CS_WR,
90244197Sgonzo	UFTDI_BULK_CS_RD,
91244197Sgonzo	UFTDI_N_TRANSFER = 4,
92244197Sgonzo};
93244197Sgonzo
94244197Sgonzostruct uftdi_softc {
95244197Sgonzo	struct usb2_com_super_softc sc_super_ucom;
96244197Sgonzo	struct usb2_com_softc sc_ucom;
97244197Sgonzo
98244197Sgonzo	struct usb2_device *sc_udev;
99244197Sgonzo	struct usb2_xfer *sc_xfer[UFTDI_N_TRANSFER];
100244197Sgonzo	device_t sc_dev;
101244197Sgonzo
102244197Sgonzo	uint32_t sc_unit;
103244197Sgonzo	enum uftdi_type sc_type;
104244197Sgonzo
105244197Sgonzo	uint16_t sc_last_lcr;
106244197Sgonzo
107244197Sgonzo	uint8_t	sc_iface_index;
108244197Sgonzo	uint8_t	sc_hdrlen;
109244197Sgonzo
110244197Sgonzo	uint8_t	sc_msr;
111244197Sgonzo	uint8_t	sc_lsr;
112244197Sgonzo
113244197Sgonzo	uint8_t	sc_flag;
114244197Sgonzo#define	UFTDI_FLAG_WRITE_STALL  0x01
115244197Sgonzo#define	UFTDI_FLAG_READ_STALL   0x02
116244197Sgonzo
117244197Sgonzo	uint8_t	sc_name[16];
118244197Sgonzo};
119244197Sgonzo
120244197Sgonzostruct uftdi_param_config {
121244197Sgonzo	uint16_t rate;
122244197Sgonzo	uint16_t lcr;
123244197Sgonzo	uint8_t	v_start;
124244197Sgonzo	uint8_t	v_stop;
125244197Sgonzo	uint8_t	v_flow;
126244197Sgonzo};
127244197Sgonzo
128244197Sgonzo/* prototypes */
129244197Sgonzo
130244197Sgonzostatic device_probe_t uftdi_probe;
131244197Sgonzostatic device_attach_t uftdi_attach;
132244197Sgonzostatic device_detach_t uftdi_detach;
133244197Sgonzo
134244197Sgonzostatic usb2_callback_t uftdi_write_callback;
135244197Sgonzostatic usb2_callback_t uftdi_write_clear_stall_callback;
136244197Sgonzostatic usb2_callback_t uftdi_read_callback;
137244197Sgonzostatic usb2_callback_t uftdi_read_clear_stall_callback;
138244197Sgonzo
139244197Sgonzostatic void	uftdi_cfg_do_request(struct uftdi_softc *,
140244197Sgonzo		    struct usb2_device_request *, void *);
141244197Sgonzostatic void	uftdi_cfg_open(struct usb2_com_softc *);
142244197Sgonzostatic void	uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
143244197Sgonzostatic void	uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t);
144244197Sgonzostatic void	uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t);
145244197Sgonzostatic int	uftdi_set_parm_soft(struct termios *,
146244197Sgonzo		    struct uftdi_param_config *, uint8_t);
147244197Sgonzostatic int	uftdi_pre_param(struct usb2_com_softc *, struct termios *);
148244197Sgonzostatic void	uftdi_cfg_param(struct usb2_com_softc *, struct termios *);
149244197Sgonzostatic void	uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *,
150244197Sgonzo		    uint8_t *);
151244197Sgonzostatic void	uftdi_start_read(struct usb2_com_softc *);
152244197Sgonzostatic void	uftdi_stop_read(struct usb2_com_softc *);
153244197Sgonzostatic void	uftdi_start_write(struct usb2_com_softc *);
154244197Sgonzostatic void	uftdi_stop_write(struct usb2_com_softc *);
155244197Sgonzostatic uint8_t	uftdi_8u232am_getrate(uint32_t, uint16_t *);
156244197Sgonzo
157244197Sgonzostatic const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = {
158244197Sgonzo
159244197Sgonzo	[UFTDI_BULK_DT_WR] = {
160244197Sgonzo		.type = UE_BULK,
161244197Sgonzo		.endpoint = UE_ADDR_ANY,
162244197Sgonzo		.direction = UE_DIR_OUT,
163244197Sgonzo		.mh.bufsize = UFTDI_OBUFSIZE,
164244197Sgonzo		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
165244197Sgonzo		.mh.callback = &uftdi_write_callback,
166244197Sgonzo	},
167244197Sgonzo
168244197Sgonzo	[UFTDI_BULK_DT_RD] = {
169244197Sgonzo		.type = UE_BULK,
170244197Sgonzo		.endpoint = UE_ADDR_ANY,
171244197Sgonzo		.direction = UE_DIR_IN,
172244197Sgonzo		.mh.bufsize = UFTDI_IBUFSIZE,
173244197Sgonzo		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
174244197Sgonzo		.mh.callback = &uftdi_read_callback,
175244197Sgonzo	},
176244197Sgonzo
177244197Sgonzo	[UFTDI_BULK_CS_WR] = {
178244197Sgonzo		.type = UE_CONTROL,
179244197Sgonzo		.endpoint = 0x00,	/* Control pipe */
180244197Sgonzo		.direction = UE_DIR_ANY,
181244197Sgonzo		.mh.bufsize = sizeof(struct usb2_device_request),
182244197Sgonzo		.mh.flags = {},
183244197Sgonzo		.mh.callback = &uftdi_write_clear_stall_callback,
184244197Sgonzo		.mh.timeout = 1000,	/* 1 second */
185244197Sgonzo		.mh.interval = 50,	/* 50ms */
186244197Sgonzo	},
187244197Sgonzo
188244197Sgonzo	[UFTDI_BULK_CS_RD] = {
189244197Sgonzo		.type = UE_CONTROL,
190244197Sgonzo		.endpoint = 0x00,	/* Control pipe */
191244197Sgonzo		.direction = UE_DIR_ANY,
192244197Sgonzo		.mh.bufsize = sizeof(struct usb2_device_request),
193244197Sgonzo		.mh.flags = {},
194244197Sgonzo		.mh.callback = &uftdi_read_clear_stall_callback,
195244197Sgonzo		.mh.timeout = 1000,	/* 1 second */
196244197Sgonzo		.mh.interval = 50,	/* 50ms */
197244197Sgonzo	},
198244197Sgonzo};
199244197Sgonzo
200244197Sgonzostatic const struct usb2_com_callback uftdi_callback = {
201244197Sgonzo	.usb2_com_cfg_get_status = &uftdi_cfg_get_status,
202244197Sgonzo	.usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr,
203244197Sgonzo	.usb2_com_cfg_set_rts = &uftdi_cfg_set_rts,
204244197Sgonzo	.usb2_com_cfg_set_break = &uftdi_cfg_set_break,
205244197Sgonzo	.usb2_com_cfg_param = &uftdi_cfg_param,
206244197Sgonzo	.usb2_com_cfg_open = &uftdi_cfg_open,
207244197Sgonzo	.usb2_com_pre_param = &uftdi_pre_param,
208244197Sgonzo	.usb2_com_start_read = &uftdi_start_read,
209244197Sgonzo	.usb2_com_stop_read = &uftdi_stop_read,
210244197Sgonzo	.usb2_com_start_write = &uftdi_start_write,
211244197Sgonzo	.usb2_com_stop_write = &uftdi_stop_write,
212244197Sgonzo};
213244197Sgonzo
214244197Sgonzostatic device_method_t uftdi_methods[] = {
215244197Sgonzo	/* Device interface */
216244197Sgonzo	DEVMETHOD(device_probe, uftdi_probe),
217244197Sgonzo	DEVMETHOD(device_attach, uftdi_attach),
218244197Sgonzo	DEVMETHOD(device_detach, uftdi_detach),
219244197Sgonzo
220244197Sgonzo	{0, 0}
221244197Sgonzo};
222244197Sgonzo
223244197Sgonzostatic devclass_t uftdi_devclass;
224244197Sgonzo
225244197Sgonzostatic driver_t uftdi_driver = {
226244197Sgonzo	.name = "uftdi",
227244197Sgonzo	.methods = uftdi_methods,
228244197Sgonzo	.size = sizeof(struct uftdi_softc),
229244197Sgonzo};
230244197Sgonzo
231244197SgonzoDRIVER_MODULE(uftdi, ushub, uftdi_driver, uftdi_devclass, NULL, 0);
232244197SgonzoMODULE_DEPEND(uftdi, usb2_serial, 1, 1, 1);
233244197SgonzoMODULE_DEPEND(uftdi, usb2_core, 1, 1, 1);
234244197Sgonzo
235244197Sgonzostatic struct usb2_device_id uftdi_devs[] = {
236244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)},
237244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)},
238244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)},
239244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)},
240244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)},
241244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)},
242244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)},
243244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)},
244244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)},
245244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)},
246244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)},
247244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)},
248244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)},
249244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)},
250244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)},
251244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)},
252244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)},
253244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)},
254244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)},
255244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)},
256244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)},
257244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)},
258244197Sgonzo	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)},
259244197Sgonzo	{USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)},
260244197Sgonzo	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)},
261244197Sgonzo	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)},
262244197Sgonzo	{USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)},
263244197Sgonzo	{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)},
264244197Sgonzo};
265244197Sgonzo
266244197Sgonzostatic int
267244197Sgonzouftdi_probe(device_t dev)
268244197Sgonzo{
269244197Sgonzo	struct usb2_attach_arg *uaa = device_get_ivars(dev);
270244197Sgonzo
271244197Sgonzo	if (uaa->usb2_mode != USB_MODE_HOST) {
272244197Sgonzo		return (ENXIO);
273244197Sgonzo	}
274244197Sgonzo	if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
275244197Sgonzo		return (ENXIO);
276244197Sgonzo	}
277244197Sgonzo	/* attach to all present interfaces */
278244197Sgonzo
279244197Sgonzo	return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
280244197Sgonzo}
281244197Sgonzo
282244197Sgonzostatic int
283244197Sgonzouftdi_attach(device_t dev)
284244197Sgonzo{
285244197Sgonzo	struct usb2_attach_arg *uaa = device_get_ivars(dev);
286244197Sgonzo	struct uftdi_softc *sc = device_get_softc(dev);
287244197Sgonzo	int error;
288244197Sgonzo
289244197Sgonzo	if (sc == NULL) {
290244197Sgonzo		return (ENOMEM);
291244197Sgonzo	}
292244197Sgonzo	sc->sc_udev = uaa->device;
293244197Sgonzo	sc->sc_dev = dev;
294244197Sgonzo	sc->sc_unit = device_get_unit(dev);
295244197Sgonzo
296244197Sgonzo	device_set_usb2_desc(dev);
297244197Sgonzo
298244197Sgonzo	snprintf(sc->sc_name, sizeof(sc->sc_name),
299244197Sgonzo	    "%s", device_get_nameunit(dev));
300244197Sgonzo
301244197Sgonzo	DPRINTF("\n");
302244197Sgonzo
303244197Sgonzo	sc->sc_iface_index = uaa->info.bIfaceIndex;
304244197Sgonzo	sc->sc_type = USB_GET_DRIVER_INFO(uaa);
305244197Sgonzo
306244197Sgonzo	switch (sc->sc_type) {
307244197Sgonzo	case UFTDI_TYPE_SIO:
308244197Sgonzo		sc->sc_hdrlen = 1;
309244197Sgonzo		break;
310244197Sgonzo	case UFTDI_TYPE_8U232AM:
311244197Sgonzo	default:
312244197Sgonzo		sc->sc_hdrlen = 0;
313244197Sgonzo		break;
314244197Sgonzo	}
315244197Sgonzo
316244197Sgonzo	error = usb2_transfer_setup(uaa->device,
317244197Sgonzo	    &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
318244197Sgonzo	    UFTDI_N_TRANSFER, sc, &Giant);
319244197Sgonzo
320244197Sgonzo	if (error) {
321244197Sgonzo		device_printf(dev, "allocating USB "
322244197Sgonzo		    "transfers failed!\n");
323244197Sgonzo		goto detach;
324244197Sgonzo	}
325244197Sgonzo	sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
326244197Sgonzo
327244197Sgonzo	/* clear stall at first run */
328244197Sgonzo
329244197Sgonzo	sc->sc_flag |= (UFTDI_FLAG_WRITE_STALL |
330244197Sgonzo	    UFTDI_FLAG_READ_STALL);
331244197Sgonzo
332244197Sgonzo	/* set a valid "lcr" value */
333244197Sgonzo
334244197Sgonzo	sc->sc_last_lcr =
335244197Sgonzo	    (FTDI_SIO_SET_DATA_STOP_BITS_2 |
336244197Sgonzo	    FTDI_SIO_SET_DATA_PARITY_NONE |
337244197Sgonzo	    FTDI_SIO_SET_DATA_BITS(8));
338244197Sgonzo
339244197Sgonzo	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
340244197Sgonzo	    &uftdi_callback, &Giant);
341244197Sgonzo	if (error) {
342244197Sgonzo		goto detach;
343244197Sgonzo	}
344244197Sgonzo	return (0);			/* success */
345244197Sgonzo
346244197Sgonzodetach:
347244197Sgonzo	uftdi_detach(dev);
348244197Sgonzo	return (ENXIO);
349244197Sgonzo}
350244197Sgonzo
351244197Sgonzostatic int
352244197Sgonzouftdi_detach(device_t dev)
353244197Sgonzo{
354244197Sgonzo	struct uftdi_softc *sc = device_get_softc(dev);
355244197Sgonzo
356244197Sgonzo	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
357244197Sgonzo
358244197Sgonzo	usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
359244197Sgonzo
360244197Sgonzo	return (0);
361244197Sgonzo}
362244197Sgonzo
363244197Sgonzostatic void
364244197Sgonzouftdi_cfg_do_request(struct uftdi_softc *sc, struct usb2_device_request *req,
365244197Sgonzo    void *data)
366244197Sgonzo{
367244197Sgonzo	uint16_t length;
368244197Sgonzo	usb2_error_t err;
369244197Sgonzo
370244197Sgonzo	if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
371244197Sgonzo		goto error;
372244197Sgonzo	}
373244197Sgonzo	err = usb2_do_request_flags
374244197Sgonzo	    (sc->sc_udev, &Giant, req, data, 0, NULL, 1000);
375244197Sgonzo
376244197Sgonzo	if (err) {
377244197Sgonzo
378244197Sgonzo		DPRINTFN(0, "device request failed, err=%s "
379244197Sgonzo		    "(ignored)\n", usb2_errstr(err));
380244197Sgonzo
381244197Sgonzoerror:
382244197Sgonzo		length = UGETW(req->wLength);
383244197Sgonzo
384244197Sgonzo		if ((req->bmRequestType & UT_READ) && length) {
385244197Sgonzo			bzero(data, length);
386244197Sgonzo		}
387244197Sgonzo	}
388244197Sgonzo}
389244197Sgonzo
390244197Sgonzostatic void
391244197Sgonzouftdi_cfg_open(struct usb2_com_softc *ucom)
392244197Sgonzo{
393244197Sgonzo	struct uftdi_softc *sc = ucom->sc_parent;
394244197Sgonzo	uint16_t wIndex = ucom->sc_portno;
395244197Sgonzo	struct usb2_device_request req;
396244197Sgonzo
397244197Sgonzo	DPRINTF("");
398244197Sgonzo
399244197Sgonzo	/* perform a full reset on the device */
400244197Sgonzo
401244197Sgonzo	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
402244197Sgonzo	req.bRequest = FTDI_SIO_RESET;
403244197Sgonzo	USETW(req.wValue, FTDI_SIO_RESET_SIO);
404244197Sgonzo	USETW(req.wIndex, wIndex);
405244197Sgonzo	USETW(req.wLength, 0);
406244197Sgonzo	uftdi_cfg_do_request(sc, &req, NULL);
407244197Sgonzo
408244197Sgonzo	/* turn on RTS/CTS flow control */
409244197Sgonzo
410244197Sgonzo	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
411244197Sgonzo	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
412244197Sgonzo	USETW(req.wValue, 0);
413244197Sgonzo	USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
414244197Sgonzo	USETW(req.wLength, 0);
415244197Sgonzo	uftdi_cfg_do_request(sc, &req, NULL);
416244197Sgonzo
417244197Sgonzo	/*
418244197Sgonzo	 * NOTE: with the new UCOM layer there will always be a
419244197Sgonzo	 * "uftdi_cfg_param()" call after "open()", so there is no need for
420244197Sgonzo	 * "open()" to configure anything
421244197Sgonzo	 */
422244197Sgonzo}
423244197Sgonzo
424244197Sgonzostatic void
425244197Sgonzouftdi_write_callback(struct usb2_xfer *xfer)
426244197Sgonzo{
427244197Sgonzo	struct uftdi_softc *sc = xfer->priv_sc;
428244197Sgonzo	uint32_t actlen;
429244197Sgonzo	uint8_t buf[1];
430244197Sgonzo
431244197Sgonzo	switch (USB_GET_STATE(xfer)) {
432244197Sgonzo	case USB_ST_SETUP:
433244197Sgonzo	case USB_ST_TRANSFERRED:
434244197Sgonzo		if (sc->sc_flag & UFTDI_FLAG_WRITE_STALL) {
435244197Sgonzo			usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_WR]);
436244197Sgonzo			return;
437244197Sgonzo		}
438244197Sgonzo		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers,
439244197Sgonzo		    sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
440244197Sgonzo		    &actlen)) {
441244197Sgonzo
442244197Sgonzo			if (sc->sc_hdrlen > 0) {
443244197Sgonzo				buf[0] =
444244197Sgonzo				    FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
445244197Sgonzo				usb2_copy_in(xfer->frbuffers, 0, buf, 1);
446244197Sgonzo			}
447244197Sgonzo			xfer->frlengths[0] = actlen + sc->sc_hdrlen;
448244197Sgonzo			usb2_start_hardware(xfer);
449244197Sgonzo		}
450244197Sgonzo		return;
451244197Sgonzo
452244197Sgonzo	default:			/* Error */
453244197Sgonzo		if (xfer->error != USB_ERR_CANCELLED) {
454244197Sgonzo			sc->sc_flag |= UFTDI_FLAG_WRITE_STALL;
455244197Sgonzo			usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_WR]);
456244197Sgonzo		}
457244197Sgonzo		return;
458244197Sgonzo
459244197Sgonzo	}
460244197Sgonzo}
461244197Sgonzo
462244197Sgonzostatic void
463244197Sgonzouftdi_write_clear_stall_callback(struct usb2_xfer *xfer)
464244197Sgonzo{
465244197Sgonzo	struct uftdi_softc *sc = xfer->priv_sc;
466244197Sgonzo	struct usb2_xfer *xfer_other = sc->sc_xfer[UFTDI_BULK_DT_WR];
467244197Sgonzo
468244197Sgonzo	if (usb2_clear_stall_callback(xfer, xfer_other)) {
469244197Sgonzo		DPRINTF("stall cleared\n");
470244197Sgonzo		sc->sc_flag &= ~UFTDI_FLAG_WRITE_STALL;
471244197Sgonzo		usb2_transfer_start(xfer_other);
472244197Sgonzo	}
473244197Sgonzo}
474244197Sgonzo
475244197Sgonzostatic void
476244197Sgonzouftdi_read_callback(struct usb2_xfer *xfer)
477244197Sgonzo{
478244197Sgonzo	struct uftdi_softc *sc = xfer->priv_sc;
479244197Sgonzo	uint8_t buf[2];
480244197Sgonzo	uint8_t ftdi_msr;
481244197Sgonzo	uint8_t msr;
482244197Sgonzo	uint8_t lsr;
483244197Sgonzo
484244197Sgonzo	switch (USB_GET_STATE(xfer)) {
485244197Sgonzo	case USB_ST_TRANSFERRED:
486244197Sgonzo
487244197Sgonzo		if (xfer->actlen < 2) {
488244197Sgonzo			goto tr_setup;
489244197Sgonzo		}
490244197Sgonzo		usb2_copy_out(xfer->frbuffers, 0, buf, 2);
491244197Sgonzo
492244197Sgonzo		ftdi_msr = FTDI_GET_MSR(buf);
493244197Sgonzo		lsr = FTDI_GET_LSR(buf);
494244197Sgonzo
495244197Sgonzo		msr = 0;
496244197Sgonzo		if (ftdi_msr & FTDI_SIO_CTS_MASK)
497244197Sgonzo			msr |= SER_CTS;
498244197Sgonzo		if (ftdi_msr & FTDI_SIO_DSR_MASK)
499244197Sgonzo			msr |= SER_DSR;
500244197Sgonzo		if (ftdi_msr & FTDI_SIO_RI_MASK)
501244197Sgonzo			msr |= SER_RI;
502244197Sgonzo		if (ftdi_msr & FTDI_SIO_RLSD_MASK)
503244197Sgonzo			msr |= SER_DCD;
504244197Sgonzo
505244197Sgonzo		if ((sc->sc_msr != msr) ||
506244197Sgonzo		    ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
507244197Sgonzo			DPRINTF("status change msr=0x%02x (0x%02x) "
508244197Sgonzo			    "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
509244197Sgonzo			    lsr, sc->sc_lsr);
510244197Sgonzo
511244197Sgonzo			sc->sc_msr = msr;
512244197Sgonzo			sc->sc_lsr = lsr;
513244197Sgonzo
514244197Sgonzo			usb2_com_status_change(&sc->sc_ucom);
515244197Sgonzo		}
516244197Sgonzo		xfer->actlen -= 2;
517244197Sgonzo
518244197Sgonzo		if (xfer->actlen > 0) {
519244197Sgonzo			usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2,
520244197Sgonzo			    xfer->actlen);
521244197Sgonzo		}
522244197Sgonzo	case USB_ST_SETUP:
523244197Sgonzotr_setup:
524244197Sgonzo		if (sc->sc_flag & UFTDI_FLAG_READ_STALL) {
525244197Sgonzo			usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_RD]);
526244197Sgonzo		} else {
527244197Sgonzo			xfer->frlengths[0] = xfer->max_data_length;
528244197Sgonzo			usb2_start_hardware(xfer);
529244197Sgonzo		}
530244197Sgonzo		return;
531244197Sgonzo
532244197Sgonzo	default:			/* Error */
533244197Sgonzo		if (xfer->error != USB_ERR_CANCELLED) {
534244197Sgonzo			sc->sc_flag |= UFTDI_FLAG_READ_STALL;
535244197Sgonzo			usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_CS_RD]);
536244197Sgonzo		}
537244197Sgonzo		return;
538244197Sgonzo
539244197Sgonzo	}
540244197Sgonzo}
541244197Sgonzo
542244197Sgonzostatic void
543244197Sgonzouftdi_read_clear_stall_callback(struct usb2_xfer *xfer)
544244197Sgonzo{
545244197Sgonzo	struct uftdi_softc *sc = xfer->priv_sc;
546244197Sgonzo	struct usb2_xfer *xfer_other = sc->sc_xfer[UFTDI_BULK_DT_RD];
547244197Sgonzo
548244197Sgonzo	if (usb2_clear_stall_callback(xfer, xfer_other)) {
549244197Sgonzo		DPRINTF("stall cleared\n");
550244197Sgonzo		sc->sc_flag &= ~UFTDI_FLAG_READ_STALL;
551244197Sgonzo		usb2_transfer_start(xfer_other);
552244197Sgonzo	}
553244197Sgonzo}
554244197Sgonzo
555244197Sgonzostatic void
556244197Sgonzouftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
557244197Sgonzo{
558244197Sgonzo	struct uftdi_softc *sc = ucom->sc_parent;
559244197Sgonzo	uint16_t wIndex = ucom->sc_portno;
560244197Sgonzo	uint16_t wValue;
561244197Sgonzo	struct usb2_device_request req;
562244197Sgonzo
563244197Sgonzo	wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
564244197Sgonzo
565244197Sgonzo	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
566244197Sgonzo	req.bRequest = FTDI_SIO_MODEM_CTRL;
567244197Sgonzo	USETW(req.wValue, wValue);
568244197Sgonzo	USETW(req.wIndex, wIndex);
569244197Sgonzo	USETW(req.wLength, 0);
570244197Sgonzo	uftdi_cfg_do_request(sc, &req, NULL);
571244197Sgonzo}
572244197Sgonzo
573244197Sgonzostatic void
574244197Sgonzouftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
575244197Sgonzo{
576244197Sgonzo	struct uftdi_softc *sc = ucom->sc_parent;
577244197Sgonzo	uint16_t wIndex = ucom->sc_portno;
578244197Sgonzo	uint16_t wValue;
579244197Sgonzo	struct usb2_device_request req;
580244197Sgonzo
581244197Sgonzo	wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
582244197Sgonzo
583244197Sgonzo	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
584244197Sgonzo	req.bRequest = FTDI_SIO_MODEM_CTRL;
585244197Sgonzo	USETW(req.wValue, wValue);
586244197Sgonzo	USETW(req.wIndex, wIndex);
587244197Sgonzo	USETW(req.wLength, 0);
588244197Sgonzo	uftdi_cfg_do_request(sc, &req, NULL);
589244197Sgonzo}
590244197Sgonzo
591244197Sgonzostatic void
592244197Sgonzouftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
593244197Sgonzo{
594244197Sgonzo	struct uftdi_softc *sc = ucom->sc_parent;
595244197Sgonzo	uint16_t wIndex = ucom->sc_portno;
596244197Sgonzo	uint16_t wValue;
597244197Sgonzo	struct usb2_device_request req;
598244197Sgonzo
599244197Sgonzo	if (onoff) {
600244197Sgonzo		sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
601244197Sgonzo	} else {
602244197Sgonzo		sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
603244197Sgonzo	}
604244197Sgonzo
605244197Sgonzo	wValue = sc->sc_last_lcr;
606244197Sgonzo
607244197Sgonzo	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
608244197Sgonzo	req.bRequest = FTDI_SIO_SET_DATA;
609244197Sgonzo	USETW(req.wValue, wValue);
610244197Sgonzo	USETW(req.wIndex, wIndex);
611244197Sgonzo	USETW(req.wLength, 0);
612266152Sian	uftdi_cfg_do_request(sc, &req, NULL);
613266152Sian}
614266152Sian
615244197Sgonzostatic int
616244197Sgonzouftdi_set_parm_soft(struct termios *t,
617244197Sgonzo    struct uftdi_param_config *cfg, uint8_t type)
618244197Sgonzo{
619244197Sgonzo	bzero(cfg, sizeof(*cfg));
620244197Sgonzo
621244197Sgonzo	switch (type) {
622244197Sgonzo	case UFTDI_TYPE_SIO:
623244197Sgonzo		switch (t->c_ospeed) {
624244197Sgonzo		case 300:
625244197Sgonzo			cfg->rate = ftdi_sio_b300;
626244197Sgonzo			break;
627244197Sgonzo		case 600:
628244197Sgonzo			cfg->rate = ftdi_sio_b600;
629244197Sgonzo			break;
630244197Sgonzo		case 1200:
631244197Sgonzo			cfg->rate = ftdi_sio_b1200;
632244197Sgonzo			break;
633244197Sgonzo		case 2400:
634244197Sgonzo			cfg->rate = ftdi_sio_b2400;
635244197Sgonzo			break;
636244197Sgonzo		case 4800:
637244197Sgonzo			cfg->rate = ftdi_sio_b4800;
638244197Sgonzo			break;
639244197Sgonzo		case 9600:
640244197Sgonzo			cfg->rate = ftdi_sio_b9600;
641244197Sgonzo			break;
642244197Sgonzo		case 19200:
643244197Sgonzo			cfg->rate = ftdi_sio_b19200;
644244197Sgonzo			break;
645244197Sgonzo		case 38400:
646244197Sgonzo			cfg->rate = ftdi_sio_b38400;
647244197Sgonzo			break;
648244197Sgonzo		case 57600:
649244197Sgonzo			cfg->rate = ftdi_sio_b57600;
650244197Sgonzo			break;
651244197Sgonzo		case 115200:
652244197Sgonzo			cfg->rate = ftdi_sio_b115200;
653244197Sgonzo			break;
654244197Sgonzo		default:
655244197Sgonzo			return (EINVAL);
656244197Sgonzo		}
657244197Sgonzo		break;
658244197Sgonzo
659244197Sgonzo	case UFTDI_TYPE_8U232AM:
660244197Sgonzo		if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
661244197Sgonzo			return (EINVAL);
662244197Sgonzo		}
663244197Sgonzo		break;
664244197Sgonzo	}
665244197Sgonzo
666244197Sgonzo	if (t->c_cflag & CSTOPB)
667244197Sgonzo		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
668244197Sgonzo	else
669244197Sgonzo		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
670244197Sgonzo
671244197Sgonzo	if (t->c_cflag & PARENB) {
672244197Sgonzo		if (t->c_cflag & PARODD) {
673244197Sgonzo			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
674244197Sgonzo		} else {
675244197Sgonzo			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
676244197Sgonzo		}
677244197Sgonzo	} else {
678244197Sgonzo		cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
679244197Sgonzo	}
680244197Sgonzo
681244197Sgonzo	switch (t->c_cflag & CSIZE) {
682244197Sgonzo	case CS5:
683244197Sgonzo		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
684244197Sgonzo		break;
685244197Sgonzo
686244197Sgonzo	case CS6:
687244197Sgonzo		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
688244197Sgonzo		break;
689244197Sgonzo
690244197Sgonzo	case CS7:
691244197Sgonzo		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
692244197Sgonzo		break;
693244197Sgonzo
694244197Sgonzo	case CS8:
695244197Sgonzo		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
696244197Sgonzo		break;
697244197Sgonzo	}
698244197Sgonzo
699244197Sgonzo	if (t->c_cflag & CRTSCTS) {
700244197Sgonzo		cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
701244197Sgonzo	} else if (t->c_iflag & (IXON | IXOFF)) {
702244197Sgonzo		cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
703244197Sgonzo		cfg->v_start = t->c_cc[VSTART];
704244197Sgonzo		cfg->v_stop = t->c_cc[VSTOP];
705244197Sgonzo	} else {
706244197Sgonzo		cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
707244197Sgonzo	}
708244197Sgonzo
709244197Sgonzo	return (0);
710244197Sgonzo}
711244197Sgonzo
712244197Sgonzostatic int
713244197Sgonzouftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t)
714244197Sgonzo{
715244197Sgonzo	struct uftdi_softc *sc = ucom->sc_parent;
716	struct uftdi_param_config cfg;
717
718	DPRINTF("\n");
719
720	return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
721}
722
723static void
724uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
725{
726	struct uftdi_softc *sc = ucom->sc_parent;
727	uint16_t wIndex = ucom->sc_portno;
728	struct uftdi_param_config cfg;
729	struct usb2_device_request req;
730
731	if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
732		/* should not happen */
733		return;
734	}
735	sc->sc_last_lcr = cfg.lcr;
736
737	DPRINTF("\n");
738
739	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
740	req.bRequest = FTDI_SIO_SET_BAUD_RATE;
741	USETW(req.wValue, cfg.rate);
742	USETW(req.wIndex, wIndex);
743	USETW(req.wLength, 0);
744	uftdi_cfg_do_request(sc, &req, NULL);
745
746	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
747	req.bRequest = FTDI_SIO_SET_DATA;
748	USETW(req.wValue, cfg.lcr);
749	USETW(req.wIndex, wIndex);
750	USETW(req.wLength, 0);
751	uftdi_cfg_do_request(sc, &req, NULL);
752
753	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
754	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
755	USETW2(req.wValue, cfg.v_stop, cfg.v_start);
756	USETW2(req.wIndex, cfg.v_flow, wIndex);
757	USETW(req.wLength, 0);
758	uftdi_cfg_do_request(sc, &req, NULL);
759}
760
761static void
762uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
763{
764	struct uftdi_softc *sc = ucom->sc_parent;
765
766	DPRINTF("msr=0x%02x lsr=0x%02x\n",
767	    sc->sc_msr, sc->sc_lsr);
768
769	*msr = sc->sc_msr;
770	*lsr = sc->sc_lsr;
771}
772
773static void
774uftdi_start_read(struct usb2_com_softc *ucom)
775{
776	struct uftdi_softc *sc = ucom->sc_parent;
777
778	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
779}
780
781static void
782uftdi_stop_read(struct usb2_com_softc *ucom)
783{
784	struct uftdi_softc *sc = ucom->sc_parent;
785
786	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_CS_RD]);
787	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
788}
789
790static void
791uftdi_start_write(struct usb2_com_softc *ucom)
792{
793	struct uftdi_softc *sc = ucom->sc_parent;
794
795	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
796}
797
798static void
799uftdi_stop_write(struct usb2_com_softc *ucom)
800{
801	struct uftdi_softc *sc = ucom->sc_parent;
802
803	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_CS_WR]);
804	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
805}
806
807/*------------------------------------------------------------------------*
808 *	uftdi_8u232am_getrate
809 *
810 * Return values:
811 *    0: Success
812 * Else: Failure
813 *------------------------------------------------------------------------*/
814static uint8_t
815uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
816{
817	/* Table of the nearest even powers-of-2 for values 0..15. */
818	static const uint8_t roundoff[16] = {
819		0, 2, 2, 4, 4, 4, 8, 8,
820		8, 8, 8, 8, 16, 16, 16, 16,
821	};
822	uint32_t d;
823	uint32_t freq;
824	uint16_t result;
825
826	if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
827		return (1);		/* prevent numerical overflow */
828
829	/* Special cases for 2M and 3M. */
830	if ((speed >= ((3000000 * 100) / 103)) &&
831	    (speed <= ((3000000 * 100) / 97))) {
832		result = 0;
833		goto done;
834	}
835	if ((speed >= ((2000000 * 100) / 103)) &&
836	    (speed <= ((2000000 * 100) / 97))) {
837		result = 1;
838		goto done;
839	}
840	d = (FTDI_8U232AM_FREQ << 4) / speed;
841	d = (d & ~15) + roundoff[d & 15];
842
843	if (d < FTDI_8U232AM_MIN_DIV)
844		d = FTDI_8U232AM_MIN_DIV;
845	else if (d > FTDI_8U232AM_MAX_DIV)
846		d = FTDI_8U232AM_MAX_DIV;
847
848	/*
849	 * Calculate the frequency needed for "d" to exactly divide down to
850	 * our target "speed", and check that the actual frequency is within
851	 * 3% of this.
852	 */
853	freq = (speed * d);
854	if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
855	    (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
856		return (1);
857
858	/*
859	 * Pack the divisor into the resultant value.  The lower 14-bits
860	 * hold the integral part, while the upper 2 bits encode the
861	 * fractional component: either 0, 0.5, 0.25, or 0.125.
862	 */
863	result = (d >> 4);
864	if (d & 8)
865		result |= 0x4000;
866	else if (d & 4)
867		result |= 0x8000;
868	else if (d & 2)
869		result |= 0xc000;
870
871done:
872	*rate = result;
873	return (0);
874}
875