uplcom.c revision 188942
1132718Skan/*	$NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $	*/
2132718Skan
3169689Skan#include <sys/cdefs.h>
4132718Skan__FBSDID("$FreeBSD: head/sys/dev/usb/serial/uplcom.c 188942 2009-02-23 18:31:00Z thompsa $");
5132718Skan
6132718Skan/*-
7132718Skan * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
8132718Skan * All rights reserved.
9132718Skan *
10132718Skan * Redistribution and use in source and binary forms, with or without
11132718Skan * modification, are permitted provided that the following conditions
12132718Skan * are met:
13132718Skan * 1. Redistributions of source code must retain the above copyright
14132718Skan *    notice, this list of conditions and the following disclaimer.
15132718Skan * 2. Redistributions in binary form must reproduce the above copyright
16132718Skan *    notice, this list of conditions and the following disclaimer in the
17132718Skan *    documentation and/or other materials provided with the distribution.
18132718Skan *
19132718Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20132718Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21132718Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22132718Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25132718Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26132718Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27132718Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28132718Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29132718Skan * SUCH DAMAGE.
30132718Skan */
31132718Skan
32132718Skan/*-
33132718Skan * Copyright (c) 2001 The NetBSD Foundation, Inc.
34132718Skan * All rights reserved.
35132718Skan *
36132718Skan * This code is derived from software contributed to The NetBSD Foundation
37132718Skan * by Ichiro FUKUHARA (ichiro@ichiro.org).
38132718Skan *
39132718Skan * Redistribution and use in source and binary forms, with or without
40132718Skan * modification, are permitted provided that the following conditions
41132718Skan * are met:
42132718Skan * 1. Redistributions of source code must retain the above copyright
43132718Skan *    notice, this list of conditions and the following disclaimer.
44132718Skan * 2. Redistributions in binary form must reproduce the above copyright
45169689Skan *    notice, this list of conditions and the following disclaimer in the
46169689Skan *    documentation and/or other materials provided with the distribution.
47132718Skan * 3. All advertising materials mentioning features or use of this software
48132718Skan *    must display the following acknowledgement:
49132718Skan *        This product includes software developed by the NetBSD
50132718Skan *        Foundation, Inc. and its contributors.
51132718Skan * 4. Neither the name of The NetBSD Foundation nor the names of its
52132718Skan *    contributors may be used to endorse or promote products derived
53132718Skan *    from this software without specific prior written permission.
54132718Skan *
55132718Skan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
56132718Skan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57132718Skan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58132718Skan * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
59132718Skan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60132718Skan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61132718Skan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62132718Skan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63132718Skan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64132718Skan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65132718Skan * POSSIBILITY OF SUCH DAMAGE.
66132718Skan */
67132718Skan
68132718Skan/*
69132718Skan * This driver supports several USB-to-RS232 serial adapters driven by
70132718Skan * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
71132718Skan * bridge chip.  The adapters are sold under many different brand
72132718Skan * names.
73132718Skan *
74132718Skan * Datasheets are available at Prolific www site at
75132718Skan * http://www.prolific.com.tw.  The datasheets don't contain full
76132718Skan * programming information for the chip.
77132718Skan *
78132718Skan * PL-2303HX is probably programmed the same as PL-2303X.
79132718Skan *
80132718Skan * There are several differences between PL-2303 and PL-2303(H)X.
81132718Skan * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
82132718Skan * different command for controlling CRTSCTS and needs special
83132718Skan * sequence of commands for initialization which aren't also
84132718Skan * documented in the datasheet.
85132718Skan */
86132718Skan
87132718Skan#include "usbdevs.h"
88132718Skan#include <dev/usb/usb.h>
89132718Skan#include <dev/usb/usb_mfunc.h>
90132718Skan#include <dev/usb/usb_error.h>
91132718Skan#include <dev/usb/usb_cdc.h>
92132718Skan
93132718Skan#define	USB_DEBUG_VAR uplcom_debug
94132718Skan
95132718Skan#include <dev/usb/usb_core.h>
96132718Skan#include <dev/usb/usb_debug.h>
97132718Skan#include <dev/usb/usb_process.h>
98132718Skan#include <dev/usb/usb_request.h>
99169689Skan#include <dev/usb/usb_lookup.h>
100169689Skan#include <dev/usb/usb_util.h>
101169689Skan#include <dev/usb/usb_busdma.h>
102169689Skan
103169689Skan#include <dev/usb/serial/usb_serial.h>
104132718Skan
105132718Skan#if USB_DEBUG
106132718Skanstatic int uplcom_debug = 0;
107132718Skan
108132718SkanSYSCTL_NODE(_hw_usb2, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
109132718SkanSYSCTL_INT(_hw_usb2_uplcom, OID_AUTO, debug, CTLFLAG_RW,
110132718Skan    &uplcom_debug, 0, "Debug level");
111132718Skan#endif
112132718Skan
113132718Skan#define	UPLCOM_MODVER			1	/* module version */
114132718Skan
115132718Skan#define	UPLCOM_CONFIG_INDEX		0
116132718Skan#define	UPLCOM_IFACE_INDEX		0
117132718Skan#define	UPLCOM_SECOND_IFACE_INDEX	1
118132718Skan
119132718Skan#ifndef UPLCOM_INTR_INTERVAL
120132718Skan#define	UPLCOM_INTR_INTERVAL		0	/* default */
121132718Skan#endif
122132718Skan
123169689Skan#define	UPLCOM_BULK_BUF_SIZE 1024	/* bytes */
124169689Skan
125132718Skan#define	UPLCOM_SET_REQUEST		0x01
126169689Skan#define	UPLCOM_SET_CRTSCTS		0x41
127169689Skan#define	UPLCOM_SET_CRTSCTS_PL2303X	0x61
128169689Skan#define	RSAQ_STATUS_CTS			0x80
129169689Skan#define	RSAQ_STATUS_DSR			0x02
130169689Skan#define	RSAQ_STATUS_DCD			0x01
131169689Skan
132169689Skan#define	TYPE_PL2303			0
133169689Skan#define	TYPE_PL2303X			1
134169689Skan
135169689Skanenum {
136169689Skan	UPLCOM_BULK_DT_WR,
137169689Skan	UPLCOM_BULK_DT_RD,
138169689Skan	UPLCOM_INTR_DT_RD,
139132718Skan	UPLCOM_N_TRANSFER,
140132718Skan};
141132718Skan
142132718Skanstruct uplcom_softc {
143132718Skan	struct usb2_com_super_softc sc_super_ucom;
144132718Skan	struct usb2_com_softc sc_ucom;
145132718Skan
146132718Skan	struct usb2_xfer *sc_xfer[UPLCOM_N_TRANSFER];
147132718Skan	struct usb2_device *sc_udev;
148132718Skan
149132718Skan	uint16_t sc_line;
150132718Skan
151132718Skan	uint8_t	sc_lsr;			/* local status register */
152132718Skan	uint8_t	sc_msr;			/* uplcom status register */
153132718Skan	uint8_t	sc_chiptype;		/* type of chip */
154132718Skan	uint8_t	sc_ctrl_iface_no;
155132718Skan	uint8_t	sc_data_iface_no;
156132718Skan	uint8_t	sc_iface_index[2];
157132718Skan};
158132718Skan
159132718Skan/* prototypes */
160132718Skan
161132718Skanstatic usb2_error_t uplcom_reset(struct uplcom_softc *, struct usb2_device *);
162132718Skanstatic int	uplcom_pl2303x_init(struct usb2_device *);
163132718Skanstatic void	uplcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
164132718Skanstatic void	uplcom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
165132718Skanstatic void	uplcom_cfg_set_break(struct usb2_com_softc *, uint8_t);
166132718Skanstatic int	uplcom_pre_param(struct usb2_com_softc *, struct termios *);
167132718Skanstatic void	uplcom_cfg_param(struct usb2_com_softc *, struct termios *);
168132718Skanstatic void	uplcom_start_read(struct usb2_com_softc *);
169132718Skanstatic void	uplcom_stop_read(struct usb2_com_softc *);
170132718Skanstatic void	uplcom_start_write(struct usb2_com_softc *);
171132718Skanstatic void	uplcom_stop_write(struct usb2_com_softc *);
172132718Skanstatic void	uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
173132718Skan		    uint8_t *);
174132718Skan
175132718Skanstatic device_probe_t uplcom_probe;
176132718Skanstatic device_attach_t uplcom_attach;
177132718Skanstatic device_detach_t uplcom_detach;
178132718Skan
179132718Skanstatic usb2_callback_t uplcom_intr_callback;
180132718Skanstatic usb2_callback_t uplcom_write_callback;
181132718Skanstatic usb2_callback_t uplcom_read_callback;
182132718Skan
183169689Skanstatic const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
184132718Skan
185132718Skan	[UPLCOM_BULK_DT_WR] = {
186132718Skan		.type = UE_BULK,
187132718Skan		.endpoint = UE_ADDR_ANY,
188132718Skan		.direction = UE_DIR_OUT,
189132718Skan		.mh.bufsize = UPLCOM_BULK_BUF_SIZE,
190132718Skan		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
191132718Skan		.mh.callback = &uplcom_write_callback,
192132718Skan		.if_index = 0,
193132718Skan	},
194169689Skan
195161651Skan	[UPLCOM_BULK_DT_RD] = {
196132718Skan		.type = UE_BULK,
197132718Skan		.endpoint = UE_ADDR_ANY,
198132718Skan		.direction = UE_DIR_IN,
199132718Skan		.mh.bufsize = UPLCOM_BULK_BUF_SIZE,
200132718Skan		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
201132718Skan		.mh.callback = &uplcom_read_callback,
202132718Skan		.if_index = 0,
203132718Skan	},
204132718Skan
205132718Skan	[UPLCOM_INTR_DT_RD] = {
206132718Skan		.type = UE_INTERRUPT,
207132718Skan		.endpoint = UE_ADDR_ANY,
208132718Skan		.direction = UE_DIR_IN,
209132718Skan		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
210132718Skan		.mh.bufsize = 0,	/* use wMaxPacketSize */
211132718Skan		.mh.callback = &uplcom_intr_callback,
212132718Skan		.if_index = 1,
213132718Skan	},
214132718Skan};
215132718Skan
216132718Skanstruct usb2_com_callback uplcom_callback = {
217132718Skan	.usb2_com_cfg_get_status = &uplcom_cfg_get_status,
218132718Skan	.usb2_com_cfg_set_dtr = &uplcom_cfg_set_dtr,
219132718Skan	.usb2_com_cfg_set_rts = &uplcom_cfg_set_rts,
220132718Skan	.usb2_com_cfg_set_break = &uplcom_cfg_set_break,
221132718Skan	.usb2_com_cfg_param = &uplcom_cfg_param,
222132718Skan	.usb2_com_pre_param = &uplcom_pre_param,
223132718Skan	.usb2_com_start_read = &uplcom_start_read,
224132718Skan	.usb2_com_stop_read = &uplcom_stop_read,
225132718Skan	.usb2_com_start_write = &uplcom_start_write,
226132718Skan	.usb2_com_stop_write = &uplcom_stop_write,
227132718Skan};
228132718Skan
229132718Skan#define	USB_UPL(v,p,rl,rh,t)				\
230132718Skan  USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl),	\
231132718Skan  USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t)
232132718Skan
233132718Skanstatic const struct usb2_device_id uplcom_devs[] = {
234132718Skan	/* Belkin F5U257 */
235132718Skan	{USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)},
236132718Skan	/* I/O DATA USB-RSAQ */
237132718Skan	{USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)},
238132718Skan	/* I/O DATA USB-RSAQ2 */
239132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)},
240132718Skan	/* I/O DATA USB-RSAQ3 */
241132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)},
242132718Skan	/* PLANEX USB-RS232 URS-03 */
243132718Skan	{USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)},
244132718Skan	/* TrendNet TU-S9 */
245132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)},
246132718Skan	/* ST Lab USB-SERIAL-4 */
247132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)},
248132718Skan	/* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
249132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)},
250132718Skan	/* TDK USB-PHS Adapter UHA6400 */
251132718Skan	{USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)},
252132718Skan	/* RATOC REX-USB60 */
253132718Skan	{USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)},
254132718Skan	/* ELECOM UC-SGT */
255132718Skan	{USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)},
256132718Skan	{USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)},
257132718Skan	/* Sagem USB-Serial Controller */
258132718Skan	{USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)},
259132718Skan	/* Sony Ericsson USB Cable */
260132718Skan	{USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)},
261132718Skan	/* SOURCENEXT KeikaiDenwa 8 */
262132718Skan	{USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)},
263132718Skan	/* SOURCENEXT KeikaiDenwa 8 with charger */
264132718Skan	{USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)},
265169689Skan	/* HAL Corporation Crossam2+USB */
266132718Skan	{USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)},
267132718Skan	/* Sitecom USB to Serial */
268132718Skan	{USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)},
269132718Skan	/* Tripp-Lite U209-000-R */
270169689Skan	{USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)},
271132718Skan	{USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)},
272132718Skan	/* Prolific Pharos */
273132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)},
274169689Skan	/* Willcom W-SIM */
275132718Skan	{USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)},
276132718Skan};
277132718Skan
278132718Skanstatic device_method_t uplcom_methods[] = {
279132718Skan	DEVMETHOD(device_probe, uplcom_probe),
280132718Skan	DEVMETHOD(device_attach, uplcom_attach),
281132718Skan	DEVMETHOD(device_detach, uplcom_detach),
282169689Skan	{0, 0}
283132718Skan};
284132718Skan
285132718Skanstatic devclass_t uplcom_devclass;
286132718Skan
287132718Skanstatic driver_t uplcom_driver = {
288132718Skan	.name = "uplcom",
289132718Skan	.methods = uplcom_methods,
290132718Skan	.size = sizeof(struct uplcom_softc),
291132718Skan};
292132718Skan
293132718SkanDRIVER_MODULE(uplcom, ushub, uplcom_driver, uplcom_devclass, NULL, 0);
294132718SkanMODULE_DEPEND(uplcom, ucom, 1, 1, 1);
295132718SkanMODULE_DEPEND(uplcom, usb, 1, 1, 1);
296132718SkanMODULE_VERSION(uplcom, UPLCOM_MODVER);
297132718Skan
298132718Skanstatic int
299132718Skanuplcom_probe(device_t dev)
300132718Skan{
301132718Skan	struct usb2_attach_arg *uaa = device_get_ivars(dev);
302132718Skan
303132718Skan	DPRINTFN(11, "\n");
304132718Skan
305132718Skan	if (uaa->usb2_mode != USB_MODE_HOST) {
306132718Skan		return (ENXIO);
307132718Skan	}
308132718Skan	if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) {
309132718Skan		return (ENXIO);
310169689Skan	}
311169689Skan	if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) {
312169689Skan		return (ENXIO);
313169689Skan	}
314169689Skan	return (usb2_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
315169689Skan}
316132718Skan
317132718Skanstatic int
318132718Skanuplcom_attach(device_t dev)
319132718Skan{
320132718Skan	struct usb2_attach_arg *uaa = device_get_ivars(dev);
321132718Skan	struct uplcom_softc *sc = device_get_softc(dev);
322132718Skan	struct usb2_interface *iface;
323132718Skan	struct usb2_interface_descriptor *id;
324132718Skan	int error;
325132718Skan
326132718Skan	DPRINTFN(11, "\n");
327132718Skan
328132718Skan	device_set_usb2_desc(dev);
329132718Skan
330132718Skan	DPRINTF("sc = %p\n", sc);
331132718Skan
332132718Skan	sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa);
333132718Skan	sc->sc_udev = uaa->device;
334132718Skan
335132718Skan	DPRINTF("chiptype: %s\n",
336169689Skan	    (sc->sc_chiptype == TYPE_PL2303X) ?
337169689Skan	    "2303X" : "2303");
338169689Skan
339132718Skan	/*
340132718Skan	 * USB-RSAQ1 has two interface
341132718Skan	 *
342132718Skan	 *  USB-RSAQ1       | USB-RSAQ2
343132718Skan	 * -----------------+-----------------
344132718Skan	 * Interface 0      |Interface 0
345132718Skan	 *  Interrupt(0x81) | Interrupt(0x81)
346132718Skan	 * -----------------+ BulkIN(0x02)
347132718Skan	 * Interface 1	    | BulkOUT(0x83)
348169689Skan	 *   BulkIN(0x02)   |
349132718Skan	 *   BulkOUT(0x83)  |
350132718Skan	 */
351132718Skan
352132718Skan	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
353132718Skan	sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX;
354132718Skan
355132718Skan	iface = usb2_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
356169689Skan	if (iface) {
357132718Skan		id = usb2_get_interface_descriptor(iface);
358132718Skan		if (id == NULL) {
359132718Skan			device_printf(dev, "no interface descriptor (2)!\n");
360132718Skan			goto detach;
361132718Skan		}
362132718Skan		sc->sc_data_iface_no = id->bInterfaceNumber;
363132718Skan		sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX;
364169689Skan		usb2_set_parent_iface(uaa->device,
365132718Skan		    UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex);
366132718Skan	} else {
367132718Skan		sc->sc_data_iface_no = sc->sc_ctrl_iface_no;
368132718Skan		sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX;
369132718Skan	}
370132718Skan
371132718Skan	error = usb2_transfer_setup(uaa->device,
372132718Skan	    sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
373132718Skan	    UPLCOM_N_TRANSFER, sc, &Giant);
374132718Skan	if (error) {
375132718Skan		DPRINTF("one or more missing USB endpoints, "
376132718Skan		    "error=%s\n", usb2_errstr(error));
377132718Skan		goto detach;
378132718Skan	}
379132718Skan	error = uplcom_reset(sc, uaa->device);
380132718Skan	if (error) {
381132718Skan		device_printf(dev, "reset failed, error=%s\n",
382132718Skan		    usb2_errstr(error));
383132718Skan		goto detach;
384132718Skan	}
385132718Skan	/* clear stall at first run */
386132718Skan	usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
387132718Skan	usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
388132718Skan
389169689Skan	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
390132718Skan	    &uplcom_callback, &Giant);
391169689Skan	if (error) {
392169689Skan		goto detach;
393169689Skan	}
394132718Skan	/*
395169689Skan	 * do the initialization during attach so that the system does not
396169689Skan	 * sleep during open:
397169689Skan	 */
398169689Skan	if (sc->sc_chiptype == TYPE_PL2303X) {
399169689Skan		if (uplcom_pl2303x_init(uaa->device)) {
400169689Skan			device_printf(dev, "init failed!\n");
401132718Skan			goto detach;
402169689Skan		}
403169689Skan	}
404132718Skan	return (0);
405132718Skan
406132718Skandetach:
407132718Skan	uplcom_detach(dev);
408132718Skan	return (ENXIO);
409132718Skan}
410132718Skan
411169689Skanstatic int
412132718Skanuplcom_detach(device_t dev)
413169689Skan{
414169689Skan	struct uplcom_softc *sc = device_get_softc(dev);
415132718Skan
416169689Skan	DPRINTF("sc=%p\n", sc);
417132718Skan
418169689Skan	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
419132718Skan
420132718Skan	usb2_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
421169689Skan
422169689Skan	return (0);
423169689Skan}
424132718Skan
425132718Skanstatic usb2_error_t
426132718Skanuplcom_reset(struct uplcom_softc *sc, struct usb2_device *udev)
427132718Skan{
428132718Skan	struct usb2_device_request req;
429132718Skan
430132718Skan	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
431132718Skan	req.bRequest = UPLCOM_SET_REQUEST;
432132718Skan	USETW(req.wValue, 0);
433259405Spfg	req.wIndex[0] = sc->sc_data_iface_no;
434132718Skan	req.wIndex[1] = 0;
435132718Skan	USETW(req.wLength, 0);
436132718Skan
437132718Skan	return (usb2_do_request(udev, &Giant, &req, NULL));
438132718Skan}
439259405Spfg
440259405Spfgstruct pl2303x_init {
441259405Spfg	uint8_t	req_type;
442259405Spfg	uint8_t	request;
443132718Skan	uint16_t value;
444259405Spfg	uint16_t index;
445259405Spfg	uint16_t length;
446132718Skan};
447259405Spfg
448259405Spfgstatic const struct pl2303x_init pl2303x[] = {
449259405Spfg	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
450259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0},
451169689Skan	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
452259405Spfg	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
453259405Spfg	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
454259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0},
455259405Spfg	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
456259405Spfg	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
457259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0},
458259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0},
459259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0},
460259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0},
461259405Spfg	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0},
462259405Spfg};
463259405Spfg
464259405Spfg#define	N_PL2302X_INIT	(sizeof(pl2303x)/sizeof(pl2303x[0]))
465259405Spfg
466259405Spfgstatic int
467259405Spfguplcom_pl2303x_init(struct usb2_device *udev)
468259405Spfg{
469259405Spfg	struct usb2_device_request req;
470259405Spfg	usb2_error_t err;
471259405Spfg	uint8_t buf[4];
472259405Spfg	uint8_t i;
473259405Spfg
474259405Spfg	for (i = 0; i != N_PL2302X_INIT; i++) {
475259405Spfg		req.bmRequestType = pl2303x[i].req_type;
476259405Spfg		req.bRequest = pl2303x[i].request;
477259405Spfg		USETW(req.wValue, pl2303x[i].value);
478259405Spfg		USETW(req.wIndex, pl2303x[i].index);
479259405Spfg		USETW(req.wLength, pl2303x[i].length);
480259405Spfg
481259405Spfg		err = usb2_do_request(udev, &Giant, &req, buf);
482259405Spfg		if (err) {
483259405Spfg			DPRINTF("error=%s\n", usb2_errstr(err));
484259405Spfg			return (EIO);
485259405Spfg		}
486259405Spfg	}
487259405Spfg	return (0);
488259405Spfg}
489259405Spfg
490259405Spfgstatic void
491259405Spfguplcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
492259405Spfg{
493259405Spfg	struct uplcom_softc *sc = ucom->sc_parent;
494259405Spfg	struct usb2_device_request req;
495132718Skan
496132718Skan	DPRINTF("onoff = %d\n", onoff);
497259405Spfg
498259405Spfg	if (onoff)
499132718Skan		sc->sc_line |= UCDC_LINE_DTR;
500259405Spfg	else
501132718Skan		sc->sc_line &= ~UCDC_LINE_DTR;
502132718Skan
503132718Skan	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
504132718Skan	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
505132718Skan	USETW(req.wValue, sc->sc_line);
506132718Skan	req.wIndex[0] = sc->sc_data_iface_no;
507132718Skan	req.wIndex[1] = 0;
508132718Skan	USETW(req.wLength, 0);
509132718Skan
510169689Skan	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
511169689Skan	    &req, NULL, 0, 1000);
512169689Skan}
513132718Skan
514169689Skanstatic void
515132718Skanuplcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
516132718Skan{
517132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
518132718Skan	struct usb2_device_request req;
519132718Skan
520132718Skan	DPRINTF("onoff = %d\n", onoff);
521132718Skan
522132718Skan	if (onoff)
523132718Skan		sc->sc_line |= UCDC_LINE_RTS;
524132718Skan	else
525132718Skan		sc->sc_line &= ~UCDC_LINE_RTS;
526132718Skan
527132718Skan	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
528132718Skan	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
529132718Skan	USETW(req.wValue, sc->sc_line);
530132718Skan	req.wIndex[0] = sc->sc_data_iface_no;
531132718Skan	req.wIndex[1] = 0;
532132718Skan	USETW(req.wLength, 0);
533132718Skan
534169689Skan	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
535169689Skan	    &req, NULL, 0, 1000);
536132718Skan}
537132718Skan
538132718Skanstatic void
539132718Skanuplcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
540132718Skan{
541132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
542132718Skan	struct usb2_device_request req;
543132718Skan	uint16_t temp;
544132718Skan
545132718Skan	DPRINTF("onoff = %d\n", onoff);
546132718Skan
547132718Skan	temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
548132718Skan
549132718Skan	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
550132718Skan	req.bRequest = UCDC_SEND_BREAK;
551132718Skan	USETW(req.wValue, temp);
552132718Skan	req.wIndex[0] = sc->sc_data_iface_no;
553132718Skan	req.wIndex[1] = 0;
554132718Skan	USETW(req.wLength, 0);
555132718Skan
556132718Skan	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
557169689Skan	    &req, NULL, 0, 1000);
558169689Skan}
559132718Skan
560132718Skanstatic const int32_t uplcom_rates[] = {
561132718Skan	75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
562132718Skan	19200, 28800, 38400, 57600, 115200,
563132718Skan	/*
564132718Skan	 * Higher speeds are probably possible. PL2303X supports up to
565132718Skan	 * 6Mb and can set any rate
566132718Skan	 */
567132718Skan	230400, 460800, 614400, 921600, 1228800
568132718Skan};
569132718Skan
570132718Skan#define	N_UPLCOM_RATES	(sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
571132718Skan
572132718Skanstatic int
573132718Skanuplcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
574132718Skan{
575132718Skan	uint8_t i;
576169689Skan
577132718Skan	DPRINTF("\n");
578132718Skan
579132718Skan	/* check requested baud rate */
580132718Skan
581132718Skan	for (i = 0;; i++) {
582132718Skan
583132718Skan		if (i != N_UPLCOM_RATES) {
584169689Skan			if (uplcom_rates[i] == t->c_ospeed) {
585132718Skan				break;
586132718Skan			}
587132718Skan		} else {
588132718Skan			DPRINTF("invalid baud rate (%d)\n", t->c_ospeed);
589132718Skan			return (EIO);
590132718Skan		}
591132718Skan	}
592132718Skan
593132718Skan	return (0);
594132718Skan}
595132718Skan
596132718Skanstatic void
597132718Skanuplcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
598132718Skan{
599132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
600132718Skan	struct usb2_cdc_line_state ls;
601132718Skan	struct usb2_device_request req;
602132718Skan
603132718Skan	DPRINTF("sc = %p\n", sc);
604132718Skan
605132718Skan	bzero(&ls, sizeof(ls));
606132718Skan
607132718Skan	USETDW(ls.dwDTERate, t->c_ospeed);
608132718Skan
609169689Skan	if (t->c_cflag & CSTOPB) {
610132718Skan		ls.bCharFormat = UCDC_STOP_BIT_2;
611132718Skan	} else {
612132718Skan		ls.bCharFormat = UCDC_STOP_BIT_1;
613132718Skan	}
614169689Skan
615132718Skan	if (t->c_cflag & PARENB) {
616132718Skan		if (t->c_cflag & PARODD) {
617169689Skan			ls.bParityType = UCDC_PARITY_ODD;
618132718Skan		} else {
619132718Skan			ls.bParityType = UCDC_PARITY_EVEN;
620132718Skan		}
621169689Skan	} else {
622169689Skan		ls.bParityType = UCDC_PARITY_NONE;
623169689Skan	}
624132718Skan
625132718Skan	switch (t->c_cflag & CSIZE) {
626132718Skan	case CS5:
627132718Skan		ls.bDataBits = 5;
628132718Skan		break;
629132718Skan	case CS6:
630132718Skan		ls.bDataBits = 6;
631132718Skan		break;
632132718Skan	case CS7:
633132718Skan		ls.bDataBits = 7;
634132718Skan		break;
635132718Skan	case CS8:
636132718Skan		ls.bDataBits = 8;
637132718Skan		break;
638132718Skan	}
639132718Skan
640132718Skan	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
641132718Skan	    UGETDW(ls.dwDTERate), ls.bCharFormat,
642132718Skan	    ls.bParityType, ls.bDataBits);
643132718Skan
644132718Skan	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
645132718Skan	req.bRequest = UCDC_SET_LINE_CODING;
646132718Skan	USETW(req.wValue, 0);
647132718Skan	req.wIndex[0] = sc->sc_data_iface_no;
648169689Skan	req.wIndex[1] = 0;
649169689Skan	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
650132718Skan
651132718Skan	usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
652132718Skan	    &req, &ls, 0, 1000);
653169689Skan
654169689Skan	if (t->c_cflag & CRTSCTS) {
655132718Skan
656132718Skan		DPRINTF("crtscts = on\n");
657132718Skan
658132718Skan		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
659132718Skan		req.bRequest = UPLCOM_SET_REQUEST;
660132718Skan		USETW(req.wValue, 0);
661169689Skan		if (sc->sc_chiptype == TYPE_PL2303X)
662169689Skan			USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
663132718Skan		else
664132718Skan			USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
665132718Skan		USETW(req.wLength, 0);
666132718Skan
667169689Skan		usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
668169689Skan		    &req, NULL, 0, 1000);
669169689Skan	} else {
670132718Skan		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
671132718Skan		req.bRequest = UPLCOM_SET_REQUEST;
672169689Skan		USETW(req.wValue, 0);
673169689Skan		USETW(req.wIndex, 0);
674132718Skan		USETW(req.wLength, 0);
675132718Skan		usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
676132718Skan		    &req, NULL, 0, 1000);
677132718Skan	}
678132718Skan}
679132718Skan
680132718Skanstatic void
681132718Skanuplcom_start_read(struct usb2_com_softc *ucom)
682132718Skan{
683169689Skan	struct uplcom_softc *sc = ucom->sc_parent;
684132718Skan
685169689Skan	/* start interrupt endpoint */
686132718Skan	usb2_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
687132718Skan
688132718Skan	/* start read endpoint */
689169689Skan	usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
690132718Skan}
691132718Skan
692132718Skanstatic void
693132718Skanuplcom_stop_read(struct usb2_com_softc *ucom)
694132718Skan{
695132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
696132718Skan
697132718Skan	/* stop interrupt endpoint */
698132718Skan	usb2_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
699132718Skan
700132718Skan	/* stop read endpoint */
701169689Skan	usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
702132718Skan}
703132718Skan
704132718Skanstatic void
705132718Skanuplcom_start_write(struct usb2_com_softc *ucom)
706132718Skan{
707132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
708132718Skan
709132718Skan	usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
710132718Skan}
711132718Skan
712132718Skanstatic void
713132718Skanuplcom_stop_write(struct usb2_com_softc *ucom)
714132718Skan{
715132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
716132718Skan
717132718Skan	usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
718132718Skan}
719132718Skan
720132718Skanstatic void
721132718Skanuplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
722132718Skan{
723132718Skan	struct uplcom_softc *sc = ucom->sc_parent;
724132718Skan
725132718Skan	DPRINTF("\n");
726169689Skan
727169689Skan	*lsr = sc->sc_lsr;
728132718Skan	*msr = sc->sc_msr;
729132718Skan}
730132718Skan
731132718Skanstatic void
732132718Skanuplcom_intr_callback(struct usb2_xfer *xfer)
733169689Skan{
734132718Skan	struct uplcom_softc *sc = xfer->priv_sc;
735169689Skan	uint8_t buf[9];
736169689Skan
737169689Skan	switch (USB_GET_STATE(xfer)) {
738132718Skan	case USB_ST_TRANSFERRED:
739132718Skan
740132718Skan		DPRINTF("actlen = %u\n", xfer->actlen);
741169689Skan
742169689Skan		if (xfer->actlen >= 9) {
743169689Skan
744169689Skan			usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
745132718Skan
746132718Skan			DPRINTF("status = 0x%02x\n", buf[8]);
747169689Skan
748169689Skan			sc->sc_lsr = 0;
749132718Skan			sc->sc_msr = 0;
750132718Skan
751132718Skan			if (buf[8] & RSAQ_STATUS_CTS) {
752132718Skan				sc->sc_msr |= SER_CTS;
753132718Skan			}
754132718Skan			if (buf[8] & RSAQ_STATUS_DSR) {
755132718Skan				sc->sc_msr |= SER_DSR;
756132718Skan			}
757132718Skan			if (buf[8] & RSAQ_STATUS_DCD) {
758132718Skan				sc->sc_msr |= SER_DCD;
759132718Skan			}
760132718Skan			usb2_com_status_change(&sc->sc_ucom);
761132718Skan		}
762132718Skan	case USB_ST_SETUP:
763132718Skantr_setup:
764132718Skan		xfer->frlengths[0] = xfer->max_data_length;
765132718Skan		usb2_start_hardware(xfer);
766169689Skan		return;
767169689Skan
768132718Skan	default:			/* Error */
769132718Skan		if (xfer->error != USB_ERR_CANCELLED) {
770132718Skan			/* try to clear stall first */
771132718Skan			xfer->flags.stall_pipe = 1;
772132718Skan			goto tr_setup;
773132718Skan		}
774132718Skan		return;
775132718Skan	}
776132718Skan}
777132718Skan
778132718Skanstatic void
779132718Skanuplcom_write_callback(struct usb2_xfer *xfer)
780132718Skan{
781132718Skan	struct uplcom_softc *sc = xfer->priv_sc;
782132718Skan	uint32_t actlen;
783132718Skan
784132718Skan	switch (USB_GET_STATE(xfer)) {
785132718Skan	case USB_ST_SETUP:
786132718Skan	case USB_ST_TRANSFERRED:
787132718Skantr_setup:
788132718Skan		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
789132718Skan		    UPLCOM_BULK_BUF_SIZE, &actlen)) {
790132718Skan
791132718Skan			DPRINTF("actlen = %d\n", actlen);
792132718Skan
793132718Skan			xfer->frlengths[0] = actlen;
794132718Skan			usb2_start_hardware(xfer);
795132718Skan		}
796132718Skan		return;
797169689Skan
798132718Skan	default:			/* Error */
799132718Skan		if (xfer->error != USB_ERR_CANCELLED) {
800132718Skan			/* try to clear stall first */
801169689Skan			xfer->flags.stall_pipe = 1;
802132718Skan			goto tr_setup;
803132718Skan		}
804169689Skan		return;
805132718Skan	}
806132718Skan}
807132718Skan
808132718Skanstatic void
809132718Skanuplcom_read_callback(struct usb2_xfer *xfer)
810132718Skan{
811132718Skan	struct uplcom_softc *sc = xfer->priv_sc;
812132718Skan
813132718Skan	switch (USB_GET_STATE(xfer)) {
814169689Skan	case USB_ST_TRANSFERRED:
815132718Skan		usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
816132718Skan
817169689Skan	case USB_ST_SETUP:
818132718Skantr_setup:
819132718Skan		xfer->frlengths[0] = xfer->max_data_length;
820132718Skan		usb2_start_hardware(xfer);
821132718Skan		return;
822132718Skan
823132718Skan	default:			/* Error */
824132718Skan		if (xfer->error != USB_ERR_CANCELLED) {
825132718Skan			/* try to clear stall first */
826132718Skan			xfer->flags.stall_pipe = 1;
827132718Skan			goto tr_setup;
828132718Skan		}
829132718Skan		return;
830132718Skan	}
831132718Skan}
832132718Skan