uplcom.c revision 194228
1184610Salfred/*	$NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $	*/
2184610Salfred
3184610Salfred#include <sys/cdefs.h>
4184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uplcom.c 194228 2009-06-15 01:02:43Z thompsa $");
5184610Salfred
6184610Salfred/*-
7184610Salfred * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
8184610Salfred * All rights reserved.
9184610Salfred *
10184610Salfred * Redistribution and use in source and binary forms, with or without
11184610Salfred * modification, are permitted provided that the following conditions
12184610Salfred * are met:
13184610Salfred * 1. Redistributions of source code must retain the above copyright
14184610Salfred *    notice, this list of conditions and the following disclaimer.
15184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
16184610Salfred *    notice, this list of conditions and the following disclaimer in the
17184610Salfred *    documentation and/or other materials provided with the distribution.
18184610Salfred *
19184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29184610Salfred * SUCH DAMAGE.
30184610Salfred */
31184610Salfred
32184610Salfred/*-
33184610Salfred * Copyright (c) 2001 The NetBSD Foundation, Inc.
34184610Salfred * All rights reserved.
35184610Salfred *
36184610Salfred * This code is derived from software contributed to The NetBSD Foundation
37184610Salfred * by Ichiro FUKUHARA (ichiro@ichiro.org).
38184610Salfred *
39184610Salfred * Redistribution and use in source and binary forms, with or without
40184610Salfred * modification, are permitted provided that the following conditions
41184610Salfred * are met:
42184610Salfred * 1. Redistributions of source code must retain the above copyright
43184610Salfred *    notice, this list of conditions and the following disclaimer.
44184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
45184610Salfred *    notice, this list of conditions and the following disclaimer in the
46184610Salfred *    documentation and/or other materials provided with the distribution.
47184610Salfred * 3. All advertising materials mentioning features or use of this software
48184610Salfred *    must display the following acknowledgement:
49184610Salfred *        This product includes software developed by the NetBSD
50184610Salfred *        Foundation, Inc. and its contributors.
51184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its
52184610Salfred *    contributors may be used to endorse or promote products derived
53184610Salfred *    from this software without specific prior written permission.
54184610Salfred *
55184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
56184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
59184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65184610Salfred * POSSIBILITY OF SUCH DAMAGE.
66184610Salfred */
67184610Salfred
68184610Salfred/*
69184610Salfred * This driver supports several USB-to-RS232 serial adapters driven by
70184610Salfred * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
71184610Salfred * bridge chip.  The adapters are sold under many different brand
72184610Salfred * names.
73184610Salfred *
74184610Salfred * Datasheets are available at Prolific www site at
75184610Salfred * http://www.prolific.com.tw.  The datasheets don't contain full
76184610Salfred * programming information for the chip.
77184610Salfred *
78184610Salfred * PL-2303HX is probably programmed the same as PL-2303X.
79184610Salfred *
80184610Salfred * There are several differences between PL-2303 and PL-2303(H)X.
81184610Salfred * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
82184610Salfred * different command for controlling CRTSCTS and needs special
83184610Salfred * sequence of commands for initialization which aren't also
84184610Salfred * documented in the datasheet.
85184610Salfred */
86184610Salfred
87188746Sthompsa#include "usbdevs.h"
88188942Sthompsa#include <dev/usb/usb.h>
89188942Sthompsa#include <dev/usb/usb_mfunc.h>
90188942Sthompsa#include <dev/usb/usb_error.h>
91188942Sthompsa#include <dev/usb/usb_cdc.h>
92184610Salfred
93184610Salfred#define	USB_DEBUG_VAR uplcom_debug
94184610Salfred
95188942Sthompsa#include <dev/usb/usb_core.h>
96188942Sthompsa#include <dev/usb/usb_debug.h>
97188942Sthompsa#include <dev/usb/usb_process.h>
98188942Sthompsa#include <dev/usb/usb_request.h>
99188942Sthompsa#include <dev/usb/usb_lookup.h>
100188942Sthompsa#include <dev/usb/usb_util.h>
101188942Sthompsa#include <dev/usb/usb_busdma.h>
102184610Salfred
103188942Sthompsa#include <dev/usb/serial/usb_serial.h>
104184610Salfred
105184610Salfred#if USB_DEBUG
106184610Salfredstatic int uplcom_debug = 0;
107184610Salfred
108192502SthompsaSYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
109192502SthompsaSYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW,
110184610Salfred    &uplcom_debug, 0, "Debug level");
111184610Salfred#endif
112184610Salfred
113184610Salfred#define	UPLCOM_MODVER			1	/* module version */
114184610Salfred
115184610Salfred#define	UPLCOM_CONFIG_INDEX		0
116184610Salfred#define	UPLCOM_IFACE_INDEX		0
117184610Salfred#define	UPLCOM_SECOND_IFACE_INDEX	1
118184610Salfred
119184610Salfred#ifndef UPLCOM_INTR_INTERVAL
120184610Salfred#define	UPLCOM_INTR_INTERVAL		0	/* default */
121184610Salfred#endif
122184610Salfred
123184610Salfred#define	UPLCOM_BULK_BUF_SIZE 1024	/* bytes */
124184610Salfred
125184610Salfred#define	UPLCOM_SET_REQUEST		0x01
126184610Salfred#define	UPLCOM_SET_CRTSCTS		0x41
127184610Salfred#define	UPLCOM_SET_CRTSCTS_PL2303X	0x61
128184610Salfred#define	RSAQ_STATUS_CTS			0x80
129184610Salfred#define	RSAQ_STATUS_DSR			0x02
130184610Salfred#define	RSAQ_STATUS_DCD			0x01
131184610Salfred
132184610Salfred#define	TYPE_PL2303			0
133184610Salfred#define	TYPE_PL2303X			1
134184610Salfred
135187259Sthompsaenum {
136187259Sthompsa	UPLCOM_BULK_DT_WR,
137187259Sthompsa	UPLCOM_BULK_DT_RD,
138187259Sthompsa	UPLCOM_INTR_DT_RD,
139188413Sthompsa	UPLCOM_N_TRANSFER,
140187259Sthompsa};
141187259Sthompsa
142184610Salfredstruct uplcom_softc {
143192984Sthompsa	struct ucom_super_softc sc_super_ucom;
144192984Sthompsa	struct ucom_softc sc_ucom;
145184610Salfred
146192984Sthompsa	struct usb_xfer *sc_xfer[UPLCOM_N_TRANSFER];
147192984Sthompsa	struct usb_device *sc_udev;
148189265Sthompsa	struct mtx sc_mtx;
149184610Salfred
150184610Salfred	uint16_t sc_line;
151184610Salfred
152184610Salfred	uint8_t	sc_lsr;			/* local status register */
153184610Salfred	uint8_t	sc_msr;			/* uplcom status register */
154184610Salfred	uint8_t	sc_chiptype;		/* type of chip */
155184610Salfred	uint8_t	sc_ctrl_iface_no;
156184610Salfred	uint8_t	sc_data_iface_no;
157184610Salfred	uint8_t	sc_iface_index[2];
158184610Salfred};
159184610Salfred
160184610Salfred/* prototypes */
161184610Salfred
162193045Sthompsastatic usb_error_t uplcom_reset(struct uplcom_softc *, struct usb_device *);
163192984Sthompsastatic int	uplcom_pl2303x_init(struct usb_device *);
164192984Sthompsastatic void	uplcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
165192984Sthompsastatic void	uplcom_cfg_set_rts(struct ucom_softc *, uint8_t);
166192984Sthompsastatic void	uplcom_cfg_set_break(struct ucom_softc *, uint8_t);
167192984Sthompsastatic int	uplcom_pre_param(struct ucom_softc *, struct termios *);
168192984Sthompsastatic void	uplcom_cfg_param(struct ucom_softc *, struct termios *);
169192984Sthompsastatic void	uplcom_start_read(struct ucom_softc *);
170192984Sthompsastatic void	uplcom_stop_read(struct ucom_softc *);
171192984Sthompsastatic void	uplcom_start_write(struct ucom_softc *);
172192984Sthompsastatic void	uplcom_stop_write(struct ucom_softc *);
173192984Sthompsastatic void	uplcom_cfg_get_status(struct ucom_softc *, uint8_t *,
174185948Sthompsa		    uint8_t *);
175184610Salfred
176184610Salfredstatic device_probe_t uplcom_probe;
177184610Salfredstatic device_attach_t uplcom_attach;
178184610Salfredstatic device_detach_t uplcom_detach;
179184610Salfred
180193045Sthompsastatic usb_callback_t uplcom_intr_callback;
181193045Sthompsastatic usb_callback_t uplcom_write_callback;
182193045Sthompsastatic usb_callback_t uplcom_read_callback;
183184610Salfred
184192984Sthompsastatic const struct usb_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
185184610Salfred
186187259Sthompsa	[UPLCOM_BULK_DT_WR] = {
187184610Salfred		.type = UE_BULK,
188184610Salfred		.endpoint = UE_ADDR_ANY,
189184610Salfred		.direction = UE_DIR_OUT,
190190734Sthompsa		.bufsize = UPLCOM_BULK_BUF_SIZE,
191190734Sthompsa		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
192190734Sthompsa		.callback = &uplcom_write_callback,
193184610Salfred		.if_index = 0,
194184610Salfred	},
195184610Salfred
196187259Sthompsa	[UPLCOM_BULK_DT_RD] = {
197184610Salfred		.type = UE_BULK,
198184610Salfred		.endpoint = UE_ADDR_ANY,
199184610Salfred		.direction = UE_DIR_IN,
200190734Sthompsa		.bufsize = UPLCOM_BULK_BUF_SIZE,
201190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
202190734Sthompsa		.callback = &uplcom_read_callback,
203184610Salfred		.if_index = 0,
204184610Salfred	},
205184610Salfred
206187259Sthompsa	[UPLCOM_INTR_DT_RD] = {
207184610Salfred		.type = UE_INTERRUPT,
208184610Salfred		.endpoint = UE_ADDR_ANY,
209184610Salfred		.direction = UE_DIR_IN,
210190734Sthompsa		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
211190734Sthompsa		.bufsize = 0,	/* use wMaxPacketSize */
212190734Sthompsa		.callback = &uplcom_intr_callback,
213184610Salfred		.if_index = 1,
214184610Salfred	},
215184610Salfred};
216184610Salfred
217194099Sthompsastatic struct ucom_callback uplcom_callback = {
218194228Sthompsa	.ucom_cfg_get_status = &uplcom_cfg_get_status,
219194228Sthompsa	.ucom_cfg_set_dtr = &uplcom_cfg_set_dtr,
220194228Sthompsa	.ucom_cfg_set_rts = &uplcom_cfg_set_rts,
221194228Sthompsa	.ucom_cfg_set_break = &uplcom_cfg_set_break,
222194228Sthompsa	.ucom_cfg_param = &uplcom_cfg_param,
223194228Sthompsa	.ucom_pre_param = &uplcom_pre_param,
224194228Sthompsa	.ucom_start_read = &uplcom_start_read,
225194228Sthompsa	.ucom_stop_read = &uplcom_stop_read,
226194228Sthompsa	.ucom_start_write = &uplcom_start_write,
227194228Sthompsa	.ucom_stop_write = &uplcom_stop_write,
228184610Salfred};
229184610Salfred
230184610Salfred#define	USB_UPL(v,p,rl,rh,t)				\
231184610Salfred  USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl),	\
232184610Salfred  USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t)
233184610Salfred
234192984Sthompsastatic const struct usb_device_id uplcom_devs[] = {
235184610Salfred	/* Belkin F5U257 */
236184610Salfred	{USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)},
237184610Salfred	/* I/O DATA USB-RSAQ */
238184610Salfred	{USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)},
239184610Salfred	/* I/O DATA USB-RSAQ2 */
240184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)},
241184610Salfred	/* I/O DATA USB-RSAQ3 */
242184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)},
243184610Salfred	/* PLANEX USB-RS232 URS-03 */
244184610Salfred	{USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)},
245184610Salfred	/* TrendNet TU-S9 */
246184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)},
247184610Salfred	/* ST Lab USB-SERIAL-4 */
248184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)},
249184610Salfred	/* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
250184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)},
251184610Salfred	/* TDK USB-PHS Adapter UHA6400 */
252184610Salfred	{USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)},
253184610Salfred	/* RATOC REX-USB60 */
254184610Salfred	{USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)},
255184610Salfred	/* ELECOM UC-SGT */
256184610Salfred	{USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)},
257184610Salfred	{USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)},
258184610Salfred	/* Sagem USB-Serial Controller */
259184610Salfred	{USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)},
260184610Salfred	/* Sony Ericsson USB Cable */
261184610Salfred	{USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)},
262184610Salfred	/* SOURCENEXT KeikaiDenwa 8 */
263184610Salfred	{USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)},
264184610Salfred	/* SOURCENEXT KeikaiDenwa 8 with charger */
265184610Salfred	{USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)},
266184610Salfred	/* HAL Corporation Crossam2+USB */
267184610Salfred	{USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)},
268184610Salfred	/* Sitecom USB to Serial */
269184610Salfred	{USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)},
270184610Salfred	/* Tripp-Lite U209-000-R */
271184610Salfred	{USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)},
272184610Salfred	{USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)},
273184610Salfred	/* Prolific Pharos */
274184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)},
275184610Salfred	/* Willcom W-SIM */
276184610Salfred	{USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)},
277189360Sthompsa	/* Mobile Action MA-620 Infrared Adapter */
278189360Sthompsa	{USB_UPL(USB_VENDOR_MOBILEACTION, USB_PRODUCT_MOBILEACTION_MA620, 0, 0xFFFF, TYPE_PL2303X)},
279189360Sthompsa
280184610Salfred};
281184610Salfred
282184610Salfredstatic device_method_t uplcom_methods[] = {
283184610Salfred	DEVMETHOD(device_probe, uplcom_probe),
284184610Salfred	DEVMETHOD(device_attach, uplcom_attach),
285184610Salfred	DEVMETHOD(device_detach, uplcom_detach),
286184610Salfred	{0, 0}
287184610Salfred};
288184610Salfred
289184610Salfredstatic devclass_t uplcom_devclass;
290184610Salfred
291184610Salfredstatic driver_t uplcom_driver = {
292184610Salfred	.name = "uplcom",
293184610Salfred	.methods = uplcom_methods,
294184610Salfred	.size = sizeof(struct uplcom_softc),
295184610Salfred};
296184610Salfred
297189275SthompsaDRIVER_MODULE(uplcom, uhub, uplcom_driver, uplcom_devclass, NULL, 0);
298188942SthompsaMODULE_DEPEND(uplcom, ucom, 1, 1, 1);
299188942SthompsaMODULE_DEPEND(uplcom, usb, 1, 1, 1);
300184610SalfredMODULE_VERSION(uplcom, UPLCOM_MODVER);
301184610Salfred
302184610Salfredstatic int
303184610Salfreduplcom_probe(device_t dev)
304184610Salfred{
305192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
306184610Salfred
307184610Salfred	DPRINTFN(11, "\n");
308184610Salfred
309192499Sthompsa	if (uaa->usb_mode != USB_MODE_HOST) {
310184610Salfred		return (ENXIO);
311184610Salfred	}
312184610Salfred	if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) {
313184610Salfred		return (ENXIO);
314184610Salfred	}
315184610Salfred	if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) {
316184610Salfred		return (ENXIO);
317184610Salfred	}
318194228Sthompsa	return (usbd_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
319184610Salfred}
320184610Salfred
321184610Salfredstatic int
322184610Salfreduplcom_attach(device_t dev)
323184610Salfred{
324192984Sthompsa	struct usb_attach_arg *uaa = device_get_ivars(dev);
325184610Salfred	struct uplcom_softc *sc = device_get_softc(dev);
326192984Sthompsa	struct usb_interface *iface;
327192984Sthompsa	struct usb_interface_descriptor *id;
328184610Salfred	int error;
329184610Salfred
330184610Salfred	DPRINTFN(11, "\n");
331184610Salfred
332194228Sthompsa	device_set_usb_desc(dev);
333189265Sthompsa	mtx_init(&sc->sc_mtx, "uplcom", NULL, MTX_DEF);
334184610Salfred
335184610Salfred	DPRINTF("sc = %p\n", sc);
336184610Salfred
337184610Salfred	sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa);
338184610Salfred	sc->sc_udev = uaa->device;
339184610Salfred
340184610Salfred	DPRINTF("chiptype: %s\n",
341184610Salfred	    (sc->sc_chiptype == TYPE_PL2303X) ?
342184610Salfred	    "2303X" : "2303");
343184610Salfred
344184610Salfred	/*
345184610Salfred	 * USB-RSAQ1 has two interface
346184610Salfred	 *
347184610Salfred	 *  USB-RSAQ1       | USB-RSAQ2
348184610Salfred	 * -----------------+-----------------
349184610Salfred	 * Interface 0      |Interface 0
350184610Salfred	 *  Interrupt(0x81) | Interrupt(0x81)
351184610Salfred	 * -----------------+ BulkIN(0x02)
352184610Salfred	 * Interface 1	    | BulkOUT(0x83)
353184610Salfred	 *   BulkIN(0x02)   |
354184610Salfred	 *   BulkOUT(0x83)  |
355184610Salfred	 */
356184610Salfred
357184610Salfred	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
358184610Salfred	sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX;
359184610Salfred
360194228Sthompsa	iface = usbd_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
361184610Salfred	if (iface) {
362194228Sthompsa		id = usbd_get_interface_descriptor(iface);
363184610Salfred		if (id == NULL) {
364184610Salfred			device_printf(dev, "no interface descriptor (2)!\n");
365184610Salfred			goto detach;
366184610Salfred		}
367184610Salfred		sc->sc_data_iface_no = id->bInterfaceNumber;
368184610Salfred		sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX;
369194228Sthompsa		usbd_set_parent_iface(uaa->device,
370184610Salfred		    UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex);
371184610Salfred	} else {
372184610Salfred		sc->sc_data_iface_no = sc->sc_ctrl_iface_no;
373184610Salfred		sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX;
374184610Salfred	}
375184610Salfred
376194228Sthompsa	error = usbd_transfer_setup(uaa->device,
377184610Salfred	    sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
378189265Sthompsa	    UPLCOM_N_TRANSFER, sc, &sc->sc_mtx);
379184610Salfred	if (error) {
380184610Salfred		DPRINTF("one or more missing USB endpoints, "
381194228Sthompsa		    "error=%s\n", usbd_errstr(error));
382184610Salfred		goto detach;
383184610Salfred	}
384184610Salfred	error = uplcom_reset(sc, uaa->device);
385184610Salfred	if (error) {
386184610Salfred		device_printf(dev, "reset failed, error=%s\n",
387194228Sthompsa		    usbd_errstr(error));
388184610Salfred		goto detach;
389184610Salfred	}
390184610Salfred	/* clear stall at first run */
391189265Sthompsa	mtx_lock(&sc->sc_mtx);
392194228Sthompsa	usbd_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
393194228Sthompsa	usbd_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
394189265Sthompsa	mtx_unlock(&sc->sc_mtx);
395184610Salfred
396194228Sthompsa	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
397189265Sthompsa	    &uplcom_callback, &sc->sc_mtx);
398184610Salfred	if (error) {
399184610Salfred		goto detach;
400184610Salfred	}
401184610Salfred	/*
402184610Salfred	 * do the initialization during attach so that the system does not
403184610Salfred	 * sleep during open:
404184610Salfred	 */
405184610Salfred	if (sc->sc_chiptype == TYPE_PL2303X) {
406184610Salfred		if (uplcom_pl2303x_init(uaa->device)) {
407184610Salfred			device_printf(dev, "init failed!\n");
408184610Salfred			goto detach;
409184610Salfred		}
410184610Salfred	}
411184610Salfred	return (0);
412184610Salfred
413184610Salfreddetach:
414184610Salfred	uplcom_detach(dev);
415184610Salfred	return (ENXIO);
416184610Salfred}
417184610Salfred
418184610Salfredstatic int
419184610Salfreduplcom_detach(device_t dev)
420184610Salfred{
421184610Salfred	struct uplcom_softc *sc = device_get_softc(dev);
422184610Salfred
423184610Salfred	DPRINTF("sc=%p\n", sc);
424184610Salfred
425194228Sthompsa	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
426194228Sthompsa	usbd_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
427189265Sthompsa	mtx_destroy(&sc->sc_mtx);
428184610Salfred
429184610Salfred	return (0);
430184610Salfred}
431184610Salfred
432193045Sthompsastatic usb_error_t
433192984Sthompsauplcom_reset(struct uplcom_softc *sc, struct usb_device *udev)
434184610Salfred{
435192984Sthompsa	struct usb_device_request req;
436184610Salfred
437184610Salfred	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
438184610Salfred	req.bRequest = UPLCOM_SET_REQUEST;
439184610Salfred	USETW(req.wValue, 0);
440184610Salfred	req.wIndex[0] = sc->sc_data_iface_no;
441184610Salfred	req.wIndex[1] = 0;
442184610Salfred	USETW(req.wLength, 0);
443184610Salfred
444194228Sthompsa	return (usbd_do_request(udev, NULL, &req, NULL));
445184610Salfred}
446184610Salfred
447184610Salfredstruct pl2303x_init {
448184610Salfred	uint8_t	req_type;
449184610Salfred	uint8_t	request;
450184610Salfred	uint16_t value;
451184610Salfred	uint16_t index;
452184610Salfred	uint16_t length;
453184610Salfred};
454184610Salfred
455184610Salfredstatic const struct pl2303x_init pl2303x[] = {
456184610Salfred	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
457184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0},
458184610Salfred	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
459184610Salfred	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
460184610Salfred	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
461184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0},
462184610Salfred	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
463184610Salfred	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
464184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0},
465184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0},
466184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0},
467184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0},
468184610Salfred	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0},
469184610Salfred};
470184610Salfred
471184610Salfred#define	N_PL2302X_INIT	(sizeof(pl2303x)/sizeof(pl2303x[0]))
472184610Salfred
473184610Salfredstatic int
474192984Sthompsauplcom_pl2303x_init(struct usb_device *udev)
475184610Salfred{
476192984Sthompsa	struct usb_device_request req;
477193045Sthompsa	usb_error_t err;
478184610Salfred	uint8_t buf[4];
479184610Salfred	uint8_t i;
480184610Salfred
481184610Salfred	for (i = 0; i != N_PL2302X_INIT; i++) {
482184610Salfred		req.bmRequestType = pl2303x[i].req_type;
483184610Salfred		req.bRequest = pl2303x[i].request;
484184610Salfred		USETW(req.wValue, pl2303x[i].value);
485184610Salfred		USETW(req.wIndex, pl2303x[i].index);
486184610Salfred		USETW(req.wLength, pl2303x[i].length);
487184610Salfred
488194228Sthompsa		err = usbd_do_request(udev, NULL, &req, buf);
489184610Salfred		if (err) {
490194228Sthompsa			DPRINTF("error=%s\n", usbd_errstr(err));
491184610Salfred			return (EIO);
492184610Salfred		}
493184610Salfred	}
494184610Salfred	return (0);
495184610Salfred}
496184610Salfred
497184610Salfredstatic void
498192984Sthompsauplcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
499184610Salfred{
500184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
501192984Sthompsa	struct usb_device_request req;
502184610Salfred
503184610Salfred	DPRINTF("onoff = %d\n", onoff);
504184610Salfred
505184610Salfred	if (onoff)
506184610Salfred		sc->sc_line |= UCDC_LINE_DTR;
507184610Salfred	else
508184610Salfred		sc->sc_line &= ~UCDC_LINE_DTR;
509184610Salfred
510184610Salfred	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
511184610Salfred	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
512184610Salfred	USETW(req.wValue, sc->sc_line);
513184610Salfred	req.wIndex[0] = sc->sc_data_iface_no;
514184610Salfred	req.wIndex[1] = 0;
515184610Salfred	USETW(req.wLength, 0);
516184610Salfred
517194228Sthompsa	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
518188413Sthompsa	    &req, NULL, 0, 1000);
519184610Salfred}
520184610Salfred
521184610Salfredstatic void
522192984Sthompsauplcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
523184610Salfred{
524184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
525192984Sthompsa	struct usb_device_request req;
526184610Salfred
527184610Salfred	DPRINTF("onoff = %d\n", onoff);
528184610Salfred
529184610Salfred	if (onoff)
530184610Salfred		sc->sc_line |= UCDC_LINE_RTS;
531184610Salfred	else
532184610Salfred		sc->sc_line &= ~UCDC_LINE_RTS;
533184610Salfred
534184610Salfred	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
535184610Salfred	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
536184610Salfred	USETW(req.wValue, sc->sc_line);
537184610Salfred	req.wIndex[0] = sc->sc_data_iface_no;
538184610Salfred	req.wIndex[1] = 0;
539184610Salfred	USETW(req.wLength, 0);
540184610Salfred
541194228Sthompsa	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
542188413Sthompsa	    &req, NULL, 0, 1000);
543184610Salfred}
544184610Salfred
545184610Salfredstatic void
546192984Sthompsauplcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
547184610Salfred{
548184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
549192984Sthompsa	struct usb_device_request req;
550184610Salfred	uint16_t temp;
551184610Salfred
552184610Salfred	DPRINTF("onoff = %d\n", onoff);
553184610Salfred
554184610Salfred	temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
555184610Salfred
556184610Salfred	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
557184610Salfred	req.bRequest = UCDC_SEND_BREAK;
558184610Salfred	USETW(req.wValue, temp);
559184610Salfred	req.wIndex[0] = sc->sc_data_iface_no;
560184610Salfred	req.wIndex[1] = 0;
561184610Salfred	USETW(req.wLength, 0);
562184610Salfred
563194228Sthompsa	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
564188413Sthompsa	    &req, NULL, 0, 1000);
565184610Salfred}
566184610Salfred
567184610Salfredstatic const int32_t uplcom_rates[] = {
568184610Salfred	75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
569184610Salfred	19200, 28800, 38400, 57600, 115200,
570184610Salfred	/*
571184610Salfred	 * Higher speeds are probably possible. PL2303X supports up to
572184610Salfred	 * 6Mb and can set any rate
573184610Salfred	 */
574184610Salfred	230400, 460800, 614400, 921600, 1228800
575184610Salfred};
576184610Salfred
577184610Salfred#define	N_UPLCOM_RATES	(sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
578184610Salfred
579184610Salfredstatic int
580192984Sthompsauplcom_pre_param(struct ucom_softc *ucom, struct termios *t)
581184610Salfred{
582184610Salfred	uint8_t i;
583184610Salfred
584184610Salfred	DPRINTF("\n");
585184610Salfred
586184610Salfred	/* check requested baud rate */
587184610Salfred
588184610Salfred	for (i = 0;; i++) {
589184610Salfred
590184610Salfred		if (i != N_UPLCOM_RATES) {
591184610Salfred			if (uplcom_rates[i] == t->c_ospeed) {
592184610Salfred				break;
593184610Salfred			}
594184610Salfred		} else {
595184610Salfred			DPRINTF("invalid baud rate (%d)\n", t->c_ospeed);
596184610Salfred			return (EIO);
597184610Salfred		}
598184610Salfred	}
599184610Salfred
600184610Salfred	return (0);
601184610Salfred}
602184610Salfred
603184610Salfredstatic void
604192984Sthompsauplcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
605184610Salfred{
606184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
607192984Sthompsa	struct usb_cdc_line_state ls;
608192984Sthompsa	struct usb_device_request req;
609184610Salfred
610184610Salfred	DPRINTF("sc = %p\n", sc);
611184610Salfred
612184610Salfred	bzero(&ls, sizeof(ls));
613184610Salfred
614184610Salfred	USETDW(ls.dwDTERate, t->c_ospeed);
615184610Salfred
616184610Salfred	if (t->c_cflag & CSTOPB) {
617184610Salfred		ls.bCharFormat = UCDC_STOP_BIT_2;
618184610Salfred	} else {
619184610Salfred		ls.bCharFormat = UCDC_STOP_BIT_1;
620184610Salfred	}
621184610Salfred
622184610Salfred	if (t->c_cflag & PARENB) {
623184610Salfred		if (t->c_cflag & PARODD) {
624184610Salfred			ls.bParityType = UCDC_PARITY_ODD;
625184610Salfred		} else {
626184610Salfred			ls.bParityType = UCDC_PARITY_EVEN;
627184610Salfred		}
628184610Salfred	} else {
629184610Salfred		ls.bParityType = UCDC_PARITY_NONE;
630184610Salfred	}
631184610Salfred
632184610Salfred	switch (t->c_cflag & CSIZE) {
633184610Salfred	case CS5:
634184610Salfred		ls.bDataBits = 5;
635184610Salfred		break;
636184610Salfred	case CS6:
637184610Salfred		ls.bDataBits = 6;
638184610Salfred		break;
639184610Salfred	case CS7:
640184610Salfred		ls.bDataBits = 7;
641184610Salfred		break;
642184610Salfred	case CS8:
643184610Salfred		ls.bDataBits = 8;
644184610Salfred		break;
645184610Salfred	}
646184610Salfred
647184610Salfred	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
648184610Salfred	    UGETDW(ls.dwDTERate), ls.bCharFormat,
649184610Salfred	    ls.bParityType, ls.bDataBits);
650184610Salfred
651184610Salfred	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
652184610Salfred	req.bRequest = UCDC_SET_LINE_CODING;
653184610Salfred	USETW(req.wValue, 0);
654184610Salfred	req.wIndex[0] = sc->sc_data_iface_no;
655184610Salfred	req.wIndex[1] = 0;
656184610Salfred	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
657184610Salfred
658194228Sthompsa	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
659188413Sthompsa	    &req, &ls, 0, 1000);
660184610Salfred
661184610Salfred	if (t->c_cflag & CRTSCTS) {
662184610Salfred
663184610Salfred		DPRINTF("crtscts = on\n");
664184610Salfred
665184610Salfred		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
666184610Salfred		req.bRequest = UPLCOM_SET_REQUEST;
667184610Salfred		USETW(req.wValue, 0);
668184610Salfred		if (sc->sc_chiptype == TYPE_PL2303X)
669184610Salfred			USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
670184610Salfred		else
671184610Salfred			USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
672184610Salfred		USETW(req.wLength, 0);
673184610Salfred
674194228Sthompsa		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
675188413Sthompsa		    &req, NULL, 0, 1000);
676184610Salfred	} else {
677184610Salfred		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
678184610Salfred		req.bRequest = UPLCOM_SET_REQUEST;
679184610Salfred		USETW(req.wValue, 0);
680184610Salfred		USETW(req.wIndex, 0);
681184610Salfred		USETW(req.wLength, 0);
682194228Sthompsa		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
683188413Sthompsa		    &req, NULL, 0, 1000);
684184610Salfred	}
685184610Salfred}
686184610Salfred
687184610Salfredstatic void
688192984Sthompsauplcom_start_read(struct ucom_softc *ucom)
689184610Salfred{
690184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
691184610Salfred
692184610Salfred	/* start interrupt endpoint */
693194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
694184610Salfred
695184610Salfred	/* start read endpoint */
696194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
697184610Salfred}
698184610Salfred
699184610Salfredstatic void
700192984Sthompsauplcom_stop_read(struct ucom_softc *ucom)
701184610Salfred{
702184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
703184610Salfred
704184610Salfred	/* stop interrupt endpoint */
705194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
706184610Salfred
707184610Salfred	/* stop read endpoint */
708194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
709184610Salfred}
710184610Salfred
711184610Salfredstatic void
712192984Sthompsauplcom_start_write(struct ucom_softc *ucom)
713184610Salfred{
714184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
715184610Salfred
716194228Sthompsa	usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
717184610Salfred}
718184610Salfred
719184610Salfredstatic void
720192984Sthompsauplcom_stop_write(struct ucom_softc *ucom)
721184610Salfred{
722184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
723184610Salfred
724194228Sthompsa	usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
725184610Salfred}
726184610Salfred
727184610Salfredstatic void
728192984Sthompsauplcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
729184610Salfred{
730184610Salfred	struct uplcom_softc *sc = ucom->sc_parent;
731184610Salfred
732184610Salfred	DPRINTF("\n");
733184610Salfred
734184610Salfred	*lsr = sc->sc_lsr;
735184610Salfred	*msr = sc->sc_msr;
736184610Salfred}
737184610Salfred
738184610Salfredstatic void
739192984Sthompsauplcom_intr_callback(struct usb_xfer *xfer)
740184610Salfred{
741184610Salfred	struct uplcom_softc *sc = xfer->priv_sc;
742184610Salfred	uint8_t buf[9];
743184610Salfred
744184610Salfred	switch (USB_GET_STATE(xfer)) {
745184610Salfred	case USB_ST_TRANSFERRED:
746184610Salfred
747184610Salfred		DPRINTF("actlen = %u\n", xfer->actlen);
748184610Salfred
749184610Salfred		if (xfer->actlen >= 9) {
750184610Salfred
751194228Sthompsa			usbd_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
752184610Salfred
753184610Salfred			DPRINTF("status = 0x%02x\n", buf[8]);
754184610Salfred
755184610Salfred			sc->sc_lsr = 0;
756184610Salfred			sc->sc_msr = 0;
757184610Salfred
758184610Salfred			if (buf[8] & RSAQ_STATUS_CTS) {
759184610Salfred				sc->sc_msr |= SER_CTS;
760184610Salfred			}
761184610Salfred			if (buf[8] & RSAQ_STATUS_DSR) {
762184610Salfred				sc->sc_msr |= SER_DSR;
763184610Salfred			}
764184610Salfred			if (buf[8] & RSAQ_STATUS_DCD) {
765184610Salfred				sc->sc_msr |= SER_DCD;
766184610Salfred			}
767194228Sthompsa			ucom_status_change(&sc->sc_ucom);
768184610Salfred		}
769184610Salfred	case USB_ST_SETUP:
770188413Sthompsatr_setup:
771188413Sthompsa		xfer->frlengths[0] = xfer->max_data_length;
772194228Sthompsa		usbd_transfer_submit(xfer);
773184610Salfred		return;
774184610Salfred
775184610Salfred	default:			/* Error */
776184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
777188413Sthompsa			/* try to clear stall first */
778188413Sthompsa			xfer->flags.stall_pipe = 1;
779188413Sthompsa			goto tr_setup;
780184610Salfred		}
781184610Salfred		return;
782184610Salfred	}
783184610Salfred}
784184610Salfred
785184610Salfredstatic void
786192984Sthompsauplcom_write_callback(struct usb_xfer *xfer)
787184610Salfred{
788184610Salfred	struct uplcom_softc *sc = xfer->priv_sc;
789184610Salfred	uint32_t actlen;
790184610Salfred
791184610Salfred	switch (USB_GET_STATE(xfer)) {
792184610Salfred	case USB_ST_SETUP:
793184610Salfred	case USB_ST_TRANSFERRED:
794188413Sthompsatr_setup:
795194228Sthompsa		if (ucom_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
796184610Salfred		    UPLCOM_BULK_BUF_SIZE, &actlen)) {
797184610Salfred
798184610Salfred			DPRINTF("actlen = %d\n", actlen);
799184610Salfred
800184610Salfred			xfer->frlengths[0] = actlen;
801194228Sthompsa			usbd_transfer_submit(xfer);
802184610Salfred		}
803184610Salfred		return;
804184610Salfred
805184610Salfred	default:			/* Error */
806184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
807188413Sthompsa			/* try to clear stall first */
808188413Sthompsa			xfer->flags.stall_pipe = 1;
809188413Sthompsa			goto tr_setup;
810184610Salfred		}
811184610Salfred		return;
812184610Salfred	}
813184610Salfred}
814184610Salfred
815184610Salfredstatic void
816192984Sthompsauplcom_read_callback(struct usb_xfer *xfer)
817184610Salfred{
818184610Salfred	struct uplcom_softc *sc = xfer->priv_sc;
819184610Salfred
820184610Salfred	switch (USB_GET_STATE(xfer)) {
821184610Salfred	case USB_ST_TRANSFERRED:
822194228Sthompsa		ucom_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
823184610Salfred
824184610Salfred	case USB_ST_SETUP:
825188413Sthompsatr_setup:
826188413Sthompsa		xfer->frlengths[0] = xfer->max_data_length;
827194228Sthompsa		usbd_transfer_submit(xfer);
828184610Salfred		return;
829184610Salfred
830184610Salfred	default:			/* Error */
831184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
832188413Sthompsa			/* try to clear stall first */
833188413Sthompsa			xfer->flags.stall_pipe = 1;
834188413Sthompsa			goto tr_setup;
835184610Salfred		}
836184610Salfred		return;
837184610Salfred	}
838184610Salfred}
839