Index: freebsd/sys/dev/usb/serial/u3g.c
===================================================================
--- freebsd/sys/dev/usb/serial/u3g.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/u3g.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,1234 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*
+ * Copyright (c) 2008 AnyWi Technologies
+ * Author: Andrea Guzzo <aguzzo@anywi.com>
+ * * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
+ * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * NOTE:
+ *
+ * - The detour through the tty layer is ridiculously expensive wrt
+ *   buffering due to the high speeds.
+ *
+ *   We should consider adding a simple r/w device which allows
+ *   attaching of PPP in a more efficient way.
+ *
+ */
+
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR u3g_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_msctest.h>
+
+#include <dev/usb/serial/usb_serial.h>
+#include <dev/usb/quirk/usb_quirk.h>
+
+#ifdef USB_DEBUG
+static int u3g_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB 3g");
+SYSCTL_INT(_hw_usb_u3g, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &u3g_debug, 0, "Debug level");
+#endif
+
+#define	U3G_MAXPORTS		12
+#define	U3G_CONFIG_INDEX	0
+#define	U3G_BSIZE		2048
+#define	U3G_TXSIZE		(U3G_BSIZE / U3G_TXFRAMES)
+#define	U3G_TXFRAMES		4
+
+/* Eject methods; See also usb_quirks.h:UQ_MSC_EJECT_* */
+#define	U3GINIT_HUAWEI		1	/* Requires Huawei init command */
+#define	U3GINIT_SIERRA		2	/* Requires Sierra init command */
+#define	U3GINIT_SCSIEJECT	3	/* Requires SCSI eject command */
+#define	U3GINIT_REZERO		4	/* Requires SCSI rezero command */
+#define	U3GINIT_ZTESTOR		5	/* Requires ZTE SCSI command */
+#define	U3GINIT_CMOTECH		6	/* Requires CMOTECH SCSI command */
+#define	U3GINIT_WAIT		7	/* Device reappears after a delay */
+#define	U3GINIT_SAEL_M460	8	/* Requires vendor init */
+#define	U3GINIT_HUAWEISCSI	9	/* Requires Huawei SCSI init command */
+#define	U3GINIT_HUAWEISCSI2	10	/* Requires Huawei SCSI init command (2) */
+#define	U3GINIT_TCT		11	/* Requires TCT Mobile init command */
+
+enum {
+	U3G_BULK_WR,
+	U3G_BULK_RD,
+	U3G_INTR,
+	U3G_N_TRANSFER,
+};
+
+struct u3g_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom[U3G_MAXPORTS];
+
+	struct usb_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER];
+	uint8_t sc_iface[U3G_MAXPORTS];			/* local status register */
+	uint8_t sc_lsr[U3G_MAXPORTS];			/* local status register */
+	uint8_t sc_msr[U3G_MAXPORTS];			/* u3g status register */
+	uint16_t sc_line[U3G_MAXPORTS];			/* line status */
+
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint8_t sc_numports;
+};
+
+static device_probe_t u3g_probe;
+static device_attach_t u3g_attach;
+static device_detach_t u3g_detach;
+static void u3g_free_softc(struct u3g_softc *);
+
+static usb_callback_t u3g_write_callback;
+static usb_callback_t u3g_read_callback;
+static usb_callback_t u3g_intr_callback;
+
+static void u3g_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
+static void u3g_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void u3g_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void u3g_start_read(struct ucom_softc *ucom);
+static void u3g_stop_read(struct ucom_softc *ucom);
+static void u3g_start_write(struct ucom_softc *ucom);
+static void u3g_stop_write(struct ucom_softc *ucom);
+static void u3g_poll(struct ucom_softc *ucom);
+static void u3g_free(struct ucom_softc *ucom);
+
+
+static void u3g_test_autoinst(void *, struct usb_device *,
+		struct usb_attach_arg *);
+static int u3g_driver_loaded(struct module *mod, int what, void *arg);
+
+static eventhandler_tag u3g_etag;
+
+static const struct usb_config u3g_config[U3G_N_TRANSFER] = {
+
+	[U3G_BULK_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = U3G_BSIZE,/* bytes */
+		.frames = U3G_TXFRAMES,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &u3g_write_callback,
+	},
+
+	[U3G_BULK_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = U3G_BSIZE,/* bytes */
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &u3g_read_callback,
+	},
+
+	[U3G_INTR] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &u3g_intr_callback,
+	},
+};
+
+static const struct ucom_callback u3g_callback = {
+	.ucom_cfg_get_status = &u3g_cfg_get_status,
+	.ucom_cfg_set_dtr = &u3g_cfg_set_dtr,
+	.ucom_cfg_set_rts = &u3g_cfg_set_rts,
+	.ucom_start_read = &u3g_start_read,
+	.ucom_stop_read = &u3g_stop_read,
+	.ucom_start_write = &u3g_start_write,
+	.ucom_stop_write = &u3g_stop_write,
+	.ucom_poll = &u3g_poll,
+	.ucom_free = &u3g_free,
+};
+
+static device_method_t u3g_methods[] = {
+	DEVMETHOD(device_probe, u3g_probe),
+	DEVMETHOD(device_attach, u3g_attach),
+	DEVMETHOD(device_detach, u3g_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t u3g_devclass;
+
+static driver_t u3g_driver = {
+	.name = "u3g",
+	.methods = u3g_methods,
+	.size = sizeof(struct u3g_softc),
+};
+
+static const STRUCT_USB_HOST_ID u3g_devs[] = {
+#define	U3G_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+	U3G_DEV(ACERP, H10, 0),
+	U3G_DEV(AIRPLUS, MCD650, 0),
+	U3G_DEV(AIRPRIME, PC5220, 0),
+	U3G_DEV(AIRPRIME, AC313U, 0),
+	U3G_DEV(ALINK, 3G, 0),
+	U3G_DEV(ALINK, 3GU, 0),
+	U3G_DEV(ALINK, DWM652U5, 0),
+	U3G_DEV(AMOI, H01, 0),
+	U3G_DEV(AMOI, H01A, 0),
+	U3G_DEV(AMOI, H02, 0),
+	U3G_DEV(ANYDATA, ADU_500A, 0),
+	U3G_DEV(ANYDATA, ADU_620UW, 0),
+	U3G_DEV(ANYDATA, ADU_E100X, 0),
+	U3G_DEV(AXESSTEL, DATAMODEM, 0),
+	U3G_DEV(CMOTECH, CDMA_MODEM1, 0),
+	U3G_DEV(CMOTECH, CGU628, U3GINIT_CMOTECH),
+	U3G_DEV(DELL, U5500, 0),
+	U3G_DEV(DELL, U5505, 0),
+	U3G_DEV(DELL, U5510, 0),
+	U3G_DEV(DELL, U5520, 0),
+	U3G_DEV(DELL, U5520_2, 0),
+	U3G_DEV(DELL, U5520_3, 0),
+	U3G_DEV(DELL, U5700, 0),
+	U3G_DEV(DELL, U5700_2, 0),
+	U3G_DEV(DELL, U5700_3, 0),
+	U3G_DEV(DELL, U5700_4, 0),
+	U3G_DEV(DELL, U5720, 0),
+	U3G_DEV(DELL, U5720_2, 0),
+	U3G_DEV(DELL, U5730, 0),
+	U3G_DEV(DELL, U5730_2, 0),
+	U3G_DEV(DELL, U5730_3, 0),
+	U3G_DEV(DELL, U740, 0),
+	U3G_DEV(DLINK, DWR510_CD, U3GINIT_SCSIEJECT),
+	U3G_DEV(DLINK, DWR510, 0),
+	U3G_DEV(DLINK, DWM157_CD, U3GINIT_SCSIEJECT),
+	U3G_DEV(DLINK, DWM157, 0),
+	U3G_DEV(DLINK3, DWM652, 0),
+	U3G_DEV(HP, EV2200, 0),
+	U3G_DEV(HP, HS2300, 0),
+	U3G_DEV(HP, UN2420_QDL, 0),
+	U3G_DEV(HP, UN2420, 0),
+	U3G_DEV(HUAWEI, E1401, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1402, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1403, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1404, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1405, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1406, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1407, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1408, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1409, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E140A, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E140B, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E140D, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E140E, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E140F, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1410, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1411, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1412, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1413, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1414, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1415, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1416, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1417, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1418, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1419, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E141A, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E141B, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E141C, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E141D, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E141E, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E141F, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1420, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1421, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1422, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1423, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1424, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1425, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1426, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1427, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1428, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1429, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E142A, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E142B, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E142C, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E142D, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E142E, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E142F, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1430, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1431, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1432, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1433, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1434, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1435, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1436, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1437, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1438, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1439, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E143A, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E143B, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E143C, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E143D, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E143E, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E143F, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E173, 0),
+	U3G_DEV(HUAWEI, E173_INIT, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, E3131, 0),
+	U3G_DEV(HUAWEI, E3131_INIT, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, E180V, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E220, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E220BIS, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E392, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, ME909U, U3GINIT_HUAWEISCSI2),
+	U3G_DEV(HUAWEI, ME909S, U3GINIT_HUAWEISCSI2),
+	U3G_DEV(HUAWEI, MOBILE, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, E1752, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, E1820, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, K3771, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, K3771_INIT, U3GINIT_HUAWEISCSI2),
+	U3G_DEV(HUAWEI, K3772, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, K3772_INIT, U3GINIT_HUAWEISCSI2),
+	U3G_DEV(HUAWEI, K3765, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, K3765_INIT, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, K3770, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, K3770_INIT, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, K4505, U3GINIT_HUAWEI),
+	U3G_DEV(HUAWEI, K4505_INIT, U3GINIT_HUAWEISCSI),
+	U3G_DEV(HUAWEI, ETS2055, U3GINIT_HUAWEI),
+	U3G_DEV(KYOCERA2, CDMA_MSM_K, 0),
+	U3G_DEV(KYOCERA2, KPC680, 0),
+	U3G_DEV(LONGCHEER, WM66, U3GINIT_HUAWEI),
+	U3G_DEV(LONGCHEER, DISK, U3GINIT_TCT),
+	U3G_DEV(LONGCHEER, W14, 0),
+	U3G_DEV(LONGCHEER, XSSTICK, 0),
+	U3G_DEV(MERLIN, V620, 0),
+	U3G_DEV(NEOTEL, PRIME, 0),
+	U3G_DEV(NOVATEL, E725, 0),
+	U3G_DEV(NOVATEL, ES620, 0),
+	U3G_DEV(NOVATEL, ES620_2, 0),
+	U3G_DEV(NOVATEL, EU730, 0),
+	U3G_DEV(NOVATEL, EU740, 0),
+	U3G_DEV(NOVATEL, EU870D, 0),
+	U3G_DEV(NOVATEL, MC760, 0),
+	U3G_DEV(NOVATEL, MC547, 0),
+	U3G_DEV(NOVATEL, MC679, 0),
+	U3G_DEV(NOVATEL, MC950D, 0),
+	U3G_DEV(NOVATEL, MC990D, 0),
+	U3G_DEV(NOVATEL, MIFI2200, U3GINIT_SCSIEJECT),
+	U3G_DEV(NOVATEL, MIFI2200V, U3GINIT_SCSIEJECT),
+	U3G_DEV(NOVATEL, U720, 0),
+	U3G_DEV(NOVATEL, U727, 0),
+	U3G_DEV(NOVATEL, U727_2, 0),
+	U3G_DEV(NOVATEL, U740, 0),
+	U3G_DEV(NOVATEL, U740_2, 0),
+	U3G_DEV(NOVATEL, U760, U3GINIT_SCSIEJECT),
+	U3G_DEV(NOVATEL, U870, 0),
+	U3G_DEV(NOVATEL, V620, 0),
+	U3G_DEV(NOVATEL, V640, 0),
+	U3G_DEV(NOVATEL, V720, 0),
+	U3G_DEV(NOVATEL, V740, 0),
+	U3G_DEV(NOVATEL, X950D, 0),
+	U3G_DEV(NOVATEL, XU870, 0),
+	U3G_DEV(MOTOROLA2, MB886, U3GINIT_SCSIEJECT),
+	U3G_DEV(OPTION, E6500, 0),
+	U3G_DEV(OPTION, E6501, 0),
+	U3G_DEV(OPTION, E6601, 0),
+	U3G_DEV(OPTION, E6721, 0),
+	U3G_DEV(OPTION, E6741, 0),
+	U3G_DEV(OPTION, E6761, 0),
+	U3G_DEV(OPTION, E6800, 0),
+	U3G_DEV(OPTION, E7021, 0),
+	U3G_DEV(OPTION, E7041, 0),
+	U3G_DEV(OPTION, E7061, 0),
+	U3G_DEV(OPTION, E7100, 0),
+	U3G_DEV(OPTION, GE40X, 0),
+	U3G_DEV(OPTION, GT3G, 0),
+	U3G_DEV(OPTION, GT3GPLUS, 0),
+	U3G_DEV(OPTION, GT3GQUAD, 0),
+	U3G_DEV(OPTION, GT3G_1, 0),
+	U3G_DEV(OPTION, GT3G_2, 0),
+	U3G_DEV(OPTION, GT3G_3, 0),
+	U3G_DEV(OPTION, GT3G_4, 0),
+	U3G_DEV(OPTION, GT3G_5, 0),
+	U3G_DEV(OPTION, GT3G_6, 0),
+	U3G_DEV(OPTION, GTHSDPA, 0),
+	U3G_DEV(OPTION, GTM380, 0),
+	U3G_DEV(OPTION, GTMAX36, 0),
+	U3G_DEV(OPTION, GTMAX380HSUPAE, 0),
+	U3G_DEV(OPTION, GTMAXHSUPA, 0),
+	U3G_DEV(OPTION, GTMAXHSUPAE, 0),
+	U3G_DEV(OPTION, VODAFONEMC3G, 0),
+	U3G_DEV(QISDA, H20_1, 0),
+	U3G_DEV(QISDA, H20_2, 0),
+	U3G_DEV(QISDA, H21_1, 0),
+	U3G_DEV(QISDA, H21_2, 0),
+	U3G_DEV(QUALCOMM, NTT_L02C_MODEM, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUALCOMM2, AC8700, 0),
+	U3G_DEV(QUALCOMM2, MF330, 0),
+	U3G_DEV(QUALCOMM2, SIM5218, 0),
+	U3G_DEV(QUALCOMM2, WM620, 0),
+	U3G_DEV(QUALCOMM2, VW110L, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUALCOMM2, GOBI2000_QDL, 0),
+	U3G_DEV(QUALCOMM2, GOBI2000, 0),
+	U3G_DEV(QUALCOMM2, VT80N, 0),
+	U3G_DEV(QUALCOMM3, VFAST2, 0),
+	U3G_DEV(QUALCOMMINC, AC2726, 0),
+	U3G_DEV(QUALCOMMINC, AC682_INIT, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUALCOMMINC, AC682, 0),
+	U3G_DEV(QUALCOMMINC, AC8700, 0),
+	U3G_DEV(QUALCOMMINC, AC8710, 0),
+	U3G_DEV(QUALCOMMINC, CDMA_MSM, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUALCOMMINC, E0002, 0),
+	U3G_DEV(QUALCOMMINC, E0003, 0),
+	U3G_DEV(QUALCOMMINC, E0004, 0),
+	U3G_DEV(QUALCOMMINC, E0005, 0),
+	U3G_DEV(QUALCOMMINC, E0006, 0),
+	U3G_DEV(QUALCOMMINC, E0007, 0),
+	U3G_DEV(QUALCOMMINC, E0008, 0),
+	U3G_DEV(QUALCOMMINC, E0009, 0),
+	U3G_DEV(QUALCOMMINC, E000A, 0),
+	U3G_DEV(QUALCOMMINC, E000B, 0),
+	U3G_DEV(QUALCOMMINC, E000C, 0),
+	U3G_DEV(QUALCOMMINC, E000D, 0),
+	U3G_DEV(QUALCOMMINC, E000E, 0),
+	U3G_DEV(QUALCOMMINC, E000F, 0),
+	U3G_DEV(QUALCOMMINC, E0010, 0),
+	U3G_DEV(QUALCOMMINC, E0011, 0),
+	U3G_DEV(QUALCOMMINC, E0012, 0),
+	U3G_DEV(QUALCOMMINC, E0013, 0),
+	U3G_DEV(QUALCOMMINC, E0014, 0),
+	U3G_DEV(QUALCOMMINC, E0017, 0),
+	U3G_DEV(QUALCOMMINC, E0018, 0),
+	U3G_DEV(QUALCOMMINC, E0019, 0),
+	U3G_DEV(QUALCOMMINC, E0020, 0),
+	U3G_DEV(QUALCOMMINC, E0021, 0),
+	U3G_DEV(QUALCOMMINC, E0022, 0),
+	U3G_DEV(QUALCOMMINC, E0023, 0),
+	U3G_DEV(QUALCOMMINC, E0024, 0),
+	U3G_DEV(QUALCOMMINC, E0025, 0),
+	U3G_DEV(QUALCOMMINC, E0026, 0),
+	U3G_DEV(QUALCOMMINC, E0027, 0),
+	U3G_DEV(QUALCOMMINC, E0028, 0),
+	U3G_DEV(QUALCOMMINC, E0029, 0),
+	U3G_DEV(QUALCOMMINC, E0030, 0),
+	U3G_DEV(QUALCOMMINC, E0032, 0),
+	U3G_DEV(QUALCOMMINC, E0033, 0),
+	U3G_DEV(QUALCOMMINC, E0037, 0),
+	U3G_DEV(QUALCOMMINC, E0039, 0),
+	U3G_DEV(QUALCOMMINC, E0042, 0),
+	U3G_DEV(QUALCOMMINC, E0043, 0),
+	U3G_DEV(QUALCOMMINC, E0048, 0),
+	U3G_DEV(QUALCOMMINC, E0049, 0),
+	U3G_DEV(QUALCOMMINC, E0051, 0),
+	U3G_DEV(QUALCOMMINC, E0052, 0),
+	U3G_DEV(QUALCOMMINC, E0054, 0),
+	U3G_DEV(QUALCOMMINC, E0055, 0),
+	U3G_DEV(QUALCOMMINC, E0057, 0),
+	U3G_DEV(QUALCOMMINC, E0058, 0),
+	U3G_DEV(QUALCOMMINC, E0059, 0),
+	U3G_DEV(QUALCOMMINC, E0060, 0),
+	U3G_DEV(QUALCOMMINC, E0061, 0),
+	U3G_DEV(QUALCOMMINC, E0062, 0),
+	U3G_DEV(QUALCOMMINC, E0063, 0),
+	U3G_DEV(QUALCOMMINC, E0064, 0),
+	U3G_DEV(QUALCOMMINC, E0066, 0),
+	U3G_DEV(QUALCOMMINC, E0069, 0),
+	U3G_DEV(QUALCOMMINC, E0070, 0),
+	U3G_DEV(QUALCOMMINC, E0073, 0),
+	U3G_DEV(QUALCOMMINC, E0076, 0),
+	U3G_DEV(QUALCOMMINC, E0078, 0),
+	U3G_DEV(QUALCOMMINC, E0082, 0),
+	U3G_DEV(QUALCOMMINC, E0086, 0),
+	U3G_DEV(QUALCOMMINC, SURFSTICK, 0),
+	U3G_DEV(QUALCOMMINC, E2002, 0),
+	U3G_DEV(QUALCOMMINC, E2003, 0),
+	U3G_DEV(QUALCOMMINC, K3772_Z, 0),
+	U3G_DEV(QUALCOMMINC, K3772_Z_INIT, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUALCOMMINC, MF112, U3GINIT_ZTESTOR),
+	U3G_DEV(QUALCOMMINC, MF195E, 0),
+	U3G_DEV(QUALCOMMINC, MF195E_INIT, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUALCOMMINC, MF626, 0),
+	U3G_DEV(QUALCOMMINC, MF628, 0),
+	U3G_DEV(QUALCOMMINC, MF633R, 0),
+	/* the following is a RNDIS device, no modem features */
+	U3G_DEV(QUALCOMMINC, ZTE_MF730M, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUANTA, GKE, 0),
+	U3G_DEV(QUANTA, GLE, 0),
+	U3G_DEV(QUANTA, GLX, 0),
+	U3G_DEV(QUANTA, Q101, 0),
+	U3G_DEV(QUANTA, Q111, 0),
+	U3G_DEV(SIERRA, AC402, 0),
+	U3G_DEV(SIERRA, AC595U, 0),
+	U3G_DEV(SIERRA, AC313U, 0),
+	U3G_DEV(SIERRA, AC597E, 0),
+	U3G_DEV(SIERRA, AC875, 0),
+	U3G_DEV(SIERRA, AC875E, 0),
+	U3G_DEV(SIERRA, AC875U, 0),
+	U3G_DEV(SIERRA, AC875U_2, 0),
+	U3G_DEV(SIERRA, AC880, 0),
+	U3G_DEV(SIERRA, AC880E, 0),
+	U3G_DEV(SIERRA, AC880U, 0),
+	U3G_DEV(SIERRA, AC881, 0),
+	U3G_DEV(SIERRA, AC881E, 0),
+	U3G_DEV(SIERRA, AC881U, 0),
+	U3G_DEV(SIERRA, AC885E, 0),
+	U3G_DEV(SIERRA, AC885E_2, 0),
+	U3G_DEV(SIERRA, AC885U, 0),
+	U3G_DEV(SIERRA, AIRCARD580, 0),
+	U3G_DEV(SIERRA, AIRCARD595, 0),
+	U3G_DEV(SIERRA, C22, 0),
+	U3G_DEV(SIERRA, C597, 0),
+	U3G_DEV(SIERRA, C888, 0),
+	U3G_DEV(SIERRA, E0029, 0),
+	U3G_DEV(SIERRA, E6892, 0),
+	U3G_DEV(SIERRA, E6893, 0),
+	U3G_DEV(SIERRA, EM5625, 0),
+	U3G_DEV(SIERRA, EM5725, 0),
+	U3G_DEV(SIERRA, MC5720, 0),
+	U3G_DEV(SIERRA, MC5720_2, 0),
+	U3G_DEV(SIERRA, MC5725, 0),
+	U3G_DEV(SIERRA, MC5727, 0),
+	U3G_DEV(SIERRA, MC5727_2, 0),
+	U3G_DEV(SIERRA, MC5728, 0),
+	U3G_DEV(SIERRA, MC7354, 0),
+	U3G_DEV(SIERRA, MC7355, 0),
+	U3G_DEV(SIERRA, MC7430, 0),
+	U3G_DEV(SIERRA, MC8700, 0),
+	U3G_DEV(SIERRA, MC8755, 0),
+	U3G_DEV(SIERRA, MC8755_2, 0),
+	U3G_DEV(SIERRA, MC8755_3, 0),
+	U3G_DEV(SIERRA, MC8755_4, 0),
+	U3G_DEV(SIERRA, MC8765, 0),
+	U3G_DEV(SIERRA, MC8765_2, 0),
+	U3G_DEV(SIERRA, MC8765_3, 0),
+	U3G_DEV(SIERRA, MC8775, 0),
+	U3G_DEV(SIERRA, MC8775_2, 0),
+	U3G_DEV(SIERRA, MC8780, 0),
+	U3G_DEV(SIERRA, MC8780_2, 0),
+	U3G_DEV(SIERRA, MC8780_3, 0),
+	U3G_DEV(SIERRA, MC8781, 0),
+	U3G_DEV(SIERRA, MC8781_2, 0),
+	U3G_DEV(SIERRA, MC8781_3, 0),
+	U3G_DEV(SIERRA, MC8785, 0),
+	U3G_DEV(SIERRA, MC8785_2, 0),
+	U3G_DEV(SIERRA, MC8790, 0),
+	U3G_DEV(SIERRA, MC8791, 0),
+	U3G_DEV(SIERRA, MC8792, 0),
+	U3G_DEV(SIERRA, MINI5725, 0),
+	U3G_DEV(SIERRA, T11, 0),
+	U3G_DEV(SIERRA, T598, 0),
+	U3G_DEV(SILABS, SAEL, U3GINIT_SAEL_M460),
+	U3G_DEV(STELERA, C105, 0),
+	U3G_DEV(STELERA, E1003, 0),
+	U3G_DEV(STELERA, E1004, 0),
+	U3G_DEV(STELERA, E1005, 0),
+	U3G_DEV(STELERA, E1006, 0),
+	U3G_DEV(STELERA, E1007, 0),
+	U3G_DEV(STELERA, E1008, 0),
+	U3G_DEV(STELERA, E1009, 0),
+	U3G_DEV(STELERA, E100A, 0),
+	U3G_DEV(STELERA, E100B, 0),
+	U3G_DEV(STELERA, E100C, 0),
+	U3G_DEV(STELERA, E100D, 0),
+	U3G_DEV(STELERA, E100E, 0),
+	U3G_DEV(STELERA, E100F, 0),
+	U3G_DEV(STELERA, E1010, 0),
+	U3G_DEV(STELERA, E1011, 0),
+	U3G_DEV(STELERA, E1012, 0),
+	U3G_DEV(TCTMOBILE, X060S, 0),
+	U3G_DEV(TCTMOBILE, X080S, U3GINIT_TCT),
+	U3G_DEV(TELIT, UC864E, 0),
+	U3G_DEV(TELIT, UC864G, 0),
+	U3G_DEV(TLAYTECH, TEU800, 0),
+	U3G_DEV(TOSHIBA, G450, 0),
+	U3G_DEV(TOSHIBA, HSDPA, 0),
+	U3G_DEV(YISO, C893, 0),
+	U3G_DEV(WETELECOM, WM_D200, 0),
+	/* Autoinstallers */
+	U3G_DEV(NOVATEL, ZEROCD, U3GINIT_SCSIEJECT),
+	U3G_DEV(OPTION, GTICON322, U3GINIT_REZERO),
+	U3G_DEV(QUALCOMMINC, ZTE_STOR, U3GINIT_ZTESTOR),
+	U3G_DEV(QUALCOMMINC, ZTE_STOR2, U3GINIT_SCSIEJECT),
+	U3G_DEV(QUANTA, Q101_STOR, U3GINIT_SCSIEJECT),
+	U3G_DEV(SIERRA, TRUINSTALL, U3GINIT_SIERRA),
+#undef	U3G_DEV
+};
+
+DRIVER_MODULE(u3g, uhub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0);
+MODULE_DEPEND(u3g, ucom, 1, 1, 1);
+MODULE_DEPEND(u3g, usb, 1, 1, 1);
+MODULE_VERSION(u3g, 1);
+USB_PNP_HOST_INFO(u3g_devs);
+
+static int
+u3g_sierra_init(struct usb_device *udev)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_VENDOR;
+	req.bRequest = UR_SET_INTERFACE;
+	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+	USETW(req.wIndex, UHF_PORT_CONNECTION);
+	USETW(req.wLength, 0);
+
+	if (usbd_do_request_flags(udev, NULL, &req,
+	    NULL, 0, NULL, USB_MS_HZ)) {
+		/* ignore any errors */
+	}
+	return (0);
+}
+
+static int
+u3g_huawei_init(struct usb_device *udev)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_DEVICE;
+	req.bRequest = UR_SET_FEATURE;
+	USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+	USETW(req.wIndex, UHF_PORT_SUSPEND);
+	USETW(req.wLength, 0);
+
+	if (usbd_do_request_flags(udev, NULL, &req,
+	    NULL, 0, NULL, USB_MS_HZ)) {
+		/* ignore any errors */
+	}
+	return (0);
+}
+
+static void
+u3g_sael_m460_init(struct usb_device *udev)
+{
+	static const uint8_t setup[][24] = {
+	     { 0x41, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	       0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+	       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+	     { 0xc1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02 },
+	     { 0xc1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 },
+	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+	     { 0xc1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 },
+	     { 0x41, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x19, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+	       0x00, 0x00, 0x00, 0x00, 0x11, 0x13 },
+	     { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	       0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+	       0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x19, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00,
+	       0x00, 0x00, 0x00, 0x00, 0x11, 0x13 },
+	     { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	       0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+	       0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 },
+	     { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 },
+	};
+
+	struct usb_device_request req;
+	usb_error_t err;
+	uint16_t len;
+	uint8_t buf[0x300];
+	uint8_t n;
+
+	DPRINTFN(1, "\n");
+
+	if (usbd_req_set_alt_interface_no(udev, NULL, 0, 0)) {
+		DPRINTFN(0, "Alt setting 0 failed\n");
+		return;
+	}
+
+	for (n = 0; n != nitems(setup); n++) {
+
+		memcpy(&req, setup[n], sizeof(req));
+
+		len = UGETW(req.wLength);
+		if (req.bmRequestType & UE_DIR_IN) {
+			if (len > sizeof(buf)) {
+				DPRINTFN(0, "too small buffer\n");
+				continue;
+			}
+			err = usbd_do_request(udev, NULL, &req, buf);
+		} else {
+			if (len > (sizeof(setup[0]) - 8)) {
+				DPRINTFN(0, "too small buffer\n");
+				continue;
+			}
+			err = usbd_do_request(udev, NULL, &req,
+			    __DECONST(uint8_t *, &setup[n][8]));
+		}
+		if (err) {
+			DPRINTFN(1, "request %u failed\n",
+			    (unsigned int)n);
+			/*
+			 * Some of the requests will fail. Stop doing
+			 * requests when we are getting timeouts so
+			 * that we don't block the explore/attach
+			 * thread forever.
+			 */
+			if (err == USB_ERR_TIMEOUT)
+				break;
+		}
+	}
+}
+
+/*
+ * The following function handles 3G modem devices (E220, Mobile,
+ * etc.) with auto-install flash disks for Windows/MacOSX on the first
+ * interface.  After some command or some delay they change appearance
+ * to a modem.
+ */
+static void
+u3g_test_autoinst(void *arg, struct usb_device *udev,
+    struct usb_attach_arg *uaa)
+{
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *id;
+	int error;
+	unsigned long method;
+
+	if (uaa->dev_state != UAA_DEV_READY)
+		return;
+
+	iface = usbd_get_iface(udev, 0);
+	if (iface == NULL)
+		return;
+	id = iface->idesc;
+	if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
+		return;
+
+	if (usb_test_quirk(uaa, UQ_MSC_EJECT_HUAWEI))
+		method = U3GINIT_HUAWEI;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_SIERRA))
+		method = U3GINIT_SIERRA;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_SCSIEJECT))
+		method = U3GINIT_SCSIEJECT;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_REZERO))
+		method = U3GINIT_REZERO;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_ZTESTOR))
+		method = U3GINIT_ZTESTOR;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_CMOTECH))
+		method = U3GINIT_CMOTECH;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_WAIT))
+		method = U3GINIT_WAIT;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_HUAWEISCSI))
+		method = U3GINIT_HUAWEISCSI;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_HUAWEISCSI2))
+		method = U3GINIT_HUAWEISCSI2;
+	else if (usb_test_quirk(uaa, UQ_MSC_EJECT_TCT))
+		method = U3GINIT_TCT;
+	else if (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa) == 0)
+		method = USB_GET_DRIVER_INFO(uaa);
+	else
+		return;		/* no device match */
+
+	if (bootverbose) {
+		printf("Ejecting %s %s using method %ld\n",
+		       usb_get_manufacturer(udev),
+		       usb_get_product(udev), method);
+	}
+
+	switch (method) {
+		case U3GINIT_HUAWEI:
+			error = u3g_huawei_init(udev);
+			break;
+		case U3GINIT_HUAWEISCSI:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_HUAWEI);
+			break;
+		case U3GINIT_HUAWEISCSI2:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_HUAWEI2);
+			break;
+		case U3GINIT_SCSIEJECT:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT);
+			break;
+		case U3GINIT_REZERO:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_REZERO);
+			break;
+		case U3GINIT_ZTESTOR:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT);
+			if (error == 0)
+			    error = usb_msc_eject(udev, 0, MSC_EJECT_ZTESTOR);
+			break;
+		case U3GINIT_CMOTECH:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_CMOTECH);
+			break;
+		case U3GINIT_TCT:
+			error = usb_msc_eject(udev, 0, MSC_EJECT_TCT);
+			break;
+		case U3GINIT_SIERRA:
+			error = u3g_sierra_init(udev);
+			break;
+		case U3GINIT_WAIT:
+			/* Just pretend we ejected, the card will timeout */
+			error = 0;
+			break;
+		default:
+			/* no 3G eject quirks */
+			error = EOPNOTSUPP;
+			break;
+	}
+	if (error == 0) {
+		/* success, mark the udev as disappearing */
+		uaa->dev_state = UAA_DEV_EJECTING;
+	}
+}
+
+static int
+u3g_driver_loaded(struct module *mod, int what, void *arg)
+{
+	switch (what) {
+	case MOD_LOAD:
+		/* register our autoinstall handler */
+		u3g_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
+		    u3g_test_autoinst, NULL, EVENTHANDLER_PRI_ANY);
+		break;
+	case MOD_UNLOAD:
+		EVENTHANDLER_DEREGISTER(usb_dev_configured, u3g_etag);
+		break;
+	default:
+		return (EOPNOTSUPP);
+	}
+ 	return (0);
+}
+
+static int
+u3g_probe(device_t self)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(self);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bInterfaceClass != UICLASS_VENDOR) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
+}
+
+static int
+u3g_attach(device_t dev)
+{
+	struct usb_config u3g_config_tmp[U3G_N_TRANSFER];
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct u3g_softc *sc = device_get_softc(dev);
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *id;
+	uint32_t iface_valid;
+	int error, type, nports;
+	int ep, n;
+	uint8_t i;
+
+	DPRINTF("sc=%p\n", sc);
+
+	type = USB_GET_DRIVER_INFO(uaa);
+	if (type == U3GINIT_SAEL_M460
+	    || usb_test_quirk(uaa, UQ_MSC_EJECT_SAEL_M460)) {
+		u3g_sael_m460_init(uaa->device);
+	}
+
+	/* copy in USB config */
+	for (n = 0; n != U3G_N_TRANSFER; n++)
+		u3g_config_tmp[n] = u3g_config[n];
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "u3g", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_udev = uaa->device;
+
+	/* Claim all interfaces on the device */
+	iface_valid = 0;
+	for (i = uaa->info.bIfaceIndex; i < USB_IFACE_MAX; i++) {
+		iface = usbd_get_iface(uaa->device, i);
+		if (iface == NULL)
+			break;
+		id = usbd_get_interface_descriptor(iface);
+		if (id == NULL || id->bInterfaceClass != UICLASS_VENDOR)
+			continue;
+		usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+		iface_valid |= (1<<i);
+	}
+
+	i = 0;		/* interface index */
+	ep = 0;		/* endpoint index */
+	nports = 0;	/* number of ports */
+	while (i < USB_IFACE_MAX) {
+		if ((iface_valid & (1<<i)) == 0) {
+			i++;
+			continue;
+		}
+
+		/* update BULK endpoint index */
+		for (n = 0; n < U3G_N_TRANSFER; n++)
+			u3g_config_tmp[n].ep_index = ep;
+
+		/* try to allocate a set of BULK endpoints */
+		error = usbd_transfer_setup(uaa->device, &i,
+		    sc->sc_xfer[nports], u3g_config_tmp, U3G_N_TRANSFER,
+		    &sc->sc_ucom[nports], &sc->sc_mtx);
+		if (error) {
+			/* next interface */
+			i++;
+			ep = 0;
+			continue;
+		}
+
+		iface = usbd_get_iface(uaa->device, i);
+		id = usbd_get_interface_descriptor(iface);
+		sc->sc_iface[nports] = id->bInterfaceNumber;
+
+		if (bootverbose && sc->sc_xfer[nports][U3G_INTR]) {
+			device_printf(dev, "port %d supports modem control\n",
+				      nports);
+		}
+
+		/* set stall by default */
+		mtx_lock(&sc->sc_mtx);
+		usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_WR]);
+		usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_RD]);
+		mtx_unlock(&sc->sc_mtx);
+
+		nports++;	/* found one port */
+		ep++;
+		if (nports == U3G_MAXPORTS)
+			break;
+	}
+	if (nports == 0) {
+		device_printf(dev, "no ports found\n");
+		goto detach;
+	}
+	sc->sc_numports = nports;
+
+	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
+	    sc->sc_numports, sc, &u3g_callback, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("ucom_attach failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+	device_printf(dev, "Found %u port%s.\n", sc->sc_numports,
+	    sc->sc_numports > 1 ? "s":"");
+
+	return (0);
+
+detach:
+	u3g_detach(dev);
+	return (ENXIO);
+}
+
+static int
+u3g_detach(device_t dev)
+{
+	struct u3g_softc *sc = device_get_softc(dev);
+	uint8_t subunit;
+
+	DPRINTF("sc=%p\n", sc);
+
+	/* NOTE: It is not dangerous to detach more ports than attached! */
+	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
+
+	for (subunit = 0; subunit != U3G_MAXPORTS; subunit++)
+		usbd_transfer_unsetup(sc->sc_xfer[subunit], U3G_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	u3g_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(u3g);
+
+static void
+u3g_free_softc(struct u3g_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+u3g_free(struct ucom_softc *ucom)
+{
+	u3g_free_softc(ucom->sc_parent);
+}
+
+static void
+u3g_start_read(struct ucom_softc *ucom)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	/* start interrupt endpoint (if configured) */
+	usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_INTR]);
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_RD]);
+}
+
+static void
+u3g_stop_read(struct ucom_softc *ucom)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt endpoint (if configured) */
+	usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_INTR]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_RD]);
+}
+
+static void
+u3g_start_write(struct ucom_softc *ucom)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_WR]);
+}
+
+static void
+u3g_stop_write(struct ucom_softc *ucom)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_WR]);
+}
+
+static void
+u3g_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ucom_softc *ucom = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+	uint32_t frame;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+	case USB_ST_SETUP:
+tr_setup:
+		for (frame = 0; frame != U3G_TXFRAMES; frame++) {
+			usbd_xfer_set_frame_offset(xfer, frame * U3G_TXSIZE, frame);
+
+			pc = usbd_xfer_get_frame(xfer, frame);
+			if (ucom_get_data(ucom, pc, 0, U3G_TXSIZE, &actlen) == 0)
+				break;
+			usbd_xfer_set_frame_len(xfer, frame, actlen);
+		}
+		if (frame != 0) {
+			usbd_xfer_set_frames(xfer, frame);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* do a builtin clear-stall */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+u3g_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ucom_softc *ucom = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* do a builtin clear-stall */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+u3g_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr[ucom->sc_subunit];
+	*msr = sc->sc_msr[ucom->sc_subunit];
+}
+
+static void
+u3g_cfg_set_line(struct ucom_softc *ucom)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line[ucom->sc_subunit]);
+	req.wIndex[0] = sc->sc_iface[ucom->sc_subunit];
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, ucom,
+	    &req, NULL, 0, 1000);
+}
+
+static void
+u3g_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line[ucom->sc_subunit] |= UCDC_LINE_DTR;
+	else
+		sc->sc_line[ucom->sc_subunit] &= ~UCDC_LINE_DTR;
+
+	u3g_cfg_set_line(ucom);
+}
+
+static void
+u3g_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line[ucom->sc_subunit] |= UCDC_LINE_RTS;
+	else
+		sc->sc_line[ucom->sc_subunit] &= ~UCDC_LINE_RTS;
+
+	u3g_cfg_set_line(ucom);
+}
+
+static void
+u3g_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ucom_softc *ucom = usbd_xfer_softc(xfer);
+	struct u3g_softc *sc = ucom->sc_parent;
+	struct usb_page_cache *pc;
+	struct usb_cdc_notification pkt;
+	int actlen;
+	uint16_t wLen;
+	uint8_t mstatus;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen < 8) {	/* usb_cdc_notification with 2 data bytes */
+			DPRINTF("message too short (expected 8, received %d)\n", actlen);
+			goto tr_setup;
+		}
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_out(pc, 0, &pkt, actlen);
+
+		wLen = UGETW(pkt.wLength);
+		if (wLen < 2) {
+			DPRINTF("message too short (expected 2 data bytes, received %d)\n", wLen);
+			goto tr_setup;
+		}
+
+		if (pkt.bmRequestType == UCDC_NOTIFICATION
+		    && pkt.bNotification == UCDC_N_SERIAL_STATE) {
+			/*
+		         * Set the serial state in ucom driver based on
+		         * the bits from the notify message
+		         */
+			DPRINTF("notify bytes = 0x%02x, 0x%02x\n",
+			    pkt.data[0], pkt.data[1]);
+
+			/* currently, lsr is always zero. */
+			sc->sc_lsr[ucom->sc_subunit] = 0;
+			sc->sc_msr[ucom->sc_subunit] = 0;
+
+			mstatus = pkt.data[0];
+
+			if (mstatus & UCDC_N_SERIAL_RI)
+				sc->sc_msr[ucom->sc_subunit] |= SER_RI;
+			if (mstatus & UCDC_N_SERIAL_DSR)
+				sc->sc_msr[ucom->sc_subunit] |= SER_DSR;
+			if (mstatus & UCDC_N_SERIAL_DCD)
+				sc->sc_msr[ucom->sc_subunit] |= SER_DCD;
+			ucom_status_change(ucom);
+		}
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+u3g_poll(struct ucom_softc *ucom)
+{
+	struct u3g_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer[ucom->sc_subunit], U3G_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/uark.c
===================================================================
--- freebsd/sys/dev/usb/serial/uark.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uark.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,472 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$OpenBSD: uark.c,v 1.1 2006/08/14 08:30:22 jsg Exp $	*/
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * NOTE: all function names beginning like "uark_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define	UARK_BUF_SIZE		1024	/* bytes */
+
+#define	UARK_SET_DATA_BITS(x)	((x) - 5)
+
+#define	UARK_PARITY_NONE	0x00
+#define	UARK_PARITY_ODD		0x08
+#define	UARK_PARITY_EVEN	0x18
+
+#define	UARK_STOP_BITS_1	0x00
+#define	UARK_STOP_BITS_2	0x04
+
+#define	UARK_BAUD_REF		3000000
+
+#define	UARK_WRITE		0x40
+#define	UARK_READ		0xc0
+
+#define	UARK_REQUEST		0xfe
+
+#define	UARK_CONFIG_INDEX	0
+#define	UARK_IFACE_INDEX	0
+
+enum {
+	UARK_BULK_DT_WR,
+	UARK_BULK_DT_RD,
+	UARK_N_TRANSFER,
+};
+
+struct uark_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UARK_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint8_t	sc_msr;
+	uint8_t	sc_lsr;
+};
+
+/* prototypes */
+
+static device_probe_t uark_probe;
+static device_attach_t uark_attach;
+static device_detach_t uark_detach;
+static void uark_free_softc(struct uark_softc *);
+
+static usb_callback_t uark_bulk_write_callback;
+static usb_callback_t uark_bulk_read_callback;
+
+static void	uark_free(struct ucom_softc *);
+static void	uark_start_read(struct ucom_softc *);
+static void	uark_stop_read(struct ucom_softc *);
+static void	uark_start_write(struct ucom_softc *);
+static void	uark_stop_write(struct ucom_softc *);
+static int	uark_pre_param(struct ucom_softc *, struct termios *);
+static void	uark_cfg_param(struct ucom_softc *, struct termios *);
+static void	uark_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	uark_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	uark_cfg_write(struct uark_softc *, uint16_t, uint16_t);
+static void	uark_poll(struct ucom_softc *ucom);
+
+static const struct usb_config
+	uark_xfer_config[UARK_N_TRANSFER] = {
+
+	[UARK_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UARK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &uark_bulk_write_callback,
+	},
+
+	[UARK_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UARK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uark_bulk_read_callback,
+	},
+};
+
+static const struct ucom_callback uark_callback = {
+	.ucom_cfg_get_status = &uark_cfg_get_status,
+	.ucom_cfg_set_break = &uark_cfg_set_break,
+	.ucom_cfg_param = &uark_cfg_param,
+	.ucom_pre_param = &uark_pre_param,
+	.ucom_start_read = &uark_start_read,
+	.ucom_stop_read = &uark_stop_read,
+	.ucom_start_write = &uark_start_write,
+	.ucom_stop_write = &uark_stop_write,
+	.ucom_poll = &uark_poll,
+	.ucom_free = &uark_free,
+};
+
+static device_method_t uark_methods[] = {
+	/* Device methods */
+	DEVMETHOD(device_probe, uark_probe),
+	DEVMETHOD(device_attach, uark_attach),
+	DEVMETHOD(device_detach, uark_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uark_devclass;
+
+static driver_t uark_driver = {
+	.name = "uark",
+	.methods = uark_methods,
+	.size = sizeof(struct uark_softc),
+};
+
+static const STRUCT_USB_HOST_ID uark_devs[] = {
+	{USB_VPI(USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116, 0)},
+};
+
+DRIVER_MODULE(uark, uhub, uark_driver, uark_devclass, NULL, 0);
+MODULE_DEPEND(uark, ucom, 1, 1, 1);
+MODULE_DEPEND(uark, usb, 1, 1, 1);
+MODULE_VERSION(uark, 1);
+USB_PNP_HOST_INFO(uark_devs);
+
+static int
+uark_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != 0) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UARK_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uark_devs, sizeof(uark_devs), uaa));
+}
+
+static int
+uark_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uark_softc *sc = device_get_softc(dev);
+	int32_t error;
+	uint8_t iface_index;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uark", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_udev = uaa->device;
+
+	iface_index = UARK_IFACE_INDEX;
+	error = usbd_transfer_setup
+	    (uaa->device, &iface_index, sc->sc_xfer,
+	    uark_xfer_config, UARK_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		device_printf(dev, "allocating control USB "
+		    "transfers failed\n");
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uark_callback, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("ucom_attach failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);			/* success */
+
+detach:
+	uark_detach(dev);
+	return (ENXIO);			/* failure */
+}
+
+static int
+uark_detach(device_t dev)
+{
+	struct uark_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UARK_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uark_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uark);
+
+static void
+uark_free_softc(struct uark_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uark_free(struct ucom_softc *ucom)
+{
+	uark_free_softc(ucom->sc_parent);
+}
+
+static void
+uark_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uark_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UARK_BUF_SIZE, &actlen)) {
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+uark_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uark_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uark_start_read(struct ucom_softc *ucom)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UARK_BULK_DT_RD]);
+}
+
+static void
+uark_stop_read(struct ucom_softc *ucom)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UARK_BULK_DT_RD]);
+}
+
+static void
+uark_start_write(struct ucom_softc *ucom)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UARK_BULK_DT_WR]);
+}
+
+static void
+uark_stop_write(struct ucom_softc *ucom)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UARK_BULK_DT_WR]);
+}
+
+static int
+uark_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	if ((t->c_ospeed < 300) || (t->c_ospeed > 115200))
+		return (EINVAL);
+	return (0);
+}
+
+static void
+uark_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+	uint32_t speed = t->c_ospeed;
+	uint16_t data;
+
+	/*
+	 * NOTE: When reverse computing the baud rate from the "data" all
+	 * allowed baud rates are within 3% of the initial baud rate.
+	 */
+	data = (UARK_BAUD_REF + (speed / 2)) / speed;
+
+	uark_cfg_write(sc, 3, 0x83);
+	uark_cfg_write(sc, 0, data & 0xFF);
+	uark_cfg_write(sc, 1, data >> 8);
+	uark_cfg_write(sc, 3, 0x03);
+
+	if (t->c_cflag & CSTOPB)
+		data = UARK_STOP_BITS_2;
+	else
+		data = UARK_STOP_BITS_1;
+
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD)
+			data |= UARK_PARITY_ODD;
+		else
+			data |= UARK_PARITY_EVEN;
+	} else
+		data |= UARK_PARITY_NONE;
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		data |= UARK_SET_DATA_BITS(5);
+		break;
+	case CS6:
+		data |= UARK_SET_DATA_BITS(6);
+		break;
+	case CS7:
+		data |= UARK_SET_DATA_BITS(7);
+		break;
+	default:
+	case CS8:
+		data |= UARK_SET_DATA_BITS(8);
+		break;
+	}
+	uark_cfg_write(sc, 3, 0x00);
+	uark_cfg_write(sc, 3, data);
+}
+
+static void
+uark_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+uark_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff=%d\n", onoff);
+
+	uark_cfg_write(sc, 4, onoff ? 0x01 : 0x00);
+}
+
+static void
+uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+
+	req.bmRequestType = UARK_WRITE;
+	req.bRequest = UARK_REQUEST;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, index);
+	USETW(req.wLength, 0);
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "device request failed, err=%s "
+		    "(ignored)\n", usbd_errstr(err));
+	}
+}
+
+static void
+uark_poll(struct ucom_softc *ucom)
+{
+	struct uark_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UARK_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/ubsa.c
===================================================================
--- freebsd/sys/dev/usb/serial/ubsa.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/ubsa.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,700 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR ubsa_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int ubsa_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa");
+SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &ubsa_debug, 0, "ubsa debug level");
+#endif
+
+#define	UBSA_BSIZE             1024	/* bytes */
+
+#define	UBSA_CONFIG_INDEX	0
+#define	UBSA_IFACE_INDEX	0
+
+#define	UBSA_REG_BAUDRATE	0x00
+#define	UBSA_REG_STOP_BITS	0x01
+#define	UBSA_REG_DATA_BITS	0x02
+#define	UBSA_REG_PARITY		0x03
+#define	UBSA_REG_DTR		0x0A
+#define	UBSA_REG_RTS		0x0B
+#define	UBSA_REG_BREAK		0x0C
+#define	UBSA_REG_FLOW_CTRL	0x10
+
+#define	UBSA_PARITY_NONE	0x00
+#define	UBSA_PARITY_EVEN	0x01
+#define	UBSA_PARITY_ODD		0x02
+#define	UBSA_PARITY_MARK	0x03
+#define	UBSA_PARITY_SPACE	0x04
+
+#define	UBSA_FLOW_NONE		0x0000
+#define	UBSA_FLOW_OCTS		0x0001
+#define	UBSA_FLOW_ODSR		0x0002
+#define	UBSA_FLOW_IDSR		0x0004
+#define	UBSA_FLOW_IDTR		0x0008
+#define	UBSA_FLOW_IRTS		0x0010
+#define	UBSA_FLOW_ORTS		0x0020
+#define	UBSA_FLOW_UNKNOWN	0x0040
+#define	UBSA_FLOW_OXON		0x0080
+#define	UBSA_FLOW_IXON		0x0100
+
+/* line status register */
+#define	UBSA_LSR_TSRE		0x40	/* Transmitter empty: byte sent */
+#define	UBSA_LSR_TXRDY		0x20	/* Transmitter buffer empty */
+#define	UBSA_LSR_BI		0x10	/* Break detected */
+#define	UBSA_LSR_FE		0x08	/* Framing error: bad stop bit */
+#define	UBSA_LSR_PE		0x04	/* Parity error */
+#define	UBSA_LSR_OE		0x02	/* Overrun, lost incoming byte */
+#define	UBSA_LSR_RXRDY		0x01	/* Byte ready in Receive Buffer */
+#define	UBSA_LSR_RCV_MASK	0x1f	/* Mask for incoming data or error */
+
+/* modem status register */
+/* All deltas are from the last read of the MSR. */
+#define	UBSA_MSR_DCD		0x80	/* Current Data Carrier Detect */
+#define	UBSA_MSR_RI		0x40	/* Current Ring Indicator */
+#define	UBSA_MSR_DSR		0x20	/* Current Data Set Ready */
+#define	UBSA_MSR_CTS		0x10	/* Current Clear to Send */
+#define	UBSA_MSR_DDCD		0x08	/* DCD has changed state */
+#define	UBSA_MSR_TERI		0x04	/* RI has toggled low to high */
+#define	UBSA_MSR_DDSR		0x02	/* DSR has changed state */
+#define	UBSA_MSR_DCTS		0x01	/* CTS has changed state */
+
+enum {
+	UBSA_BULK_DT_WR,
+	UBSA_BULK_DT_RD,
+	UBSA_INTR_DT_RD,
+	UBSA_N_TRANSFER,
+};
+
+struct ubsa_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UBSA_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint8_t	sc_iface_no;		/* interface number */
+	uint8_t	sc_iface_index;		/* interface index */
+	uint8_t	sc_lsr;			/* local status register */
+	uint8_t	sc_msr;			/* UBSA status register */
+};
+
+static device_probe_t ubsa_probe;
+static device_attach_t ubsa_attach;
+static device_detach_t ubsa_detach;
+static void ubsa_free_softc(struct ubsa_softc *);
+
+static usb_callback_t ubsa_write_callback;
+static usb_callback_t ubsa_read_callback;
+static usb_callback_t ubsa_intr_callback;
+
+static void	ubsa_cfg_request(struct ubsa_softc *, uint8_t, uint16_t);
+static void	ubsa_free(struct ucom_softc *);
+static void	ubsa_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	ubsa_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	ubsa_cfg_set_break(struct ucom_softc *, uint8_t);
+static int	ubsa_pre_param(struct ucom_softc *, struct termios *);
+static void	ubsa_cfg_param(struct ucom_softc *, struct termios *);
+static void	ubsa_start_read(struct ucom_softc *);
+static void	ubsa_stop_read(struct ucom_softc *);
+static void	ubsa_start_write(struct ucom_softc *);
+static void	ubsa_stop_write(struct ucom_softc *);
+static void	ubsa_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	ubsa_poll(struct ucom_softc *ucom);
+
+static const struct usb_config ubsa_config[UBSA_N_TRANSFER] = {
+
+	[UBSA_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UBSA_BSIZE,	/* bytes */
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &ubsa_write_callback,
+	},
+
+	[UBSA_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UBSA_BSIZE,	/* bytes */
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &ubsa_read_callback,
+	},
+
+	[UBSA_INTR_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &ubsa_intr_callback,
+	},
+};
+
+static const struct ucom_callback ubsa_callback = {
+	.ucom_cfg_get_status = &ubsa_cfg_get_status,
+	.ucom_cfg_set_dtr = &ubsa_cfg_set_dtr,
+	.ucom_cfg_set_rts = &ubsa_cfg_set_rts,
+	.ucom_cfg_set_break = &ubsa_cfg_set_break,
+	.ucom_cfg_param = &ubsa_cfg_param,
+	.ucom_pre_param = &ubsa_pre_param,
+	.ucom_start_read = &ubsa_start_read,
+	.ucom_stop_read = &ubsa_stop_read,
+	.ucom_start_write = &ubsa_start_write,
+	.ucom_stop_write = &ubsa_stop_write,
+	.ucom_poll = &ubsa_poll,
+	.ucom_free = &ubsa_free,
+};
+
+static const STRUCT_USB_HOST_ID ubsa_devs[] = {
+	/* AnyData ADU-500A */
+	{USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_500A, 0)},
+	/* AnyData ADU-E100A/H */
+	{USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_E100X, 0)},
+	/* Axesstel MV100H */
+	{USB_VPI(USB_VENDOR_AXESSTEL, USB_PRODUCT_AXESSTEL_DATAMODEM, 0)},
+	/* BELKIN F5U103 */
+	{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103, 0)},
+	/* BELKIN F5U120 */
+	{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120, 0)},
+	/* GoHubs GO-COM232 */
+	{USB_VPI(USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM, 0)},
+	/* GoHubs GO-COM232 */
+	{USB_VPI(USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232, 0)},
+	/* Peracom */
+	{USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, 0)},
+};
+
+static device_method_t ubsa_methods[] = {
+	DEVMETHOD(device_probe, ubsa_probe),
+	DEVMETHOD(device_attach, ubsa_attach),
+	DEVMETHOD(device_detach, ubsa_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t ubsa_devclass;
+
+static driver_t ubsa_driver = {
+	.name = "ubsa",
+	.methods = ubsa_methods,
+	.size = sizeof(struct ubsa_softc),
+};
+
+DRIVER_MODULE(ubsa, uhub, ubsa_driver, ubsa_devclass, NULL, 0);
+MODULE_DEPEND(ubsa, ucom, 1, 1, 1);
+MODULE_DEPEND(ubsa, usb, 1, 1, 1);
+MODULE_VERSION(ubsa, 1);
+USB_PNP_HOST_INFO(ubsa_devs);
+
+static int
+ubsa_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UBSA_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UBSA_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(ubsa_devs, sizeof(ubsa_devs), uaa));
+}
+
+static int
+ubsa_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct ubsa_softc *sc = device_get_softc(dev);
+	int error;
+
+	DPRINTF("sc=%p\n", sc);
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "ubsa", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_udev = uaa->device;
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+	sc->sc_iface_index = UBSA_IFACE_INDEX;
+
+	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
+	    sc->sc_xfer, ubsa_config, UBSA_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		DPRINTF("could not allocate all pipes\n");
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &ubsa_callback, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("ucom_attach failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	ubsa_detach(dev);
+	return (ENXIO);
+}
+
+static int
+ubsa_detach(device_t dev)
+{
+	struct ubsa_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UBSA_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	ubsa_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(ubsa);
+
+static void
+ubsa_free_softc(struct ubsa_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+ubsa_free(struct ucom_softc *ucom)
+{
+	ubsa_free_softc(ucom->sc_parent);
+}
+
+static void
+ubsa_cfg_request(struct ubsa_softc *sc, uint8_t index, uint16_t value)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = index;
+	USETW(req.wValue, value);
+	req.wIndex[0] = sc->sc_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "device request failed, err=%s "
+		    "(ignored)\n", usbd_errstr(err));
+	}
+}
+
+static void
+ubsa_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	ubsa_cfg_request(sc, UBSA_REG_DTR, onoff ? 1 : 0);
+}
+
+static void
+ubsa_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	ubsa_cfg_request(sc, UBSA_REG_RTS, onoff ? 1 : 0);
+}
+
+static void
+ubsa_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	ubsa_cfg_request(sc, UBSA_REG_BREAK, onoff ? 1 : 0);
+}
+
+static int
+ubsa_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+
+	DPRINTF("sc = %p\n", ucom->sc_parent);
+
+	switch (t->c_ospeed) {
+	case B0:
+	case B300:
+	case B600:
+	case B1200:
+	case B2400:
+	case B4800:
+	case B9600:
+	case B19200:
+	case B38400:
+	case B57600:
+	case B115200:
+	case B230400:
+		break;
+	default:
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static void
+ubsa_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+	uint16_t value = 0;
+
+	DPRINTF("sc = %p\n", sc);
+
+	switch (t->c_ospeed) {
+	case B0:
+		ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, 0);
+		ubsa_cfg_set_dtr(&sc->sc_ucom, 0);
+		ubsa_cfg_set_rts(&sc->sc_ucom, 0);
+		break;
+	case B300:
+	case B600:
+	case B1200:
+	case B2400:
+	case B4800:
+	case B9600:
+	case B19200:
+	case B38400:
+	case B57600:
+	case B115200:
+	case B230400:
+		value = B230400 / t->c_ospeed;
+		ubsa_cfg_request(sc, UBSA_REG_BAUDRATE, value);
+		break;
+	default:
+		return;
+	}
+
+	if (t->c_cflag & PARENB)
+		value = (t->c_cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN;
+	else
+		value = UBSA_PARITY_NONE;
+
+	ubsa_cfg_request(sc, UBSA_REG_PARITY, value);
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		value = 0;
+		break;
+	case CS6:
+		value = 1;
+		break;
+	case CS7:
+		value = 2;
+		break;
+	default:
+	case CS8:
+		value = 3;
+		break;
+	}
+
+	ubsa_cfg_request(sc, UBSA_REG_DATA_BITS, value);
+
+	value = (t->c_cflag & CSTOPB) ? 1 : 0;
+
+	ubsa_cfg_request(sc, UBSA_REG_STOP_BITS, value);
+
+	value = 0;
+	if (t->c_cflag & CRTSCTS)
+		value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS;
+
+	if (t->c_iflag & (IXON | IXOFF))
+		value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON;
+
+	ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, value);
+}
+
+static void
+ubsa_start_read(struct ucom_softc *ucom)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	/* start interrupt endpoint */
+	usbd_transfer_start(sc->sc_xfer[UBSA_INTR_DT_RD]);
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UBSA_BULK_DT_RD]);
+}
+
+static void
+ubsa_stop_read(struct ucom_softc *ucom)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UBSA_INTR_DT_RD]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_RD]);
+}
+
+static void
+ubsa_start_write(struct ucom_softc *ucom)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UBSA_BULK_DT_WR]);
+}
+
+static void
+ubsa_stop_write(struct ucom_softc *ucom)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_WR]);
+}
+
+static void
+ubsa_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+ubsa_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ubsa_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UBSA_BSIZE, &actlen)) {
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+ubsa_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ubsa_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+ubsa_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ubsa_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[4];
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		if (actlen >= (int)sizeof(buf)) {
+			pc = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_out(pc, 0, buf, sizeof(buf));
+
+			/*
+			 * MSR bits need translation from ns16550 to SER_* values.
+			 * LSR bits are ns16550 in hardware and ucom.
+			 */
+			sc->sc_msr = 0;
+			if (buf[3] & UBSA_MSR_CTS)
+				sc->sc_msr |= SER_CTS;
+			if (buf[3] & UBSA_MSR_DCD)
+				sc->sc_msr |= SER_DCD;
+			if (buf[3] & UBSA_MSR_RI)
+				sc->sc_msr |= SER_RI;
+			if (buf[3] & UBSA_MSR_DSR)
+				sc->sc_msr |= SER_DSR;
+			sc->sc_lsr = buf[2];
+
+			DPRINTF("lsr = 0x%02x, msr = 0x%02x\n",
+			    sc->sc_lsr, sc->sc_msr);
+
+			ucom_status_change(&sc->sc_ucom);
+		} else {
+			DPRINTF("ignoring short packet, %d bytes\n", actlen);
+		}
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+ubsa_poll(struct ucom_softc *ucom)
+{
+	struct ubsa_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UBSA_N_TRANSFER);
+
+}
Index: freebsd/sys/dev/usb/serial/ubser.c
===================================================================
--- freebsd/sys/dev/usb/serial/ubser.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/ubser.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,561 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2004 Bernd Walter <ticso@FreeBSD.org>
+ *
+ * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $
+ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
+ * $Author: ticso $
+ * $Rev: 1127 $
+ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BWCT serial adapter driver
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR ubser_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define	UBSER_UNIT_MAX	32
+
+/* Vendor Interface Requests */
+#define	VENDOR_GET_NUMSER		0x01
+#define	VENDOR_SET_BREAK		0x02
+#define	VENDOR_CLEAR_BREAK		0x03
+
+#ifdef USB_DEBUG
+static int ubser_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
+SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &ubser_debug, 0, "ubser debug level");
+#endif
+
+enum {
+	UBSER_BULK_DT_WR,
+	UBSER_BULK_DT_RD,
+	UBSER_N_TRANSFER,
+};
+
+struct ubser_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom[UBSER_UNIT_MAX];
+
+	struct usb_xfer *sc_xfer[UBSER_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint16_t sc_tx_size;
+
+	uint8_t	sc_numser;
+	uint8_t	sc_iface_no;
+	uint8_t	sc_iface_index;
+	uint8_t	sc_curr_tx_unit;
+};
+
+/* prototypes */
+
+static device_probe_t ubser_probe;
+static device_attach_t ubser_attach;
+static device_detach_t ubser_detach;
+static void ubser_free_softc(struct ubser_softc *);
+
+static usb_callback_t ubser_write_callback;
+static usb_callback_t ubser_read_callback;
+
+static void	ubser_free(struct ucom_softc *);
+static int	ubser_pre_param(struct ucom_softc *, struct termios *);
+static void	ubser_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	ubser_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	ubser_start_read(struct ucom_softc *);
+static void	ubser_stop_read(struct ucom_softc *);
+static void	ubser_start_write(struct ucom_softc *);
+static void	ubser_stop_write(struct ucom_softc *);
+static void	ubser_poll(struct ucom_softc *ucom);
+
+static const struct usb_config ubser_config[UBSER_N_TRANSFER] = {
+
+	[UBSER_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &ubser_write_callback,
+	},
+
+	[UBSER_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &ubser_read_callback,
+	},
+};
+
+static const struct ucom_callback ubser_callback = {
+	.ucom_cfg_set_break = &ubser_cfg_set_break,
+	.ucom_cfg_get_status = &ubser_cfg_get_status,
+	.ucom_pre_param = &ubser_pre_param,
+	.ucom_start_read = &ubser_start_read,
+	.ucom_stop_read = &ubser_stop_read,
+	.ucom_start_write = &ubser_start_write,
+	.ucom_stop_write = &ubser_stop_write,
+	.ucom_poll = &ubser_poll,
+	.ucom_free = &ubser_free,
+};
+
+static device_method_t ubser_methods[] = {
+	DEVMETHOD(device_probe, ubser_probe),
+	DEVMETHOD(device_attach, ubser_attach),
+	DEVMETHOD(device_detach, ubser_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t ubser_devclass;
+
+static driver_t ubser_driver = {
+	.name = "ubser",
+	.methods = ubser_methods,
+	.size = sizeof(struct ubser_softc),
+};
+
+DRIVER_MODULE(ubser, uhub, ubser_driver, ubser_devclass, NULL, 0);
+MODULE_DEPEND(ubser, ucom, 1, 1, 1);
+MODULE_DEPEND(ubser, usb, 1, 1, 1);
+MODULE_VERSION(ubser, 1);
+
+static int
+ubser_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	/* check if this is a BWCT vendor specific ubser interface */
+	if ((strcmp(usb_get_manufacturer(uaa->device), "BWCT") == 0) &&
+	    (uaa->info.bInterfaceClass == 0xff) &&
+	    (uaa->info.bInterfaceSubClass == 0x00))
+		return (0);
+
+	return (ENXIO);
+}
+
+static int
+ubser_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct ubser_softc *sc = device_get_softc(dev);
+	struct usb_device_request req;
+	uint8_t n;
+	int error;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "ubser", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+	sc->sc_iface_index = uaa->info.bIfaceIndex;
+	sc->sc_udev = uaa->device;
+
+	/* get number of serials */
+	req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+	req.bRequest = VENDOR_GET_NUMSER;
+	USETW(req.wValue, 0);
+	req.wIndex[0] = sc->sc_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 1);
+	error = usbd_do_request_flags(uaa->device, NULL,
+	    &req, &sc->sc_numser,
+	    0, NULL, USB_DEFAULT_TIMEOUT);
+
+	if (error || (sc->sc_numser == 0)) {
+		device_printf(dev, "failed to get number "
+		    "of serial ports: %s\n",
+		    usbd_errstr(error));
+		goto detach;
+	}
+	if (sc->sc_numser > UBSER_UNIT_MAX)
+		sc->sc_numser = UBSER_UNIT_MAX;
+
+	device_printf(dev, "found %i serials\n", sc->sc_numser);
+
+	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
+	    sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	sc->sc_tx_size = usbd_xfer_max_len(sc->sc_xfer[UBSER_BULK_DT_WR]);
+
+	if (sc->sc_tx_size == 0) {
+		DPRINTFN(0, "invalid tx_size\n");
+		goto detach;
+	}
+	/* initialize port numbers */
+
+	for (n = 0; n < sc->sc_numser; n++) {
+		sc->sc_ucom[n].sc_portno = n;
+	}
+
+	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom,
+	    sc->sc_numser, sc, &ubser_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]);
+	usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	return (0);			/* success */
+
+detach:
+	ubser_detach(dev);
+	return (ENXIO);			/* failure */
+}
+
+static int
+ubser_detach(device_t dev)
+{
+	struct ubser_softc *sc = device_get_softc(dev);
+
+	DPRINTF("\n");
+
+	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	ubser_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(ubser);
+
+static void
+ubser_free_softc(struct ubser_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+ubser_free(struct ucom_softc *ucom)
+{
+	ubser_free_softc(ucom->sc_parent);
+}
+
+static int
+ubser_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	DPRINTF("\n");
+
+	/*
+	 * The firmware on our devices can only do 8n1@9600bps
+	 * without handshake.
+	 * We refuse to accept other configurations.
+	 */
+
+	/* ensure 9600bps */
+	switch (t->c_ospeed) {
+	case 9600:
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	/* 2 stop bits not possible */
+	if (t->c_cflag & CSTOPB)
+		return (EINVAL);
+
+	/* XXX parity handling not possible with current firmware */
+	if (t->c_cflag & PARENB)
+		return (EINVAL);
+
+	/* we can only do 8 data bits */
+	switch (t->c_cflag & CSIZE) {
+	case CS8:
+		break;
+	default:
+		return (EINVAL);
+	}
+
+	/* we can't do any kind of hardware handshaking */
+	if ((t->c_cflag &
+	    (CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW)) != 0)
+		return (EINVAL);
+
+	/*
+	 * XXX xon/xoff not supported by the firmware!
+	 * This is handled within FreeBSD only and may overflow buffers
+	 * because of delayed reaction due to device buffering.
+	 */
+
+	return (0);
+}
+
+static __inline void
+ubser_inc_tx_unit(struct ubser_softc *sc)
+{
+	sc->sc_curr_tx_unit++;
+	if (sc->sc_curr_tx_unit >= sc->sc_numser) {
+		sc->sc_curr_tx_unit = 0;
+	}
+}
+
+static void
+ubser_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ubser_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[1];
+	uint8_t first_unit = sc->sc_curr_tx_unit;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		do {
+			if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit,
+			    pc, 1, sc->sc_tx_size - 1,
+			    &actlen)) {
+
+				buf[0] = sc->sc_curr_tx_unit;
+
+				usbd_copy_in(pc, 0, buf, 1);
+
+				usbd_xfer_set_frame_len(xfer, 0, actlen + 1);
+				usbd_transfer_submit(xfer);
+
+				ubser_inc_tx_unit(sc);	/* round robin */
+
+				break;
+			}
+			ubser_inc_tx_unit(sc);
+
+		} while (sc->sc_curr_tx_unit != first_unit);
+
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+ubser_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ubser_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[1];
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen < 1) {
+			DPRINTF("invalid actlen=0!\n");
+			goto tr_setup;
+		}
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_out(pc, 0, buf, 1);
+
+		if (buf[0] >= sc->sc_numser) {
+			DPRINTF("invalid serial number!\n");
+			goto tr_setup;
+		}
+		ucom_put_data(sc->sc_ucom + buf[0], pc, 1, actlen - 1);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+ubser_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ubser_softc *sc = ucom->sc_parent;
+	uint8_t x = ucom->sc_portno;
+	struct usb_device_request req;
+	usb_error_t err;
+
+	if (onoff) {
+
+		req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+		req.bRequest = VENDOR_SET_BREAK;
+		req.wValue[0] = x;
+		req.wValue[1] = 0;
+		req.wIndex[0] = sc->sc_iface_no;
+		req.wIndex[1] = 0;
+		USETW(req.wLength, 0);
+
+		err = ucom_cfg_do_request(sc->sc_udev, ucom, 
+		    &req, NULL, 0, 1000);
+		if (err) {
+			DPRINTFN(0, "send break failed, error=%s\n",
+			    usbd_errstr(err));
+		}
+	}
+}
+
+static void
+ubser_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	/* fake status bits */
+	*lsr = 0;
+	*msr = SER_DCD;
+}
+
+static void
+ubser_start_read(struct ucom_softc *ucom)
+{
+	struct ubser_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
+}
+
+static void
+ubser_stop_read(struct ucom_softc *ucom)
+{
+	struct ubser_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]);
+}
+
+static void
+ubser_start_write(struct ucom_softc *ucom)
+{
+	struct ubser_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]);
+}
+
+static void
+ubser_stop_write(struct ucom_softc *ucom)
+{
+	struct ubser_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]);
+}
+
+static void
+ubser_poll(struct ucom_softc *ucom)
+{
+	struct ubser_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UBSER_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/uchcom.c
===================================================================
--- freebsd/sys/dev/usb/serial/uchcom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uchcom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,880 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $	*/
+
+/*-
+ * Copyright (c) 2007, Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Takuya SHIOZAKI (tshiozak@netbsd.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for WinChipHead CH341/340, the worst USB-serial chip in the
+ * world.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR uchcom_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int uchcom_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom");
+SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &uchcom_debug, 0, "uchcom debug level");
+#endif
+
+#define	UCHCOM_IFACE_INDEX	0
+#define	UCHCOM_CONFIG_INDEX	0
+
+#define	UCHCOM_REV_CH340	0x0250
+#define	UCHCOM_INPUT_BUF_SIZE	8
+
+#define	UCHCOM_REQ_GET_VERSION	0x5F
+#define	UCHCOM_REQ_READ_REG	0x95
+#define	UCHCOM_REQ_WRITE_REG	0x9A
+#define	UCHCOM_REQ_RESET	0xA1
+#define	UCHCOM_REQ_SET_DTRRTS	0xA4
+
+#define	UCHCOM_REG_STAT1	0x06
+#define	UCHCOM_REG_STAT2	0x07
+#define	UCHCOM_REG_BPS_PRE	0x12
+#define	UCHCOM_REG_BPS_DIV	0x13
+#define	UCHCOM_REG_BPS_MOD	0x14
+#define	UCHCOM_REG_BPS_PAD	0x0F
+#define	UCHCOM_REG_BREAK1	0x05
+#define	UCHCOM_REG_BREAK2	0x18
+#define	UCHCOM_REG_LCR1		0x18
+#define	UCHCOM_REG_LCR2		0x25
+
+#define	UCHCOM_VER_20		0x20
+
+#define	UCHCOM_BASE_UNKNOWN	0
+#define	UCHCOM_BPS_MOD_BASE	20000000
+#define	UCHCOM_BPS_MOD_BASE_OFS	1100
+
+#define	UCHCOM_DTR_MASK		0x20
+#define	UCHCOM_RTS_MASK		0x40
+
+#define	UCHCOM_BRK1_MASK	0x01
+#define	UCHCOM_BRK2_MASK	0x40
+
+#define	UCHCOM_LCR1_MASK	0xAF
+#define	UCHCOM_LCR2_MASK	0x07
+#define	UCHCOM_LCR1_PARENB	0x80
+#define	UCHCOM_LCR2_PAREVEN	0x07
+#define	UCHCOM_LCR2_PARODD	0x06
+#define	UCHCOM_LCR2_PARMARK	0x05
+#define	UCHCOM_LCR2_PARSPACE	0x04
+
+#define	UCHCOM_INTR_STAT1	0x02
+#define	UCHCOM_INTR_STAT2	0x03
+#define	UCHCOM_INTR_LEAST	4
+
+#define	UCHCOM_BULK_BUF_SIZE 1024	/* bytes */
+
+enum {
+	UCHCOM_BULK_DT_WR,
+	UCHCOM_BULK_DT_RD,
+	UCHCOM_INTR_DT_RD,
+	UCHCOM_N_TRANSFER,
+};
+
+struct uchcom_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint8_t	sc_dtr;			/* local copy */
+	uint8_t	sc_rts;			/* local copy */
+	uint8_t	sc_version;
+	uint8_t	sc_msr;
+	uint8_t	sc_lsr;			/* local status register */
+};
+
+struct uchcom_divider {
+	uint8_t	dv_prescaler;
+	uint8_t	dv_div;
+	uint8_t	dv_mod;
+};
+
+struct uchcom_divider_record {
+	uint32_t dvr_high;
+	uint32_t dvr_low;
+	uint32_t dvr_base_clock;
+	struct uchcom_divider dvr_divider;
+};
+
+static const struct uchcom_divider_record dividers[] =
+{
+	{307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}},
+	{921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}},
+	{2999999, 23530, 6000000, {3, 0, 0}},
+	{23529, 2942, 750000, {2, 0, 0}},
+	{2941, 368, 93750, {1, 0, 0}},
+	{367, 1, 11719, {0, 0, 0}},
+};
+
+#define	NUM_DIVIDERS	nitems(dividers)
+
+static const STRUCT_USB_HOST_ID uchcom_devs[] = {
+	{USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
+	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)},
+	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_2, 0)},
+};
+
+/* protypes */
+
+static void	uchcom_free(struct ucom_softc *);
+static int	uchcom_pre_param(struct ucom_softc *, struct termios *);
+static void	uchcom_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	uchcom_cfg_open(struct ucom_softc *ucom);
+static void	uchcom_cfg_param(struct ucom_softc *, struct termios *);
+static void	uchcom_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	uchcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	uchcom_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	uchcom_start_read(struct ucom_softc *);
+static void	uchcom_start_write(struct ucom_softc *);
+static void	uchcom_stop_read(struct ucom_softc *);
+static void	uchcom_stop_write(struct ucom_softc *);
+static void	uchcom_update_version(struct uchcom_softc *);
+static void	uchcom_convert_status(struct uchcom_softc *, uint8_t);
+static void	uchcom_update_status(struct uchcom_softc *);
+static void	uchcom_set_dtr_rts(struct uchcom_softc *);
+static int	uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
+static void	uchcom_set_baudrate(struct uchcom_softc *, uint32_t);
+static void	uchcom_poll(struct ucom_softc *ucom);
+
+static device_probe_t uchcom_probe;
+static device_attach_t uchcom_attach;
+static device_detach_t uchcom_detach;
+static void uchcom_free_softc(struct uchcom_softc *);
+
+static usb_callback_t uchcom_intr_callback;
+static usb_callback_t uchcom_write_callback;
+static usb_callback_t uchcom_read_callback;
+
+static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
+
+	[UCHCOM_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UCHCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &uchcom_write_callback,
+	},
+
+	[UCHCOM_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UCHCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uchcom_read_callback,
+	},
+
+	[UCHCOM_INTR_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &uchcom_intr_callback,
+	},
+};
+
+static struct ucom_callback uchcom_callback = {
+	.ucom_cfg_get_status = &uchcom_cfg_get_status,
+	.ucom_cfg_set_dtr = &uchcom_cfg_set_dtr,
+	.ucom_cfg_set_rts = &uchcom_cfg_set_rts,
+	.ucom_cfg_set_break = &uchcom_cfg_set_break,
+	.ucom_cfg_open = &uchcom_cfg_open,
+	.ucom_cfg_param = &uchcom_cfg_param,
+	.ucom_pre_param = &uchcom_pre_param,
+	.ucom_start_read = &uchcom_start_read,
+	.ucom_stop_read = &uchcom_stop_read,
+	.ucom_start_write = &uchcom_start_write,
+	.ucom_stop_write = &uchcom_stop_write,
+	.ucom_poll = &uchcom_poll,
+	.ucom_free = &uchcom_free,
+};
+
+/* ----------------------------------------------------------------------
+ * driver entry points
+ */
+
+static int
+uchcom_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	DPRINTFN(11, "\n");
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa));
+}
+
+static int
+uchcom_attach(device_t dev)
+{
+	struct uchcom_softc *sc = device_get_softc(dev);
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	int error;
+	uint8_t iface_index;
+
+	DPRINTFN(11, "\n");
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uchcom", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_udev = uaa->device;
+
+	switch (uaa->info.bcdDevice) {
+	case UCHCOM_REV_CH340:
+		device_printf(dev, "CH340 detected\n");
+		break;
+	default:
+		device_printf(dev, "CH341 detected\n");
+		break;
+	}
+
+	iface_index = UCHCOM_IFACE_INDEX;
+	error = usbd_transfer_setup(uaa->device,
+	    &iface_index, sc->sc_xfer, uchcom_config_data,
+	    UCHCOM_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		DPRINTF("one or more missing USB endpoints, "
+		    "error=%s\n", usbd_errstr(error));
+		goto detach;
+	}
+
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uchcom_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	uchcom_detach(dev);
+	return (ENXIO);
+}
+
+static int
+uchcom_detach(device_t dev)
+{
+	struct uchcom_softc *sc = device_get_softc(dev);
+
+	DPRINTFN(11, "\n");
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uchcom_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uchcom);
+
+static void
+uchcom_free_softc(struct uchcom_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uchcom_free(struct ucom_softc *ucom)
+{
+	uchcom_free_softc(ucom->sc_parent);
+}
+
+/* ----------------------------------------------------------------------
+ * low level i/o
+ */
+
+static void
+uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno,
+    uint16_t value, uint16_t index)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = reqno;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, index);
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev,
+	    &sc->sc_ucom, &req, NULL, 0, 1000);
+}
+
+static void
+uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno,
+    uint16_t value, uint16_t index, void *buf, uint16_t buflen)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = reqno;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, index);
+	USETW(req.wLength, buflen);
+
+	ucom_cfg_do_request(sc->sc_udev,
+	    &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000);
+}
+
+static void
+uchcom_write_reg(struct uchcom_softc *sc,
+    uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
+{
+	DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
+	    (unsigned)reg1, (unsigned)val1,
+	    (unsigned)reg2, (unsigned)val2);
+	uchcom_ctrl_write(
+	    sc, UCHCOM_REQ_WRITE_REG,
+	    reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8));
+}
+
+static void
+uchcom_read_reg(struct uchcom_softc *sc,
+    uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
+{
+	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
+
+	uchcom_ctrl_read(
+	    sc, UCHCOM_REQ_READ_REG,
+	    reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf));
+
+	DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n",
+	    (unsigned)reg1, (unsigned)buf[0],
+	    (unsigned)reg2, (unsigned)buf[1]);
+
+	if (rval1)
+		*rval1 = buf[0];
+	if (rval2)
+		*rval2 = buf[1];
+}
+
+static void
+uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
+{
+	uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
+
+	uchcom_ctrl_read(sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf));
+
+	if (rver)
+		*rver = buf[0];
+}
+
+static void
+uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval)
+{
+	uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
+}
+
+static void
+uchcom_set_dtr_rts_10(struct uchcom_softc *sc, uint8_t val)
+{
+	uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
+}
+
+static void
+uchcom_set_dtr_rts_20(struct uchcom_softc *sc, uint8_t val)
+{
+	uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
+}
+
+
+/* ----------------------------------------------------------------------
+ * middle layer
+ */
+
+static void
+uchcom_update_version(struct uchcom_softc *sc)
+{
+	uchcom_get_version(sc, &sc->sc_version);
+}
+
+static void
+uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
+{
+	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
+	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
+
+	cur = ~cur & 0x0F;
+	sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
+}
+
+static void
+uchcom_update_status(struct uchcom_softc *sc)
+{
+	uint8_t cur;
+
+	uchcom_get_status(sc, &cur);
+	uchcom_convert_status(sc, cur);
+}
+
+
+static void
+uchcom_set_dtr_rts(struct uchcom_softc *sc)
+{
+	uint8_t val = 0;
+
+	if (sc->sc_dtr)
+		val |= UCHCOM_DTR_MASK;
+	if (sc->sc_rts)
+		val |= UCHCOM_RTS_MASK;
+
+	if (sc->sc_version < UCHCOM_VER_20)
+		uchcom_set_dtr_rts_10(sc, ~val);
+	else
+		uchcom_set_dtr_rts_20(sc, ~val);
+}
+
+static void
+uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+	uint8_t brk1;
+	uint8_t brk2;
+
+	uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2);
+	if (onoff) {
+		/* on - clear bits */
+		brk1 &= ~UCHCOM_BRK1_MASK;
+		brk2 &= ~UCHCOM_BRK2_MASK;
+	} else {
+		/* off - set bits */
+		brk1 |= UCHCOM_BRK1_MASK;
+		brk2 |= UCHCOM_BRK2_MASK;
+	}
+	uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2);
+}
+
+static int
+uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
+{
+	const struct uchcom_divider_record *rp;
+	uint32_t div;
+	uint32_t rem;
+	uint32_t mod;
+	uint8_t i;
+
+	/* find record */
+	for (i = 0; i != NUM_DIVIDERS; i++) {
+		if (dividers[i].dvr_high >= rate &&
+		    dividers[i].dvr_low <= rate) {
+			rp = &dividers[i];
+			goto found;
+		}
+	}
+	return (-1);
+
+found:
+	dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
+	if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
+		dp->dv_div = rp->dvr_divider.dv_div;
+	else {
+		div = rp->dvr_base_clock / rate;
+		rem = rp->dvr_base_clock % rate;
+		if (div == 0 || div >= 0xFF)
+			return (-1);
+		if ((rem << 1) >= rate)
+			div += 1;
+		dp->dv_div = (uint8_t)-div;
+	}
+
+	mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS;
+	mod = mod + (mod / 2);
+
+	dp->dv_mod = (mod + 0xFF) / 0x100;
+
+	return (0);
+}
+
+static void
+uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate)
+{
+	struct uchcom_divider dv;
+
+	if (uchcom_calc_divider_settings(&dv, rate))
+		return;
+
+	uchcom_write_reg(sc,
+	    UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
+	    UCHCOM_REG_BPS_DIV, dv.dv_div);
+	uchcom_write_reg(sc,
+	    UCHCOM_REG_BPS_MOD, dv.dv_mod,
+	    UCHCOM_REG_BPS_PAD, 0);
+}
+
+/* ----------------------------------------------------------------------
+ * methods for ucom
+ */
+static void
+uchcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	sc->sc_dtr = onoff;
+	uchcom_set_dtr_rts(sc);
+}
+
+static void
+uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	sc->sc_rts = onoff;
+	uchcom_set_dtr_rts(sc);
+}
+
+static void
+uchcom_cfg_open(struct ucom_softc *ucom)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	uchcom_update_version(sc);
+	uchcom_update_status(sc);
+}
+
+static int
+uchcom_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uchcom_divider dv;
+
+	switch (t->c_cflag & CSIZE) {
+	case CS8:
+		break;
+	default:
+		return (EIO);
+	}
+
+	if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
+		return (EIO);
+	}
+	return (0);			/* success */
+}
+
+static void
+uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	uchcom_get_version(sc, 0);
+	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
+	uchcom_set_baudrate(sc, t->c_ospeed);
+	uchcom_read_reg(sc, 0x18, 0, 0x25, 0);
+	uchcom_write_reg(sc, 0x18, 0x50, 0x25, 0x00);
+	uchcom_update_status(sc);
+	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a);
+	uchcom_set_baudrate(sc, t->c_ospeed);
+	uchcom_set_dtr_rts(sc);
+	uchcom_update_status(sc);
+}
+
+static void
+uchcom_start_read(struct ucom_softc *ucom)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	/* start interrupt endpoint */
+	usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+}
+
+static void
+uchcom_stop_read(struct ucom_softc *ucom)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+}
+
+static void
+uchcom_start_write(struct ucom_softc *ucom)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+}
+
+static void
+uchcom_stop_write(struct ucom_softc *ucom)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+}
+
+/* ----------------------------------------------------------------------
+ * callback when the modem status is changed.
+ */
+static void
+uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[UCHCOM_INTR_LEAST];
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		DPRINTF("actlen = %u\n", actlen);
+
+		if (actlen >= UCHCOM_INTR_LEAST) {
+			pc = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST);
+
+			DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
+			    (unsigned)buf[0], (unsigned)buf[1],
+			    (unsigned)buf[2], (unsigned)buf[3]);
+
+			uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
+			ucom_status_change(&sc->sc_ucom);
+		}
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    usbd_xfer_max_len(xfer), &actlen)) {
+
+			DPRINTF("actlen = %d\n", actlen);
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		if (actlen > 0) {
+			pc = usbd_xfer_get_frame(xfer, 0);
+			ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+		}
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+uchcom_poll(struct ucom_softc *ucom)
+{
+	struct uchcom_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER);
+}
+
+static device_method_t uchcom_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, uchcom_probe),
+	DEVMETHOD(device_attach, uchcom_attach),
+	DEVMETHOD(device_detach, uchcom_detach),
+	DEVMETHOD_END
+};
+
+static driver_t uchcom_driver = {
+	.name = "uchcom",
+	.methods = uchcom_methods,
+	.size = sizeof(struct uchcom_softc)
+};
+
+static devclass_t uchcom_devclass;
+
+DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, 0);
+MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uchcom, usb, 1, 1, 1);
+MODULE_VERSION(uchcom, 1);
+USB_PNP_HOST_INFO(uchcom_devs);
Index: freebsd/sys/dev/usb/serial/ucycom.c
===================================================================
--- freebsd/sys/dev/usb/serial/ucycom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/ucycom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,614 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2004 Dag-Erling CoÃ¯dan SmÃžrgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
+ * RS232 bridges.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define	UCYCOM_MAX_IOLEN	(1024 + 2)	/* bytes */
+
+#define	UCYCOM_IFACE_INDEX	0
+
+enum {
+	UCYCOM_CTRL_RD,
+	UCYCOM_INTR_RD,
+	UCYCOM_N_TRANSFER,
+};
+
+struct ucycom_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_device *sc_udev;
+	struct usb_xfer *sc_xfer[UCYCOM_N_TRANSFER];
+	struct mtx sc_mtx;
+
+	uint32_t sc_model;
+#define	MODEL_CY7C63743		0x63743
+#define	MODEL_CY7C64013		0x64013
+
+	uint16_t sc_flen;		/* feature report length */
+	uint16_t sc_ilen;		/* input report length */
+	uint16_t sc_olen;		/* output report length */
+
+	uint8_t	sc_fid;			/* feature report id */
+	uint8_t	sc_iid;			/* input report id */
+	uint8_t	sc_oid;			/* output report id */
+	uint8_t	sc_cfg;
+#define	UCYCOM_CFG_RESET	0x80
+#define	UCYCOM_CFG_PARODD	0x20
+#define	UCYCOM_CFG_PAREN	0x10
+#define	UCYCOM_CFG_STOPB	0x08
+#define	UCYCOM_CFG_DATAB	0x03
+	uint8_t	sc_ist;			/* status flags from last input */
+	uint8_t	sc_iface_no;
+	uint8_t	sc_temp_cfg[32];
+};
+
+/* prototypes */
+
+static device_probe_t ucycom_probe;
+static device_attach_t ucycom_attach;
+static device_detach_t ucycom_detach;
+static void ucycom_free_softc(struct ucycom_softc *);
+
+static usb_callback_t ucycom_ctrl_write_callback;
+static usb_callback_t ucycom_intr_read_callback;
+
+static void	ucycom_free(struct ucom_softc *);
+static void	ucycom_cfg_open(struct ucom_softc *);
+static void	ucycom_start_read(struct ucom_softc *);
+static void	ucycom_stop_read(struct ucom_softc *);
+static void	ucycom_start_write(struct ucom_softc *);
+static void	ucycom_stop_write(struct ucom_softc *);
+static void	ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t);
+static int	ucycom_pre_param(struct ucom_softc *, struct termios *);
+static void	ucycom_cfg_param(struct ucom_softc *, struct termios *);
+static void	ucycom_poll(struct ucom_softc *ucom);
+
+static const struct usb_config ucycom_config[UCYCOM_N_TRANSFER] = {
+
+	[UCYCOM_CTRL_RD] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,	/* Control pipe */
+		.direction = UE_DIR_ANY,
+		.bufsize = (sizeof(struct usb_device_request) + UCYCOM_MAX_IOLEN),
+		.callback = &ucycom_ctrl_write_callback,
+		.timeout = 1000,	/* 1 second */
+	},
+
+	[UCYCOM_INTR_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = UCYCOM_MAX_IOLEN,
+		.callback = &ucycom_intr_read_callback,
+	},
+};
+
+static const struct ucom_callback ucycom_callback = {
+	.ucom_cfg_param = &ucycom_cfg_param,
+	.ucom_cfg_open = &ucycom_cfg_open,
+	.ucom_pre_param = &ucycom_pre_param,
+	.ucom_start_read = &ucycom_start_read,
+	.ucom_stop_read = &ucycom_stop_read,
+	.ucom_start_write = &ucycom_start_write,
+	.ucom_stop_write = &ucycom_stop_write,
+	.ucom_poll = &ucycom_poll,
+	.ucom_free = &ucycom_free,
+};
+
+static device_method_t ucycom_methods[] = {
+	DEVMETHOD(device_probe, ucycom_probe),
+	DEVMETHOD(device_attach, ucycom_attach),
+	DEVMETHOD(device_detach, ucycom_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t ucycom_devclass;
+
+static driver_t ucycom_driver = {
+	.name = "ucycom",
+	.methods = ucycom_methods,
+	.size = sizeof(struct ucycom_softc),
+};
+
+/*
+ * Supported devices
+ */
+static const STRUCT_USB_HOST_ID ucycom_devs[] = {
+	{USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)},
+};
+
+DRIVER_MODULE(ucycom, uhub, ucycom_driver, ucycom_devclass, NULL, 0);
+MODULE_DEPEND(ucycom, ucom, 1, 1, 1);
+MODULE_DEPEND(ucycom, usb, 1, 1, 1);
+MODULE_VERSION(ucycom, 1);
+USB_PNP_HOST_INFO(ucycom_devs);
+
+#define	UCYCOM_DEFAULT_RATE	 4800
+#define	UCYCOM_DEFAULT_CFG	 0x03	/* N-8-1 */
+
+static int
+ucycom_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != 0) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa));
+}
+
+static int
+ucycom_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct ucycom_softc *sc = device_get_softc(dev);
+	void *urd_ptr = NULL;
+	int32_t error;
+	uint16_t urd_len;
+	uint8_t iface_index;
+
+	sc->sc_udev = uaa->device;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "ucycom", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	DPRINTF("\n");
+
+	/* get chip model */
+	sc->sc_model = USB_GET_DRIVER_INFO(uaa);
+	if (sc->sc_model == 0) {
+		device_printf(dev, "unsupported device\n");
+		goto detach;
+	}
+	device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
+
+	/* get report descriptor */
+
+	error = usbd_req_get_hid_desc(uaa->device, NULL,
+	    &urd_ptr, &urd_len, M_USBDEV,
+	    UCYCOM_IFACE_INDEX);
+
+	if (error) {
+		device_printf(dev, "failed to get report "
+		    "descriptor: %s\n",
+		    usbd_errstr(error));
+		goto detach;
+	}
+	/* get report sizes */
+
+	sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid);
+	sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid);
+	sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid);
+
+	if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) ||
+	    (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) ||
+	    (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) {
+		device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n",
+		    sc->sc_ilen, sc->sc_olen, sc->sc_flen,
+		    UCYCOM_MAX_IOLEN);
+		goto detach;
+	}
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+
+	iface_index = UCYCOM_IFACE_INDEX;
+	error = usbd_transfer_setup(uaa->device, &iface_index,
+	    sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER,
+	    sc, &sc->sc_mtx);
+	if (error) {
+		device_printf(dev, "allocating USB "
+		    "transfers failed\n");
+		goto detach;
+	}
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &ucycom_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	if (urd_ptr) {
+		free(urd_ptr, M_USBDEV);
+	}
+
+	return (0);			/* success */
+
+detach:
+	if (urd_ptr) {
+		free(urd_ptr, M_USBDEV);
+	}
+	ucycom_detach(dev);
+	return (ENXIO);
+}
+
+static int
+ucycom_detach(device_t dev)
+{
+	struct ucycom_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	ucycom_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(ucycom);
+
+static void
+ucycom_free_softc(struct ucycom_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+ucycom_free(struct ucom_softc *ucom)
+{
+	ucycom_free_softc(ucom->sc_parent);
+}
+
+static void
+ucycom_cfg_open(struct ucom_softc *ucom)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+
+	/* set default configuration */
+	ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
+}
+
+static void
+ucycom_start_read(struct ucom_softc *ucom)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]);
+}
+
+static void
+ucycom_stop_read(struct ucom_softc *ucom)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]);
+}
+
+static void
+ucycom_start_write(struct ucom_softc *ucom)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]);
+}
+
+static void
+ucycom_stop_write(struct ucom_softc *ucom)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]);
+}
+
+static void
+ucycom_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ucycom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_device_request req;
+	struct usb_page_cache *pc0, *pc1;
+	uint8_t data[2];
+	uint8_t offset;
+	uint32_t actlen;
+
+	pc0 = usbd_xfer_get_frame(xfer, 0);
+	pc1 = usbd_xfer_get_frame(xfer, 1);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+tr_transferred:
+	case USB_ST_SETUP:
+
+		switch (sc->sc_model) {
+		case MODEL_CY7C63743:
+			offset = 1;
+			break;
+		case MODEL_CY7C64013:
+			offset = 2;
+			break;
+		default:
+			offset = 0;
+			break;
+		}
+
+		if (ucom_get_data(&sc->sc_ucom, pc1, offset,
+		    sc->sc_olen - offset, &actlen)) {
+
+			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+			req.bRequest = UR_SET_REPORT;
+			USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid);
+			req.wIndex[0] = sc->sc_iface_no;
+			req.wIndex[1] = 0;
+			USETW(req.wLength, sc->sc_olen);
+
+			switch (sc->sc_model) {
+			case MODEL_CY7C63743:
+				data[0] = actlen;
+				break;
+			case MODEL_CY7C64013:
+				data[0] = 0;
+				data[1] = actlen;
+				break;
+			default:
+				break;
+			}
+
+			usbd_copy_in(pc0, 0, &req, sizeof(req));
+			usbd_copy_in(pc1, 0, data, offset);
+
+			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+			usbd_xfer_set_frame_len(xfer, 1, sc->sc_olen);
+			usbd_xfer_set_frames(xfer, sc->sc_olen ? 2 : 1);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error == USB_ERR_CANCELLED) {
+			return;
+		}
+		DPRINTF("error=%s\n",
+		    usbd_errstr(error));
+		goto tr_transferred;
+	}
+}
+
+static void
+ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
+{
+	struct usb_device_request req;
+	uint16_t len;
+	usb_error_t err;
+
+	len = sc->sc_flen;
+	if (len > sizeof(sc->sc_temp_cfg)) {
+		len = sizeof(sc->sc_temp_cfg);
+	}
+	sc->sc_cfg = cfg;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UR_SET_REPORT;
+	USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid);
+	req.wIndex[0] = sc->sc_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, len);
+
+	sc->sc_temp_cfg[0] = (baud & 0xff);
+	sc->sc_temp_cfg[1] = (baud >> 8) & 0xff;
+	sc->sc_temp_cfg[2] = (baud >> 16) & 0xff;
+	sc->sc_temp_cfg[3] = (baud >> 24) & 0xff;
+	sc->sc_temp_cfg[4] = cfg;
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+	    &req, sc->sc_temp_cfg, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "device request failed, err=%s "
+		    "(ignored)\n", usbd_errstr(err));
+	}
+}
+
+static int
+ucycom_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	switch (t->c_ospeed) {
+		case 600:
+		case 1200:
+		case 2400:
+		case 4800:
+		case 9600:
+		case 19200:
+		case 38400:
+		case 57600:
+#if 0
+		/*
+		 * Stock chips only support standard baud rates in the 600 - 57600
+		 * range, but higher rates can be achieved using custom firmware.
+		 */
+		case 115200:
+		case 153600:
+		case 192000:
+#endif
+		break;
+	default:
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static void
+ucycom_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+	uint8_t cfg;
+
+	DPRINTF("\n");
+
+	if (t->c_cflag & CIGNORE) {
+		cfg = sc->sc_cfg;
+	} else {
+		cfg = 0;
+		switch (t->c_cflag & CSIZE) {
+		default:
+		case CS8:
+			++cfg;
+		case CS7:
+			++cfg;
+		case CS6:
+			++cfg;
+		case CS5:
+			break;
+		}
+
+		if (t->c_cflag & CSTOPB)
+			cfg |= UCYCOM_CFG_STOPB;
+		if (t->c_cflag & PARENB)
+			cfg |= UCYCOM_CFG_PAREN;
+		if (t->c_cflag & PARODD)
+			cfg |= UCYCOM_CFG_PARODD;
+	}
+
+	ucycom_cfg_write(sc, t->c_ospeed, cfg);
+}
+
+static void
+ucycom_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ucycom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[2];
+	uint32_t offset;
+	int len;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+	pc = usbd_xfer_get_frame(xfer, 0);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		switch (sc->sc_model) {
+		case MODEL_CY7C63743:
+			if (actlen < 1) {
+				goto tr_setup;
+			}
+			usbd_copy_out(pc, 0, buf, 1);
+
+			sc->sc_ist = buf[0] & ~0x07;
+			len = buf[0] & 0x07;
+
+			actlen--;
+			offset = 1;
+
+			break;
+
+		case MODEL_CY7C64013:
+			if (actlen < 2) {
+				goto tr_setup;
+			}
+			usbd_copy_out(pc, 0, buf, 2);
+
+			sc->sc_ist = buf[0] & ~0x07;
+			len = buf[1];
+
+			actlen -= 2;
+			offset = 2;
+
+			break;
+
+		default:
+			DPRINTFN(0, "unsupported model number\n");
+			goto tr_setup;
+		}
+
+		if (len > actlen)
+			len = actlen;
+		if (len)
+			ucom_put_data(&sc->sc_ucom, pc, offset, len);
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, sc->sc_ilen);
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+ucycom_poll(struct ucom_softc *ucom)
+{
+	struct ucycom_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UCYCOM_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/ufoma.c
===================================================================
--- freebsd/sys/dev/usb/serial/ufoma.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/ufoma.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,1270 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $	*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#define UFOMA_HANDSFREE
+/*-
+ * Copyright (c) 2005, Takanori Watanabe
+ * Copyright (c) 2003, M. Warner Losh <imp@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Implement a Call Device for modems without multiplexed commands.
+ */
+
+/*
+ * NOTE: all function names beginning like "ufoma_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/sbuf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+typedef struct ufoma_mobile_acm_descriptor {
+	uint8_t	bFunctionLength;
+	uint8_t	bDescriptorType;
+	uint8_t	bDescriptorSubtype;
+	uint8_t	bType;
+	uint8_t	bMode[1];
+} __packed usb_mcpc_acm_descriptor;
+
+#define	UISUBCLASS_MCPC 0x88
+
+#define	UDESC_VS_INTERFACE 0x44
+#define	UDESCSUB_MCPC_ACM  0x11
+
+#define	UMCPC_ACM_TYPE_AB1 0x1
+#define	UMCPC_ACM_TYPE_AB2 0x2
+#define	UMCPC_ACM_TYPE_AB5 0x5
+#define	UMCPC_ACM_TYPE_AB6 0x6
+
+#define	UMCPC_ACM_MODE_DEACTIVATED 0x0
+#define	UMCPC_ACM_MODE_MODEM 0x1
+#define	UMCPC_ACM_MODE_ATCOMMAND 0x2
+#define	UMCPC_ACM_MODE_OBEX 0x60
+#define	UMCPC_ACM_MODE_VENDOR1 0xc0
+#define	UMCPC_ACM_MODE_VENDOR2 0xfe
+#define	UMCPC_ACM_MODE_UNLINKED 0xff
+
+#define	UMCPC_CM_MOBILE_ACM 0x0
+
+#define	UMCPC_ACTIVATE_MODE 0x60
+#define	UMCPC_GET_MODETABLE 0x61
+#define	UMCPC_SET_LINK 0x62
+#define	UMCPC_CLEAR_LINK 0x63
+
+#define	UMCPC_REQUEST_ACKNOWLEDGE 0x31
+
+#define	UFOMA_MAX_TIMEOUT 15		/* standard says 10 seconds */
+#define	UFOMA_CMD_BUF_SIZE 64		/* bytes */
+
+#define	UFOMA_BULK_BUF_SIZE 1024	/* bytes */
+
+enum {
+	UFOMA_CTRL_ENDPT_INTR,
+	UFOMA_CTRL_ENDPT_READ,
+	UFOMA_CTRL_ENDPT_WRITE,
+	UFOMA_CTRL_ENDPT_MAX,
+};
+
+enum {
+	UFOMA_BULK_ENDPT_WRITE,
+	UFOMA_BULK_ENDPT_READ,	
+	UFOMA_BULK_ENDPT_MAX,
+};
+
+struct ufoma_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+	struct cv sc_cv;
+	struct mtx sc_mtx;
+
+	struct usb_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX];
+	struct usb_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX];
+	uint8_t *sc_modetable;
+	device_t sc_dev;
+	struct usb_device *sc_udev;
+
+	uint32_t sc_unit;
+
+	uint16_t sc_line;
+
+	uint8_t	sc_num_msg;
+	uint8_t	sc_nobulk;
+	uint8_t	sc_ctrl_iface_no;
+	uint8_t	sc_ctrl_iface_index;
+	uint8_t	sc_data_iface_no;
+	uint8_t	sc_data_iface_index;
+	uint8_t	sc_cm_cap;
+	uint8_t	sc_acm_cap;
+	uint8_t	sc_lsr;
+	uint8_t	sc_msr;
+	uint8_t	sc_modetoactivate;
+	uint8_t	sc_currentmode;
+};
+
+/* prototypes */
+
+static device_probe_t ufoma_probe;
+static device_attach_t ufoma_attach;
+static device_detach_t ufoma_detach;
+static void ufoma_free_softc(struct ufoma_softc *);
+
+static usb_callback_t ufoma_ctrl_read_callback;
+static usb_callback_t ufoma_ctrl_write_callback;
+static usb_callback_t ufoma_intr_callback;
+static usb_callback_t ufoma_bulk_write_callback;
+static usb_callback_t ufoma_bulk_read_callback;
+
+static void	*ufoma_get_intconf(struct usb_config_descriptor *,
+		    struct usb_interface_descriptor *, uint8_t, uint8_t);
+static void	ufoma_cfg_link_state(struct ufoma_softc *);
+static void	ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t);
+static void	ufoma_free(struct ucom_softc *);
+static void	ufoma_cfg_open(struct ucom_softc *);
+static void	ufoma_cfg_close(struct ucom_softc *);
+static void	ufoma_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	ufoma_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	ufoma_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	ufoma_cfg_set_rts(struct ucom_softc *, uint8_t);
+static int	ufoma_pre_param(struct ucom_softc *, struct termios *);
+static void	ufoma_cfg_param(struct ucom_softc *, struct termios *);
+static int	ufoma_modem_setup(device_t, struct ufoma_softc *,
+		    struct usb_attach_arg *);
+static void	ufoma_start_read(struct ucom_softc *);
+static void	ufoma_stop_read(struct ucom_softc *);
+static void	ufoma_start_write(struct ucom_softc *);
+static void	ufoma_stop_write(struct ucom_softc *);
+static void	ufoma_poll(struct ucom_softc *ucom);
+
+/*sysctl stuff*/
+static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS);
+static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS);
+static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS);
+
+static const struct usb_config
+	ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = {
+
+	[UFOMA_CTRL_ENDPT_INTR] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = sizeof(struct usb_cdc_notification),
+		.callback = &ufoma_intr_callback,
+	},
+
+	[UFOMA_CTRL_ENDPT_READ] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,	/* Control pipe */
+		.direction = UE_DIR_ANY,
+		.bufsize = (sizeof(struct usb_device_request) + UFOMA_CMD_BUF_SIZE),
+		.flags = {.short_xfer_ok = 1,},
+		.callback = &ufoma_ctrl_read_callback,
+		.timeout = 1000,	/* 1 second */
+	},
+
+	[UFOMA_CTRL_ENDPT_WRITE] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,	/* Control pipe */
+		.direction = UE_DIR_ANY,
+		.bufsize = (sizeof(struct usb_device_request) + 1),
+		.callback = &ufoma_ctrl_write_callback,
+		.timeout = 1000,	/* 1 second */
+	},
+};
+
+static const struct usb_config
+	ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = {
+
+	[UFOMA_BULK_ENDPT_WRITE] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UFOMA_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &ufoma_bulk_write_callback,
+	},
+
+	[UFOMA_BULK_ENDPT_READ] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UFOMA_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &ufoma_bulk_read_callback,
+	},
+};
+
+static const struct ucom_callback ufoma_callback = {
+	.ucom_cfg_get_status = &ufoma_cfg_get_status,
+	.ucom_cfg_set_dtr = &ufoma_cfg_set_dtr,
+	.ucom_cfg_set_rts = &ufoma_cfg_set_rts,
+	.ucom_cfg_set_break = &ufoma_cfg_set_break,
+	.ucom_cfg_param = &ufoma_cfg_param,
+	.ucom_cfg_open = &ufoma_cfg_open,
+	.ucom_cfg_close = &ufoma_cfg_close,
+	.ucom_pre_param = &ufoma_pre_param,
+	.ucom_start_read = &ufoma_start_read,
+	.ucom_stop_read = &ufoma_stop_read,
+	.ucom_start_write = &ufoma_start_write,
+	.ucom_stop_write = &ufoma_stop_write,
+	.ucom_poll = &ufoma_poll,
+	.ucom_free = &ufoma_free,
+};
+
+static device_method_t ufoma_methods[] = {
+	/* Device methods */
+	DEVMETHOD(device_probe, ufoma_probe),
+	DEVMETHOD(device_attach, ufoma_attach),
+	DEVMETHOD(device_detach, ufoma_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t ufoma_devclass;
+
+static driver_t ufoma_driver = {
+	.name = "ufoma",
+	.methods = ufoma_methods,
+	.size = sizeof(struct ufoma_softc),
+};
+
+static const STRUCT_USB_HOST_ID ufoma_devs[] = {
+	{USB_IFACE_CLASS(UICLASS_CDC),
+	 USB_IFACE_SUBCLASS(UISUBCLASS_MCPC),},
+};
+
+DRIVER_MODULE(ufoma, uhub, ufoma_driver, ufoma_devclass, NULL, 0);
+MODULE_DEPEND(ufoma, ucom, 1, 1, 1);
+MODULE_DEPEND(ufoma, usb, 1, 1, 1);
+MODULE_VERSION(ufoma, 1);
+USB_PNP_HOST_INFO(ufoma_devs);
+
+static int
+ufoma_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct usb_interface_descriptor *id;
+	struct usb_config_descriptor *cd;
+	usb_mcpc_acm_descriptor *mad;
+	int error;
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return (ENXIO);
+
+	error = usbd_lookup_id_by_uaa(ufoma_devs, sizeof(ufoma_devs), uaa);
+	if (error)
+		return (error);
+
+	id = usbd_get_interface_descriptor(uaa->iface);
+	cd = usbd_get_config_descriptor(uaa->device);
+
+	if (id == NULL || cd == NULL)
+		return (ENXIO);
+
+	mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
+	if (mad == NULL)
+		return (ENXIO);
+
+#ifndef UFOMA_HANDSFREE
+	if ((mad->bType == UMCPC_ACM_TYPE_AB5) ||
+	    (mad->bType == UMCPC_ACM_TYPE_AB6))
+		return (ENXIO);
+#endif
+	return (BUS_PROBE_GENERIC);
+}
+
+static int
+ufoma_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct ufoma_softc *sc = device_get_softc(dev);
+	struct usb_config_descriptor *cd;
+	struct usb_interface_descriptor *id;
+	struct sysctl_ctx_list *sctx;
+	struct sysctl_oid *soid;
+
+	usb_mcpc_acm_descriptor *mad;
+	uint8_t elements;
+	int32_t error;
+
+	sc->sc_udev = uaa->device;
+	sc->sc_dev = dev;
+	sc->sc_unit = device_get_unit(dev);
+
+	mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+	cv_init(&sc->sc_cv, "CWAIT");
+
+	device_set_usb_desc(dev);
+
+	DPRINTF("\n");
+
+	/* setup control transfers */
+
+	cd = usbd_get_config_descriptor(uaa->device);
+	id = usbd_get_interface_descriptor(uaa->iface);
+	sc->sc_ctrl_iface_no = id->bInterfaceNumber;
+	sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex;
+
+	error = usbd_transfer_setup(uaa->device,
+	    &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer,
+	    ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &sc->sc_mtx);
+
+	if (error) {
+		device_printf(dev, "allocating control USB "
+		    "transfers failed\n");
+		goto detach;
+	}
+	mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
+	if (mad == NULL) {
+		goto detach;
+	}
+	if (mad->bFunctionLength < sizeof(*mad)) {
+		device_printf(dev, "invalid MAD descriptor\n");
+		goto detach;
+	}
+	if ((mad->bType == UMCPC_ACM_TYPE_AB5) ||
+	    (mad->bType == UMCPC_ACM_TYPE_AB6)) {
+		sc->sc_nobulk = 1;
+	} else {
+		sc->sc_nobulk = 0;
+		if (ufoma_modem_setup(dev, sc, uaa)) {
+			goto detach;
+		}
+	}
+
+	elements = (mad->bFunctionLength - sizeof(*mad) + 1);
+
+	/* initialize mode variables */
+
+	sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK);
+
+	if (sc->sc_modetable == NULL) {
+		goto detach;
+	}
+	sc->sc_modetable[0] = (elements + 1);
+	memcpy(&sc->sc_modetable[1], mad->bMode, elements);
+
+	sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED;
+	sc->sc_modetoactivate = mad->bMode[0];
+
+	/* clear stall at first run, if any */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+	usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &ufoma_callback, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("ucom_attach failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	/*Sysctls*/
+	sctx = device_get_sysctl_ctx(dev);
+	soid = device_get_sysctl_tree(dev);
+
+	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode",
+			CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support,
+			"A", "Supporting port role");
+
+	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode",
+			CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current,
+			"A", "Current port role");
+
+	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode",
+			CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open,
+			"A", "Mode to transit when port is opened");
+	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit",
+			CTLFLAG_RD, &(sc->sc_super_ucom.sc_unit), 0, 
+			"Unit number as USB serial");
+
+	return (0);			/* success */
+
+detach:
+	ufoma_detach(dev);
+	return (ENXIO);			/* failure */
+}
+
+static int
+ufoma_detach(device_t dev)
+{
+	struct ufoma_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);
+	usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX);
+
+	if (sc->sc_modetable) {
+		free(sc->sc_modetable, M_USBDEV);
+	}
+	cv_destroy(&sc->sc_cv);
+
+	device_claim_softc(dev);
+
+	ufoma_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(ufoma);
+
+static void
+ufoma_free_softc(struct ufoma_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+ufoma_free(struct ucom_softc *ucom)
+{
+	ufoma_free_softc(ucom->sc_parent);
+}
+
+static void *
+ufoma_get_intconf(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id,
+    uint8_t type, uint8_t subtype)
+{
+	struct usb_descriptor *desc = (void *)id;
+
+	while ((desc = usb_desc_foreach(cd, desc))) {
+
+		if (desc->bDescriptorType == UDESC_INTERFACE) {
+			return (NULL);
+		}
+		if ((desc->bDescriptorType == type) &&
+		    (desc->bDescriptorSubtype == subtype)) {
+			break;
+		}
+	}
+	return (desc);
+}
+
+static void
+ufoma_cfg_link_state(struct ufoma_softc *sc)
+{
+	struct usb_device_request req;
+	int32_t error;
+
+	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+	req.bRequest = UMCPC_SET_LINK;
+	USETW(req.wValue, UMCPC_CM_MOBILE_ACM);
+	USETW(req.wIndex, sc->sc_ctrl_iface_no);
+	USETW(req.wLength, sc->sc_modetable[0]);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, sc->sc_modetable, 0, 1000);
+
+	error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz);
+
+	if (error) {
+		DPRINTF("NO response\n");
+	}
+}
+
+static void
+ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state)
+{
+	struct usb_device_request req;
+	int32_t error;
+
+	req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+	req.bRequest = UMCPC_ACTIVATE_MODE;
+	USETW(req.wValue, state);
+	USETW(req.wIndex, sc->sc_ctrl_iface_no);
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+
+	error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx,
+	    (UFOMA_MAX_TIMEOUT * hz));
+	if (error) {
+		DPRINTF("No response\n");
+	}
+}
+
+static void
+ufoma_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ufoma_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_device_request req;
+	struct usb_page_cache *pc0, *pc1;
+	int len, aframes, nframes;
+
+	usbd_xfer_status(xfer, NULL, NULL, &aframes, &nframes);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+tr_transferred:
+		if (aframes != nframes)
+			goto tr_setup;
+		pc1 = usbd_xfer_get_frame(xfer, 1);
+		len = usbd_xfer_frame_len(xfer, 1);
+		if (len > 0)
+			ucom_put_data(&sc->sc_ucom, pc1, 0, len);
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		if (sc->sc_num_msg) {
+			sc->sc_num_msg--;
+
+			req.bmRequestType = UT_READ_CLASS_INTERFACE;
+			req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
+			USETW(req.wIndex, sc->sc_ctrl_iface_no);
+			USETW(req.wValue, 0);
+			USETW(req.wLength, UFOMA_CMD_BUF_SIZE);
+
+			pc0 = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_in(pc0, 0, &req, sizeof(req));
+
+			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+			usbd_xfer_set_frame_len(xfer, 1, UFOMA_CMD_BUF_SIZE);
+			usbd_xfer_set_frames(xfer, 2);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		DPRINTF("error = %s\n",
+		    usbd_errstr(error));
+
+		if (error == USB_ERR_CANCELLED) {
+			return;
+		}
+		goto tr_transferred;
+	}
+}
+
+static void
+ufoma_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ufoma_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_device_request req;
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+tr_transferred:
+	case USB_ST_SETUP:
+		pc = usbd_xfer_get_frame(xfer, 1);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0, 1, &actlen)) {
+
+			req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+			req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
+			USETW(req.wIndex, sc->sc_ctrl_iface_no);
+			USETW(req.wValue, 0);
+			USETW(req.wLength, 1);
+
+			pc = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_in(pc, 0, &req, sizeof(req));
+
+			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+			usbd_xfer_set_frame_len(xfer, 1, 1);
+			usbd_xfer_set_frames(xfer, 2);
+
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		DPRINTF("error = %s\n", usbd_errstr(error));
+
+		if (error == USB_ERR_CANCELLED) {
+			return;
+		}
+		goto tr_transferred;
+	}
+}
+
+static void
+ufoma_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ufoma_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_cdc_notification pkt;
+	struct usb_page_cache *pc;
+	uint16_t wLen;
+	uint16_t temp;
+	uint8_t mstatus;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen < 8) {
+			DPRINTF("too short message\n");
+			goto tr_setup;
+		}
+		if (actlen > (int)sizeof(pkt)) {
+			DPRINTF("truncating message\n");
+			actlen = sizeof(pkt);
+		}
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_out(pc, 0, &pkt, actlen);
+
+		actlen -= 8;
+
+		wLen = UGETW(pkt.wLength);
+		if (actlen > wLen) {
+			actlen = wLen;
+		}
+		if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) &&
+		    (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) {
+			temp = UGETW(pkt.wValue);
+			sc->sc_currentmode = (temp >> 8);
+			if (!(temp & 0xff)) {
+				DPRINTF("Mode change failed!\n");
+			}
+			cv_signal(&sc->sc_cv);
+		}
+		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
+			goto tr_setup;
+		}
+		switch (pkt.bNotification) {
+		case UCDC_N_RESPONSE_AVAILABLE:
+			if (!(sc->sc_nobulk)) {
+				DPRINTF("Wrong serial state!\n");
+				break;
+			}
+			if (sc->sc_num_msg != 0xFF) {
+				sc->sc_num_msg++;
+			}
+			usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+			break;
+
+		case UCDC_N_SERIAL_STATE:
+			if (sc->sc_nobulk) {
+				DPRINTF("Wrong serial state!\n");
+				break;
+			}
+			/*
+		         * Set the serial state in ucom driver based on
+		         * the bits from the notify message
+		         */
+			if (actlen < 2) {
+				DPRINTF("invalid notification "
+				    "length, %d bytes!\n", actlen);
+				break;
+			}
+			DPRINTF("notify bytes = 0x%02x, 0x%02x\n",
+			    pkt.data[0], pkt.data[1]);
+
+			/* currently, lsr is always zero. */
+			sc->sc_lsr = 0;
+			sc->sc_msr = 0;
+
+			mstatus = pkt.data[0];
+
+			if (mstatus & UCDC_N_SERIAL_RI) {
+				sc->sc_msr |= SER_RI;
+			}
+			if (mstatus & UCDC_N_SERIAL_DSR) {
+				sc->sc_msr |= SER_DSR;
+			}
+			if (mstatus & UCDC_N_SERIAL_DCD) {
+				sc->sc_msr |= SER_DCD;
+			}
+			ucom_status_change(&sc->sc_ucom);
+			break;
+
+		default:
+			break;
+		}
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+ufoma_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ufoma_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UFOMA_BULK_BUF_SIZE, &actlen)) {
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+ufoma_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ufoma_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+ufoma_cfg_open(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	/* empty input queue */
+
+	if (sc->sc_num_msg != 0xFF) {
+		sc->sc_num_msg++;
+	}
+	if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) {
+		ufoma_cfg_link_state(sc);
+	}
+	if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) {
+		ufoma_cfg_activate_state(sc, sc->sc_modetoactivate);
+	}
+}
+
+static void
+ufoma_cfg_close(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED);
+}
+
+static void
+ufoma_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t wValue;
+
+	if (sc->sc_nobulk ||
+	    (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
+		return;
+	}
+	if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) {
+		return;
+	}
+	wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SEND_BREAK;
+	USETW(req.wValue, wValue);
+	req.wIndex[0] = sc->sc_ctrl_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+ufoma_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+ufoma_cfg_set_line_state(struct ufoma_softc *sc)
+{
+	struct usb_device_request req;
+
+	/* Don't send line state emulation request for OBEX port */
+	if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) {
+		return;
+	}
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = sc->sc_ctrl_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+ufoma_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	if (sc->sc_nobulk) {
+		return;
+	}
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_DTR;
+	else
+		sc->sc_line &= ~UCDC_LINE_DTR;
+
+	ufoma_cfg_set_line_state(sc);
+}
+
+static void
+ufoma_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	if (sc->sc_nobulk) {
+		return;
+	}
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_RTS;
+	else
+		sc->sc_line &= ~UCDC_LINE_RTS;
+
+	ufoma_cfg_set_line_state(sc);
+}
+
+static int
+ufoma_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	return (0);			/* we accept anything */
+}
+
+static void
+ufoma_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	struct usb_cdc_line_state ls;
+
+	if (sc->sc_nobulk ||
+	    (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
+		return;
+	}
+	DPRINTF("\n");
+
+	memset(&ls, 0, sizeof(ls));
+
+	USETDW(ls.dwDTERate, t->c_ospeed);
+
+	if (t->c_cflag & CSTOPB) {
+		ls.bCharFormat = UCDC_STOP_BIT_2;
+	} else {
+		ls.bCharFormat = UCDC_STOP_BIT_1;
+	}
+
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD) {
+			ls.bParityType = UCDC_PARITY_ODD;
+		} else {
+			ls.bParityType = UCDC_PARITY_EVEN;
+		}
+	} else {
+		ls.bParityType = UCDC_PARITY_NONE;
+	}
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		ls.bDataBits = 5;
+		break;
+	case CS6:
+		ls.bDataBits = 6;
+		break;
+	case CS7:
+		ls.bDataBits = 7;
+		break;
+	case CS8:
+		ls.bDataBits = 8;
+		break;
+	}
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_LINE_CODING;
+	USETW(req.wValue, 0);
+	req.wIndex[0] = sc->sc_ctrl_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, &ls, 0, 1000);
+}
+
+static int
+ufoma_modem_setup(device_t dev, struct ufoma_softc *sc,
+    struct usb_attach_arg *uaa)
+{
+	struct usb_config_descriptor *cd;
+	struct usb_cdc_acm_descriptor *acm;
+	struct usb_cdc_cm_descriptor *cmd;
+	struct usb_interface_descriptor *id;
+	struct usb_interface *iface;
+	uint8_t i;
+	int32_t error;
+
+	cd = usbd_get_config_descriptor(uaa->device);
+	id = usbd_get_interface_descriptor(uaa->iface);
+
+	cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+
+	if ((cmd == NULL) ||
+	    (cmd->bLength < sizeof(*cmd))) {
+		return (EINVAL);
+	}
+	sc->sc_cm_cap = cmd->bmCapabilities;
+	sc->sc_data_iface_no = cmd->bDataInterface;
+
+	acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+
+	if ((acm == NULL) ||
+	    (acm->bLength < sizeof(*acm))) {
+		return (EINVAL);
+	}
+	sc->sc_acm_cap = acm->bmCapabilities;
+
+	device_printf(dev, "data interface %d, has %sCM over data, "
+	    "has %sbreak\n",
+	    sc->sc_data_iface_no,
+	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+	/* get the data interface too */
+
+	for (i = 0;; i++) {
+
+		iface = usbd_get_iface(uaa->device, i);
+
+		if (iface) {
+
+			id = usbd_get_interface_descriptor(iface);
+
+			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
+				sc->sc_data_iface_index = i;
+				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+				break;
+			}
+		} else {
+			device_printf(dev, "no data interface\n");
+			return (EINVAL);
+		}
+	}
+
+	error = usbd_transfer_setup(uaa->device,
+	    &sc->sc_data_iface_index, sc->sc_bulk_xfer,
+	    ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &sc->sc_mtx);
+
+	if (error) {
+		device_printf(dev, "allocating BULK USB "
+		    "transfers failed\n");
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static void
+ufoma_start_read(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	/* start interrupt transfer */
+	usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
+
+	/* start data transfer */
+	if (sc->sc_nobulk) {
+		usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+	} else {
+		usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+	}
+}
+
+static void
+ufoma_stop_read(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt transfer */
+	usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
+
+	/* stop data transfer */
+	if (sc->sc_nobulk) {
+		usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+	} else {
+		usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+	}
+}
+
+static void
+ufoma_start_write(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	if (sc->sc_nobulk) {
+		usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
+	} else {
+		usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+	}
+}
+
+static void
+ufoma_stop_write(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+
+	if (sc->sc_nobulk) {
+		usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
+	} else {
+		usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+	}
+}
+
+static struct umcpc_modetostr_tab{
+	int mode;
+	char *str;
+}umcpc_modetostr_tab[]={
+	{UMCPC_ACM_MODE_DEACTIVATED, "deactivated"},
+	{UMCPC_ACM_MODE_MODEM, "modem"},
+	{UMCPC_ACM_MODE_ATCOMMAND, "handsfree"},
+	{UMCPC_ACM_MODE_OBEX, "obex"},
+	{UMCPC_ACM_MODE_VENDOR1, "vendor1"},
+	{UMCPC_ACM_MODE_VENDOR2, "vendor2"},
+	{UMCPC_ACM_MODE_UNLINKED, "unlinked"},
+	{0, NULL}
+};
+
+static char *ufoma_mode_to_str(int mode)
+{
+	int i;
+	for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
+		if(umcpc_modetostr_tab[i].mode == mode){
+			return umcpc_modetostr_tab[i].str;
+		}
+	}
+	return NULL;
+}
+
+static int ufoma_str_to_mode(char *str)
+{
+	int i;
+	for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
+		if(strcmp(str, umcpc_modetostr_tab[i].str)==0){
+			return umcpc_modetostr_tab[i].mode;
+		}
+	}
+	return -1;
+}
+
+static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS)
+{
+	struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+	struct sbuf sb;
+	int i;
+	char *mode;
+
+	sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND);
+	for(i = 1; i < sc->sc_modetable[0]; i++){
+		mode = ufoma_mode_to_str(sc->sc_modetable[i]);
+		if(mode !=NULL){
+			sbuf_cat(&sb, mode);
+		}else{
+			sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]);
+		}
+		if(i < (sc->sc_modetable[0]-1))
+			sbuf_cat(&sb, ",");
+	}
+	sbuf_trim(&sb);
+	sbuf_finish(&sb);
+	sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+	sbuf_delete(&sb);
+	
+	return 0;
+}
+static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS)
+{
+	struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+	char *mode;
+	char subbuf[]="(XXX)";
+	mode = ufoma_mode_to_str(sc->sc_currentmode);
+	if(!mode){
+		mode = subbuf;
+		snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode);
+	}
+	sysctl_handle_string(oidp, mode, strlen(mode), req);
+	
+	return 0;
+	
+}
+static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS)
+{
+	struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+	char *mode;
+	char subbuf[40];
+	int newmode;
+	int error;
+	int i;
+
+	mode = ufoma_mode_to_str(sc->sc_modetoactivate);
+	if(mode){
+		strncpy(subbuf, mode, sizeof(subbuf));
+	}else{
+		snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate);
+	}
+	error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req);
+	if(error != 0 || req->newptr == NULL){
+		return error;
+	}
+	
+	if((newmode = ufoma_str_to_mode(subbuf)) == -1){
+		return EINVAL;
+	}
+	
+	for(i = 1 ; i < sc->sc_modetable[0] ; i++){
+		if(sc->sc_modetable[i] == newmode){
+			sc->sc_modetoactivate = newmode;
+			return 0;
+		}
+	}
+	
+	return EINVAL;
+}
+
+static void
+ufoma_poll(struct ucom_softc *ucom)
+{
+	struct ufoma_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);
+	usbd_transfer_poll(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX);
+}
Index: freebsd/sys/dev/usb/serial/uftdi.c
===================================================================
--- freebsd/sys/dev/usb/serial/uftdi.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uftdi.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,2011 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $	*/
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * NOTE: all function names beginning like "uftdi_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+/*
+ * FTDI FT232x, FT2232x, FT4232x, FT8U100AX and FT8U232xM serial adapters.
+ *
+ * Note that we specifically do not do a reset or otherwise alter the state of
+ * the chip during attach, detach, open, and close, because it could be
+ * pre-initialized (via an attached serial eeprom) to power-on into a mode such
+ * as bitbang in which the pins are being driven to a specific state which we
+ * must not perturb.  The device gets reset at power-on, and doesn't need to be
+ * reset again after that to function, except as directed by ioctl() calls.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_ioctl.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR uftdi_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+#include <dev/usb/serial/uftdi_reg.h>
+#include <dev/usb/uftdiio.h>
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
+
+#ifdef USB_DEBUG
+static int uftdi_debug = 0;
+SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &uftdi_debug, 0, "Debug level");
+#endif
+
+#define	UFTDI_CONFIG_INDEX	0
+
+/*
+ * IO buffer sizes and FTDI device procotol sizes.
+ *
+ * Note that the output packet size in the following defines is not the usb
+ * protocol packet size based on bus speed, it is the size dictated by the FTDI
+ * device itself, and is used only on older chips.
+ *
+ * We allocate buffers bigger than the hardware's packet size, and process
+ * multiple packets within each buffer.  This allows the controller to make
+ * optimal use of the usb bus by conducting multiple transfers with the device
+ * during a single bus timeslice to fill or drain the chip's fifos.
+ *
+ * The output data on newer chips has no packet header, and we are able to pack
+ * any number of output bytes into a buffer.  On some older chips, each output
+ * packet contains a 1-byte header and up to 63 bytes of payload.  The size is
+ * encoded in 6 bits of the header, hence the 64-byte limit on packet size.  We
+ * loop to fill the buffer with many of these header+payload packets.
+ *
+ * The input data on all chips consists of packets which contain a 2-byte header
+ * followed by data payload.  The total size of the packet is wMaxPacketSize
+ * which can change based on the bus speed (e.g., 64 for full speed, 512 for
+ * high speed).  We loop to extract the headers and payloads from the packets
+ * packed into an input buffer.
+ */
+#define	UFTDI_IBUFSIZE	2048
+#define	UFTDI_IHDRSIZE	   2
+#define	UFTDI_OBUFSIZE	2048
+#define	UFTDI_OPKTSIZE	  64
+
+enum {
+	UFTDI_BULK_DT_WR,
+	UFTDI_BULK_DT_RD,
+	UFTDI_N_TRANSFER,
+};
+
+enum {
+	DEVT_SIO,
+	DEVT_232A,
+	DEVT_232B,
+	DEVT_2232D,	/* Includes 2232C */
+	DEVT_232R,
+	DEVT_2232H,
+	DEVT_4232H,
+	DEVT_232H,
+	DEVT_230X,
+};
+
+#define	DEVF_BAUDBITS_HINDEX	0x01	/* Baud bits in high byte of index. */
+#define	DEVF_BAUDCLK_12M	0X02	/* Base baud clock is 12MHz. */
+
+struct uftdi_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_device *sc_udev;
+	struct usb_xfer *sc_xfer[UFTDI_N_TRANSFER];
+	device_t sc_dev;
+	struct mtx sc_mtx;
+
+	uint32_t sc_unit;
+
+	uint16_t sc_last_lcr;
+	uint16_t sc_bcdDevice;
+
+	uint8_t sc_devtype;
+	uint8_t sc_devflags;
+	uint8_t	sc_hdrlen;
+	uint8_t	sc_msr;
+	uint8_t	sc_lsr;
+	uint8_t sc_bitmode;
+};
+
+struct uftdi_param_config {
+	uint16_t baud_lobits;
+	uint16_t baud_hibits;
+	uint16_t lcr;
+	uint8_t	v_start;
+	uint8_t	v_stop;
+	uint8_t	v_flow;
+};
+
+/* prototypes */
+
+static device_probe_t uftdi_probe;
+static device_attach_t uftdi_attach;
+static device_detach_t uftdi_detach;
+static void uftdi_free_softc(struct uftdi_softc *);
+
+static usb_callback_t uftdi_write_callback;
+static usb_callback_t uftdi_read_callback;
+
+static void	uftdi_free(struct ucom_softc *);
+static void	uftdi_cfg_open(struct ucom_softc *);
+static void	uftdi_cfg_close(struct ucom_softc *);
+static void	uftdi_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	uftdi_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	uftdi_cfg_set_break(struct ucom_softc *, uint8_t);
+static int	uftdi_set_parm_soft(struct ucom_softc *, struct termios *,
+		    struct uftdi_param_config *);
+static int	uftdi_pre_param(struct ucom_softc *, struct termios *);
+static void	uftdi_cfg_param(struct ucom_softc *, struct termios *);
+static void	uftdi_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static int	uftdi_reset(struct ucom_softc *, int);
+static int	uftdi_set_bitmode(struct ucom_softc *, uint8_t, uint8_t);
+static int	uftdi_get_bitmode(struct ucom_softc *, uint8_t *, uint8_t *);
+static int	uftdi_set_latency(struct ucom_softc *, int);
+static int	uftdi_get_latency(struct ucom_softc *, int *);
+static int	uftdi_set_event_char(struct ucom_softc *, int);
+static int	uftdi_set_error_char(struct ucom_softc *, int);
+static int	uftdi_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
+		    struct thread *);
+static void	uftdi_start_read(struct ucom_softc *);
+static void	uftdi_stop_read(struct ucom_softc *);
+static void	uftdi_start_write(struct ucom_softc *);
+static void	uftdi_stop_write(struct ucom_softc *);
+static void	uftdi_poll(struct ucom_softc *ucom);
+
+static const struct usb_config uftdi_config[UFTDI_N_TRANSFER] = {
+
+	[UFTDI_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UFTDI_OBUFSIZE,
+		.flags = {.pipe_bof = 1,},
+		.callback = &uftdi_write_callback,
+	},
+
+	[UFTDI_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UFTDI_IBUFSIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uftdi_read_callback,
+	},
+};
+
+static const struct ucom_callback uftdi_callback = {
+	.ucom_cfg_get_status = &uftdi_cfg_get_status,
+	.ucom_cfg_set_dtr = &uftdi_cfg_set_dtr,
+	.ucom_cfg_set_rts = &uftdi_cfg_set_rts,
+	.ucom_cfg_set_break = &uftdi_cfg_set_break,
+	.ucom_cfg_param = &uftdi_cfg_param,
+	.ucom_cfg_open = &uftdi_cfg_open,
+	.ucom_cfg_close = &uftdi_cfg_close,
+	.ucom_pre_param = &uftdi_pre_param,
+	.ucom_ioctl = &uftdi_ioctl,
+	.ucom_start_read = &uftdi_start_read,
+	.ucom_stop_read = &uftdi_stop_read,
+	.ucom_start_write = &uftdi_start_write,
+	.ucom_stop_write = &uftdi_stop_write,
+	.ucom_poll = &uftdi_poll,
+	.ucom_free = &uftdi_free,
+};
+
+static device_method_t uftdi_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, uftdi_probe),
+	DEVMETHOD(device_attach, uftdi_attach),
+	DEVMETHOD(device_detach, uftdi_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uftdi_devclass;
+
+static driver_t uftdi_driver = {
+	.name = "uftdi",
+	.methods = uftdi_methods,
+	.size = sizeof(struct uftdi_softc),
+};
+
+static const STRUCT_USB_HOST_ID uftdi_devs[] = {
+#define	UFTDI_DEV(v, p, i) \
+  { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+	UFTDI_DEV(ACTON, SPECTRAPRO, 0),
+	UFTDI_DEV(ALTI2, N3, 0),
+	UFTDI_DEV(ANALOGDEVICES, GNICE, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(ANALOGDEVICES, GNICEPLUS, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(ATMEL, STK541, 0),
+	UFTDI_DEV(BAYER, CONTOUR_CABLE, 0),
+	UFTDI_DEV(BBELECTRONICS, 232USB9M, 0),
+	UFTDI_DEV(BBELECTRONICS, 485USB9F_2W, 0),
+	UFTDI_DEV(BBELECTRONICS, 485USB9F_4W, 0),
+	UFTDI_DEV(BBELECTRONICS, 485USBTB_2W, 0),
+	UFTDI_DEV(BBELECTRONICS, 485USBTB_4W, 0),
+	UFTDI_DEV(BBELECTRONICS, TTL3USB9M, 0),
+	UFTDI_DEV(BBELECTRONICS, TTL5USB9M, 0),
+	UFTDI_DEV(BBELECTRONICS, USO9ML2, 0),
+	UFTDI_DEV(BBELECTRONICS, USO9ML2DR, 0),
+	UFTDI_DEV(BBELECTRONICS, USO9ML2DR_2, 0),
+	UFTDI_DEV(BBELECTRONICS, USOPTL4, 0),
+	UFTDI_DEV(BBELECTRONICS, USOPTL4DR, 0),
+	UFTDI_DEV(BBELECTRONICS, USOPTL4DR2, 0),
+	UFTDI_DEV(BBELECTRONICS, USOTL4, 0),
+	UFTDI_DEV(BBELECTRONICS, USPTL4, 0),
+	UFTDI_DEV(BBELECTRONICS, USTL4, 0),
+	UFTDI_DEV(BBELECTRONICS, ZZ_PROG1_USB, 0),
+	UFTDI_DEV(CONTEC, COM1USBH, 0),
+	UFTDI_DEV(DRESDENELEKTRONIK, SENSORTERMINALBOARD, 0),
+	UFTDI_DEV(DRESDENELEKTRONIK, WIRELESSHANDHELDTERMINAL, 0),
+	UFTDI_DEV(DRESDENELEKTRONIK, DE_RFNODE, 0),
+	UFTDI_DEV(DRESDENELEKTRONIK, LEVELSHIFTERSTICKLOWCOST, 0),
+	UFTDI_DEV(ELEKTOR, FT323R, 0),
+	UFTDI_DEV(EVOLUTION, ER1, 0),
+	UFTDI_DEV(EVOLUTION, HYBRID, 0),
+	UFTDI_DEV(EVOLUTION, RCM4, 0),
+	UFTDI_DEV(FALCOM, SAMBA, 0),
+	UFTDI_DEV(FALCOM, TWIST, 0),
+	UFTDI_DEV(FIC, NEO1973_DEBUG, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FIC, NEO1973_DEBUG, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, 232EX, 0),
+	UFTDI_DEV(FTDI, 232H, 0),
+	UFTDI_DEV(FTDI, 232RL, 0),
+	UFTDI_DEV(FTDI, 4N_GALAXY_DE_1, 0),
+	UFTDI_DEV(FTDI, 4N_GALAXY_DE_2, 0),
+	UFTDI_DEV(FTDI, 4N_GALAXY_DE_3, 0),
+	UFTDI_DEV(FTDI, 8U232AM_ALT, 0),
+	UFTDI_DEV(FTDI, ACCESSO, 0),
+	UFTDI_DEV(FTDI, ACG_HFDUAL, 0),
+	UFTDI_DEV(FTDI, ACTIVE_ROBOTS, 0),
+	UFTDI_DEV(FTDI, ACTZWAVE, 0),
+	UFTDI_DEV(FTDI, AMC232, 0),
+	UFTDI_DEV(FTDI, ARTEMIS, 0),
+	UFTDI_DEV(FTDI, ASK_RDR400, 0),
+	UFTDI_DEV(FTDI, ATIK_ATK16, 0),
+	UFTDI_DEV(FTDI, ATIK_ATK16C, 0),
+	UFTDI_DEV(FTDI, ATIK_ATK16HR, 0),
+	UFTDI_DEV(FTDI, ATIK_ATK16HRC, 0),
+	UFTDI_DEV(FTDI, ATIK_ATK16IC, 0),
+	UFTDI_DEV(FTDI, BCS_SE923, 0),
+	UFTDI_DEV(FTDI, CANDAPTER, 0),
+	UFTDI_DEV(FTDI, CANUSB, 0),
+	UFTDI_DEV(FTDI, CCSICDU20_0, 0),
+	UFTDI_DEV(FTDI, CCSICDU40_1, 0),
+	UFTDI_DEV(FTDI, CCSICDU64_4, 0),
+	UFTDI_DEV(FTDI, CCSLOAD_N_GO_3, 0),
+	UFTDI_DEV(FTDI, CCSMACHX_2, 0),
+	UFTDI_DEV(FTDI, CCSPRIME8_5, 0),
+	UFTDI_DEV(FTDI, CFA_631, 0),
+	UFTDI_DEV(FTDI, CFA_632, 0),
+	UFTDI_DEV(FTDI, CFA_633, 0),
+	UFTDI_DEV(FTDI, CFA_634, 0),
+	UFTDI_DEV(FTDI, CFA_635, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_24_MASTER_WING, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_MAXI_WING, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_MEDIA_WING, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_MIDI_TIMECODE, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_MINI_WING, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_PC_WING, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_USB_DMX, 0),
+	UFTDI_DEV(FTDI, CHAMSYS_WING, 0),
+	UFTDI_DEV(FTDI, COM4SM, 0),
+	UFTDI_DEV(FTDI, CONVERTER_0, 0),
+	UFTDI_DEV(FTDI, CONVERTER_1, 0),
+	UFTDI_DEV(FTDI, CONVERTER_2, 0),
+	UFTDI_DEV(FTDI, CONVERTER_3, 0),
+	UFTDI_DEV(FTDI, CONVERTER_4, 0),
+	UFTDI_DEV(FTDI, CONVERTER_5, 0),
+	UFTDI_DEV(FTDI, CONVERTER_6, 0),
+	UFTDI_DEV(FTDI, CONVERTER_7, 0),
+	UFTDI_DEV(FTDI, CTI_USB_MINI_485, 0),
+	UFTDI_DEV(FTDI, CTI_USB_NANO_485, 0),
+	UFTDI_DEV(FTDI, DMX4ALL, 0),
+	UFTDI_DEV(FTDI, DOMINTELL_DGQG, 0),
+	UFTDI_DEV(FTDI, DOMINTELL_DUSB, 0),
+	UFTDI_DEV(FTDI, DOTEC, 0),
+	UFTDI_DEV(FTDI, ECLO_COM_1WIRE, 0),
+	UFTDI_DEV(FTDI, ECO_PRO_CDS, 0),
+	UFTDI_DEV(FTDI, EISCOU, 0),
+	UFTDI_DEV(FTDI, ELSTER_UNICOM, 0),
+	UFTDI_DEV(FTDI, ELV_ALC8500, 0),
+	UFTDI_DEV(FTDI, ELV_CLI7000, 0),
+	UFTDI_DEV(FTDI, ELV_CSI8, 0),
+	UFTDI_DEV(FTDI, ELV_EC3000, 0),
+	UFTDI_DEV(FTDI, ELV_EM1000DL, 0),
+	UFTDI_DEV(FTDI, ELV_EM1010PC, 0),
+	UFTDI_DEV(FTDI, ELV_FEM, 0),
+	UFTDI_DEV(FTDI, ELV_FHZ1000PC, 0),
+	UFTDI_DEV(FTDI, ELV_FHZ1300PC, 0),
+	UFTDI_DEV(FTDI, ELV_FM3RX, 0),
+	UFTDI_DEV(FTDI, ELV_FS20SIG, 0),
+	UFTDI_DEV(FTDI, ELV_HS485, 0),
+	UFTDI_DEV(FTDI, ELV_KL100, 0),
+	UFTDI_DEV(FTDI, ELV_MSM1, 0),
+	UFTDI_DEV(FTDI, ELV_PCD200, 0),
+	UFTDI_DEV(FTDI, ELV_PCK100, 0),
+	UFTDI_DEV(FTDI, ELV_PPS7330, 0),
+	UFTDI_DEV(FTDI, ELV_RFP500, 0),
+	UFTDI_DEV(FTDI, ELV_T1100, 0),
+	UFTDI_DEV(FTDI, ELV_TFD128, 0),
+	UFTDI_DEV(FTDI, ELV_TFM100, 0),
+	UFTDI_DEV(FTDI, ELV_TWS550, 0),
+	UFTDI_DEV(FTDI, ELV_UAD8, 0),
+	UFTDI_DEV(FTDI, ELV_UDA7, 0),
+	UFTDI_DEV(FTDI, ELV_UDF77, 0),
+	UFTDI_DEV(FTDI, ELV_UIO88, 0),
+	UFTDI_DEV(FTDI, ELV_ULA200, 0),
+	UFTDI_DEV(FTDI, ELV_UM100, 0),
+	UFTDI_DEV(FTDI, ELV_UMS100, 0),
+	UFTDI_DEV(FTDI, ELV_UO100, 0),
+	UFTDI_DEV(FTDI, ELV_UR100, 0),
+	UFTDI_DEV(FTDI, ELV_USI2, 0),
+	UFTDI_DEV(FTDI, ELV_USR, 0),
+	UFTDI_DEV(FTDI, ELV_UTP8, 0),
+	UFTDI_DEV(FTDI, ELV_WS300PC, 0),
+	UFTDI_DEV(FTDI, ELV_WS444PC, 0),
+	UFTDI_DEV(FTDI, ELV_WS500, 0),
+	UFTDI_DEV(FTDI, ELV_WS550, 0),
+	UFTDI_DEV(FTDI, ELV_WS777, 0),
+	UFTDI_DEV(FTDI, ELV_WS888, 0),
+	UFTDI_DEV(FTDI, EMCU2D, 0),
+	UFTDI_DEV(FTDI, EMCU2H, 0),
+	UFTDI_DEV(FTDI, FUTURE_0, 0),
+	UFTDI_DEV(FTDI, FUTURE_1, 0),
+	UFTDI_DEV(FTDI, FUTURE_2, 0),
+	UFTDI_DEV(FTDI, GAMMASCOUT, 0),
+	UFTDI_DEV(FTDI, GENERIC, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E808, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E809, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E80A, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E80B, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E80C, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E80D, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E80E, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E80F, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E88D, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E88E, 0),
+	UFTDI_DEV(FTDI, GUDEADS_E88F, 0),
+	UFTDI_DEV(FTDI, HD_RADIO, 0),
+	UFTDI_DEV(FTDI, HO720, 0),
+	UFTDI_DEV(FTDI, HO730, 0),
+	UFTDI_DEV(FTDI, HO820, 0),
+	UFTDI_DEV(FTDI, HO870, 0),
+	UFTDI_DEV(FTDI, IBS_APP70, 0),
+	UFTDI_DEV(FTDI, IBS_PCMCIA, 0),
+	UFTDI_DEV(FTDI, IBS_PEDO, 0),
+	UFTDI_DEV(FTDI, IBS_PICPRO, 0),
+	UFTDI_DEV(FTDI, IBS_PK1, 0),
+	UFTDI_DEV(FTDI, IBS_PROD, 0),
+	UFTDI_DEV(FTDI, IBS_RS232MON, 0),
+	UFTDI_DEV(FTDI, IBS_US485, 0),
+	UFTDI_DEV(FTDI, IPLUS, 0),
+	UFTDI_DEV(FTDI, IPLUS2, 0),
+	UFTDI_DEV(FTDI, IRTRANS, 0),
+	UFTDI_DEV(FTDI, KBS, 0),
+	UFTDI_DEV(FTDI, KTLINK, 0),
+	UFTDI_DEV(FTDI, LENZ_LIUSB, 0),
+	UFTDI_DEV(FTDI, LK202, 0),
+	UFTDI_DEV(FTDI, LK204, 0),
+	UFTDI_DEV(FTDI, LM3S_DEVEL_BOARD, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, LM3S_EVAL_BOARD, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, LM3S_ICDI_B_BOARD, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, MASTERDEVEL2, 0),
+	UFTDI_DEV(FTDI, MAXSTREAM, 0),
+	UFTDI_DEV(FTDI, MHAM_DB9, 0),
+	UFTDI_DEV(FTDI, MHAM_IC, 0),
+	UFTDI_DEV(FTDI, MHAM_KW, 0),
+	UFTDI_DEV(FTDI, MHAM_RS232, 0),
+	UFTDI_DEV(FTDI, MHAM_Y6, 0),
+	UFTDI_DEV(FTDI, MHAM_Y8, 0),
+	UFTDI_DEV(FTDI, MHAM_Y9, 0),
+	UFTDI_DEV(FTDI, MHAM_YS, 0),
+	UFTDI_DEV(FTDI, MICRO_CHAMELEON, 0),
+	UFTDI_DEV(FTDI, MTXORB_5, 0),
+	UFTDI_DEV(FTDI, MTXORB_6, 0),
+	UFTDI_DEV(FTDI, MX2_3, 0),
+	UFTDI_DEV(FTDI, MX4_5, 0),
+	UFTDI_DEV(FTDI, NXTCAM, 0),
+	UFTDI_DEV(FTDI, OCEANIC, 0),
+	UFTDI_DEV(FTDI, OOCDLINK, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, OPENDCC, 0),
+	UFTDI_DEV(FTDI, OPENDCC_GATEWAY, 0),
+	UFTDI_DEV(FTDI, OPENDCC_GBM, 0),
+	UFTDI_DEV(FTDI, OPENDCC_SNIFFER, 0),
+	UFTDI_DEV(FTDI, OPENDCC_THROTTLE, 0),
+	UFTDI_DEV(FTDI, PCDJ_DAC2, 0),
+	UFTDI_DEV(FTDI, PCMSFU, 0),
+	UFTDI_DEV(FTDI, PERLE_ULTRAPORT, 0),
+	UFTDI_DEV(FTDI, PHI_FISCO, 0),
+	UFTDI_DEV(FTDI, PIEGROUP, 0),
+	UFTDI_DEV(FTDI, PROPOX_JTAGCABLEII, 0),
+	UFTDI_DEV(FTDI, R2000KU_TRUE_RNG, 0),
+	UFTDI_DEV(FTDI, R2X0, 0),
+	UFTDI_DEV(FTDI, RELAIS, 0),
+	UFTDI_DEV(FTDI, REU_TINY, 0),
+	UFTDI_DEV(FTDI, RMP200, 0),
+	UFTDI_DEV(FTDI, RM_CANVIEW, 0),
+	UFTDI_DEV(FTDI, RRCIRKITS_LOCOBUFFER, 0),
+	UFTDI_DEV(FTDI, SCIENCESCOPE_HS_LOGBOOK, 0),
+	UFTDI_DEV(FTDI, SCIENCESCOPE_LOGBOOKML, 0),
+	UFTDI_DEV(FTDI, SCIENCESCOPE_LS_LOGBOOK, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_0, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_1, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_2, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_3, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_4, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_5, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_6, 0),
+	UFTDI_DEV(FTDI, SCS_DEVICE_7, 0),
+	UFTDI_DEV(FTDI, SCX8_USB_PHOENIX, 0),
+	UFTDI_DEV(FTDI, SDMUSBQSS, 0),
+	UFTDI_DEV(FTDI, SEMC_DSS20, 0),
+	UFTDI_DEV(FTDI, SERIAL_2232C, UFTDI_JTAG_CHECK_STRING),
+	UFTDI_DEV(FTDI, SERIAL_2232D, 0),
+	UFTDI_DEV(FTDI, SERIAL_232RL, 0),
+	UFTDI_DEV(FTDI, SERIAL_4232H, 0),
+	UFTDI_DEV(FTDI, SERIAL_8U100AX, 0),
+	UFTDI_DEV(FTDI, SERIAL_8U232AM, 0),
+	UFTDI_DEV(FTDI, SERIAL_8U232AM4, 0),
+	UFTDI_DEV(FTDI, SIGNALYZER_SH2, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, SIGNALYZER_SH4, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, SIGNALYZER_SLITE, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, SIGNALYZER_ST, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, SPECIAL_1, 0),
+	UFTDI_DEV(FTDI, SPECIAL_3, 0),
+	UFTDI_DEV(FTDI, SPECIAL_4, 0),
+	UFTDI_DEV(FTDI, SPROG_II, 0),
+	UFTDI_DEV(FTDI, SR_RADIO, 0),
+	UFTDI_DEV(FTDI, SUUNTO_SPORTS, 0),
+	UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13M, 0),
+	UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13S, 0),
+	UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13U, 0),
+	UFTDI_DEV(FTDI, TAVIR_STK500, 0),
+	UFTDI_DEV(FTDI, TERATRONIK_D2XX, 0),
+	UFTDI_DEV(FTDI, TERATRONIK_VCP, 0),
+	UFTDI_DEV(FTDI, THORLABS, 0),
+	UFTDI_DEV(FTDI, TNC_X, 0),
+	UFTDI_DEV(FTDI, TTUSB, 0),
+	UFTDI_DEV(FTDI, TURTELIZER2, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, UOPTBR, 0),
+	UFTDI_DEV(FTDI, USBSERIAL, 0),
+	UFTDI_DEV(FTDI, USBX_707, 0),
+	UFTDI_DEV(FTDI, USB_UIRT, 0),
+	UFTDI_DEV(FTDI, USINT_CAT, 0),
+	UFTDI_DEV(FTDI, USINT_RS232, 0),
+	UFTDI_DEV(FTDI, USINT_WKEY, 0),
+	UFTDI_DEV(FTDI, VARDAAN, 0),
+	UFTDI_DEV(FTDI, VNHCPCUSB_D, 0),
+	UFTDI_DEV(FTDI, WESTREX_MODEL_777, 0),
+	UFTDI_DEV(FTDI, WESTREX_MODEL_8900F, 0),
+	UFTDI_DEV(FTDI, XDS100V2, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, XDS100V3, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(FTDI, XF_547, 0),
+	UFTDI_DEV(FTDI, XF_640, 0),
+	UFTDI_DEV(FTDI, XF_642, 0),
+	UFTDI_DEV(FTDI, XM_RADIO, 0),
+	UFTDI_DEV(FTDI, YEI_SERVOCENTER31, 0),
+	UFTDI_DEV(GNOTOMETRICS, USB, 0),
+	UFTDI_DEV(ICOM, SP1, 0),
+	UFTDI_DEV(ICOM, OPC_U_UC, 0),
+	UFTDI_DEV(ICOM, RP2C1, 0),
+	UFTDI_DEV(ICOM, RP2C2, 0),
+	UFTDI_DEV(ICOM, RP2D, 0),
+	UFTDI_DEV(ICOM, RP2KVR, 0),
+	UFTDI_DEV(ICOM, RP2KVT, 0),
+	UFTDI_DEV(ICOM, RP2VR, 0),
+	UFTDI_DEV(ICOM, RP2VT, 0),
+	UFTDI_DEV(ICOM, RP4KVR, 0),
+	UFTDI_DEV(ICOM, RP4KVT, 0),
+	UFTDI_DEV(IDTECH, IDT1221U, 0),
+	UFTDI_DEV(INTERBIOMETRICS, IOBOARD, 0),
+	UFTDI_DEV(INTERBIOMETRICS, MINI_IOBOARD, 0),
+	UFTDI_DEV(INTREPIDCS, NEOVI, 0),
+	UFTDI_DEV(INTREPIDCS, VALUECAN, 0),
+	UFTDI_DEV(IONICS, PLUGCOMPUTER, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(JETI, SPC1201, 0),
+	UFTDI_DEV(KOBIL, CONV_B1, 0),
+	UFTDI_DEV(KOBIL, CONV_KAAN, 0),
+	UFTDI_DEV(LARSENBRUSGAARD, ALTITRACK, 0),
+	UFTDI_DEV(MARVELL, SHEEVAPLUG, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0100, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0101, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0102, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0103, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0104, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0105, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0106, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0107, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0108, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0109, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0110, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0111, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0112, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0113, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0114, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0115, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0116, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0117, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0118, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0119, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0120, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0121, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0122, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0123, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0124, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0125, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0126, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0128, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0129, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0130, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0131, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0132, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0133, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0134, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0135, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0136, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0137, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0138, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0139, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0140, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0141, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0142, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0143, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0144, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0145, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0146, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0147, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0148, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0149, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0150, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0151, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0152, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0159, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0160, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0161, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0162, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0163, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0164, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0165, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0166, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0167, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0168, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0169, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0170, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0171, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0172, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0173, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0174, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0175, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0176, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0177, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0178, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0179, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0180, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0181, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0182, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0183, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0184, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0185, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0186, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0187, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0188, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0189, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0190, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0191, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0192, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0193, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0194, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0195, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0196, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0197, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0198, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0199, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019A, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019B, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019C, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019D, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019E, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019F, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A0, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A1, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A2, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A3, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A4, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A5, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A6, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A7, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A8, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A9, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AA, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AB, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AC, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AD, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AE, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AF, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B0, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B1, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B2, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B3, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B4, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B5, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B6, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B7, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B8, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B9, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BA, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BB, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BC, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BD, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BE, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BF, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C0, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C1, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C2, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C3, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C4, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C5, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C6, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C7, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C8, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C9, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CA, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CB, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CC, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CD, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CE, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CF, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D0, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D1, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D2, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D3, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D4, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D5, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D6, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D7, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D8, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D9, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DA, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DB, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DC, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DD, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DE, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DF, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E0, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E1, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E2, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E3, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E4, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E5, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E6, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E7, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E8, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E9, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EA, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EB, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EC, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01ED, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EE, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EF, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F0, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F1, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F2, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F3, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F4, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F5, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F6, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F7, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F8, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F9, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FA, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FB, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FC, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FD, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FE, 0),
+	UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FF, 0),
+	UFTDI_DEV(MATRIXORBITAL, MOUA, 0),
+	UFTDI_DEV(MELCO, PCOPRS1, 0),
+	UFTDI_DEV(METAGEEK, TELLSTICK, 0),
+	UFTDI_DEV(MOBILITY, USB_SERIAL, 0),
+	UFTDI_DEV(OLIMEX, ARM_USB_OCD, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(OLIMEX, ARM_USB_OCD_H, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(OPTO, CRD7734, 0),
+	UFTDI_DEV(OPTO, CRD7734_1, 0),
+	UFTDI_DEV(PAPOUCH, AD4USB, 0),
+	UFTDI_DEV(PAPOUCH, AP485, 0),
+	UFTDI_DEV(PAPOUCH, AP485_2, 0),
+	UFTDI_DEV(PAPOUCH, DRAK5, 0),
+	UFTDI_DEV(PAPOUCH, DRAK6, 0),
+	UFTDI_DEV(PAPOUCH, GMSR, 0),
+	UFTDI_DEV(PAPOUCH, GMUX, 0),
+	UFTDI_DEV(PAPOUCH, IRAMP, 0),
+	UFTDI_DEV(PAPOUCH, LEC, 0),
+	UFTDI_DEV(PAPOUCH, MU, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO10X1, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO2X16, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO2X2, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO30X3, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO3X32, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO4X4, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO60X3, 0),
+	UFTDI_DEV(PAPOUCH, QUIDO8X8, 0),
+	UFTDI_DEV(PAPOUCH, SB232, 0),
+	UFTDI_DEV(PAPOUCH, SB422, 0),
+	UFTDI_DEV(PAPOUCH, SB422_2, 0),
+	UFTDI_DEV(PAPOUCH, SB485, 0),
+	UFTDI_DEV(PAPOUCH, SB485C, 0),
+	UFTDI_DEV(PAPOUCH, SB485S, 0),
+	UFTDI_DEV(PAPOUCH, SB485_2, 0),
+	UFTDI_DEV(PAPOUCH, SIMUKEY, 0),
+	UFTDI_DEV(PAPOUCH, TMU, 0),
+	UFTDI_DEV(PAPOUCH, UPSUSB, 0),
+	UFTDI_DEV(POSIFLEX, PP7000, 0),
+	UFTDI_DEV(QIHARDWARE, JTAGSERIAL, UFTDI_JTAG_IFACE(0)),
+	UFTDI_DEV(RATOC, REXUSB60F, 0),
+	UFTDI_DEV(RTSYSTEMS, CT29B, 0),
+	UFTDI_DEV(RTSYSTEMS, SERIAL_VX7, 0),
+	UFTDI_DEV(SEALEVEL, 2101, 0),
+	UFTDI_DEV(SEALEVEL, 2102, 0),
+	UFTDI_DEV(SEALEVEL, 2103, 0),
+	UFTDI_DEV(SEALEVEL, 2104, 0),
+	UFTDI_DEV(SEALEVEL, 2106, 0),
+	UFTDI_DEV(SEALEVEL, 2201_1, 0),
+	UFTDI_DEV(SEALEVEL, 2201_2, 0),
+	UFTDI_DEV(SEALEVEL, 2202_1, 0),
+	UFTDI_DEV(SEALEVEL, 2202_2, 0),
+	UFTDI_DEV(SEALEVEL, 2203_1, 0),
+	UFTDI_DEV(SEALEVEL, 2203_2, 0),
+	UFTDI_DEV(SEALEVEL, 2401_1, 0),
+	UFTDI_DEV(SEALEVEL, 2401_2, 0),
+	UFTDI_DEV(SEALEVEL, 2401_3, 0),
+	UFTDI_DEV(SEALEVEL, 2401_4, 0),
+	UFTDI_DEV(SEALEVEL, 2402_1, 0),
+	UFTDI_DEV(SEALEVEL, 2402_2, 0),
+	UFTDI_DEV(SEALEVEL, 2402_3, 0),
+	UFTDI_DEV(SEALEVEL, 2402_4, 0),
+	UFTDI_DEV(SEALEVEL, 2403_1, 0),
+	UFTDI_DEV(SEALEVEL, 2403_2, 0),
+	UFTDI_DEV(SEALEVEL, 2403_3, 0),
+	UFTDI_DEV(SEALEVEL, 2403_4, 0),
+	UFTDI_DEV(SEALEVEL, 2801_1, 0),
+	UFTDI_DEV(SEALEVEL, 2801_2, 0),
+	UFTDI_DEV(SEALEVEL, 2801_3, 0),
+	UFTDI_DEV(SEALEVEL, 2801_4, 0),
+	UFTDI_DEV(SEALEVEL, 2801_5, 0),
+	UFTDI_DEV(SEALEVEL, 2801_6, 0),
+	UFTDI_DEV(SEALEVEL, 2801_7, 0),
+	UFTDI_DEV(SEALEVEL, 2801_8, 0),
+	UFTDI_DEV(SEALEVEL, 2802_1, 0),
+	UFTDI_DEV(SEALEVEL, 2802_2, 0),
+	UFTDI_DEV(SEALEVEL, 2802_3, 0),
+	UFTDI_DEV(SEALEVEL, 2802_4, 0),
+	UFTDI_DEV(SEALEVEL, 2802_5, 0),
+	UFTDI_DEV(SEALEVEL, 2802_6, 0),
+	UFTDI_DEV(SEALEVEL, 2802_7, 0),
+	UFTDI_DEV(SEALEVEL, 2802_8, 0),
+	UFTDI_DEV(SEALEVEL, 2803_1, 0),
+	UFTDI_DEV(SEALEVEL, 2803_2, 0),
+	UFTDI_DEV(SEALEVEL, 2803_3, 0),
+	UFTDI_DEV(SEALEVEL, 2803_4, 0),
+	UFTDI_DEV(SEALEVEL, 2803_5, 0),
+	UFTDI_DEV(SEALEVEL, 2803_6, 0),
+	UFTDI_DEV(SEALEVEL, 2803_7, 0),
+	UFTDI_DEV(SEALEVEL, 2803_8, 0),
+	UFTDI_DEV(SIIG2, DK201, 0),
+	UFTDI_DEV(SIIG2, US2308, 0),
+	UFTDI_DEV(TESTO, USB_INTERFACE, 0),
+	UFTDI_DEV(TML, USB_SERIAL, 0),
+	UFTDI_DEV(TTI, QL355P, 0),
+	UFTDI_DEV(UNKNOWN4, NF_RIC, 0),
+#undef UFTDI_DEV
+};
+
+DRIVER_MODULE(uftdi, uhub, uftdi_driver, uftdi_devclass, NULL, NULL);
+MODULE_DEPEND(uftdi, ucom, 1, 1, 1);
+MODULE_DEPEND(uftdi, usb, 1, 1, 1);
+MODULE_VERSION(uftdi, 1);
+USB_PNP_HOST_INFO(uftdi_devs);
+
+/*
+ * Jtag product name strings table.  Some products have one or more interfaces
+ * dedicated to jtag or gpio, but use a product ID that's the same as other
+ * products which don't.  They are marked with a flag in the table above, and
+ * the following string table is checked for flagged products.  The string check
+ * is done with strstr(); in effect there is an implicit wildcard at the
+ * beginning and end of each product name string in table.
+ */
+static const struct jtag_by_name {
+	const char *	product_name;
+	uint32_t	jtag_interfaces;
+} jtag_products_by_name[] = {
+        /* TI Beaglebone and TI XDS100Vn jtag product line. */
+	{"XDS100V",	UFTDI_JTAG_IFACE(0)},
+};
+
+/*
+ * Set up a sysctl and tunable to en/disable the feature of skipping the
+ * creation of tty devices for jtag interfaces.  Enabled by default.
+ */
+static int skip_jtag_interfaces = 1;
+SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, skip_jtag_interfaces, CTLFLAG_RWTUN,
+    &skip_jtag_interfaces, 1, "Skip creating tty devices for jtag interfaces");
+
+static boolean_t
+is_jtag_interface(struct usb_attach_arg *uaa, const struct usb_device_id *id)
+{
+	int i, iface_bit;
+	const char * product_name;
+	const struct jtag_by_name *jbn;
+
+	/* We only allocate 8 flag bits for jtag interface flags. */
+	if (uaa->info.bIfaceIndex >= UFTDI_JTAG_IFACES_MAX)
+		return (0);
+	iface_bit = UFTDI_JTAG_IFACE(uaa->info.bIfaceIndex);
+
+	/*
+	 * If requested, search the name strings table and use the interface
+	 * bits from that table when the product name string matches, else use
+	 * the jtag interface bits from the main ID table.
+	 */
+	if ((id->driver_info & UFTDI_JTAG_MASK) == UFTDI_JTAG_CHECK_STRING) {
+		product_name = usb_get_product(uaa->device);
+		for (i = 0; i < nitems(jtag_products_by_name); i++) {
+			jbn = &jtag_products_by_name[i];
+			if (strstr(product_name, jbn->product_name) != NULL &&
+			    (jbn->jtag_interfaces & iface_bit) != 0)
+				return (1);
+		}
+	} else if ((id->driver_info & iface_bit) != 0)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * Set up softc fields whose value depends on the device type.
+ *
+ * Note that the 2232C and 2232D devices are the same for our purposes.  In the
+ * silicon the difference is that the D series has CPU FIFO mode and C doesn't.
+ * I haven't found any way of determining the C/D difference from info provided
+ * by the chip other than trying to set CPU FIFO mode and having it work or not.
+ * 
+ * Due to a hardware bug, a 232B chip without an eeprom reports itself as a 
+ * 232A, but if the serial number is also zero we know it's really a 232B. 
+ */
+static void
+uftdi_devtype_setup(struct uftdi_softc *sc, struct usb_attach_arg *uaa)
+{
+	struct usb_device_descriptor *dd;
+
+	sc->sc_bcdDevice = uaa->info.bcdDevice;
+
+	switch (uaa->info.bcdDevice) {
+	case 0x200:
+		dd = usbd_get_device_descriptor(sc->sc_udev);
+		if (dd->iSerialNumber == 0) {
+			sc->sc_devtype = DEVT_232B;
+		} else {
+			sc->sc_devtype = DEVT_232A;
+		}
+		sc->sc_ucom.sc_portno = 0;
+		break;
+	case 0x400:
+		sc->sc_devtype = DEVT_232B;
+		sc->sc_ucom.sc_portno = 0;
+		break;
+	case 0x500:
+		sc->sc_devtype = DEVT_2232D;
+		sc->sc_devflags |= DEVF_BAUDBITS_HINDEX;
+		sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+		break;
+	case 0x600:
+		sc->sc_devtype = DEVT_232R;
+		sc->sc_ucom.sc_portno = 0;
+		break;
+	case 0x700:
+		sc->sc_devtype = DEVT_2232H;
+		sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M;
+		sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+		break;
+	case 0x800:
+		sc->sc_devtype = DEVT_4232H;
+		sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M;
+		sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+		break;
+	case 0x900:
+		sc->sc_devtype = DEVT_232H;
+		sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M;
+		sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+		break;
+	case 0x1000:
+		sc->sc_devtype = DEVT_230X;
+		sc->sc_devflags |= DEVF_BAUDBITS_HINDEX;
+		sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+		break;
+	default:
+		if (uaa->info.bcdDevice < 0x200) {
+			sc->sc_devtype = DEVT_SIO;
+			sc->sc_hdrlen = 1;
+		} else {
+			sc->sc_devtype = DEVT_232R;
+			device_printf(sc->sc_dev, "Warning: unknown FTDI "
+			    "device type, bcdDevice=0x%04x, assuming 232R\n", 
+			    uaa->info.bcdDevice);
+		}
+		sc->sc_ucom.sc_portno = 0;
+		break;
+	}
+}
+
+static int
+uftdi_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	const struct usb_device_id *id;
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+
+	/*
+	 * Attach to all present interfaces unless this is a JTAG one, which
+	 * we leave for userland.
+	 */
+	id = usbd_lookup_id_by_info(uftdi_devs, sizeof(uftdi_devs),
+	    &uaa->info);
+	if (id == NULL)
+		return (ENXIO);
+	if (skip_jtag_interfaces && is_jtag_interface(uaa, id)) {
+		printf("%s: skipping JTAG interface #%d for '%s' at %u.%u\n",
+		    device_get_name(dev),
+		    uaa->info.bIfaceIndex,
+		    usb_get_product(uaa->device),
+		    usbd_get_bus_index(uaa->device),
+		    usbd_get_device_index(uaa->device));
+		return (ENXIO);
+	}
+	uaa->driver_info = id->driver_info;
+	return (BUS_PROBE_SPECIFIC);
+}
+
+static int
+uftdi_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uftdi_softc *sc = device_get_softc(dev);
+	int error;
+
+	DPRINTF("\n");
+
+	sc->sc_udev = uaa->device;
+	sc->sc_dev = dev;
+	sc->sc_unit = device_get_unit(dev);
+	sc->sc_bitmode = UFTDI_BITMODE_NONE;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+
+	uftdi_devtype_setup(sc, uaa);
+
+	error = usbd_transfer_setup(uaa->device,
+	    &uaa->info.bIfaceIndex, sc->sc_xfer, uftdi_config,
+	    UFTDI_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		device_printf(dev, "allocating USB "
+		    "transfers failed\n");
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	/* set a valid "lcr" value */
+
+	sc->sc_last_lcr =
+	    (FTDI_SIO_SET_DATA_STOP_BITS_2 |
+	    FTDI_SIO_SET_DATA_PARITY_NONE |
+	    FTDI_SIO_SET_DATA_BITS(8));
+
+	/* Indicate tx bits in sc_lsr can be used to determine busy vs idle. */
+	ucom_use_lsr_txbits(&sc->sc_ucom);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uftdi_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);			/* success */
+
+detach:
+	uftdi_detach(dev);
+	return (ENXIO);
+}
+
+static int
+uftdi_detach(device_t dev)
+{
+	struct uftdi_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uftdi_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uftdi);
+
+static void
+uftdi_free_softc(struct uftdi_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uftdi_free(struct ucom_softc *ucom)
+{
+	uftdi_free_softc(ucom->sc_parent);
+}
+
+static void
+uftdi_cfg_open(struct ucom_softc *ucom)
+{
+
+	/*
+	 * This do-nothing open routine exists for the sole purpose of this
+	 * DPRINTF() so that you can see the point at which open gets called
+	 * when debugging is enabled.
+	 */
+	DPRINTF("\n");
+}
+
+static void
+uftdi_cfg_close(struct ucom_softc *ucom)
+{
+
+	/*
+	 * This do-nothing close routine exists for the sole purpose of this
+	 * DPRINTF() so that you can see the point at which close gets called
+	 * when debugging is enabled.
+	 */
+	DPRINTF("\n");
+}
+
+static void
+uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uftdi_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t pktlen;
+	uint32_t buflen;
+	uint8_t buf[1];
+
+	DPRINTFN(3, "\n");
+
+	switch (USB_GET_STATE(xfer)) {
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+		}
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+		/*
+		 * If output packets don't require headers (the common case) we
+		 * can just load the buffer up with payload bytes all at once.
+		 * Otherwise, loop to format packets into the buffer while there
+		 * is data available, and room for a packet header and at least
+		 * one byte of payload.
+		 *
+		 * NOTE: The FTDI chip doesn't accept zero length
+		 * packets. This cannot happen because the "pktlen"
+		 * will always be non-zero when "ucom_get_data()"
+		 * returns non-zero which we check below.
+		 */
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (sc->sc_hdrlen == 0) {
+			if (ucom_get_data(&sc->sc_ucom, pc, 0, UFTDI_OBUFSIZE, 
+			    &buflen) == 0)
+				break;
+		} else {
+			buflen = 0;
+			while (buflen < UFTDI_OBUFSIZE - sc->sc_hdrlen - 1 &&
+			    ucom_get_data(&sc->sc_ucom, pc, buflen + 
+			    sc->sc_hdrlen, UFTDI_OPKTSIZE - sc->sc_hdrlen, 
+			    &pktlen) != 0) {
+				buf[0] = FTDI_OUT_TAG(pktlen, 
+				    sc->sc_ucom.sc_portno);
+				usbd_copy_in(pc, buflen, buf, 1);
+				buflen += pktlen + sc->sc_hdrlen;
+			}
+		}
+		if (buflen != 0) {
+			usbd_xfer_set_frame_len(xfer, 0, buflen);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+	}
+}
+
+static void
+uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uftdi_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[2];
+	uint8_t ftdi_msr;
+	uint8_t msr;
+	uint8_t lsr;
+	int buflen;
+	int pktlen;
+	int pktmax;
+	int offset;
+
+	DPRINTFN(3, "\n");
+
+	usbd_xfer_status(xfer, &buflen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (buflen < UFTDI_IHDRSIZE)
+			goto tr_setup;
+		pc = usbd_xfer_get_frame(xfer, 0);
+		pktmax = xfer->max_packet_size - UFTDI_IHDRSIZE;
+		lsr = 0;
+		msr = 0;
+		offset = 0;
+		/*
+		 * Extract packet headers and payload bytes from the buffer.
+		 * Feed payload bytes to ucom/tty layer; OR-accumulate the
+		 * receiver-related header status bits which are transient and
+		 * could toggle with each packet, but for transmitter-related
+		 * bits keep only the ones from the last packet.
+		 *
+		 * After processing all packets in the buffer, process the
+		 * accumulated transient MSR and LSR values along with the
+		 * non-transient bits from the last packet header.
+		 */
+		while (buflen >= UFTDI_IHDRSIZE) {
+			usbd_copy_out(pc, offset, buf, UFTDI_IHDRSIZE);
+			offset += UFTDI_IHDRSIZE;
+			buflen -= UFTDI_IHDRSIZE;
+			lsr &= ~(ULSR_TXRDY | ULSR_TSRE);
+			lsr |= FTDI_GET_LSR(buf);
+			if (FTDI_GET_MSR(buf) & FTDI_SIO_RI_MASK)
+				msr |= SER_RI;
+			pktlen = min(buflen, pktmax);
+			if (pktlen != 0) {
+				ucom_put_data(&sc->sc_ucom, pc, offset, 
+				    pktlen);
+				offset += pktlen;
+				buflen -= pktlen;
+			}
+		}
+		ftdi_msr = FTDI_GET_MSR(buf);
+
+		if (ftdi_msr & FTDI_SIO_CTS_MASK)
+			msr |= SER_CTS;
+		if (ftdi_msr & FTDI_SIO_DSR_MASK)
+			msr |= SER_DSR;
+		if (ftdi_msr & FTDI_SIO_RI_MASK)
+			msr |= SER_RI;
+		if (ftdi_msr & FTDI_SIO_RLSD_MASK)
+			msr |= SER_DCD;
+
+		if (sc->sc_msr != msr || sc->sc_lsr != lsr) {
+			DPRINTF("status change msr=0x%02x (0x%02x) "
+			    "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
+			    lsr, sc->sc_lsr);
+
+			sc->sc_msr = msr;
+			sc->sc_lsr = lsr;
+
+			ucom_status_change(&sc->sc_ucom);
+		}
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uftdi_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	uint16_t wIndex = ucom->sc_portno;
+	uint16_t wValue;
+	struct usb_device_request req;
+
+	DPRINTFN(2, "DTR=%u\n", onoff);
+
+	wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_MODEM_CTRL;
+	USETW(req.wValue, wValue);
+	USETW(req.wIndex, wIndex);
+	USETW(req.wLength, 0);
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	uint16_t wIndex = ucom->sc_portno;
+	uint16_t wValue;
+	struct usb_device_request req;
+
+	DPRINTFN(2, "RTS=%u\n", onoff);
+
+	wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_MODEM_CTRL;
+	USETW(req.wValue, wValue);
+	USETW(req.wIndex, wIndex);
+	USETW(req.wLength, 0);
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	uint16_t wIndex = ucom->sc_portno;
+	uint16_t wValue;
+	struct usb_device_request req;
+
+	DPRINTFN(2, "BREAK=%u\n", onoff);
+
+	if (onoff) {
+		sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
+	} else {
+		sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
+	}
+
+	wValue = sc->sc_last_lcr;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_DATA;
+	USETW(req.wValue, wValue);
+	USETW(req.wIndex, wIndex);
+	USETW(req.wLength, 0);
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+/*
+ * Return true if the given speed is within operational tolerance of the target
+ * speed.  FTDI recommends that the hardware speed be within 3% of nominal.
+ */
+static inline boolean_t
+uftdi_baud_within_tolerance(uint64_t speed, uint64_t target)
+{
+	return ((speed >= (target * 100) / 103) &&
+	    (speed <= (target * 100) / 97));
+}
+
+static int
+uftdi_sio_encode_baudrate(struct uftdi_softc *sc, speed_t speed,
+	struct uftdi_param_config *cfg)
+{
+	u_int i;
+	const speed_t sio_speeds[] = {
+		300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
+	};
+
+	/*
+	 * The original SIO chips were limited to a small choice of speeds
+	 * listed in an internal table of speeds chosen by an index value.
+	 */
+	for (i = 0; i < nitems(sio_speeds); ++i) {
+		if (speed == sio_speeds[i]) {
+			cfg->baud_lobits = i;
+			cfg->baud_hibits = 0;
+			return (0);
+		}
+	}
+	return (ERANGE);
+}
+
+static int
+uftdi_encode_baudrate(struct uftdi_softc *sc, speed_t speed,
+	struct uftdi_param_config *cfg)
+{
+	static const uint8_t encoded_fraction[8] = {0, 3, 2, 4, 1, 5, 6, 7};
+	static const uint8_t roundoff_232a[16] = {
+		0,  1,  0,  1,  0, -1,  2,  1,
+		0, -1, -2, -3,  4,  3,  2,  1,
+	};
+	uint32_t clk, divisor, fastclk_flag, frac, hwspeed;
+
+	/*
+	 * If this chip has the fast clock capability and the speed is within
+	 * range, use the 12MHz clock, otherwise the standard clock is 3MHz.
+	 */
+	if ((sc->sc_devflags & DEVF_BAUDCLK_12M) && speed >= 1200) {
+		clk = 12000000;
+		fastclk_flag = (1 << 17);
+	} else {
+		clk = 3000000;
+		fastclk_flag = 0;
+	}
+
+	/*
+	 * Make sure the requested speed is reachable with the available clock
+	 * and a 14-bit divisor.
+	 */
+	if (speed < (clk >> 14) || speed > clk)
+		return (ERANGE);
+
+	/*
+	 * Calculate the divisor, initially yielding a fixed point number with a
+	 * 4-bit (1/16ths) fraction, then round it to the nearest fraction the
+	 * hardware can handle.  When the integral part of the divisor is
+	 * greater than one, the fractional part is in 1/8ths of the base clock.
+	 * The FT8U232AM chips can handle only 0.125, 0.250, and 0.5 fractions.
+	 * Later chips can handle all 1/8th fractions.
+	 *
+	 * If the integral part of the divisor is 1, a special rule applies: the
+	 * fractional part can only be .0 or .5 (this is a limitation of the
+	 * hardware).  We handle this by truncating the fraction rather than
+	 * rounding, because this only applies to the two fastest speeds the
+	 * chip can achieve and rounding doesn't matter, either you've asked for
+	 * that exact speed or you've asked for something the chip can't do.
+	 *
+	 * For the FT8U232AM chips, use a roundoff table to adjust the result
+	 * to the nearest 1/8th fraction that is supported by the hardware,
+	 * leaving a fixed-point number with a 3-bit fraction which exactly
+	 * represents the math the hardware divider will do.  For later-series
+	 * chips that support all 8 fractional divisors, just round 16ths to
+	 * 8ths by adding 1 and dividing by 2.
+	 */
+	divisor = (clk << 4) / speed;
+	if ((divisor & 0xf) == 1)
+		divisor &= 0xfffffff8;
+	else if (sc->sc_devtype == DEVT_232A)
+		divisor += roundoff_232a[divisor & 0x0f];
+	else
+		divisor += 1;  /* Rounds odd 16ths up to next 8th. */
+	divisor >>= 1;
+
+	/*
+	 * Ensure the resulting hardware speed will be within operational
+	 * tolerance (within 3% of nominal).
+	 */
+	hwspeed = (clk << 3) / divisor;
+	if (!uftdi_baud_within_tolerance(hwspeed, speed))
+		return (ERANGE);
+
+	/*
+	 * Re-pack the divisor into hardware format. The lower 14-bits hold the
+	 * integral part, while the upper bits specify the fraction by indexing
+	 * a table of fractions within the hardware which is laid out as:
+	 *    {0.0, 0.5, 0.25, 0.125, 0.325, 0.625, 0.725, 0.875}
+	 * The A-series chips only have the first four table entries; the
+	 * roundoff table logic above ensures that the fractional part for those
+	 * chips will be one of the first four values.
+	 *
+	 * When the divisor is 1 a special encoding applies:  1.0 is encoded as
+	 * 0.0, and 1.5 is encoded as 1.0.  The rounding logic above has already
+	 * ensured that the fraction is either .0 or .5 if the integral is 1.
+	 */
+	frac = divisor & 0x07;
+	divisor >>= 3;
+	if (divisor == 1) {
+		if (frac == 0)
+			divisor = 0;  /* 1.0 becomes 0.0 */
+		else
+			frac = 0;     /* 1.5 becomes 1.0 */
+	}
+	divisor |= (encoded_fraction[frac] << 14) | fastclk_flag;
+        
+	cfg->baud_lobits = (uint16_t)divisor;
+	cfg->baud_hibits = (uint16_t)(divisor >> 16);
+
+	/*
+	 * If this chip requires the baud bits to be in the high byte of the
+	 * index word, move the bits up to that location.
+	 */
+	if (sc->sc_devflags & DEVF_BAUDBITS_HINDEX) {
+		cfg->baud_hibits <<= 8;
+	}
+
+	return (0);
+}
+
+static int
+uftdi_set_parm_soft(struct ucom_softc *ucom, struct termios *t,
+    struct uftdi_param_config *cfg)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	int err;
+
+	memset(cfg, 0, sizeof(*cfg));
+
+	if (sc->sc_devtype == DEVT_SIO)
+		err = uftdi_sio_encode_baudrate(sc, t->c_ospeed, cfg);
+	else
+		err = uftdi_encode_baudrate(sc, t->c_ospeed, cfg);
+	if (err != 0)
+		return (err);
+
+	if (t->c_cflag & CSTOPB)
+		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
+	else
+		cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
+
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD) {
+			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
+		} else {
+			cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
+		}
+	} else {
+		cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
+	}
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
+		break;
+
+	case CS6:
+		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
+		break;
+
+	case CS7:
+		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
+		break;
+
+	case CS8:
+		cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
+		break;
+	}
+
+	if (t->c_cflag & CRTSCTS) {
+		cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
+	} else if (t->c_iflag & (IXON | IXOFF)) {
+		cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
+		cfg->v_start = t->c_cc[VSTART];
+		cfg->v_stop = t->c_cc[VSTOP];
+	} else {
+		cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
+	}
+
+	return (0);
+}
+
+static int
+uftdi_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uftdi_param_config cfg;
+
+	DPRINTF("\n");
+
+	return (uftdi_set_parm_soft(ucom, t, &cfg));
+}
+
+static void
+uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	uint16_t wIndex = ucom->sc_portno;
+	struct uftdi_param_config cfg;
+	struct usb_device_request req;
+
+	DPRINTF("\n");
+
+	if (uftdi_set_parm_soft(ucom, t, &cfg)) {
+		/* should not happen */
+		return;
+	}
+	sc->sc_last_lcr = cfg.lcr;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_BAUD_RATE;
+	USETW(req.wValue, cfg.baud_lobits);
+	USETW(req.wIndex, cfg.baud_hibits | wIndex);
+	USETW(req.wLength, 0);
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_DATA;
+	USETW(req.wValue, cfg.lcr);
+	USETW(req.wIndex, wIndex);
+	USETW(req.wLength, 0);
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+	USETW2(req.wValue, cfg.v_stop, cfg.v_start);
+	USETW2(req.wIndex, cfg.v_flow, wIndex);
+	USETW(req.wLength, 0);
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+
+	DPRINTFN(3, "msr=0x%02x lsr=0x%02x\n", sc->sc_msr, sc->sc_lsr);
+
+	*msr = sc->sc_msr;
+	*lsr = sc->sc_lsr;
+}
+
+static int
+uftdi_reset(struct ucom_softc *ucom, int reset_type)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	DPRINTFN(2, "\n");
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_RESET;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW(req.wValue, reset_type);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_set_bitmode(struct ucom_softc *ucom, uint8_t bitmode, uint8_t iomask)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	int rv;
+
+	DPRINTFN(2, "\n");
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_BITMODE;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+
+	if (bitmode == UFTDI_BITMODE_NONE)
+	    USETW2(req.wValue, 0, 0);
+	else
+	    USETW2(req.wValue, (1 << bitmode), iomask);
+
+	rv = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL);
+	if (rv == USB_ERR_NORMAL_COMPLETION)
+		sc->sc_bitmode = bitmode;
+
+	return (rv);
+}
+
+static int
+uftdi_get_bitmode(struct ucom_softc *ucom, uint8_t *bitmode, uint8_t *iomask)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	DPRINTFN(2, "\n");
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_GET_BITMODE;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 1);
+	USETW(req.wValue,  0);
+
+	*bitmode = sc->sc_bitmode;
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, iomask));
+}
+
+static int
+uftdi_set_latency(struct ucom_softc *ucom, int latency)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	DPRINTFN(2, "\n");
+
+	if (latency < 0 || latency > 255)
+		return (USB_ERR_INVAL);
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_LATENCY;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW2(req.wValue, 0, latency);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_get_latency(struct ucom_softc *ucom, int *latency)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	usb_error_t err;
+	uint8_t buf;
+
+	DPRINTFN(2, "\n");
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_GET_LATENCY;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 1);
+	USETW(req.wValue, 0);
+
+	err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &buf);
+	*latency = buf;
+
+	return (err);
+}
+
+static int
+uftdi_set_event_char(struct ucom_softc *ucom, int echar)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	uint8_t enable;
+
+	DPRINTFN(2, "\n");
+
+	enable = (echar == -1) ? 0 : 1;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_EVENT_CHAR;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW2(req.wValue, enable, echar & 0xff);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_set_error_char(struct ucom_softc *ucom, int echar)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	uint8_t enable;
+
+	DPRINTFN(2, "\n");
+
+	enable = (echar == -1) ? 0 : 1;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_ERROR_CHAR;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW2(req.wValue, enable, echar & 0xff);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_read_eeprom(struct ucom_softc *ucom, struct uftdi_eeio *eeio)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	usb_error_t err;
+	uint16_t widx, wlength, woffset;
+
+	DPRINTFN(3, "\n");
+
+	/* Offset and length must both be evenly divisible by two. */
+	if ((eeio->offset | eeio->length) & 0x01)
+		return (EINVAL);
+
+	woffset = eeio->offset / 2U;
+	wlength = eeio->length / 2U;
+	for (widx = 0; widx < wlength; widx++) {
+		req.bmRequestType = UT_READ_VENDOR_DEVICE;
+		req.bRequest = FTDI_SIO_READ_EEPROM;
+		USETW(req.wIndex, widx + woffset);
+		USETW(req.wLength, 2);
+		USETW(req.wValue, 0);
+		err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req,
+		    &eeio->data[widx]);
+		if (err != USB_ERR_NORMAL_COMPLETION)
+			return (err);
+	}
+	return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static int
+uftdi_write_eeprom(struct ucom_softc *ucom, struct uftdi_eeio *eeio)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	usb_error_t err;
+	uint16_t widx, wlength, woffset;
+
+	DPRINTFN(3, "\n");
+
+	/* Offset and length must both be evenly divisible by two. */
+	if ((eeio->offset | eeio->length) & 0x01)
+		return (EINVAL);
+
+	woffset = eeio->offset / 2U;
+	wlength = eeio->length / 2U;
+	for (widx = 0; widx < wlength; widx++) {
+		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+		req.bRequest = FTDI_SIO_WRITE_EEPROM;
+		USETW(req.wIndex, widx + woffset);
+		USETW(req.wLength, 0);
+		USETW(req.wValue, eeio->data[widx]);
+		err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL);
+		if (err != USB_ERR_NORMAL_COMPLETION)
+			return (err);
+	}
+	return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static int
+uftdi_erase_eeprom(struct ucom_softc *ucom, int confirmation)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	usb_error_t err;
+
+	DPRINTFN(2, "\n");
+
+	/* Small effort to prevent accidental erasure. */
+	if (confirmation != UFTDI_CONFIRM_ERASE)
+		return (EINVAL);
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_ERASE_EEPROM;
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, 0);
+	USETW(req.wValue, 0);
+	err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL);
+
+	return (err);
+}
+
+static int
+uftdi_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
+    int flag, struct thread *td)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	int err;
+	struct uftdi_bitmode * mode;
+
+	switch (cmd) {
+	case UFTDIIOC_RESET_IO:
+	case UFTDIIOC_RESET_RX:
+	case UFTDIIOC_RESET_TX:
+		err = uftdi_reset(ucom, 
+		    cmd == UFTDIIOC_RESET_IO ? FTDI_SIO_RESET_SIO :
+		    (cmd == UFTDIIOC_RESET_RX ? FTDI_SIO_RESET_PURGE_RX :
+		    FTDI_SIO_RESET_PURGE_TX));
+		break;
+	case UFTDIIOC_SET_BITMODE:
+		mode = (struct uftdi_bitmode *)data;
+		err = uftdi_set_bitmode(ucom, mode->mode, mode->iomask);
+		break;
+	case UFTDIIOC_GET_BITMODE:
+		mode = (struct uftdi_bitmode *)data;
+		err = uftdi_get_bitmode(ucom, &mode->mode, &mode->iomask);
+		break;
+	case UFTDIIOC_SET_LATENCY:
+		err = uftdi_set_latency(ucom, *((int *)data));
+		break;
+	case UFTDIIOC_GET_LATENCY:
+		err = uftdi_get_latency(ucom, (int *)data);
+		break;
+	case UFTDIIOC_SET_ERROR_CHAR:
+		err = uftdi_set_error_char(ucom, *(int *)data);
+		break;
+	case UFTDIIOC_SET_EVENT_CHAR:
+		err = uftdi_set_event_char(ucom, *(int *)data);
+		break;
+	case UFTDIIOC_GET_HWREV:
+		*(int *)data = sc->sc_bcdDevice;
+		err = 0;
+		break;
+	case UFTDIIOC_READ_EEPROM:
+		err = uftdi_read_eeprom(ucom, (struct uftdi_eeio *)data);
+		break;
+	case UFTDIIOC_WRITE_EEPROM:
+		err = uftdi_write_eeprom(ucom, (struct uftdi_eeio *)data);
+		break;
+	case UFTDIIOC_ERASE_EEPROM:
+		err = uftdi_erase_eeprom(ucom, *(int *)data);
+		break;
+	default:
+		return (ENOIOCTL);
+	}
+	if (err != USB_ERR_NORMAL_COMPLETION)
+		return (EIO);
+	return (0);
+}
+
+static void
+uftdi_start_read(struct ucom_softc *ucom)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+}
+
+static void
+uftdi_stop_read(struct ucom_softc *ucom)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+}
+
+static void
+uftdi_start_write(struct ucom_softc *ucom)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+}
+
+static void
+uftdi_stop_write(struct ucom_softc *ucom)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+}
+
+static void
+uftdi_poll(struct ucom_softc *ucom)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_poll(sc->sc_xfer, UFTDI_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/uftdi_reg.h
===================================================================
--- freebsd/sys/dev/usb/serial/uftdi_reg.h	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uftdi_reg.h	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,314 @@
+/*	$NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */
+/*	$FreeBSD$	*/
+
+/*
+ * Definitions for the FTDI USB Single Port Serial Converter -
+ * known as FTDI_SIO (Serial Input/Output application of the chipset)
+ *
+ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
+ * USB on the other.
+ *
+ * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * of the protocol required to talk to the device and ongoing assistence
+ * during development.
+ *
+ * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc. is the original
+ * author of this file.
+ */
+/* Modified by Lennart Augustsson */
+
+/* Vendor Request Interface */
+#define	FTDI_SIO_RESET 		0	/* Reset the port */
+#define	FTDI_SIO_MODEM_CTRL 	1	/* Set the modem control register */
+#define	FTDI_SIO_SET_FLOW_CTRL	2	/* Set flow control register */
+#define	FTDI_SIO_SET_BAUD_RATE	3	/* Set baud rate */
+#define	FTDI_SIO_SET_DATA	4	/* Set the data characteristics of the
+					 * port */
+#define	FTDI_SIO_GET_STATUS	5	/* Retrieve current value of status
+					 * reg */
+#define	FTDI_SIO_SET_EVENT_CHAR	6	/* Set the event character */
+#define	FTDI_SIO_SET_ERROR_CHAR	7	/* Set the error character */
+#define	FTDI_SIO_SET_LATENCY	9	/* Set the latency timer */
+#define	FTDI_SIO_GET_LATENCY	10	/* Read the latency timer */
+#define	FTDI_SIO_SET_BITMODE	11	/* Set the bit bang I/O mode */
+#define	FTDI_SIO_GET_BITMODE	12	/* Read pin states from any mode */
+#define	FTDI_SIO_READ_EEPROM	144	/* Read eeprom word */
+#define	FTDI_SIO_WRITE_EEPROM	145	/* Write eeprom word */
+#define	FTDI_SIO_ERASE_EEPROM	146	/* Erase entire eeprom */
+
+/* Port Identifier Table */
+#define	FTDI_PIT_DEFAULT 	0	/* SIOA */
+#define	FTDI_PIT_SIOA		1	/* SIOA */
+#define	FTDI_PIT_SIOB		2	/* SIOB */
+#define	FTDI_PIT_PARALLEL	3	/* Parallel */
+
+/* Values for driver_info */
+#define	UFTDI_JTAG_IFACE(i)	(1 << i)	/* Flag interface as jtag */
+#define	UFTDI_JTAG_IFACES_MAX	8		/* Allow up to 8 jtag intfs */
+#define	UFTDI_JTAG_CHECK_STRING	0xff		/* Check product names table */
+#define	UFTDI_JTAG_MASK		0xff
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_RESET
+ * wValue:         Control Value
+ *                   0 = Reset SIO
+ *                   1 = Purge RX buffer
+ *                   2 = Purge TX buffer
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           None
+ *
+ * The Reset SIO command has this effect:
+ *
+ *    Sets flow control set to 'none'
+ *    Event char = 0x0d
+ *    Event trigger = disabled
+ *    Purge RX buffer
+ *    Purge TX buffer
+ *    Clear DTR
+ *    Clear RTS
+ *    baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ */
+/* FTDI_SIO_RESET */
+#define	FTDI_SIO_RESET_SIO 0
+#define	FTDI_SIO_RESET_PURGE_RX 1
+#define	FTDI_SIO_RESET_PURGE_TX 2
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_SET_BAUDRATE
+ * wValue:         BaudRate low bits
+ * wIndex:         Port and BaudRate high bits 
+ * wLength:        0
+ * Data:           None
+ */
+/* FTDI_SIO_SET_BAUDRATE */
+
+/*
+ * BmRequestType:  0100 0000B
+ * bRequest:       FTDI_SIO_SET_DATA
+ * wValue:         Data characteristics (see below)
+ * wIndex:         Port
+ * wLength:        0
+ * Data:           No
+ *
+ * Data characteristics
+ *
+ *   B0..7   Number of data bits
+ *   B8..10  Parity
+ *           0 = None
+ *           1 = Odd
+ *           2 = Even
+ *           3 = Mark
+ *           4 = Space
+ *   B11..13 Stop Bits
+ *           0 = 1
+ *           1 = 1.5
+ *           2 = 2
+ *   B14..15 Reserved
+ *
+ */
+/* FTDI_SIO_SET_DATA */
+#define	FTDI_SIO_SET_DATA_BITS(n) (n)
+#define	FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
+#define	FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
+#define	FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
+#define	FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
+#define	FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
+#define	FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
+#define	FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
+#define	FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
+#define	FTDI_SIO_SET_BREAK (0x1 << 14)
+
+/*
+ * BmRequestType:   0100 0000B
+ * bRequest:        FTDI_SIO_MODEM_CTRL
+ * wValue:          ControlValue (see below)
+ * wIndex:          Port
+ * wLength:         0
+ * Data:            None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ *
+ * ControlValue
+ * B0    DTR state
+ *          0 = reset
+ *          1 = set
+ * B1    RTS state
+ *          0 = reset
+ *          1 = set
+ * B2..7 Reserved
+ * B8    DTR state enable
+ *          0 = ignore
+ *          1 = use DTR state
+ * B9    RTS state enable
+ *          0 = ignore
+ *          1 = use RTS state
+ * B10..15 Reserved
+ */
+/* FTDI_SIO_MODEM_CTRL */
+#define	FTDI_SIO_SET_DTR_MASK 0x1
+#define	FTDI_SIO_SET_DTR_HIGH (1 | ( FTDI_SIO_SET_DTR_MASK  << 8))
+#define	FTDI_SIO_SET_DTR_LOW  (0 | ( FTDI_SIO_SET_DTR_MASK  << 8))
+#define	FTDI_SIO_SET_RTS_MASK 0x2
+#define	FTDI_SIO_SET_RTS_HIGH (2 | ( FTDI_SIO_SET_RTS_MASK << 8))
+#define	FTDI_SIO_SET_RTS_LOW (0 | ( FTDI_SIO_SET_RTS_MASK << 8))
+
+/*
+ *   BmRequestType:  0100 0000b
+ *   bRequest:       FTDI_SIO_SET_FLOW_CTRL
+ *   wValue:         Xoff/Xon
+ *   wIndex:         Protocol/Port - hIndex is protocol / lIndex is port
+ *   wLength:        0
+ *   Data:           None
+ *
+ * hIndex protocol is:
+ *   B0 Output handshaking using RTS/CTS
+ *       0 = disabled
+ *       1 = enabled
+ *   B1 Output handshaking using DTR/DSR
+ *       0 = disabled
+ *       1 = enabled
+ *   B2 Xon/Xoff handshaking
+ *       0 = disabled
+ *       1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define	FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define	FTDI_SIO_RTS_CTS_HS 0x1
+#define	FTDI_SIO_DTR_DSR_HS 0x2
+#define	FTDI_SIO_XON_XOFF_HS 0x4
+
+/*
+ *  BmRequestType:   0100 0000b
+ *  bRequest:        FTDI_SIO_SET_EVENT_CHAR
+ *  wValue:          Event Char
+ *  wIndex:          Port
+ *  wLength:         0
+ *  Data:            None
+ *
+ * wValue:
+ *   B0..7   Event Character
+ *   B8      Event Character Processing
+ *             0 = disabled
+ *             1 = enabled
+ *   B9..15  Reserved
+ *
+ * FTDI_SIO_SET_EVENT_CHAR
+ *
+ * Set the special event character for the specified communications port.
+ * If the device sees this character it will immediately return the
+ * data read so far - rather than wait 40ms or until 62 bytes are read
+ * which is what normally happens.
+ */
+
+/*
+ *  BmRequestType:  0100 0000b
+ *  bRequest:       FTDI_SIO_SET_ERROR_CHAR
+ *  wValue:         Error Char
+ *  wIndex:         Port
+ *  wLength:        0
+ *  Data:           None
+ *
+ *  Error Char
+ *  B0..7  Error Character
+ *  B8     Error Character Processing
+ *           0 = disabled
+ *           1 = enabled
+ *  B9..15 Reserved
+ * FTDI_SIO_SET_ERROR_CHAR
+ * Set the parity error replacement character for the specified communications
+ * port.
+ */
+
+/*
+ *   BmRequestType:   1100 0000b
+ *   bRequest:        FTDI_SIO_GET_MODEM_STATUS
+ *   wValue:          zero
+ *   wIndex:          Port
+ *   wLength:         1
+ *   Data:            Status
+ *
+ * One byte of data is returned
+ * B0..3 0
+ * B4    CTS
+ *         0 = inactive
+ *         1 = active
+ * B5    DSR
+ *         0 = inactive
+ *         1 = active
+ * B6    Ring Indicator (RI)
+ *         0 = inactive
+ *         1 = active
+ * B7    Receive Line Signal Detect (RLSD)
+ *         0 = inactive
+ *         1 = active
+ *
+ * FTDI_SIO_GET_MODEM_STATUS
+ * Retrieve the current value of the modem status register.
+ */
+#define	FTDI_SIO_CTS_MASK 0x10
+#define	FTDI_SIO_DSR_MASK 0x20
+#define	FTDI_SIO_RI_MASK  0x40
+#define	FTDI_SIO_RLSD_MASK 0x80
+
+/*
+ * DATA FORMAT
+ *
+ * IN Endpoint
+ *
+ * The device reserves the first two bytes of data on this endpoint to contain
+ * the current values of the modem and line status registers. In the absence of
+ * data, the device generates a message consisting of these two status bytes
+ * every 40 ms.
+ *
+ * Byte 0: Modem Status
+ *   NOTE: 4 upper bits have same layout as the MSR register in a 16550
+ *
+ * Offset	Description
+ * B0..3	Port
+ * B4		Clear to Send (CTS)
+ * B5		Data Set Ready (DSR)
+ * B6		Ring Indicator (RI)
+ * B7		Receive Line Signal Detect (RLSD)
+ *
+ * Byte 1: Line Status
+ *   NOTE: same layout as the LSR register in a 16550
+ *
+ * Offset	Description
+ * B0	Data Ready (DR)
+ * B1	Overrun Error (OE)
+ * B2	Parity Error (PE)
+ * B3	Framing Error (FE)
+ * B4	Break Interrupt (BI)
+ * B5	Transmitter Holding Register (THRE)
+ * B6	Transmitter Empty (TEMT)
+ * B7	Error in RCVR FIFO
+ * OUT Endpoint
+ *
+ * This device reserves the first bytes of data on this endpoint contain the
+ * length and port identifier of the message. For the FTDI USB Serial converter
+ * the port identifier is always 1.
+ *
+ * Byte 0: Port & length
+ *
+ * Offset	Description
+ * B0..1	Port
+ * B2..7	Length of message - (not including Byte 0)
+ */
+#define	FTDI_PORT_MASK 0x0f
+#define	FTDI_MSR_MASK 0xf0
+#define	FTDI_GET_MSR(p) (((p)[0]) & FTDI_MSR_MASK)
+#define	FTDI_GET_LSR(p) ((p)[1])
+#define	FTDI_LSR_MASK (~0x60)		/* interesting bits */
+#define	FTDI_OUT_TAG(len, port) (((len) << 2) | (port))
Index: freebsd/sys/dev/usb/serial/ugensa.c
===================================================================
--- freebsd/sys/dev/usb/serial/ugensa.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/ugensa.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,403 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $FreeBSD$ */
+/*	$NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $	*/
+
+/*
+ * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roland C. Dowdeswell <elric@netbsd.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * NOTE: all function names beginning like "ugensa_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define	UGENSA_BUF_SIZE		2048	/* bytes */
+#define	UGENSA_CONFIG_INDEX	0
+#define	UGENSA_IFACE_INDEX	0
+#define	UGENSA_IFACE_MAX	8	/* exclusivly */
+
+enum {
+	UGENSA_BULK_DT_WR,
+	UGENSA_BULK_DT_RD,
+	UGENSA_N_TRANSFER,
+};
+
+struct ugensa_sub_softc {
+	struct ucom_softc *sc_ucom_ptr;
+	struct usb_xfer *sc_xfer[UGENSA_N_TRANSFER];
+};
+
+struct ugensa_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom[UGENSA_IFACE_MAX];
+	struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX];
+
+	struct mtx sc_mtx;
+	uint8_t	sc_niface;
+};
+
+/* prototypes */
+
+static device_probe_t ugensa_probe;
+static device_attach_t ugensa_attach;
+static device_detach_t ugensa_detach;
+static void ugensa_free_softc(struct ugensa_softc *);
+
+static usb_callback_t ugensa_bulk_write_callback;
+static usb_callback_t ugensa_bulk_read_callback;
+
+static void	ugensa_free(struct ucom_softc *);
+static void	ugensa_start_read(struct ucom_softc *);
+static void	ugensa_stop_read(struct ucom_softc *);
+static void	ugensa_start_write(struct ucom_softc *);
+static void	ugensa_stop_write(struct ucom_softc *);
+static void	ugensa_poll(struct ucom_softc *ucom);
+
+static const struct usb_config ugensa_xfer_config[UGENSA_N_TRANSFER] = {
+
+	[UGENSA_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UGENSA_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &ugensa_bulk_write_callback,
+	},
+
+	[UGENSA_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UGENSA_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &ugensa_bulk_read_callback,
+	},
+};
+
+static const struct ucom_callback ugensa_callback = {
+	.ucom_start_read = &ugensa_start_read,
+	.ucom_stop_read = &ugensa_stop_read,
+	.ucom_start_write = &ugensa_start_write,
+	.ucom_stop_write = &ugensa_stop_write,
+	.ucom_poll = &ugensa_poll,
+	.ucom_free = &ugensa_free,
+};
+
+static device_method_t ugensa_methods[] = {
+	/* Device methods */
+	DEVMETHOD(device_probe, ugensa_probe),
+	DEVMETHOD(device_attach, ugensa_attach),
+	DEVMETHOD(device_detach, ugensa_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t ugensa_devclass;
+
+static driver_t ugensa_driver = {
+	.name = "ugensa",
+	.methods = ugensa_methods,
+	.size = sizeof(struct ugensa_softc),
+};
+
+static const STRUCT_USB_HOST_ID ugensa_devs[] = {
+	{USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)},
+	{USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)},
+	{USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)},
+	{USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)},
+	{USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)},
+};
+
+DRIVER_MODULE(ugensa, uhub, ugensa_driver, ugensa_devclass, NULL, 0);
+MODULE_DEPEND(ugensa, ucom, 1, 1, 1);
+MODULE_DEPEND(ugensa, usb, 1, 1, 1);
+MODULE_VERSION(ugensa, 1);
+USB_PNP_HOST_INFO(ugensa_devs);
+
+static int
+ugensa_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != 0) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa));
+}
+
+static int
+ugensa_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct ugensa_softc *sc = device_get_softc(dev);
+	struct ugensa_sub_softc *ssc;
+	struct usb_interface *iface;
+	int32_t error;
+	uint8_t iface_index;
+	int x, cnt;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	/* Figure out how many interfaces this device has got */
+	for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) {
+		if ((usbd_get_endpoint(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) ||
+		    (usbd_get_endpoint(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) {
+			/* we have reached the end */
+			break;
+		}
+	}
+
+	if (cnt == 0) {
+		device_printf(dev, "No interfaces\n");
+		goto detach;
+	}
+	for (x = 0; x < cnt; x++) {
+		iface = usbd_get_iface(uaa->device, x);
+		if (iface->idesc->bInterfaceClass != UICLASS_VENDOR)
+			/* Not a serial port, most likely a SD reader */
+			continue;
+
+		ssc = sc->sc_sub + sc->sc_niface;
+		ssc->sc_ucom_ptr = sc->sc_ucom + sc->sc_niface;
+
+		iface_index = (UGENSA_IFACE_INDEX + x);
+		error = usbd_transfer_setup(uaa->device,
+		    &iface_index, ssc->sc_xfer, ugensa_xfer_config,
+		    UGENSA_N_TRANSFER, ssc, &sc->sc_mtx);
+
+		if (error) {
+			device_printf(dev, "allocating USB "
+			    "transfers failed\n");
+			goto detach;
+		}
+		/* clear stall at first run */
+		mtx_lock(&sc->sc_mtx);
+		usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+		usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+		mtx_unlock(&sc->sc_mtx);
+
+		/* initialize port number */
+		ssc->sc_ucom_ptr->sc_portno = sc->sc_niface;
+		sc->sc_niface++;
+		if (x != uaa->info.bIfaceIndex)
+			usbd_set_parent_iface(uaa->device, x,
+			    uaa->info.bIfaceIndex);
+	}
+	device_printf(dev, "Found %d interfaces.\n", sc->sc_niface);
+
+	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc,
+	    &ugensa_callback, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("attach failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);			/* success */
+
+detach:
+	ugensa_detach(dev);
+	return (ENXIO);			/* failure */
+}
+
+static int
+ugensa_detach(device_t dev)
+{
+	struct ugensa_softc *sc = device_get_softc(dev);
+	uint8_t x;
+
+	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
+
+	for (x = 0; x < sc->sc_niface; x++) {
+		usbd_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER);
+	}
+
+	device_claim_softc(dev);
+
+	ugensa_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(ugensa);
+
+static void
+ugensa_free_softc(struct ugensa_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+ugensa_free(struct ucom_softc *ucom)
+{
+	ugensa_free_softc(ucom->sc_parent);
+}
+
+static void
+ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0,
+		    UGENSA_BUF_SIZE, &actlen)) {
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+ugensa_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(ssc->sc_ucom_ptr, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+ugensa_start_read(struct ucom_softc *ucom)
+{
+	struct ugensa_softc *sc = ucom->sc_parent;
+	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+	usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+}
+
+static void
+ugensa_stop_read(struct ucom_softc *ucom)
+{
+	struct ugensa_softc *sc = ucom->sc_parent;
+	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+	usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+}
+
+static void
+ugensa_start_write(struct ucom_softc *ucom)
+{
+	struct ugensa_softc *sc = ucom->sc_parent;
+	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+	usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+}
+
+static void
+ugensa_stop_write(struct ucom_softc *ucom)
+{
+	struct ugensa_softc *sc = ucom->sc_parent;
+	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+	usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+}
+
+static void
+ugensa_poll(struct ucom_softc *ucom)
+{
+	struct ugensa_softc *sc = ucom->sc_parent;
+	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+	usbd_transfer_poll(ssc->sc_xfer, UGENSA_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/uipaq.c
===================================================================
--- freebsd/sys/dev/usb/serial/uipaq.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uipaq.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,1377 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: uipaq.c,v 1.4 2006/11/16 01:33:27 christos Exp $	*/
+/*	$OpenBSD: uipaq.c,v 1.1 2005/06/17 23:50:33 deraadt Exp $	*/
+
+/*
+ * Copyright (c) 2000-2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * iPAQ driver
+ *
+ * 19 July 2003:	Incorporated changes suggested by Sam Lawrance from
+ * 			the uppc module
+ *
+ *
+ * Contact isis@cs.umd.edu if you have any questions/comments about this driver
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define	UIPAQ_CONFIG_INDEX	0	/* config number 1 */
+#define	UIPAQ_IFACE_INDEX	0
+
+#define	UIPAQ_BUF_SIZE		1024
+
+enum {
+	UIPAQ_BULK_DT_WR,
+	UIPAQ_BULK_DT_RD,
+	UIPAQ_N_TRANSFER,
+};
+
+struct uipaq_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UIPAQ_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint16_t sc_line;
+
+	uint8_t	sc_lsr;			/* local status register */
+	uint8_t	sc_msr;			/* modem status register */
+};
+
+static device_probe_t uipaq_probe;
+static device_attach_t uipaq_attach;
+static device_detach_t uipaq_detach;
+static void uipaq_free_softc(struct uipaq_softc *);
+
+static usb_callback_t uipaq_write_callback;
+static usb_callback_t uipaq_read_callback;
+
+static void	uipaq_free(struct ucom_softc *);
+static void	uipaq_start_read(struct ucom_softc *);
+static void	uipaq_stop_read(struct ucom_softc *);
+static void	uipaq_start_write(struct ucom_softc *);
+static void	uipaq_stop_write(struct ucom_softc *);
+static void	uipaq_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	uipaq_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	uipaq_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	uipaq_poll(struct ucom_softc *ucom);
+
+static const struct usb_config uipaq_config_data[UIPAQ_N_TRANSFER] = {
+
+	[UIPAQ_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UIPAQ_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &uipaq_write_callback,
+	},
+
+	[UIPAQ_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UIPAQ_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uipaq_read_callback,
+	},
+};
+
+static const struct ucom_callback uipaq_callback = {
+	.ucom_cfg_set_dtr = &uipaq_cfg_set_dtr,
+	.ucom_cfg_set_rts = &uipaq_cfg_set_rts,
+	.ucom_cfg_set_break = &uipaq_cfg_set_break,
+	.ucom_start_read = &uipaq_start_read,
+	.ucom_stop_read = &uipaq_stop_read,
+	.ucom_start_write = &uipaq_start_write,
+	.ucom_stop_write = &uipaq_stop_write,
+	.ucom_poll = &uipaq_poll,
+	.ucom_free = &uipaq_free,
+};
+
+/*
+ * Much of this list is generated from lists of other drivers that
+ * support the same hardware. Numeric values are used where no usbdevs
+ * entries exist.
+ */
+static const STRUCT_USB_HOST_ID uipaq_devs[] = {
+	/* Socket USB Sync */
+	{USB_VPI(0x0104, 0x00be, 0)},
+	/* USB Sync 0301 */
+	{USB_VPI(0x04ad, 0x0301, 0)},
+	/* USB Sync 0302 */
+	{USB_VPI(0x04ad, 0x0302, 0)},
+	/* USB Sync 0303 */
+	{USB_VPI(0x04ad, 0x0303, 0)},
+	/* GPS Pocket PC USB Sync */
+	{USB_VPI(0x04ad, 0x0306, 0)},
+	/* HHP PDT */
+	{USB_VPI(0x0536, 0x01a0, 0)},
+	/* Intermec Mobile Computer */
+	{USB_VPI(0x067e, 0x1001, 0)},
+	/* Linkup Systems USB Sync */
+	{USB_VPI(0x094b, 0x0001, 0)},
+	/* BCOM USB Sync 0065 */
+	{USB_VPI(0x0960, 0x0065, 0)},
+	/* BCOM USB Sync 0066 */
+	{USB_VPI(0x0960, 0x0066, 0)},
+	/* BCOM USB Sync 0067 */
+	{USB_VPI(0x0960, 0x0067, 0)},
+	/* Portatec USB Sync */
+	{USB_VPI(0x0961, 0x0010, 0)},
+	/* Trimble GeoExplorer */
+	{USB_VPI(0x099e, 0x0052, 0)},
+	/* TDS Data Collector */
+	{USB_VPI(0x099e, 0x4000, 0)},
+	/* Motorola iDEN Smartphone */
+	{USB_VPI(0x0c44, 0x03a2, 0)},
+	/* Cesscom Luxian Series */
+	{USB_VPI(0x0c8e, 0x6000, 0)},
+	/* Motorola PowerPad Pocket PCDevice */
+	{USB_VPI(0x0cad, 0x9001, 0)},
+	/* Freedom Scientific USB Sync */
+	{USB_VPI(0x0f4e, 0x0200, 0)},
+	/* Cyberbank USB Sync */
+	{USB_VPI(0x0f98, 0x0201, 0)},
+	/* Wistron USB Sync */
+	{USB_VPI(0x0fb8, 0x3001, 0)},
+	/* Wistron USB Sync */
+	{USB_VPI(0x0fb8, 0x3002, 0)},
+	/* Wistron USB Sync */
+	{USB_VPI(0x0fb8, 0x3003, 0)},
+	/* Wistron USB Sync */
+	{USB_VPI(0x0fb8, 0x4001, 0)},
+	/* E-TEN USB Sync */
+	{USB_VPI(0x1066, 0x00ce, 0)},
+	/* E-TEN P3XX Pocket PC */
+	{USB_VPI(0x1066, 0x0300, 0)},
+	/* E-TEN P5XX Pocket PC */
+	{USB_VPI(0x1066, 0x0500, 0)},
+	/* E-TEN P6XX Pocket PC */
+	{USB_VPI(0x1066, 0x0600, 0)},
+	/* E-TEN P7XX Pocket PC */
+	{USB_VPI(0x1066, 0x0700, 0)},
+	/* Psion Teklogix Sync 753x */
+	{USB_VPI(0x1114, 0x0001, 0)},
+	/* Psion Teklogix Sync netBookPro */
+	{USB_VPI(0x1114, 0x0004, 0)},
+	/* Psion Teklogix Sync 7525 */
+	{USB_VPI(0x1114, 0x0006, 0)},
+	/* VES USB Sync */
+	{USB_VPI(0x1182, 0x1388, 0)},
+	/* Rugged Pocket PC 2003 */
+	{USB_VPI(0x11d9, 0x1002, 0)},
+	/* Rugged Pocket PC 2003 */
+	{USB_VPI(0x11d9, 0x1003, 0)},
+	/* USB Sync 03 */
+	{USB_VPI(0x1231, 0xce01, 0)},
+	/* USB Sync 03 */
+	{USB_VPI(0x1231, 0xce02, 0)},
+	/* Mio DigiWalker PPC StrongARM */
+	{USB_VPI(0x3340, 0x011c, 0)},
+	/* Mio DigiWalker 338 */
+	{USB_VPI(0x3340, 0x0326, 0)},
+	/* Mio DigiWalker 338 */
+	{USB_VPI(0x3340, 0x0426, 0)},
+	/* Mio DigiWalker USB Sync */
+	{USB_VPI(0x3340, 0x043a, 0)},
+	/* MiTAC USB Sync 528 */
+	{USB_VPI(0x3340, 0x051c, 0)},
+	/* Mio DigiWalker SmartPhone USB Sync */
+	{USB_VPI(0x3340, 0x053a, 0)},
+	/* MiTAC USB Sync */
+	{USB_VPI(0x3340, 0x071c, 0)},
+	/* Generic PPC StrongARM */
+	{USB_VPI(0x3340, 0x0b1c, 0)},
+	/* Generic PPC USB Sync */
+	{USB_VPI(0x3340, 0x0e3a, 0)},
+	/* Itautec USB Sync */
+	{USB_VPI(0x3340, 0x0f1c, 0)},
+	/* Generic SmartPhone USB Sync */
+	{USB_VPI(0x3340, 0x0f3a, 0)},
+	/* Itautec USB Sync */
+	{USB_VPI(0x3340, 0x1326, 0)},
+	/* YAKUMO USB Sync */
+	{USB_VPI(0x3340, 0x191c, 0)},
+	/* Vobis USB Sync */
+	{USB_VPI(0x3340, 0x2326, 0)},
+	/* MEDION Winodws Moble USB Sync */
+	{USB_VPI(0x3340, 0x3326, 0)},
+	/* Legend USB Sync */
+	{USB_VPI(0x3708, 0x20ce, 0)},
+	/* Lenovo USB Sync */
+	{USB_VPI(0x3708, 0x21ce, 0)},
+	/* Mobile Media Technology USB Sync */
+	{USB_VPI(0x4113, 0x0210, 0)},
+	/* Mobile Media Technology USB Sync */
+	{USB_VPI(0x4113, 0x0211, 0)},
+	/* Mobile Media Technology USB Sync */
+	{USB_VPI(0x4113, 0x0400, 0)},
+	/* Mobile Media Technology USB Sync */
+	{USB_VPI(0x4113, 0x0410, 0)},
+	/* Smartphone */
+	{USB_VPI(0x4505, 0x0010, 0)},
+	/* SAGEM Wireless Assistant */
+	{USB_VPI(0x5e04, 0xce00, 0)},
+	/* c10 Series */
+	{USB_VPI(USB_VENDOR_ACER, 0x1631, 0)},
+	/* c20 Series */
+	{USB_VPI(USB_VENDOR_ACER, 0x1632, 0)},
+	/* Acer n10 Handheld USB Sync */
+	{USB_VPI(USB_VENDOR_ACER, 0x16e1, 0)},
+	/* Acer n20 Handheld USB Sync */
+	{USB_VPI(USB_VENDOR_ACER, 0x16e2, 0)},
+	/* Acer n30 Handheld USB Sync */
+	{USB_VPI(USB_VENDOR_ACER, 0x16e3, 0)},
+	/* ASUS USB Sync */
+	{USB_VPI(USB_VENDOR_ASUS, 0x4200, 0)},
+	/* ASUS USB Sync */
+	{USB_VPI(USB_VENDOR_ASUS, 0x4201, 0)},
+	/* ASUS USB Sync */
+	{USB_VPI(USB_VENDOR_ASUS, 0x4202, 0)},
+	/* ASUS USB Sync */
+	{USB_VPI(USB_VENDOR_ASUS, 0x9200, 0)},
+	/* ASUS USB Sync */
+	{USB_VPI(USB_VENDOR_ASUS, 0x9202, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_P535, 0)},
+	/* CASIO USB Sync 2001 */
+	{USB_VPI(USB_VENDOR_CASIO, 0x2001, 0)},
+	/* CASIO USB Sync 2003 */
+	{USB_VPI(USB_VENDOR_CASIO, 0x2003, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_CASIO, USB_PRODUCT_CASIO_BE300, 0)},
+	/* MyGuide 7000 XL USB Sync */
+	{USB_VPI(USB_VENDOR_COMPAL, 0x0531, 0)},
+	/* Compaq iPAQ USB Sync */
+	{USB_VPI(USB_VENDOR_COMPAQ, 0x0032, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQPOCKETPC, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4001, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4002, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4003, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4004, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4005, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4006, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4007, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4008, 0)},
+	/* Dell Axim USB Sync */
+	{USB_VPI(USB_VENDOR_DELL, 0x4009, 0)},
+	/* Fujitsu Siemens Computers USB Sync */
+	{USB_VPI(USB_VENDOR_FSC, 0x1001, 0)},
+	/* FUJITSU USB Sync */
+	{USB_VPI(USB_VENDOR_FUJITSU, 0x1058, 0)},
+	/* FUJITSU USB Sync */
+	{USB_VPI(USB_VENDOR_FUJITSU, 0x1079, 0)},
+	/* Askey USB Sync */
+	{USB_VPI(USB_VENDOR_GIGASET, 0x0601, 0)},
+	/* Hitachi USB Sync */
+	{USB_VPI(USB_VENDOR_HITACHI, 0x0014, 0)},
+	/* HP USB Sync 1612 */
+	{USB_VPI(USB_VENDOR_HP, 0x1216, 0)},
+	/* HP USB Sync 1620 */
+	{USB_VPI(USB_VENDOR_HP, 0x2016, 0)},
+	/* HP USB Sync 1621 */
+	{USB_VPI(USB_VENDOR_HP, 0x2116, 0)},
+	/* HP USB Sync 1622 */
+	{USB_VPI(USB_VENDOR_HP, 0x2216, 0)},
+	/* HP USB Sync 1630 */
+	{USB_VPI(USB_VENDOR_HP, 0x3016, 0)},
+	/* HP USB Sync 1631 */
+	{USB_VPI(USB_VENDOR_HP, 0x3116, 0)},
+	/* HP USB Sync 1632 */
+	{USB_VPI(USB_VENDOR_HP, 0x3216, 0)},
+	/* HP USB Sync 1640 */
+	{USB_VPI(USB_VENDOR_HP, 0x4016, 0)},
+	/* HP USB Sync 1641 */
+	{USB_VPI(USB_VENDOR_HP, 0x4116, 0)},
+	/* HP USB Sync 1642 */
+	{USB_VPI(USB_VENDOR_HP, 0x4216, 0)},
+	/* HP USB Sync 1650 */
+	{USB_VPI(USB_VENDOR_HP, 0x5016, 0)},
+	/* HP USB Sync 1651 */
+	{USB_VPI(USB_VENDOR_HP, 0x5116, 0)},
+	/* HP USB Sync 1652 */
+	{USB_VPI(USB_VENDOR_HP, 0x5216, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2215, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_568J, 0)},
+	/* HTC USB Modem */
+	{USB_VPI(USB_VENDOR_HTC, 0x00cf, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a01, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a02, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a03, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a04, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a05, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a06, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a07, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a08, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a09, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a0a, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a0b, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a0c, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a0d, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a0e, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a0f, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a10, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a11, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a12, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a13, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a14, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a15, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a16, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a17, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a18, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a19, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a1a, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a1b, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a1c, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a1d, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a1e, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a1f, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a20, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a21, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a22, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a23, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a24, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a25, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a26, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a27, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a28, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a29, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a2a, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a2b, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a2c, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a2d, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a2e, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a2f, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a30, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a31, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a32, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a33, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a34, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a35, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a36, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a37, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a38, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a39, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a3a, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a3b, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a3c, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a3d, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a3e, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a3f, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a40, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a41, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a42, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a43, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a44, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a45, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a46, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a47, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a48, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a49, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a4a, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a4b, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a4c, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a4d, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a4e, 0)},
+	/* PocketPC USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a4f, 0)},
+	/* HTC SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a50, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a52, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a53, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a54, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a55, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a56, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a57, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a58, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a59, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a5a, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a5b, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a5c, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a5d, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a5e, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a5f, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a60, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a61, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a62, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a63, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a64, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a65, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a66, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a67, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a68, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a69, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a6a, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a6b, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a6c, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a6d, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a6e, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a6f, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a70, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a71, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a72, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a73, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a74, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a75, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a76, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a77, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a78, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a79, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a7a, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a7b, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a7c, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a7d, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a7e, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a7f, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a80, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a81, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a82, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a83, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a84, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a85, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a86, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a87, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a88, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a89, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a8a, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a8b, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a8c, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a8d, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a8e, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a8f, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a90, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a91, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a92, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a93, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a94, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a95, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a96, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a97, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a98, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a99, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a9a, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a9b, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a9c, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a9d, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a9e, 0)},
+	/* SmartPhone USB Sync */
+	{USB_VPI(USB_VENDOR_HTC, 0x0a9f, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_PPC6700MODEM, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_SMARTPHONE, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_WINMOBILE, 0)},
+	/* High Tech Computer Wizard Smartphone */
+	{USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_WIZARD, 0)},
+	/* JVC USB Sync */
+	{USB_VPI(USB_VENDOR_JVC, 0x3011, 0)},
+	/* JVC USB Sync */
+	{USB_VPI(USB_VENDOR_JVC, 0x3012, 0)},
+	/* LGE USB Sync */
+	{USB_VPI(USB_VENDOR_LG, 0x9c01, 0)},
+	/* Microsoft USB Sync */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x00ce, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0400, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0401, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0402, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0403, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0404, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0405, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0406, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0407, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0408, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0409, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x040a, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x040b, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x040c, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x040d, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x040e, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x040f, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0410, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0411, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0412, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0413, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0414, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0415, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0416, 0)},
+	/* Windows Pocket PC 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0417, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0432, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0433, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0434, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0435, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0436, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0437, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0438, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0439, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x043a, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x043b, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x043c, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x043d, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x043e, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x043f, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0440, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0441, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0442, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0443, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0444, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0445, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0446, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0447, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0448, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0449, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x044a, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x044b, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x044c, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x044d, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x044e, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x044f, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0450, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0451, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0452, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0453, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0454, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0455, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0456, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0457, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0458, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0459, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x045a, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x045b, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x045c, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x045d, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x045e, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x045f, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0460, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0461, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0462, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0463, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0464, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0465, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0466, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0467, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0468, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0469, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x046a, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x046b, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x046c, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x046d, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x046e, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x046f, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0470, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0471, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0472, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0473, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0474, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0475, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0476, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0477, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0478, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x0479, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x047a, 0)},
+	/* Windows Pocket PC 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x047b, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04c8, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04c9, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04ca, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04cb, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04cc, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04cd, 0)},
+	/* Windows Smartphone 2002 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04ce, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04d7, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04d8, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04d9, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04da, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04db, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04dc, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04dd, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04de, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04df, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e0, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e1, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e2, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e3, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e4, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e5, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e6, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e7, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e8, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04e9, 0)},
+	/* Windows Smartphone 2003 */
+	{USB_VPI(USB_VENDOR_MICROSOFT, 0x04ea, 0)},
+	/* Motorola MPx200 Smartphone */
+	{USB_VPI(USB_VENDOR_MOTOROLA2, 0x4204, 0)},
+	/* Motorola MPc GSM */
+	{USB_VPI(USB_VENDOR_MOTOROLA2, 0x4214, 0)},
+	/* Motorola MPx220 Smartphone */
+	{USB_VPI(USB_VENDOR_MOTOROLA2, 0x4224, 0)},
+	/* Motorola MPc CDMA */
+	{USB_VPI(USB_VENDOR_MOTOROLA2, 0x4234, 0)},
+	/* Motorola MPx100 Smartphone */
+	{USB_VPI(USB_VENDOR_MOTOROLA2, 0x4244, 0)},
+	/* NEC USB Sync */
+	{USB_VPI(USB_VENDOR_NEC, 0x00d5, 0)},
+	/* NEC USB Sync */
+	{USB_VPI(USB_VENDOR_NEC, 0x00d6, 0)},
+	/* NEC USB Sync */
+	{USB_VPI(USB_VENDOR_NEC, 0x00d7, 0)},
+	/* NEC USB Sync */
+	{USB_VPI(USB_VENDOR_NEC, 0x8024, 0)},
+	/* NEC USB Sync */
+	{USB_VPI(USB_VENDOR_NEC, 0x8025, 0)},
+	/* Panasonic USB Sync */
+	{USB_VPI(USB_VENDOR_PANASONIC, 0x2500, 0)},
+	/* Samsung NEXiO USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x5f00, 0)},
+	/* Samsung NEXiO USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x5f01, 0)},
+	/* Samsung NEXiO USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x5f02, 0)},
+	/* Samsung NEXiO USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x5f03, 0)},
+	/* Samsung NEXiO USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x5f04, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6611, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6613, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6615, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6617, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6619, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x661b, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x662e, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6630, 0)},
+	/* Samsung MITs USB Sync */
+	{USB_VPI(USB_VENDOR_SAMSUNG, 0x6632, 0)},
+	/* SHARP WS003SH USB Modem */
+	{USB_VPI(USB_VENDOR_SHARP, 0x9102, 0)},
+	/* SHARP WS004SH USB Modem */
+	{USB_VPI(USB_VENDOR_SHARP, 0x9121, 0)},
+	/* SHARP S01SH USB Modem */
+	{USB_VPI(USB_VENDOR_SHARP, 0x9151, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ES, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ADES, 0)},
+	/**/
+	{USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WILLCOM03, 0)},
+	/* Symbol USB Sync */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2000, 0)},
+	/* Symbol USB Sync 0x2001 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2001, 0)},
+	/* Symbol USB Sync 0x2002 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2002, 0)},
+	/* Symbol USB Sync 0x2003 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2003, 0)},
+	/* Symbol USB Sync 0x2004 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2004, 0)},
+	/* Symbol USB Sync 0x2005 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2005, 0)},
+	/* Symbol USB Sync 0x2006 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2006, 0)},
+	/* Symbol USB Sync 0x2007 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2007, 0)},
+	/* Symbol USB Sync 0x2008 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2008, 0)},
+	/* Symbol USB Sync 0x2009 */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x2009, 0)},
+	/* Symbol USB Sync 0x200a */
+	{USB_VPI(USB_VENDOR_SYMBOL, 0x200a, 0)},
+	/* TOSHIBA USB Sync 0700 */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x0700, 0)},
+	/* TOSHIBA Pocket PC e310 */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x0705, 0)},
+	/* TOSHIBA Pocket PC e330 Series */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x0707, 0)},
+	/* TOSHIBA Pocket PC e350Series */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x0708, 0)},
+	/* TOSHIBA Pocket PC e750 Series */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x0709, 0)},
+	/* TOSHIBA Pocket PC e400 Series */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x070a, 0)},
+	/* TOSHIBA Pocket PC e800 Series */
+	{USB_VPI(USB_VENDOR_TOSHIBA, 0x070b, 0)},
+	/* TOSHIBA Pocket PC e740 */
+	{USB_VPI(USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740, 0)},
+	/* ViewSonic Color Pocket PC V35 */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x0ed9, 0)},
+	/* ViewSonic Color Pocket PC V36 */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x1527, 0)},
+	/* ViewSonic Color Pocket PC V37 */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x1529, 0)},
+	/* ViewSonic Color Pocket PC V38 */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x152b, 0)},
+	/* ViewSonic Pocket PC */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x152e, 0)},
+	/* ViewSonic Communicator Pocket PC */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x1921, 0)},
+	/* ViewSonic Smartphone */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x1922, 0)},
+	/* ViewSonic Pocket PC V30 */
+	{USB_VPI(USB_VENDOR_VIEWSONIC, 0x1923, 0)},
+};
+
+static device_method_t uipaq_methods[] = {
+	DEVMETHOD(device_probe, uipaq_probe),
+	DEVMETHOD(device_attach, uipaq_attach),
+	DEVMETHOD(device_detach, uipaq_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uipaq_devclass;
+
+static driver_t uipaq_driver = {
+	.name = "uipaq",
+	.methods = uipaq_methods,
+	.size = sizeof(struct uipaq_softc),
+};
+
+DRIVER_MODULE(uipaq, uhub, uipaq_driver, uipaq_devclass, NULL, 0);
+MODULE_DEPEND(uipaq, ucom, 1, 1, 1);
+MODULE_DEPEND(uipaq, usb, 1, 1, 1);
+MODULE_VERSION(uipaq, 1);
+USB_PNP_HOST_INFO(uipaq_devs);
+
+static int
+uipaq_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UIPAQ_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UIPAQ_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bInterfaceClass == UICLASS_IAD) {
+		DPRINTF("IAD detected - not UIPAQ serial device\n");
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uipaq_devs, sizeof(uipaq_devs), uaa));
+}
+
+static int
+uipaq_attach(device_t dev)
+{
+	struct usb_device_request req;
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uipaq_softc *sc = device_get_softc(dev);
+	int error;
+	uint8_t iface_index;
+	uint8_t i;
+
+	sc->sc_udev = uaa->device;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uipaq", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	/*
+	 * Send magic bytes, cribbed from Linux ipaq driver that
+	 * claims to have sniffed them from Win98. Wait for driver to
+	 * become ready on device side?
+	 */
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, UCDC_LINE_DTR);
+	USETW(req.wIndex, 0x0);
+	USETW(req.wLength, 0);
+	for (i = 0; i != 64; i++) {
+		error =
+		    usbd_do_request_flags(uaa->device, NULL, &req,
+		    NULL, 0, NULL, 100);
+		if (error == 0)
+			break;
+		usb_pause_mtx(NULL, hz / 10);
+	}
+
+	iface_index = UIPAQ_IFACE_INDEX;
+	error = usbd_transfer_setup(uaa->device, &iface_index,
+	    sc->sc_xfer, uipaq_config_data,
+	    UIPAQ_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uipaq_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	uipaq_detach(dev);
+	return (ENXIO);
+}
+
+int
+uipaq_detach(device_t dev)
+{
+	struct uipaq_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UIPAQ_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uipaq_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uipaq);
+
+static void
+uipaq_free_softc(struct uipaq_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uipaq_free(struct ucom_softc *ucom)
+{
+	uipaq_free_softc(ucom->sc_parent);
+}
+
+static void
+uipaq_start_read(struct ucom_softc *ucom)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+}
+
+static void
+uipaq_stop_read(struct ucom_softc *ucom)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+}
+
+static void
+uipaq_start_write(struct ucom_softc *ucom)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+}
+
+static void
+uipaq_stop_write(struct ucom_softc *ucom)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+}
+
+static void
+uipaq_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	DPRINTF("onoff=%d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_DTR;
+	else
+		sc->sc_line &= ~UCDC_LINE_DTR;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = UIPAQ_IFACE_INDEX;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	DPRINTF("onoff=%d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_RTS;
+	else
+		sc->sc_line &= ~UCDC_LINE_RTS;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = UIPAQ_IFACE_INDEX;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t temp;
+
+	temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SEND_BREAK;
+	USETW(req.wValue, temp);
+	req.wIndex[0] = UIPAQ_IFACE_INDEX;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uipaq_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UIPAQ_BUF_SIZE, &actlen)) {
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uipaq_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uipaq_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uipaq_poll(struct ucom_softc *ucom)
+{
+	struct uipaq_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UIPAQ_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/ulpt.c
===================================================================
--- freebsd/sys/dev/usb/serial/ulpt.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/ulpt.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,764 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*	$NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF
+ * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/syslog.h>
+#include <sys/selinfo.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR ulpt_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#ifdef USB_DEBUG
+static int ulpt_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt");
+SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &ulpt_debug, 0, "Debug level");
+#endif
+
+#define	ULPT_BSIZE		(1<<15)	/* bytes */
+#define	ULPT_IFQ_MAXLEN         2	/* units */
+
+#define	UR_GET_DEVICE_ID        0x00
+#define	UR_GET_PORT_STATUS      0x01
+#define	UR_SOFT_RESET           0x02
+
+#define	LPS_NERR		0x08	/* printer no error */
+#define	LPS_SELECT		0x10	/* printer selected */
+#define	LPS_NOPAPER		0x20	/* printer out of paper */
+#define	LPS_INVERT      (LPS_SELECT|LPS_NERR)
+#define	LPS_MASK        (LPS_SELECT|LPS_NERR|LPS_NOPAPER)
+
+enum {
+	ULPT_BULK_DT_WR,
+	ULPT_BULK_DT_RD,
+	ULPT_INTR_DT_RD,
+	ULPT_N_TRANSFER,
+};
+
+struct ulpt_softc {
+	struct usb_fifo_sc sc_fifo;
+	struct usb_fifo_sc sc_fifo_noreset;
+	struct mtx sc_mtx;
+	struct usb_callout sc_watchdog;
+
+	device_t sc_dev;
+	struct usb_device *sc_udev;
+	struct usb_fifo *sc_fifo_open[2];
+	struct usb_xfer *sc_xfer[ULPT_N_TRANSFER];
+
+	int	sc_fflags;		/* current open flags, FREAD and
+					 * FWRITE */
+	uint8_t	sc_iface_no;
+	uint8_t	sc_last_status;
+	uint8_t	sc_zlps;		/* number of consequtive zero length
+					 * packets received */
+};
+
+/* prototypes */
+
+static device_probe_t ulpt_probe;
+static device_attach_t ulpt_attach;
+static device_detach_t ulpt_detach;
+
+static usb_callback_t ulpt_write_callback;
+static usb_callback_t ulpt_read_callback;
+static usb_callback_t ulpt_status_callback;
+
+static void	ulpt_reset(struct ulpt_softc *);
+static void	ulpt_watchdog(void *);
+
+static usb_fifo_close_t ulpt_close;
+static usb_fifo_cmd_t ulpt_start_read;
+static usb_fifo_cmd_t ulpt_start_write;
+static usb_fifo_cmd_t ulpt_stop_read;
+static usb_fifo_cmd_t ulpt_stop_write;
+static usb_fifo_ioctl_t ulpt_ioctl;
+static usb_fifo_open_t ulpt_open;
+static usb_fifo_open_t unlpt_open;
+
+static struct usb_fifo_methods ulpt_fifo_methods = {
+	.f_close = &ulpt_close,
+	.f_ioctl = &ulpt_ioctl,
+	.f_open = &ulpt_open,
+	.f_start_read = &ulpt_start_read,
+	.f_start_write = &ulpt_start_write,
+	.f_stop_read = &ulpt_stop_read,
+	.f_stop_write = &ulpt_stop_write,
+	.basename[0] = "ulpt",
+};
+
+static struct usb_fifo_methods unlpt_fifo_methods = {
+	.f_close = &ulpt_close,
+	.f_ioctl = &ulpt_ioctl,
+	.f_open = &unlpt_open,
+	.f_start_read = &ulpt_start_read,
+	.f_start_write = &ulpt_start_write,
+	.f_stop_read = &ulpt_stop_read,
+	.f_stop_write = &ulpt_stop_write,
+	.basename[0] = "unlpt",
+};
+
+static void
+ulpt_reset(struct ulpt_softc *sc)
+{
+	struct usb_device_request req;
+
+	DPRINTFN(2, "\n");
+
+	req.bRequest = UR_SOFT_RESET;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+	/*
+	 * There was a mistake in the USB printer 1.0 spec that gave the
+	 * request type as UT_WRITE_CLASS_OTHER; it should have been
+	 * UT_WRITE_CLASS_INTERFACE.  Many printers use the old one,
+	 * so we try both.
+	 */
+
+	mtx_lock(&sc->sc_mtx);
+	req.bmRequestType = UT_WRITE_CLASS_OTHER;
+	if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+	    &req, NULL, 0, NULL, 2 * USB_MS_HZ)) {	/* 1.0 */
+		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+		if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+		    &req, NULL, 0, NULL, 2 * USB_MS_HZ)) {	/* 1.1 */
+			/* ignore error */
+		}
+	}
+	mtx_unlock(&sc->sc_mtx);
+}
+
+static void
+ulpt_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ulpt_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_fifo *f = sc->sc_fifo_open[USB_FIFO_TX];
+	struct usb_page_cache *pc;
+	int actlen, max;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	if (f == NULL) {
+		/* should not happen */
+		DPRINTF("no FIFO\n");
+		return;
+	}
+	DPRINTF("state=0x%x actlen=%d\n", USB_GET_STATE(xfer), actlen);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+	case USB_ST_SETUP:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		max = usbd_xfer_max_len(xfer);
+		if (usb_fifo_get_data(f, pc, 0, max, &actlen, 0)) {
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+ulpt_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ulpt_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_fifo *f = sc->sc_fifo_open[USB_FIFO_RX];
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	if (f == NULL) {
+		/* should not happen */
+		DPRINTF("no FIFO\n");
+		return;
+	}
+	DPRINTF("state=0x%x\n", USB_GET_STATE(xfer));
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		if (actlen == 0) {
+
+			if (sc->sc_zlps == 4) {
+				/* enable BULK throttle */
+				usbd_xfer_set_interval(xfer, 500); /* ms */
+			} else {
+				sc->sc_zlps++;
+			}
+		} else {
+			/* disable BULK throttle */
+
+			usbd_xfer_set_interval(xfer, 0);
+			sc->sc_zlps = 0;
+		}
+
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usb_fifo_put_data(f, pc, 0, actlen, 1);
+
+	case USB_ST_SETUP:
+tr_setup:
+		if (usb_fifo_put_bytes_max(f) != 0) {
+			usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+			usbd_transfer_submit(xfer);
+		}
+		break;
+
+	default:			/* Error */
+		/* disable BULK throttle */
+		usbd_xfer_set_interval(xfer, 0);
+		sc->sc_zlps = 0;
+
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+ulpt_status_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct ulpt_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_device_request req;
+	struct usb_page_cache *pc;
+	uint8_t cur_status;
+	uint8_t new_status;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		pc = usbd_xfer_get_frame(xfer, 1);
+		usbd_copy_out(pc, 0, &cur_status, 1);
+
+		cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK;
+		new_status = cur_status & ~sc->sc_last_status;
+		sc->sc_last_status = cur_status;
+
+		if (new_status & LPS_SELECT)
+			log(LOG_NOTICE, "%s: offline\n",
+			    device_get_nameunit(sc->sc_dev));
+		else if (new_status & LPS_NOPAPER)
+			log(LOG_NOTICE, "%s: out of paper\n",
+			    device_get_nameunit(sc->sc_dev));
+		else if (new_status & LPS_NERR)
+			log(LOG_NOTICE, "%s: output error\n",
+			    device_get_nameunit(sc->sc_dev));
+		break;
+
+	case USB_ST_SETUP:
+		req.bmRequestType = UT_READ_CLASS_INTERFACE;
+		req.bRequest = UR_GET_PORT_STATUS;
+		USETW(req.wValue, 0);
+		req.wIndex[0] = sc->sc_iface_no;
+		req.wIndex[1] = 0;
+		USETW(req.wLength, 1);
+
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_in(pc, 0, &req, sizeof(req));
+
+		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+		usbd_xfer_set_frame_len(xfer, 1, 1);
+		usbd_xfer_set_frames(xfer, 2);
+		usbd_transfer_submit(xfer);
+		break;
+
+	default:			/* Error */
+		DPRINTF("error=%s\n", usbd_errstr(error));
+		if (error != USB_ERR_CANCELLED) {
+			/* wait for next watchdog timeout */
+		}
+		break;
+	}
+}
+
+static const struct usb_config ulpt_config[ULPT_N_TRANSFER] = {
+	[ULPT_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = ULPT_BSIZE,
+		.flags = {.pipe_bof = 1,.proxy_buffer = 1},
+		.callback = &ulpt_write_callback,
+	},
+
+	[ULPT_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = ULPT_BSIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1},
+		.callback = &ulpt_read_callback,
+	},
+
+	[ULPT_INTR_DT_RD] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,	/* Control pipe */
+		.direction = UE_DIR_ANY,
+		.bufsize = sizeof(struct usb_device_request) + 1,
+		.callback = &ulpt_status_callback,
+		.timeout = 1000,	/* 1 second */
+	},
+};
+
+static void
+ulpt_start_read(struct usb_fifo *fifo)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	usbd_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]);
+}
+
+static void
+ulpt_stop_read(struct usb_fifo *fifo)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	usbd_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]);
+}
+
+static void
+ulpt_start_write(struct usb_fifo *fifo)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	usbd_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]);
+}
+
+static void
+ulpt_stop_write(struct usb_fifo *fifo)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	usbd_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]);
+}
+
+static int
+ulpt_open(struct usb_fifo *fifo, int fflags)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	/* we assume that open is a serial process */
+
+	if (sc->sc_fflags == 0) {
+
+		/* reset USB parallel port */
+
+		ulpt_reset(sc);
+	}
+	return (unlpt_open(fifo, fflags));
+}
+
+static int
+unlpt_open(struct usb_fifo *fifo, int fflags)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	if (sc->sc_fflags & fflags) {
+		return (EBUSY);
+	}
+	if (fflags & FREAD) {
+		/* clear stall first */
+		mtx_lock(&sc->sc_mtx);
+		usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]);
+		mtx_unlock(&sc->sc_mtx);
+		if (usb_fifo_alloc_buffer(fifo,
+		    usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_RD]),
+		    ULPT_IFQ_MAXLEN)) {
+			return (ENOMEM);
+		}
+		/* set which FIFO is opened */
+		sc->sc_fifo_open[USB_FIFO_RX] = fifo;
+	}
+	if (fflags & FWRITE) {
+		/* clear stall first */
+		mtx_lock(&sc->sc_mtx);
+		usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]);
+		mtx_unlock(&sc->sc_mtx);
+		if (usb_fifo_alloc_buffer(fifo,
+		    usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_WR]),
+		    ULPT_IFQ_MAXLEN)) {
+			return (ENOMEM);
+		}
+		/* set which FIFO is opened */
+		sc->sc_fifo_open[USB_FIFO_TX] = fifo;
+	}
+	sc->sc_fflags |= fflags & (FREAD | FWRITE);
+	return (0);
+}
+
+static void
+ulpt_close(struct usb_fifo *fifo, int fflags)
+{
+	struct ulpt_softc *sc = usb_fifo_softc(fifo);
+
+	sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
+
+	if (fflags & (FREAD | FWRITE)) {
+		usb_fifo_free_buffer(fifo);
+	}
+}
+
+static int
+ulpt_ioctl(struct usb_fifo *fifo, u_long cmd, void *data,
+    int fflags)
+{
+	return (ENODEV);
+}
+
+static const STRUCT_USB_HOST_ID ulpt_devs[] = {
+	/* Uni-directional USB printer */
+	{USB_IFACE_CLASS(UICLASS_PRINTER),
+	 USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER),
+	 USB_IFACE_PROTOCOL(UIPROTO_PRINTER_UNI)},
+
+	/* Bi-directional USB printer */
+	{USB_IFACE_CLASS(UICLASS_PRINTER),
+	 USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER),
+	 USB_IFACE_PROTOCOL(UIPROTO_PRINTER_BI)},
+
+	/* 1284 USB printer */
+	{USB_IFACE_CLASS(UICLASS_PRINTER),
+	 USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER),
+	 USB_IFACE_PROTOCOL(UIPROTO_PRINTER_1284)},
+};
+
+static int
+ulpt_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	int error;
+
+	DPRINTFN(11, "\n");
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return (ENXIO);
+
+	error = usbd_lookup_id_by_uaa(ulpt_devs, sizeof(ulpt_devs), uaa);
+	if (error)
+		return (error);
+
+	return (BUS_PROBE_GENERIC);
+}
+
+static int
+ulpt_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct ulpt_softc *sc = device_get_softc(dev);
+	struct usb_interface_descriptor *id;
+	int unit = device_get_unit(dev);
+	int error;
+	uint8_t iface_index = uaa->info.bIfaceIndex;
+	uint8_t alt_index;
+
+	DPRINTFN(11, "sc=%p\n", sc);
+
+	sc->sc_dev = dev;
+	sc->sc_udev = uaa->device;
+
+	device_set_usb_desc(dev);
+
+	mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE);
+
+	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
+
+	/* search through all the descriptors looking for bidir mode */
+
+	id = usbd_get_interface_descriptor(uaa->iface);
+	alt_index = 0xFF;
+	while (1) {
+		if (id == NULL) {
+			break;
+		}
+		if ((id->bDescriptorType == UDESC_INTERFACE) &&
+		    (id->bLength >= sizeof(*id))) {
+			if (id->bInterfaceNumber != uaa->info.bIfaceNum) {
+				break;
+			} else {
+				alt_index++;
+				if ((id->bInterfaceClass == UICLASS_PRINTER) &&
+				    (id->bInterfaceSubClass == UISUBCLASS_PRINTER) &&
+				    (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) {
+					goto found;
+				}
+			}
+		}
+		id = (void *)usb_desc_foreach(
+		    usbd_get_config_descriptor(uaa->device), (void *)id);
+	}
+	goto detach;
+
+found:
+
+	DPRINTF("setting alternate "
+	    "config number: %d\n", alt_index);
+
+	if (alt_index) {
+
+		error = usbd_set_alt_interface_index
+		    (uaa->device, iface_index, alt_index);
+
+		if (error) {
+			DPRINTF("could not set alternate "
+			    "config, error=%s\n", usbd_errstr(error));
+			goto detach;
+		}
+	}
+	sc->sc_iface_no = id->bInterfaceNumber;
+
+	error = usbd_transfer_setup(uaa->device, &iface_index,
+	    sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER,
+	    sc, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("error=%s\n", usbd_errstr(error));
+		goto detach;
+	}
+	device_printf(sc->sc_dev, "using bi-directional mode\n");
+
+#if 0
+/*
+ * This code is disabled because for some mysterious reason it causes
+ * printing not to work.  But only sometimes, and mostly with
+ * UHCI and less often with OHCI.  *sigh*
+ */
+	{
+		struct usb_config_descriptor *cd = usbd_get_config_descriptor(dev);
+		struct usb_device_request req;
+		int len, alen;
+
+		req.bmRequestType = UT_READ_CLASS_INTERFACE;
+		req.bRequest = UR_GET_DEVICE_ID;
+		USETW(req.wValue, cd->bConfigurationValue);
+		USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting);
+		USETW(req.wLength, sizeof devinfo - 1);
+		error = usbd_do_request_flags(dev, &req, devinfo, USB_SHORT_XFER_OK,
+		    &alen, USB_DEFAULT_TIMEOUT);
+		if (error) {
+			device_printf(sc->sc_dev, "cannot get device id\n");
+		} else if (alen <= 2) {
+			device_printf(sc->sc_dev, "empty device id, no "
+			    "printer connected?\n");
+		} else {
+			/* devinfo now contains an IEEE-1284 device ID */
+			len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff);
+			if (len > sizeof devinfo - 3)
+				len = sizeof devinfo - 3;
+			devinfo[len] = 0;
+			printf("%s: device id <", device_get_nameunit(sc->sc_dev));
+			ieee1284_print_id(devinfo + 2);
+			printf(">\n");
+		}
+	}
+#endif
+
+	error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+	    &ulpt_fifo_methods, &sc->sc_fifo,
+	    unit, -1, uaa->info.bIfaceIndex,
+	    UID_ROOT, GID_OPERATOR, 0644);
+	if (error) {
+		goto detach;
+	}
+	error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+	    &unlpt_fifo_methods, &sc->sc_fifo_noreset,
+	    unit, -1, uaa->info.bIfaceIndex,
+	    UID_ROOT, GID_OPERATOR, 0644);
+	if (error) {
+		goto detach;
+	}
+	/* start reading of status */
+
+	mtx_lock(&sc->sc_mtx);
+	ulpt_watchdog(sc);
+	mtx_unlock(&sc->sc_mtx);
+	return (0);
+
+detach:
+	ulpt_detach(dev);
+	return (ENOMEM);
+}
+
+static int
+ulpt_detach(device_t dev)
+{
+	struct ulpt_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	usb_fifo_detach(&sc->sc_fifo);
+	usb_fifo_detach(&sc->sc_fifo_noreset);
+
+	mtx_lock(&sc->sc_mtx);
+	usb_callout_stop(&sc->sc_watchdog);
+	mtx_unlock(&sc->sc_mtx);
+
+	usbd_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER);
+	usb_callout_drain(&sc->sc_watchdog);
+	mtx_destroy(&sc->sc_mtx);
+
+	return (0);
+}
+
+#if 0
+/* XXX This does not belong here. */
+
+/*
+ * Compare two strings until the second ends.
+ */
+
+static uint8_t
+ieee1284_compare(const char *a, const char *b)
+{
+	while (1) {
+
+		if (*b == 0) {
+			break;
+		}
+		if (*a != *b) {
+			return 1;
+		}
+		b++;
+		a++;
+	}
+	return 0;
+}
+
+/*
+ * Print select parts of an IEEE 1284 device ID.
+ */
+void
+ieee1284_print_id(char *str)
+{
+	char *p, *q;
+
+	for (p = str - 1; p; p = strchr(p, ';')) {
+		p++;			/* skip ';' */
+		if (ieee1284_compare(p, "MFG:") == 0 ||
+		    ieee1284_compare(p, "MANUFACTURER:") == 0 ||
+		    ieee1284_compare(p, "MDL:") == 0 ||
+		    ieee1284_compare(p, "MODEL:") == 0) {
+			q = strchr(p, ';');
+			if (q)
+				printf("%.*s", (int)(q - p + 1), p);
+		}
+	}
+}
+
+#endif
+
+static void
+ulpt_watchdog(void *arg)
+{
+	struct ulpt_softc *sc = arg;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	/* 
+	 * Only read status while the device is not opened, due to
+	 * possible hardware or firmware bug in some printers.
+	 */
+	if (sc->sc_fflags == 0)
+		usbd_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]);
+
+	usb_callout_reset(&sc->sc_watchdog,
+	    hz, &ulpt_watchdog, sc);
+}
+
+static devclass_t ulpt_devclass;
+
+static device_method_t ulpt_methods[] = {
+	DEVMETHOD(device_probe, ulpt_probe),
+	DEVMETHOD(device_attach, ulpt_attach),
+	DEVMETHOD(device_detach, ulpt_detach),
+	DEVMETHOD_END
+};
+
+static driver_t ulpt_driver = {
+	.name = "ulpt",
+	.methods = ulpt_methods,
+	.size = sizeof(struct ulpt_softc),
+};
+
+DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, NULL, 0);
+MODULE_DEPEND(ulpt, usb, 1, 1, 1);
+MODULE_VERSION(ulpt, 1);
+USB_PNP_HOST_INFO(ulpt_devs);
Index: freebsd/sys/dev/usb/serial/umcs.c
===================================================================
--- freebsd/sys/dev/usb/serial/umcs.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/umcs.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,1107 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2010 Lev Serebryakov <lev@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This driver supports several multiport USB-to-RS232 serial adapters driven
+ * by MosChip mos7820 and mos7840, bridge chips.
+ * The adapters are sold under many different brand names.
+ *
+ * Datasheets are available at MosChip www site at
+ * http://www.moschip.com.  The datasheets don't contain full
+ * programming information for the chip.
+ *
+ * It is nornal to have only two enabled ports in devices, based on
+ * quad-port mos7840.
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/linker_set.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR umcs_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#include <dev/usb/serial/umcs.h>
+
+#define	UMCS7840_MODVER	1
+
+#ifdef USB_DEBUG
+static int umcs_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, umcs, CTLFLAG_RW, 0, "USB umcs quadport serial adapter");
+SYSCTL_INT(_hw_usb_umcs, OID_AUTO, debug, CTLFLAG_RWTUN, &umcs_debug, 0, "Debug level");
+#endif					/* USB_DEBUG */
+
+
+/*
+ * Two-port devices (both with 7820 chip and 7840 chip configured as two-port)
+ * have ports 0 and 2, with ports 1 and 3 omitted.
+ * So,PHYSICAL port numbers (indexes) on two-port device will be 0 and 2.
+ * This driver trys to use physical numbers as much as possible.
+ */
+
+/*
+ * Indexed by PHYSICAL port number.
+ * Pack non-regular registers to array to easier if-less access.
+ */
+struct umcs7840_port_registers {
+	uint8_t	reg_sp;			/* SP register. */
+	uint8_t	reg_control;		/* CONTROL register. */
+	uint8_t	reg_dcr;		/* DCR0 register. DCR1 & DCR2 can be
+					 * calculated */
+};
+
+static const struct umcs7840_port_registers umcs7840_port_registers[UMCS7840_MAX_PORTS] = {
+	{.reg_sp = MCS7840_DEV_REG_SP1,.reg_control = MCS7840_DEV_REG_CONTROL1,.reg_dcr = MCS7840_DEV_REG_DCR0_1},
+	{.reg_sp = MCS7840_DEV_REG_SP2,.reg_control = MCS7840_DEV_REG_CONTROL2,.reg_dcr = MCS7840_DEV_REG_DCR0_2},
+	{.reg_sp = MCS7840_DEV_REG_SP3,.reg_control = MCS7840_DEV_REG_CONTROL3,.reg_dcr = MCS7840_DEV_REG_DCR0_3},
+	{.reg_sp = MCS7840_DEV_REG_SP4,.reg_control = MCS7840_DEV_REG_CONTROL4,.reg_dcr = MCS7840_DEV_REG_DCR0_4},
+};
+
+enum {
+	UMCS7840_BULK_RD_EP,
+	UMCS7840_BULK_WR_EP,
+	UMCS7840_N_TRANSFERS
+};
+
+struct umcs7840_softc_oneport {
+	struct usb_xfer *sc_xfer[UMCS7840_N_TRANSFERS];	/* Control structures
+							 * for two transfers */
+
+	uint8_t	sc_lcr;			/* local line control register */
+	uint8_t	sc_mcr;			/* local modem control register */
+};
+
+struct umcs7840_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom[UMCS7840_MAX_PORTS];	/* Need to be continuous
+							 * array, so indexed by
+							 * LOGICAL port
+							 * (subunit) number */
+
+	struct usb_xfer *sc_intr_xfer;	/* Interrupt endpoint */
+
+	device_t sc_dev;		/* Device for error prints */
+	struct usb_device *sc_udev;	/* USB Device for all operations */
+	struct mtx sc_mtx;		/* ucom requires this */
+
+	uint8_t	sc_driver_done;		/* Flag when enumeration is finished */
+
+	uint8_t	sc_numports;		/* Number of ports (subunits) */
+	struct umcs7840_softc_oneport sc_ports[UMCS7840_MAX_PORTS];	/* Indexed by PHYSICAL
+									 * port number. */
+};
+
+/* prototypes */
+static usb_error_t umcs7840_get_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t *);
+static usb_error_t umcs7840_set_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t);
+static usb_error_t umcs7840_get_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t *);
+static usb_error_t umcs7840_set_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t);
+
+static usb_error_t umcs7840_set_baudrate(struct umcs7840_softc *, uint8_t, uint32_t);
+static usb_error_t umcs7840_calc_baudrate(uint32_t rate, uint16_t *, uint8_t *);
+
+static void	umcs7840_free(struct ucom_softc *);
+static void umcs7840_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
+static void umcs7840_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void umcs7840_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void umcs7840_cfg_set_break(struct ucom_softc *, uint8_t);
+static void umcs7840_cfg_param(struct ucom_softc *, struct termios *);
+static void umcs7840_cfg_open(struct ucom_softc *);
+static void umcs7840_cfg_close(struct ucom_softc *);
+
+static int umcs7840_pre_param(struct ucom_softc *, struct termios *);
+
+static void umcs7840_start_read(struct ucom_softc *);
+static void umcs7840_stop_read(struct ucom_softc *);
+
+static void umcs7840_start_write(struct ucom_softc *);
+static void umcs7840_stop_write(struct ucom_softc *);
+
+static void umcs7840_poll(struct ucom_softc *ucom);
+
+static device_probe_t umcs7840_probe;
+static device_attach_t umcs7840_attach;
+static device_detach_t umcs7840_detach;
+static void umcs7840_free_softc(struct umcs7840_softc *);
+
+static usb_callback_t umcs7840_intr_callback;
+static usb_callback_t umcs7840_read_callback1;
+static usb_callback_t umcs7840_read_callback2;
+static usb_callback_t umcs7840_read_callback3;
+static usb_callback_t umcs7840_read_callback4;
+static usb_callback_t umcs7840_write_callback1;
+static usb_callback_t umcs7840_write_callback2;
+static usb_callback_t umcs7840_write_callback3;
+static usb_callback_t umcs7840_write_callback4;
+
+static void umcs7840_read_callbackN(struct usb_xfer *, usb_error_t, uint8_t);
+static void umcs7840_write_callbackN(struct usb_xfer *, usb_error_t, uint8_t);
+
+/* Indexed by LOGICAL port number (subunit), so two-port device uses 0 & 1 */
+static usb_callback_t *umcs7840_rw_callbacks[UMCS7840_MAX_PORTS][UMCS7840_N_TRANSFERS] = {
+	{&umcs7840_read_callback1, &umcs7840_write_callback1},
+	{&umcs7840_read_callback2, &umcs7840_write_callback2},
+	{&umcs7840_read_callback3, &umcs7840_write_callback3},
+	{&umcs7840_read_callback4, &umcs7840_write_callback4},
+};
+
+static const struct usb_config umcs7840_bulk_config_data[UMCS7840_N_TRANSFERS] = {
+	[UMCS7840_BULK_RD_EP] = {
+		.type = UE_BULK,
+		.endpoint = 0x01,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,		/* use wMaxPacketSize */
+		.callback = &umcs7840_read_callback1,
+		.if_index = 0,
+	},
+
+	[UMCS7840_BULK_WR_EP] = {
+		.type = UE_BULK,
+		.endpoint = 0x02,
+		.direction = UE_DIR_OUT,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,		/* use wMaxPacketSize */
+		.callback = &umcs7840_write_callback1,
+		.if_index = 0,
+	},
+};
+
+static const struct usb_config umcs7840_intr_config_data[1] = {
+	[0] = {
+		.type = UE_INTERRUPT,
+		.endpoint = 0x09,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,		/* use wMaxPacketSize */
+		.callback = &umcs7840_intr_callback,
+		.if_index = 0,
+	},
+};
+
+static struct ucom_callback umcs7840_callback = {
+	.ucom_cfg_get_status = &umcs7840_cfg_get_status,
+
+	.ucom_cfg_set_dtr = &umcs7840_cfg_set_dtr,
+	.ucom_cfg_set_rts = &umcs7840_cfg_set_rts,
+	.ucom_cfg_set_break = &umcs7840_cfg_set_break,
+
+	.ucom_cfg_param = &umcs7840_cfg_param,
+	.ucom_cfg_open = &umcs7840_cfg_open,
+	.ucom_cfg_close = &umcs7840_cfg_close,
+
+	.ucom_pre_param = &umcs7840_pre_param,
+
+	.ucom_start_read = &umcs7840_start_read,
+	.ucom_stop_read = &umcs7840_stop_read,
+
+	.ucom_start_write = &umcs7840_start_write,
+	.ucom_stop_write = &umcs7840_stop_write,
+
+	.ucom_poll = &umcs7840_poll,
+	.ucom_free = &umcs7840_free,
+};
+
+static const STRUCT_USB_HOST_ID umcs7840_devs[] = {
+	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7820, 0)},
+	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7840, 0)},
+};
+
+static device_method_t umcs7840_methods[] = {
+	DEVMETHOD(device_probe, umcs7840_probe),
+	DEVMETHOD(device_attach, umcs7840_attach),
+	DEVMETHOD(device_detach, umcs7840_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t umcs7840_devclass;
+
+static driver_t umcs7840_driver = {
+	.name = "umcs7840",
+	.methods = umcs7840_methods,
+	.size = sizeof(struct umcs7840_softc),
+};
+
+DRIVER_MODULE(umcs7840, uhub, umcs7840_driver, umcs7840_devclass, 0, 0);
+MODULE_DEPEND(umcs7840, ucom, 1, 1, 1);
+MODULE_DEPEND(umcs7840, usb, 1, 1, 1);
+MODULE_VERSION(umcs7840, UMCS7840_MODVER);
+USB_PNP_HOST_INFO(umcs7840_devs);
+
+static int
+umcs7840_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST)
+		return (ENXIO);
+	if (uaa->info.bConfigIndex != MCS7840_CONFIG_INDEX)
+		return (ENXIO);
+	if (uaa->info.bIfaceIndex != MCS7840_IFACE_INDEX)
+		return (ENXIO);
+	return (usbd_lookup_id_by_uaa(umcs7840_devs, sizeof(umcs7840_devs), uaa));
+}
+
+static int
+umcs7840_attach(device_t dev)
+{
+	struct usb_config umcs7840_config_tmp[UMCS7840_N_TRANSFERS];
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct umcs7840_softc *sc = device_get_softc(dev);
+
+	uint8_t iface_index = MCS7840_IFACE_INDEX;
+	int error;
+	int subunit;
+	int n;
+	uint8_t data;
+
+	for (n = 0; n < UMCS7840_N_TRANSFERS; ++n)
+		umcs7840_config_tmp[n] = umcs7840_bulk_config_data[n];
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "umcs7840", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_dev = dev;
+	sc->sc_udev = uaa->device;
+
+	/*
+	 * Get number of ports
+	 * Documentation (full datasheet) says, that number of ports is
+	 * set as MCS7840_DEV_MODE_SELECT24S bit in MODE R/Only
+	 * register. But vendor driver uses these undocumented
+	 * register & bit.
+	 *
+	 * Experiments show, that MODE register can have `0'
+	 * (4 ports) bit on 2-port device, so use vendor driver's way.
+	 *
+	 * Also, see notes in header file for these constants.
+	 */
+	umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_GPIO, &data);
+	if (data & MCS7840_DEV_GPIO_4PORTS) {
+		sc->sc_numports = 4;
+		/* Store physical port numbers in sc_portno */
+		sc->sc_ucom[0].sc_portno = 0;
+		sc->sc_ucom[1].sc_portno = 1;
+		sc->sc_ucom[2].sc_portno = 2;
+		sc->sc_ucom[3].sc_portno = 3;
+	} else {
+		sc->sc_numports = 2;
+		/* Store physical port numbers in sc_portno */
+		sc->sc_ucom[0].sc_portno = 0;
+		sc->sc_ucom[1].sc_portno = 2;	/* '1' is skipped */
+	}
+	device_printf(dev, "Chip mcs%04x, found %d active ports\n", uaa->info.idProduct, sc->sc_numports);
+	if (!umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_MODE, &data)) {
+		device_printf(dev, "On-die confguration: RST: active %s, HRD: %s, PLL: %s, POR: %s, Ports: %s, EEPROM write %s, IrDA is %savailable\n",
+		    (data & MCS7840_DEV_MODE_RESET) ? "low" : "high",
+		    (data & MCS7840_DEV_MODE_SER_PRSNT) ? "yes" : "no",
+		    (data & MCS7840_DEV_MODE_PLLBYPASS) ? "bypassed" : "avail",
+		    (data & MCS7840_DEV_MODE_PORBYPASS) ? "bypassed" : "avail",
+		    (data & MCS7840_DEV_MODE_SELECT24S) ? "2" : "4",
+		    (data & MCS7840_DEV_MODE_EEPROMWR) ? "enabled" : "disabled",
+		    (data & MCS7840_DEV_MODE_IRDA) ? "" : "not ");
+	}
+	/* Setup all transfers */
+	for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
+		for (n = 0; n < UMCS7840_N_TRANSFERS; ++n) {
+			/* Set endpoint address */
+			umcs7840_config_tmp[n].endpoint = umcs7840_bulk_config_data[n].endpoint + 2 * sc->sc_ucom[subunit].sc_portno;
+			umcs7840_config_tmp[n].callback = umcs7840_rw_callbacks[subunit][n];
+		}
+		error = usbd_transfer_setup(uaa->device,
+		    &iface_index, sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, umcs7840_config_tmp,
+		    UMCS7840_N_TRANSFERS, sc, &sc->sc_mtx);
+		if (error) {
+			device_printf(dev, "allocating USB transfers failed for subunit %d of %d\n",
+			    subunit + 1, sc->sc_numports);
+			goto detach;
+		}
+	}
+	error = usbd_transfer_setup(uaa->device,
+	    &iface_index, &sc->sc_intr_xfer, umcs7840_intr_config_data,
+	    1, sc, &sc->sc_mtx);
+	if (error) {
+		device_printf(dev, "allocating USB transfers failed for interrupt\n");
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
+		usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_RD_EP]);
+		usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]);
+	}
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numports, sc,
+	    &umcs7840_callback, &sc->sc_mtx);
+	if (error)
+		goto detach;
+
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	umcs7840_detach(dev);
+	return (ENXIO);
+}
+
+static int
+umcs7840_detach(device_t dev)
+{
+	struct umcs7840_softc *sc = device_get_softc(dev);
+	int subunit;
+
+	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
+
+	for (subunit = 0; subunit < sc->sc_numports; ++subunit)
+		usbd_transfer_unsetup(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, UMCS7840_N_TRANSFERS);
+	usbd_transfer_unsetup(&sc->sc_intr_xfer, 1);
+
+	device_claim_softc(dev);
+
+	umcs7840_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(umcs7840);
+
+static void
+umcs7840_free_softc(struct umcs7840_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+umcs7840_free(struct ucom_softc *ucom)
+{
+	umcs7840_free_softc(ucom->sc_parent);
+}
+
+static void
+umcs7840_cfg_open(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint16_t pn = ucom->sc_portno;
+	uint8_t data;
+
+	/* If it very first open, finish global configuration */
+	if (!sc->sc_driver_done) {
+		/*
+		 * USB enumeration is finished, pass internal memory to FIFOs
+		 * If it is done in the end of "attach", kernel panics.
+		 */
+		if (umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, &data))
+			return;
+		data |= MCS7840_DEV_CONTROL1_DRIVER_DONE;
+		if (umcs7840_set_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, data))
+			return;
+		sc->sc_driver_done = 1;
+	}
+	/* Toggle reset bit on-off */
+	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data))
+		return;
+	data |= MCS7840_DEV_SPx_UART_RESET;
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
+		return;
+	data &= ~MCS7840_DEV_SPx_UART_RESET;
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
+		return;
+
+	/* Set RS-232 mode */
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_SCRATCHPAD, MCS7840_UART_SCRATCHPAD_RS232))
+		return;
+
+	/* Disable RX on time of initialization */
+	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
+		return;
+	data |= MCS7840_DEV_CONTROLx_RX_DISABLE;
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
+		return;
+
+	/* Disable all interrupts */
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0))
+		return;
+
+	/* Reset FIFO -- documented */
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR, 0))
+		return;
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR,
+	    MCS7840_UART_FCR_ENABLE | MCS7840_UART_FCR_FLUSHRHR |
+	    MCS7840_UART_FCR_FLUSHTHR | MCS7840_UART_FCR_RTL_1_14))
+		return;
+
+	/* Set 8 bit, no parity, 1 stop bit -- documented */
+	sc->sc_ports[pn].sc_lcr = MCS7840_UART_LCR_DATALEN8 | MCS7840_UART_LCR_STOPB1;
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr))
+		return;
+
+	/*
+	 * Enable DTR/RTS on modem control, enable modem interrupts --
+	 * documented
+	 */
+	sc->sc_ports[pn].sc_mcr = MCS7840_UART_MCR_DTR | MCS7840_UART_MCR_RTS | MCS7840_UART_MCR_IE;
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr))
+		return;
+
+	/* Clearing Bulkin and Bulkout FIFO */
+	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data))
+		return;
+	data |= MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO;
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
+		return;
+	data &= ~(MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO);
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data))
+		return;
+
+	/* Set speed 9600 */
+	if (umcs7840_set_baudrate(sc, pn, 9600))
+		return;
+
+
+	/* Finally enable all interrupts -- documented */
+	/*
+	 * Copied from vendor driver, I don't know why we should read LCR
+	 * here
+	 */
+	if (umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, &sc->sc_ports[pn].sc_lcr))
+		return;
+	if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER,
+	    MCS7840_UART_IER_RXSTAT | MCS7840_UART_IER_MODEM))
+		return;
+
+	/* Enable RX */
+	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
+		return;
+	data &= ~MCS7840_DEV_CONTROLx_RX_DISABLE;
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
+		return;
+
+	DPRINTF("Port %d has been opened\n", pn);
+}
+
+static void
+umcs7840_cfg_close(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint16_t pn = ucom->sc_portno;
+	uint8_t data;
+
+	umcs7840_stop_read(ucom);
+	umcs7840_stop_write(ucom);
+
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, 0);
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0);
+
+	/* Disable RX */
+	if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data))
+		return;
+	data |= MCS7840_DEV_CONTROLx_RX_DISABLE;
+	if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data))
+		return;
+	DPRINTF("Port %d has been closed\n", pn);
+}
+
+static void
+umcs7840_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	if (onoff)
+		sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR;
+	else
+		sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_DTR;
+
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
+	DPRINTF("Port %d DTR set to: %s\n", pn, onoff ? "on" : "off");
+}
+
+static void
+umcs7840_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	if (onoff)
+		sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_RTS;
+	else
+		sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_RTS;
+
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
+	DPRINTF("Port %d RTS set to: %s\n", pn, onoff ? "on" : "off");
+}
+
+static void
+umcs7840_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	if (onoff)
+		sc->sc_ports[pn].sc_lcr |= MCS7840_UART_LCR_BREAK;
+	else
+		sc->sc_ports[pn].sc_lcr &= ~MCS7840_UART_LCR_BREAK;
+
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr);
+	DPRINTF("Port %d BREAK set to: %s\n", pn, onoff ? "on" : "off");
+}
+
+
+static void
+umcs7840_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+	uint8_t lcr = sc->sc_ports[pn].sc_lcr;
+	uint8_t mcr = sc->sc_ports[pn].sc_mcr;
+
+	DPRINTF("Port %d config:\n", pn);
+	if (t->c_cflag & CSTOPB) {
+		DPRINTF("  2 stop bits\n");
+		lcr |= MCS7840_UART_LCR_STOPB2;
+	} else {
+		lcr |= MCS7840_UART_LCR_STOPB1;
+		DPRINTF("  1 stop bit\n");
+	}
+
+	lcr &= ~MCS7840_UART_LCR_PARITYMASK;
+	if (t->c_cflag & PARENB) {
+		lcr |= MCS7840_UART_LCR_PARITYON;
+		if (t->c_cflag & PARODD) {
+			lcr = MCS7840_UART_LCR_PARITYODD;
+			DPRINTF("  parity on - odd\n");
+		} else {
+			lcr = MCS7840_UART_LCR_PARITYEVEN;
+			DPRINTF("  parity on - even\n");
+		}
+	} else {
+		lcr &= ~MCS7840_UART_LCR_PARITYON;
+		DPRINTF("  parity off\n");
+	}
+
+	lcr &= ~MCS7840_UART_LCR_DATALENMASK;
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		lcr |= MCS7840_UART_LCR_DATALEN5;
+		DPRINTF("  5 bit\n");
+		break;
+	case CS6:
+		lcr |= MCS7840_UART_LCR_DATALEN6;
+		DPRINTF("  6 bit\n");
+		break;
+	case CS7:
+		lcr |= MCS7840_UART_LCR_DATALEN7;
+		DPRINTF("  7 bit\n");
+		break;
+	case CS8:
+		lcr |= MCS7840_UART_LCR_DATALEN8;
+		DPRINTF("  8 bit\n");
+		break;
+	}
+
+	if (t->c_cflag & CRTSCTS) {
+		mcr |= MCS7840_UART_MCR_CTSRTS;
+		DPRINTF("  CTS/RTS\n");
+	} else
+		mcr &= ~MCS7840_UART_MCR_CTSRTS;
+
+	if (t->c_cflag & (CDTR_IFLOW | CDSR_OFLOW)) {
+		mcr |= MCS7840_UART_MCR_DTRDSR;
+		DPRINTF("  DTR/DSR\n");
+	} else
+		mcr &= ~MCS7840_UART_MCR_DTRDSR;
+
+	sc->sc_ports[pn].sc_lcr = lcr;
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr);
+	DPRINTF("Port %d LCR=%02x\n", pn, sc->sc_ports[pn].sc_lcr);
+
+	sc->sc_ports[pn].sc_mcr = mcr;
+	umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr);
+	DPRINTF("Port %d MCR=%02x\n", pn, sc->sc_ports[pn].sc_mcr);
+
+	umcs7840_set_baudrate(sc, pn, t->c_ospeed);
+}
+
+
+static int
+umcs7840_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	uint8_t clk;
+	uint16_t divisor;
+
+	if (umcs7840_calc_baudrate(t->c_ospeed, &divisor, &clk) || !divisor)
+		return (EINVAL);
+	return (0);
+}
+
+static void
+umcs7840_start_read(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	/* Start interrupt transfer */
+	usbd_transfer_start(sc->sc_intr_xfer);
+
+	/* Start read transfer */
+	usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]);
+}
+
+static void
+umcs7840_stop_read(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	/* Stop read transfer */
+	usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]);
+}
+
+static void
+umcs7840_start_write(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	/* Start interrupt transfer */
+	usbd_transfer_start(sc->sc_intr_xfer);
+
+	/* Start write transfer */
+	usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]);
+}
+
+static void
+umcs7840_stop_write(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+
+	/* Stop write transfer */
+	usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]);
+}
+
+static void
+umcs7840_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+	uint8_t pn = ucom->sc_portno;
+	uint8_t	hw_msr = 0;	/* local modem status register */
+
+	/*
+	 * Read status registers.  MSR bits need translation from ns16550 to
+	 * SER_* values.  LSR bits are ns16550 in hardware and ucom.
+	 */
+	umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LSR, lsr);
+	umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_MSR, &hw_msr);
+
+	if (hw_msr & MCS7840_UART_MSR_NEGCTS)
+		*msr |= SER_CTS;
+
+	if (hw_msr & MCS7840_UART_MSR_NEGDCD)
+		*msr |= SER_DCD;
+
+	if (hw_msr & MCS7840_UART_MSR_NEGRI)
+		*msr |= SER_RI;
+
+	if (hw_msr & MCS7840_UART_MSR_NEGDSR)
+		*msr |= SER_DSR;
+
+	DPRINTF("Port %d status: LSR=%02x MSR=%02x\n", ucom->sc_portno, *lsr, *msr);
+}
+
+static void
+umcs7840_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[13];
+	int actlen;
+	int subunit;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen == 5 || actlen == 13) {
+			pc = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_out(pc, 0, buf, actlen);
+			/* Check status of all ports */
+			for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
+				uint8_t pn = sc->sc_ucom[subunit].sc_portno;
+
+				if (buf[pn] & MCS7840_UART_ISR_NOPENDING)
+					continue;
+				DPRINTF("Port %d has pending interrupt: %02x (FIFO: %02x)\n", pn, buf[pn] & MCS7840_UART_ISR_INTMASK, buf[pn] & (~MCS7840_UART_ISR_INTMASK));
+				switch (buf[pn] & MCS7840_UART_ISR_INTMASK) {
+				case MCS7840_UART_ISR_RXERR:
+				case MCS7840_UART_ISR_RXHASDATA:
+				case MCS7840_UART_ISR_RXTIMEOUT:
+				case MCS7840_UART_ISR_MSCHANGE:
+					ucom_status_change(&sc->sc_ucom[subunit]);
+					break;
+				default:
+					/* Do nothing */
+					break;
+				}
+			}
+		} else
+			device_printf(sc->sc_dev, "Invalid interrupt data length %d", actlen);
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umcs7840_read_callback1(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_read_callbackN(xfer, error, 0);
+}
+
+static void
+umcs7840_read_callback2(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_read_callbackN(xfer, error, 1);
+}
+static void
+umcs7840_read_callback3(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_read_callbackN(xfer, error, 2);
+}
+
+static void
+umcs7840_read_callback4(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_read_callbackN(xfer, error, 3);
+}
+
+static void
+umcs7840_read_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit)
+{
+	struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
+	struct ucom_softc *ucom = &sc->sc_ucom[subunit];
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	DPRINTF("Port %d read, state = %d, data length = %d\n", ucom->sc_portno, USB_GET_STATE(xfer), actlen);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(ucom, pc, 0, actlen);
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umcs7840_write_callback1(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_write_callbackN(xfer, error, 0);
+}
+
+static void
+umcs7840_write_callback2(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_write_callbackN(xfer, error, 1);
+}
+
+static void
+umcs7840_write_callback3(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_write_callbackN(xfer, error, 2);
+}
+
+static void
+umcs7840_write_callback4(struct usb_xfer *xfer, usb_error_t error)
+{
+	umcs7840_write_callbackN(xfer, error, 3);
+}
+
+static void
+umcs7840_write_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit)
+{
+	struct umcs7840_softc *sc = usbd_xfer_softc(xfer);
+	struct ucom_softc *ucom = &sc->sc_ucom[subunit];
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	DPRINTF("Port %d write, state = %d\n", ucom->sc_portno, USB_GET_STATE(xfer));
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(ucom, pc, 0, usbd_xfer_max_len(xfer), &actlen)) {
+			DPRINTF("Port %d write, has %d bytes\n", ucom->sc_portno, actlen);
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umcs7840_poll(struct ucom_softc *ucom)
+{
+	struct umcs7840_softc *sc = ucom->sc_parent;
+
+	DPRINTF("Port %d poll\n", ucom->sc_portno);
+	usbd_transfer_poll(sc->sc_ports[ucom->sc_portno].sc_xfer, UMCS7840_N_TRANSFERS);
+	usbd_transfer_poll(&sc->sc_intr_xfer, 1);
+}
+
+static usb_error_t
+umcs7840_get_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t *data)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+	uint16_t len;
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = MCS7840_RDREQ;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, reg);
+	USETW(req.wLength, UMCS7840_READ_LENGTH);
+
+	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT);
+	if (err == USB_ERR_NORMAL_COMPLETION && len != 1) {
+		device_printf(sc->sc_dev, "Reading register %d failed: invalid length %d\n", reg, len);
+		return (USB_ERR_INVAL);
+	} else if (err)
+		device_printf(sc->sc_dev, "Reading register %d failed: %s\n", reg, usbd_errstr(err));
+	return (err);
+}
+
+static usb_error_t
+umcs7840_set_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t data)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = MCS7840_WRREQ;
+	USETW(req.wValue, data);
+	USETW(req.wIndex, reg);
+	USETW(req.wLength, 0);
+
+	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT);
+	if (err)
+		device_printf(sc->sc_dev, "Writing register %d failed: %s\n", reg, usbd_errstr(err));
+
+	return (err);
+}
+
+static usb_error_t
+umcs7840_get_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t *data)
+{
+	struct usb_device_request req;
+	uint16_t wVal;
+	usb_error_t err;
+	uint16_t len;
+
+	/* portno is port number */
+	wVal = ((uint16_t)(portno + 1)) << 8;
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = MCS7840_RDREQ;
+	USETW(req.wValue, wVal);
+	USETW(req.wIndex, reg);
+	USETW(req.wLength, UMCS7840_READ_LENGTH);
+
+	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT);
+	if (err == USB_ERR_NORMAL_COMPLETION && len != 1) {
+		device_printf(sc->sc_dev, "Reading UART%d register %d failed: invalid length %d\n", portno, reg, len);
+		return (USB_ERR_INVAL);
+	} else if (err)
+		device_printf(sc->sc_dev, "Reading UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err));
+	return (err);
+}
+
+static usb_error_t
+umcs7840_set_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t data)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+	uint16_t wVal;
+
+	/* portno is port number */
+	wVal = ((uint16_t)(portno + 1)) << 8 | data;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = MCS7840_WRREQ;
+	USETW(req.wValue, wVal);
+	USETW(req.wIndex, reg);
+	USETW(req.wLength, 0);
+
+	err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT);
+	if (err)
+		device_printf(sc->sc_dev, "Writing UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err));
+	return (err);
+}
+
+static usb_error_t
+umcs7840_set_baudrate(struct umcs7840_softc *sc, uint8_t portno, uint32_t rate)
+{
+	usb_error_t err;
+	uint16_t divisor;
+	uint8_t clk;
+	uint8_t data;
+
+	if (umcs7840_calc_baudrate(rate, &divisor, &clk)) {
+		DPRINTF("Port %d bad speed: %d\n", portno, rate);
+		return (-1);
+	}
+	if (divisor == 0 || (clk & MCS7840_DEV_SPx_CLOCK_MASK) != clk) {
+		DPRINTF("Port %d bad speed calculation: %d\n", portno, rate);
+		return (-1);
+	}
+	DPRINTF("Port %d set speed: %d (%02x / %d)\n", portno, rate, clk, divisor);
+
+	/* Set clock source for standard BAUD frequences */
+	err = umcs7840_get_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, &data);
+	if (err)
+		return (err);
+	data &= MCS7840_DEV_SPx_CLOCK_MASK;
+	data |= clk;
+	err = umcs7840_set_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, data);
+	if (err)
+		return (err);
+
+	/* Set divider */
+	sc->sc_ports[portno].sc_lcr |= MCS7840_UART_LCR_DIVISORS;
+	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr);
+	if (err)
+		return (err);
+
+	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLL, (uint8_t)(divisor & 0xff));
+	if (err)
+		return (err);
+	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLM, (uint8_t)((divisor >> 8) & 0xff));
+	if (err)
+		return (err);
+
+	/* Turn off access to DLL/DLM registers of UART */
+	sc->sc_ports[portno].sc_lcr &= ~MCS7840_UART_LCR_DIVISORS;
+	err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr);
+	if (err)
+		return (err);
+	return (0);
+}
+
+/* Maximum speeds for standard frequences, when PLL is not used */
+static const uint32_t umcs7840_baudrate_divisors[] = {0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728,};
+static const uint8_t umcs7840_baudrate_divisors_len = nitems(umcs7840_baudrate_divisors);
+
+static usb_error_t
+umcs7840_calc_baudrate(uint32_t rate, uint16_t *divisor, uint8_t *clk)
+{
+	uint8_t i = 0;
+
+	if (rate > umcs7840_baudrate_divisors[umcs7840_baudrate_divisors_len - 1])
+		return (-1);
+
+	for (i = 0; i < umcs7840_baudrate_divisors_len - 1 &&
+	    !(rate > umcs7840_baudrate_divisors[i] && rate <= umcs7840_baudrate_divisors[i + 1]); ++i);
+	if (rate == 0)
+		*divisor = 1;	/* XXX */
+	else
+		*divisor = umcs7840_baudrate_divisors[i + 1] / rate;
+	/* 0x00 .. 0x70 */
+	*clk = i << MCS7840_DEV_SPx_CLOCK_SHIFT;
+	return (0);
+}
Index: freebsd/sys/dev/usb/serial/umcs.h
===================================================================
--- freebsd/sys/dev/usb/serial/umcs.h	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/umcs.h	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,644 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2010 Lev Serebryakov <lev@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _UMCS7840_H_
+#define	_UMCS7840_H_
+
+#define	UMCS7840_MAX_PORTS	4
+
+#define	UMCS7840_READ_LENGTH	1	/* bytes */
+#define	UMCS7840_CTRL_TIMEOUT	500	/* ms */
+
+/* Read/Wrtire registers vendor commands */
+#define	MCS7840_RDREQ		0x0d
+#define	MCS7840_WRREQ		0x0e
+
+/* Read/Wrtie EEPROM values */
+#define	MCS7840_EEPROM_RW_WVALUE	0x0900
+
+/*
+ *   All these registers are documented only in full datasheet,
+ * which can be requested from MosChip tech support.
+ */
+#define	MCS7840_DEV_REG_SP1		0x00	/* Options for for UART 1, R/W */
+#define	MCS7840_DEV_REG_CONTROL1	0x01	/* Control bits for UART 1,
+						 * R/W */
+#define	MCS7840_DEV_REG_PINPONGHIGH	0x02	/* High bits of ping-pong
+						 * register, R/W */
+#define	MCS7840_DEV_REG_PINPONGLOW	0x03	/* Low bits of ping-pong
+						 * register, R/W */
+/* DCRx_1 Registers goes here (see below, they are documented) */
+#define	MCS7840_DEV_REG_GPIO		0x07	/* GPIO_0 and GPIO_1 bits,
+						 * undocumented, see notes
+						 * below R/W */
+#define	MCS7840_DEV_REG_SP2		0x08	/* Options for for UART 2, R/W */
+#define	MCS7840_DEV_REG_CONTROL2	0x09	/* Control bits for UART 2,
+						 * R/W */
+#define	MCS7840_DEV_REG_SP3		0x0a	/* Options for for UART 3, R/W */
+#define	MCS7840_DEV_REG_CONTROL3	0x0b	/* Control bits for UART 3,
+						 * R/W */
+#define	MCS7840_DEV_REG_SP4		0x0c	/* Options for for UART 4, R/W */
+#define	MCS7840_DEV_REG_CONTROL4	0x0d	/* Control bits for UART 4,
+						 * R/W */
+#define	MCS7840_DEV_REG_PLL_DIV_M	0x0e	/* Pre-diviedr for PLL, R/W */
+#define	MCS7840_DEV_REG_UNKNOWN1	0x0f	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_PLL_DIV_N	0x10	/* Loop divider for PLL, R/W */
+#define	MCS7840_DEV_REG_CLOCK_MUX	0x12	/* PLL input clock & Interrupt
+						 * endpoint control, R/W */
+#define	MCS7840_DEV_REG_UNKNOWN2	0x11	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_CLOCK_SELECT12	0x13	/* Clock source for ports 1 &
+						 * 2, R/W */
+#define	MCS7840_DEV_REG_CLOCK_SELECT34	0x14	/* Clock source for ports 3 &
+						 * 4, R/W */
+#define	MCS7840_DEV_REG_UNKNOWN3	0x15	/* NOT MENTIONED AND NOT USED */
+/* DCRx_2-DCRx_4 Registers goes here (see below, they are documented) */
+#define	MCS7840_DEV_REG_UNKNOWN4	0x1f	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWN5	0x20	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWN6	0x21	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWN7	0x22	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWN8	0x23	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWN9	0x24	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWNA	0x25	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWNB	0x26	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWNC	0x27	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWND	0x28	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWNE	0x29	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_UNKNOWNF	0x2a	/* NOT MENTIONED AND NOT USED */
+#define	MCS7840_DEV_REG_MODE		0x2b	/* Hardware configuration,
+						 * R/Only */
+#define	MCS7840_DEV_REG_SP1_ICG		0x2c	/* Inter character gap
+						 * configuration for Port 1,
+						 * R/W */
+#define	MCS7840_DEV_REG_SP2_ICG		0x2d	/* Inter character gap
+						 * configuration for Port 2,
+						 * R/W */
+#define	MCS7840_DEV_REG_SP3_ICG		0x2e	/* Inter character gap
+						 * configuration for Port 3,
+						 * R/W */
+#define	MCS7840_DEV_REG_SP4_ICG		0x2f	/* Inter character gap
+						 * configuration for Port 4,
+						 * R/W */
+#define	MCS7840_DEV_REG_RX_SAMPLING12	0x30	/* RX sampling for ports 1 &
+						 * 2, R/W */
+#define	MCS7840_DEV_REG_RX_SAMPLING34	0x31	/* RX sampling for ports 3 &
+						 * 4, R/W */
+#define	MCS7840_DEV_REG_BI_FIFO_STAT1	0x32	/* Bulk-In FIFO Stat for Port
+						 * 1, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BO_FIFO_STAT1	0x33	/* Bulk-out FIFO Stat for Port
+						 * 1, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BI_FIFO_STAT2	0x34	/* Bulk-In FIFO Stat for Port
+						 * 2, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BO_FIFO_STAT2	0x35	/* Bulk-out FIFO Stat for Port
+						 * 2, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BI_FIFO_STAT3	0x36	/* Bulk-In FIFO Stat for Port
+						 * 3, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BO_FIFO_STAT3	0x37	/* Bulk-out FIFO Stat for Port
+						 * 3, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BI_FIFO_STAT4	0x38	/* Bulk-In FIFO Stat for Port
+						 * 4, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_BO_FIFO_STAT4	0x39	/* Bulk-out FIFO Stat for Port
+						 * 4, contains number of
+						 * available bytes, R/Only */
+#define	MCS7840_DEV_REG_ZERO_PERIOD1	0x3a	/* Period between zero out
+						 * frames for Port 1, R/W */
+#define	MCS7840_DEV_REG_ZERO_PERIOD2	0x3b	/* Period between zero out
+						 * frames for Port 1, R/W */
+#define	MCS7840_DEV_REG_ZERO_PERIOD3	0x3c	/* Period between zero out
+						 * frames for Port 1, R/W */
+#define	MCS7840_DEV_REG_ZERO_PERIOD4	0x3d	/* Period between zero out
+						 * frames for Port 1, R/W */
+#define	MCS7840_DEV_REG_ZERO_ENABLE	0x3e	/* Enable/disable of zero out
+						 * frames, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_LOW1	0x3f	/* Low 8 bits of threshold
+						 * value for Bulk-Out for Port
+						 * 1, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_HIGH1	0x40	/* High 1 bit of threshold
+						 * value for Bulk-Out and
+						 * enable flag for Port 1, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_LOW2	0x41	/* Low 8 bits of threshold
+						 * value for Bulk-Out for Port
+						 * 2, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_HIGH2	0x42	/* High 1 bit of threshold
+						 * value for Bulk-Out and
+						 * enable flag for Port 2, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_LOW3	0x43	/* Low 8 bits of threshold
+						 * value for Bulk-Out for Port
+						 * 3, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_HIGH3	0x44	/* High 1 bit of threshold
+						 * value for Bulk-Out and
+						 * enable flag for Port 3, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_LOW4	0x45	/* Low 8 bits of threshold
+						 * value for Bulk-Out for Port
+						 * 4, R/W */
+#define	MCS7840_DEV_REG_THR_VAL_HIGH4	0x46	/* High 1 bit of threshold
+						 * value for Bulk-Out and
+						 * enable flag for Port 4, R/W */
+
+/* Bits for SPx registers */
+#define	MCS7840_DEV_SPx_LOOP_PIPES	0x01	/* Loop Bulk-Out FIFO to the
+						 * Bulk-In FIFO, default = 0 */
+#define	MCS7840_DEV_SPx_SKIP_ERR_DATA	0x02	/* Drop data bytes from UART,
+						 * which were recevied with
+						 * errors, default = 0 */
+#define	MCS7840_DEV_SPx_RESET_OUT_FIFO	0x04	/* Reset Bulk-Out FIFO */
+#define	MCS7840_DEV_SPx_RESET_IN_FIFO	0x08	/* Reset Bulk-In FIFO */
+#define	MCS7840_DEV_SPx_CLOCK_MASK	0x70	/* Mask to extract Baud CLK
+						 * source */
+#define	MCS7840_DEV_SPx_CLOCK_X1	0x00	/* CLK =  1.8432Mhz, max speed
+						 * = 115200 bps, default */
+#define	MCS7840_DEV_SPx_CLOCK_X2	0x10	/* CLK =  3.6864Mhz, max speed
+						 * = 230400 bps */
+#define	MCS7840_DEV_SPx_CLOCK_X35	0x20	/* CLK =  6.4512Mhz, max speed
+						 * = 403200 bps */
+#define	MCS7840_DEV_SPx_CLOCK_X4	0x30	/* CLK =  7.3728Mhz, max speed
+						 * = 460800 bps */
+#define	MCS7840_DEV_SPx_CLOCK_X7	0x40	/* CLK = 12.9024Mhz, max speed
+						 * = 806400 bps */
+#define	MCS7840_DEV_SPx_CLOCK_X8	0x50	/* CLK = 14.7456Mhz, max speed
+						 * = 921600 bps */
+#define	MCS7840_DEV_SPx_CLOCK_24MHZ	0x60	/* CLK = 24.0000Mhz, max speed
+						 * = 1.5 Mbps */
+#define	MCS7840_DEV_SPx_CLOCK_48MHZ	0x70	/* CLK = 48.0000Mhz, max speed
+						 * = 3.0 Mbps */
+#define	MCS7840_DEV_SPx_CLOCK_SHIFT	4	/* Value 0..7 can be shifted
+						 * to get clock value */
+#define	MCS7840_DEV_SPx_UART_RESET	0x80	/* Reset UART */
+
+/* Bits for CONTROLx registers */
+#define	MCS7840_DEV_CONTROLx_HWFC		0x01	/* Enable hardware flow
+							 * control (when power
+							 * down? It is unclear
+							 * in documents),
+							 * default = 0 */
+#define	MCS7840_DEV_CONTROLx_UNUNSED1		0x02	/* Reserved */
+#define	MCS7840_DEV_CONTROLx_CTS_ENABLE		0x04	/* CTS changes are
+							 * translated to MSR,
+							 * default = 0 */
+#define	MCS7840_DEV_CONTROLx_UNUSED2		0x08	/* Reserved for ports
+							 * 2,3,4 */
+#define	MCS7840_DEV_CONTROL1_DRIVER_DONE	0x08	/* USB enumerating is
+							 * finished, USB
+							 * enumeration memory
+							 * can be used as FIFOs */
+#define	MCS7840_DEV_CONTROLx_RX_NEGATE		0x10	/* Negate RX input,
+							 * works for IrDA mode
+							 * only, default = 0 */
+#define	MCS7840_DEV_CONTROLx_RX_DISABLE		0x20	/* Disable RX logic,
+							 * works only for
+							 * RS-232/RS-485 mode,
+							 * default = 0 */
+#define	MCS7840_DEV_CONTROLx_FSM_CONTROL	0x40	/* Disable RX FSM when
+							 * TX is in progress,
+							 * works for IrDA mode
+							 * only, default = 0 */
+#define	MCS7840_DEV_CONTROLx_UNUSED3		0x80	/* Reserved */
+
+/*
+ * Bits for PINPONGx registers
+ * These registers control how often two input buffers
+ * for Bulk-In FIFOs are swapped. One of buffers is used
+ * for USB trnasfer, other for receiving data from UART.
+ * Exact meaning of 15 bit value in these registers is unknown
+ */
+#define	MCS7840_DEV_PINPONGHIGH_MULT	128	/* Only 7 bits in PINPONGLOW
+						 * register */
+#define	MCS7840_DEV_PINPONGLOW_BITS	7	/* Only 7 bits in PINPONGLOW
+						 * register */
+
+/*
+ *  THIS ONE IS UNDOCUMENTED IN FULL DATASHEET, but e-mail from tech support
+ * confirms, that it is register for GPIO_0 and GPIO_1 data input/output.
+ *  Chips has 2 GPIO, but first one (lower bit) MUST be used by device
+ * authors as "number of port" indicator, grounded (0) for two-port
+ * devices and pulled-up to 1 for 4-port devices.
+ */
+#define	MCS7840_DEV_GPIO_4PORTS		0x01	/* Device has 4 ports
+						 * configured */
+#define	MCS7840_DEV_GPIO_GPIO_0		0x01	/* The same as above */
+#define	MCS7840_DEV_GPIO_GPIO_1		0x02	/* GPIO_1 data */
+
+/*
+ * Constants for PLL dividers
+ * Ouptut frequency of PLL is:
+ *   Fout = (N/M) * Fin.
+ * Default PLL input frequency Fin is 12Mhz (on-chip).
+ */
+#define	MCS7840_DEV_PLL_DIV_M_BITS	6	/* Number of useful bits for M
+						 * divider */
+#define	MCS7840_DEV_PLL_DIV_M_MASK	0x3f	/* Mask for M divider */
+#define	MCS7840_DEV_PLL_DIV_M_MIN	1	/* Minimum value for M, 0 is
+						 * forbidden */
+#define	MCS7840_DEV_PLL_DIV_M_DEF	1	/* Default value for M */
+#define	MCS7840_DEV_PLL_DIV_M_MAX	63	/* Maximum value for M */
+#define	MCS7840_DEV_PLL_DIV_N_BITS	6	/* Number of useful bits for N
+						 * divider */
+#define	MCS7840_DEV_PLL_DIV_N_MASK	0x3f	/* Mask for N divider */
+#define	MCS7840_DEV_PLL_DIV_N_MIN	1	/* Minimum value for N, 0 is
+						 * forbidden */
+#define	MCS7840_DEV_PLL_DIV_N_DEF	8	/* Default value for N */
+#define	MCS7840_DEV_PLL_DIV_N_MAX	63	/* Maximum value for N */
+
+/* Bits for CLOCK_MUX register */
+#define	MCS7840_DEV_CLOCK_MUX_INPUTMASK	0x03	/* Mask to extract PLL clock
+						 * input */
+#define	MCS7840_DEV_CLOCK_MUX_IN12MHZ	0x00	/* 12Mhz PLL input, default */
+#define	MCS7840_DEV_CLOCK_MUX_INEXTRN	0x01	/* External (device-depended)
+						 * PLL input */
+#define	MCS7840_DEV_CLOCK_MUX_INRSV1	0x02	/* Reserved */
+#define	MCS7840_DEV_CLOCK_MUX_INRSV2	0x03	/* Reserved */
+#define	MCS7840_DEV_CLOCK_MUX_PLLHIGH	0x04	/* 0 = PLL Output is
+						 * 20MHz-100MHz (default), 1 =
+						 * 100MHz-300MHz range */
+#define	MCS7840_DEV_CLOCK_MUX_INTRFIFOS	0x08	/* Enable additional 8 bytes
+						 * fro Interrupt USB pipe with
+						 * USB FIFOs statuses, default
+						 * = 0 */
+#define	MCS7840_DEV_CLOCK_MUX_RESERVED1	0x10	/* Unused */
+#define	MCS7840_DEV_CLOCK_MUX_RESERVED2	0x20	/* Unused */
+#define	MCS7840_DEV_CLOCK_MUX_RESERVED3	0x40	/* Unused */
+#define	MCS7840_DEV_CLOCK_MUX_RESERVED4	0x80	/* Unused */
+
+/* Bits for CLOCK_SELECTxx registers	*/
+#define	MCS7840_DEV_CLOCK_SELECT1_MASK	0x07	/* Bits for port 1 in
+						 * CLOCK_SELECT12 */
+#define	MCS7840_DEV_CLOCK_SELECT1_SHIFT	0	/* Shift for port 1in
+						 * CLOCK_SELECT12 */
+#define	MCS7840_DEV_CLOCK_SELECT2_MASK	0x38	/* Bits for port 2 in
+						 * CLOCK_SELECT12 */
+#define	MCS7840_DEV_CLOCK_SELECT2_SHIFT	3	/* Shift for port 2 in
+						 * CLOCK_SELECT12 */
+#define	MCS7840_DEV_CLOCK_SELECT3_MASK	0x07	/* Bits for port 3 in
+						 * CLOCK_SELECT23 */
+#define	MCS7840_DEV_CLOCK_SELECT3_SHIFT	0	/* Shift for port 3 in
+						 * CLOCK_SELECT23 */
+#define	MCS7840_DEV_CLOCK_SELECT4_MASK	0x38	/* Bits for port 4 in
+						 * CLOCK_SELECT23 */
+#define	MCS7840_DEV_CLOCK_SELECT4_SHIFT	3	/* Shift for port 4 in
+						 * CLOCK_SELECT23 */
+#define	MCS7840_DEV_CLOCK_SELECT_STD	0x00	/* STANDARD baudrate derived
+						 * from 96Mhz, default for all
+						 * ports */
+#define	MCS7840_DEV_CLOCK_SELECT_30MHZ	0x01	/* 30Mhz */
+#define	MCS7840_DEV_CLOCK_SELECT_96MHZ	0x02	/* 96Mhz direct */
+#define	MCS7840_DEV_CLOCK_SELECT_120MHZ	0x03	/* 120Mhz */
+#define	MCS7840_DEV_CLOCK_SELECT_PLL	0x04	/* PLL output (see for M and N
+						 * dividers) */
+#define	MCS7840_DEV_CLOCK_SELECT_EXT	0x05	/* External clock input
+						 * (device-dependend) */
+#define	MCS7840_DEV_CLOCK_SELECT_RES1	0x06	/* Unused */
+#define	MCS7840_DEV_CLOCK_SELECT_RES2	0x07	/* Unused */
+
+/* Bits for MODE register */
+#define	MCS7840_DEV_MODE_RESERVED1	0x01	/* Unused */
+#define	MCS7840_DEV_MODE_RESET		0x02	/* 0: RESET = Active High
+						 * (default), 1: Reserved (?) */
+#define	MCS7840_DEV_MODE_SER_PRSNT	0x04	/* 0: Reserved, 1: Do not use
+						 * hardocded values (default)
+						 * (?) */
+#define	MCS7840_DEV_MODE_PLLBYPASS	0x08	/* 1: PLL output is bypassed,
+						 * default = 0 */
+#define	MCS7840_DEV_MODE_PORBYPASS	0x10	/* 1: Power-On Reset is
+						 * bypassed, default = 0 */
+#define	MCS7840_DEV_MODE_SELECT24S	0x20	/* 0: 4 Serial Ports / IrDA
+						 * active, 1: 2 Serial Ports /
+						 * IrDA active */
+#define	MCS7840_DEV_MODE_EEPROMWR	0x40	/* EEPROM write is enabled,
+						 * default */
+#define	MCS7840_DEV_MODE_IRDA		0x80	/* IrDA mode is activated
+						 * (could be turned on),
+						 * default */
+
+/* Bits for SPx ICG */
+#define	MCS7840_DEV_SPx_ICG_DEF		0x24	/* All 8 bits is used as
+						 * number of BAUD clocks of
+						 * pause */
+
+/*
+ * Bits for RX_SAMPLINGxx registers
+ * These registers control when bit value will be sampled within
+ * the baud period.
+ * 0 is very beginning of period, 15 is very end, 7 is the middle.
+ */
+#define	MCS7840_DEV_RX_SAMPLING1_MASK	0x0f	/* Bits for port 1 in
+						 * RX_SAMPLING12 */
+#define	MCS7840_DEV_RX_SAMPLING1_SHIFT	0	/* Shift for port 1in
+						 * RX_SAMPLING12 */
+#define	MCS7840_DEV_RX_SAMPLING2_MASK	0xf0	/* Bits for port 2 in
+						 * RX_SAMPLING12 */
+#define	MCS7840_DEV_RX_SAMPLING2_SHIFT	4	/* Shift for port 2 in
+						 * RX_SAMPLING12 */
+#define	MCS7840_DEV_RX_SAMPLING3_MASK	0x0f	/* Bits for port 3 in
+						 * RX_SAMPLING23 */
+#define	MCS7840_DEV_RX_SAMPLING3_SHIFT	0	/* Shift for port 3 in
+						 * RX_SAMPLING23 */
+#define	MCS7840_DEV_RX_SAMPLING4_MASK	0xf0	/* Bits for port 4 in
+						 * RX_SAMPLING23 */
+#define	MCS7840_DEV_RX_SAMPLING4_SHIFT	4	/* Shift for port 4 in
+						 * RX_SAMPLING23 */
+#define	MCS7840_DEV_RX_SAMPLINGx_MIN	0	/* Max for any RX Sampling */
+#define	MCS7840_DEV_RX_SAMPLINGx_DEF	7	/* Default for any RX
+						 * Sampling, center of period */
+#define	MCS7840_DEV_RX_SAMPLINGx_MAX	15	/* Min for any RX Sampling */
+
+/* Bits for ZERO_PERIODx */
+#define	MCS7840_DEV_ZERO_PERIODx_DEF	20	/* Number of Bulk-in requests
+						 * befor sending zero-sized
+						 * reply */
+
+/* Bits for ZERO_ENABLE */
+#define	MCS7840_DEV_ZERO_ENABLE_PORT1	0x01	/* Enable of sending
+						 * zero-sized replies for port
+						 * 1, default */
+#define	MCS7840_DEV_ZERO_ENABLE_PORT2	0x02	/* Enable of sending
+						 * zero-sized replies for port
+						 * 2, default */
+#define	MCS7840_DEV_ZERO_ENABLE_PORT3	0x04	/* Enable of sending
+						 * zero-sized replies for port
+						 * 3, default */
+#define	MCS7840_DEV_ZERO_ENABLE_PORT4	0x08	/* Enable of sending
+						 * zero-sized replies for port
+						 * 4, default */
+
+/* Bits for THR_VAL_HIGHx */
+#define	MCS7840_DEV_THR_VAL_HIGH_MASK	0x01	/* Only one bit is used */
+#define	MCS7840_DEV_THR_VAL_HIGH_MUL	256	/* This one bit is means "256" */
+#define	MCS7840_DEV_THR_VAL_HIGH_SHIFT	8	/* This one bit is means "256" */
+#define	MCS7840_DEV_THR_VAL_HIGH_ENABLE	0x80	/* Enable threshold */
+
+/* These are documented in "public" datasheet */
+#define	MCS7840_DEV_REG_DCR0_1	0x04	/* Device contol register 0 for Port
+					 * 1, R/W */
+#define	MCS7840_DEV_REG_DCR1_1	0x05	/* Device contol register 1 for Port
+					 * 1, R/W */
+#define	MCS7840_DEV_REG_DCR2_1	0x06	/* Device contol register 2 for Port
+					 * 1, R/W */
+#define	MCS7840_DEV_REG_DCR0_2	0x16	/* Device contol register 0 for Port
+					 * 2, R/W */
+#define	MCS7840_DEV_REG_DCR1_2	0x17	/* Device contol register 1 for Port
+					 * 2, R/W */
+#define	MCS7840_DEV_REG_DCR2_2	0x18	/* Device contol register 2 for Port
+					 * 2, R/W */
+#define	MCS7840_DEV_REG_DCR0_3	0x19	/* Device contol register 0 for Port
+					 * 3, R/W */
+#define	MCS7840_DEV_REG_DCR1_3	0x1a	/* Device contol register 1 for Port
+					 * 3, R/W */
+#define	MCS7840_DEV_REG_DCR2_3	0x1b	/* Device contol register 2 for Port
+					 * 3, R/W */
+#define	MCS7840_DEV_REG_DCR0_4	0x1c	/* Device contol register 0 for Port
+					 * 4, R/W */
+#define	MCS7840_DEV_REG_DCR1_4	0x1d	/* Device contol register 1 for Port
+					 * 4, R/W */
+#define	MCS7840_DEV_REG_DCR2_4	0x1e	/* Device contol register 2 for Port
+					 * 4, R/W */
+
+/* Bits of DCR0 registers, documented in datasheet */
+#define	MCS7840_DEV_DCR0_PWRSAVE		0x01	/* Shutdown transiver
+							 * when USB Suspend is
+							 * engaged, default = 1 */
+#define	MCS7840_DEV_DCR0_RESERVED1		0x02	/* Unused */
+#define	MCS7840_DEV_DCR0_GPIO_MODE_MASK		0x0c	/* GPIO Mode bits, WORKS
+							 * ONLY FOR PORT 1 */
+#define	MCS7840_DEV_DCR0_GPIO_MODE_IN		0x00	/* GPIO Mode - Input
+							 * (0b00), WORKS ONLY
+							 * FOR PORT 1 */
+#define	MCS7840_DEV_DCR0_GPIO_MODE_OUT		0x08	/* GPIO Mode - Input
+							 * (0b10), WORKS ONLY
+							 * FOR PORT 1 */
+#define	MCS7840_DEV_DCR0_RTS_ACTIVE_HIGH	0x10	/* RTS Active is HIGH,
+							 * default = 0 (low) */
+#define	MCS7840_DEV_DCR0_RTS_AUTO		0x20	/* RTS is controlled by
+							 * state of TX buffer,
+							 * default = 0
+							 * (controlled by MCR) */
+#define	MCS7840_DEV_DCR0_IRDA			0x40	/* IrDA mode */
+#define	MCS7840_DEV_DCR0_RESERVED2		0x80	/* Unused */
+
+/* Bits of DCR1 registers, documented in datasheet */
+#define	MCS7840_DEV_DCR1_GPIO_CURRENT_MASK	0x03	/* Mask to extract GPIO
+							 * current value, WORKS
+							 * ONLY FOR PORT 1 */
+#define	MCS7840_DEV_DCR1_GPIO_CURRENT_6MA	0x00	/* GPIO output current
+							 * 6mA, WORKS ONLY FOR
+							 * PORT 1 */
+#define	MCS7840_DEV_DCR1_GPIO_CURRENT_8MA	0x01	/* GPIO output current
+							 * 8mA, defauilt, WORKS
+							 * ONLY FOR PORT 1 */
+#define	MCS7840_DEV_DCR1_GPIO_CURRENT_10MA	0x02	/* GPIO output current
+							 * 10mA, WORKS ONLY FOR
+							 * PORT 1 */
+#define	MCS7840_DEV_DCR1_GPIO_CURRENT_12MA	0x03	/* GPIO output current
+							 * 12mA, WORKS ONLY FOR
+							 * PORT 1 */
+#define	MCS7840_DEV_DCR1_UART_CURRENT_MASK	0x0c	/* Mask to extract UART
+							 * signals current value */
+#define	MCS7840_DEV_DCR1_UART_CURRENT_6MA	0x00	/* UART output current
+							 * 6mA */
+#define	MCS7840_DEV_DCR1_UART_CURRENT_8MA	0x04	/* UART output current
+							 * 8mA, defauilt */
+#define	MCS7840_DEV_DCR1_UART_CURRENT_10MA	0x08	/* UART output current
+							 * 10mA */
+#define	MCS7840_DEV_DCR1_UART_CURRENT_12MA	0x0c	/* UART output current
+							 * 12mA */
+#define	MCS7840_DEV_DCR1_WAKEUP_DISABLE		0x10	/* Disable Remote USB
+							 * Wakeup */
+#define	MCS7840_DEV_DCR1_PLLPWRDOWN_DISABLE	0x20	/* Disable PLL power
+							 * down when not needed,
+							 * WORKS ONLY FOR PORT 1 */
+#define	MCS7840_DEV_DCR1_LONG_INTERRUPT		0x40	/* Enable 13 bytes of
+							 * interrupt data, with
+							 * FIFO statistics,
+							 * WORKS ONLY FOR PORT 1 */
+#define	MCS7840_DEV_DCR1_RESERVED1		0x80	/* Unused */
+
+/*
+ * Bits of DCR2 registers, documented in datasheet
+ * Wakeup will work only if DCR0_IRDA = 0 (RS-xxx mode) and
+ * DCR1_WAKEUP_DISABLE = 0 (wakeup enabled).
+ */
+#define	MCS7840_DEV_DCR2_WAKEUP_CTS	0x01	/* Wakeup on CTS change,
+						 * default = 0 */
+#define	MCS7840_DEV_DCR2_WAKEUP_DCD	0x02	/* Wakeup on DCD change,
+						 * default = 0 */
+#define	MCS7840_DEV_DCR2_WAKEUP_RI	0x04	/* Wakeup on RI change,
+						 * default = 1 */
+#define	MCS7840_DEV_DCR2_WAKEUP_DSR	0x08	/* Wakeup on DSR change,
+						 * default = 0 */
+#define	MCS7840_DEV_DCR2_WAKEUP_RXD	0x10	/* Wakeup on RX Data change,
+						 * default = 0 */
+#define	MCS7840_DEV_DCR2_WAKEUP_RESUME	0x20	/* Wakeup issues RESUME
+						 * signal, DISCONNECT
+						 * otherwise, default = 1 */
+#define	MCS7840_DEV_DCR2_RESERVED1	0x40	/* Unused */
+#define	MCS7840_DEV_DCR2_SHDN_POLARITY	0x80	/* 0: Pin 12 Active Low, 1:
+						 * Pin 12 Active High, default
+						 * = 0 */
+
+/* Interrupt endpoint bytes & bits */
+#define	MCS7840_IEP_FIFO_STATUS_INDEX	5
+/*
+ * Thesse can be calculated as "1 << portnumber" for Bulk-out and
+ * "1 << (portnumber+1)" for Bulk-in
+ */
+#define	MCS7840_IEP_BO_PORT1_HASDATA	0x01
+#define	MCS7840_IEP_BI_PORT1_HASDATA	0x02
+#define	MCS7840_IEP_BO_PORT2_HASDATA	0x04
+#define	MCS7840_IEP_BI_PORT2_HASDATA	0x08
+#define	MCS7840_IEP_BO_PORT3_HASDATA	0x10
+#define	MCS7840_IEP_BI_PORT3_HASDATA	0x20
+#define	MCS7840_IEP_BO_PORT4_HASDATA	0x40
+#define	MCS7840_IEP_BI_PORT4_HASDATA	0x80
+
+/* Documented UART registers (fully compatible with 16550 UART) */
+#define	MCS7840_UART_REG_THR		0x00	/* Transmitter Holding
+						 * Register W/Only */
+#define	MCS7840_UART_REG_RHR		0x00	/* Receiver Holding Register
+						 * R/Only */
+#define	MCS7840_UART_REG_IER		0x01	/* Interrupt enable register -
+						 * R/W */
+#define	MCS7840_UART_REG_FCR		0x02	/* FIFO Control register -
+						 * W/Only */
+#define	MCS7840_UART_REG_ISR		0x02	/* Interrupt Status Registter
+						 * R/Only */
+#define	MCS7840_UART_REG_LCR		0x03	/* Line control register R/W */
+#define	MCS7840_UART_REG_MCR		0x04	/* Modem control register R/W */
+#define	MCS7840_UART_REG_LSR		0x05	/* Line status register R/Only */
+#define	MCS7840_UART_REG_MSR		0x06	/* Modem status register
+						 * R/Only */
+#define	MCS7840_UART_REG_SCRATCHPAD	0x07	/* Scratch pad register */
+
+#define	MCS7840_UART_REG_DLL		0x00	/* Low bits of BAUD divider */
+#define	MCS7840_UART_REG_DLM		0x01	/* High bits of BAUD divider */
+
+/* IER bits */
+#define	MCS7840_UART_IER_RXREADY	0x01	/* RX Ready interrumpt mask */
+#define	MCS7840_UART_IER_TXREADY	0x02	/* TX Ready interrumpt mask */
+#define	MCS7840_UART_IER_RXSTAT		0x04	/* RX Status interrumpt mask */
+#define	MCS7840_UART_IER_MODEM		0x08	/* Modem status change
+						 * interrumpt mask */
+#define	MCS7840_UART_IER_SLEEP		0x10	/* SLEEP enable */
+
+/* FCR bits */
+#define	MCS7840_UART_FCR_ENABLE		0x01	/* Enable FIFO */
+#define	MCS7840_UART_FCR_FLUSHRHR	0x02	/* Flush RHR and FIFO */
+#define	MCS7840_UART_FCR_FLUSHTHR	0x04	/* Flush THR and FIFO */
+#define	MCS7840_UART_FCR_RTLMASK	0xa0	/* Mask to select RHR
+						 * Interrupt Trigger level */
+#define	MCS7840_UART_FCR_RTL_1_1	0x00	/* L1 = 1, L2 = 1 */
+#define	MCS7840_UART_FCR_RTL_1_4	0x40	/* L1 = 1, L2 = 4 */
+#define	MCS7840_UART_FCR_RTL_1_8	0x80	/* L1 = 1, L2 = 8 */
+#define	MCS7840_UART_FCR_RTL_1_14	0xa0	/* L1 = 1, L2 = 14 */
+
+/* ISR bits */
+#define	MCS7840_UART_ISR_NOPENDING	0x01	/* No interrupt pending */
+#define	MCS7840_UART_ISR_INTMASK	0x3f	/* Mask to select interrupt
+						 * source */
+#define	MCS7840_UART_ISR_RXERR		0x06	/* Recevir error */
+#define	MCS7840_UART_ISR_RXHASDATA	0x04	/* Recevier has data */
+#define	MCS7840_UART_ISR_RXTIMEOUT	0x0c	/* Recevier timeout */
+#define	MCS7840_UART_ISR_TXEMPTY	0x02	/* Transmitter empty */
+#define	MCS7840_UART_ISR_MSCHANGE	0x00	/* Modem status change */
+
+/* LCR bits */
+#define	MCS7840_UART_LCR_DATALENMASK	0x03	/* Mask for data length */
+#define	MCS7840_UART_LCR_DATALEN5	0x00	/* 5 data bits */
+#define	MCS7840_UART_LCR_DATALEN6	0x01	/* 6 data bits */
+#define	MCS7840_UART_LCR_DATALEN7	0x02	/* 7 data bits */
+#define	MCS7840_UART_LCR_DATALEN8	0x03	/* 8 data bits */
+
+#define	MCS7840_UART_LCR_STOPBMASK	0x04	/* Mask for stop bits */
+#define	MCS7840_UART_LCR_STOPB1		0x00	/* 1 stop bit in any case */
+#define	MCS7840_UART_LCR_STOPB2		0x04	/* 1.5-2 stop bits depends on
+						 * data length */
+
+#define	MCS7840_UART_LCR_PARITYMASK	0x38	/* Mask for all parity data */
+#define	MCS7840_UART_LCR_PARITYON	0x08	/* Parity ON/OFF - ON */
+#define	MCS7840_UART_LCR_PARITYODD	0x00	/* Parity Odd */
+#define	MCS7840_UART_LCR_PARITYEVEN	0x10	/* Parity Even */
+#define	MCS7840_UART_LCR_PARITYODD	0x00	/* Parity Odd */
+#define	MCS7840_UART_LCR_PARITYFORCE	0x20	/* Force parity odd/even */
+
+#define	MCS7840_UART_LCR_BREAK		0x40	/* Send BREAK */
+#define	MCS7840_UART_LCR_DIVISORS	0x80	/* Map DLL/DLM instead of
+						 * xHR/IER */
+
+/* LSR bits */
+#define	MCS7840_UART_LSR_RHRAVAIL	0x01	/* Data available for read */
+#define	MCS7840_UART_LSR_RHROVERRUN	0x02	/* Data FIFO/register overflow */
+#define	MCS7840_UART_LSR_PARITYERR	0x04	/* Parity error */
+#define	MCS7840_UART_LSR_FRAMEERR	0x10	/* Framing error */
+#define	MCS7840_UART_LSR_BREAKERR	0x20	/* BREAK signal received */
+#define	MCS7840_UART_LSR_THREMPTY	0x40	/* THR register is empty,
+						 * ready for transmit */
+#define	MCS7840_UART_LSR_HASERR		0x80	/* Has error in receiver FIFO */
+
+/* MCR bits */
+#define	MCS7840_UART_MCR_DTR		0x01	/* Force DTR to be active
+						 * (low) */
+#define	MCS7840_UART_MCR_RTS		0x02	/* Force RTS to be active
+						 * (low) */
+#define	MCS7840_UART_MCR_IE		0x04	/* Enable interrupts (from
+						 * code, not documented) */
+#define	MCS7840_UART_MCR_LOOPBACK	0x10	/* Enable local loopback test
+						 * mode */
+#define	MCS7840_UART_MCR_CTSRTS		0x20	/* Enable CTS/RTS flow control
+						 * in 550 (FIFO) mode */
+#define	MCS7840_UART_MCR_DTRDSR		0x40	/* Enable DTR/DSR flow control
+						 * in 550 (FIFO) mode */
+#define	MCS7840_UART_MCR_DCD		0x80	/* Enable DCD flow control in
+						 * 550 (FIFO) mode */
+
+/* MSR bits */
+#define	MCS7840_UART_MSR_DELTACTS	0x01	/* CTS was changed since last
+						 * read */
+#define	MCS7840_UART_MSR_DELTADSR	0x02	/* DSR was changed since last
+						 * read */
+#define	MCS7840_UART_MSR_DELTARI	0x04	/* RI was changed from low to
+						 * high since last read */
+#define	MCS7840_UART_MSR_DELTADCD	0x08	/* DCD was changed since last
+						 * read */
+#define	MCS7840_UART_MSR_NEGCTS		0x10	/* Negated CTS signal */
+#define	MCS7840_UART_MSR_NEGDSR		0x20	/* Negated DSR signal */
+#define	MCS7840_UART_MSR_NEGRI		0x40	/* Negated RI signal */
+#define	MCS7840_UART_MSR_NEGDCD		0x80	/* Negated DCD signal */
+
+/* SCRATCHPAD bits */
+#define	MCS7840_UART_SCRATCHPAD_RS232		0x00	/* RS-485 disabled */
+#define	MCS7840_UART_SCRATCHPAD_RS485_DTRRX	0x80	/* RS-485 mode, DTR High
+							 * = RX */
+#define	MCS7840_UART_SCRATCHPAD_RS485_DTRTX	0xc0	/* RS-485 mode, DTR High
+							 * = TX */
+
+#define	MCS7840_CONFIG_INDEX	0
+#define	MCS7840_IFACE_INDEX	0
+
+#endif
Index: freebsd/sys/dev/usb/serial/umct.c
===================================================================
--- freebsd/sys/dev/usb/serial/umct.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/umct.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,683 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2003 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Driver for the MCT (Magic Control Technology) USB-RS232 Converter.
+ * Based on the superb documentation from the linux mct_u232 driver by
+ * Wolfgang Grandeggar <wolfgang@cec.ch>.
+ * This device smells a lot like the Belkin F5U103, except that it has
+ * suffered some mild brain-damage.  This driver is based off of the ubsa.c
+ * driver from Alexander Kabaev <kan@FreeBSD.org>.  Merging the two together
+ * might be useful, though the subtle differences might lead to lots of
+ * #ifdef's.
+ */
+
+/*
+ * NOTE: all function names beginning like "umct_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR usb_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+/* The UMCT advertises the standard 8250 UART registers */
+#define	UMCT_GET_MSR		2	/* Get Modem Status Register */
+#define	UMCT_GET_MSR_SIZE	1
+#define	UMCT_GET_LCR		6	/* Get Line Control Register */
+#define	UMCT_GET_LCR_SIZE	1
+#define	UMCT_SET_BAUD		5	/* Set the Baud Rate Divisor */
+#define	UMCT_SET_BAUD_SIZE	4
+#define	UMCT_SET_LCR		7	/* Set Line Control Register */
+#define	UMCT_SET_LCR_SIZE	1
+#define	UMCT_SET_MCR		10	/* Set Modem Control Register */
+#define	UMCT_SET_MCR_SIZE	1
+
+#define	UMCT_MSR_CTS_CHG	0x01
+#define	UMCT_MSR_DSR_CHG	0x02
+#define	UMCT_MSR_RI_CHG		0x04
+#define	UMCT_MSR_CD_CHG		0x08
+#define	UMCT_MSR_CTS		0x10
+#define	UMCT_MSR_RTS		0x20
+#define	UMCT_MSR_RI		0x40
+#define	UMCT_MSR_CD		0x80
+
+#define	UMCT_INTR_INTERVAL	100
+#define	UMCT_IFACE_INDEX	0
+#define	UMCT_CONFIG_INDEX	0
+
+enum {
+	UMCT_BULK_DT_WR,
+	UMCT_BULK_DT_RD,
+	UMCT_INTR_DT_RD,
+	UMCT_N_TRANSFER,
+};
+
+struct umct_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_device *sc_udev;
+	struct usb_xfer *sc_xfer[UMCT_N_TRANSFER];
+	struct mtx sc_mtx;
+
+	uint32_t sc_unit;
+
+	uint16_t sc_obufsize;
+
+	uint8_t	sc_lsr;
+	uint8_t	sc_msr;
+	uint8_t	sc_lcr;
+	uint8_t	sc_mcr;
+	uint8_t	sc_iface_no;
+	uint8_t sc_swap_cb;
+};
+
+/* prototypes */
+
+static device_probe_t umct_probe;
+static device_attach_t umct_attach;
+static device_detach_t umct_detach;
+static void umct_free_softc(struct umct_softc *);
+
+static usb_callback_t umct_intr_callback;
+static usb_callback_t umct_intr_callback_sub;
+static usb_callback_t umct_read_callback;
+static usb_callback_t umct_read_callback_sub;
+static usb_callback_t umct_write_callback;
+
+static void	umct_cfg_do_request(struct umct_softc *sc, uint8_t request,
+		    uint16_t len, uint32_t value);
+static void	umct_free(struct ucom_softc *);
+static void	umct_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	umct_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	umct_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	umct_cfg_set_rts(struct ucom_softc *, uint8_t);
+static uint8_t	umct_calc_baud(uint32_t);
+static int	umct_pre_param(struct ucom_softc *, struct termios *);
+static void	umct_cfg_param(struct ucom_softc *, struct termios *);
+static void	umct_start_read(struct ucom_softc *);
+static void	umct_stop_read(struct ucom_softc *);
+static void	umct_start_write(struct ucom_softc *);
+static void	umct_stop_write(struct ucom_softc *);
+static void	umct_poll(struct ucom_softc *ucom);
+
+static const struct usb_config umct_config[UMCT_N_TRANSFER] = {
+
+	[UMCT_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &umct_write_callback,
+	},
+
+	[UMCT_BULK_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &umct_read_callback,
+		.ep_index = 0,		/* first interrupt endpoint */
+	},
+
+	[UMCT_INTR_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &umct_intr_callback,
+		.ep_index = 1,		/* second interrupt endpoint */
+	},
+};
+
+static const struct ucom_callback umct_callback = {
+	.ucom_cfg_get_status = &umct_cfg_get_status,
+	.ucom_cfg_set_dtr = &umct_cfg_set_dtr,
+	.ucom_cfg_set_rts = &umct_cfg_set_rts,
+	.ucom_cfg_set_break = &umct_cfg_set_break,
+	.ucom_cfg_param = &umct_cfg_param,
+	.ucom_pre_param = &umct_pre_param,
+	.ucom_start_read = &umct_start_read,
+	.ucom_stop_read = &umct_stop_read,
+	.ucom_start_write = &umct_start_write,
+	.ucom_stop_write = &umct_stop_write,
+	.ucom_poll = &umct_poll,
+	.ucom_free = &umct_free,
+};
+
+static const STRUCT_USB_HOST_ID umct_devs[] = {
+	{USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0)},
+	{USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, 0)},
+	{USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, 0)},
+	{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109, 0)},
+	{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409, 0)},
+};
+
+static device_method_t umct_methods[] = {
+	DEVMETHOD(device_probe, umct_probe),
+	DEVMETHOD(device_attach, umct_attach),
+	DEVMETHOD(device_detach, umct_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t umct_devclass;
+
+static driver_t umct_driver = {
+	.name = "umct",
+	.methods = umct_methods,
+	.size = sizeof(struct umct_softc),
+};
+
+DRIVER_MODULE(umct, uhub, umct_driver, umct_devclass, NULL, 0);
+MODULE_DEPEND(umct, ucom, 1, 1, 1);
+MODULE_DEPEND(umct, usb, 1, 1, 1);
+MODULE_VERSION(umct, 1);
+USB_PNP_HOST_INFO(umct_devs);
+
+static int
+umct_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UMCT_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UMCT_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(umct_devs, sizeof(umct_devs), uaa));
+}
+
+static int
+umct_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct umct_softc *sc = device_get_softc(dev);
+	int32_t error;
+	uint16_t maxp;
+	uint8_t iface_index;
+
+	sc->sc_udev = uaa->device;
+	sc->sc_unit = device_get_unit(dev);
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "umct", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+
+	iface_index = UMCT_IFACE_INDEX;
+	error = usbd_transfer_setup(uaa->device, &iface_index,
+	    sc->sc_xfer, umct_config, UMCT_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		device_printf(dev, "allocating USB "
+		    "transfers failed\n");
+		goto detach;
+	}
+
+	/*
+	 * The real bulk-in endpoint is also marked as an interrupt.
+	 * The only way to differentiate it from the real interrupt
+	 * endpoint is to look at the wMaxPacketSize field.
+	 */
+	maxp = usbd_xfer_max_framelen(sc->sc_xfer[UMCT_BULK_DT_RD]);
+	if (maxp == 0x2) {
+
+		/* guessed wrong - switch around endpoints */
+
+		struct usb_xfer *temp = sc->sc_xfer[UMCT_INTR_DT_RD];
+
+		sc->sc_xfer[UMCT_INTR_DT_RD] = sc->sc_xfer[UMCT_BULK_DT_RD];
+		sc->sc_xfer[UMCT_BULK_DT_RD] = temp;
+		sc->sc_swap_cb = 1;
+	}
+
+	sc->sc_obufsize = usbd_xfer_max_len(sc->sc_xfer[UMCT_BULK_DT_WR]);
+
+	if (uaa->info.idProduct == USB_PRODUCT_MCT_SITECOM_USB232) {
+		if (sc->sc_obufsize > 16) {
+			sc->sc_obufsize = 16;
+		}
+	}
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &umct_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);			/* success */
+
+detach:
+	umct_detach(dev);
+	return (ENXIO);			/* failure */
+}
+
+static int
+umct_detach(device_t dev)
+{
+	struct umct_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UMCT_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	umct_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(umct);
+
+static void
+umct_free_softc(struct umct_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+umct_free(struct ucom_softc *ucom)
+{
+	umct_free_softc(ucom->sc_parent);
+}
+
+static void
+umct_cfg_do_request(struct umct_softc *sc, uint8_t request,
+    uint16_t len, uint32_t value)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+	uint8_t temp[4];
+
+	if (len > 4)
+		len = 4;
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = request;
+	USETW(req.wValue, 0);
+	req.wIndex[0] = sc->sc_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, len);
+	USETDW(temp, value);
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, temp, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "device request failed, err=%s "
+		    "(ignored)\n", usbd_errstr(err));
+	}
+	return;
+}
+
+static void
+umct_intr_callback_sub(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umct_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[2];
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen < 2) {
+			DPRINTF("too short message\n");
+			goto tr_setup;
+		}
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_out(pc, 0, buf, sizeof(buf));
+
+		/*
+		 * MSR bits need translation from ns16550 to SER_* values.
+		 * LSR bits are ns16550 in hardware and ucom.
+		 */
+		sc->sc_msr = 0;
+		if (buf[0] & UMCT_MSR_CTS)
+			sc->sc_msr |= SER_CTS;
+		if (buf[0] & UMCT_MSR_CD)
+			sc->sc_msr |= SER_DCD;
+		if (buf[0] & UMCT_MSR_RI)
+			sc->sc_msr |= SER_RI;
+		if (buf[0] & UMCT_MSR_RTS)
+			sc->sc_msr |= SER_DSR;
+		sc->sc_lsr = buf[1];
+
+		ucom_status_change(&sc->sc_ucom);
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umct_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+umct_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	if (onoff)
+		sc->sc_lcr |= 0x40;
+	else
+		sc->sc_lcr &= ~0x40;
+
+	umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr);
+}
+
+static void
+umct_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	if (onoff)
+		sc->sc_mcr |= 0x01;
+	else
+		sc->sc_mcr &= ~0x01;
+
+	umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+}
+
+static void
+umct_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	if (onoff)
+		sc->sc_mcr |= 0x02;
+	else
+		sc->sc_mcr &= ~0x02;
+
+	umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+}
+
+static uint8_t
+umct_calc_baud(uint32_t baud)
+{
+	switch (baud) {
+		case B300:return (0x1);
+	case B600:
+		return (0x2);
+	case B1200:
+		return (0x3);
+	case B2400:
+		return (0x4);
+	case B4800:
+		return (0x6);
+	case B9600:
+		return (0x8);
+	case B19200:
+		return (0x9);
+	case B38400:
+		return (0xa);
+	case B57600:
+		return (0xb);
+	case 115200:
+		return (0xc);
+	case B0:
+	default:
+		break;
+	}
+	return (0x0);
+}
+
+static int
+umct_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	return (0);			/* we accept anything */
+}
+
+static void
+umct_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+	uint32_t value;
+
+	value = umct_calc_baud(t->c_ospeed);
+	umct_cfg_do_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value);
+
+	value = (sc->sc_lcr & 0x40);
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		value |= 0x0;
+		break;
+	case CS6:
+		value |= 0x1;
+		break;
+	case CS7:
+		value |= 0x2;
+		break;
+	default:
+	case CS8:
+		value |= 0x3;
+		break;
+	}
+
+	value |= (t->c_cflag & CSTOPB) ? 0x4 : 0;
+	if (t->c_cflag & PARENB) {
+		value |= 0x8;
+		value |= (t->c_cflag & PARODD) ? 0x0 : 0x10;
+	}
+	/*
+	 * XXX There doesn't seem to be a way to tell the device
+	 * to use flow control.
+	 */
+
+	sc->sc_lcr = value;
+	umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value);
+}
+
+static void
+umct_start_read(struct ucom_softc *ucom)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	/* start interrupt endpoint */
+	usbd_transfer_start(sc->sc_xfer[UMCT_INTR_DT_RD]);
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]);
+}
+
+static void
+umct_stop_read(struct ucom_softc *ucom)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UMCT_INTR_DT_RD]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]);
+}
+
+static void
+umct_start_write(struct ucom_softc *ucom)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]);
+}
+
+static void
+umct_stop_write(struct ucom_softc *ucom)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]);
+}
+
+static void
+umct_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umct_softc *sc = usbd_xfer_softc(xfer);
+
+	if (sc->sc_swap_cb)
+		umct_intr_callback_sub(xfer, error);
+	else
+		umct_read_callback_sub(xfer, error);
+}
+
+static void
+umct_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umct_softc *sc = usbd_xfer_softc(xfer);
+
+	if (sc->sc_swap_cb)
+		umct_read_callback_sub(xfer, error);
+	else
+		umct_intr_callback_sub(xfer, error);
+}
+
+static void
+umct_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umct_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    sc->sc_obufsize, &actlen)) {
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umct_read_callback_sub(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umct_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umct_poll(struct ucom_softc *ucom)
+{
+	struct umct_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UMCT_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/umodem.c
===================================================================
--- freebsd/sys/dev/usb/serial/umodem.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/umodem.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,1028 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $	*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2003, M. Warner Losh <imp@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Comm Class spec:  http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ *                   http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ *                   http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip
+ */
+
+/*
+ * TODO:
+ * - Add error recovery in various places; the big problem is what
+ *   to do in a callback if there is an error.
+ * - Implement a Call Device for modems without multiplexed commands.
+ *
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+#include <rtems/bsd/local/usb_if.h>
+
+#include <dev/usb/usb_ioctl.h>
+
+#define	USB_DEBUG_VAR umodem_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/quirk/usb_quirk.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int umodem_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
+SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &umodem_debug, 0, "Debug level");
+#endif
+
+static const STRUCT_USB_DUAL_ID umodem_dual_devs[] = {
+	/* Generic Modem class match */
+	{USB_IFACE_CLASS(UICLASS_CDC),
+		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+		USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
+	{USB_IFACE_CLASS(UICLASS_CDC),
+		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+		USB_IFACE_PROTOCOL(UIPROTO_CDC_NONE)},
+};
+
+static const STRUCT_USB_HOST_ID umodem_host_devs[] = {
+	/* Huawei Modem class match */
+	{USB_VENDOR(USB_VENDOR_HUAWEI),USB_IFACE_CLASS(UICLASS_CDC),
+		USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+		USB_IFACE_PROTOCOL(0xFF)},
+	/* Kyocera AH-K3001V */
+	{USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
+	{USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
+	{USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
+};
+
+/*
+ * As speeds for umodem devices increase, these numbers will need to
+ * be increased. They should be good for G3 speeds and below.
+ *
+ * TODO: The TTY buffers should be increased!
+ */
+#define	UMODEM_BUF_SIZE 1024
+
+enum {
+	UMODEM_BULK_WR,
+	UMODEM_BULK_RD,
+	UMODEM_INTR_WR,
+	UMODEM_INTR_RD,
+	UMODEM_N_TRANSFER,
+};
+
+#define	UMODEM_MODVER			1	/* module version */
+
+struct umodem_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint16_t sc_line;
+
+	uint8_t	sc_lsr;			/* local status register */
+	uint8_t	sc_msr;			/* modem status register */
+	uint8_t	sc_ctrl_iface_no;
+	uint8_t	sc_data_iface_no;
+	uint8_t sc_iface_index[2];
+	uint8_t	sc_cm_over_data;
+	uint8_t	sc_cm_cap;		/* CM capabilities */
+	uint8_t	sc_acm_cap;		/* ACM capabilities */
+	uint8_t	sc_line_coding[32];	/* used in USB device mode */
+	uint8_t	sc_abstract_state[32];	/* used in USB device mode */
+};
+
+static device_probe_t umodem_probe;
+static device_attach_t umodem_attach;
+static device_detach_t umodem_detach;
+static usb_handle_request_t umodem_handle_request;
+
+static void umodem_free_softc(struct umodem_softc *);
+
+static usb_callback_t umodem_intr_read_callback;
+static usb_callback_t umodem_intr_write_callback;
+static usb_callback_t umodem_write_callback;
+static usb_callback_t umodem_read_callback;
+
+static void	umodem_free(struct ucom_softc *);
+static void	umodem_start_read(struct ucom_softc *);
+static void	umodem_stop_read(struct ucom_softc *);
+static void	umodem_start_write(struct ucom_softc *);
+static void	umodem_stop_write(struct ucom_softc *);
+static void	umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *);
+static void	umodem_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static int	umodem_pre_param(struct ucom_softc *, struct termios *);
+static void	umodem_cfg_param(struct ucom_softc *, struct termios *);
+static int	umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
+		    struct thread *);
+static void	umodem_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	umodem_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	umodem_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	*umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t);
+static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t,
+		    uint16_t, uint16_t);
+static void	umodem_poll(struct ucom_softc *ucom);
+static void	umodem_find_data_iface(struct usb_attach_arg *uaa,
+		    uint8_t, uint8_t *, uint8_t *);
+
+static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
+
+	[UMODEM_BULK_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_TX,
+		.if_index = 0,
+		.bufsize = UMODEM_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &umodem_write_callback,
+		.usb_mode = USB_MODE_DUAL,
+	},
+
+	[UMODEM_BULK_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_RX,
+		.if_index = 0,
+		.bufsize = UMODEM_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &umodem_read_callback,
+		.usb_mode = USB_MODE_DUAL,
+	},
+
+	[UMODEM_INTR_WR] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_TX,
+		.if_index = 1,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &umodem_intr_write_callback,
+		.usb_mode = USB_MODE_DEVICE,
+	},
+
+	[UMODEM_INTR_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_RX,
+		.if_index = 1,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &umodem_intr_read_callback,
+		.usb_mode = USB_MODE_HOST,
+	},
+};
+
+static const struct ucom_callback umodem_callback = {
+	.ucom_cfg_get_status = &umodem_cfg_get_status,
+	.ucom_cfg_set_dtr = &umodem_cfg_set_dtr,
+	.ucom_cfg_set_rts = &umodem_cfg_set_rts,
+	.ucom_cfg_set_break = &umodem_cfg_set_break,
+	.ucom_cfg_param = &umodem_cfg_param,
+	.ucom_pre_param = &umodem_pre_param,
+	.ucom_ioctl = &umodem_ioctl,
+	.ucom_start_read = &umodem_start_read,
+	.ucom_stop_read = &umodem_stop_read,
+	.ucom_start_write = &umodem_start_write,
+	.ucom_stop_write = &umodem_stop_write,
+	.ucom_poll = &umodem_poll,
+	.ucom_free = &umodem_free,
+};
+
+static device_method_t umodem_methods[] = {
+	/* USB interface */
+	DEVMETHOD(usb_handle_request, umodem_handle_request),
+
+	/* Device interface */
+	DEVMETHOD(device_probe, umodem_probe),
+	DEVMETHOD(device_attach, umodem_attach),
+	DEVMETHOD(device_detach, umodem_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t umodem_devclass;
+
+static driver_t umodem_driver = {
+	.name = "umodem",
+	.methods = umodem_methods,
+	.size = sizeof(struct umodem_softc),
+};
+
+DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0);
+MODULE_DEPEND(umodem, ucom, 1, 1, 1);
+MODULE_DEPEND(umodem, usb, 1, 1, 1);
+MODULE_VERSION(umodem, UMODEM_MODVER);
+USB_PNP_DUAL_INFO(umodem_dual_devs);
+USB_PNP_HOST_INFO(umodem_host_devs);
+
+static int
+umodem_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	int error;
+
+	DPRINTFN(11, "\n");
+
+	error = usbd_lookup_id_by_uaa(umodem_host_devs,
+	    sizeof(umodem_host_devs), uaa);
+	if (error) {
+		error = usbd_lookup_id_by_uaa(umodem_dual_devs,
+		    sizeof(umodem_dual_devs), uaa);
+		if (error)
+			return (error);
+	}
+	return (BUS_PROBE_GENERIC);
+}
+
+static int
+umodem_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct umodem_softc *sc = device_get_softc(dev);
+	struct usb_cdc_cm_descriptor *cmd;
+	struct usb_cdc_union_descriptor *cud;
+	uint8_t i;
+	int error;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
+	sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
+	sc->sc_udev = uaa->device;
+
+	umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
+
+	/* get the data interface number */
+
+	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+
+	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
+
+		cud = usbd_find_descriptor(uaa->device, NULL,
+		    uaa->info.bIfaceIndex, UDESC_CS_INTERFACE,
+		    0xFF, UDESCSUB_CDC_UNION, 0xFF);
+
+		if ((cud == NULL) || (cud->bLength < sizeof(*cud))) {
+			DPRINTF("Missing descriptor. "
+			    "Assuming data interface is next.\n");
+			if (sc->sc_ctrl_iface_no == 0xFF) {
+				goto detach;
+			} else {
+				uint8_t class_match = 0;
+
+				/* set default interface number */
+				sc->sc_data_iface_no = 0xFF;
+
+				/* try to find the data interface backwards */
+				umodem_find_data_iface(uaa,
+				    uaa->info.bIfaceIndex - 1,
+				    &sc->sc_data_iface_no, &class_match);
+
+				/* try to find the data interface forwards */
+				umodem_find_data_iface(uaa,
+				    uaa->info.bIfaceIndex + 1,
+				    &sc->sc_data_iface_no, &class_match);
+
+				/* check if nothing was found */
+				if (sc->sc_data_iface_no == 0xFF)
+					goto detach;
+			}
+		} else {
+			sc->sc_data_iface_no = cud->bSlaveInterface[0];
+		}
+	} else {
+		sc->sc_data_iface_no = cmd->bDataInterface;
+	}
+
+	device_printf(dev, "data interface %d, has %sCM over "
+	    "data, has %sbreak\n",
+	    sc->sc_data_iface_no,
+	    sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+	    sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+	/* get the data interface too */
+
+	for (i = 0;; i++) {
+		struct usb_interface *iface;
+		struct usb_interface_descriptor *id;
+
+		iface = usbd_get_iface(uaa->device, i);
+
+		if (iface) {
+
+			id = usbd_get_interface_descriptor(iface);
+
+			if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
+				sc->sc_iface_index[0] = i;
+				usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+				break;
+			}
+		} else {
+			device_printf(dev, "no data interface\n");
+			goto detach;
+		}
+	}
+
+	if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) {
+		sc->sc_cm_over_data = 1;
+	} else {
+		if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
+			if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
+
+				error = umodem_set_comm_feature
+				(uaa->device, sc->sc_ctrl_iface_no,
+				 UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
+
+				/* ignore any errors */
+			}
+			sc->sc_cm_over_data = 1;
+		}
+	}
+	error = usbd_transfer_setup(uaa->device,
+	    sc->sc_iface_index, sc->sc_xfer,
+	    umodem_config, UMODEM_N_TRANSFER,
+	    sc, &sc->sc_mtx);
+	if (error) {
+		device_printf(dev, "Can't setup transfer\n");
+		goto detach;
+	}
+
+	/* clear stall at first run, if USB host mode */
+	if (uaa->usb_mode == USB_MODE_HOST) {
+		mtx_lock(&sc->sc_mtx);
+		usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
+		usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
+		mtx_unlock(&sc->sc_mtx);
+	}
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &umodem_callback, &sc->sc_mtx);
+	if (error) {
+		device_printf(dev, "Can't attach com\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	umodem_detach(dev);
+	return (ENXIO);
+}
+
+static void
+umodem_find_data_iface(struct usb_attach_arg *uaa,
+    uint8_t iface_index, uint8_t *p_data_no, uint8_t *p_match_class)
+{
+	struct usb_interface_descriptor *id;
+	struct usb_interface *iface;
+	
+	iface = usbd_get_iface(uaa->device, iface_index);
+
+	/* check for end of interfaces */
+	if (iface == NULL)
+		return;
+
+	id = usbd_get_interface_descriptor(iface);
+
+	/* check for non-matching interface class */
+	if (id->bInterfaceClass != UICLASS_CDC_DATA ||
+	    id->bInterfaceSubClass != UISUBCLASS_DATA) {
+		/* if we got a class match then return */
+		if (*p_match_class)
+			return;
+	} else {
+		*p_match_class = 1;
+	}
+
+	DPRINTFN(11, "Match at index %u\n", iface_index);
+
+	*p_data_no = id->bInterfaceNumber;
+}
+
+static void
+umodem_start_read(struct ucom_softc *ucom)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+
+	/* start interrupt endpoint, if any */
+	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
+}
+
+static void
+umodem_stop_read(struct ucom_softc *ucom)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt endpoint, if any */
+	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
+}
+
+static void
+umodem_start_write(struct ucom_softc *ucom)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_WR]);
+	usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
+}
+
+static void
+umodem_stop_write(struct ucom_softc *ucom)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_WR]);
+	usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
+}
+
+static void
+umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
+{
+	struct usb_cdc_cm_descriptor *cmd;
+	struct usb_cdc_acm_descriptor *cad;
+
+	cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+	if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
+		DPRINTF("no CM desc (faking one)\n");
+		*cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA;
+	} else
+		*cm = cmd->bmCapabilities;
+
+	cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+	if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
+		DPRINTF("no ACM desc\n");
+		*acm = 0;
+	} else
+		*acm = cad->bmCapabilities;
+}
+
+static void
+umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static int
+umodem_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	return (0);			/* we accept anything */
+}
+
+static void
+umodem_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+	struct usb_cdc_line_state ls;
+	struct usb_device_request req;
+
+	DPRINTF("sc=%p\n", sc);
+
+	memset(&ls, 0, sizeof(ls));
+
+	USETDW(ls.dwDTERate, t->c_ospeed);
+
+	ls.bCharFormat = (t->c_cflag & CSTOPB) ?
+	    UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
+
+	ls.bParityType = (t->c_cflag & PARENB) ?
+	    ((t->c_cflag & PARODD) ?
+	    UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		ls.bDataBits = 5;
+		break;
+	case CS6:
+		ls.bDataBits = 6;
+		break;
+	case CS7:
+		ls.bDataBits = 7;
+		break;
+	case CS8:
+		ls.bDataBits = 8;
+		break;
+	}
+
+	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
+	    UGETDW(ls.dwDTERate), ls.bCharFormat,
+	    ls.bParityType, ls.bDataBits);
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_LINE_CODING;
+	USETW(req.wValue, 0);
+	req.wIndex[0] = sc->sc_ctrl_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, sizeof(ls));
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, &ls, 0, 1000);
+}
+
+static int
+umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
+    int flag, struct thread *td)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+	int error = 0;
+
+	DPRINTF("cmd=0x%08x\n", cmd);
+
+	switch (cmd) {
+	case USB_GET_CM_OVER_DATA:
+		*(int *)data = sc->sc_cm_over_data;
+		break;
+
+	case USB_SET_CM_OVER_DATA:
+		if (*(int *)data != sc->sc_cm_over_data) {
+			/* XXX change it */
+		}
+		break;
+
+	default:
+		DPRINTF("unknown\n");
+		error = ENOIOCTL;
+		break;
+	}
+
+	return (error);
+}
+
+static void
+umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	DPRINTF("onoff=%d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_DTR;
+	else
+		sc->sc_line &= ~UCDC_LINE_DTR;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = sc->sc_ctrl_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	DPRINTF("onoff=%d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_RTS;
+	else
+		sc->sc_line &= ~UCDC_LINE_RTS;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = sc->sc_ctrl_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t temp;
+
+	DPRINTF("onoff=%d\n", onoff);
+
+	if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
+
+		temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
+
+		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+		req.bRequest = UCDC_SEND_BREAK;
+		USETW(req.wValue, temp);
+		req.wIndex[0] = sc->sc_ctrl_iface_no;
+		req.wIndex[1] = 0;
+		USETW(req.wLength, 0);
+
+		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+		    &req, NULL, 0, 1000);
+	}
+}
+
+static void
+umodem_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		DPRINTF("Transferred %d bytes\n", actlen);
+
+		/* FALLTHROUGH */
+	case USB_ST_SETUP:
+tr_setup:
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* start clear stall */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+umodem_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct usb_cdc_notification pkt;
+	struct umodem_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint16_t wLen;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		if (actlen < 8) {
+			DPRINTF("received short packet, "
+			    "%d bytes\n", actlen);
+			goto tr_setup;
+		}
+		if (actlen > (int)sizeof(pkt)) {
+			DPRINTF("truncating message\n");
+			actlen = sizeof(pkt);
+		}
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_out(pc, 0, &pkt, actlen);
+
+		actlen -= 8;
+
+		wLen = UGETW(pkt.wLength);
+		if (actlen > wLen) {
+			actlen = wLen;
+		}
+		if (pkt.bmRequestType != UCDC_NOTIFICATION) {
+			DPRINTF("unknown message type, "
+			    "0x%02x, on notify pipe!\n",
+			    pkt.bmRequestType);
+			goto tr_setup;
+		}
+		switch (pkt.bNotification) {
+		case UCDC_N_SERIAL_STATE:
+			/*
+			 * Set the serial state in ucom driver based on
+			 * the bits from the notify message
+			 */
+			if (actlen < 2) {
+				DPRINTF("invalid notification "
+				    "length, %d bytes!\n", actlen);
+				break;
+			}
+			DPRINTF("notify bytes = %02x%02x\n",
+			    pkt.data[0],
+			    pkt.data[1]);
+
+			/* Currently, lsr is always zero. */
+			sc->sc_lsr = 0;
+			sc->sc_msr = 0;
+
+			if (pkt.data[0] & UCDC_N_SERIAL_RI) {
+				sc->sc_msr |= SER_RI;
+			}
+			if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
+				sc->sc_msr |= SER_DSR;
+			}
+			if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
+				sc->sc_msr |= SER_DCD;
+			}
+			ucom_status_change(&sc->sc_ucom);
+			break;
+
+		default:
+			DPRINTF("unknown notify message: 0x%02x\n",
+			    pkt.bNotification);
+			break;
+		}
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+
+	}
+}
+
+static void
+umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umodem_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UMODEM_BUF_SIZE, &actlen)) {
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umodem_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umodem_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		DPRINTF("actlen=%d\n", actlen);
+
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void *
+umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype)
+{
+	return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
+	    type, 0xFF, subtype, 0xFF));
+}
+
+static usb_error_t
+umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no,
+    uint16_t feature, uint16_t state)
+{
+	struct usb_device_request req;
+	struct usb_cdc_abstract_state ast;
+
+	DPRINTF("feature=%d state=%d\n",
+	    feature, state);
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_COMM_FEATURE;
+	USETW(req.wValue, feature);
+	req.wIndex[0] = iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
+	USETW(ast.wState, state);
+
+	return (usbd_do_request(udev, NULL, &req, &ast));
+}
+
+static int
+umodem_detach(device_t dev)
+{
+	struct umodem_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	umodem_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(umodem);
+
+static void
+umodem_free_softc(struct umodem_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+umodem_free(struct ucom_softc *ucom)
+{
+	umodem_free_softc(ucom->sc_parent);
+}
+
+static void
+umodem_poll(struct ucom_softc *ucom)
+{
+	struct umodem_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
+}
+
+static int
+umodem_handle_request(device_t dev,
+    const void *preq, void **pptr, uint16_t *plen,
+    uint16_t offset, uint8_t *pstate)
+{
+	struct umodem_softc *sc = device_get_softc(dev);
+	const struct usb_device_request *req = preq;
+	uint8_t is_complete = *pstate;
+
+	DPRINTF("sc=%p\n", sc);
+
+	if (!is_complete) {
+		if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+		    (req->bRequest == UCDC_SET_LINE_CODING) &&
+		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+		    (req->wIndex[1] == 0x00) &&
+		    (req->wValue[0] == 0x00) &&
+		    (req->wValue[1] == 0x00)) {
+			if (offset == 0) {
+				*plen = sizeof(sc->sc_line_coding);
+				*pptr = &sc->sc_line_coding;
+			} else {
+				*plen = 0;
+			}
+			return (0);
+		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+		    (req->wIndex[1] == 0x00) &&
+		    (req->bRequest == UCDC_SET_COMM_FEATURE)) {
+			if (offset == 0) {
+				*plen = sizeof(sc->sc_abstract_state);
+				*pptr = &sc->sc_abstract_state;
+			} else {
+				*plen = 0;
+			}
+			return (0);
+		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+		    (req->wIndex[1] == 0x00) &&
+		    (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
+			*plen = 0;
+			return (0);
+		} else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+		    (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+		    (req->wIndex[1] == 0x00) &&
+		    (req->bRequest == UCDC_SEND_BREAK)) {
+			*plen = 0;
+			return (0);
+		}
+	}
+	return (ENXIO);			/* use builtin handler */
+}
Index: freebsd/sys/dev/usb/serial/umoscom.c
===================================================================
--- freebsd/sys/dev/usb/serial/umoscom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/umoscom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,736 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/* $FreeBSD$ */
+/*	$OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $	*/
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR umoscom_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int umoscom_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
+SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &umoscom_debug, 0, "Debug level");
+#endif
+
+#define	UMOSCOM_BUFSIZE	       1024	/* bytes */
+
+#define	UMOSCOM_CONFIG_INDEX	0
+#define	UMOSCOM_IFACE_INDEX	0
+
+/* interrupt packet */
+#define	UMOSCOM_IIR_RLS		0x06
+#define	UMOSCOM_IIR_RDA		0x04
+#define	UMOSCOM_IIR_CTI		0x0c
+#define	UMOSCOM_IIR_THR		0x02
+#define	UMOSCOM_IIR_MS		0x00
+
+/* registers */
+#define	UMOSCOM_READ		0x0d
+#define	UMOSCOM_WRITE		0x0e
+#define	UMOSCOM_UART_REG	0x0300
+#define	UMOSCOM_VEND_REG	0x0000
+
+#define	UMOSCOM_TXBUF		0x00	/* Write */
+#define	UMOSCOM_RXBUF		0x00	/* Read */
+#define	UMOSCOM_INT		0x01
+#define	UMOSCOM_FIFO		0x02	/* Write */
+#define	UMOSCOM_ISR		0x02	/* Read */
+#define	UMOSCOM_LCR		0x03
+#define	UMOSCOM_MCR		0x04
+#define	UMOSCOM_LSR		0x05
+#define	UMOSCOM_MSR		0x06
+#define	UMOSCOM_SCRATCH		0x07
+#define	UMOSCOM_DIV_LO		0x08
+#define	UMOSCOM_DIV_HI		0x09
+#define	UMOSCOM_EFR		0x0a
+#define	UMOSCOM_XON1		0x0b
+#define	UMOSCOM_XON2		0x0c
+#define	UMOSCOM_XOFF1		0x0d
+#define	UMOSCOM_XOFF2		0x0e
+
+#define	UMOSCOM_BAUDLO		0x00
+#define	UMOSCOM_BAUDHI		0x01
+
+#define	UMOSCOM_INT_RXEN	0x01
+#define	UMOSCOM_INT_TXEN	0x02
+#define	UMOSCOM_INT_RSEN	0x04
+#define	UMOSCOM_INT_MDMEM	0x08
+#define	UMOSCOM_INT_SLEEP	0x10
+#define	UMOSCOM_INT_XOFF	0x20
+#define	UMOSCOM_INT_RTS		0x40
+
+#define	UMOSCOM_FIFO_EN		0x01
+#define	UMOSCOM_FIFO_RXCLR	0x02
+#define	UMOSCOM_FIFO_TXCLR	0x04
+#define	UMOSCOM_FIFO_DMA_BLK	0x08
+#define	UMOSCOM_FIFO_TXLVL_MASK	0x30
+#define	UMOSCOM_FIFO_TXLVL_8	0x00
+#define	UMOSCOM_FIFO_TXLVL_16	0x10
+#define	UMOSCOM_FIFO_TXLVL_32	0x20
+#define	UMOSCOM_FIFO_TXLVL_56	0x30
+#define	UMOSCOM_FIFO_RXLVL_MASK	0xc0
+#define	UMOSCOM_FIFO_RXLVL_8	0x00
+#define	UMOSCOM_FIFO_RXLVL_16	0x40
+#define	UMOSCOM_FIFO_RXLVL_56	0x80
+#define	UMOSCOM_FIFO_RXLVL_80	0xc0
+
+#define	UMOSCOM_ISR_MDM		0x00
+#define	UMOSCOM_ISR_NONE	0x01
+#define	UMOSCOM_ISR_TX		0x02
+#define	UMOSCOM_ISR_RX		0x04
+#define	UMOSCOM_ISR_LINE	0x06
+#define	UMOSCOM_ISR_RXTIMEOUT	0x0c
+#define	UMOSCOM_ISR_RX_XOFF	0x10
+#define	UMOSCOM_ISR_RTSCTS	0x20
+#define	UMOSCOM_ISR_FIFOEN	0xc0
+
+#define	UMOSCOM_LCR_DBITS(x)	((x) - 5)
+#define	UMOSCOM_LCR_STOP_BITS_1	0x00
+#define	UMOSCOM_LCR_STOP_BITS_2	0x04	/* 2 if 6-8 bits/char or 1.5 if 5 */
+#define	UMOSCOM_LCR_PARITY_NONE	0x00
+#define	UMOSCOM_LCR_PARITY_ODD	0x08
+#define	UMOSCOM_LCR_PARITY_EVEN	0x18
+#define	UMOSCOM_LCR_BREAK	0x40
+#define	UMOSCOM_LCR_DIVLATCH_EN	0x80
+
+#define	UMOSCOM_MCR_DTR		0x01
+#define	UMOSCOM_MCR_RTS		0x02
+#define	UMOSCOM_MCR_LOOP	0x04
+#define	UMOSCOM_MCR_INTEN	0x08
+#define	UMOSCOM_MCR_LOOPBACK	0x10
+#define	UMOSCOM_MCR_XONANY	0x20
+#define	UMOSCOM_MCR_IRDA_EN	0x40
+#define	UMOSCOM_MCR_BAUD_DIV4	0x80
+
+#define	UMOSCOM_LSR_RXDATA	0x01
+#define	UMOSCOM_LSR_RXOVER	0x02
+#define	UMOSCOM_LSR_RXPAR_ERR	0x04
+#define	UMOSCOM_LSR_RXFRM_ERR	0x08
+#define	UMOSCOM_LSR_RXBREAK	0x10
+#define	UMOSCOM_LSR_TXEMPTY	0x20
+#define	UMOSCOM_LSR_TXALLEMPTY	0x40
+#define	UMOSCOM_LSR_TXFIFO_ERR	0x80
+
+#define	UMOSCOM_MSR_CTS_CHG	0x01
+#define	UMOSCOM_MSR_DSR_CHG	0x02
+#define	UMOSCOM_MSR_RI_CHG	0x04
+#define	UMOSCOM_MSR_CD_CHG	0x08
+#define	UMOSCOM_MSR_CTS		0x10
+#define	UMOSCOM_MSR_RTS		0x20
+#define	UMOSCOM_MSR_RI		0x40
+#define	UMOSCOM_MSR_CD		0x80
+
+#define	UMOSCOM_BAUD_REF	115200
+
+enum {
+	UMOSCOM_BULK_DT_WR,
+	UMOSCOM_BULK_DT_RD,
+	UMOSCOM_INTR_DT_RD,
+	UMOSCOM_N_TRANSFER,
+};
+
+struct umoscom_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UMOSCOM_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint8_t	sc_mcr;
+	uint8_t	sc_lcr;
+};
+
+/* prototypes */
+
+static device_probe_t umoscom_probe;
+static device_attach_t umoscom_attach;
+static device_detach_t umoscom_detach;
+static void umoscom_free_softc(struct umoscom_softc *);
+
+static usb_callback_t umoscom_write_callback;
+static usb_callback_t umoscom_read_callback;
+static usb_callback_t umoscom_intr_callback;
+
+static void	umoscom_free(struct ucom_softc *);
+static void	umoscom_cfg_open(struct ucom_softc *);
+static void	umoscom_cfg_close(struct ucom_softc *);
+static void	umoscom_cfg_set_break(struct ucom_softc *, uint8_t);
+static void	umoscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	umoscom_cfg_set_rts(struct ucom_softc *, uint8_t);
+static int	umoscom_pre_param(struct ucom_softc *, struct termios *);
+static void	umoscom_cfg_param(struct ucom_softc *, struct termios *);
+static void	umoscom_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t);
+static uint8_t	umoscom_cfg_read(struct umoscom_softc *, uint16_t);
+static void	umoscom_start_read(struct ucom_softc *);
+static void	umoscom_stop_read(struct ucom_softc *);
+static void	umoscom_start_write(struct ucom_softc *);
+static void	umoscom_stop_write(struct ucom_softc *);
+static void	umoscom_poll(struct ucom_softc *ucom);
+
+static const struct usb_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
+
+	[UMOSCOM_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UMOSCOM_BUFSIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &umoscom_write_callback,
+	},
+
+	[UMOSCOM_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UMOSCOM_BUFSIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &umoscom_read_callback,
+	},
+
+	[UMOSCOM_INTR_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &umoscom_intr_callback,
+	},
+};
+
+static const struct ucom_callback umoscom_callback = {
+	/* configuration callbacks */
+	.ucom_cfg_get_status = &umoscom_cfg_get_status,
+	.ucom_cfg_set_dtr = &umoscom_cfg_set_dtr,
+	.ucom_cfg_set_rts = &umoscom_cfg_set_rts,
+	.ucom_cfg_set_break = &umoscom_cfg_set_break,
+	.ucom_cfg_param = &umoscom_cfg_param,
+	.ucom_cfg_open = &umoscom_cfg_open,
+	.ucom_cfg_close = &umoscom_cfg_close,
+
+	/* other callbacks */
+	.ucom_pre_param = &umoscom_pre_param,
+	.ucom_start_read = &umoscom_start_read,
+	.ucom_stop_read = &umoscom_stop_read,
+	.ucom_start_write = &umoscom_start_write,
+	.ucom_stop_write = &umoscom_stop_write,
+	.ucom_poll = &umoscom_poll,
+	.ucom_free = &umoscom_free,
+};
+
+static device_method_t umoscom_methods[] = {
+	DEVMETHOD(device_probe, umoscom_probe),
+	DEVMETHOD(device_attach, umoscom_attach),
+	DEVMETHOD(device_detach, umoscom_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t umoscom_devclass;
+
+static driver_t umoscom_driver = {
+	.name = "umoscom",
+	.methods = umoscom_methods,
+	.size = sizeof(struct umoscom_softc),
+};
+
+static const STRUCT_USB_HOST_ID umoscom_devs[] = {
+	{USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
+};
+
+DRIVER_MODULE(umoscom, uhub, umoscom_driver, umoscom_devclass, NULL, 0);
+MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
+MODULE_DEPEND(umoscom, usb, 1, 1, 1);
+MODULE_VERSION(umoscom, 1);
+USB_PNP_HOST_INFO(umoscom_devs);
+
+static int
+umoscom_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
+}
+
+static int
+umoscom_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct umoscom_softc *sc = device_get_softc(dev);
+	int error;
+	uint8_t iface_index;
+
+	sc->sc_udev = uaa->device;
+	sc->sc_mcr = 0x08;		/* enable interrupts */
+
+	/* XXX the device doesn't provide any ID string, so set a static one */
+	device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
+	device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
+
+	mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	iface_index = UMOSCOM_IFACE_INDEX;
+	error = usbd_transfer_setup(uaa->device, &iface_index,
+	    sc->sc_xfer, umoscom_config_data,
+	    UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &umoscom_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	device_printf(dev, "attach error: %s\n", usbd_errstr(error));
+	umoscom_detach(dev);
+	return (ENXIO);
+}
+
+static int
+umoscom_detach(device_t dev)
+{
+	struct umoscom_softc *sc = device_get_softc(dev);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	umoscom_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(umoscom);
+
+static void
+umoscom_free_softc(struct umoscom_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+umoscom_free(struct ucom_softc *ucom)
+{
+	umoscom_free_softc(ucom->sc_parent);
+}
+
+static void
+umoscom_cfg_open(struct ucom_softc *ucom)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	/* Purge FIFOs or odd things happen */
+	umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
+
+	/* Enable FIFO */
+	umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
+	    UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
+	    UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
+	    UMOSCOM_UART_REG);
+
+	/* Enable Interrupt Registers */
+	umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
+
+	/* Magic */
+	umoscom_cfg_write(sc, 0x01, 0x08);
+
+	/* Magic */
+	umoscom_cfg_write(sc, 0x00, 0x02);
+}
+
+static void
+umoscom_cfg_close(struct ucom_softc *ucom)
+{
+	return;
+}
+
+static void
+umoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+	uint16_t val;
+
+	val = sc->sc_lcr;
+	if (onoff)
+		val |= UMOSCOM_LCR_BREAK;
+
+	umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
+}
+
+static void
+umoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+	if (onoff)
+		sc->sc_mcr |= UMOSCOM_MCR_DTR;
+	else
+		sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
+
+	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
+}
+
+static void
+umoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+	if (onoff)
+		sc->sc_mcr |= UMOSCOM_MCR_RTS;
+	else
+		sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
+
+	umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
+}
+
+static int
+umoscom_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
+		return (EINVAL);
+
+	return (0);
+}
+
+static void
+umoscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+	uint16_t data;
+
+	DPRINTF("speed=%d\n", t->c_ospeed);
+
+	data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
+
+	if (data == 0) {
+		DPRINTF("invalid baud rate!\n");
+		return;
+	}
+	umoscom_cfg_write(sc, UMOSCOM_LCR,
+	    UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
+
+	umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
+	    (data & 0xFF) | UMOSCOM_UART_REG);
+
+	umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
+	    ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
+
+	if (t->c_cflag & CSTOPB)
+		data = UMOSCOM_LCR_STOP_BITS_2;
+	else
+		data = UMOSCOM_LCR_STOP_BITS_1;
+
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD)
+			data |= UMOSCOM_LCR_PARITY_ODD;
+		else
+			data |= UMOSCOM_LCR_PARITY_EVEN;
+	} else
+		data |= UMOSCOM_LCR_PARITY_NONE;
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		data |= UMOSCOM_LCR_DBITS(5);
+		break;
+	case CS6:
+		data |= UMOSCOM_LCR_DBITS(6);
+		break;
+	case CS7:
+		data |= UMOSCOM_LCR_DBITS(7);
+		break;
+	case CS8:
+		data |= UMOSCOM_LCR_DBITS(8);
+		break;
+	}
+
+	sc->sc_lcr = data;
+	umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
+}
+
+static void
+umoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+	uint8_t msr;
+
+	DPRINTFN(5, "\n");
+
+	/*
+	 * Read status registers.  MSR bits need translation from ns16550 to
+	 * SER_* values.  LSR bits are ns16550 in hardware and ucom.
+	 */
+
+	*p_lsr = umoscom_cfg_read(sc, UMOSCOM_LSR);
+	msr = umoscom_cfg_read(sc, UMOSCOM_MSR);
+
+	/* translate bits */
+
+	if (msr & UMOSCOM_MSR_CTS)
+		*p_msr |= SER_CTS;
+
+	if (msr & UMOSCOM_MSR_CD)
+		*p_msr |= SER_DCD;
+
+	if (msr & UMOSCOM_MSR_RI)
+		*p_msr |= SER_RI;
+
+	if (msr & UMOSCOM_MSR_RTS)
+		*p_msr |= SER_DSR;
+}
+
+static void
+umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = UMOSCOM_WRITE;
+	USETW(req.wValue, val);
+	USETW(req.wIndex, reg);
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+	    &req, NULL, 0, 1000);
+}
+
+static uint8_t
+umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg)
+{
+	struct usb_device_request req;
+	uint8_t val;
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = UMOSCOM_READ;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, reg);
+	USETW(req.wLength, 1);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, &val, 0, 1000);
+
+	DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
+
+	return (val);
+}
+
+static void
+umoscom_start_read(struct ucom_softc *ucom)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+#if 0
+	/* start interrupt endpoint */
+	usbd_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
+#endif
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+}
+
+static void
+umoscom_stop_read(struct ucom_softc *ucom)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt transfer */
+	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+}
+
+static void
+umoscom_start_write(struct ucom_softc *ucom)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+}
+
+static void
+umoscom_stop_write(struct ucom_softc *ucom)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+}
+
+static void
+umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		DPRINTF("\n");
+
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UMOSCOM_BUFSIZE, &actlen)) {
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			DPRINTFN(0, "transfer failed\n");
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umoscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		DPRINTF("got %d bytes\n", actlen);
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		DPRINTF("\n");
+
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			DPRINTFN(0, "transfer failed\n");
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct umoscom_softc *sc = usbd_xfer_softc(xfer);
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen < 2) {
+			DPRINTF("too short message\n");
+			goto tr_setup;
+		}
+		ucom_status_change(&sc->sc_ucom);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			DPRINTFN(0, "transfer failed\n");
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+umoscom_poll(struct ucom_softc *ucom)
+{
+	struct umoscom_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/uplcom.c
===================================================================
--- freebsd/sys/dev/usb/serial/uplcom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uplcom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,936 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $	*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This driver supports several USB-to-RS232 serial adapters driven by
+ * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
+ * bridge chip.  The adapters are sold under many different brand
+ * names.
+ *
+ * Datasheets are available at Prolific www site at
+ * http://www.prolific.com.tw.  The datasheets don't contain full
+ * programming information for the chip.
+ *
+ * PL-2303HX is probably programmed the same as PL-2303X.
+ *
+ * There are several differences between PL-2303 and PL-2303(H)X.
+ * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
+ * different command for controlling CRTSCTS and needs special
+ * sequence of commands for initialization which aren't also
+ * documented in the datasheet.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_cdc.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR uplcom_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int uplcom_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
+SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &uplcom_debug, 0, "Debug level");
+#endif
+
+#define	UPLCOM_MODVER			1	/* module version */
+
+#define	UPLCOM_CONFIG_INDEX		0
+#define	UPLCOM_IFACE_INDEX		0
+#define	UPLCOM_SECOND_IFACE_INDEX	1
+
+#ifndef UPLCOM_INTR_INTERVAL
+#define	UPLCOM_INTR_INTERVAL		0	/* default */
+#endif
+
+#define	UPLCOM_BULK_BUF_SIZE 1024	/* bytes */
+
+#define	UPLCOM_SET_REQUEST		0x01
+#define	UPLCOM_SET_CRTSCTS		0x41
+#define	UPLCOM_SET_CRTSCTS_PL2303X	0x61
+#define	RSAQ_STATUS_CTS			0x80
+#define	RSAQ_STATUS_DSR			0x02
+#define	RSAQ_STATUS_DCD			0x01
+
+#define	TYPE_PL2303			0
+#define	TYPE_PL2303HX			1
+
+enum {
+	UPLCOM_BULK_DT_WR,
+	UPLCOM_BULK_DT_RD,
+	UPLCOM_INTR_DT_RD,
+	UPLCOM_N_TRANSFER,
+};
+
+struct uplcom_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UPLCOM_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint16_t sc_line;
+
+	uint8_t	sc_lsr;			/* local status register */
+	uint8_t	sc_msr;			/* uplcom status register */
+	uint8_t	sc_chiptype;		/* type of chip */
+	uint8_t	sc_ctrl_iface_no;
+	uint8_t	sc_data_iface_no;
+	uint8_t	sc_iface_index[2];
+};
+
+/* prototypes */
+
+static usb_error_t uplcom_reset(struct uplcom_softc *, struct usb_device *);
+static usb_error_t uplcom_pl2303_do(struct usb_device *, uint8_t, uint8_t,
+			uint16_t, uint16_t, uint16_t);
+static int	uplcom_pl2303_init(struct usb_device *, uint8_t);
+static void	uplcom_free(struct ucom_softc *);
+static void	uplcom_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	uplcom_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	uplcom_cfg_set_break(struct ucom_softc *, uint8_t);
+static int	uplcom_pre_param(struct ucom_softc *, struct termios *);
+static void	uplcom_cfg_param(struct ucom_softc *, struct termios *);
+static void	uplcom_start_read(struct ucom_softc *);
+static void	uplcom_stop_read(struct ucom_softc *);
+static void	uplcom_start_write(struct ucom_softc *);
+static void	uplcom_stop_write(struct ucom_softc *);
+static void	uplcom_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	uplcom_poll(struct ucom_softc *ucom);
+
+static device_probe_t uplcom_probe;
+static device_attach_t uplcom_attach;
+static device_detach_t uplcom_detach;
+static void uplcom_free_softc(struct uplcom_softc *);
+
+static usb_callback_t uplcom_intr_callback;
+static usb_callback_t uplcom_write_callback;
+static usb_callback_t uplcom_read_callback;
+
+static const struct usb_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
+
+	[UPLCOM_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UPLCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &uplcom_write_callback,
+		.if_index = 0,
+	},
+
+	[UPLCOM_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UPLCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uplcom_read_callback,
+		.if_index = 0,
+	},
+
+	[UPLCOM_INTR_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &uplcom_intr_callback,
+		.if_index = 1,
+	},
+};
+
+static struct ucom_callback uplcom_callback = {
+	.ucom_cfg_get_status = &uplcom_cfg_get_status,
+	.ucom_cfg_set_dtr = &uplcom_cfg_set_dtr,
+	.ucom_cfg_set_rts = &uplcom_cfg_set_rts,
+	.ucom_cfg_set_break = &uplcom_cfg_set_break,
+	.ucom_cfg_param = &uplcom_cfg_param,
+	.ucom_pre_param = &uplcom_pre_param,
+	.ucom_start_read = &uplcom_start_read,
+	.ucom_stop_read = &uplcom_stop_read,
+	.ucom_start_write = &uplcom_start_write,
+	.ucom_stop_write = &uplcom_stop_write,
+	.ucom_poll = &uplcom_poll,
+	.ucom_free = &uplcom_free,
+};
+
+#define	UPLCOM_DEV(v,p)				\
+  { USB_VENDOR(USB_VENDOR_##v), USB_PRODUCT(USB_PRODUCT_##v##_##p) }
+
+static const STRUCT_USB_HOST_ID uplcom_devs[] = {
+	UPLCOM_DEV(ACERP, S81),			/* BenQ S81 phone */
+	UPLCOM_DEV(ADLINK, ND6530),		/* ADLINK ND-6530 USB-Serial */
+	UPLCOM_DEV(ALCATEL, OT535),		/* Alcatel One Touch 535/735 */
+	UPLCOM_DEV(ALCOR, AU9720),		/* Alcor AU9720 USB 2.0-RS232 */
+	UPLCOM_DEV(ANCHOR, SERIAL),		/* Anchor Serial adapter */
+	UPLCOM_DEV(ATEN, UC232A),		/* PLANEX USB-RS232 URS-03 */
+	UPLCOM_DEV(BELKIN, F5U257),		/* Belkin F5U257 USB to Serial */
+	UPLCOM_DEV(COREGA, CGUSBRS232R),	/* Corega CG-USBRS232R */
+	UPLCOM_DEV(EPSON, CRESSI_EDY),		/* Cressi Edy diving computer */
+	UPLCOM_DEV(EPSON, N2ITION3),		/* Zeagle N2iTion3 diving computer */
+	UPLCOM_DEV(ELECOM, UCSGT),		/* ELECOM UC-SGT Serial Adapter */
+	UPLCOM_DEV(ELECOM, UCSGT0),		/* ELECOM UC-SGT Serial Adapter */
+	UPLCOM_DEV(HAL, IMR001),		/* HAL Corporation Crossam2+USB */
+	UPLCOM_DEV(HP, LD220),			/* HP LD220 POS Display */
+	UPLCOM_DEV(IODATA, USBRSAQ),		/* I/O DATA USB-RSAQ */
+	UPLCOM_DEV(IODATA, USBRSAQ5),		/* I/O DATA USB-RSAQ5 */
+	UPLCOM_DEV(ITEGNO, WM1080A),		/* iTegno WM1080A GSM/GFPRS modem */
+	UPLCOM_DEV(ITEGNO, WM2080A),		/* iTegno WM2080A CDMA modem */
+	UPLCOM_DEV(LEADTEK, 9531),		/* Leadtek 9531 GPS */
+	UPLCOM_DEV(MICROSOFT, 700WX),		/* Microsoft Palm 700WX */
+	UPLCOM_DEV(MOBILEACTION, MA620),	/* Mobile Action MA-620 Infrared Adapter */
+	UPLCOM_DEV(NETINDEX, WS002IN),		/* Willcom W-S002IN */
+	UPLCOM_DEV(NOKIA2, CA42),		/* Nokia CA-42 cable */
+	UPLCOM_DEV(OTI, DKU5),			/* OTI DKU-5 cable */
+	UPLCOM_DEV(PANASONIC, TYTP50P6S),	/* Panasonic TY-TP50P6-S flat screen */
+	UPLCOM_DEV(PLX, CA42),			/* PLX CA-42 clone cable */
+	UPLCOM_DEV(PROLIFIC, ALLTRONIX_GPRS),	/* Alltronix ACM003U00 modem */
+	UPLCOM_DEV(PROLIFIC, ALDIGA_AL11U),	/* AlDiga AL-11U modem */
+	UPLCOM_DEV(PROLIFIC, DCU11),		/* DCU-11 Phone Cable */
+	UPLCOM_DEV(PROLIFIC, HCR331),		/* HCR331 Card Reader */
+	UPLCOM_DEV(PROLIFIC, MICROMAX_610U),	/* Micromax 610U modem */
+	UPLCOM_DEV(PROLIFIC, MOTOROLA),		/* Motorola cable */
+	UPLCOM_DEV(PROLIFIC, PHAROS),		/* Prolific Pharos */
+	UPLCOM_DEV(PROLIFIC, PL2303),		/* Generic adapter */
+	UPLCOM_DEV(PROLIFIC, RSAQ2),		/* I/O DATA USB-RSAQ2 */
+	UPLCOM_DEV(PROLIFIC, RSAQ3),		/* I/O DATA USB-RSAQ3 */
+	UPLCOM_DEV(PROLIFIC, UIC_MSR206),	/* UIC MSR206 Card Reader */
+	UPLCOM_DEV(PROLIFIC2, PL2303),		/* Prolific adapter */
+	UPLCOM_DEV(RADIOSHACK, USBCABLE),	/* Radio Shack USB Adapter */
+	UPLCOM_DEV(RATOC, REXUSB60),		/* RATOC REX-USB60 */
+	UPLCOM_DEV(SAGEM, USBSERIAL),		/* Sagem USB-Serial Controller */
+	UPLCOM_DEV(SAMSUNG, I330),		/* Samsung I330 phone cradle */
+	UPLCOM_DEV(SANWA, KB_USB2),		/* Sanwa KB-USB2 Multimeter cable */
+	UPLCOM_DEV(SIEMENS3, EF81),		/* Siemens EF81 */
+	UPLCOM_DEV(SIEMENS3, SX1),		/* Siemens SX1 */
+	UPLCOM_DEV(SIEMENS3, X65),		/* Siemens X65 */
+	UPLCOM_DEV(SIEMENS3, X75),		/* Siemens X75 */
+	UPLCOM_DEV(SITECOM, SERIAL),		/* Sitecom USB to Serial */
+	UPLCOM_DEV(SMART, PL2303),		/* SMART Technologies USB to Serial */
+	UPLCOM_DEV(SONY, QN3),			/* Sony QN3 phone cable */
+	UPLCOM_DEV(SONYERICSSON, DATAPILOT),	/* Sony Ericsson Datapilot */
+	UPLCOM_DEV(SONYERICSSON, DCU10),	/* Sony Ericsson DCU-10 Cable */
+	UPLCOM_DEV(SOURCENEXT, KEIKAI8),	/* SOURCENEXT KeikaiDenwa 8 */
+	UPLCOM_DEV(SOURCENEXT, KEIKAI8_CHG),	/* SOURCENEXT KeikaiDenwa 8 with charger */
+	UPLCOM_DEV(SPEEDDRAGON, MS3303H),	/* Speed Dragon USB-Serial */
+	UPLCOM_DEV(SYNTECH, CPT8001C),		/* Syntech CPT-8001C Barcode scanner */
+	UPLCOM_DEV(TDK, UHA6400),		/* TDK USB-PHS Adapter UHA6400 */
+	UPLCOM_DEV(TDK, UPA9664),		/* TDK USB-PHS Adapter UPA9664 */
+	UPLCOM_DEV(TRIPPLITE, U209),		/* Tripp-Lite U209-000-R USB to Serial */
+	UPLCOM_DEV(YCCABLE, PL2303),		/* YC Cable USB-Serial */
+};
+#undef UPLCOM_DEV
+
+static device_method_t uplcom_methods[] = {
+	DEVMETHOD(device_probe, uplcom_probe),
+	DEVMETHOD(device_attach, uplcom_attach),
+	DEVMETHOD(device_detach, uplcom_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uplcom_devclass;
+
+static driver_t uplcom_driver = {
+	.name = "uplcom",
+	.methods = uplcom_methods,
+	.size = sizeof(struct uplcom_softc),
+};
+
+DRIVER_MODULE(uplcom, uhub, uplcom_driver, uplcom_devclass, NULL, 0);
+MODULE_DEPEND(uplcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uplcom, usb, 1, 1, 1);
+MODULE_VERSION(uplcom, UPLCOM_MODVER);
+USB_PNP_HOST_INFO(uplcom_devs);
+
+static int
+uplcom_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	DPRINTFN(11, "\n");
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
+}
+
+static int
+uplcom_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uplcom_softc *sc = device_get_softc(dev);
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *id;
+	struct usb_device_descriptor *dd;
+	int error;
+
+	DPRINTFN(11, "\n");
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uplcom", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	DPRINTF("sc = %p\n", sc);
+
+	sc->sc_udev = uaa->device;
+
+	/* Determine the chip type.  This algorithm is taken from Linux. */
+	dd = usbd_get_device_descriptor(sc->sc_udev);
+	if (dd->bDeviceClass == 0x02)
+		sc->sc_chiptype = TYPE_PL2303;
+	else if (dd->bMaxPacketSize == 0x40)
+		sc->sc_chiptype = TYPE_PL2303HX;
+	else
+		sc->sc_chiptype = TYPE_PL2303;
+
+	DPRINTF("chiptype: %s\n",
+	    (sc->sc_chiptype == TYPE_PL2303HX) ?
+	    "2303X" : "2303");
+
+	/*
+	 * USB-RSAQ1 has two interface
+	 *
+	 *  USB-RSAQ1       | USB-RSAQ2
+	 * -----------------+-----------------
+	 * Interface 0      |Interface 0
+	 *  Interrupt(0x81) | Interrupt(0x81)
+	 * -----------------+ BulkIN(0x02)
+	 * Interface 1	    | BulkOUT(0x83)
+	 *   BulkIN(0x02)   |
+	 *   BulkOUT(0x83)  |
+	 */
+
+	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
+	sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX;
+
+	iface = usbd_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
+	if (iface) {
+		id = usbd_get_interface_descriptor(iface);
+		if (id == NULL) {
+			device_printf(dev, "no interface descriptor (2)\n");
+			goto detach;
+		}
+		sc->sc_data_iface_no = id->bInterfaceNumber;
+		sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX;
+		usbd_set_parent_iface(uaa->device,
+		    UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex);
+	} else {
+		sc->sc_data_iface_no = sc->sc_ctrl_iface_no;
+		sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX;
+	}
+
+	error = usbd_transfer_setup(uaa->device,
+	    sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
+	    UPLCOM_N_TRANSFER, sc, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("one or more missing USB endpoints, "
+		    "error=%s\n", usbd_errstr(error));
+		goto detach;
+	}
+	error = uplcom_reset(sc, uaa->device);
+	if (error) {
+		device_printf(dev, "reset failed, error=%s\n",
+		    usbd_errstr(error));
+		goto detach;
+	}
+
+	if (sc->sc_chiptype != TYPE_PL2303HX) {
+		/* HX variants seem to lock up after a clear stall request. */
+		mtx_lock(&sc->sc_mtx);
+		usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+		usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+		mtx_unlock(&sc->sc_mtx);
+	} else {
+		if (uplcom_pl2303_do(sc->sc_udev, UT_WRITE_VENDOR_DEVICE,
+		    UPLCOM_SET_REQUEST, 8, 0, 0) ||
+		    uplcom_pl2303_do(sc->sc_udev, UT_WRITE_VENDOR_DEVICE,
+		    UPLCOM_SET_REQUEST, 9, 0, 0)) {
+			goto detach;
+		}
+	}
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uplcom_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	/*
+	 * do the initialization during attach so that the system does not
+	 * sleep during open:
+	 */
+	if (uplcom_pl2303_init(uaa->device, sc->sc_chiptype)) {
+		device_printf(dev, "init failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	uplcom_detach(dev);
+	return (ENXIO);
+}
+
+static int
+uplcom_detach(device_t dev)
+{
+	struct uplcom_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uplcom_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uplcom);
+
+static void
+uplcom_free_softc(struct uplcom_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uplcom_free(struct ucom_softc *ucom)
+{
+	uplcom_free_softc(ucom->sc_parent);
+}
+
+static usb_error_t
+uplcom_reset(struct uplcom_softc *sc, struct usb_device *udev)
+{
+	struct usb_device_request req;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = UPLCOM_SET_REQUEST;
+	USETW(req.wValue, 0);
+	req.wIndex[0] = sc->sc_data_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	return (usbd_do_request(udev, NULL, &req, NULL));
+}
+
+static usb_error_t
+uplcom_pl2303_do(struct usb_device *udev, uint8_t req_type, uint8_t request,
+    uint16_t value, uint16_t index, uint16_t length)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+	uint8_t buf[4];
+
+	req.bmRequestType = req_type;
+	req.bRequest = request;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, index);
+	USETW(req.wLength, length);
+
+	err = usbd_do_request(udev, NULL, &req, buf);
+	if (err) {
+		DPRINTF("error=%s\n", usbd_errstr(err));
+		return (1);
+	}
+	return (0);
+}
+
+static int
+uplcom_pl2303_init(struct usb_device *udev, uint8_t chiptype)
+{
+	int err;
+
+	if (uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
+	    || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0)
+	    || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
+	    || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1)
+	    || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
+	    || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0)
+	    || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1)
+	    || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1)
+	    || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0)
+	    || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0))
+		return (EIO);
+
+	if (chiptype == TYPE_PL2303HX)
+		err = uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0);
+	else
+		err = uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x24, 0);
+	if (err)
+		return (EIO);
+	
+	return (0);
+}
+
+static void
+uplcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_DTR;
+	else
+		sc->sc_line &= ~UCDC_LINE_DTR;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = sc->sc_data_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uplcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UCDC_LINE_RTS;
+	else
+		sc->sc_line &= ~UCDC_LINE_RTS;
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+	USETW(req.wValue, sc->sc_line);
+	req.wIndex[0] = sc->sc_data_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static void
+uplcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t temp;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SEND_BREAK;
+	USETW(req.wValue, temp);
+	req.wIndex[0] = sc->sc_data_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, 0);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+}
+
+static const uint32_t uplcom_rates[] = {
+	75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
+	19200, 28800, 38400, 57600, 115200,
+	/*
+	 * Higher speeds are probably possible. PL2303X supports up to
+	 * 6Mb and can set any rate
+	 */
+	230400, 460800, 614400, 921600, 1228800
+};
+
+#define	N_UPLCOM_RATES	nitems(uplcom_rates)
+
+static int
+uplcom_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+	uint8_t i;
+
+	DPRINTF("\n");
+
+	/**
+	 * Check requested baud rate.
+	 *
+	 * The PL2303 can only set specific baud rates, up to 1228800 baud.
+	 * The PL2303X can set any baud rate up to 6Mb.
+	 * The PL2303HX rev. D can set any baud rate up to 12Mb.
+	 *
+	 * XXX: We currently cannot identify the PL2303HX rev. D, so treat
+	 *      it the same as the PL2303X.
+	 */
+	if (sc->sc_chiptype != TYPE_PL2303HX) {
+		for (i = 0; i < N_UPLCOM_RATES; i++) {
+			if (uplcom_rates[i] == t->c_ospeed)
+				return (0);
+		}
+ 	} else {
+		if (t->c_ospeed <= 6000000)
+			return (0);
+	}
+
+	DPRINTF("uplcom_param: bad baud rate (%d)\n", t->c_ospeed);
+	return (EIO);
+}
+
+static void
+uplcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+	struct usb_cdc_line_state ls;
+	struct usb_device_request req;
+
+	DPRINTF("sc = %p\n", sc);
+
+	memset(&ls, 0, sizeof(ls));
+
+	USETDW(ls.dwDTERate, t->c_ospeed);
+
+	if (t->c_cflag & CSTOPB) {
+		ls.bCharFormat = UCDC_STOP_BIT_2;
+	} else {
+		ls.bCharFormat = UCDC_STOP_BIT_1;
+	}
+
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD) {
+			ls.bParityType = UCDC_PARITY_ODD;
+		} else {
+			ls.bParityType = UCDC_PARITY_EVEN;
+		}
+	} else {
+		ls.bParityType = UCDC_PARITY_NONE;
+	}
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		ls.bDataBits = 5;
+		break;
+	case CS6:
+		ls.bDataBits = 6;
+		break;
+	case CS7:
+		ls.bDataBits = 7;
+		break;
+	case CS8:
+		ls.bDataBits = 8;
+		break;
+	}
+
+	DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
+	    UGETDW(ls.dwDTERate), ls.bCharFormat,
+	    ls.bParityType, ls.bDataBits);
+
+	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+	req.bRequest = UCDC_SET_LINE_CODING;
+	USETW(req.wValue, 0);
+	req.wIndex[0] = sc->sc_data_iface_no;
+	req.wIndex[1] = 0;
+	USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, &ls, 0, 1000);
+
+	if (t->c_cflag & CRTSCTS) {
+
+		DPRINTF("crtscts = on\n");
+
+		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+		req.bRequest = UPLCOM_SET_REQUEST;
+		USETW(req.wValue, 0);
+		if (sc->sc_chiptype == TYPE_PL2303HX)
+			USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
+		else
+			USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
+		USETW(req.wLength, 0);
+
+		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+		    &req, NULL, 0, 1000);
+	} else {
+		req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+		req.bRequest = UPLCOM_SET_REQUEST;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, 0);
+		ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+		    &req, NULL, 0, 1000);
+	}
+}
+
+static void
+uplcom_start_read(struct ucom_softc *ucom)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+
+	/* start interrupt endpoint */
+	usbd_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+}
+
+static void
+uplcom_stop_read(struct ucom_softc *ucom)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+
+	/* stop interrupt endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+}
+
+static void
+uplcom_start_write(struct ucom_softc *ucom)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+}
+
+static void
+uplcom_stop_write(struct ucom_softc *ucom)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+}
+
+static void
+uplcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+uplcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uplcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[9];
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+
+		DPRINTF("actlen = %u\n", actlen);
+
+		if (actlen >= 9) {
+
+			pc = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_out(pc, 0, buf, sizeof(buf));
+
+			DPRINTF("status = 0x%02x\n", buf[8]);
+
+			sc->sc_lsr = 0;
+			sc->sc_msr = 0;
+
+			if (buf[8] & RSAQ_STATUS_CTS) {
+				sc->sc_msr |= SER_CTS;
+			}
+			if (buf[8] & RSAQ_STATUS_DSR) {
+				sc->sc_msr |= SER_DSR;
+			}
+			if (buf[8] & RSAQ_STATUS_DCD) {
+				sc->sc_msr |= SER_DCD;
+			}
+			ucom_status_change(&sc->sc_ucom);
+		}
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uplcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uplcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UPLCOM_BULK_BUF_SIZE, &actlen)) {
+
+			DPRINTF("actlen = %d\n", actlen);
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uplcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uplcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uplcom_poll(struct ucom_softc *ucom)
+{
+	struct uplcom_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UPLCOM_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/usb_serial.c
===================================================================
--- freebsd/sys/dev/usb/serial/usb_serial.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/usb_serial.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,1742 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $	*/
+
+/*-
+ * Copyright (c) 2001-2003, 2005, 2008
+ *	Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+#include <sys/cons.h>
+
+#include <dev/uart/uart_ppstypes.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#define	USB_DEBUG_VAR ucom_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#include <rtems/bsd/local/opt_gdb.h>
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
+
+static int ucom_pps_mode;
+
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RWTUN,
+    &ucom_pps_mode, 0, 
+    "pulse capture mode: 0/1/2=disabled/CTS/DCD; add 0x10 to invert");
+
+#ifdef USB_DEBUG
+static int ucom_debug = 0;
+
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &ucom_debug, 0, "ucom debug level");
+#endif
+
+#define	UCOM_CONS_BUFSIZE 1024
+
+static uint8_t ucom_cons_rx_buf[UCOM_CONS_BUFSIZE];
+static uint8_t ucom_cons_tx_buf[UCOM_CONS_BUFSIZE];
+
+static unsigned int ucom_cons_rx_low = 0;
+static unsigned int ucom_cons_rx_high = 0;
+
+static unsigned int ucom_cons_tx_low = 0;
+static unsigned int ucom_cons_tx_high = 0;
+
+static int ucom_cons_unit = -1;
+static int ucom_cons_subunit = 0;
+static int ucom_cons_baud = 9600;
+static struct ucom_softc *ucom_cons_softc = NULL;
+
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_unit, CTLFLAG_RWTUN,
+    &ucom_cons_unit, 0, "console unit number");
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_subunit, CTLFLAG_RWTUN,
+    &ucom_cons_subunit, 0, "console subunit number");
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, cons_baud, CTLFLAG_RWTUN,
+    &ucom_cons_baud, 0, "console baud rate");
+
+static usb_proc_callback_t ucom_cfg_start_transfers;
+static usb_proc_callback_t ucom_cfg_open;
+static usb_proc_callback_t ucom_cfg_close;
+static usb_proc_callback_t ucom_cfg_line_state;
+static usb_proc_callback_t ucom_cfg_status_change;
+static usb_proc_callback_t ucom_cfg_param;
+
+static int	ucom_unit_alloc(void);
+static void	ucom_unit_free(int);
+static int	ucom_attach_tty(struct ucom_super_softc *, struct ucom_softc *);
+static void	ucom_detach_tty(struct ucom_super_softc *, struct ucom_softc *);
+static void	ucom_queue_command(struct ucom_softc *,
+		    usb_proc_callback_t *, struct termios *pt,
+		    struct usb_proc_msg *t0, struct usb_proc_msg *t1);
+static void	ucom_shutdown(struct ucom_softc *);
+static void	ucom_ring(struct ucom_softc *, uint8_t);
+static void	ucom_break(struct ucom_softc *, uint8_t);
+static void	ucom_dtr(struct ucom_softc *, uint8_t);
+static void	ucom_rts(struct ucom_softc *, uint8_t);
+
+static tsw_open_t ucom_open;
+static tsw_close_t ucom_close;
+static tsw_ioctl_t ucom_ioctl;
+static tsw_modem_t ucom_modem;
+static tsw_param_t ucom_param;
+static tsw_outwakeup_t ucom_outwakeup;
+static tsw_inwakeup_t ucom_inwakeup;
+static tsw_free_t ucom_free;
+static tsw_busy_t ucom_busy;
+
+static struct ttydevsw ucom_class = {
+	.tsw_flags = TF_INITLOCK | TF_CALLOUT,
+	.tsw_open = ucom_open,
+	.tsw_close = ucom_close,
+	.tsw_outwakeup = ucom_outwakeup,
+	.tsw_inwakeup = ucom_inwakeup,
+	.tsw_ioctl = ucom_ioctl,
+	.tsw_param = ucom_param,
+	.tsw_modem = ucom_modem,
+	.tsw_free = ucom_free,
+	.tsw_busy = ucom_busy,
+};
+
+MODULE_DEPEND(ucom, usb, 1, 1, 1);
+MODULE_VERSION(ucom, 1);
+
+#define	UCOM_UNIT_MAX 		128	/* maximum number of units */
+#define	UCOM_TTY_PREFIX		"U"
+
+static struct unrhdr *ucom_unrhdr;
+static struct mtx ucom_mtx;
+static int ucom_close_refs;
+
+static void
+ucom_init(void *arg)
+{
+	DPRINTF("\n");
+	ucom_unrhdr = new_unrhdr(0, UCOM_UNIT_MAX - 1, NULL);
+	mtx_init(&ucom_mtx, "UCOM MTX", NULL, MTX_DEF);
+}
+SYSINIT(ucom_init, SI_SUB_KLD - 1, SI_ORDER_ANY, ucom_init, NULL);
+
+static void
+ucom_uninit(void *arg)
+{
+	struct unrhdr *hdr;
+	hdr = ucom_unrhdr;
+	ucom_unrhdr = NULL;
+
+	DPRINTF("\n");
+
+	if (hdr != NULL)
+		delete_unrhdr(hdr);
+
+	mtx_destroy(&ucom_mtx);
+}
+SYSUNINIT(ucom_uninit, SI_SUB_KLD - 3, SI_ORDER_ANY, ucom_uninit, NULL);
+
+/*
+ * Mark a unit number (the X in cuaUX) as in use.
+ *
+ * Note that devices using a different naming scheme (see ucom_tty_name()
+ * callback) still use this unit allocation.
+ */
+static int
+ucom_unit_alloc(void)
+{
+	int unit;
+
+	/* sanity checks */
+	if (ucom_unrhdr == NULL) {
+		DPRINTF("ucom_unrhdr is NULL\n");
+		return (-1);
+	}
+	unit = alloc_unr(ucom_unrhdr);
+	DPRINTF("unit %d is allocated\n", unit);
+	return (unit);
+}
+
+/*
+ * Mark the unit number as not in use.
+ */
+static void
+ucom_unit_free(int unit)
+{
+	/* sanity checks */
+	if (unit < 0 || unit >= UCOM_UNIT_MAX || ucom_unrhdr == NULL) {
+		DPRINTF("cannot free unit number\n");
+		return;
+	}
+	DPRINTF("unit %d is freed\n", unit);
+	free_unr(ucom_unrhdr, unit);
+}
+
+/*
+ * Setup a group of one or more serial ports.
+ *
+ * The mutex pointed to by "mtx" is applied before all
+ * callbacks are called back. Also "mtx" must be applied
+ * before calling into the ucom-layer!
+ */
+int
+ucom_attach(struct ucom_super_softc *ssc, struct ucom_softc *sc,
+    int subunits, void *parent,
+    const struct ucom_callback *callback, struct mtx *mtx)
+{
+	int subunit;
+	int error = 0;
+
+	if ((sc == NULL) ||
+	    (subunits <= 0) ||
+	    (callback == NULL) ||
+	    (mtx == NULL)) {
+		return (EINVAL);
+	}
+
+	/* allocate a uniq unit number */
+	ssc->sc_unit = ucom_unit_alloc();
+	if (ssc->sc_unit == -1)
+		return (ENOMEM);
+
+	/* generate TTY name string */
+	snprintf(ssc->sc_ttyname, sizeof(ssc->sc_ttyname),
+	    UCOM_TTY_PREFIX "%d", ssc->sc_unit);
+
+	/* create USB request handling process */
+	error = usb_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
+	if (error) {
+		ucom_unit_free(ssc->sc_unit);
+		return (error);
+	}
+	ssc->sc_subunits = subunits;
+	ssc->sc_flag = UCOM_FLAG_ATTACHED |
+	    UCOM_FLAG_FREE_UNIT;
+
+	if (callback->ucom_free == NULL)
+		ssc->sc_flag |= UCOM_FLAG_WAIT_REFS;
+
+	/* increment reference count */
+	ucom_ref(ssc);
+
+	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
+		sc[subunit].sc_subunit = subunit;
+		sc[subunit].sc_super = ssc;
+		sc[subunit].sc_mtx = mtx;
+		sc[subunit].sc_parent = parent;
+		sc[subunit].sc_callback = callback;
+
+		error = ucom_attach_tty(ssc, &sc[subunit]);
+		if (error) {
+			ucom_detach(ssc, &sc[0]);
+			return (error);
+		}
+		/* increment reference count */
+		ucom_ref(ssc);
+
+		/* set subunit attached */
+		sc[subunit].sc_flag |= UCOM_FLAG_ATTACHED;
+	}
+
+	DPRINTF("tp = %p, unit = %d, subunits = %d\n",
+		sc->sc_tty, ssc->sc_unit, ssc->sc_subunits);
+
+	return (0);
+}
+
+/*
+ * The following function will do nothing if the structure pointed to
+ * by "ssc" and "sc" is zero or has already been detached.
+ */
+void
+ucom_detach(struct ucom_super_softc *ssc, struct ucom_softc *sc)
+{
+	int subunit;
+
+	if (!(ssc->sc_flag & UCOM_FLAG_ATTACHED))
+		return;		/* not initialized */
+
+	if (ssc->sc_sysctl_ttyname != NULL) {
+		sysctl_remove_oid(ssc->sc_sysctl_ttyname, 1, 0);
+		ssc->sc_sysctl_ttyname = NULL;
+	}
+
+	if (ssc->sc_sysctl_ttyports != NULL) {
+		sysctl_remove_oid(ssc->sc_sysctl_ttyports, 1, 0);
+		ssc->sc_sysctl_ttyports = NULL;
+	}
+
+	usb_proc_drain(&ssc->sc_tq);
+
+	for (subunit = 0; subunit < ssc->sc_subunits; subunit++) {
+		if (sc[subunit].sc_flag & UCOM_FLAG_ATTACHED) {
+
+			ucom_detach_tty(ssc, &sc[subunit]);
+
+			/* avoid duplicate detach */
+			sc[subunit].sc_flag &= ~UCOM_FLAG_ATTACHED;
+		}
+	}
+	usb_proc_free(&ssc->sc_tq);
+
+	ucom_unref(ssc);
+
+	if (ssc->sc_flag & UCOM_FLAG_WAIT_REFS)
+		ucom_drain(ssc);
+
+	/* make sure we don't detach twice */
+	ssc->sc_flag &= ~UCOM_FLAG_ATTACHED;
+}
+
+void
+ucom_drain(struct ucom_super_softc *ssc)
+{
+	mtx_lock(&ucom_mtx);
+	while (ssc->sc_refs > 0) {
+		printf("ucom: Waiting for a TTY device to close.\n");
+		usb_pause_mtx(&ucom_mtx, hz);
+	}
+	mtx_unlock(&ucom_mtx);
+}
+
+void
+ucom_drain_all(void *arg)
+{
+	mtx_lock(&ucom_mtx);
+	while (ucom_close_refs > 0) {
+		printf("ucom: Waiting for all detached TTY "
+		    "devices to have open fds closed.\n");
+		usb_pause_mtx(&ucom_mtx, hz);
+	}
+	mtx_unlock(&ucom_mtx);
+}
+
+static int
+ucom_attach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
+{
+	struct tty *tp;
+	char buf[32];			/* temporary TTY device name buffer */
+
+	tp = tty_alloc_mutex(&ucom_class, sc, sc->sc_mtx);
+	if (tp == NULL)
+		return (ENOMEM);
+
+	/* Check if the client has a custom TTY name */
+	buf[0] = '\0';
+	if (sc->sc_callback->ucom_tty_name) {
+		sc->sc_callback->ucom_tty_name(sc, buf,
+		    sizeof(buf), ssc->sc_unit, sc->sc_subunit);
+	}
+	if (buf[0] == 0) {
+		/* Use default TTY name */
+		if (ssc->sc_subunits > 1) {
+			/* multiple modems in one */
+			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u.%u",
+			    ssc->sc_unit, sc->sc_subunit);
+		} else {
+			/* single modem */
+			snprintf(buf, sizeof(buf), UCOM_TTY_PREFIX "%u",
+			    ssc->sc_unit);
+		}
+	}
+	tty_makedev(tp, NULL, "%s", buf);
+
+	sc->sc_tty = tp;
+
+	sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
+	sc->sc_pps.driver_abi = PPS_ABI_VERSION;
+	sc->sc_pps.driver_mtx = sc->sc_mtx;
+	pps_init_abi(&sc->sc_pps);
+
+	DPRINTF("ttycreate: %s\n", buf);
+
+	/* Check if this device should be a console */
+	if ((ucom_cons_softc == NULL) && 
+	    (ssc->sc_unit == ucom_cons_unit) &&
+	    (sc->sc_subunit == ucom_cons_subunit)) {
+
+		DPRINTF("unit %d subunit %d is console",
+		    ssc->sc_unit, sc->sc_subunit);
+
+		ucom_cons_softc = sc;
+
+		tty_init_console(tp, ucom_cons_baud);
+
+		UCOM_MTX_LOCK(ucom_cons_softc);
+		ucom_cons_rx_low = 0;
+		ucom_cons_rx_high = 0;
+		ucom_cons_tx_low = 0;
+		ucom_cons_tx_high = 0;
+		sc->sc_flag |= UCOM_FLAG_CONSOLE;
+		ucom_open(ucom_cons_softc->sc_tty);
+		ucom_param(ucom_cons_softc->sc_tty, &tp->t_termios_init_in);
+		UCOM_MTX_UNLOCK(ucom_cons_softc);
+	}
+
+	return (0);
+}
+
+static void
+ucom_detach_tty(struct ucom_super_softc *ssc, struct ucom_softc *sc)
+{
+	struct tty *tp = sc->sc_tty;
+
+	DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
+
+	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
+		UCOM_MTX_LOCK(ucom_cons_softc);
+		ucom_close(ucom_cons_softc->sc_tty);
+		sc->sc_flag &= ~UCOM_FLAG_CONSOLE;
+		UCOM_MTX_UNLOCK(ucom_cons_softc);
+		ucom_cons_softc = NULL;
+	}
+
+	/* the config thread has been stopped when we get here */
+
+	UCOM_MTX_LOCK(sc);
+	sc->sc_flag |= UCOM_FLAG_GONE;
+	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_LL_READY);
+	UCOM_MTX_UNLOCK(sc);
+
+	if (tp) {
+		mtx_lock(&ucom_mtx);
+		ucom_close_refs++;
+		mtx_unlock(&ucom_mtx);
+
+		tty_lock(tp);
+
+		ucom_close(tp);	/* close, if any */
+
+		tty_rel_gone(tp);
+
+		UCOM_MTX_LOCK(sc);
+		/*
+		 * make sure that read and write transfers are stopped
+		 */
+		if (sc->sc_callback->ucom_stop_read)
+			(sc->sc_callback->ucom_stop_read) (sc);
+		if (sc->sc_callback->ucom_stop_write)
+			(sc->sc_callback->ucom_stop_write) (sc);
+		UCOM_MTX_UNLOCK(sc);
+	}
+}
+
+void
+ucom_set_pnpinfo_usb(struct ucom_super_softc *ssc, device_t dev)
+{
+	char buf[64];
+	uint8_t iface_index;
+	struct usb_attach_arg *uaa;
+
+	snprintf(buf, sizeof(buf), "ttyname=" UCOM_TTY_PREFIX
+	    "%d ttyports=%d", ssc->sc_unit, ssc->sc_subunits);
+
+	/* Store the PNP info in the first interface for the device */
+	uaa = device_get_ivars(dev);
+	iface_index = uaa->info.bIfaceIndex;
+    
+	if (usbd_set_pnpinfo(uaa->device, iface_index, buf) != 0)
+		device_printf(dev, "Could not set PNP info\n");
+
+	/*
+	 * The following information is also replicated in the PNP-info
+	 * string which is registered above:
+	 */
+	if (ssc->sc_sysctl_ttyname == NULL) {
+		ssc->sc_sysctl_ttyname = SYSCTL_ADD_STRING(NULL,
+		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+		    OID_AUTO, "ttyname", CTLFLAG_RD, ssc->sc_ttyname, 0,
+		    "TTY device basename");
+	}
+	if (ssc->sc_sysctl_ttyports == NULL) {
+		ssc->sc_sysctl_ttyports = SYSCTL_ADD_INT(NULL,
+		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
+		    OID_AUTO, "ttyports", CTLFLAG_RD,
+		    NULL, ssc->sc_subunits, "Number of ports");
+	}
+}
+
+static void
+ucom_queue_command(struct ucom_softc *sc,
+    usb_proc_callback_t *fn, struct termios *pt,
+    struct usb_proc_msg *t0, struct usb_proc_msg *t1)
+{
+	struct ucom_super_softc *ssc = sc->sc_super;
+	struct ucom_param_task *task;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (usb_proc_is_gone(&ssc->sc_tq)) {
+		DPRINTF("proc is gone\n");
+		return;         /* nothing to do */
+	}
+	/* 
+	 * NOTE: The task cannot get executed before we drop the
+	 * "sc_mtx" mutex. It is safe to update fields in the message
+	 * structure after that the message got queued.
+	 */
+	task = (struct ucom_param_task *)
+	  usb_proc_msignal(&ssc->sc_tq, t0, t1);
+
+	/* Setup callback and softc pointers */
+	task->hdr.pm_callback = fn;
+	task->sc = sc;
+
+	/* 
+	 * Make a copy of the termios. This field is only present if
+	 * the "pt" field is not NULL.
+	 */
+	if (pt != NULL)
+		task->termios_copy = *pt;
+
+	/*
+	 * Closing the device should be synchronous.
+	 */
+	if (fn == ucom_cfg_close)
+		usb_proc_mwait(&ssc->sc_tq, t0, t1);
+
+	/*
+	 * In case of multiple configure requests,
+	 * keep track of the last one!
+	 */
+	if (fn == ucom_cfg_start_transfers)
+		sc->sc_last_start_xfer = &task->hdr;
+}
+
+static void
+ucom_shutdown(struct ucom_softc *sc)
+{
+	struct tty *tp = sc->sc_tty;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	DPRINTF("\n");
+
+	/*
+	 * Hang up if necessary:
+	 */
+	if (tp->t_termios.c_cflag & HUPCL) {
+		ucom_modem(tp, 0, SER_DTR);
+	}
+}
+
+/*
+ * Return values:
+ *    0: normal
+ * else: taskqueue is draining or gone
+ */
+uint8_t
+ucom_cfg_is_gone(struct ucom_softc *sc)
+{
+	struct ucom_super_softc *ssc = sc->sc_super;
+
+	return (usb_proc_is_gone(&ssc->sc_tq));
+}
+
+static void
+ucom_cfg_start_transfers(struct usb_proc_msg *_task)
+{
+	struct ucom_cfg_task *task = 
+	    (struct ucom_cfg_task *)_task;
+	struct ucom_softc *sc = task->sc;
+
+	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+		return;
+	}
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		/* TTY device closed */
+		return;
+	}
+
+	if (_task == sc->sc_last_start_xfer)
+		sc->sc_flag |= UCOM_FLAG_GP_DATA;
+
+	if (sc->sc_callback->ucom_start_read) {
+		(sc->sc_callback->ucom_start_read) (sc);
+	}
+	if (sc->sc_callback->ucom_start_write) {
+		(sc->sc_callback->ucom_start_write) (sc);
+	}
+}
+
+static void
+ucom_start_transfers(struct ucom_softc *sc)
+{
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		return;
+	}
+	/*
+	 * Make sure that data transfers are started in both
+	 * directions:
+	 */
+	if (sc->sc_callback->ucom_start_read) {
+		(sc->sc_callback->ucom_start_read) (sc);
+	}
+	if (sc->sc_callback->ucom_start_write) {
+		(sc->sc_callback->ucom_start_write) (sc);
+	}
+}
+
+static void
+ucom_cfg_open(struct usb_proc_msg *_task)
+{
+	struct ucom_cfg_task *task = 
+	    (struct ucom_cfg_task *)_task;
+	struct ucom_softc *sc = task->sc;
+
+	DPRINTF("\n");
+
+	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
+
+		/* already opened */
+
+	} else {
+
+		sc->sc_flag |= UCOM_FLAG_LL_READY;
+
+		if (sc->sc_callback->ucom_cfg_open) {
+			(sc->sc_callback->ucom_cfg_open) (sc);
+
+			/* wait a little */
+			usb_pause_mtx(sc->sc_mtx, hz / 10);
+		}
+	}
+}
+
+static int
+ucom_open(struct tty *tp)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+	int error;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (sc->sc_flag & UCOM_FLAG_GONE) {
+		return (ENXIO);
+	}
+	if (sc->sc_flag & UCOM_FLAG_HL_READY) {
+		/* already opened */
+		return (0);
+	}
+	DPRINTF("tp = %p\n", tp);
+
+	if (sc->sc_callback->ucom_pre_open) {
+		/*
+		 * give the lower layer a chance to disallow TTY open, for
+		 * example if the device is not present:
+		 */
+		error = (sc->sc_callback->ucom_pre_open) (sc);
+		if (error) {
+			return (error);
+		}
+	}
+	sc->sc_flag |= UCOM_FLAG_HL_READY;
+
+	/* Disable transfers */
+	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
+
+	sc->sc_lsr = 0;
+	sc->sc_msr = 0;
+	sc->sc_mcr = 0;
+
+	/* reset programmed line state */
+	sc->sc_pls_curr = 0;
+	sc->sc_pls_set = 0;
+	sc->sc_pls_clr = 0;
+
+	/* reset jitter buffer */
+	sc->sc_jitterbuf_in = 0;
+	sc->sc_jitterbuf_out = 0;
+
+	ucom_queue_command(sc, ucom_cfg_open, NULL,
+	    &sc->sc_open_task[0].hdr,
+	    &sc->sc_open_task[1].hdr);
+
+	/* Queue transfer enable command last */
+	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
+	    &sc->sc_start_task[0].hdr, 
+	    &sc->sc_start_task[1].hdr);
+
+	ucom_modem(tp, SER_DTR | SER_RTS, 0);
+
+	ucom_ring(sc, 0);
+
+	ucom_break(sc, 0);
+
+	ucom_status_change(sc);
+
+	return (0);
+}
+
+static void
+ucom_cfg_close(struct usb_proc_msg *_task)
+{
+	struct ucom_cfg_task *task = 
+	    (struct ucom_cfg_task *)_task;
+	struct ucom_softc *sc = task->sc;
+
+	DPRINTF("\n");
+
+	if (sc->sc_flag & UCOM_FLAG_LL_READY) {
+		sc->sc_flag &= ~UCOM_FLAG_LL_READY;
+		if (sc->sc_callback->ucom_cfg_close)
+			(sc->sc_callback->ucom_cfg_close) (sc);
+	} else {
+		/* already closed */
+	}
+}
+
+static void
+ucom_close(struct tty *tp)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	DPRINTF("tp=%p\n", tp);
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		DPRINTF("tp=%p already closed\n", tp);
+		return;
+	}
+	ucom_shutdown(sc);
+
+	ucom_queue_command(sc, ucom_cfg_close, NULL,
+	    &sc->sc_close_task[0].hdr,
+	    &sc->sc_close_task[1].hdr);
+
+	sc->sc_flag &= ~(UCOM_FLAG_HL_READY | UCOM_FLAG_RTS_IFLOW);
+
+	if (sc->sc_callback->ucom_stop_read) {
+		(sc->sc_callback->ucom_stop_read) (sc);
+	}
+}
+
+static void
+ucom_inwakeup(struct tty *tp)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+	uint16_t pos;
+
+	if (sc == NULL)
+		return;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	DPRINTF("tp=%p\n", tp);
+
+	if (ttydisc_can_bypass(tp) != 0 || 
+	    (sc->sc_flag & UCOM_FLAG_HL_READY) == 0 ||
+	    (sc->sc_flag & UCOM_FLAG_INWAKEUP) != 0) {
+		return;
+	}
+
+	/* prevent recursion */
+	sc->sc_flag |= UCOM_FLAG_INWAKEUP;
+
+	pos = sc->sc_jitterbuf_out;
+
+	while (sc->sc_jitterbuf_in != pos) {
+		int c;
+
+		c = (char)sc->sc_jitterbuf[pos];
+
+		if (ttydisc_rint(tp, c, 0) == -1)
+			break;
+		pos++;
+		if (pos >= UCOM_JITTERBUF_SIZE)
+			pos -= UCOM_JITTERBUF_SIZE;
+	}
+
+	sc->sc_jitterbuf_out = pos;
+
+	/* clear RTS in async fashion */
+	if ((sc->sc_jitterbuf_in == pos) && 
+	    (sc->sc_flag & UCOM_FLAG_RTS_IFLOW))
+		ucom_rts(sc, 0);
+
+	sc->sc_flag &= ~UCOM_FLAG_INWAKEUP;
+}
+
+static int
+ucom_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+	int error;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		return (EIO);
+	}
+	DPRINTF("cmd = 0x%08lx\n", cmd);
+
+	switch (cmd) {
+#if 0
+	case TIOCSRING:
+		ucom_ring(sc, 1);
+		error = 0;
+		break;
+	case TIOCCRING:
+		ucom_ring(sc, 0);
+		error = 0;
+		break;
+#endif
+	case TIOCSBRK:
+		ucom_break(sc, 1);
+		error = 0;
+		break;
+	case TIOCCBRK:
+		ucom_break(sc, 0);
+		error = 0;
+		break;
+	default:
+		if (sc->sc_callback->ucom_ioctl) {
+			error = (sc->sc_callback->ucom_ioctl)
+			    (sc, cmd, data, 0, td);
+		} else {
+			error = ENOIOCTL;
+		}
+		if (error == ENOIOCTL)
+			error = pps_ioctl(cmd, data, &sc->sc_pps);
+		break;
+	}
+	return (error);
+}
+
+static int
+ucom_modem(struct tty *tp, int sigon, int sigoff)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+	uint8_t onoff;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		return (0);
+	}
+	if ((sigon == 0) && (sigoff == 0)) {
+
+		if (sc->sc_mcr & SER_DTR) {
+			sigon |= SER_DTR;
+		}
+		if (sc->sc_mcr & SER_RTS) {
+			sigon |= SER_RTS;
+		}
+		if (sc->sc_msr & SER_CTS) {
+			sigon |= SER_CTS;
+		}
+		if (sc->sc_msr & SER_DCD) {
+			sigon |= SER_DCD;
+		}
+		if (sc->sc_msr & SER_DSR) {
+			sigon |= SER_DSR;
+		}
+		if (sc->sc_msr & SER_RI) {
+			sigon |= SER_RI;
+		}
+		return (sigon);
+	}
+	if (sigon & SER_DTR) {
+		sc->sc_mcr |= SER_DTR;
+	}
+	if (sigoff & SER_DTR) {
+		sc->sc_mcr &= ~SER_DTR;
+	}
+	if (sigon & SER_RTS) {
+		sc->sc_mcr |= SER_RTS;
+	}
+	if (sigoff & SER_RTS) {
+		sc->sc_mcr &= ~SER_RTS;
+	}
+	onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
+	ucom_dtr(sc, onoff);
+
+	onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
+	ucom_rts(sc, onoff);
+
+	return (0);
+}
+
+static void
+ucom_cfg_line_state(struct usb_proc_msg *_task)
+{
+	struct ucom_cfg_task *task = 
+	    (struct ucom_cfg_task *)_task;
+	struct ucom_softc *sc = task->sc;
+	uint8_t notch_bits;
+	uint8_t any_bits;
+	uint8_t prev_value;
+	uint8_t last_value;
+	uint8_t mask;
+
+	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+		return;
+	}
+
+	mask = 0;
+	/* compute callback mask */
+	if (sc->sc_callback->ucom_cfg_set_dtr)
+		mask |= UCOM_LS_DTR;
+	if (sc->sc_callback->ucom_cfg_set_rts)
+		mask |= UCOM_LS_RTS;
+	if (sc->sc_callback->ucom_cfg_set_break)
+		mask |= UCOM_LS_BREAK;
+	if (sc->sc_callback->ucom_cfg_set_ring)
+		mask |= UCOM_LS_RING;
+
+	/* compute the bits we are to program */
+	notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
+	any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
+	prev_value = sc->sc_pls_curr ^ notch_bits;
+	last_value = sc->sc_pls_curr;
+
+	/* reset programmed line state */
+	sc->sc_pls_curr = 0;
+	sc->sc_pls_set = 0;
+	sc->sc_pls_clr = 0;
+
+	/* ensure that we don't lose any levels */
+	if (notch_bits & UCOM_LS_DTR)
+		sc->sc_callback->ucom_cfg_set_dtr(sc,
+		    (prev_value & UCOM_LS_DTR) ? 1 : 0);
+	if (notch_bits & UCOM_LS_RTS)
+		sc->sc_callback->ucom_cfg_set_rts(sc,
+		    (prev_value & UCOM_LS_RTS) ? 1 : 0);
+	if (notch_bits & UCOM_LS_BREAK)
+		sc->sc_callback->ucom_cfg_set_break(sc,
+		    (prev_value & UCOM_LS_BREAK) ? 1 : 0);
+	if (notch_bits & UCOM_LS_RING)
+		sc->sc_callback->ucom_cfg_set_ring(sc,
+		    (prev_value & UCOM_LS_RING) ? 1 : 0);
+
+	/* set last value */
+	if (any_bits & UCOM_LS_DTR)
+		sc->sc_callback->ucom_cfg_set_dtr(sc,
+		    (last_value & UCOM_LS_DTR) ? 1 : 0);
+	if (any_bits & UCOM_LS_RTS)
+		sc->sc_callback->ucom_cfg_set_rts(sc,
+		    (last_value & UCOM_LS_RTS) ? 1 : 0);
+	if (any_bits & UCOM_LS_BREAK)
+		sc->sc_callback->ucom_cfg_set_break(sc,
+		    (last_value & UCOM_LS_BREAK) ? 1 : 0);
+	if (any_bits & UCOM_LS_RING)
+		sc->sc_callback->ucom_cfg_set_ring(sc,
+		    (last_value & UCOM_LS_RING) ? 1 : 0);
+}
+
+static void
+ucom_line_state(struct ucom_softc *sc,
+    uint8_t set_bits, uint8_t clear_bits)
+{
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		return;
+	}
+
+	DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
+
+	/* update current programmed line state */
+	sc->sc_pls_curr |= set_bits;
+	sc->sc_pls_curr &= ~clear_bits;
+	sc->sc_pls_set |= set_bits;
+	sc->sc_pls_clr |= clear_bits;
+
+	/* defer driver programming */
+	ucom_queue_command(sc, ucom_cfg_line_state, NULL,
+	    &sc->sc_line_state_task[0].hdr, 
+	    &sc->sc_line_state_task[1].hdr);
+}
+
+static void
+ucom_ring(struct ucom_softc *sc, uint8_t onoff)
+{
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		ucom_line_state(sc, UCOM_LS_RING, 0);
+	else
+		ucom_line_state(sc, 0, UCOM_LS_RING);
+}
+
+static void
+ucom_break(struct ucom_softc *sc, uint8_t onoff)
+{
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		ucom_line_state(sc, UCOM_LS_BREAK, 0);
+	else
+		ucom_line_state(sc, 0, UCOM_LS_BREAK);
+}
+
+static void
+ucom_dtr(struct ucom_softc *sc, uint8_t onoff)
+{
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		ucom_line_state(sc, UCOM_LS_DTR, 0);
+	else
+		ucom_line_state(sc, 0, UCOM_LS_DTR);
+}
+
+static void
+ucom_rts(struct ucom_softc *sc, uint8_t onoff)
+{
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		ucom_line_state(sc, UCOM_LS_RTS, 0);
+	else
+		ucom_line_state(sc, 0, UCOM_LS_RTS);
+}
+
+static void
+ucom_cfg_status_change(struct usb_proc_msg *_task)
+{
+	struct ucom_cfg_task *task = 
+	    (struct ucom_cfg_task *)_task;
+	struct ucom_softc *sc = task->sc;
+	struct tty *tp;
+	int onoff;
+	uint8_t new_msr;
+	uint8_t new_lsr;
+	uint8_t msr_delta;
+	uint8_t lsr_delta;
+	uint8_t pps_signal;
+
+	tp = sc->sc_tty;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+		return;
+	}
+	if (sc->sc_callback->ucom_cfg_get_status == NULL) {
+		return;
+	}
+	/* get status */
+
+	new_msr = 0;
+	new_lsr = 0;
+
+	(sc->sc_callback->ucom_cfg_get_status) (sc, &new_lsr, &new_msr);
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		/* TTY device closed */
+		return;
+	}
+	msr_delta = (sc->sc_msr ^ new_msr);
+	lsr_delta = (sc->sc_lsr ^ new_lsr);
+
+	sc->sc_msr = new_msr;
+	sc->sc_lsr = new_lsr;
+
+	/*
+	 * Time pulse counting support.
+	 */
+	switch(ucom_pps_mode & UART_PPS_SIGNAL_MASK) {
+	case UART_PPS_CTS:
+		pps_signal = SER_CTS;
+		break;
+	case UART_PPS_DCD:
+		pps_signal = SER_DCD;
+		break;
+	default:
+		pps_signal = 0;
+		break;
+	}
+
+	if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
+	    (msr_delta & pps_signal)) {
+		pps_capture(&sc->sc_pps);
+		onoff = (sc->sc_msr & pps_signal) ? 1 : 0;
+		if (ucom_pps_mode & UART_PPS_INVERT_PULSE)
+			onoff = !onoff;
+		pps_event(&sc->sc_pps, onoff ? PPS_CAPTUREASSERT :
+		    PPS_CAPTURECLEAR);
+	}
+
+	if (msr_delta & SER_DCD) {
+
+		onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
+
+		DPRINTF("DCD changed to %d\n", onoff);
+
+		ttydisc_modem(tp, onoff);
+	}
+
+	if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
+
+		DPRINTF("BREAK detected\n");
+
+		ttydisc_rint(tp, 0, TRE_BREAK);
+		ttydisc_rint_done(tp);
+	}
+
+	if ((lsr_delta & ULSR_FE) && (sc->sc_lsr & ULSR_FE)) {
+
+		DPRINTF("Frame error detected\n");
+
+		ttydisc_rint(tp, 0, TRE_FRAMING);
+		ttydisc_rint_done(tp);
+	}
+
+	if ((lsr_delta & ULSR_PE) && (sc->sc_lsr & ULSR_PE)) {
+
+		DPRINTF("Parity error detected\n");
+
+		ttydisc_rint(tp, 0, TRE_PARITY);
+		ttydisc_rint_done(tp);
+	}
+}
+
+void
+ucom_status_change(struct ucom_softc *sc)
+{
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (sc->sc_flag & UCOM_FLAG_CONSOLE)
+		return;		/* not supported */
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		return;
+	}
+	DPRINTF("\n");
+
+	ucom_queue_command(sc, ucom_cfg_status_change, NULL,
+	    &sc->sc_status_task[0].hdr,
+	    &sc->sc_status_task[1].hdr);
+}
+
+static void
+ucom_cfg_param(struct usb_proc_msg *_task)
+{
+	struct ucom_param_task *task = 
+	    (struct ucom_param_task *)_task;
+	struct ucom_softc *sc = task->sc;
+
+	if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+		return;
+	}
+	if (sc->sc_callback->ucom_cfg_param == NULL) {
+		return;
+	}
+
+	(sc->sc_callback->ucom_cfg_param) (sc, &task->termios_copy);
+
+	/* wait a little */
+	usb_pause_mtx(sc->sc_mtx, hz / 10);
+}
+
+static int
+ucom_param(struct tty *tp, struct termios *t)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+	uint8_t opened;
+	int error;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	opened = 0;
+	error = 0;
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+
+		/* XXX the TTY layer should call "open()" first! */
+		/*
+		 * Not quite: Its ordering is partly backwards, but
+		 * some parameters must be set early in ttydev_open(),
+		 * possibly before calling ttydevsw_open().
+		 */
+		error = ucom_open(tp);
+		if (error)
+			goto done;
+
+		opened = 1;
+	}
+	DPRINTF("sc = %p\n", sc);
+
+	/* Check requested parameters. */
+	if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
+		/* XXX c_ospeed == 0 is perfectly valid. */
+		DPRINTF("mismatch ispeed and ospeed\n");
+		error = EINVAL;
+		goto done;
+	}
+	t->c_ispeed = t->c_ospeed;
+
+	if (sc->sc_callback->ucom_pre_param) {
+		/* Let the lower layer verify the parameters */
+		error = (sc->sc_callback->ucom_pre_param) (sc, t);
+		if (error) {
+			DPRINTF("callback error = %d\n", error);
+			goto done;
+		}
+	}
+
+	/* Disable transfers */
+	sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
+
+	/* Queue baud rate programming command first */
+	ucom_queue_command(sc, ucom_cfg_param, t,
+	    &sc->sc_param_task[0].hdr,
+	    &sc->sc_param_task[1].hdr);
+
+	/* Queue transfer enable command last */
+	ucom_queue_command(sc, ucom_cfg_start_transfers, NULL,
+	    &sc->sc_start_task[0].hdr, 
+	    &sc->sc_start_task[1].hdr);
+
+	if (t->c_cflag & CRTS_IFLOW) {
+		sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
+	} else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
+		sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
+		ucom_modem(tp, SER_RTS, 0);
+	}
+done:
+	if (error) {
+		if (opened) {
+			ucom_close(tp);
+		}
+	}
+	return (error);
+}
+
+static void
+ucom_outwakeup(struct tty *tp)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	DPRINTF("sc = %p\n", sc);
+
+	if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+		/* The higher layer is not ready */
+		return;
+	}
+	ucom_start_transfers(sc);
+}
+
+static bool
+ucom_busy(struct tty *tp)
+{
+	struct ucom_softc *sc = tty_softc(tp);
+	const uint8_t txidle = ULSR_TXRDY | ULSR_TSRE;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	DPRINTFN(3, "sc = %p lsr 0x%02x\n", sc, sc->sc_lsr);
+
+	/*
+	 * If the driver maintains the txidle bits in LSR, we can use them to
+	 * determine whether the transmitter is busy or idle.  Otherwise we have
+	 * to assume it is idle to avoid hanging forever on tcdrain(3).
+	 */
+	if (sc->sc_flag & UCOM_FLAG_LSRTXIDLE)
+		return ((sc->sc_lsr & txidle) != txidle);
+	else
+		return (false);
+}
+
+/*------------------------------------------------------------------------*
+ *	ucom_get_data
+ *
+ * Return values:
+ * 0: No data is available.
+ * Else: Data is available.
+ *------------------------------------------------------------------------*/
+uint8_t
+ucom_get_data(struct ucom_softc *sc, struct usb_page_cache *pc,
+    uint32_t offset, uint32_t len, uint32_t *actlen)
+{
+	struct usb_page_search res;
+	struct tty *tp = sc->sc_tty;
+	uint32_t cnt;
+	uint32_t offset_orig;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
+		unsigned int temp;
+
+		/* get total TX length */
+
+		temp = ucom_cons_tx_high - ucom_cons_tx_low;
+		temp %= UCOM_CONS_BUFSIZE;
+
+		/* limit TX length */
+
+		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_tx_low))
+			temp = (UCOM_CONS_BUFSIZE - ucom_cons_tx_low);
+
+		if (temp > len)
+			temp = len;
+
+		/* copy in data */
+
+		usbd_copy_in(pc, offset, ucom_cons_tx_buf + ucom_cons_tx_low, temp);
+
+		/* update counters */
+
+		ucom_cons_tx_low += temp;
+		ucom_cons_tx_low %= UCOM_CONS_BUFSIZE;
+
+		/* store actual length */
+
+		*actlen = temp;
+
+		return (temp ? 1 : 0);
+	}
+
+	if (tty_gone(tp) ||
+	    !(sc->sc_flag & UCOM_FLAG_GP_DATA)) {
+		actlen[0] = 0;
+		return (0);		/* multiport device polling */
+	}
+	offset_orig = offset;
+
+	while (len != 0) {
+
+		usbd_get_page(pc, offset, &res);
+
+		if (res.length > len) {
+			res.length = len;
+		}
+		/* copy data directly into USB buffer */
+		cnt = ttydisc_getc(tp, res.buffer, res.length);
+
+		offset += cnt;
+		len -= cnt;
+
+		if (cnt < res.length) {
+			/* end of buffer */
+			break;
+		}
+	}
+
+	actlen[0] = offset - offset_orig;
+
+	DPRINTF("cnt=%d\n", actlen[0]);
+
+	if (actlen[0] == 0) {
+		return (0);
+	}
+	return (1);
+}
+
+void
+ucom_put_data(struct ucom_softc *sc, struct usb_page_cache *pc,
+    uint32_t offset, uint32_t len)
+{
+	struct usb_page_search res;
+	struct tty *tp = sc->sc_tty;
+	char *buf;
+	uint32_t cnt;
+
+	UCOM_MTX_ASSERT(sc, MA_OWNED);
+
+	if (sc->sc_flag & UCOM_FLAG_CONSOLE) {
+		unsigned int temp;
+
+		/* get maximum RX length */
+
+		temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_rx_high + ucom_cons_rx_low;
+		temp %= UCOM_CONS_BUFSIZE;
+
+		/* limit RX length */
+
+		if (temp > (UCOM_CONS_BUFSIZE - ucom_cons_rx_high))
+			temp = (UCOM_CONS_BUFSIZE - ucom_cons_rx_high);
+
+		if (temp > len)
+			temp = len;
+
+		/* copy out data */
+
+		usbd_copy_out(pc, offset, ucom_cons_rx_buf + ucom_cons_rx_high, temp);
+
+		/* update counters */
+
+		ucom_cons_rx_high += temp;
+		ucom_cons_rx_high %= UCOM_CONS_BUFSIZE;
+
+		return;
+	}
+
+	if (tty_gone(tp))
+		return;			/* multiport device polling */
+
+	if (len == 0)
+		return;			/* no data */
+
+	/* set a flag to prevent recursation ? */
+
+	while (len > 0) {
+
+		usbd_get_page(pc, offset, &res);
+
+		if (res.length > len) {
+			res.length = len;
+		}
+		len -= res.length;
+		offset += res.length;
+
+		/* pass characters to tty layer */
+
+		buf = res.buffer;
+		cnt = res.length;
+
+		/* first check if we can pass the buffer directly */
+
+		if (ttydisc_can_bypass(tp)) {
+
+			/* clear any jitter buffer */
+			sc->sc_jitterbuf_in = 0;
+			sc->sc_jitterbuf_out = 0;
+
+			if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
+				DPRINTF("tp=%p, data lost\n", tp);
+			}
+			continue;
+		}
+		/* need to loop */
+
+		for (cnt = 0; cnt != res.length; cnt++) {
+			if (sc->sc_jitterbuf_in != sc->sc_jitterbuf_out ||
+			    ttydisc_rint(tp, buf[cnt], 0) == -1) {
+				uint16_t end;
+				uint16_t pos;
+
+				pos = sc->sc_jitterbuf_in;
+				end = sc->sc_jitterbuf_out +
+				    UCOM_JITTERBUF_SIZE - 1;
+				if (end >= UCOM_JITTERBUF_SIZE)
+					end -= UCOM_JITTERBUF_SIZE;
+
+				for (; cnt != res.length; cnt++) {
+					if (pos == end)
+						break;
+					sc->sc_jitterbuf[pos] = buf[cnt];
+					pos++;
+					if (pos >= UCOM_JITTERBUF_SIZE)
+						pos -= UCOM_JITTERBUF_SIZE;
+				}
+
+				sc->sc_jitterbuf_in = pos;
+
+				/* set RTS in async fashion */
+				if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW)
+					ucom_rts(sc, 1);
+
+				DPRINTF("tp=%p, lost %d "
+				    "chars\n", tp, res.length - cnt);
+				break;
+			}
+		}
+	}
+	ttydisc_rint_done(tp);
+}
+
+static void
+ucom_free(void *xsc)
+{
+	struct ucom_softc *sc = xsc;
+
+	if (sc->sc_callback->ucom_free != NULL)
+		sc->sc_callback->ucom_free(sc);
+	else
+		ucom_unref(sc->sc_super);
+
+	mtx_lock(&ucom_mtx);
+	ucom_close_refs--;
+	mtx_unlock(&ucom_mtx);
+}
+
+static cn_probe_t ucom_cnprobe;
+static cn_init_t ucom_cninit;
+static cn_term_t ucom_cnterm;
+static cn_getc_t ucom_cngetc;
+static cn_putc_t ucom_cnputc;
+static cn_grab_t ucom_cngrab;
+static cn_ungrab_t ucom_cnungrab;
+
+CONSOLE_DRIVER(ucom);
+
+static void
+ucom_cnprobe(struct consdev  *cp)
+{
+	if (ucom_cons_unit != -1)
+		cp->cn_pri = CN_NORMAL;
+	else
+		cp->cn_pri = CN_DEAD;
+
+	strlcpy(cp->cn_name, "ucom", sizeof(cp->cn_name));
+}
+
+static void
+ucom_cninit(struct consdev  *cp)
+{
+}
+
+static void
+ucom_cnterm(struct consdev  *cp)
+{
+}
+
+static void
+ucom_cngrab(struct consdev *cp)
+{
+}
+
+static void
+ucom_cnungrab(struct consdev *cp)
+{
+}
+
+static int
+ucom_cngetc(struct consdev *cd)
+{
+	struct ucom_softc *sc = ucom_cons_softc;
+	int c;
+
+	if (sc == NULL)
+		return (-1);
+
+	UCOM_MTX_LOCK(sc);
+
+	if (ucom_cons_rx_low != ucom_cons_rx_high) {
+		c = ucom_cons_rx_buf[ucom_cons_rx_low];
+		ucom_cons_rx_low ++;
+		ucom_cons_rx_low %= UCOM_CONS_BUFSIZE;
+	} else {
+		c = -1;
+	}
+
+	/* start USB transfers */
+	ucom_outwakeup(sc->sc_tty);
+
+	UCOM_MTX_UNLOCK(sc);
+
+	/* poll if necessary */
+	if (USB_IN_POLLING_MODE_FUNC() && sc->sc_callback->ucom_poll)
+		(sc->sc_callback->ucom_poll) (sc);
+
+	return (c);
+}
+
+static void
+ucom_cnputc(struct consdev *cd, int c)
+{
+	struct ucom_softc *sc = ucom_cons_softc;
+	unsigned int temp;
+
+	if (sc == NULL)
+		return;
+
+ repeat:
+
+	UCOM_MTX_LOCK(sc);
+
+	/* compute maximum TX length */
+
+	temp = (UCOM_CONS_BUFSIZE - 1) - ucom_cons_tx_high + ucom_cons_tx_low;
+	temp %= UCOM_CONS_BUFSIZE;
+
+	if (temp) {
+		ucom_cons_tx_buf[ucom_cons_tx_high] = c;
+		ucom_cons_tx_high ++;
+		ucom_cons_tx_high %= UCOM_CONS_BUFSIZE;
+	}
+
+	/* start USB transfers */
+	ucom_outwakeup(sc->sc_tty);
+
+	UCOM_MTX_UNLOCK(sc);
+
+	/* poll if necessary */
+	if (USB_IN_POLLING_MODE_FUNC() && sc->sc_callback->ucom_poll) {
+		(sc->sc_callback->ucom_poll) (sc);
+		/* simple flow control */
+		if (temp == 0)
+			goto repeat;
+	}
+}
+
+/*------------------------------------------------------------------------*
+ *	ucom_ref
+ *
+ * This function will increment the super UCOM reference count.
+ *------------------------------------------------------------------------*/
+void
+ucom_ref(struct ucom_super_softc *ssc)
+{
+	mtx_lock(&ucom_mtx);
+	ssc->sc_refs++;
+	mtx_unlock(&ucom_mtx);
+}
+
+/*------------------------------------------------------------------------*
+ *	ucom_free_unit
+ *
+ * This function will free the super UCOM's allocated unit
+ * number. This function can be called on a zero-initialized
+ * structure. This function can be called multiple times.
+ *------------------------------------------------------------------------*/
+static void
+ucom_free_unit(struct ucom_super_softc *ssc)
+{
+	if (!(ssc->sc_flag & UCOM_FLAG_FREE_UNIT))
+		return;
+
+	ucom_unit_free(ssc->sc_unit);
+
+	ssc->sc_flag &= ~UCOM_FLAG_FREE_UNIT;
+}
+
+/*------------------------------------------------------------------------*
+ *	ucom_unref
+ *
+ * This function will decrement the super UCOM reference count.
+ *
+ * Return values:
+ * 0: UCOM structures are still referenced.
+ * Else: UCOM structures are no longer referenced.
+ *------------------------------------------------------------------------*/
+int
+ucom_unref(struct ucom_super_softc *ssc)
+{
+	int retval;
+
+	mtx_lock(&ucom_mtx);
+	retval = (ssc->sc_refs < 2);
+	ssc->sc_refs--;
+	mtx_unlock(&ucom_mtx);
+
+	if (retval)
+		ucom_free_unit(ssc);
+
+	return (retval);
+}
+
+#if defined(GDB)
+
+#include <gdb/gdb.h>
+
+static gdb_probe_f ucom_gdbprobe;
+static gdb_init_f ucom_gdbinit;
+static gdb_term_f ucom_gdbterm;
+static gdb_getc_f ucom_gdbgetc;
+static gdb_putc_f ucom_gdbputc;
+
+GDB_DBGPORT(sio, ucom_gdbprobe, ucom_gdbinit, ucom_gdbterm, ucom_gdbgetc, ucom_gdbputc);
+
+static int
+ucom_gdbprobe(void)
+{
+	return ((ucom_cons_softc != NULL) ? 0 : -1);
+}
+
+static void
+ucom_gdbinit(void)
+{
+}
+
+static void
+ucom_gdbterm(void)
+{
+}
+
+static void
+ucom_gdbputc(int c)
+{
+        ucom_cnputc(NULL, c);
+}
+
+static int
+ucom_gdbgetc(void)
+{
+        return (ucom_cngetc(NULL));
+}
+
+#endif
Index: freebsd/sys/dev/usb/serial/usb_serial.h
===================================================================
--- freebsd/sys/dev/usb/serial/usb_serial.h	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/usb_serial.h	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,230 @@
+/*	$NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $	*/
+/*	$FreeBSD$	*/
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _USB_SERIAL_H_
+#define	_USB_SERIAL_H_
+
+#include <sys/tty.h>
+#include <sys/serial.h>
+#include <sys/fcntl.h>
+#include <sys/sysctl.h>
+#include <sys/timepps.h>
+
+/* Module interface related macros */
+#define	UCOM_MODVER	1
+
+#define	UCOM_MINVER	1
+#define	UCOM_PREFVER	UCOM_MODVER
+#define	UCOM_MAXVER	1
+#define	UCOM_JITTERBUF_SIZE	128	/* bytes */
+
+struct usb_device;
+struct ucom_softc;
+struct usb_device_request;
+struct thread;
+
+/*
+ * NOTE: There is no guarantee that "ucom_cfg_close()" will
+ * be called after "ucom_cfg_open()" if the device is detached
+ * while it is open!
+ */
+struct ucom_callback {
+	void    (*ucom_cfg_get_status) (struct ucom_softc *, uint8_t *plsr, uint8_t *pmsr);
+	void    (*ucom_cfg_set_dtr) (struct ucom_softc *, uint8_t);
+	void    (*ucom_cfg_set_rts) (struct ucom_softc *, uint8_t);
+	void    (*ucom_cfg_set_break) (struct ucom_softc *, uint8_t);
+	void    (*ucom_cfg_set_ring) (struct ucom_softc *, uint8_t);
+	void    (*ucom_cfg_param) (struct ucom_softc *, struct termios *);
+	void    (*ucom_cfg_open) (struct ucom_softc *);
+	void    (*ucom_cfg_close) (struct ucom_softc *);
+	int     (*ucom_pre_open) (struct ucom_softc *);
+	int     (*ucom_pre_param) (struct ucom_softc *, struct termios *);
+	int     (*ucom_ioctl) (struct ucom_softc *, uint32_t, caddr_t, int, struct thread *);
+	void    (*ucom_start_read) (struct ucom_softc *);
+	void    (*ucom_stop_read) (struct ucom_softc *);
+	void    (*ucom_start_write) (struct ucom_softc *);
+	void    (*ucom_stop_write) (struct ucom_softc *);
+	void    (*ucom_tty_name) (struct ucom_softc *, char *pbuf, uint16_t buflen, uint16_t unit, uint16_t subunit);
+	void    (*ucom_poll) (struct ucom_softc *);
+	void	(*ucom_free) (struct ucom_softc *);
+};
+
+/* Line status register */
+#define	ULSR_RCV_FIFO	0x80
+#define	ULSR_TSRE	0x40		/* Transmitter empty: byte sent */
+#define	ULSR_TXRDY	0x20		/* Transmitter buffer empty */
+#define	ULSR_BI		0x10		/* Break detected */
+#define	ULSR_FE		0x08		/* Framing error: bad stop bit */
+#define	ULSR_PE		0x04		/* Parity error */
+#define	ULSR_OE		0x02		/* Overrun, lost incoming byte */
+#define	ULSR_RXRDY	0x01		/* Byte ready in Receive Buffer */
+#define	ULSR_RCV_MASK	0x1f		/* Mask for incoming data or error */
+
+struct ucom_cfg_task {
+	struct usb_proc_msg hdr;
+	struct ucom_softc *sc;
+};
+
+struct ucom_param_task {
+	struct usb_proc_msg hdr;
+	struct ucom_softc *sc;
+	struct termios termios_copy;
+};
+
+struct ucom_super_softc {
+	struct usb_process sc_tq;
+	int sc_unit;
+	int sc_subunits;
+	int sc_refs;
+	int sc_flag;	/* see UCOM_FLAG_XXX */
+	struct sysctl_oid *sc_sysctl_ttyname;
+	struct sysctl_oid *sc_sysctl_ttyports;
+	char sc_ttyname[16];
+};
+
+struct ucom_softc {
+	/*
+	 * NOTE: To avoid losing level change information we use two
+	 * tasks instead of one for all commands.
+	 *
+	 * Level changes are transitions like:
+	 *
+	 * ON->OFF
+	 * OFF->ON
+	 * OPEN->CLOSE
+	 * CLOSE->OPEN
+	 */
+	struct ucom_cfg_task	sc_start_task[2];
+	struct ucom_cfg_task	sc_open_task[2];
+	struct ucom_cfg_task	sc_close_task[2];
+	struct ucom_cfg_task	sc_line_state_task[2];
+	struct ucom_cfg_task	sc_status_task[2];
+	struct ucom_param_task	sc_param_task[2];
+	/* pulse capturing support, PPS */
+	struct pps_state	sc_pps;
+	/* Used to set "UCOM_FLAG_GP_DATA" flag: */
+	struct usb_proc_msg	*sc_last_start_xfer;
+	const struct ucom_callback *sc_callback;
+	struct ucom_super_softc *sc_super;
+	struct tty *sc_tty;
+	struct mtx *sc_mtx;
+	void   *sc_parent;
+	int sc_subunit;
+	uint16_t sc_jitterbuf_in;
+	uint16_t sc_jitterbuf_out;
+	uint16_t sc_portno;
+	uint16_t sc_flag;
+#define	UCOM_FLAG_RTS_IFLOW	0x01	/* use RTS input flow control */
+#define	UCOM_FLAG_GONE		0x02	/* the device is gone */
+#define	UCOM_FLAG_ATTACHED	0x04	/* set if attached */
+#define	UCOM_FLAG_GP_DATA	0x08	/* set if get and put data is possible */
+#define	UCOM_FLAG_LL_READY	0x20	/* set if low layer is ready */
+#define	UCOM_FLAG_HL_READY	0x40	/* set if high layer is ready */
+#define	UCOM_FLAG_CONSOLE	0x80	/* set if device is a console */
+#define	UCOM_FLAG_WAIT_REFS   0x0100	/* set if we must wait for refs */
+#define	UCOM_FLAG_FREE_UNIT   0x0200	/* set if we must free the unit */
+#define	UCOM_FLAG_INWAKEUP    0x0400	/* set if we are in the tsw_inwakeup callback */
+#define	UCOM_FLAG_LSRTXIDLE   0x0800	/* set if sc_lsr bits ULSR_TSRE+TXRDY work */
+	uint8_t	sc_lsr;
+	uint8_t	sc_msr;
+	uint8_t	sc_mcr;
+	/* programmed line state bits */
+	uint8_t sc_pls_set;		/* set bits */
+	uint8_t sc_pls_clr;		/* cleared bits */
+	uint8_t sc_pls_curr;		/* last state */
+#define	UCOM_LS_DTR	0x01
+#define	UCOM_LS_RTS	0x02
+#define	UCOM_LS_BREAK	0x04
+#define	UCOM_LS_RING	0x08
+	uint8_t sc_jitterbuf[UCOM_JITTERBUF_SIZE];
+};
+
+#define	UCOM_MTX_ASSERT(sc, what) USB_MTX_ASSERT((sc)->sc_mtx, what)
+#define	UCOM_MTX_LOCK(sc) USB_MTX_LOCK((sc)->sc_mtx)
+#define	UCOM_MTX_UNLOCK(sc) USB_MTX_UNLOCK((sc)->sc_mtx)
+#define	UCOM_UNLOAD_DRAIN(x) \
+SYSUNINIT(var, SI_SUB_KLD - 2, SI_ORDER_ANY, ucom_drain_all, 0)
+
+#define	ucom_cfg_do_request(udev,com,req,ptr,flags,timo) \
+    usbd_do_request_proc(udev,&(com)->sc_super->sc_tq,req,ptr,flags,NULL,timo)
+
+int	ucom_attach(struct ucom_super_softc *,
+	    struct ucom_softc *, int, void *,
+	    const struct ucom_callback *callback, struct mtx *);
+void	ucom_detach(struct ucom_super_softc *, struct ucom_softc *);
+void	ucom_set_pnpinfo_usb(struct ucom_super_softc *, device_t);
+void	ucom_status_change(struct ucom_softc *);
+uint8_t	ucom_get_data(struct ucom_softc *, struct usb_page_cache *,
+	    uint32_t, uint32_t, uint32_t *);
+void	ucom_put_data(struct ucom_softc *, struct usb_page_cache *,
+	    uint32_t, uint32_t);
+uint8_t	ucom_cfg_is_gone(struct ucom_softc *);
+void	ucom_drain(struct ucom_super_softc *);
+void	ucom_drain_all(void *);
+void	ucom_ref(struct ucom_super_softc *);
+int	ucom_unref(struct ucom_super_softc *);
+
+static inline void
+ucom_use_lsr_txbits(struct ucom_softc *sc)
+{
+
+	sc->sc_flag |= UCOM_FLAG_LSRTXIDLE;
+}
+
+#endif					/* _USB_SERIAL_H_ */
Index: freebsd/sys/dev/usb/serial/uslcom.c
===================================================================
--- freebsd/sys/dev/usb/serial/uslcom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uslcom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,946 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $	*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for Silicon Laboratories CP2101/CP2102/CP2103/CP2104/CP2105
+ * USB-Serial adapters.  Based on datasheet AN571, publicly available from
+ * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN571.pdf
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_ioctl.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR uslcom_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int uslcom_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
+SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &uslcom_debug, 0, "Debug level");
+#endif
+
+#define	USLCOM_BULK_BUF_SIZE		1024
+#define	USLCOM_CONFIG_INDEX	0
+
+/* Request types */
+#define	USLCOM_WRITE		0x41
+#define	USLCOM_READ		0xc1
+
+/* Request codes */
+#define	USLCOM_IFC_ENABLE	0x00
+#define	USLCOM_SET_BAUDDIV	0x01	
+#define	USLCOM_SET_LINE_CTL	0x03
+#define	USLCOM_SET_BREAK	0x05
+#define	USLCOM_SET_MHS		0x07
+#define	USLCOM_GET_MDMSTS	0x08
+#define	USLCOM_SET_FLOW		0x13
+#define	USLCOM_SET_BAUDRATE	0x1e	
+#define	USLCOM_VENDOR_SPECIFIC	0xff
+
+/* USLCOM_IFC_ENABLE values */
+#define	USLCOM_IFC_ENABLE_DIS	0x00
+#define	USLCOM_IFC_ENABLE_EN	0x01
+
+/* USLCOM_SET_MHS/USLCOM_GET_MDMSTS values */
+#define	USLCOM_MHS_DTR_ON	0x0001	
+#define	USLCOM_MHS_DTR_SET	0x0100
+#define	USLCOM_MHS_RTS_ON	0x0002
+#define	USLCOM_MHS_RTS_SET	0x0200
+#define	USLCOM_MHS_CTS		0x0010
+#define	USLCOM_MHS_DSR		0x0020
+#define	USLCOM_MHS_RI		0x0040
+#define	USLCOM_MHS_DCD		0x0080
+
+/* USLCOM_SET_BAUDDIV values */
+#define	USLCOM_BAUDDIV_REF	3686400 /* 3.6864 MHz */
+
+/* USLCOM_SET_LINE_CTL values */
+#define	USLCOM_STOP_BITS_1	0x00
+#define	USLCOM_STOP_BITS_2	0x02
+#define	USLCOM_PARITY_NONE	0x00
+#define	USLCOM_PARITY_ODD	0x10
+#define	USLCOM_PARITY_EVEN	0x20
+#define	USLCOM_SET_DATA_BITS(x)	((x) << 8)
+
+/* USLCOM_SET_BREAK values */
+#define	USLCOM_SET_BREAK_OFF	0x00
+#define	USLCOM_SET_BREAK_ON	0x01
+
+/* USLCOM_SET_FLOW values - 1st word */
+#define	USLCOM_FLOW_DTR_ON      0x00000001 /* DTR static active */
+#define	USLCOM_FLOW_CTS_HS      0x00000008 /* CTS handshake */
+/* USLCOM_SET_FLOW values - 2nd word */
+#define	USLCOM_FLOW_RTS_ON      0x00000040 /* RTS static active */
+#define	USLCOM_FLOW_RTS_HS      0x00000080 /* RTS handshake */
+
+/* USLCOM_VENDOR_SPECIFIC values */
+#define	USLCOM_GET_PARTNUM	0x370B
+#define	USLCOM_WRITE_LATCH	0x37E1
+#define	USLCOM_READ_LATCH	0x00C2
+
+/* USLCOM_GET_PARTNUM values from hardware */
+#define	USLCOM_PARTNUM_CP2101	1
+#define	USLCOM_PARTNUM_CP2102	2
+#define	USLCOM_PARTNUM_CP2103	3
+#define	USLCOM_PARTNUM_CP2104	4
+#define	USLCOM_PARTNUM_CP2105	5
+
+enum {
+	USLCOM_BULK_DT_WR,
+	USLCOM_BULK_DT_RD,
+	USLCOM_CTRL_DT_RD,
+	USLCOM_N_TRANSFER,
+};
+
+struct uslcom_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+	struct usb_callout sc_watchdog;
+
+	struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint8_t		 sc_msr;
+	uint8_t		 sc_lsr;
+	uint8_t		 sc_iface_no;
+	uint8_t		 sc_partnum;
+};
+
+static device_probe_t uslcom_probe;
+static device_attach_t uslcom_attach;
+static device_detach_t uslcom_detach;
+static void uslcom_free_softc(struct uslcom_softc *);
+
+static usb_callback_t uslcom_write_callback;
+static usb_callback_t uslcom_read_callback;
+static usb_callback_t uslcom_control_callback;
+
+static void	uslcom_free(struct ucom_softc *);
+static void uslcom_open(struct ucom_softc *);
+static void uslcom_close(struct ucom_softc *);
+static uint8_t uslcom_get_partnum(struct uslcom_softc *);
+static void uslcom_set_dtr(struct ucom_softc *, uint8_t);
+static void uslcom_set_rts(struct ucom_softc *, uint8_t);
+static void uslcom_set_break(struct ucom_softc *, uint8_t);
+static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
+		struct thread *);
+static int uslcom_pre_param(struct ucom_softc *, struct termios *);
+static void uslcom_param(struct ucom_softc *, struct termios *);
+static void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
+static void uslcom_start_read(struct ucom_softc *);
+static void uslcom_stop_read(struct ucom_softc *);
+static void uslcom_start_write(struct ucom_softc *);
+static void uslcom_stop_write(struct ucom_softc *);
+static void uslcom_poll(struct ucom_softc *ucom);
+
+static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = {
+
+	[USLCOM_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = USLCOM_BULK_BUF_SIZE,
+               .flags = {.pipe_bof = 1,},
+		.callback = &uslcom_write_callback,
+	},
+
+	[USLCOM_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = USLCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uslcom_read_callback,
+	},
+	[USLCOM_CTRL_DT_RD] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,
+		.direction = UE_DIR_ANY,
+		.bufsize = sizeof(struct usb_device_request) + 8,
+		.flags = {.pipe_bof = 1,},
+		.callback = &uslcom_control_callback,
+		.timeout = 1000,	/* 1 second timeout */
+	},
+};
+
+static struct ucom_callback uslcom_callback = {
+	.ucom_cfg_open = &uslcom_open,
+	.ucom_cfg_close = &uslcom_close,
+	.ucom_cfg_get_status = &uslcom_get_status,
+	.ucom_cfg_set_dtr = &uslcom_set_dtr,
+	.ucom_cfg_set_rts = &uslcom_set_rts,
+	.ucom_cfg_set_break = &uslcom_set_break,
+	.ucom_ioctl = &uslcom_ioctl,
+	.ucom_cfg_param = &uslcom_param,
+	.ucom_pre_param = &uslcom_pre_param,
+	.ucom_start_read = &uslcom_start_read,
+	.ucom_stop_read = &uslcom_stop_read,
+	.ucom_start_write = &uslcom_start_write,
+	.ucom_stop_write = &uslcom_stop_write,
+	.ucom_poll = &uslcom_poll,
+	.ucom_free = &uslcom_free,
+};
+
+static const STRUCT_USB_HOST_ID uslcom_devs[] = {
+#define	USLCOM_DEV(v,p)  { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) }
+    USLCOM_DEV(BALTECH, CARDREADER),
+    USLCOM_DEV(CLIPSAL, 5000CT2),
+    USLCOM_DEV(CLIPSAL, 5500PACA),
+    USLCOM_DEV(CLIPSAL, 5500PCU),
+    USLCOM_DEV(CLIPSAL, 560884),
+    USLCOM_DEV(CLIPSAL, 5800PC),
+    USLCOM_DEV(CLIPSAL, C5000CT2),
+    USLCOM_DEV(CLIPSAL, L51xx),
+    USLCOM_DEV(DATAAPEX, MULTICOM),
+    USLCOM_DEV(DELL, DW700),
+    USLCOM_DEV(DIGIANSWER, ZIGBEE802154),
+    USLCOM_DEV(DYNASTREAM, ANTDEVBOARD),
+    USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2),
+    USLCOM_DEV(DYNASTREAM, ANT2USB),
+    USLCOM_DEV(ELV, USBI2C),
+    USLCOM_DEV(FESTO, CMSP),
+    USLCOM_DEV(FESTO, CPX_USB),
+    USLCOM_DEV(FOXCONN, PIRELLI_DP_L10),
+    USLCOM_DEV(FOXCONN, TCOM_TC_300),
+    USLCOM_DEV(GEMALTO, PROXPU),
+    USLCOM_DEV(JABLOTRON, PC60B),
+    USLCOM_DEV(KAMSTRUP, OPTICALEYE),
+    USLCOM_DEV(KAMSTRUP, MBUS_250D),
+    USLCOM_DEV(LAKESHORE, 121),
+    USLCOM_DEV(LAKESHORE, 218A),
+    USLCOM_DEV(LAKESHORE, 219),
+    USLCOM_DEV(LAKESHORE, 233),
+    USLCOM_DEV(LAKESHORE, 235),
+    USLCOM_DEV(LAKESHORE, 335),
+    USLCOM_DEV(LAKESHORE, 336),
+    USLCOM_DEV(LAKESHORE, 350),
+    USLCOM_DEV(LAKESHORE, 371),
+    USLCOM_DEV(LAKESHORE, 411),
+    USLCOM_DEV(LAKESHORE, 425),
+    USLCOM_DEV(LAKESHORE, 455A),
+    USLCOM_DEV(LAKESHORE, 465),
+    USLCOM_DEV(LAKESHORE, 475A),
+    USLCOM_DEV(LAKESHORE, 625A),
+    USLCOM_DEV(LAKESHORE, 642A),
+    USLCOM_DEV(LAKESHORE, 648),
+    USLCOM_DEV(LAKESHORE, 737),
+    USLCOM_DEV(LAKESHORE, 776),
+    USLCOM_DEV(LINKINSTRUMENTS, MSO19),
+    USLCOM_DEV(LINKINSTRUMENTS, MSO28),
+    USLCOM_DEV(LINKINSTRUMENTS, MSO28_2),
+    USLCOM_DEV(MEI, CASHFLOW_SC),
+    USLCOM_DEV(MEI, S2000),
+    USLCOM_DEV(NETGEAR, M4100),
+    USLCOM_DEV(OWEN, AC4),
+    USLCOM_DEV(OWL, CM_160),
+    USLCOM_DEV(PHILIPS, ACE1001),
+    USLCOM_DEV(PLX, CA42),
+    USLCOM_DEV(RENESAS, RX610),
+    USLCOM_DEV(SEL, C662),
+    USLCOM_DEV(SILABS, AC_SERV_CAN),
+    USLCOM_DEV(SILABS, AC_SERV_CIS),
+    USLCOM_DEV(SILABS, AC_SERV_IBUS),
+    USLCOM_DEV(SILABS, AC_SERV_OBD),
+    USLCOM_DEV(SILABS, AEROCOMM),
+    USLCOM_DEV(SILABS, AMBER_AMB2560),
+    USLCOM_DEV(SILABS, ARGUSISP),
+    USLCOM_DEV(SILABS, ARKHAM_DS101_A),
+    USLCOM_DEV(SILABS, ARKHAM_DS101_M),
+    USLCOM_DEV(SILABS, ARYGON_MIFARE),
+    USLCOM_DEV(SILABS, AVIT_USB_TTL),
+    USLCOM_DEV(SILABS, B_G_H3000),
+    USLCOM_DEV(SILABS, BALLUFF_RFID),
+    USLCOM_DEV(SILABS, BEI_VCP),
+    USLCOM_DEV(SILABS, BSM7DUSB),
+    USLCOM_DEV(SILABS, BURNSIDE),
+    USLCOM_DEV(SILABS, C2_EDGE_MODEM),
+    USLCOM_DEV(SILABS, CP2102),
+    USLCOM_DEV(SILABS, CP210X_2),
+    USLCOM_DEV(SILABS, CP210X_3),
+    USLCOM_DEV(SILABS, CP210X_4),
+    USLCOM_DEV(SILABS, CRUMB128),
+    USLCOM_DEV(SILABS, CYGNAL),
+    USLCOM_DEV(SILABS, CYGNAL_DEBUG),
+    USLCOM_DEV(SILABS, CYGNAL_GPS),
+    USLCOM_DEV(SILABS, DEGREE),
+    USLCOM_DEV(SILABS, DEKTEK_DTAPLUS),
+    USLCOM_DEV(SILABS, EMS_C1007),
+    USLCOM_DEV(SILABS, HAMLINKUSB),
+    USLCOM_DEV(SILABS, HELICOM),
+    USLCOM_DEV(SILABS, IMS_USB_RS422),
+    USLCOM_DEV(SILABS, INFINITY_MIC),
+    USLCOM_DEV(SILABS, INGENI_ZIGBEE),
+    USLCOM_DEV(SILABS, INSYS_MODEM),
+    USLCOM_DEV(SILABS, IRZ_SG10),
+    USLCOM_DEV(SILABS, KYOCERA_GPS),
+    USLCOM_DEV(SILABS, LIPOWSKY_HARP),
+    USLCOM_DEV(SILABS, LIPOWSKY_JTAG),
+    USLCOM_DEV(SILABS, LIPOWSKY_LIN),
+    USLCOM_DEV(SILABS, MC35PU),
+    USLCOM_DEV(SILABS, MMB_ZIGBEE),
+    USLCOM_DEV(SILABS, MJS_TOSLINK),
+    USLCOM_DEV(SILABS, MSD_DASHHAWK),
+    USLCOM_DEV(SILABS, MULTIPLEX_RC),
+    USLCOM_DEV(SILABS, OPTRIS_MSPRO),
+    USLCOM_DEV(SILABS, POLOLU),
+    USLCOM_DEV(SILABS, PROCYON_AVS),
+    USLCOM_DEV(SILABS, SB_PARAMOUNT_ME),
+    USLCOM_DEV(SILABS, SUUNTO),
+    USLCOM_DEV(SILABS, TAMSMASTER),
+    USLCOM_DEV(SILABS, TELEGESIS_ETRX2),
+    USLCOM_DEV(SILABS, TRACIENT),
+    USLCOM_DEV(SILABS, TRAQMATE),
+    USLCOM_DEV(SILABS, USBCOUNT50),
+    USLCOM_DEV(SILABS, USBPULSE100),
+    USLCOM_DEV(SILABS, USBSCOPE50),
+    USLCOM_DEV(SILABS, USBWAVE12),
+    USLCOM_DEV(SILABS, V_PREON32),
+    USLCOM_DEV(SILABS, VSTABI),
+    USLCOM_DEV(SILABS, WAVIT),
+    USLCOM_DEV(SILABS, WMRBATT),
+    USLCOM_DEV(SILABS, WMRRIGBLASTER),
+    USLCOM_DEV(SILABS, WMRRIGTALK),
+    USLCOM_DEV(SILABS, ZEPHYR_BIO),
+    USLCOM_DEV(SILABS2, DCU11CLONE),
+    USLCOM_DEV(SILABS3, GPRS_MODEM),
+    USLCOM_DEV(SILABS4, 100EU_MODEM),
+    USLCOM_DEV(SYNTECH, CYPHERLAB100),
+    USLCOM_DEV(USI, MC60),
+    USLCOM_DEV(VAISALA, CABLE),
+    USLCOM_DEV(WAGO, SERVICECABLE),
+    USLCOM_DEV(WAVESENSE, JAZZ),
+    USLCOM_DEV(WESTMOUNTAIN, RIGBLASTER_ADVANTAGE),
+    USLCOM_DEV(WIENERPLEINBAUS, PL512),
+    USLCOM_DEV(WIENERPLEINBAUS, RCM),
+    USLCOM_DEV(WIENERPLEINBAUS, MPOD),
+    USLCOM_DEV(WIENERPLEINBAUS, CML),
+#undef USLCOM_DEV
+};
+
+static device_method_t uslcom_methods[] = {
+	DEVMETHOD(device_probe, uslcom_probe),
+	DEVMETHOD(device_attach, uslcom_attach),
+	DEVMETHOD(device_detach, uslcom_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uslcom_devclass;
+
+static driver_t uslcom_driver = {
+	.name = "uslcom",
+	.methods = uslcom_methods,
+	.size = sizeof(struct uslcom_softc),
+};
+
+DRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0);
+MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uslcom, usb, 1, 1, 1);
+MODULE_VERSION(uslcom, 1);
+USB_PNP_HOST_INFO(uslcom_devs);
+
+static void
+uslcom_watchdog(void *arg)
+{
+	struct uslcom_softc *sc = arg;
+
+	mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+	usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
+
+	usb_callout_reset(&sc->sc_watchdog,
+	    hz / 4, &uslcom_watchdog, sc);
+}
+
+static int
+uslcom_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	DPRINTFN(11, "\n");
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
+}
+
+static int
+uslcom_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uslcom_softc *sc = device_get_softc(dev);
+	int error;
+
+	DPRINTFN(11, "\n");
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+	usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
+
+	sc->sc_udev = uaa->device;
+	/* use the interface number from the USB interface descriptor */
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+
+	error = usbd_transfer_setup(uaa->device,
+	    &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
+	    USLCOM_N_TRANSFER, sc, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("one or more missing USB endpoints, "
+		    "error=%s\n", usbd_errstr(error));
+		goto detach;
+	}
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	sc->sc_partnum = uslcom_get_partnum(sc);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uslcom_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	uslcom_detach(dev);
+	return (ENXIO);
+}
+
+static int
+uslcom_detach(device_t dev)
+{
+	struct uslcom_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
+
+	usb_callout_drain(&sc->sc_watchdog);
+
+	device_claim_softc(dev);
+
+	uslcom_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uslcom);
+
+static void
+uslcom_free_softc(struct uslcom_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uslcom_free(struct ucom_softc *ucom)
+{
+	uslcom_free_softc(ucom->sc_parent);
+}
+
+static void
+uslcom_open(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_IFC_ENABLE;
+	USETW(req.wValue, USLCOM_IFC_ENABLE_EN);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000)) {
+		DPRINTF("UART enable failed (ignored)\n");
+	}
+
+	/* start polling status */
+	uslcom_watchdog(sc);
+}
+
+static void
+uslcom_close(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+
+	/* stop polling status */
+	usb_callout_stop(&sc->sc_watchdog);
+
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_IFC_ENABLE;
+	USETW(req.wValue, USLCOM_IFC_ENABLE_DIS);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+	if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000)) {
+		DPRINTF("UART disable failed (ignored)\n");
+	}
+}
+
+static uint8_t
+uslcom_get_partnum(struct uslcom_softc *sc)
+{
+	struct usb_device_request req;
+	uint8_t partnum;
+
+	/* Find specific chip type */
+	partnum = 0;
+	req.bmRequestType = USLCOM_READ;
+	req.bRequest = USLCOM_VENDOR_SPECIFIC;
+	USETW(req.wValue, USLCOM_GET_PARTNUM);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, sizeof(partnum));
+
+	if (usbd_do_request_flags(sc->sc_udev, NULL,
+	    &req, &partnum, 0, NULL, 1000)) {
+		DPRINTF("GET_PARTNUM failed\n");
+	}
+
+	return(partnum);
+}
+
+static void
+uslcom_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+        struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t ctl;
+
+        DPRINTF("onoff = %d\n", onoff);
+
+	ctl = onoff ? USLCOM_MHS_DTR_ON : 0;
+	ctl |= USLCOM_MHS_DTR_SET;
+
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_MHS;
+	USETW(req.wValue, ctl);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000)) {
+		DPRINTF("Setting DTR failed (ignored)\n");
+	}
+}
+
+static void
+uslcom_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+        struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t ctl;
+
+        DPRINTF("onoff = %d\n", onoff);
+
+	ctl = onoff ? USLCOM_MHS_RTS_ON : 0;
+	ctl |= USLCOM_MHS_RTS_SET;
+
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_MHS;
+	USETW(req.wValue, ctl);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000)) {
+		DPRINTF("Setting DTR failed (ignored)\n");
+	}
+}
+
+static int
+uslcom_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
+		return (EINVAL);
+	return (0);
+}
+
+static void
+uslcom_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint32_t baudrate, flowctrl[4];
+	uint16_t data;
+
+	DPRINTF("\n");
+
+	baudrate = t->c_ospeed;
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_BAUDRATE;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, sizeof(baudrate));
+
+	if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, &baudrate, 0, 1000)) {
+		DPRINTF("Set baudrate failed (ignored)\n");
+	}
+
+	if (t->c_cflag & CSTOPB)
+		data = USLCOM_STOP_BITS_2;
+	else
+		data = USLCOM_STOP_BITS_1;
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD)
+			data |= USLCOM_PARITY_ODD;
+		else
+			data |= USLCOM_PARITY_EVEN;
+	} else
+		data |= USLCOM_PARITY_NONE;
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		data |= USLCOM_SET_DATA_BITS(5);
+		break;
+	case CS6:
+		data |= USLCOM_SET_DATA_BITS(6);
+		break;
+	case CS7:
+		data |= USLCOM_SET_DATA_BITS(7);
+		break;
+	case CS8:
+		data |= USLCOM_SET_DATA_BITS(8);
+		break;
+	}
+
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_LINE_CTL;
+	USETW(req.wValue, data);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000)) {
+		DPRINTF("Set format failed (ignored)\n");
+	}
+       
+	if (t->c_cflag & CRTSCTS) {
+		flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS);
+		flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS);
+	} else {
+		flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON);
+		flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON);
+	}
+	flowctrl[2] = 0;
+	flowctrl[3] = 0;
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_FLOW;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, sizeof(flowctrl));
+
+	if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, flowctrl, 0, 1000)) {
+		DPRINTF("Set flowcontrol failed (ignored)\n");
+	}
+}
+
+static void
+uslcom_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("\n");
+
+	/* XXX Note: sc_lsr is always zero */
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+uslcom_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+        struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	uint16_t brk = onoff ? USLCOM_SET_BREAK_ON : USLCOM_SET_BREAK_OFF;
+
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_BREAK;
+	USETW(req.wValue, brk);
+	USETW(req.wIndex, sc->sc_iface_no);
+	USETW(req.wLength, 0);
+
+        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000)) {
+		DPRINTF("Set BREAK failed (ignored)\n");
+	}
+}
+
+static int
+uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
+    int flag, struct thread *td)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+	struct usb_device_request req;
+	int error = 0;
+	uint8_t latch;
+
+	DPRINTF("cmd=0x%08x\n", cmd);
+
+	switch (cmd) {
+	case USB_GET_GPIO:
+		if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) {
+			error = ENODEV;
+			break;
+		}
+		req.bmRequestType = USLCOM_READ;
+		req.bRequest = USLCOM_VENDOR_SPECIFIC;
+		USETW(req.wValue, USLCOM_READ_LATCH);
+		USETW(req.wIndex, sc->sc_iface_no);
+		USETW(req.wLength, sizeof(latch));
+
+		if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+		    &req, &latch, 0, 1000)) {
+			DPRINTF("Get LATCH failed\n");
+			error = EIO;
+		}
+		*(int *)data = latch;
+		break;
+
+	case USB_SET_GPIO:
+		if (sc->sc_partnum < USLCOM_PARTNUM_CP2103)
+			error = ENODEV;
+		else if ((sc->sc_partnum == USLCOM_PARTNUM_CP2103) ||
+		    (sc->sc_partnum == USLCOM_PARTNUM_CP2104)) {
+			req.bmRequestType = USLCOM_WRITE;
+			req.bRequest = USLCOM_VENDOR_SPECIFIC;
+			USETW(req.wValue, USLCOM_WRITE_LATCH);
+			USETW(req.wIndex, (*(int *)data));
+			USETW(req.wLength, 0);
+
+			if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+			    &req, NULL, 0, 1000)) {
+				DPRINTF("Set LATCH failed\n");
+				error = EIO;
+			}
+		} else
+			error = ENODEV;	/* Not yet */
+		break;
+
+	default:
+		DPRINTF("Unknown IOCTL\n");
+		error = ENOIOCTL;
+		break;
+	}
+	return (error);
+}
+
+static void
+uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    USLCOM_BULK_BUF_SIZE, &actlen)) {
+
+			DPRINTF("actlen = %d\n", actlen);
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uslcom_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	struct usb_device_request req;
+	uint8_t msr = 0;
+	uint8_t buf;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 1);
+		usbd_copy_out(pc, 0, &buf, sizeof(buf));
+		if (buf & USLCOM_MHS_CTS)
+			msr |= SER_CTS;
+		if (buf & USLCOM_MHS_DSR)
+			msr |= SER_DSR;
+		if (buf & USLCOM_MHS_RI)
+			msr |= SER_RI;
+		if (buf & USLCOM_MHS_DCD)
+			msr |= SER_DCD;
+
+		if (msr != sc->sc_msr) {
+			DPRINTF("status change msr=0x%02x "
+			    "(was 0x%02x)\n", msr, sc->sc_msr);
+			sc->sc_msr = msr;
+			ucom_status_change(&sc->sc_ucom);
+		}
+		break;
+
+	case USB_ST_SETUP:
+		req.bmRequestType = USLCOM_READ;
+		req.bRequest = USLCOM_GET_MDMSTS;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, sc->sc_iface_no);
+		USETW(req.wLength, sizeof(buf));
+               
+		usbd_xfer_set_frames(xfer, 2);
+		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+		usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
+
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_in(pc, 0, &req, sizeof(req));
+		usbd_transfer_submit(xfer);
+		break;
+
+	default:		/* error */
+		if (error != USB_ERR_CANCELLED)
+			DPRINTF("error=%s\n", usbd_errstr(error));
+		break;
+	}
+}
+
+static void
+uslcom_start_read(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+
+	/* start read endpoint */
+	usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+}
+
+static void
+uslcom_stop_read(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+
+	/* stop read endpoint */
+	usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+}
+
+static void
+uslcom_start_write(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+}
+
+static void
+uslcom_stop_write(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+}
+
+static void
+uslcom_poll(struct ucom_softc *ucom)
+{
+	struct uslcom_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER);
+}
Index: freebsd/sys/dev/usb/serial/uvisor.c
===================================================================
--- freebsd/sys/dev/usb/serial/uvisor.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uvisor.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,679 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $	*/
+/*      $FreeBSD$ */
+
+/* Also already merged from NetBSD:
+ *	$NetBSD: uvisor.c,v 1.12 2001/11/13 06:24:57 lukem Exp $
+ *	$NetBSD: uvisor.c,v 1.13 2002/02/11 15:11:49 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.15 2002/06/16 15:01:31 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.16 2002/07/11 21:14:36 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.17 2002/08/13 11:38:15 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.18 2003/02/05 00:50:14 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.19 2003/02/07 18:12:37 augustss Exp $
+ *	$NetBSD: uvisor.c,v 1.20 2003/04/11 01:30:10 simonb Exp $
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Handspring Visor (Palmpilot compatible PDA) driver
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR uvisor_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int uvisor_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor");
+SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &uvisor_debug, 0, "Debug level");
+#endif
+
+#define	UVISOR_CONFIG_INDEX	0
+#define	UVISOR_IFACE_INDEX	0
+
+/*
+ * The following buffer sizes are hardcoded due to the way the Palm
+ * firmware works. It looks like the device is not short terminating
+ * the data transferred.
+ */
+#define	UVISORIBUFSIZE	       0	/* Use wMaxPacketSize */
+#define	UVISOROBUFSIZE	       32	/* bytes */
+#define	UVISOROFRAMES	       32	/* units */
+
+/* From the Linux driver */
+/*
+ * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transferred to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ */
+#define	UVISOR_REQUEST_BYTES_AVAILABLE		0x01
+
+/*
+ * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ */
+#define	UVISOR_CLOSE_NOTIFICATION		0x02
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ */
+#define	UVISOR_GET_CONNECTION_INFORMATION	0x03
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ */
+#define	UVISOR_MAX_CONN 8
+struct uvisor_connection_info {
+	uWord	num_ports;
+	struct {
+		uByte	port_function_id;
+		uByte	port;
+	} __packed connections[UVISOR_MAX_CONN];
+} __packed;
+
+#define	UVISOR_CONNECTION_INFO_SIZE 18
+
+/* struct uvisor_connection_info.connection[x].port defines: */
+#define	UVISOR_ENDPOINT_1		0x01
+#define	UVISOR_ENDPOINT_2		0x02
+
+/* struct uvisor_connection_info.connection[x].port_function_id defines: */
+#define	UVISOR_FUNCTION_GENERIC		0x00
+#define	UVISOR_FUNCTION_DEBUGGER	0x01
+#define	UVISOR_FUNCTION_HOTSYNC		0x02
+#define	UVISOR_FUNCTION_CONSOLE		0x03
+#define	UVISOR_FUNCTION_REMOTE_FILE_SYS	0x04
+
+/*
+ * Unknown PalmOS stuff.
+ */
+#define	UVISOR_GET_PALM_INFORMATION		0x04
+#define	UVISOR_GET_PALM_INFORMATION_LEN		0x44
+
+struct uvisor_palm_connection_info {
+	uByte	num_ports;
+	uByte	endpoint_numbers_different;
+	uWord	reserved1;
+	struct {
+		uDWord	port_function_id;
+		uByte	port;
+		uByte	end_point_info;
+		uWord	reserved;
+	} __packed connections[UVISOR_MAX_CONN];
+} __packed;
+
+enum {
+	UVISOR_BULK_DT_WR,
+	UVISOR_BULK_DT_RD,
+	UVISOR_N_TRANSFER,
+};
+
+struct uvisor_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UVISOR_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint16_t sc_flag;
+#define	UVISOR_FLAG_PALM4       0x0001
+#define	UVISOR_FLAG_VISOR       0x0002
+#define	UVISOR_FLAG_PALM35      0x0004
+#define	UVISOR_FLAG_SEND_NOTIFY 0x0008
+
+	uint8_t	sc_iface_no;
+	uint8_t	sc_iface_index;
+};
+
+/* prototypes */
+
+static device_probe_t uvisor_probe;
+static device_attach_t uvisor_attach;
+static device_detach_t uvisor_detach;
+static void uvisor_free_softc(struct uvisor_softc *);
+
+static usb_callback_t uvisor_write_callback;
+static usb_callback_t uvisor_read_callback;
+
+static usb_error_t uvisor_init(struct uvisor_softc *, struct usb_device *,
+		    struct usb_config *);
+static void	uvisor_free(struct ucom_softc *);
+static void	uvisor_cfg_open(struct ucom_softc *);
+static void	uvisor_cfg_close(struct ucom_softc *);
+static void	uvisor_start_read(struct ucom_softc *);
+static void	uvisor_stop_read(struct ucom_softc *);
+static void	uvisor_start_write(struct ucom_softc *);
+static void	uvisor_stop_write(struct ucom_softc *);
+
+static const struct usb_config uvisor_config[UVISOR_N_TRANSFER] = {
+
+	[UVISOR_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UVISOROBUFSIZE * UVISOROFRAMES,
+		.frames = UVISOROFRAMES,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &uvisor_write_callback,
+	},
+
+	[UVISOR_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UVISORIBUFSIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uvisor_read_callback,
+	},
+};
+
+static const struct ucom_callback uvisor_callback = {
+	.ucom_cfg_open = &uvisor_cfg_open,
+	.ucom_cfg_close = &uvisor_cfg_close,
+	.ucom_start_read = &uvisor_start_read,
+	.ucom_stop_read = &uvisor_stop_read,
+	.ucom_start_write = &uvisor_start_write,
+	.ucom_stop_write = &uvisor_stop_write,
+	.ucom_free = &uvisor_free,
+};
+
+static device_method_t uvisor_methods[] = {
+	DEVMETHOD(device_probe, uvisor_probe),
+	DEVMETHOD(device_attach, uvisor_attach),
+	DEVMETHOD(device_detach, uvisor_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uvisor_devclass;
+
+static driver_t uvisor_driver = {
+	.name = "uvisor",
+	.methods = uvisor_methods,
+	.size = sizeof(struct uvisor_softc),
+};
+
+static const STRUCT_USB_HOST_ID uvisor_devs[] = {
+#define	UVISOR_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) }
+	UVISOR_DEV(ACEECA, MEZ1000, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(ALPHASMART, DANA_SYNC, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(GARMIN, IQUE_3600, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(FOSSIL, WRISTPDA, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(HANDSPRING, VISOR, UVISOR_FLAG_VISOR),
+	UVISOR_DEV(HANDSPRING, TREO, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(HANDSPRING, TREO600, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, M500, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, M505, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, M515, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, I705, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, M125, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, M130, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, TUNGSTEN_Z, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, TUNGSTEN_T, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, ZIRE, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(PALM, ZIRE31, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(SAMSUNG, I500, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(SONY, CLIE_40, 0),
+	UVISOR_DEV(SONY, CLIE_41, 0),
+	UVISOR_DEV(SONY, CLIE_S360, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(SONY, CLIE_NX60, UVISOR_FLAG_PALM4),
+	UVISOR_DEV(SONY, CLIE_35, UVISOR_FLAG_PALM35),
+/*  UVISOR_DEV(SONY, CLIE_25, UVISOR_FLAG_PALM4 ), */
+	UVISOR_DEV(SONY, CLIE_TJ37, UVISOR_FLAG_PALM4),
+/*  UVISOR_DEV(SONY, CLIE_TH55, UVISOR_FLAG_PALM4 ), See PR 80935 */
+	UVISOR_DEV(TAPWAVE, ZODIAC, UVISOR_FLAG_PALM4),
+#undef UVISOR_DEV
+};
+
+DRIVER_MODULE(uvisor, uhub, uvisor_driver, uvisor_devclass, NULL, 0);
+MODULE_DEPEND(uvisor, ucom, 1, 1, 1);
+MODULE_DEPEND(uvisor, usb, 1, 1, 1);
+MODULE_VERSION(uvisor, 1);
+USB_PNP_HOST_INFO(uvisor_devs);
+
+static int
+uvisor_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UVISOR_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UVISOR_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uvisor_devs, sizeof(uvisor_devs), uaa));
+}
+
+static int
+uvisor_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uvisor_softc *sc = device_get_softc(dev);
+	struct usb_config uvisor_config_copy[UVISOR_N_TRANSFER];
+	int error;
+
+	DPRINTF("sc=%p\n", sc);
+	memcpy(uvisor_config_copy, uvisor_config,
+	    sizeof(uvisor_config_copy));
+
+	device_set_usb_desc(dev);
+
+	mtx_init(&sc->sc_mtx, "uvisor", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_udev = uaa->device;
+
+	/* configure the device */
+
+	sc->sc_flag = USB_GET_DRIVER_INFO(uaa);
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+	sc->sc_iface_index = UVISOR_IFACE_INDEX;
+
+	error = uvisor_init(sc, uaa->device, uvisor_config_copy);
+
+	if (error) {
+		DPRINTF("init failed, error=%s\n",
+		    usbd_errstr(error));
+		goto detach;
+	}
+	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
+	    sc->sc_xfer, uvisor_config_copy, UVISOR_N_TRANSFER,
+	    sc, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("could not allocate all pipes\n");
+		goto detach;
+	}
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uvisor_callback, &sc->sc_mtx);
+	if (error) {
+		DPRINTF("ucom_attach failed\n");
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	return (0);
+
+detach:
+	uvisor_detach(dev);
+	return (ENXIO);
+}
+
+static int
+uvisor_detach(device_t dev)
+{
+	struct uvisor_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UVISOR_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uvisor_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uvisor);
+
+static void
+uvisor_free_softc(struct uvisor_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uvisor_free(struct ucom_softc *ucom)
+{
+	uvisor_free_softc(ucom->sc_parent);
+}
+
+static usb_error_t
+uvisor_init(struct uvisor_softc *sc, struct usb_device *udev, struct usb_config *config)
+{
+	usb_error_t err = 0;
+	struct usb_device_request req;
+	struct uvisor_connection_info coninfo;
+	struct uvisor_palm_connection_info pconinfo;
+	uint16_t actlen;
+	uint8_t buffer[256];
+
+	if (sc->sc_flag & UVISOR_FLAG_VISOR) {
+		DPRINTF("getting connection info\n");
+		req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+		req.bRequest = UVISOR_GET_CONNECTION_INFORMATION;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+		err = usbd_do_request_flags(udev, NULL,
+		    &req, &coninfo, USB_SHORT_XFER_OK,
+		    &actlen, USB_DEFAULT_TIMEOUT);
+
+		if (err) {
+			goto done;
+		}
+	}
+#ifdef USB_DEBUG
+	if (sc->sc_flag & UVISOR_FLAG_VISOR) {
+		uint16_t i, np;
+		const char *desc;
+
+		np = UGETW(coninfo.num_ports);
+		if (np > UVISOR_MAX_CONN) {
+			np = UVISOR_MAX_CONN;
+		}
+		DPRINTF("Number of ports: %d\n", np);
+
+		for (i = 0; i < np; ++i) {
+			switch (coninfo.connections[i].port_function_id) {
+			case UVISOR_FUNCTION_GENERIC:
+				desc = "Generic";
+				break;
+			case UVISOR_FUNCTION_DEBUGGER:
+				desc = "Debugger";
+				break;
+			case UVISOR_FUNCTION_HOTSYNC:
+				desc = "HotSync";
+				break;
+			case UVISOR_FUNCTION_REMOTE_FILE_SYS:
+				desc = "Remote File System";
+				break;
+			default:
+				desc = "unknown";
+				break;
+			}
+			DPRINTF("Port %d is for %s\n",
+			    coninfo.connections[i].port, desc);
+		}
+	}
+#endif
+
+	if (sc->sc_flag & UVISOR_FLAG_PALM4) {
+		uint8_t port;
+
+		/* Palm OS 4.0 Hack */
+		req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+		req.bRequest = UVISOR_GET_PALM_INFORMATION;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+
+		err = usbd_do_request_flags
+		    (udev, NULL, &req, &pconinfo, USB_SHORT_XFER_OK,
+		    &actlen, USB_DEFAULT_TIMEOUT);
+
+		if (err) {
+			goto done;
+		}
+		if (actlen < 12) {
+			DPRINTF("too little data\n");
+			err = USB_ERR_INVAL;
+			goto done;
+		}
+		if (pconinfo.endpoint_numbers_different) {
+			port = pconinfo.connections[0].end_point_info;
+			config[0].endpoint = (port & 0xF);	/* output */
+			config[1].endpoint = (port >> 4);	/* input */
+		} else {
+			port = pconinfo.connections[0].port;
+			config[0].endpoint = (port & 0xF);	/* output */
+			config[1].endpoint = (port & 0xF);	/* input */
+		}
+#if 0
+		req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+		req.bRequest = UVISOR_GET_PALM_INFORMATION;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+		err = usbd_do_request(udev, &req, buffer);
+		if (err) {
+			goto done;
+		}
+#endif
+	}
+	if (sc->sc_flag & UVISOR_FLAG_PALM35) {
+		/* get the config number */
+		DPRINTF("getting config info\n");
+		req.bmRequestType = UT_READ;
+		req.bRequest = UR_GET_CONFIG;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, 1);
+
+		err = usbd_do_request(udev, NULL, &req, buffer);
+		if (err) {
+			goto done;
+		}
+		/* get the interface number */
+		DPRINTF("get the interface number\n");
+		req.bmRequestType = UT_READ_DEVICE;
+		req.bRequest = UR_GET_INTERFACE;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, 1);
+		err = usbd_do_request(udev, NULL, &req, buffer);
+		if (err) {
+			goto done;
+		}
+	}
+#if 0
+	uWord wAvail;
+
+	DPRINTF("getting available bytes\n");
+	req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+	req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, 5);
+	USETW(req.wLength, sizeof(wAvail));
+	err = usbd_do_request(udev, NULL, &req, &wAvail);
+	if (err) {
+		goto done;
+	}
+	DPRINTF("avail=%d\n", UGETW(wAvail));
+#endif
+
+	DPRINTF("done\n");
+done:
+	return (err);
+}
+
+static void
+uvisor_cfg_open(struct ucom_softc *ucom)
+{
+	return;
+}
+
+static void
+uvisor_cfg_close(struct ucom_softc *ucom)
+{
+	struct uvisor_softc *sc = ucom->sc_parent;
+	uint8_t buffer[UVISOR_CONNECTION_INFO_SIZE];
+	struct usb_device_request req;
+	usb_error_t err;
+
+	req.bmRequestType = UT_READ_VENDOR_ENDPOINT;	/* XXX read? */
+	req.bRequest = UVISOR_CLOSE_NOTIFICATION;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, buffer, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "close notification failed, error=%s\n",
+		    usbd_errstr(err));
+	}
+}
+
+static void
+uvisor_start_read(struct ucom_softc *ucom)
+{
+	struct uvisor_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+}
+
+static void
+uvisor_stop_read(struct ucom_softc *ucom)
+{
+	struct uvisor_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+}
+
+static void
+uvisor_start_write(struct ucom_softc *ucom)
+{
+	struct uvisor_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+}
+
+static void
+uvisor_stop_write(struct ucom_softc *ucom)
+{
+	struct uvisor_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+}
+
+static void
+uvisor_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvisor_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+	uint8_t x;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		for (x = 0; x != UVISOROFRAMES; x++) {
+
+			usbd_xfer_set_frame_offset(xfer, 
+			    x * UVISOROBUFSIZE, x);
+
+			pc = usbd_xfer_get_frame(xfer, x);
+			if (ucom_get_data(&sc->sc_ucom, pc, 0,
+			    UVISOROBUFSIZE, &actlen)) {
+				usbd_xfer_set_frame_len(xfer, x, actlen);
+			} else {
+				break;
+			}
+		}
+		/* check for data */
+		if (x != 0) {
+			usbd_xfer_set_frames(xfer, x);
+			usbd_transfer_submit(xfer);
+		}
+		break;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		break;
+	}
+}
+
+static void
+uvisor_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvisor_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
Index: freebsd/sys/dev/usb/serial/uvscom.c
===================================================================
--- freebsd/sys/dev/usb/serial/uvscom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
+++ freebsd/sys/dev/usb/serial/uvscom.c	(revision 286c391e39287b0615462de4d6c4ec9e183e99cc)
@@ -0,0 +1,773 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*	$NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $	*/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * uvscom: SUNTAC Slipper U VS-10U driver.
+ * Slipper U is a PC Card to USB converter for data communication card
+ * adapter.  It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in,
+ * P-in m@ater and various data communication card adapters.
+ */
+
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <rtems/bsd/sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <rtems/bsd/sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <rtems/bsd/local/usbdevs.h>
+
+#define	USB_DEBUG_VAR uvscom_debug
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#ifdef USB_DEBUG
+static int uvscom_debug = 0;
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
+SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &uvscom_debug, 0, "Debug level");
+#endif
+
+#define	UVSCOM_MODVER		1	/* module version */
+
+#define	UVSCOM_CONFIG_INDEX	0
+#define	UVSCOM_IFACE_INDEX	0
+
+/* Request */
+#define	UVSCOM_SET_SPEED	0x10
+#define	UVSCOM_LINE_CTL		0x11
+#define	UVSCOM_SET_PARAM	0x12
+#define	UVSCOM_READ_STATUS	0xd0
+#define	UVSCOM_SHUTDOWN		0xe0
+
+/* UVSCOM_SET_SPEED parameters */
+#define	UVSCOM_SPEED_150BPS	0x00
+#define	UVSCOM_SPEED_300BPS	0x01
+#define	UVSCOM_SPEED_600BPS	0x02
+#define	UVSCOM_SPEED_1200BPS	0x03
+#define	UVSCOM_SPEED_2400BPS	0x04
+#define	UVSCOM_SPEED_4800BPS	0x05
+#define	UVSCOM_SPEED_9600BPS	0x06
+#define	UVSCOM_SPEED_19200BPS	0x07
+#define	UVSCOM_SPEED_38400BPS	0x08
+#define	UVSCOM_SPEED_57600BPS	0x09
+#define	UVSCOM_SPEED_115200BPS	0x0a
+
+/* UVSCOM_LINE_CTL parameters */
+#define	UVSCOM_BREAK		0x40
+#define	UVSCOM_RTS		0x02
+#define	UVSCOM_DTR		0x01
+#define	UVSCOM_LINE_INIT	0x08
+
+/* UVSCOM_SET_PARAM parameters */
+#define	UVSCOM_DATA_MASK	0x03
+#define	UVSCOM_DATA_BIT_8	0x03
+#define	UVSCOM_DATA_BIT_7	0x02
+#define	UVSCOM_DATA_BIT_6	0x01
+#define	UVSCOM_DATA_BIT_5	0x00
+
+#define	UVSCOM_STOP_MASK	0x04
+#define	UVSCOM_STOP_BIT_2	0x04
+#define	UVSCOM_STOP_BIT_1	0x00
+
+#define	UVSCOM_PARITY_MASK	0x18
+#define	UVSCOM_PARITY_EVEN	0x18
+#define	UVSCOM_PARITY_ODD	0x08
+#define	UVSCOM_PARITY_NONE	0x00
+
+/* Status bits */
+#define	UVSCOM_TXRDY		0x04
+#define	UVSCOM_RXRDY		0x01
+
+#define	UVSCOM_DCD		0x08
+#define	UVSCOM_NOCARD		0x04
+#define	UVSCOM_DSR		0x02
+#define	UVSCOM_CTS		0x01
+#define	UVSCOM_USTAT_MASK	(UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS)
+
+#define	UVSCOM_BULK_BUF_SIZE	1024	/* bytes */
+
+enum {
+	UVSCOM_BULK_DT_WR,
+	UVSCOM_BULK_DT_RD,
+	UVSCOM_INTR_DT_RD,
+	UVSCOM_N_TRANSFER,
+};
+
+struct uvscom_softc {
+	struct ucom_super_softc sc_super_ucom;
+	struct ucom_softc sc_ucom;
+
+	struct usb_xfer *sc_xfer[UVSCOM_N_TRANSFER];
+	struct usb_device *sc_udev;
+	struct mtx sc_mtx;
+
+	uint16_t sc_line;		/* line control register */
+
+	uint8_t	sc_iface_no;		/* interface number */
+	uint8_t	sc_iface_index;		/* interface index */
+	uint8_t	sc_lsr;			/* local status register */
+	uint8_t	sc_msr;			/* uvscom status register */
+	uint8_t	sc_unit_status;		/* unit status */
+};
+
+/* prototypes */
+
+static device_probe_t uvscom_probe;
+static device_attach_t uvscom_attach;
+static device_detach_t uvscom_detach;
+static void uvscom_free_softc(struct uvscom_softc *);
+
+static usb_callback_t uvscom_write_callback;
+static usb_callback_t uvscom_read_callback;
+static usb_callback_t uvscom_intr_callback;
+
+static void	uvscom_free(struct ucom_softc *);
+static void	uvscom_cfg_set_dtr(struct ucom_softc *, uint8_t);
+static void	uvscom_cfg_set_rts(struct ucom_softc *, uint8_t);
+static void	uvscom_cfg_set_break(struct ucom_softc *, uint8_t);
+static int	uvscom_pre_param(struct ucom_softc *, struct termios *);
+static void	uvscom_cfg_param(struct ucom_softc *, struct termios *);
+static int	uvscom_pre_open(struct ucom_softc *);
+static void	uvscom_cfg_open(struct ucom_softc *);
+static void	uvscom_cfg_close(struct ucom_softc *);
+static void	uvscom_start_read(struct ucom_softc *);
+static void	uvscom_stop_read(struct ucom_softc *);
+static void	uvscom_start_write(struct ucom_softc *);
+static void	uvscom_stop_write(struct ucom_softc *);
+static void	uvscom_cfg_get_status(struct ucom_softc *, uint8_t *,
+		    uint8_t *);
+static void	uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t);
+static uint16_t	uvscom_cfg_read_status(struct uvscom_softc *);
+static void	uvscom_poll(struct ucom_softc *ucom);
+
+static const struct usb_config uvscom_config[UVSCOM_N_TRANSFER] = {
+
+	[UVSCOM_BULK_DT_WR] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_OUT,
+		.bufsize = UVSCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.callback = &uvscom_write_callback,
+	},
+
+	[UVSCOM_BULK_DT_RD] = {
+		.type = UE_BULK,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.bufsize = UVSCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uvscom_read_callback,
+	},
+
+	[UVSCOM_INTR_DT_RD] = {
+		.type = UE_INTERRUPT,
+		.endpoint = UE_ADDR_ANY,
+		.direction = UE_DIR_IN,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.bufsize = 0,	/* use wMaxPacketSize */
+		.callback = &uvscom_intr_callback,
+	},
+};
+
+static const struct ucom_callback uvscom_callback = {
+	.ucom_cfg_get_status = &uvscom_cfg_get_status,
+	.ucom_cfg_set_dtr = &uvscom_cfg_set_dtr,
+	.ucom_cfg_set_rts = &uvscom_cfg_set_rts,
+	.ucom_cfg_set_break = &uvscom_cfg_set_break,
+	.ucom_cfg_param = &uvscom_cfg_param,
+	.ucom_cfg_open = &uvscom_cfg_open,
+	.ucom_cfg_close = &uvscom_cfg_close,
+	.ucom_pre_open = &uvscom_pre_open,
+	.ucom_pre_param = &uvscom_pre_param,
+	.ucom_start_read = &uvscom_start_read,
+	.ucom_stop_read = &uvscom_stop_read,
+	.ucom_start_write = &uvscom_start_write,
+	.ucom_stop_write = &uvscom_stop_write,
+	.ucom_poll = &uvscom_poll,
+	.ucom_free = &uvscom_free,
+};
+
+static const STRUCT_USB_HOST_ID uvscom_devs[] = {
+	/* SUNTAC U-Cable type A4 */
+	{USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4, 0)},
+	/* SUNTAC U-Cable type D2 */
+	{USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L, 0)},
+	/* SUNTAC Ir-Trinity */
+	{USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U, 0)},
+	/* SUNTAC U-Cable type P1 */
+	{USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1, 0)},
+	/* SUNTAC Slipper U */
+	{USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U, 0)},
+};
+
+static device_method_t uvscom_methods[] = {
+	DEVMETHOD(device_probe, uvscom_probe),
+	DEVMETHOD(device_attach, uvscom_attach),
+	DEVMETHOD(device_detach, uvscom_detach),
+	DEVMETHOD_END
+};
+
+static devclass_t uvscom_devclass;
+
+static driver_t uvscom_driver = {
+	.name = "uvscom",
+	.methods = uvscom_methods,
+	.size = sizeof(struct uvscom_softc),
+};
+
+DRIVER_MODULE(uvscom, uhub, uvscom_driver, uvscom_devclass, NULL, 0);
+MODULE_DEPEND(uvscom, ucom, 1, 1, 1);
+MODULE_DEPEND(uvscom, usb, 1, 1, 1);
+MODULE_VERSION(uvscom, UVSCOM_MODVER);
+USB_PNP_HOST_INFO(uvscom_devs);
+
+static int
+uvscom_probe(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+	if (uaa->usb_mode != USB_MODE_HOST) {
+		return (ENXIO);
+	}
+	if (uaa->info.bConfigIndex != UVSCOM_CONFIG_INDEX) {
+		return (ENXIO);
+	}
+	if (uaa->info.bIfaceIndex != UVSCOM_IFACE_INDEX) {
+		return (ENXIO);
+	}
+	return (usbd_lookup_id_by_uaa(uvscom_devs, sizeof(uvscom_devs), uaa));
+}
+
+static int
+uvscom_attach(device_t dev)
+{
+	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct uvscom_softc *sc = device_get_softc(dev);
+	int error;
+
+	device_set_usb_desc(dev);
+	mtx_init(&sc->sc_mtx, "uvscom", NULL, MTX_DEF);
+	ucom_ref(&sc->sc_super_ucom);
+
+	sc->sc_udev = uaa->device;
+
+	DPRINTF("sc=%p\n", sc);
+
+	sc->sc_iface_no = uaa->info.bIfaceNum;
+	sc->sc_iface_index = UVSCOM_IFACE_INDEX;
+
+	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
+	    sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &sc->sc_mtx);
+
+	if (error) {
+		DPRINTF("could not allocate all USB transfers!\n");
+		goto detach;
+	}
+	sc->sc_line = UVSCOM_LINE_INIT;
+
+	/* clear stall at first run */
+	mtx_lock(&sc->sc_mtx);
+	usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+	usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+	    &uvscom_callback, &sc->sc_mtx);
+	if (error) {
+		goto detach;
+	}
+	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
+
+	/* start interrupt pipe */
+	mtx_lock(&sc->sc_mtx);
+	usbd_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
+	mtx_unlock(&sc->sc_mtx);
+
+	return (0);
+
+detach:
+	uvscom_detach(dev);
+	return (ENXIO);
+}
+
+static int
+uvscom_detach(device_t dev)
+{
+	struct uvscom_softc *sc = device_get_softc(dev);
+
+	DPRINTF("sc=%p\n", sc);
+
+	/* stop interrupt pipe */
+
+	if (sc->sc_xfer[UVSCOM_INTR_DT_RD])
+		usbd_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
+
+	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
+	usbd_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER);
+
+	device_claim_softc(dev);
+
+	uvscom_free_softc(sc);
+
+	return (0);
+}
+
+UCOM_UNLOAD_DRAIN(uvscom);
+
+static void
+uvscom_free_softc(struct uvscom_softc *sc)
+{
+	if (ucom_unref(&sc->sc_super_ucom)) {
+		mtx_destroy(&sc->sc_mtx);
+		device_free_softc(sc);
+	}
+}
+
+static void
+uvscom_free(struct ucom_softc *ucom)
+{
+	uvscom_free_softc(ucom->sc_parent);
+}
+
+static void
+uvscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvscom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint32_t actlen;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_SETUP:
+	case USB_ST_TRANSFERRED:
+tr_setup:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		if (ucom_get_data(&sc->sc_ucom, pc, 0,
+		    UVSCOM_BULK_BUF_SIZE, &actlen)) {
+
+			usbd_xfer_set_frame_len(xfer, 0, actlen);
+			usbd_transfer_submit(xfer);
+		}
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uvscom_read_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvscom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 0);
+		ucom_put_data(&sc->sc_ucom, pc, 0, actlen);
+
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uvscom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uvscom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf[2];
+	int actlen;
+
+	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		if (actlen >= 2) {
+
+			pc = usbd_xfer_get_frame(xfer, 0);
+			usbd_copy_out(pc, 0, buf, sizeof(buf));
+
+			sc->sc_lsr = 0;
+			sc->sc_msr = 0;
+			sc->sc_unit_status = buf[1];
+
+			if (buf[0] & UVSCOM_TXRDY) {
+				sc->sc_lsr |= ULSR_TXRDY;
+			}
+			if (buf[0] & UVSCOM_RXRDY) {
+				sc->sc_lsr |= ULSR_RXRDY;
+			}
+			if (buf[1] & UVSCOM_CTS) {
+				sc->sc_msr |= SER_CTS;
+			}
+			if (buf[1] & UVSCOM_DSR) {
+				sc->sc_msr |= SER_DSR;
+			}
+			if (buf[1] & UVSCOM_DCD) {
+				sc->sc_msr |= SER_DCD;
+			}
+			/*
+			 * the UCOM layer will ignore this call if the TTY
+			 * device is closed!
+			 */
+			ucom_status_change(&sc->sc_ucom);
+		}
+	case USB_ST_SETUP:
+tr_setup:
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
+		usbd_transfer_submit(xfer);
+		return;
+
+	default:			/* Error */
+		if (error != USB_ERR_CANCELLED) {
+			/* try to clear stall first */
+			usbd_xfer_set_stall(xfer);
+			goto tr_setup;
+		}
+		return;
+	}
+}
+
+static void
+uvscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UVSCOM_DTR;
+	else
+		sc->sc_line &= ~UVSCOM_DTR;
+
+	uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
+}
+
+static void
+uvscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UVSCOM_RTS;
+	else
+		sc->sc_line &= ~UVSCOM_RTS;
+
+	uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
+}
+
+static void
+uvscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("onoff = %d\n", onoff);
+
+	if (onoff)
+		sc->sc_line |= UVSCOM_BREAK;
+	else
+		sc->sc_line &= ~UVSCOM_BREAK;
+
+	uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
+}
+
+static int
+uvscom_pre_param(struct ucom_softc *ucom, struct termios *t)
+{
+	switch (t->c_ospeed) {
+		case B150:
+		case B300:
+		case B600:
+		case B1200:
+		case B2400:
+		case B4800:
+		case B9600:
+		case B19200:
+		case B38400:
+		case B57600:
+		case B115200:
+		default:
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static void
+uvscom_cfg_param(struct ucom_softc *ucom, struct termios *t)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+	uint16_t value;
+
+	DPRINTF("\n");
+
+	switch (t->c_ospeed) {
+	case B150:
+		value = UVSCOM_SPEED_150BPS;
+		break;
+	case B300:
+		value = UVSCOM_SPEED_300BPS;
+		break;
+	case B600:
+		value = UVSCOM_SPEED_600BPS;
+		break;
+	case B1200:
+		value = UVSCOM_SPEED_1200BPS;
+		break;
+	case B2400:
+		value = UVSCOM_SPEED_2400BPS;
+		break;
+	case B4800:
+		value = UVSCOM_SPEED_4800BPS;
+		break;
+	case B9600:
+		value = UVSCOM_SPEED_9600BPS;
+		break;
+	case B19200:
+		value = UVSCOM_SPEED_19200BPS;
+		break;
+	case B38400:
+		value = UVSCOM_SPEED_38400BPS;
+		break;
+	case B57600:
+		value = UVSCOM_SPEED_57600BPS;
+		break;
+	case B115200:
+		value = UVSCOM_SPEED_115200BPS;
+		break;
+	default:
+		return;
+	}
+
+	uvscom_cfg_write(sc, UVSCOM_SET_SPEED, value);
+
+	value = 0;
+
+	if (t->c_cflag & CSTOPB) {
+		value |= UVSCOM_STOP_BIT_2;
+	}
+	if (t->c_cflag & PARENB) {
+		if (t->c_cflag & PARODD) {
+			value |= UVSCOM_PARITY_ODD;
+		} else {
+			value |= UVSCOM_PARITY_EVEN;
+		}
+	} else {
+		value |= UVSCOM_PARITY_NONE;
+	}
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		value |= UVSCOM_DATA_BIT_5;
+		break;
+	case CS6:
+		value |= UVSCOM_DATA_BIT_6;
+		break;
+	case CS7:
+		value |= UVSCOM_DATA_BIT_7;
+		break;
+	default:
+	case CS8:
+		value |= UVSCOM_DATA_BIT_8;
+		break;
+	}
+
+	uvscom_cfg_write(sc, UVSCOM_SET_PARAM, value);
+}
+
+static int
+uvscom_pre_open(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("sc = %p\n", sc);
+
+	/* check if PC card was inserted */
+
+	if (sc->sc_unit_status & UVSCOM_NOCARD) {
+		DPRINTF("no PC card!\n");
+		return (ENXIO);
+	}
+	return (0);
+}
+
+static void
+uvscom_cfg_open(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("sc = %p\n", sc);
+
+	uvscom_cfg_read_status(sc);
+}
+
+static void
+uvscom_cfg_close(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	DPRINTF("sc=%p\n", sc);
+
+	uvscom_cfg_write(sc, UVSCOM_SHUTDOWN, 0);
+}
+
+static void
+uvscom_start_read(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+}
+
+static void
+uvscom_stop_read(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+}
+
+static void
+uvscom_start_write(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+}
+
+static void
+uvscom_stop_write(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	usbd_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+}
+
+static void
+uvscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+
+	*lsr = sc->sc_lsr;
+	*msr = sc->sc_msr;
+}
+
+static void
+uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = index;
+	USETW(req.wValue, value);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, 0);
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, NULL, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "device request failed, err=%s "
+		    "(ignored)\n", usbd_errstr(err));
+	}
+}
+
+static uint16_t
+uvscom_cfg_read_status(struct uvscom_softc *sc)
+{
+	struct usb_device_request req;
+	usb_error_t err;
+	uint8_t data[2];
+
+	req.bmRequestType = UT_READ_VENDOR_DEVICE;
+	req.bRequest = UVSCOM_READ_STATUS;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, 0);
+	USETW(req.wLength, 2);
+
+	err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, data, 0, 1000);
+	if (err) {
+		DPRINTFN(0, "device request failed, err=%s "
+		    "(ignored)\n", usbd_errstr(err));
+	}
+	return (data[0] | (data[1] << 8));
+}
+
+static void
+uvscom_poll(struct ucom_softc *ucom)
+{
+	struct uvscom_softc *sc = ucom->sc_parent;
+	usbd_transfer_poll(sc->sc_xfer, UVSCOM_N_TRANSFER);
+}
