summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Kirspel <kevin-kirspel@idexx.com>2017-05-12 08:16:22 -0400
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-05-12 14:18:38 +0200
commit286c391e39287b0615462de4d6c4ec9e183e99cc (patch)
tree2e4a928725de817cf47b4d5aaea4f11e1886b8f4
parentUpdating scripts for FREEBSD TTY support (diff)
downloadrtems-libbsd-286c391e39287b0615462de4d6c4ec9e183e99cc.tar.bz2
Adding FREEBSD USB Serial Drivers
-rw-r--r--freebsd/sys/dev/usb/serial/u3g.c1234
-rw-r--r--freebsd/sys/dev/usb/serial/uark.c472
-rw-r--r--freebsd/sys/dev/usb/serial/ubsa.c700
-rw-r--r--freebsd/sys/dev/usb/serial/ubser.c561
-rw-r--r--freebsd/sys/dev/usb/serial/uchcom.c880
-rw-r--r--freebsd/sys/dev/usb/serial/ucycom.c614
-rw-r--r--freebsd/sys/dev/usb/serial/ufoma.c1270
-rw-r--r--freebsd/sys/dev/usb/serial/uftdi.c2011
-rw-r--r--freebsd/sys/dev/usb/serial/uftdi_reg.h314
-rw-r--r--freebsd/sys/dev/usb/serial/ugensa.c403
-rw-r--r--freebsd/sys/dev/usb/serial/uipaq.c1377
-rw-r--r--freebsd/sys/dev/usb/serial/ulpt.c764
-rw-r--r--freebsd/sys/dev/usb/serial/umcs.c1107
-rw-r--r--freebsd/sys/dev/usb/serial/umcs.h644
-rw-r--r--freebsd/sys/dev/usb/serial/umct.c683
-rw-r--r--freebsd/sys/dev/usb/serial/umodem.c1028
-rw-r--r--freebsd/sys/dev/usb/serial/umoscom.c736
-rw-r--r--freebsd/sys/dev/usb/serial/uplcom.c936
-rw-r--r--freebsd/sys/dev/usb/serial/usb_serial.c1742
-rw-r--r--freebsd/sys/dev/usb/serial/usb_serial.h230
-rw-r--r--freebsd/sys/dev/usb/serial/uslcom.c946
-rw-r--r--freebsd/sys/dev/usb/serial/uvisor.c679
-rw-r--r--freebsd/sys/dev/usb/serial/uvscom.c773
23 files changed, 20104 insertions, 0 deletions
diff --git a/freebsd/sys/dev/usb/serial/u3g.c b/freebsd/sys/dev/usb/serial/u3g.c
new file mode 100644
index 00000000..2f3289f0
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/u3g.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uark.c b/freebsd/sys/dev/usb/serial/uark.c
new file mode 100644
index 00000000..c04516ac
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uark.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/ubsa.c b/freebsd/sys/dev/usb/serial/ubsa.c
new file mode 100644
index 00000000..a83163a2
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/ubsa.c
@@ -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);
+
+}
diff --git a/freebsd/sys/dev/usb/serial/ubser.c b/freebsd/sys/dev/usb/serial/ubser.c
new file mode 100644
index 00000000..fbac4c76
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/ubser.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uchcom.c b/freebsd/sys/dev/usb/serial/uchcom.c
new file mode 100644
index 00000000..6daef909
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uchcom.c
@@ -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);
diff --git a/freebsd/sys/dev/usb/serial/ucycom.c b/freebsd/sys/dev/usb/serial/ucycom.c
new file mode 100644
index 00000000..09f90ab3
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/ucycom.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/ufoma.c b/freebsd/sys/dev/usb/serial/ufoma.c
new file mode 100644
index 00000000..6a481add
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/ufoma.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uftdi.c b/freebsd/sys/dev/usb/serial/uftdi.c
new file mode 100644
index 00000000..3445b4c7
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uftdi.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uftdi_reg.h b/freebsd/sys/dev/usb/serial/uftdi_reg.h
new file mode 100644
index 00000000..a1ea325b
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uftdi_reg.h
@@ -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))
diff --git a/freebsd/sys/dev/usb/serial/ugensa.c b/freebsd/sys/dev/usb/serial/ugensa.c
new file mode 100644
index 00000000..3e6680dc
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/ugensa.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uipaq.c b/freebsd/sys/dev/usb/serial/uipaq.c
new file mode 100644
index 00000000..e4eb0dab
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uipaq.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/ulpt.c b/freebsd/sys/dev/usb/serial/ulpt.c
new file mode 100644
index 00000000..77600bf7
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/ulpt.c
@@ -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);
diff --git a/freebsd/sys/dev/usb/serial/umcs.c b/freebsd/sys/dev/usb/serial/umcs.c
new file mode 100644
index 00000000..97712ed6
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/umcs.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/umcs.h b/freebsd/sys/dev/usb/serial/umcs.h
new file mode 100644
index 00000000..8ba57c13
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/umcs.h
@@ -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
diff --git a/freebsd/sys/dev/usb/serial/umct.c b/freebsd/sys/dev/usb/serial/umct.c
new file mode 100644
index 00000000..6b41fa94
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/umct.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/umodem.c b/freebsd/sys/dev/usb/serial/umodem.c
new file mode 100644
index 00000000..f0d14914
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/umodem.c
@@ -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 */
+}
diff --git a/freebsd/sys/dev/usb/serial/umoscom.c b/freebsd/sys/dev/usb/serial/umoscom.c
new file mode 100644
index 00000000..87a47c15
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/umoscom.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uplcom.c b/freebsd/sys/dev/usb/serial/uplcom.c
new file mode 100644
index 00000000..31b58670
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uplcom.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/usb_serial.c b/freebsd/sys/dev/usb/serial/usb_serial.c
new file mode 100644
index 00000000..b5d7ef7a
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/usb_serial.c
@@ -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
diff --git a/freebsd/sys/dev/usb/serial/usb_serial.h b/freebsd/sys/dev/usb/serial/usb_serial.h
new file mode 100644
index 00000000..2c53f445
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/usb_serial.h
@@ -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_ */
diff --git a/freebsd/sys/dev/usb/serial/uslcom.c b/freebsd/sys/dev/usb/serial/uslcom.c
new file mode 100644
index 00000000..03cc96bc
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uslcom.c
@@ -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);
+}
diff --git a/freebsd/sys/dev/usb/serial/uvisor.c b/freebsd/sys/dev/usb/serial/uvisor.c
new file mode 100644
index 00000000..e26c596e
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uvisor.c
@@ -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;
+ }
+}
diff --git a/freebsd/sys/dev/usb/serial/uvscom.c b/freebsd/sys/dev/usb/serial/uvscom.c
new file mode 100644
index 00000000..deb80011
--- /dev/null
+++ b/freebsd/sys/dev/usb/serial/uvscom.c
@@ -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);
+}