uplcom.c revision 185948
1/*	$NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $	*/
2
3#include <sys/cdefs.h>
4__FBSDID("$FreeBSD: head/sys/dev/usb2/serial/uplcom2.c 185948 2008-12-11 23:13:02Z thompsa $");
5
6/*-
7 * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*-
33 * Copyright (c) 2001 The NetBSD Foundation, Inc.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to The NetBSD Foundation
37 * by Ichiro FUKUHARA (ichiro@ichiro.org).
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 *    notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 *    notice, this list of conditions and the following disclaimer in the
46 *    documentation and/or other materials provided with the distribution.
47 * 3. All advertising materials mentioning features or use of this software
48 *    must display the following acknowledgement:
49 *        This product includes software developed by the NetBSD
50 *        Foundation, Inc. and its contributors.
51 * 4. Neither the name of The NetBSD Foundation nor the names of its
52 *    contributors may be used to endorse or promote products derived
53 *    from this software without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
56 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
57 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
58 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
59 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
60 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
61 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
62 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
63 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
65 * POSSIBILITY OF SUCH DAMAGE.
66 */
67
68/*
69 * This driver supports several USB-to-RS232 serial adapters driven by
70 * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
71 * bridge chip.  The adapters are sold under many different brand
72 * names.
73 *
74 * Datasheets are available at Prolific www site at
75 * http://www.prolific.com.tw.  The datasheets don't contain full
76 * programming information for the chip.
77 *
78 * PL-2303HX is probably programmed the same as PL-2303X.
79 *
80 * There are several differences between PL-2303 and PL-2303(H)X.
81 * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
82 * different command for controlling CRTSCTS and needs special
83 * sequence of commands for initialization which aren't also
84 * documented in the datasheet.
85 */
86
87#include <dev/usb2/include/usb2_devid.h>
88#include <dev/usb2/include/usb2_standard.h>
89#include <dev/usb2/include/usb2_mfunc.h>
90#include <dev/usb2/include/usb2_error.h>
91#include <dev/usb2/include/usb2_cdc.h>
92
93#define	USB_DEBUG_VAR uplcom_debug
94
95#include <dev/usb2/core/usb2_core.h>
96#include <dev/usb2/core/usb2_debug.h>
97#include <dev/usb2/core/usb2_process.h>
98#include <dev/usb2/core/usb2_config_td.h>
99#include <dev/usb2/core/usb2_request.h>
100#include <dev/usb2/core/usb2_lookup.h>
101#include <dev/usb2/core/usb2_util.h>
102#include <dev/usb2/core/usb2_busdma.h>
103
104#include <dev/usb2/serial/usb2_serial.h>
105
106#if USB_DEBUG
107static int uplcom_debug = 0;
108
109SYSCTL_NODE(_hw_usb2, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
110SYSCTL_INT(_hw_usb2_uplcom, OID_AUTO, debug, CTLFLAG_RW,
111    &uplcom_debug, 0, "Debug level");
112#endif
113
114#define	UPLCOM_MODVER			1	/* module version */
115
116#define	UPLCOM_CONFIG_INDEX		0
117#define	UPLCOM_IFACE_INDEX		0
118#define	UPLCOM_SECOND_IFACE_INDEX	1
119
120#ifndef UPLCOM_INTR_INTERVAL
121#define	UPLCOM_INTR_INTERVAL		0	/* default */
122#endif
123
124#define	UPLCOM_BULK_BUF_SIZE 1024	/* bytes */
125#define	UPLCOM_N_TRANSFER 6
126
127#define	UPLCOM_SET_REQUEST		0x01
128#define	UPLCOM_SET_CRTSCTS		0x41
129#define	UPLCOM_SET_CRTSCTS_PL2303X	0x61
130#define	RSAQ_STATUS_CTS			0x80
131#define	RSAQ_STATUS_DSR			0x02
132#define	RSAQ_STATUS_DCD			0x01
133
134#define	TYPE_PL2303			0
135#define	TYPE_PL2303X			1
136
137struct uplcom_softc {
138	struct usb2_com_super_softc sc_super_ucom;
139	struct usb2_com_softc sc_ucom;
140
141	struct usb2_xfer *sc_xfer[UPLCOM_N_TRANSFER];
142	struct usb2_device *sc_udev;
143
144	uint16_t sc_line;
145
146	uint8_t	sc_flag;
147#define	UPLCOM_FLAG_INTR_STALL  0x01
148#define	UPLCOM_FLAG_READ_STALL  0x02
149#define	UPLCOM_FLAG_WRITE_STALL 0x04
150
151	uint8_t	sc_lsr;			/* local status register */
152	uint8_t	sc_msr;			/* uplcom status register */
153	uint8_t	sc_chiptype;		/* type of chip */
154	uint8_t	sc_ctrl_iface_no;
155	uint8_t	sc_data_iface_no;
156	uint8_t	sc_iface_index[2];
157};
158
159/* prototypes */
160
161static usb2_error_t uplcom_reset(struct uplcom_softc *, struct usb2_device *);
162static int	uplcom_pl2303x_init(struct usb2_device *);
163static void	uplcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
164static void	uplcom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
165static void	uplcom_cfg_set_break(struct usb2_com_softc *, uint8_t);
166static int	uplcom_pre_param(struct usb2_com_softc *, struct termios *);
167static void	uplcom_cfg_param(struct usb2_com_softc *, struct termios *);
168static void	uplcom_start_read(struct usb2_com_softc *);
169static void	uplcom_stop_read(struct usb2_com_softc *);
170static void	uplcom_start_write(struct usb2_com_softc *);
171static void	uplcom_stop_write(struct usb2_com_softc *);
172static void	uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
173		    uint8_t *);
174static int	uplcom_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int,
175		    struct thread *);
176static void	uplcom_cfg_do_request(struct uplcom_softc *,
177		    struct usb2_device_request *, void *);
178
179static device_probe_t uplcom_probe;
180static device_attach_t uplcom_attach;
181static device_detach_t uplcom_detach;
182
183static usb2_callback_t uplcom_intr_callback;
184static usb2_callback_t uplcom_intr_clear_stall_callback;
185static usb2_callback_t uplcom_write_callback;
186static usb2_callback_t uplcom_write_clear_stall_callback;
187static usb2_callback_t uplcom_read_callback;
188static usb2_callback_t uplcom_read_clear_stall_callback;
189
190static const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
191
192	[0] = {
193		.type = UE_BULK,
194		.endpoint = UE_ADDR_ANY,
195		.direction = UE_DIR_OUT,
196		.mh.bufsize = UPLCOM_BULK_BUF_SIZE,
197		.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
198		.mh.callback = &uplcom_write_callback,
199		.if_index = 0,
200	},
201
202	[1] = {
203		.type = UE_BULK,
204		.endpoint = UE_ADDR_ANY,
205		.direction = UE_DIR_IN,
206		.mh.bufsize = UPLCOM_BULK_BUF_SIZE,
207		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
208		.mh.callback = &uplcom_read_callback,
209		.if_index = 0,
210	},
211
212	[2] = {
213		.type = UE_CONTROL,
214		.endpoint = 0x00,	/* Control pipe */
215		.direction = UE_DIR_ANY,
216		.mh.bufsize = sizeof(struct usb2_device_request),
217		.mh.callback = &uplcom_write_clear_stall_callback,
218		.mh.timeout = 1000,	/* 1 second */
219		.mh.interval = 50,	/* 50ms */
220		.if_index = 0,
221	},
222
223	[3] = {
224		.type = UE_CONTROL,
225		.endpoint = 0x00,	/* Control pipe */
226		.direction = UE_DIR_ANY,
227		.mh.bufsize = sizeof(struct usb2_device_request),
228		.mh.callback = &uplcom_read_clear_stall_callback,
229		.mh.timeout = 1000,	/* 1 second */
230		.mh.interval = 50,	/* 50ms */
231		.if_index = 0,
232	},
233
234	[4] = {
235		.type = UE_INTERRUPT,
236		.endpoint = UE_ADDR_ANY,
237		.direction = UE_DIR_IN,
238		.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
239		.mh.bufsize = 0,	/* use wMaxPacketSize */
240		.mh.callback = &uplcom_intr_callback,
241		.if_index = 1,
242	},
243
244	[5] = {
245		.type = UE_CONTROL,
246		.endpoint = 0x00,	/* Control pipe */
247		.direction = UE_DIR_ANY,
248		.mh.bufsize = sizeof(struct usb2_device_request),
249		.mh.callback = &uplcom_intr_clear_stall_callback,
250		.mh.timeout = 1000,	/* 1 second */
251		.mh.interval = 50,	/* 50ms */
252		.if_index = 1,
253	},
254};
255
256struct usb2_com_callback uplcom_callback = {
257	.usb2_com_cfg_get_status = &uplcom_cfg_get_status,
258	.usb2_com_cfg_set_dtr = &uplcom_cfg_set_dtr,
259	.usb2_com_cfg_set_rts = &uplcom_cfg_set_rts,
260	.usb2_com_cfg_set_break = &uplcom_cfg_set_break,
261	.usb2_com_cfg_param = &uplcom_cfg_param,
262	.usb2_com_pre_param = &uplcom_pre_param,
263	.usb2_com_ioctl = &uplcom_ioctl,
264	.usb2_com_start_read = &uplcom_start_read,
265	.usb2_com_stop_read = &uplcom_stop_read,
266	.usb2_com_start_write = &uplcom_start_write,
267	.usb2_com_stop_write = &uplcom_stop_write,
268};
269
270#define	USB_UPL(v,p,rl,rh,t)				\
271  USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl),	\
272  USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t)
273
274static const struct usb2_device_id uplcom_devs[] = {
275	/* Belkin F5U257 */
276	{USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)},
277	/* I/O DATA USB-RSAQ */
278	{USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)},
279	/* I/O DATA USB-RSAQ2 */
280	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)},
281	/* I/O DATA USB-RSAQ3 */
282	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)},
283	/* PLANEX USB-RS232 URS-03 */
284	{USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)},
285	/* TrendNet TU-S9 */
286	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)},
287	/* ST Lab USB-SERIAL-4 */
288	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)},
289	/* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
290	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)},
291	/* TDK USB-PHS Adapter UHA6400 */
292	{USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)},
293	/* RATOC REX-USB60 */
294	{USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)},
295	/* ELECOM UC-SGT */
296	{USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)},
297	{USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)},
298	/* Sagem USB-Serial Controller */
299	{USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)},
300	/* Sony Ericsson USB Cable */
301	{USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)},
302	/* SOURCENEXT KeikaiDenwa 8 */
303	{USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)},
304	/* SOURCENEXT KeikaiDenwa 8 with charger */
305	{USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)},
306	/* HAL Corporation Crossam2+USB */
307	{USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)},
308	/* Sitecom USB to Serial */
309	{USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)},
310	/* Tripp-Lite U209-000-R */
311	{USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)},
312	{USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)},
313	/* Prolific Pharos */
314	{USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)},
315	/* Willcom W-SIM */
316	{USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)},
317};
318
319static device_method_t uplcom_methods[] = {
320	DEVMETHOD(device_probe, uplcom_probe),
321	DEVMETHOD(device_attach, uplcom_attach),
322	DEVMETHOD(device_detach, uplcom_detach),
323	{0, 0}
324};
325
326static devclass_t uplcom_devclass;
327
328static driver_t uplcom_driver = {
329	.name = "uplcom",
330	.methods = uplcom_methods,
331	.size = sizeof(struct uplcom_softc),
332};
333
334DRIVER_MODULE(uplcom, ushub, uplcom_driver, uplcom_devclass, NULL, 0);
335MODULE_DEPEND(uplcom, usb2_serial, 1, 1, 1);
336MODULE_DEPEND(uplcom, usb2_core, 1, 1, 1);
337MODULE_VERSION(uplcom, UPLCOM_MODVER);
338
339static int
340uplcom_probe(device_t dev)
341{
342	struct usb2_attach_arg *uaa = device_get_ivars(dev);
343
344	DPRINTFN(11, "\n");
345
346	if (uaa->usb2_mode != USB_MODE_HOST) {
347		return (ENXIO);
348	}
349	if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) {
350		return (ENXIO);
351	}
352	if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) {
353		return (ENXIO);
354	}
355	return (usb2_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
356}
357
358static int
359uplcom_attach(device_t dev)
360{
361	struct usb2_attach_arg *uaa = device_get_ivars(dev);
362	struct uplcom_softc *sc = device_get_softc(dev);
363	struct usb2_interface *iface;
364	struct usb2_interface_descriptor *id;
365	int error;
366
367	DPRINTFN(11, "\n");
368
369	if (sc == NULL) {
370		return (ENOMEM);
371	}
372	device_set_usb2_desc(dev);
373
374	DPRINTF("sc = %p\n", sc);
375
376	sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa);
377	sc->sc_udev = uaa->device;
378
379	DPRINTF("chiptype: %s\n",
380	    (sc->sc_chiptype == TYPE_PL2303X) ?
381	    "2303X" : "2303");
382
383	/*
384	 * USB-RSAQ1 has two interface
385	 *
386	 *  USB-RSAQ1       | USB-RSAQ2
387	 * -----------------+-----------------
388	 * Interface 0      |Interface 0
389	 *  Interrupt(0x81) | Interrupt(0x81)
390	 * -----------------+ BulkIN(0x02)
391	 * Interface 1	    | BulkOUT(0x83)
392	 *   BulkIN(0x02)   |
393	 *   BulkOUT(0x83)  |
394	 */
395
396	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
397	sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX;
398
399	iface = usb2_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
400	if (iface) {
401		id = usb2_get_interface_descriptor(iface);
402		if (id == NULL) {
403			device_printf(dev, "no interface descriptor (2)!\n");
404			goto detach;
405		}
406		sc->sc_data_iface_no = id->bInterfaceNumber;
407		sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX;
408		usb2_set_parent_iface(uaa->device,
409		    UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex);
410	} else {
411		sc->sc_data_iface_no = sc->sc_ctrl_iface_no;
412		sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX;
413	}
414
415	error = usb2_transfer_setup(uaa->device,
416	    sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
417	    UPLCOM_N_TRANSFER, sc, &Giant);
418	if (error) {
419		DPRINTF("one or more missing USB endpoints, "
420		    "error=%s\n", usb2_errstr(error));
421		goto detach;
422	}
423	error = uplcom_reset(sc, uaa->device);
424	if (error) {
425		device_printf(dev, "reset failed, error=%s\n",
426		    usb2_errstr(error));
427		goto detach;
428	}
429	/* clear stall at first run */
430	sc->sc_flag |= (UPLCOM_FLAG_READ_STALL |
431	    UPLCOM_FLAG_WRITE_STALL);
432
433	error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
434	    &uplcom_callback, &Giant);
435	if (error) {
436		goto detach;
437	}
438	/*
439	 * do the initialization during attach so that the system does not
440	 * sleep during open:
441	 */
442	if (sc->sc_chiptype == TYPE_PL2303X) {
443		if (uplcom_pl2303x_init(uaa->device)) {
444			device_printf(dev, "init failed!\n");
445			goto detach;
446		}
447	}
448	return (0);
449
450detach:
451	uplcom_detach(dev);
452	return (ENXIO);
453}
454
455static int
456uplcom_detach(device_t dev)
457{
458	struct uplcom_softc *sc = device_get_softc(dev);
459
460	DPRINTF("sc=%p\n", sc);
461
462	usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
463
464	usb2_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
465
466	return (0);
467}
468
469static usb2_error_t
470uplcom_reset(struct uplcom_softc *sc, struct usb2_device *udev)
471{
472	struct usb2_device_request req;
473
474	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
475	req.bRequest = UPLCOM_SET_REQUEST;
476	USETW(req.wValue, 0);
477	req.wIndex[0] = sc->sc_data_iface_no;
478	req.wIndex[1] = 0;
479	USETW(req.wLength, 0);
480
481	return (usb2_do_request(udev, &Giant, &req, NULL));
482}
483
484struct pl2303x_init {
485	uint8_t	req_type;
486	uint8_t	request;
487	uint16_t value;
488	uint16_t index;
489	uint16_t length;
490};
491
492static const struct pl2303x_init pl2303x[] = {
493	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
494	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0},
495	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
496	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
497	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
498	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0},
499	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
500	{UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
501	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0},
502	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0},
503	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0},
504	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0},
505	{UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0},
506};
507
508#define	N_PL2302X_INIT	(sizeof(pl2303x)/sizeof(pl2303x[0]))
509
510static int
511uplcom_pl2303x_init(struct usb2_device *udev)
512{
513	struct usb2_device_request req;
514	usb2_error_t err;
515	uint8_t buf[4];
516	uint8_t i;
517
518	for (i = 0; i != N_PL2302X_INIT; i++) {
519		req.bmRequestType = pl2303x[i].req_type;
520		req.bRequest = pl2303x[i].request;
521		USETW(req.wValue, pl2303x[i].value);
522		USETW(req.wIndex, pl2303x[i].index);
523		USETW(req.wLength, pl2303x[i].length);
524
525		err = usb2_do_request(udev, &Giant, &req, buf);
526		if (err) {
527			DPRINTF("error=%s\n", usb2_errstr(err));
528			return (EIO);
529		}
530	}
531	return (0);
532}
533
534static void
535uplcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
536{
537	struct uplcom_softc *sc = ucom->sc_parent;
538	struct usb2_device_request req;
539
540	DPRINTF("onoff = %d\n", onoff);
541
542	if (onoff)
543		sc->sc_line |= UCDC_LINE_DTR;
544	else
545		sc->sc_line &= ~UCDC_LINE_DTR;
546
547	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
548	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
549	USETW(req.wValue, sc->sc_line);
550	req.wIndex[0] = sc->sc_data_iface_no;
551	req.wIndex[1] = 0;
552	USETW(req.wLength, 0);
553
554	uplcom_cfg_do_request(sc, &req, NULL);
555	return;
556}
557
558static void
559uplcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
560{
561	struct uplcom_softc *sc = ucom->sc_parent;
562	struct usb2_device_request req;
563
564	DPRINTF("onoff = %d\n", onoff);
565
566	if (onoff)
567		sc->sc_line |= UCDC_LINE_RTS;
568	else
569		sc->sc_line &= ~UCDC_LINE_RTS;
570
571	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
572	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
573	USETW(req.wValue, sc->sc_line);
574	req.wIndex[0] = sc->sc_data_iface_no;
575	req.wIndex[1] = 0;
576	USETW(req.wLength, 0);
577
578	uplcom_cfg_do_request(sc, &req, NULL);
579	return;
580}
581
582static void
583uplcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
584{
585	struct uplcom_softc *sc = ucom->sc_parent;
586	struct usb2_device_request req;
587	uint16_t temp;
588
589	DPRINTF("onoff = %d\n", onoff);
590
591	temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
592
593	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
594	req.bRequest = UCDC_SEND_BREAK;
595	USETW(req.wValue, temp);
596	req.wIndex[0] = sc->sc_data_iface_no;
597	req.wIndex[1] = 0;
598	USETW(req.wLength, 0);
599
600	uplcom_cfg_do_request(sc, &req, NULL);
601	return;
602}
603
604static const int32_t uplcom_rates[] = {
605	75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
606	19200, 28800, 38400, 57600, 115200,
607	/*
608	 * Higher speeds are probably possible. PL2303X supports up to
609	 * 6Mb and can set any rate
610	 */
611	230400, 460800, 614400, 921600, 1228800
612};
613
614#define	N_UPLCOM_RATES	(sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
615
616static int
617uplcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
618{
619	uint8_t i;
620
621	DPRINTF("\n");
622
623	/* check requested baud rate */
624
625	for (i = 0;; i++) {
626
627		if (i != N_UPLCOM_RATES) {
628			if (uplcom_rates[i] == t->c_ospeed) {
629				break;
630			}
631		} else {
632			DPRINTF("invalid baud rate (%d)\n", t->c_ospeed);
633			return (EIO);
634		}
635	}
636
637	return (0);
638}
639
640static void
641uplcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
642{
643	struct uplcom_softc *sc = ucom->sc_parent;
644	struct usb2_cdc_line_state ls;
645	struct usb2_device_request req;
646
647	DPRINTF("sc = %p\n", sc);
648
649	bzero(&ls, sizeof(ls));
650
651	USETDW(ls.dwDTERate, t->c_ospeed);
652
653	if (t->c_cflag & CSTOPB) {
654		ls.bCharFormat = UCDC_STOP_BIT_2;
655	} else {
656		ls.bCharFormat = UCDC_STOP_BIT_1;
657	}
658
659	if (t->c_cflag & PARENB) {
660		if (t->c_cflag & PARODD) {
661			ls.bParityType = UCDC_PARITY_ODD;
662		} else {
663			ls.bParityType = UCDC_PARITY_EVEN;
664		}
665	} else {
666		ls.bParityType = UCDC_PARITY_NONE;
667	}
668
669	switch (t->c_cflag & CSIZE) {
670	case CS5:
671		ls.bDataBits = 5;
672		break;
673	case CS6:
674		ls.bDataBits = 6;
675		break;
676	case CS7:
677		ls.bDataBits = 7;
678		break;
679	case CS8:
680		ls.bDataBits = 8;
681		break;
682	}
683
684	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
685	    UGETDW(ls.dwDTERate), ls.bCharFormat,
686	    ls.bParityType, ls.bDataBits);
687
688	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
689	req.bRequest = UCDC_SET_LINE_CODING;
690	USETW(req.wValue, 0);
691	req.wIndex[0] = sc->sc_data_iface_no;
692	req.wIndex[1] = 0;
693	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
694
695	uplcom_cfg_do_request(sc, &req, &ls);
696
697	if (t->c_cflag & CRTSCTS) {
698
699		DPRINTF("crtscts = on\n");
700
701		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
702		req.bRequest = UPLCOM_SET_REQUEST;
703		USETW(req.wValue, 0);
704		if (sc->sc_chiptype == TYPE_PL2303X)
705			USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
706		else
707			USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
708		USETW(req.wLength, 0);
709
710		uplcom_cfg_do_request(sc, &req, NULL);
711	} else {
712		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
713		req.bRequest = UPLCOM_SET_REQUEST;
714		USETW(req.wValue, 0);
715		USETW(req.wIndex, 0);
716		USETW(req.wLength, 0);
717		uplcom_cfg_do_request(sc, &req, NULL);
718	}
719	return;
720}
721
722static void
723uplcom_start_read(struct usb2_com_softc *ucom)
724{
725	struct uplcom_softc *sc = ucom->sc_parent;
726
727	/* start interrupt endpoint */
728	usb2_transfer_start(sc->sc_xfer[4]);
729
730	/* start read endpoint */
731	usb2_transfer_start(sc->sc_xfer[1]);
732	return;
733}
734
735static void
736uplcom_stop_read(struct usb2_com_softc *ucom)
737{
738	struct uplcom_softc *sc = ucom->sc_parent;
739
740	/* stop interrupt endpoint */
741	usb2_transfer_stop(sc->sc_xfer[4]);
742
743	/* stop read endpoint */
744	usb2_transfer_stop(sc->sc_xfer[3]);
745	usb2_transfer_stop(sc->sc_xfer[1]);
746	return;
747}
748
749static void
750uplcom_start_write(struct usb2_com_softc *ucom)
751{
752	struct uplcom_softc *sc = ucom->sc_parent;
753
754	usb2_transfer_start(sc->sc_xfer[0]);
755	return;
756}
757
758static void
759uplcom_stop_write(struct usb2_com_softc *ucom)
760{
761	struct uplcom_softc *sc = ucom->sc_parent;
762
763	usb2_transfer_stop(sc->sc_xfer[2]);
764	usb2_transfer_stop(sc->sc_xfer[0]);
765	return;
766}
767
768static void
769uplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
770{
771	struct uplcom_softc *sc = ucom->sc_parent;
772
773	DPRINTF("\n");
774
775	*lsr = sc->sc_lsr;
776	*msr = sc->sc_msr;
777	return;
778}
779
780static int
781uplcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag,
782    struct thread *td)
783{
784	return (ENOTTY);
785}
786
787static void
788uplcom_intr_callback(struct usb2_xfer *xfer)
789{
790	struct uplcom_softc *sc = xfer->priv_sc;
791	uint8_t buf[9];
792
793	switch (USB_GET_STATE(xfer)) {
794	case USB_ST_TRANSFERRED:
795
796		DPRINTF("actlen = %u\n", xfer->actlen);
797
798		if (xfer->actlen >= 9) {
799
800			usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
801
802			DPRINTF("status = 0x%02x\n", buf[8]);
803
804			sc->sc_lsr = 0;
805			sc->sc_msr = 0;
806
807			if (buf[8] & RSAQ_STATUS_CTS) {
808				sc->sc_msr |= SER_CTS;
809			}
810			if (buf[8] & RSAQ_STATUS_DSR) {
811				sc->sc_msr |= SER_DSR;
812			}
813			if (buf[8] & RSAQ_STATUS_DCD) {
814				sc->sc_msr |= SER_DCD;
815			}
816			usb2_com_status_change(&sc->sc_ucom);
817		}
818	case USB_ST_SETUP:
819		if (sc->sc_flag & UPLCOM_FLAG_INTR_STALL) {
820			usb2_transfer_start(sc->sc_xfer[5]);
821		} else {
822			xfer->frlengths[0] = xfer->max_data_length;
823			usb2_start_hardware(xfer);
824		}
825		return;
826
827	default:			/* Error */
828		if (xfer->error != USB_ERR_CANCELLED) {
829			sc->sc_flag |= UPLCOM_FLAG_INTR_STALL;
830			usb2_transfer_start(sc->sc_xfer[5]);
831		}
832		return;
833
834	}
835}
836
837static void
838uplcom_intr_clear_stall_callback(struct usb2_xfer *xfer)
839{
840	struct uplcom_softc *sc = xfer->priv_sc;
841	struct usb2_xfer *xfer_other = sc->sc_xfer[4];
842
843	if (usb2_clear_stall_callback(xfer, xfer_other)) {
844		DPRINTF("stall cleared\n");
845		sc->sc_flag &= ~UPLCOM_FLAG_INTR_STALL;
846		usb2_transfer_start(xfer_other);
847	}
848	return;
849}
850
851static void
852uplcom_write_callback(struct usb2_xfer *xfer)
853{
854	struct uplcom_softc *sc = xfer->priv_sc;
855	uint32_t actlen;
856
857	switch (USB_GET_STATE(xfer)) {
858	case USB_ST_SETUP:
859	case USB_ST_TRANSFERRED:
860		if (sc->sc_flag & UPLCOM_FLAG_WRITE_STALL) {
861			usb2_transfer_start(sc->sc_xfer[2]);
862			return;
863		}
864		if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
865		    UPLCOM_BULK_BUF_SIZE, &actlen)) {
866
867			DPRINTF("actlen = %d\n", actlen);
868
869			xfer->frlengths[0] = actlen;
870			usb2_start_hardware(xfer);
871		}
872		return;
873
874	default:			/* Error */
875		if (xfer->error != USB_ERR_CANCELLED) {
876			sc->sc_flag |= UPLCOM_FLAG_WRITE_STALL;
877			usb2_transfer_start(sc->sc_xfer[2]);
878		}
879		return;
880
881	}
882}
883
884static void
885uplcom_write_clear_stall_callback(struct usb2_xfer *xfer)
886{
887	struct uplcom_softc *sc = xfer->priv_sc;
888	struct usb2_xfer *xfer_other = sc->sc_xfer[0];
889
890	if (usb2_clear_stall_callback(xfer, xfer_other)) {
891		DPRINTF("stall cleared\n");
892		sc->sc_flag &= ~UPLCOM_FLAG_WRITE_STALL;
893		usb2_transfer_start(xfer_other);
894	}
895	return;
896}
897
898static void
899uplcom_read_callback(struct usb2_xfer *xfer)
900{
901	struct uplcom_softc *sc = xfer->priv_sc;
902
903	switch (USB_GET_STATE(xfer)) {
904	case USB_ST_TRANSFERRED:
905		usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
906
907	case USB_ST_SETUP:
908		if (sc->sc_flag & UPLCOM_FLAG_READ_STALL) {
909			usb2_transfer_start(sc->sc_xfer[3]);
910		} else {
911			xfer->frlengths[0] = xfer->max_data_length;
912			usb2_start_hardware(xfer);
913		}
914		return;
915
916	default:			/* Error */
917		if (xfer->error != USB_ERR_CANCELLED) {
918			sc->sc_flag |= UPLCOM_FLAG_READ_STALL;
919			usb2_transfer_start(sc->sc_xfer[3]);
920		}
921		return;
922
923	}
924}
925
926static void
927uplcom_read_clear_stall_callback(struct usb2_xfer *xfer)
928{
929	struct uplcom_softc *sc = xfer->priv_sc;
930	struct usb2_xfer *xfer_other = sc->sc_xfer[1];
931
932	if (usb2_clear_stall_callback(xfer, xfer_other)) {
933		DPRINTF("stall cleared\n");
934		sc->sc_flag &= ~UPLCOM_FLAG_READ_STALL;
935		usb2_transfer_start(xfer_other);
936	}
937	return;
938}
939
940static void
941uplcom_cfg_do_request(struct uplcom_softc *sc, struct usb2_device_request *req,
942    void *data)
943{
944	uint16_t length;
945	usb2_error_t err;
946
947	if (usb2_com_cfg_is_gone(&sc->sc_ucom)) {
948		goto error;
949	}
950	err = usb2_do_request_flags(sc->sc_udev, &Giant, req,
951	    data, 0, NULL, 1000);
952
953	if (err) {
954
955		DPRINTFN(0, "device request failed, err=%s "
956		    "(ignored)\n", usb2_errstr(err));
957
958error:
959		length = UGETW(req->wLength);
960
961		if ((req->bmRequestType & UT_READ) && length) {
962			bzero(data, length);
963		}
964	}
965	return;
966}
967