uftdi.c revision 190633
1117610Sdes/*	$NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $	*/
2271947Sdes
3117610Sdes/*-
4271947Sdes * Copyright (c) 2000 The NetBSD Foundation, Inc.
5117610Sdes * All rights reserved.
6174832Sdes *
7117610Sdes * This code is derived from software contributed to The NetBSD Foundation
8228692Sdes * by Lennart Augustsson (lennart@augustsson.net).
9255376Sdes *
10228692Sdes * Redistribution and use in source and binary forms, with or without
11228692Sdes * modification, are permitted provided that the following conditions
12117610Sdes * are met:
13117610Sdes * 1. Redistributions of source code must retain the above copyright
14228692Sdes *    notice, this list of conditions and the following disclaimer.
15228692Sdes * 2. Redistributions in binary form must reproduce the above copyright
16228692Sdes *    notice, this list of conditions and the following disclaimer in the
17117610Sdes *    documentation and/or other materials provided with the distribution.
18174832Sdes * 3. All advertising materials mentioning features or use of this software
19174832Sdes *    must display the following acknowledgement:
20228692Sdes *        This product includes software developed by the NetBSD
21117610Sdes *        Foundation, Inc. and its contributors.
22117610Sdes * 4. Neither the name of The NetBSD Foundation nor the names of its
23228692Sdes *    contributors may be used to endorse or promote products derived
24141098Sdes *    from this software without specific prior written permission.
25141098Sdes *
26174832Sdes * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27174832Sdes * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28228692Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29228692Sdes * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30228692Sdes * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31228692Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32228692Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33174832Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34117610Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35117610Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36174832Sdes * POSSIBILITY OF SUCH DAMAGE.
37228692Sdes */
38228692Sdes
39228692Sdes#include <sys/cdefs.h>
40228692Sdes__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uftdi.c 190633 2009-04-01 20:23:47Z piso $");
41228692Sdes
42228692Sdes/*
43228692Sdes * NOTE: all function names beginning like "uftdi_cfg_" can only
44228692Sdes * be called from within the config thread function !
45228692Sdes */
46228692Sdes
47228692Sdes/*
48228692Sdes * FTDI FT8U100AX serial adapter driver
49228692Sdes */
50228692Sdes
51228692Sdes#include "usbdevs.h"
52228692Sdes#include <dev/usb/usb.h>
53228692Sdes#include <dev/usb/usb_mfunc.h>
54228692Sdes#include <dev/usb/usb_error.h>
55228692Sdes#include <dev/usb/usb_cdc.h>
56228692Sdes
57174832Sdes#define	USB_DEBUG_VAR uftdi_debug
58228692Sdes
59228692Sdes#include <dev/usb/usb_core.h>
60228692Sdes#include <dev/usb/usb_debug.h>
61228692Sdes#include <dev/usb/usb_process.h>
62228692Sdes#include <dev/usb/usb_request.h>
63228692Sdes#include <dev/usb/usb_lookup.h>
64228692Sdes#include <dev/usb/usb_util.h>
65228692Sdes#include <dev/usb/usb_busdma.h>
66228692Sdes
67228692Sdes#include <dev/usb/serial/usb_serial.h>
68228692Sdes#include <dev/usb/serial/uftdi_reg.h>
69228692Sdes
70174832Sdes#if USB_DEBUG
71228692Sdesstatic int uftdi_debug = 0;
72228692Sdes
73174832SdesSYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
74174832SdesSYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW,
75228692Sdes    &uftdi_debug, 0, "Debug level");
76228692Sdes#endif
77228692Sdes
78228692Sdes#define	UFTDI_CONFIG_INDEX	0
79228692Sdes#define	UFTDI_IFACE_INDEX	0
80228692Sdes
81228692Sdes#define	UFTDI_IBUFSIZE 64		/* bytes, maximum number of bytes per
82117610Sdes					 * frame */
83117610Sdes#define	UFTDI_OBUFSIZE 64		/* bytes, cannot be increased due to
84117610Sdes					 * do size encoding */
85174832Sdes
86174832Sdesenum {
87174832Sdes	UFTDI_BULK_DT_WR,
88174832Sdes	UFTDI_BULK_DT_RD,
89174832Sdes	UFTDI_N_TRANSFER,
90174832Sdes};
91174832Sdes
92174832Sdesstruct uftdi_softc {
93228692Sdes	struct usb2_com_super_softc sc_super_ucom;
94228692Sdes	struct usb2_com_softc sc_ucom;
95174832Sdes
96174832Sdes	struct usb2_device *sc_udev;
97174832Sdes	struct usb2_xfer *sc_xfer[UFTDI_N_TRANSFER];
98174832Sdes	device_t sc_dev;
99174832Sdes	struct mtx sc_mtx;
100174832Sdes
101228692Sdes	uint32_t sc_unit;
102228692Sdes	enum uftdi_type sc_type;
103174832Sdes
104174832Sdes	uint16_t sc_last_lcr;
105174832Sdes
106174832Sdes	uint8_t	sc_iface_index;
107174832Sdes	uint8_t	sc_hdrlen;
108174832Sdes	uint8_t	sc_msr;
109174832Sdes	uint8_t	sc_lsr;
110174832Sdes
111174832Sdes	uint8_t	sc_name[16];
112174832Sdes};
113228692Sdes
114228692Sdesstruct uftdi_param_config {
115174832Sdes	uint16_t rate;
116174832Sdes	uint16_t lcr;
117228692Sdes	uint8_t	v_start;
118228692Sdes	uint8_t	v_stop;
119228692Sdes	uint8_t	v_flow;
120228692Sdes};
121228692Sdes
122228692Sdes/* prototypes */
123228692Sdes
124174832Sdesstatic device_probe_t uftdi_probe;
125141098Sdesstatic device_attach_t uftdi_attach;
126141098Sdesstatic device_detach_t uftdi_detach;
127141098Sdes
128117610Sdesstatic usb2_callback_t uftdi_write_callback;
129141098Sdesstatic usb2_callback_t uftdi_read_callback;
130228692Sdes
131228692Sdesstatic void	uftdi_cfg_open(struct usb2_com_softc *);
132228692Sdesstatic void	uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
133228692Sdesstatic void	uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t);
134141098Sdesstatic void	uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t);
135174832Sdesstatic int	uftdi_set_parm_soft(struct termios *,
136228692Sdes		    struct uftdi_param_config *, uint8_t);
137141098Sdesstatic int	uftdi_pre_param(struct usb2_com_softc *, struct termios *);
138255376Sdesstatic void	uftdi_cfg_param(struct usb2_com_softc *, struct termios *);
139255376Sdesstatic void	uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *,
140255376Sdes		    uint8_t *);
141255376Sdesstatic void	uftdi_start_read(struct usb2_com_softc *);
142255376Sdesstatic void	uftdi_stop_read(struct usb2_com_softc *);
143255376Sdesstatic void	uftdi_start_write(struct usb2_com_softc *);
144255376Sdesstatic void	uftdi_stop_write(struct usb2_com_softc *);
145255376Sdesstatic uint8_t	uftdi_8u232am_getrate(uint32_t, uint16_t *);
146255376Sdes
147255376Sdesstatic const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = {
148255376Sdes
149255376Sdes	[UFTDI_BULK_DT_WR] = {
150255376Sdes		.type = UE_BULK,
151255376Sdes		.endpoint = UE_ADDR_ANY,
152255376Sdes		.direction = UE_DIR_OUT,
153255376Sdes		.mh.bufsize = UFTDI_OBUFSIZE,
154255376Sdes		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
155255376Sdes		.mh.callback = &uftdi_write_callback,
156255376Sdes	},
157255376Sdes
158255376Sdes	[UFTDI_BULK_DT_RD] = {
159255376Sdes		.type = UE_BULK,
160255376Sdes		.endpoint = UE_ADDR_ANY,
161255376Sdes		.direction = UE_DIR_IN,
162255376Sdes		.mh.bufsize = UFTDI_IBUFSIZE,
163174832Sdes		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
164228692Sdes		.mh.callback = &uftdi_read_callback,
165228692Sdes	},
166228692Sdes};
167228692Sdes
168228692Sdesstatic const struct usb2_com_callback uftdi_callback = {
169228692Sdes	.usb2_com_cfg_get_status = &uftdi_cfg_get_status,
170228692Sdes	.usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr,
171174832Sdes	.usb2_com_cfg_set_rts = &uftdi_cfg_set_rts,
172228692Sdes	.usb2_com_cfg_set_break = &uftdi_cfg_set_break,
173228692Sdes	.usb2_com_cfg_param = &uftdi_cfg_param,
174228692Sdes	.usb2_com_cfg_open = &uftdi_cfg_open,
175228692Sdes	.usb2_com_pre_param = &uftdi_pre_param,
176228692Sdes	.usb2_com_start_read = &uftdi_start_read,
177228692Sdes	.usb2_com_stop_read = &uftdi_stop_read,
178117610Sdes	.usb2_com_start_write = &uftdi_start_write,
179228692Sdes	.usb2_com_stop_write = &uftdi_stop_write,
180228692Sdes};
181228692Sdes
182228692Sdesstatic device_method_t uftdi_methods[] = {
183228692Sdes	/* Device interface */
184228692Sdes	DEVMETHOD(device_probe, uftdi_probe),
185117610Sdes	DEVMETHOD(device_attach, uftdi_attach),
186174832Sdes	DEVMETHOD(device_detach, uftdi_detach),
187228692Sdes
188228692Sdes	{0, 0}
189228692Sdes};
190228692Sdes
191228692Sdesstatic devclass_t uftdi_devclass;
192117610Sdes
193174832Sdesstatic driver_t uftdi_driver = {
194228692Sdes	.name = "uftdi",
195174832Sdes	.methods = uftdi_methods,
196255376Sdes	.size = sizeof(struct uftdi_softc),
197255376Sdes};
198228692Sdes
199228692SdesDRIVER_MODULE(uftdi, uhub, uftdi_driver, uftdi_devclass, NULL, 0);
200228692SdesMODULE_DEPEND(uftdi, ucom, 1, 1, 1);
201228692SdesMODULE_DEPEND(uftdi, usb, 1, 1, 1);
202228692Sdes
203174832Sdesstatic struct usb2_device_id uftdi_devs[] = {
204228692Sdes	{USB_VPI(USB_VENDOR_ATMEL, USB_PRODUCT_ATMEL_STK541, UFTDI_TYPE_8U232AM)},
205228692Sdes	{USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD, UFTDI_TYPE_8U232AM)},
206228692Sdes	{USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_WIRELESSHANDHELDTERMINAL, UFTDI_TYPE_8U232AM)},
207228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)},
208228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)},
209228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)},
210228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)},
211228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)},
212228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)},
213174832Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)},
214228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)},
215174832Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)},
216228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)},
217174832Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)},
218174832Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)},
219228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)},
220228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)},
221117610Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)},
222117610Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)},
223117610Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)},
224117610Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)},
225228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)},
226228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)},
227117610Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)},
228174832Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)},
229228692Sdes	{USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)},
230228692Sdes	{USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)},
231228692Sdes	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)},
232228692Sdes	{USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)},
233228692Sdes	{USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)},
234228692Sdes	{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)},
235228692Sdes};
236228692Sdes
237228692Sdesstatic int
238174832Sdesuftdi_probe(device_t dev)
239174832Sdes{
240228692Sdes	struct usb2_attach_arg *uaa = device_get_ivars(dev);
241174832Sdes
242228692Sdes	if (uaa->usb2_mode != USB_MODE_HOST) {
243228692Sdes		return (ENXIO);
244228692Sdes	}
245228692Sdes	if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
246174832Sdes		return (ENXIO);
247174832Sdes	}
248174832Sdes	/* attach to all present interfaces */
249228692Sdes
250255376Sdes	return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
251255376Sdes}
252255376Sdes
253255376Sdesstatic int
254255376Sdesuftdi_attach(device_t dev)
255255376Sdes{
256255376Sdes	struct usb2_attach_arg *uaa = device_get_ivars(dev);
257255376Sdes	struct uftdi_softc *sc = device_get_softc(dev);
258255376Sdes	int error;
259255376Sdes
260255376Sdes	sc->sc_udev = uaa->device;
261255376Sdes	sc->sc_dev = dev;
262255376Sdes	sc->sc_unit = device_get_unit(dev);
263255376Sdes
264255376Sdes	device_set_usb2_desc(dev);
265255376Sdes	mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF);
266255376Sdes
267255376Sdes	snprintf(sc->sc_name, sizeof(sc->sc_name),
268255376Sdes	    "%s", device_get_nameunit(dev));
269174832Sdes
270174832Sdes	DPRINTF("\n");
271228692Sdes
272228692Sdes	sc->sc_iface_index = uaa->info.bIfaceIndex;
273228692Sdes	sc->sc_type = USB_GET_DRIVER_INFO(uaa);
274228692Sdes
275228692Sdes	switch (sc->sc_type) {
276228692Sdes	case UFTDI_TYPE_SIO:
277228692Sdes		sc->sc_hdrlen = 1;
278228692Sdes		break;
279228692Sdes	case UFTDI_TYPE_8U232AM:
280228692Sdes	default:
281228692Sdes		sc->sc_hdrlen = 0;
282228692Sdes		break;
283228692Sdes	}
284174832Sdes
285174832Sdes	error = usb2_transfer_setup(uaa->device,
286174832Sdes	    &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
287228692Sdes	    UFTDI_N_TRANSFER, sc, &sc->sc_mtx);
288228692Sdes
289228692Sdes	if (error) {
290228692Sdes		device_printf(dev, "allocating USB "
291228692Sdes		    "transfers failed!\n");
292174832Sdes		goto detach;
293228692Sdes	}
294228692Sdes	sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
295228692Sdes
296228692Sdes	/* clear stall at first run */
297228692Sdes	mtx_lock(&sc->sc_mtx);
298228692Sdes	usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
299228692Sdes	usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
300228692Sdes	mtx_unlock(&sc->sc_mtx);
301228692Sdes
302228692Sdes	/* set a valid "lcr" value */
303228692Sdes
304174832Sdes	sc->sc_last_lcr =
305228692Sdes	    (FTDI_SIO_SET_DATA_STOP_BITS_2 |
306228692Sdes	    FTDI_SIO_SET_DATA_PARITY_NONE |
307228692Sdes	    FTDI_SIO_SET_DATA_BITS(8));
308228692Sdes
309228692Sdes	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
310228692Sdes	    &uftdi_callback, &sc->sc_mtx);
311228692Sdes	if (error) {
312174832Sdes		goto detach;
313228692Sdes	}
314228692Sdes	return (0);			/* success */
315228692Sdes
316228692Sdesdetach:
317228692Sdes	uftdi_detach(dev);
318228692Sdes	return (ENXIO);
319228692Sdes}
320228692Sdes
321228692Sdesstatic int
322174832Sdesuftdi_detach(device_t dev)
323228692Sdes{
324228692Sdes	struct uftdi_softc *sc = device_get_softc(dev);
325228692Sdes
326228692Sdes	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
327228692Sdes	usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
328174832Sdes	mtx_destroy(&sc->sc_mtx);
329228692Sdes
330228692Sdes	return (0);
331228692Sdes}
332228692Sdes
333228692Sdesstatic void
334228692Sdesuftdi_cfg_open(struct usb2_com_softc *ucom)
335228692Sdes{
336228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
337228692Sdes	uint16_t wIndex = ucom->sc_portno;
338228692Sdes	struct usb2_device_request req;
339228692Sdes
340228692Sdes	DPRINTF("");
341228692Sdes
342228692Sdes	/* perform a full reset on the device */
343228692Sdes
344228692Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
345228692Sdes	req.bRequest = FTDI_SIO_RESET;
346228692Sdes	USETW(req.wValue, FTDI_SIO_RESET_SIO);
347228692Sdes	USETW(req.wIndex, wIndex);
348228692Sdes	USETW(req.wLength, 0);
349228692Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
350228692Sdes	    &req, NULL, 0, 1000);
351228692Sdes
352228692Sdes	/* turn on RTS/CTS flow control */
353228692Sdes
354228692Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
355228692Sdes	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
356228692Sdes	USETW(req.wValue, 0);
357228692Sdes	USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
358228692Sdes	USETW(req.wLength, 0);
359228692Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
360228692Sdes	    &req, NULL, 0, 1000);
361228692Sdes
362228692Sdes	/*
363228692Sdes	 * NOTE: with the new UCOM layer there will always be a
364228692Sdes	 * "uftdi_cfg_param()" call after "open()", so there is no need for
365228692Sdes	 * "open()" to configure anything
366228692Sdes	 */
367117610Sdes}
368174832Sdes
369228692Sdesstatic void
370255376Sdesuftdi_write_callback(struct usb2_xfer *xfer)
371255376Sdes{
372255376Sdes	struct uftdi_softc *sc = xfer->priv_sc;
373255376Sdes	uint32_t actlen;
374255376Sdes	uint8_t buf[1];
375255376Sdes
376255376Sdes	switch (USB_GET_STATE(xfer)) {
377255376Sdes	case USB_ST_SETUP:
378228692Sdes	case USB_ST_TRANSFERRED:
379228692Sdestr_setup:
380228692Sdes		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers,
381228692Sdes		    sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
382228692Sdes		    &actlen)) {
383228692Sdes
384228692Sdes			if (sc->sc_hdrlen > 0) {
385228692Sdes				buf[0] =
386228692Sdes				    FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
387228692Sdes				usb2_copy_in(xfer->frbuffers, 0, buf, 1);
388228692Sdes			}
389228692Sdes			xfer->frlengths[0] = actlen + sc->sc_hdrlen;
390228692Sdes			usb2_start_hardware(xfer);
391228692Sdes		}
392228692Sdes		return;
393228692Sdes
394228692Sdes	default:			/* Error */
395174832Sdes		if (xfer->error != USB_ERR_CANCELLED) {
396228692Sdes			/* try to clear stall first */
397228692Sdes			xfer->flags.stall_pipe = 1;
398228692Sdes			goto tr_setup;
399228692Sdes		}
400228692Sdes		return;
401228692Sdes	}
402228692Sdes}
403228692Sdes
404228692Sdesstatic void
405228692Sdesuftdi_read_callback(struct usb2_xfer *xfer)
406228692Sdes{
407228692Sdes	struct uftdi_softc *sc = xfer->priv_sc;
408228692Sdes	uint8_t buf[2];
409228692Sdes	uint8_t ftdi_msr;
410228692Sdes	uint8_t msr;
411228692Sdes	uint8_t lsr;
412174832Sdes
413174832Sdes	switch (USB_GET_STATE(xfer)) {
414228692Sdes	case USB_ST_TRANSFERRED:
415228692Sdes
416228692Sdes		if (xfer->actlen < 2) {
417228692Sdes			goto tr_setup;
418228692Sdes		}
419228692Sdes		usb2_copy_out(xfer->frbuffers, 0, buf, 2);
420228692Sdes
421228692Sdes		ftdi_msr = FTDI_GET_MSR(buf);
422228692Sdes		lsr = FTDI_GET_LSR(buf);
423228692Sdes
424228692Sdes		msr = 0;
425228692Sdes		if (ftdi_msr & FTDI_SIO_CTS_MASK)
426228692Sdes			msr |= SER_CTS;
427228692Sdes		if (ftdi_msr & FTDI_SIO_DSR_MASK)
428228692Sdes			msr |= SER_DSR;
429174832Sdes		if (ftdi_msr & FTDI_SIO_RI_MASK)
430228692Sdes			msr |= SER_RI;
431228692Sdes		if (ftdi_msr & FTDI_SIO_RLSD_MASK)
432228692Sdes			msr |= SER_DCD;
433174832Sdes
434228692Sdes		if ((sc->sc_msr != msr) ||
435174832Sdes		    ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
436174832Sdes			DPRINTF("status change msr=0x%02x (0x%02x) "
437228692Sdes			    "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
438228692Sdes			    lsr, sc->sc_lsr);
439174832Sdes
440228692Sdes			sc->sc_msr = msr;
441174832Sdes			sc->sc_lsr = lsr;
442174832Sdes
443228692Sdes			usb2_com_status_change(&sc->sc_ucom);
444228692Sdes		}
445174832Sdes		xfer->actlen -= 2;
446228692Sdes
447174832Sdes		if (xfer->actlen > 0) {
448174832Sdes			usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2,
449228692Sdes			    xfer->actlen);
450228692Sdes		}
451228692Sdes	case USB_ST_SETUP:
452228692Sdestr_setup:
453228692Sdes		xfer->frlengths[0] = xfer->max_data_length;
454228692Sdes		usb2_start_hardware(xfer);
455228692Sdes		return;
456228692Sdes
457228692Sdes	default:			/* Error */
458228692Sdes		if (xfer->error != USB_ERR_CANCELLED) {
459228692Sdes			/* try to clear stall first */
460228692Sdes			xfer->flags.stall_pipe = 1;
461228692Sdes			goto tr_setup;
462228692Sdes		}
463228692Sdes		return;
464228692Sdes	}
465228692Sdes}
466228692Sdes
467174832Sdesstatic void
468228692Sdesuftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
469228692Sdes{
470228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
471228692Sdes	uint16_t wIndex = ucom->sc_portno;
472228692Sdes	uint16_t wValue;
473228692Sdes	struct usb2_device_request req;
474174832Sdes
475174832Sdes	wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
476228692Sdes
477228692Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
478228692Sdes	req.bRequest = FTDI_SIO_MODEM_CTRL;
479228692Sdes	USETW(req.wValue, wValue);
480228692Sdes	USETW(req.wIndex, wIndex);
481174832Sdes	USETW(req.wLength, 0);
482174832Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
483174832Sdes	    &req, NULL, 0, 1000);
484174832Sdes}
485117610Sdes
486174832Sdesstatic void
487174832Sdesuftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
488174832Sdes{
489174832Sdes	struct uftdi_softc *sc = ucom->sc_parent;
490117610Sdes	uint16_t wIndex = ucom->sc_portno;
491174832Sdes	uint16_t wValue;
492174832Sdes	struct usb2_device_request req;
493117610Sdes
494174832Sdes	wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
495117610Sdes
496174832Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
497228692Sdes	req.bRequest = FTDI_SIO_MODEM_CTRL;
498117610Sdes	USETW(req.wValue, wValue);
499255376Sdes	USETW(req.wIndex, wIndex);
500255376Sdes	USETW(req.wLength, 0);
501255376Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
502255376Sdes	    &req, NULL, 0, 1000);
503117610Sdes}
504117610Sdes
505174832Sdesstatic void
506174832Sdesuftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
507117610Sdes{
508117610Sdes	struct uftdi_softc *sc = ucom->sc_parent;
509117610Sdes	uint16_t wIndex = ucom->sc_portno;
510117610Sdes	uint16_t wValue;
511174832Sdes	struct usb2_device_request req;
512228692Sdes
513174832Sdes	if (onoff) {
514228692Sdes		sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
515174832Sdes	} else {
516228692Sdes		sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
517228692Sdes	}
518228692Sdes
519174832Sdes	wValue = sc->sc_last_lcr;
520174832Sdes
521174832Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
522117610Sdes	req.bRequest = FTDI_SIO_SET_DATA;
523117610Sdes	USETW(req.wValue, wValue);
524117610Sdes	USETW(req.wIndex, wIndex);
525174832Sdes	USETW(req.wLength, 0);
526174832Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
527174832Sdes	    &req, NULL, 0, 1000);
528174832Sdes}
529228692Sdes
530174832Sdesstatic int
531228692Sdesuftdi_set_parm_soft(struct termios *t,
532228692Sdes    struct uftdi_param_config *cfg, uint8_t type)
533228692Sdes{
534228692Sdes	bzero(cfg, sizeof(*cfg));
535228692Sdes
536228692Sdes	switch (type) {
537255376Sdes	case UFTDI_TYPE_SIO:
538228692Sdes		switch (t->c_ospeed) {
539255376Sdes		case 300:
540228692Sdes			cfg->rate = ftdi_sio_b300;
541228692Sdes			break;
542228692Sdes		case 600:
543255376Sdes			cfg->rate = ftdi_sio_b600;
544228692Sdes			break;
545117610Sdes		case 1200:
546255376Sdes			cfg->rate = ftdi_sio_b1200;
547117610Sdes			break;
548174832Sdes		case 2400:
549174832Sdes			cfg->rate = ftdi_sio_b2400;
550117610Sdes			break;
551141098Sdes		case 4800:
552228692Sdes			cfg->rate = ftdi_sio_b4800;
553141098Sdes			break;
554141098Sdes		case 9600:
555141098Sdes			cfg->rate = ftdi_sio_b9600;
556141098Sdes			break;
557141098Sdes		case 19200:
558255376Sdes			cfg->rate = ftdi_sio_b19200;
559255376Sdes			break;
560117610Sdes		case 38400:
561117610Sdes			cfg->rate = ftdi_sio_b38400;
562141098Sdes			break;
563117610Sdes		case 57600:
564117610Sdes			cfg->rate = ftdi_sio_b57600;
565141098Sdes			break;
566117610Sdes		case 115200:
567141098Sdes			cfg->rate = ftdi_sio_b115200;
568141098Sdes			break;
569141098Sdes		default:
570228692Sdes			return (EINVAL);
571228692Sdes		}
572141098Sdes		break;
573117610Sdes
574228692Sdes	case UFTDI_TYPE_8U232AM:
575117610Sdes		if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
576117610Sdes			return (EINVAL);
577117610Sdes		}
578117610Sdes		break;
579117610Sdes	}
580117610Sdes
581117610Sdes	if (t->c_cflag & CSTOPB)
582174832Sdes		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
583141098Sdes	else
584174832Sdes		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
585117610Sdes
586117610Sdes	if (t->c_cflag & PARENB) {
587117610Sdes		if (t->c_cflag & PARODD) {
588117610Sdes			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
589117610Sdes		} else {
590117610Sdes			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
591117610Sdes		}
592117610Sdes	} else {
593271947Sdes		cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
594271947Sdes	}
595174832Sdes
596255376Sdes	switch (t->c_cflag & CSIZE) {
597117610Sdes	case CS5:
598255376Sdes		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
599117610Sdes		break;
600117610Sdes
601117610Sdes	case CS6:
602174832Sdes		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
603117610Sdes		break;
604117610Sdes
605174832Sdes	case CS7:
606117610Sdes		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
607117610Sdes		break;
608174832Sdes
609117610Sdes	case CS8:
610117610Sdes		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
611117610Sdes		break;
612174832Sdes	}
613117610Sdes
614117610Sdes	if (t->c_cflag & CRTSCTS) {
615117610Sdes		cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
616174832Sdes	} else if (t->c_iflag & (IXON | IXOFF)) {
617174832Sdes		cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
618117610Sdes		cfg->v_start = t->c_cc[VSTART];
619117610Sdes		cfg->v_stop = t->c_cc[VSTOP];
620117610Sdes	} else {
621117610Sdes		cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
622174832Sdes	}
623117610Sdes
624117610Sdes	return (0);
625174832Sdes}
626117610Sdes
627117610Sdesstatic int
628174832Sdesuftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t)
629174832Sdes{
630174832Sdes	struct uftdi_softc *sc = ucom->sc_parent;
631174832Sdes	struct uftdi_param_config cfg;
632117610Sdes
633117610Sdes	DPRINTF("\n");
634117610Sdes
635228692Sdes	return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
636228692Sdes}
637228692Sdes
638228692Sdesstatic void
639255376Sdesuftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
640228692Sdes{
641228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
642228692Sdes	uint16_t wIndex = ucom->sc_portno;
643228692Sdes	struct uftdi_param_config cfg;
644228692Sdes	struct usb2_device_request req;
645228692Sdes
646228692Sdes	if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
647228692Sdes		/* should not happen */
648228692Sdes		return;
649228692Sdes	}
650255376Sdes	sc->sc_last_lcr = cfg.lcr;
651255376Sdes
652228692Sdes	DPRINTF("\n");
653228692Sdes
654228692Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
655228692Sdes	req.bRequest = FTDI_SIO_SET_BAUD_RATE;
656228692Sdes	USETW(req.wValue, cfg.rate);
657228692Sdes	USETW(req.wIndex, wIndex);
658228692Sdes	USETW(req.wLength, 0);
659228692Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
660228692Sdes	    &req, NULL, 0, 1000);
661228692Sdes
662228692Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
663228692Sdes	req.bRequest = FTDI_SIO_SET_DATA;
664228692Sdes	USETW(req.wValue, cfg.lcr);
665228692Sdes	USETW(req.wIndex, wIndex);
666228692Sdes	USETW(req.wLength, 0);
667228692Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
668228692Sdes	    &req, NULL, 0, 1000);
669228692Sdes
670228692Sdes	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
671228692Sdes	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
672228692Sdes	USETW2(req.wValue, cfg.v_stop, cfg.v_start);
673228692Sdes	USETW2(req.wIndex, cfg.v_flow, wIndex);
674228692Sdes	USETW(req.wLength, 0);
675228692Sdes	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
676228692Sdes	    &req, NULL, 0, 1000);
677228692Sdes}
678228692Sdes
679174832Sdesstatic void
680228692Sdesuftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
681228692Sdes{
682228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
683228692Sdes
684228692Sdes	DPRINTF("msr=0x%02x lsr=0x%02x\n",
685228692Sdes	    sc->sc_msr, sc->sc_lsr);
686228692Sdes
687255376Sdes	*msr = sc->sc_msr;
688228692Sdes	*lsr = sc->sc_lsr;
689228692Sdes}
690228692Sdes
691228692Sdesstatic void
692228692Sdesuftdi_start_read(struct usb2_com_softc *ucom)
693228692Sdes{
694228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
695228692Sdes
696228692Sdes	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
697228692Sdes}
698228692Sdes
699228692Sdesstatic void
700174832Sdesuftdi_stop_read(struct usb2_com_softc *ucom)
701255376Sdes{
702255376Sdes	struct uftdi_softc *sc = ucom->sc_parent;
703255376Sdes
704255376Sdes	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
705228692Sdes}
706228692Sdes
707228692Sdesstatic void
708228692Sdesuftdi_start_write(struct usb2_com_softc *ucom)
709228692Sdes{
710228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
711228692Sdes
712228692Sdes	usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
713228692Sdes}
714174832Sdes
715228692Sdesstatic void
716228692Sdesuftdi_stop_write(struct usb2_com_softc *ucom)
717228692Sdes{
718228692Sdes	struct uftdi_softc *sc = ucom->sc_parent;
719228692Sdes
720228692Sdes	usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
721228692Sdes}
722228692Sdes
723228692Sdes/*------------------------------------------------------------------------*
724228692Sdes *	uftdi_8u232am_getrate
725228692Sdes *
726228692Sdes * Return values:
727174832Sdes *    0: Success
728228692Sdes * Else: Failure
729228692Sdes *------------------------------------------------------------------------*/
730228692Sdesstatic uint8_t
731228692Sdesuftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
732228692Sdes{
733228692Sdes	/* Table of the nearest even powers-of-2 for values 0..15. */
734228692Sdes	static const uint8_t roundoff[16] = {
735228692Sdes		0, 2, 2, 4, 4, 4, 8, 8,
736228692Sdes		8, 8, 8, 8, 16, 16, 16, 16,
737228692Sdes	};
738228692Sdes	uint32_t d;
739228692Sdes	uint32_t freq;
740228692Sdes	uint16_t result;
741228692Sdes
742228692Sdes	if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
743228692Sdes		return (1);		/* prevent numerical overflow */
744228692Sdes
745228692Sdes	/* Special cases for 2M and 3M. */
746228692Sdes	if ((speed >= ((3000000 * 100) / 103)) &&
747228692Sdes	    (speed <= ((3000000 * 100) / 97))) {
748228692Sdes		result = 0;
749228692Sdes		goto done;
750228692Sdes	}
751228692Sdes	if ((speed >= ((2000000 * 100) / 103)) &&
752228692Sdes	    (speed <= ((2000000 * 100) / 97))) {
753228692Sdes		result = 1;
754228692Sdes		goto done;
755228692Sdes	}
756228692Sdes	d = (FTDI_8U232AM_FREQ << 4) / speed;
757228692Sdes	d = (d & ~15) + roundoff[d & 15];
758228692Sdes
759228692Sdes	if (d < FTDI_8U232AM_MIN_DIV)
760228692Sdes		d = FTDI_8U232AM_MIN_DIV;
761228692Sdes	else if (d > FTDI_8U232AM_MAX_DIV)
762228692Sdes		d = FTDI_8U232AM_MAX_DIV;
763228692Sdes
764228692Sdes	/*
765228692Sdes	 * Calculate the frequency needed for "d" to exactly divide down to
766141098Sdes	 * our target "speed", and check that the actual frequency is within
767228692Sdes	 * 3% of this.
768228692Sdes	 */
769255376Sdes	freq = (speed * d);
770228692Sdes	if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
771228692Sdes	    (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
772228692Sdes		return (1);
773228692Sdes
774228692Sdes	/*
775228692Sdes	 * Pack the divisor into the resultant value.  The lower 14-bits
776228692Sdes	 * hold the integral part, while the upper 2 bits encode the
777228692Sdes	 * fractional component: either 0, 0.5, 0.25, or 0.125.
778228692Sdes	 */
779228692Sdes	result = (d >> 4);
780228692Sdes	if (d & 8)
781228692Sdes		result |= 0x4000;
782228692Sdes	else if (d & 4)
783228692Sdes		result |= 0x8000;
784228692Sdes	else if (d & 2)
785228692Sdes		result |= 0xc000;
786228692Sdes
787228692Sdesdone:
788228692Sdes	*rate = result;
789174832Sdes	return (0);
790174832Sdes}
791174832Sdes