summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/serial
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/serial')
-rw-r--r--sys/dev/usb/serial/u3g.c583
-rw-r--r--sys/dev/usb/serial/uark.c407
-rw-r--r--sys/dev/usb/serial/ubsa.c634
-rw-r--r--sys/dev/usb/serial/ubser.c518
-rw-r--r--sys/dev/usb/serial/uchcom.c883
-rw-r--r--sys/dev/usb/serial/ucycom.c564
-rw-r--r--sys/dev/usb/serial/ufoma.c1212
-rw-r--r--sys/dev/usb/serial/uftdi.c784
-rw-r--r--sys/dev/usb/serial/uftdi_reg.h340
-rw-r--r--sys/dev/usb/serial/ugensa.c352
-rw-r--r--sys/dev/usb/serial/uipaq.c1314
-rw-r--r--sys/dev/usb/serial/ulpt.c726
-rw-r--r--sys/dev/usb/serial/umct.c579
-rw-r--r--sys/dev/usb/serial/umodem.c788
-rw-r--r--sys/dev/usb/serial/umoscom.c672
-rw-r--r--sys/dev/usb/serial/uplcom.c831
-rw-r--r--sys/dev/usb/serial/usb_serial.c1127
-rw-r--r--sys/dev/usb/serial/usb_serial.h198
-rw-r--r--sys/dev/usb/serial/uslcom.c539
-rw-r--r--sys/dev/usb/serial/uvisor.c610
-rw-r--r--sys/dev/usb/serial/uvscom.c707
21 files changed, 14368 insertions, 0 deletions
diff --git a/sys/dev/usb/serial/u3g.c b/sys/dev/usb/serial/u3g.c
new file mode 100644
index 0000000..ce963d5
--- /dev/null
+++ b/sys/dev/usb/serial/u3g.c
@@ -0,0 +1,583 @@
+/*
+ * 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.
+ *
+ * NOTE:
+ *
+ * - The device ID's are stored in "core/usb2_msctest.c"
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR u3g_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_msctest.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int u3g_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB u3g");
+SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW,
+ &u3g_debug, 0, "u3g debug level");
+#endif
+
+#define U3G_MAXPORTS 4
+#define U3G_CONFIG_INDEX 0
+#define U3G_BSIZE 2048
+
+#define U3GSP_GPRS 0
+#define U3GSP_EDGE 1
+#define U3GSP_CDMA 2
+#define U3GSP_UMTS 3
+#define U3GSP_HSDPA 4
+#define U3GSP_HSUPA 5
+#define U3GSP_HSPA 6
+#define U3GSP_MAX 7
+
+#define U3GFL_NONE 0x00 /* No flags */
+#define U3GFL_HUAWEI_INIT 0x01 /* Init command required */
+#define U3GFL_SCSI_EJECT 0x02 /* SCSI eject command required */
+#define U3GFL_SIERRA_INIT 0x04 /* Init command required */
+
+struct u3g_speeds_s {
+ uint32_t ispeed;
+ uint32_t ospeed;
+};
+
+enum {
+ U3G_BULK_WR,
+ U3G_BULK_RD,
+ U3G_N_TRANSFER,
+};
+
+struct u3g_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom[U3G_MAXPORTS];
+
+ struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* U3G status register */
+ uint8_t sc_numports;
+};
+
+static device_probe_t u3g_probe;
+static device_attach_t u3g_attach;
+static device_detach_t u3g_detach;
+
+static usb2_callback_t u3g_write_callback;
+static usb2_callback_t u3g_read_callback;
+
+static void u3g_start_read(struct usb2_com_softc *ucom);
+static void u3g_stop_read(struct usb2_com_softc *ucom);
+static void u3g_start_write(struct usb2_com_softc *ucom);
+static void u3g_stop_write(struct usb2_com_softc *ucom);
+
+static int u3g_driver_loaded(struct module *mod, int what, void *arg);
+
+static const struct usb2_config u3g_config[U3G_N_TRANSFER] = {
+
+ [U3G_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = U3G_BSIZE,/* bytes */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &u3g_write_callback,
+ },
+
+ [U3G_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = U3G_BSIZE,/* bytes */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &u3g_read_callback,
+ },
+};
+
+static const struct usb2_com_callback u3g_callback = {
+ .usb2_com_start_read = &u3g_start_read,
+ .usb2_com_stop_read = &u3g_stop_read,
+ .usb2_com_start_write = &u3g_start_write,
+ .usb2_com_stop_write = &u3g_stop_write,
+};
+
+#if 0
+static const struct u3g_speeds_s u3g_speeds[U3GSP_MAX] = {
+ [U3GSP_GPRS] = {64000, 64000},
+ [U3GSP_EDGE] = {384000, 64000},
+ [U3GSP_CDMA] = {384000, 64000},
+ [U3GSP_UMTS] = {384000, 64000},
+ [U3GSP_HSDPA] = {1200000, 384000},
+ [U3GSP_HSUPA] = {1200000, 384000},
+ [U3GSP_HSPA] = {7200000, 384000},
+};
+#endif
+
+static device_method_t u3g_methods[] = {
+ DEVMETHOD(device_probe, u3g_probe),
+ DEVMETHOD(device_attach, u3g_attach),
+ DEVMETHOD(device_detach, u3g_detach),
+ {0, 0}
+};
+
+static devclass_t u3g_devclass;
+
+static driver_t u3g_driver = {
+ .name = "u3g",
+ .methods = u3g_methods,
+ .size = sizeof(struct u3g_softc),
+};
+
+DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0);
+MODULE_DEPEND(u3g, ucom, 1, 1, 1);
+MODULE_DEPEND(u3g, usb, 1, 1, 1);
+
+/* Huawei specific defines */
+
+#define U3GINFO(flag,speed) ((flag)|((speed) * 256))
+#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256)
+
+/*
+ * NOTE: The entries marked with XXX should be checked for the correct
+ * speed indication to set the buffer sizes.
+ */
+static const struct usb2_device_id u3g_devs[] = {
+ /* OEM: Option */
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ /* OEM: Qualcomm, Inc. */
+ {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
+ /* OEM: Huawei */
+ {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))},
+ {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))},
+ /* OEM: Novatel */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ /* OEM: Merlin */
+ {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ /* OEM: Sierra Wireless: */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSPA, U3GFL_NONE))}, /* XXX */
+ /* Sierra TruInstaller device ID */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))},
+};
+
+static void
+u3g_sierra_init(struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+
+ DPRINTFN(0, "\n");
+
+ 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 (usb2_do_request_flags(udev, NULL, &req,
+ NULL, 0, NULL, USB_MS_HZ)) {
+ /* ignore any errors */
+ }
+ return;
+}
+
+static void
+u3g_huawei_init(struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+
+ DPRINTFN(0, "\n");
+
+ 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 (usb2_do_request_flags(udev, NULL, &req,
+ NULL, 0, NULL, USB_MS_HZ)) {
+ /* ignore any errors */
+ }
+ return;
+}
+
+static int
+u3g_lookup_huawei(struct usb2_attach_arg *uaa)
+{
+ /* Calling the lookup function will also set the driver info! */
+ return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
+}
+
+/*
+ * 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 usb2_error_t
+u3g_test_huawei_autoinst(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ uint32_t flags;
+
+ if (udev == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ iface = usb2_get_iface(udev, 0);
+ if (iface == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ id = iface->idesc;
+ if (id == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ if (id->bInterfaceClass != UICLASS_MASS) {
+ return (USB_ERR_INVAL);
+ }
+ if (u3g_lookup_huawei(uaa)) {
+ /* no device match */
+ return (USB_ERR_INVAL);
+ }
+ flags = USB_GET_DRIVER_INFO(uaa);
+
+ if (flags & U3GFL_HUAWEI_INIT) {
+ u3g_huawei_init(udev);
+ } else if (flags & U3GFL_SCSI_EJECT) {
+ return (usb2_test_autoinstall(udev, 0, 1));
+ } else if (flags & U3GFL_SIERRA_INIT) {
+ u3g_sierra_init(udev);
+ } else {
+ /* no quirks */
+ return (USB_ERR_INVAL);
+ }
+ return (0); /* success */
+}
+
+static int
+u3g_driver_loaded(struct module *mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ /* register our autoinstall handler */
+ usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst;
+ break;
+ case MOD_UNLOAD:
+ usb2_test_huawei_unload(NULL);
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+static int
+u3g_probe(device_t self)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bInterfaceClass != UICLASS_VENDOR) {
+ return (ENXIO);
+ }
+ return (u3g_lookup_huawei(uaa));
+}
+
+static int
+u3g_attach(device_t dev)
+{
+ struct usb2_config u3g_config_tmp[U3G_N_TRANSFER];
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct u3g_softc *sc = device_get_softc(dev);
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ uint8_t m;
+ uint8_t n;
+ uint8_t i;
+ uint8_t x;
+ int error;
+
+ DPRINTF("sc=%p\n", sc);
+
+ /* copy in USB config */
+ for (n = 0; n != U3G_N_TRANSFER; n++)
+ u3g_config_tmp[n] = u3g_config[n];
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ x = 0; /* interface index */
+ i = 0; /* endpoint index */
+ m = 0; /* number of ports */
+
+ while (m != U3G_MAXPORTS) {
+
+ /* update BULK endpoint index */
+ for (n = 0; n != U3G_N_TRANSFER; n++)
+ u3g_config_tmp[n].ep_index = i;
+
+ iface = usb2_get_iface(uaa->device, x);
+ if (iface == NULL) {
+ if (m != 0)
+ break; /* end of interfaces */
+ DPRINTF("did not find any modem endpoints\n");
+ goto detach;
+ }
+
+ id = usb2_get_interface_descriptor(iface);
+ if ((id == NULL) ||
+ (id->bInterfaceClass != UICLASS_VENDOR)) {
+ /* next interface */
+ x++;
+ i = 0;
+ continue;
+ }
+
+ /* try to allocate a set of BULK endpoints */
+ error = usb2_transfer_setup(uaa->device, &x,
+ sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER,
+ &sc->sc_ucom[m], &Giant);
+ if (error) {
+ /* next interface */
+ x++;
+ i = 0;
+ continue;
+ }
+
+ /* grab other interface, if any */
+ if (x != uaa->info.bIfaceIndex)
+ usb2_set_parent_iface(uaa->device, x,
+ uaa->info.bIfaceIndex);
+
+ /* set stall by default */
+ usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]);
+
+ m++; /* found one port */
+ i++; /* next endpoint index */
+ }
+
+ sc->sc_numports = m;
+
+ error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom,
+ sc->sc_numports, sc, &u3g_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ if (sc->sc_numports != 1) {
+ /* be verbose */
+ device_printf(dev, "Found %u ports.\n",
+ (unsigned int)sc->sc_numports);
+ }
+ 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 m;
+
+ DPRINTF("sc=%p\n", sc);
+
+ /* NOTE: It is not dangerous to detach more ports than attached! */
+ usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS);
+
+ for (m = 0; m != U3G_MAXPORTS; m++)
+ usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+u3g_start_read(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
+ return;
+}
+
+static void
+u3g_stop_read(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
+ return;
+}
+
+static void
+u3g_start_write(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
+ return;
+}
+
+static void
+u3g_stop_write(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
+ return;
+}
+
+static void
+u3g_write_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_com_softc *ucom = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_com_get_data(ucom, xfer->frbuffers, 0,
+ U3G_BSIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* do a builtin clear-stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+ return;
+}
+
+static void
+u3g_read_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_com_softc *ucom = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* do a builtin clear-stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+ return;
+}
diff --git a/sys/dev/usb/serial/uark.c b/sys/dev/usb/serial/uark.c
new file mode 100644
index 0000000..ce76854
--- /dev/null
+++ b/sys/dev/usb/serial/uark.c
@@ -0,0 +1,407 @@
+/* $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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UARK_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_callback_t uark_bulk_write_callback;
+static usb2_callback_t uark_bulk_read_callback;
+
+static void uark_start_read(struct usb2_com_softc *);
+static void uark_stop_read(struct usb2_com_softc *);
+static void uark_start_write(struct usb2_com_softc *);
+static void uark_stop_write(struct usb2_com_softc *);
+static int uark_pre_param(struct usb2_com_softc *, struct termios *);
+static void uark_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uark_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uark_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void uark_cfg_write(struct uark_softc *, uint16_t, uint16_t);
+
+static const struct usb2_config
+ uark_xfer_config[UARK_N_TRANSFER] = {
+
+ [UARK_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UARK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uark_bulk_write_callback,
+ },
+
+ [UARK_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UARK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uark_bulk_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uark_callback = {
+ .usb2_com_cfg_get_status = &uark_cfg_get_status,
+ .usb2_com_cfg_set_break = &uark_cfg_set_break,
+ .usb2_com_cfg_param = &uark_cfg_param,
+ .usb2_com_pre_param = &uark_pre_param,
+ .usb2_com_start_read = &uark_start_read,
+ .usb2_com_stop_read = &uark_stop_read,
+ .usb2_com_start_write = &uark_start_write,
+ .usb2_com_stop_write = &uark_stop_write,
+};
+
+static device_method_t uark_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_probe, uark_probe),
+ DEVMETHOD(device_attach, uark_attach),
+ DEVMETHOD(device_detach, uark_detach),
+ {0, 0}
+};
+
+static devclass_t uark_devclass;
+
+static driver_t uark_driver = {
+ .name = "uark",
+ .methods = uark_methods,
+ .size = sizeof(struct uark_softc),
+};
+
+DRIVER_MODULE(uark, ushub, uark_driver, uark_devclass, NULL, 0);
+MODULE_DEPEND(uark, ucom, 1, 1, 1);
+MODULE_DEPEND(uark, usb, 1, 1, 1);
+
+static const struct usb2_device_id uark_devs[] = {
+ {USB_VPI(USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116, 0)},
+};
+
+static int
+uark_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != 0) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UARK_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uark_devs, sizeof(uark_devs), uaa));
+}
+
+static int
+uark_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uark_softc *sc = device_get_softc(dev);
+ int32_t error;
+ uint8_t iface_index;
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ iface_index = UARK_IFACE_INDEX;
+ error = usb2_transfer_setup
+ (uaa->device, &iface_index, sc->sc_xfer,
+ uark_xfer_config, UARK_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating control USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uark_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UARK_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uark_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct uark_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UARK_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+uark_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct uark_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uark_start_read(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_RD]);
+}
+
+static void
+uark_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_RD]);
+}
+
+static void
+uark_start_write(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_WR]);
+}
+
+static void
+uark_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_WR]);
+}
+
+static int
+uark_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ if ((t->c_ospeed < 300) || (t->c_ospeed > 115200))
+ return (EINVAL);
+ return (0);
+}
+
+static void
+uark_cfg_param(struct usb2_com_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 usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uark_cfg_set_break(struct usb2_com_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 usb2_device_request req;
+ usb2_error_t err;
+
+ req.bmRequestType = UARK_WRITE;
+ req.bRequest = UARK_REQUEST;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 0);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
diff --git a/sys/dev/usb/serial/ubsa.c b/sys/dev/usb/serial/ubsa.c
new file mode 100644
index 0000000..718df6d
--- /dev/null
+++ b/sys/dev/usb/serial/ubsa.c
@@ -0,0 +1,634 @@
+/*-
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR ubsa_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int ubsa_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa");
+SYSCTL_INT(_hw_usb2_ubsa, OID_AUTO, debug, CTLFLAG_RW,
+ &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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UBSA_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_callback_t ubsa_write_callback;
+static usb2_callback_t ubsa_read_callback;
+static usb2_callback_t ubsa_intr_callback;
+
+static void ubsa_cfg_request(struct ubsa_softc *, uint8_t, uint16_t);
+static void ubsa_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void ubsa_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void ubsa_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int ubsa_pre_param(struct usb2_com_softc *, struct termios *);
+static void ubsa_cfg_param(struct usb2_com_softc *, struct termios *);
+static void ubsa_start_read(struct usb2_com_softc *);
+static void ubsa_stop_read(struct usb2_com_softc *);
+static void ubsa_start_write(struct usb2_com_softc *);
+static void ubsa_stop_write(struct usb2_com_softc *);
+static void ubsa_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+
+static const struct usb2_config ubsa_config[UBSA_N_TRANSFER] = {
+
+ [UBSA_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UBSA_BSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ubsa_write_callback,
+ },
+
+ [UBSA_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UBSA_BSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ubsa_read_callback,
+ },
+
+ [UBSA_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &ubsa_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback ubsa_callback = {
+ .usb2_com_cfg_get_status = &ubsa_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &ubsa_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &ubsa_cfg_set_rts,
+ .usb2_com_cfg_set_break = &ubsa_cfg_set_break,
+ .usb2_com_cfg_param = &ubsa_cfg_param,
+ .usb2_com_pre_param = &ubsa_pre_param,
+ .usb2_com_start_read = &ubsa_start_read,
+ .usb2_com_stop_read = &ubsa_stop_read,
+ .usb2_com_start_write = &ubsa_start_write,
+ .usb2_com_stop_write = &ubsa_stop_write,
+};
+
+static const struct usb2_device_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),
+ {0, 0}
+};
+
+static devclass_t ubsa_devclass;
+
+static driver_t ubsa_driver = {
+ .name = "ubsa",
+ .methods = ubsa_methods,
+ .size = sizeof(struct ubsa_softc),
+};
+
+DRIVER_MODULE(ubsa, ushub, ubsa_driver, ubsa_devclass, NULL, 0);
+MODULE_DEPEND(ubsa, ucom, 1, 1, 1);
+MODULE_DEPEND(ubsa, usb, 1, 1, 1);
+
+static int
+ubsa_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_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 (usb2_lookup_id_by_uaa(ubsa_devs, sizeof(ubsa_devs), uaa));
+}
+
+static int
+ubsa_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ubsa_softc *sc = device_get_softc(dev);
+ int error;
+
+ DPRINTF("sc=%p\n", sc);
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = UBSA_IFACE_INDEX;
+
+ error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, ubsa_config, UBSA_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ DPRINTF("could not allocate all pipes\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &ubsa_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UBSA_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+ubsa_cfg_request(struct ubsa_softc *sc, uint8_t index, uint16_t value)
+{
+ struct usb2_device_request req;
+ usb2_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 = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
+
+static void
+ubsa_cfg_set_dtr(struct usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_softc *ucom, struct termios *t)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ DPRINTF("sc = %p\n", sc);
+
+ 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 usb2_com_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 usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UBSA_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_RD]);
+}
+
+static void
+ubsa_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UBSA_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_RD]);
+}
+
+static void
+ubsa_start_write(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_WR]);
+}
+
+static void
+ubsa_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_WR]);
+}
+
+static void
+ubsa_cfg_get_status(struct usb2_com_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 usb2_xfer *xfer)
+{
+ struct ubsa_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UBSA_BSIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubsa_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubsa_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubsa_intr_callback(struct usb2_xfer *xfer)
+{
+ struct ubsa_softc *sc = xfer->priv_sc;
+ uint8_t buf[4];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen >= sizeof(buf)) {
+
+ usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
+
+ /*
+ * incidentally, Belkin adapter status bits match
+ * UART 16550 bits
+ */
+ sc->sc_lsr = buf[2];
+ sc->sc_msr = buf[3];
+
+ DPRINTF("lsr = 0x%02x, msr = 0x%02x\n",
+ sc->sc_lsr, sc->sc_msr);
+
+ usb2_com_status_change(&sc->sc_ucom);
+ } else {
+ DPRINTF("ignoring short packet, %d bytes\n",
+ xfer->actlen);
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
diff --git a/sys/dev/usb/serial/ubser.c b/sys/dev/usb/serial/ubser.c
new file mode 100644
index 0000000..1de4f82
--- /dev/null
+++ b/sys/dev/usb/serial/ubser.c
@@ -0,0 +1,518 @@
+/*-
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR ubser_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.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
+
+#if USB_DEBUG
+static int ubser_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
+SYSCTL_INT(_hw_usb2_ubser, OID_AUTO, debug, CTLFLAG_RW,
+ &ubser_debug, 0, "ubser debug level");
+#endif
+
+enum {
+ UBSER_BULK_DT_WR,
+ UBSER_BULK_DT_RD,
+ UBSER_N_TRANSFER,
+};
+
+struct ubser_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom[UBSER_UNIT_MAX];
+
+ struct usb2_xfer *sc_xfer[UBSER_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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;
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t ubser_probe;
+static device_attach_t ubser_attach;
+static device_detach_t ubser_detach;
+
+static usb2_callback_t ubser_write_callback;
+static usb2_callback_t ubser_read_callback;
+
+static int ubser_pre_param(struct usb2_com_softc *, struct termios *);
+static void ubser_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void ubser_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void ubser_start_read(struct usb2_com_softc *);
+static void ubser_stop_read(struct usb2_com_softc *);
+static void ubser_start_write(struct usb2_com_softc *);
+static void ubser_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config ubser_config[UBSER_N_TRANSFER] = {
+
+ [UBSER_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ubser_write_callback,
+ },
+
+ [UBSER_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ubser_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ubser_callback = {
+ .usb2_com_cfg_set_break = &ubser_cfg_set_break,
+ .usb2_com_cfg_get_status = &ubser_cfg_get_status,
+ .usb2_com_pre_param = &ubser_pre_param,
+ .usb2_com_start_read = &ubser_start_read,
+ .usb2_com_stop_read = &ubser_stop_read,
+ .usb2_com_start_write = &ubser_start_write,
+ .usb2_com_stop_write = &ubser_stop_write,
+};
+
+static device_method_t ubser_methods[] = {
+ DEVMETHOD(device_probe, ubser_probe),
+ DEVMETHOD(device_attach, ubser_attach),
+ DEVMETHOD(device_detach, ubser_detach),
+ {0, 0}
+};
+
+static devclass_t ubser_devclass;
+
+static driver_t ubser_driver = {
+ .name = "ubser",
+ .methods = ubser_methods,
+ .size = sizeof(struct ubser_softc),
+};
+
+DRIVER_MODULE(ubser, ushub, ubser_driver, ubser_devclass, NULL, 0);
+MODULE_DEPEND(ubser, ucom, 1, 1, 1);
+MODULE_DEPEND(ubser, usb, 1, 1, 1);
+
+static int
+ubser_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ /* check if this is a BWCT vendor specific ubser interface */
+ if ((strcmp(uaa->device->manufacturer, "BWCT") == 0) &&
+ (uaa->info.bInterfaceClass == 0xff) &&
+ (uaa->info.bInterfaceSubClass == 0x00))
+ return (0);
+
+ return (ENXIO);
+}
+
+static int
+ubser_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ubser_softc *sc = device_get_softc(dev);
+ struct usb2_device_request req;
+ uint8_t n;
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
+ device_get_nameunit(dev));
+
+ 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 = usb2_do_request_flags
+ (uaa->device, &Giant, &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",
+ usb2_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 = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &Giant);
+ if (error) {
+ goto detach;
+ }
+ sc->sc_tx_size = sc->sc_xfer[UBSER_BULK_DT_WR]->max_data_length;
+
+ 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 = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom,
+ sc->sc_numser, sc, &ubser_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ mtx_lock(&Giant);
+
+ usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]);
+
+ usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
+
+ mtx_unlock(&Giant);
+
+ 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");
+
+ usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numser);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER);
+
+ return (0);
+}
+
+static int
+ubser_pre_param(struct usb2_com_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 usb2_xfer *xfer)
+{
+ struct ubser_softc *sc = xfer->priv_sc;
+ 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:
+ do {
+ if (usb2_com_get_data(sc->sc_ucom + sc->sc_curr_tx_unit,
+ xfer->frbuffers, 1, sc->sc_tx_size - 1,
+ &actlen)) {
+
+ buf[0] = sc->sc_curr_tx_unit;
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 1);
+
+ xfer->frlengths[0] = actlen + 1;
+ usb2_start_hardware(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 (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubser_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubser_softc *sc = xfer->priv_sc;
+ uint8_t buf[1];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 1) {
+ DPRINTF("invalid actlen=0!\n");
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 1);
+
+ if (buf[0] >= sc->sc_numser) {
+ DPRINTF("invalid serial number!\n");
+ goto tr_setup;
+ }
+ usb2_com_put_data(sc->sc_ucom + buf[0],
+ xfer->frbuffers, 1, xfer->actlen - 1);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubser_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+ uint8_t x = ucom->sc_portno;
+ struct usb2_device_request req;
+ usb2_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 = usb2_com_cfg_do_request(sc->sc_udev, ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "send break failed, error=%s\n",
+ usb2_errstr(err));
+ }
+ }
+}
+
+static void
+ubser_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ /* fake status bits */
+ *lsr = 0;
+ *msr = SER_DCD;
+}
+
+static void
+ubser_start_read(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
+}
+
+static void
+ubser_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]);
+}
+
+static void
+ubser_start_write(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]);
+}
+
+static void
+ubser_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/serial/uchcom.c b/sys/dev/usb/serial/uchcom.c
new file mode 100644
index 0000000..c8238c4
--- /dev/null
+++ b/sys/dev/usb/serial/uchcom.c
@@ -0,0 +1,883 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uchcom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uchcom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom");
+SYSCTL_INT(_hw_usb2_uchcom, OID_AUTO, debug, CTLFLAG_RW,
+ &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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UCHCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 (sizeof (dividers) / sizeof (dividers[0]))
+
+static const struct usb2_device_id uchcom_devs[] = {
+ {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
+};
+
+/* protypes */
+
+static int uchcom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uchcom_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uchcom_start_read(struct usb2_com_softc *);
+static void uchcom_start_write(struct usb2_com_softc *);
+static void uchcom_stop_read(struct usb2_com_softc *);
+static void uchcom_stop_write(struct usb2_com_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_dtrrts(struct uchcom_softc *);
+static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
+static void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t);
+static void uchcom_set_line_control(struct uchcom_softc *, tcflag_t);
+static void uchcom_clear_chip(struct uchcom_softc *);
+static void uchcom_reset_chip(struct uchcom_softc *);
+
+static device_probe_t uchcom_probe;
+static device_attach_t uchcom_attach;
+static device_detach_t uchcom_detach;
+
+static usb2_callback_t uchcom_intr_callback;
+static usb2_callback_t uchcom_write_callback;
+static usb2_callback_t uchcom_read_callback;
+
+static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
+
+ [UCHCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UCHCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uchcom_write_callback,
+ },
+
+ [UCHCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UCHCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uchcom_read_callback,
+ },
+
+ [UCHCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uchcom_intr_callback,
+ },
+};
+
+struct usb2_com_callback uchcom_callback = {
+ .usb2_com_cfg_get_status = &uchcom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uchcom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uchcom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uchcom_cfg_set_break,
+ .usb2_com_cfg_param = &uchcom_cfg_param,
+ .usb2_com_pre_param = &uchcom_pre_param,
+ .usb2_com_start_read = &uchcom_start_read,
+ .usb2_com_stop_read = &uchcom_stop_read,
+ .usb2_com_start_write = &uchcom_start_write,
+ .usb2_com_stop_write = &uchcom_stop_write,
+};
+
+/* ----------------------------------------------------------------------
+ * driver entry points
+ */
+
+static int
+uchcom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_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 (usb2_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 usb2_attach_arg *uaa = device_get_ivars(dev);
+ int error;
+ uint8_t iface_index;
+
+ DPRINTFN(11, "\n");
+
+ device_set_usb2_desc(dev);
+
+ 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 = usb2_transfer_setup(uaa->device,
+ &iface_index, sc->sc_xfer, uchcom_config_data,
+ UCHCOM_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ DPRINTF("one or more missing USB endpoints, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ /*
+ * Do the initialization during attach so that the system does not
+ * sleep during open:
+ */
+ uchcom_update_version(sc);
+ uchcom_clear_chip(sc);
+ uchcom_reset_chip(sc);
+ uchcom_update_status(sc);
+
+ sc->sc_dtr = 1;
+ sc->sc_rts = 1;
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uchcom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ 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");
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER);
+
+ return (0);
+}
+
+/* ----------------------------------------------------------------------
+ * low level i/o
+ */
+
+static void
+uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno,
+ uint16_t value, uint16_t index)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 0);
+
+ usb2_com_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 usb2_device_request req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, buflen);
+
+ usb2_com_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_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
+{
+ uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
+}
+
+static void
+uchcom_set_dtrrts_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_dtrrts(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_dtrrts_10(sc, ~val);
+ else
+ uchcom_set_dtrrts_20(sc, ~val);
+}
+
+static void
+uchcom_cfg_set_break(struct usb2_com_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 / 0x100;
+
+ return (0);
+}
+
+static void
+uchcom_set_dte_rate(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);
+}
+
+static void
+uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
+{
+ uint8_t lcr1 = 0;
+ uint8_t lcr2 = 0;
+
+ uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
+
+ lcr1 &= ~UCHCOM_LCR1_MASK;
+ lcr2 &= ~UCHCOM_LCR2_MASK;
+
+ /*
+ * XXX: it is difficult to handle the line control appropriately:
+ * - CS8, !CSTOPB and any parity mode seems ok, but
+ * - the chip doesn't have the function to calculate parity
+ * in !CS8 mode.
+ * - it is unclear that the chip supports CS5,6 mode.
+ * - it is unclear how to handle stop bits.
+ */
+
+ if (cflag & PARENB) {
+ lcr1 |= UCHCOM_LCR1_PARENB;
+ if (cflag & PARODD)
+ lcr2 |= UCHCOM_LCR2_PARODD;
+ else
+ lcr2 |= UCHCOM_LCR2_PAREVEN;
+ }
+ uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2);
+}
+
+static void
+uchcom_clear_chip(struct uchcom_softc *sc)
+{
+ DPRINTF("\n");
+ uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
+}
+
+static void
+uchcom_reset_chip(struct uchcom_softc *sc)
+{
+ uint16_t val;
+ uint16_t idx;
+ uint8_t lcr1;
+ uint8_t lcr2;
+ uint8_t pre;
+ uint8_t div;
+ uint8_t mod;
+
+ uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
+ uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div);
+ uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL);
+
+ val = 0;
+ idx = 0;
+ val |= (uint16_t)(lcr1 & 0xF0) << 8;
+ val |= 0x01;
+ val |= (uint16_t)(lcr2 & 0x0F) << 8;
+ val |= 0x02;
+ idx |= pre & 0x07;
+ val |= 0x04;
+ idx |= (uint16_t)div << 8;
+ val |= 0x08;
+ idx |= mod & 0xF8;
+ val |= 0x10;
+
+ DPRINTF("reset v=0x%04X, i=0x%04X\n", val, idx);
+
+ uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, val, idx);
+}
+
+/* ----------------------------------------------------------------------
+ * methods for ucom
+ */
+static void
+uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ sc->sc_dtr = onoff;
+ uchcom_set_dtrrts(sc);
+}
+
+static void
+uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ sc->sc_rts = onoff;
+ uchcom_set_dtrrts(sc);
+}
+
+static int
+uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uchcom_divider dv;
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ case CS6:
+ case CS7:
+ return (EIO);
+ default:
+ break;
+ }
+
+ if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
+ return (EIO);
+ }
+ return (0); /* success */
+}
+
+static void
+uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ uchcom_set_line_control(sc, t->c_cflag);
+ uchcom_set_dte_rate(sc, t->c_ospeed);
+}
+
+static void
+uchcom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+}
+
+static void
+uchcom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+}
+
+static void
+uchcom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+}
+
+static void
+uchcom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+}
+
+/* ----------------------------------------------------------------------
+ * callback when the modem status is changed.
+ */
+static void
+uchcom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uchcom_softc *sc = xfer->priv_sc;
+ uint8_t buf[UCHCOM_INTR_LEAST];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen = %u\n", xfer->actlen);
+
+ if (xfer->actlen >= UCHCOM_INTR_LEAST) {
+ usb2_copy_out(xfer->frbuffers, 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]);
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+uchcom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uchcom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UCHCOM_BULK_BUF_SIZE, &actlen)) {
+
+ DPRINTF("actlen = %d\n", actlen);
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+uchcom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uchcom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static device_method_t uchcom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uchcom_probe),
+ DEVMETHOD(device_attach, uchcom_attach),
+ DEVMETHOD(device_detach, uchcom_detach),
+
+ {0, 0}
+};
+
+static driver_t uchcom_driver = {
+ "ucom",
+ uchcom_methods,
+ sizeof(struct uchcom_softc)
+};
+
+static devclass_t uchcom_devclass;
+
+DRIVER_MODULE(uchcom, ushub, uchcom_driver, uchcom_devclass, NULL, 0);
+MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uchcom, usb, 1, 1, 1);
diff --git a/sys/dev/usb/serial/ucycom.c b/sys/dev/usb/serial/ucycom.c
new file mode 100644
index 0000000..cabb7fb
--- /dev/null
+++ b/sys/dev/usb/serial/ucycom.c
@@ -0,0 +1,564 @@
+#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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_hid.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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[UCYCOM_N_TRANSFER];
+
+ 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_name[16];
+ 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 usb2_callback_t ucycom_ctrl_write_callback;
+static usb2_callback_t ucycom_intr_read_callback;
+
+static void ucycom_cfg_open(struct usb2_com_softc *);
+static void ucycom_start_read(struct usb2_com_softc *);
+static void ucycom_stop_read(struct usb2_com_softc *);
+static void ucycom_start_write(struct usb2_com_softc *);
+static void ucycom_stop_write(struct usb2_com_softc *);
+static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t);
+static int ucycom_pre_param(struct usb2_com_softc *, struct termios *);
+static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *);
+
+static const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = {
+
+ [UCYCOM_CTRL_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN),
+ .mh.flags = {},
+ .mh.callback = &ucycom_ctrl_write_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+
+ [UCYCOM_INTR_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = UCYCOM_MAX_IOLEN,
+ .mh.callback = &ucycom_intr_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ucycom_callback = {
+ .usb2_com_cfg_param = &ucycom_cfg_param,
+ .usb2_com_cfg_open = &ucycom_cfg_open,
+ .usb2_com_pre_param = &ucycom_pre_param,
+ .usb2_com_start_read = &ucycom_start_read,
+ .usb2_com_stop_read = &ucycom_stop_read,
+ .usb2_com_start_write = &ucycom_start_write,
+ .usb2_com_stop_write = &ucycom_stop_write,
+};
+
+static device_method_t ucycom_methods[] = {
+ DEVMETHOD(device_probe, ucycom_probe),
+ DEVMETHOD(device_attach, ucycom_attach),
+ DEVMETHOD(device_detach, ucycom_detach),
+ {0, 0}
+};
+
+static devclass_t ucycom_devclass;
+
+static driver_t ucycom_driver = {
+ .name = "ucycom",
+ .methods = ucycom_methods,
+ .size = sizeof(struct ucycom_softc),
+};
+
+DRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0);
+MODULE_DEPEND(ucycom, ucom, 1, 1, 1);
+MODULE_DEPEND(ucycom, usb, 1, 1, 1);
+
+/*
+ * Supported devices
+ */
+static const struct usb2_device_id ucycom_devs[] = {
+ {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)},
+};
+
+#define UCYCOM_DEFAULT_RATE 4800
+#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */
+
+static int
+ucycom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != 0) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa));
+}
+
+static int
+ucycom_attach(device_t dev)
+{
+ struct usb2_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_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ 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 = usb2_req_get_hid_desc
+ (uaa->device, &Giant,
+ &urd_ptr, &urd_len, M_USBDEV,
+ UCYCOM_IFACE_INDEX);
+
+ if (error) {
+ device_printf(dev, "failed to get report "
+ "descriptor: %s\n",
+ usb2_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 = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER,
+ sc, &Giant);
+ if (error) {
+ device_printf(dev, "allocating USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &ucycom_callback, &Giant);
+
+ if (error) {
+ goto detach;
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+ucycom_cfg_open(struct usb2_com_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 usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]);
+}
+
+static void
+ucycom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]);
+}
+
+static void
+ucycom_start_write(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]);
+}
+
+static void
+ucycom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]);
+}
+
+static void
+ucycom_ctrl_write_callback(struct usb2_xfer *xfer)
+{
+ struct ucycom_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint8_t data[2];
+ uint8_t offset;
+ uint32_t actlen;
+
+ 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 (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, 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;
+ }
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_copy_in(xfer->frbuffers + 1, 0, data, offset);
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = sc->sc_olen;
+ xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ }
+ DPRINTF("error=%s\n",
+ usb2_errstr(xfer->error));
+ goto tr_transferred;
+ }
+}
+
+static void
+ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
+{
+ struct usb2_device_request req;
+ uint16_t len;
+ usb2_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 = usb2_com_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", usb2_errstr(err));
+ }
+}
+
+static int
+ucycom_pre_param(struct usb2_com_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 usb2_com_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 usb2_xfer *xfer)
+{
+ struct ucycom_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+ uint32_t offset;
+ uint32_t len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ switch (sc->sc_model) {
+ case MODEL_CY7C63743:
+ if (xfer->actlen < 1) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 1);
+
+ sc->sc_ist = buf[0] & ~0x07;
+ len = buf[0] & 0x07;
+
+ (xfer->actlen)--;
+
+ offset = 1;
+
+ break;
+
+ case MODEL_CY7C64013:
+ if (xfer->actlen < 2) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+
+ sc->sc_ist = buf[0] & ~0x07;
+ len = buf[1];
+
+ (xfer->actlen) -= 2;
+
+ offset = 2;
+
+ break;
+
+ default:
+ DPRINTFN(0, "unsupported model number!\n");
+ goto tr_setup;
+ }
+
+ if (len > xfer->actlen) {
+ len = xfer->actlen;
+ }
+ if (len) {
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
+ offset, len);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = sc->sc_ilen;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
diff --git a/sys/dev/usb/serial/ufoma.c b/sys/dev/usb/serial/ufoma.c
new file mode 100644
index 0000000..1077676
--- /dev/null
+++ b/sys/dev/usb/serial/ufoma.c
@@ -0,0 +1,1212 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+#include <sys/sysctl.h>
+#include <sys/sbuf.h>
+
+typedef struct ufoma_mobile_acm_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bType;
+ uint8_t bMode[1];
+} __packed usb2_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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+ struct cv sc_cv;
+
+ struct usb2_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX];
+ struct usb2_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX];
+ uint8_t *sc_modetable;
+ device_t sc_dev;
+ struct usb2_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;
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t ufoma_probe;
+static device_attach_t ufoma_attach;
+static device_detach_t ufoma_detach;
+
+static usb2_callback_t ufoma_ctrl_read_callback;
+static usb2_callback_t ufoma_ctrl_write_callback;
+static usb2_callback_t ufoma_intr_callback;
+static usb2_callback_t ufoma_bulk_write_callback;
+static usb2_callback_t ufoma_bulk_read_callback;
+
+static void *ufoma_get_intconf(struct usb2_config_descriptor *,
+ struct usb2_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_cfg_open(struct usb2_com_softc *);
+static void ufoma_cfg_close(struct usb2_com_softc *);
+static void ufoma_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void ufoma_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void ufoma_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void ufoma_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static int ufoma_pre_param(struct usb2_com_softc *, struct termios *);
+static void ufoma_cfg_param(struct usb2_com_softc *, struct termios *);
+static int ufoma_modem_setup(device_t, struct ufoma_softc *,
+ struct usb2_attach_arg *);
+static void ufoma_start_read(struct usb2_com_softc *);
+static void ufoma_stop_read(struct usb2_com_softc *);
+static void ufoma_start_write(struct usb2_com_softc *);
+static void ufoma_stop_write(struct usb2_com_softc *);
+
+/*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 usb2_config
+ ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = {
+
+ [UFOMA_CTRL_ENDPT_INTR] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = sizeof(struct usb2_cdc_notification),
+ .mh.callback = &ufoma_intr_callback,
+ },
+
+ [UFOMA_CTRL_ENDPT_READ] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + UFOMA_CMD_BUF_SIZE),
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &ufoma_ctrl_read_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+
+ [UFOMA_CTRL_ENDPT_WRITE] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + 1),
+ .mh.flags = {},
+ .mh.callback = &ufoma_ctrl_write_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static const struct usb2_config
+ ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = {
+
+ [UFOMA_BULK_ENDPT_WRITE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UFOMA_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ufoma_bulk_write_callback,
+ },
+
+ [UFOMA_BULK_ENDPT_READ] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UFOMA_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ufoma_bulk_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ufoma_callback = {
+ .usb2_com_cfg_get_status = &ufoma_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &ufoma_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &ufoma_cfg_set_rts,
+ .usb2_com_cfg_set_break = &ufoma_cfg_set_break,
+ .usb2_com_cfg_param = &ufoma_cfg_param,
+ .usb2_com_cfg_open = &ufoma_cfg_open,
+ .usb2_com_cfg_close = &ufoma_cfg_close,
+ .usb2_com_pre_param = &ufoma_pre_param,
+ .usb2_com_start_read = &ufoma_start_read,
+ .usb2_com_stop_read = &ufoma_stop_read,
+ .usb2_com_start_write = &ufoma_start_write,
+ .usb2_com_stop_write = &ufoma_stop_write,
+};
+
+static device_method_t ufoma_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_probe, ufoma_probe),
+ DEVMETHOD(device_attach, ufoma_attach),
+ DEVMETHOD(device_detach, ufoma_detach),
+ {0, 0}
+};
+
+static devclass_t ufoma_devclass;
+
+static driver_t ufoma_driver = {
+ .name = "ufoma",
+ .methods = ufoma_methods,
+ .size = sizeof(struct ufoma_softc),
+};
+
+DRIVER_MODULE(ufoma, ushub, ufoma_driver, ufoma_devclass, NULL, 0);
+MODULE_DEPEND(ufoma, ucom, 1, 1, 1);
+MODULE_DEPEND(ufoma, usb, 1, 1, 1);
+
+static int
+ufoma_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+ struct usb2_config_descriptor *cd;
+ usb2_mcpc_acm_descriptor *mad;
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ id = usb2_get_interface_descriptor(uaa->iface);
+ cd = usb2_get_config_descriptor(uaa->device);
+
+ if ((id == NULL) ||
+ (cd == NULL) ||
+ (id->bInterfaceClass != UICLASS_CDC) ||
+ (id->bInterfaceSubClass != UISUBCLASS_MCPC)) {
+ 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 (0);
+}
+
+static int
+ufoma_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ufoma_softc *sc = device_get_softc(dev);
+ struct usb2_config_descriptor *cd;
+ struct usb2_interface_descriptor *id;
+ struct sysctl_ctx_list *sctx;
+ struct sysctl_oid *soid;
+
+ usb2_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);
+
+ usb2_cv_init(&sc->sc_cv, "CWAIT");
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ DPRINTF("\n");
+
+ /* setup control transfers */
+
+ cd = usb2_get_config_descriptor(uaa->device);
+ id = usb2_get_interface_descriptor(uaa->iface);
+ sc->sc_ctrl_iface_no = id->bInterfaceNumber;
+ sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex;
+
+ error = usb2_transfer_setup(uaa->device,
+ &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer,
+ ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &Giant);
+
+ 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);
+ bcopy(mad->bMode, &sc->sc_modetable[1], elements);
+
+ sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED;
+ sc->sc_modetoactivate = mad->bMode[0];
+
+ /* clear stall at first run, if any */
+ usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+ usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &ufoma_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ /*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_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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);
+
+ usb2_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX);
+
+ if (sc->sc_modetable) {
+ free(sc->sc_modetable, M_USBDEV);
+ }
+ usb2_cv_destroy(&sc->sc_cv);
+
+ return (0);
+}
+
+static void *
+ufoma_get_intconf(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id,
+ uint8_t type, uint8_t subtype)
+{
+ struct usb2_descriptor *desc = (void *)id;
+
+ while ((desc = usb2_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 usb2_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]);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, sc->sc_modetable, 0, 1000);
+
+ error = usb2_cv_timedwait(&sc->sc_cv, &Giant, hz);
+
+ if (error) {
+ DPRINTF("NO response\n");
+ }
+}
+
+static void
+ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state)
+{
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ error = usb2_cv_timedwait(&sc->sc_cv, &Giant,
+ (UFOMA_MAX_TIMEOUT * hz));
+ if (error) {
+ DPRINTF("No response\n");
+ }
+}
+
+static void
+ufoma_ctrl_read_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ if (xfer->aframes != xfer->nframes) {
+ goto tr_setup;
+ }
+ if (xfer->frlengths[1] > 0) {
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers + 1,
+ 0, xfer->frlengths[1]);
+ }
+ 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);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = UFOMA_CMD_BUF_SIZE;
+ xfer->nframes = 2;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ } else {
+ goto tr_setup;
+ }
+
+ goto tr_transferred;
+ }
+}
+
+static void
+ufoma_ctrl_write_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1,
+ 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);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = 1;
+ xfer->nframes = 2;
+
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ } else {
+ goto tr_setup;
+ }
+
+ goto tr_transferred;
+ }
+}
+
+static void
+ufoma_intr_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ struct usb2_cdc_notification pkt;
+ uint16_t wLen;
+ uint16_t temp;
+ uint8_t mstatus;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 8) {
+ DPRINTF("too short message\n");
+ goto tr_setup;
+ }
+ if (xfer->actlen > sizeof(pkt)) {
+ DPRINTF("truncating message\n");
+ xfer->actlen = sizeof(pkt);
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
+
+ xfer->actlen -= 8;
+
+ wLen = UGETW(pkt.wLength);
+ if (xfer->actlen > wLen) {
+ xfer->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");
+ }
+ usb2_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++;
+ }
+ usb2_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 (xfer->actlen < 2) {
+ DPRINTF("invalid notification "
+ "length, %d bytes!\n", xfer->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;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+ break;
+
+ default:
+ break;
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ufoma_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UFOMA_BULK_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ufoma_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ufoma_cfg_open(struct usb2_com_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 usb2_com_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 usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+ufoma_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+ufoma_cfg_set_line_state(struct ufoma_softc *sc)
+{
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+ufoma_cfg_set_dtr(struct usb2_com_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 usb2_com_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 usb2_com_softc *ucom, struct termios *t)
+{
+ return (0); /* we accept anything */
+}
+
+static void
+ufoma_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ struct usb2_cdc_line_state ls;
+
+ if (sc->sc_nobulk ||
+ (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
+ return;
+ }
+ DPRINTF("\n");
+
+ bzero(&ls, 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);
+
+ usb2_com_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 usb2_attach_arg *uaa)
+{
+ struct usb2_config_descriptor *cd;
+ struct usb2_cdc_acm_descriptor *acm;
+ struct usb2_cdc_cm_descriptor *cmd;
+ struct usb2_interface_descriptor *id;
+ struct usb2_interface *iface;
+ uint8_t i;
+ int32_t error;
+
+ cd = usb2_get_config_descriptor(uaa->device);
+ id = usb2_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 = usb2_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usb2_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
+ sc->sc_data_iface_index = i;
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+ break;
+ }
+ } else {
+ device_printf(dev, "no data interface!\n");
+ return (EINVAL);
+ }
+ }
+
+ error = usb2_transfer_setup(uaa->device,
+ &sc->sc_data_iface_index, sc->sc_bulk_xfer,
+ ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating BULK USB "
+ "transfers failed!\n");
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static void
+ufoma_start_read(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ /* start interrupt transfer */
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
+
+ /* start data transfer */
+ if (sc->sc_nobulk) {
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+ } else {
+ usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+ }
+}
+
+static void
+ufoma_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt transfer */
+ usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
+
+ /* stop data transfer */
+ if (sc->sc_nobulk) {
+ usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+ } else {
+ usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+ }
+}
+
+static void
+ufoma_start_write(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ if (sc->sc_nobulk) {
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
+ } else {
+ usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+ }
+}
+
+static void
+ufoma_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ if (sc->sc_nobulk) {
+ usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
+ } else {
+ usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+ }
+}
+
+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;
+}
diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c
new file mode 100644
index 0000000..8ff7250
--- /dev/null
+++ b/sys/dev/usb/serial/uftdi.c
@@ -0,0 +1,784 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 FT8U100AX serial adapter driver
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR uftdi_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+#include <dev/usb/serial/uftdi_reg.h>
+
+#if USB_DEBUG
+static int uftdi_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
+SYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW,
+ &uftdi_debug, 0, "Debug level");
+#endif
+
+#define UFTDI_CONFIG_INDEX 0
+#define UFTDI_IFACE_INDEX 0
+
+#define UFTDI_IBUFSIZE 64 /* bytes, maximum number of bytes per
+ * frame */
+#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to
+ * do size encoding */
+
+enum {
+ UFTDI_BULK_DT_WR,
+ UFTDI_BULK_DT_RD,
+ UFTDI_N_TRANSFER,
+};
+
+struct uftdi_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[UFTDI_N_TRANSFER];
+ device_t sc_dev;
+
+ uint32_t sc_unit;
+ enum uftdi_type sc_type;
+
+ uint16_t sc_last_lcr;
+
+ uint8_t sc_iface_index;
+ uint8_t sc_hdrlen;
+ uint8_t sc_msr;
+ uint8_t sc_lsr;
+
+ uint8_t sc_name[16];
+};
+
+struct uftdi_param_config {
+ uint16_t rate;
+ 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 usb2_callback_t uftdi_write_callback;
+static usb2_callback_t uftdi_read_callback;
+
+static void uftdi_cfg_open(struct usb2_com_softc *);
+static void uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int uftdi_set_parm_soft(struct termios *,
+ struct uftdi_param_config *, uint8_t);
+static int uftdi_pre_param(struct usb2_com_softc *, struct termios *);
+static void uftdi_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uftdi_start_read(struct usb2_com_softc *);
+static void uftdi_stop_read(struct usb2_com_softc *);
+static void uftdi_start_write(struct usb2_com_softc *);
+static void uftdi_stop_write(struct usb2_com_softc *);
+static uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *);
+
+static const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = {
+
+ [UFTDI_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UFTDI_OBUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uftdi_write_callback,
+ },
+
+ [UFTDI_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UFTDI_IBUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uftdi_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uftdi_callback = {
+ .usb2_com_cfg_get_status = &uftdi_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uftdi_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uftdi_cfg_set_break,
+ .usb2_com_cfg_param = &uftdi_cfg_param,
+ .usb2_com_cfg_open = &uftdi_cfg_open,
+ .usb2_com_pre_param = &uftdi_pre_param,
+ .usb2_com_start_read = &uftdi_start_read,
+ .usb2_com_stop_read = &uftdi_stop_read,
+ .usb2_com_start_write = &uftdi_start_write,
+ .usb2_com_stop_write = &uftdi_stop_write,
+};
+
+static device_method_t uftdi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uftdi_probe),
+ DEVMETHOD(device_attach, uftdi_attach),
+ DEVMETHOD(device_detach, uftdi_detach),
+
+ {0, 0}
+};
+
+static devclass_t uftdi_devclass;
+
+static driver_t uftdi_driver = {
+ .name = "uftdi",
+ .methods = uftdi_methods,
+ .size = sizeof(struct uftdi_softc),
+};
+
+DRIVER_MODULE(uftdi, ushub, uftdi_driver, uftdi_devclass, NULL, 0);
+MODULE_DEPEND(uftdi, ucom, 1, 1, 1);
+MODULE_DEPEND(uftdi, usb, 1, 1, 1);
+
+static struct usb2_device_id uftdi_devs[] = {
+ {USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)},
+};
+
+static int
+uftdi_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ /* attach to all present interfaces */
+
+ return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
+}
+
+static int
+uftdi_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uftdi_softc *sc = device_get_softc(dev);
+ int error;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = dev;
+ sc->sc_unit = device_get_unit(dev);
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ DPRINTF("\n");
+
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+ sc->sc_type = USB_GET_DRIVER_INFO(uaa);
+
+ switch (sc->sc_type) {
+ case UFTDI_TYPE_SIO:
+ sc->sc_hdrlen = 1;
+ break;
+ case UFTDI_TYPE_8U232AM:
+ default:
+ sc->sc_hdrlen = 0;
+ break;
+ }
+
+ error = usb2_transfer_setup(uaa->device,
+ &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
+ UFTDI_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+
+ /* 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));
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uftdi_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ uftdi_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uftdi_detach(device_t dev)
+{
+ struct uftdi_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uftdi_cfg_open(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ struct usb2_device_request req;
+
+ DPRINTF("");
+
+ /* perform a full reset on the device */
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_RESET;
+ USETW(req.wValue, FTDI_SIO_RESET_SIO);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ /* turn on RTS/CTS flow control */
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW(req.wValue, 0);
+ USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ /*
+ * NOTE: with the new UCOM layer there will always be a
+ * "uftdi_cfg_param()" call after "open()", so there is no need for
+ * "open()" to configure anything
+ */
+}
+
+static void
+uftdi_write_callback(struct usb2_xfer *xfer)
+{
+ struct uftdi_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+ uint8_t buf[1];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers,
+ sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
+ &actlen)) {
+
+ if (sc->sc_hdrlen > 0) {
+ buf[0] =
+ FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
+ usb2_copy_in(xfer->frbuffers, 0, buf, 1);
+ }
+ xfer->frlengths[0] = actlen + sc->sc_hdrlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uftdi_read_callback(struct usb2_xfer *xfer)
+{
+ struct uftdi_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+ uint8_t ftdi_msr;
+ uint8_t msr;
+ uint8_t lsr;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < 2) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+
+ ftdi_msr = FTDI_GET_MSR(buf);
+ lsr = FTDI_GET_LSR(buf);
+
+ msr = 0;
+ 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 & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
+ 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;
+
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ xfer->actlen -= 2;
+
+ if (xfer->actlen > 0) {
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2,
+ xfer->actlen);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ uint16_t wValue;
+ struct usb2_device_request req;
+
+ 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);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ uint16_t wValue;
+ struct usb2_device_request req;
+
+ 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);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ uint16_t wValue;
+ struct usb2_device_request req;
+
+ 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);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static int
+uftdi_set_parm_soft(struct termios *t,
+ struct uftdi_param_config *cfg, uint8_t type)
+{
+ bzero(cfg, sizeof(*cfg));
+
+ switch (type) {
+ case UFTDI_TYPE_SIO:
+ switch (t->c_ospeed) {
+ case 300:
+ cfg->rate = ftdi_sio_b300;
+ break;
+ case 600:
+ cfg->rate = ftdi_sio_b600;
+ break;
+ case 1200:
+ cfg->rate = ftdi_sio_b1200;
+ break;
+ case 2400:
+ cfg->rate = ftdi_sio_b2400;
+ break;
+ case 4800:
+ cfg->rate = ftdi_sio_b4800;
+ break;
+ case 9600:
+ cfg->rate = ftdi_sio_b9600;
+ break;
+ case 19200:
+ cfg->rate = ftdi_sio_b19200;
+ break;
+ case 38400:
+ cfg->rate = ftdi_sio_b38400;
+ break;
+ case 57600:
+ cfg->rate = ftdi_sio_b57600;
+ break;
+ case 115200:
+ cfg->rate = ftdi_sio_b115200;
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case UFTDI_TYPE_8U232AM:
+ if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ 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 usb2_com_softc *ucom, struct termios *t)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ struct uftdi_param_config cfg;
+
+ DPRINTF("\n");
+
+ return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
+}
+
+static void
+uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ struct uftdi_param_config cfg;
+ struct usb2_device_request req;
+
+ if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
+ /* should not happen */
+ return;
+ }
+ sc->sc_last_lcr = cfg.lcr;
+
+ DPRINTF("\n");
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_BAUD_RATE;
+ USETW(req.wValue, cfg.rate);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_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);
+ usb2_com_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);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ DPRINTF("msr=0x%02x lsr=0x%02x\n",
+ sc->sc_msr, sc->sc_lsr);
+
+ *msr = sc->sc_msr;
+ *lsr = sc->sc_lsr;
+}
+
+static void
+uftdi_start_read(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+}
+
+static void
+uftdi_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+}
+
+static void
+uftdi_start_write(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+}
+
+static void
+uftdi_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+}
+
+/*------------------------------------------------------------------------*
+ * uftdi_8u232am_getrate
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
+{
+ /* Table of the nearest even powers-of-2 for values 0..15. */
+ static const uint8_t roundoff[16] = {
+ 0, 2, 2, 4, 4, 4, 8, 8,
+ 8, 8, 8, 8, 16, 16, 16, 16,
+ };
+ uint32_t d;
+ uint32_t freq;
+ uint16_t result;
+
+ if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
+ return (1); /* prevent numerical overflow */
+
+ /* Special cases for 2M and 3M. */
+ if ((speed >= ((3000000 * 100) / 103)) &&
+ (speed <= ((3000000 * 100) / 97))) {
+ result = 0;
+ goto done;
+ }
+ if ((speed >= ((2000000 * 100) / 103)) &&
+ (speed <= ((2000000 * 100) / 97))) {
+ result = 1;
+ goto done;
+ }
+ d = (FTDI_8U232AM_FREQ << 4) / speed;
+ d = (d & ~15) + roundoff[d & 15];
+
+ if (d < FTDI_8U232AM_MIN_DIV)
+ d = FTDI_8U232AM_MIN_DIV;
+ else if (d > FTDI_8U232AM_MAX_DIV)
+ d = FTDI_8U232AM_MAX_DIV;
+
+ /*
+ * Calculate the frequency needed for "d" to exactly divide down to
+ * our target "speed", and check that the actual frequency is within
+ * 3% of this.
+ */
+ freq = (speed * d);
+ if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
+ (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
+ return (1);
+
+ /*
+ * Pack the divisor into the resultant value. The lower 14-bits
+ * hold the integral part, while the upper 2 bits encode the
+ * fractional component: either 0, 0.5, 0.25, or 0.125.
+ */
+ result = (d >> 4);
+ if (d & 8)
+ result |= 0x4000;
+ else if (d & 4)
+ result |= 0x8000;
+ else if (d & 2)
+ result |= 0xc000;
+
+done:
+ *rate = result;
+ return (0);
+}
diff --git a/sys/dev/usb/serial/uftdi_reg.h b/sys/dev/usb/serial/uftdi_reg.h
new file mode 100644
index 0000000..0074bc5
--- /dev/null
+++ b/sys/dev/usb/serial/uftdi_reg.h
@@ -0,0 +1,340 @@
+/* $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 */
+
+/* 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 */
+
+enum uftdi_type {
+ UFTDI_TYPE_SIO,
+ UFTDI_TYPE_8U232AM
+};
+
+/*
+ * 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 value - see below
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ */
+/* FTDI_SIO_SET_BAUDRATE */
+enum {
+ ftdi_sio_b300 = 0,
+ ftdi_sio_b600 = 1,
+ ftdi_sio_b1200 = 2,
+ ftdi_sio_b2400 = 3,
+ ftdi_sio_b4800 = 4,
+ ftdi_sio_b9600 = 5,
+ ftdi_sio_b19200 = 6,
+ ftdi_sio_b38400 = 7,
+ ftdi_sio_b57600 = 8,
+ ftdi_sio_b115200 = 9
+};
+
+#define FTDI_8U232AM_FREQ 3000000
+
+/* Bounds for normal divisors as 4-bit fixed precision ints. */
+#define FTDI_8U232AM_MIN_DIV 0x20
+#define FTDI_8U232AM_MAX_DIV 0x3fff8
+
+/*
+ * 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 protocl / 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/sys/dev/usb/serial/ugensa.c b/sys/dev/usb/serial/ugensa.c
new file mode 100644
index 0000000..79676d6
--- /dev/null
+++ b/sys/dev/usb/serial/ugensa.c
@@ -0,0 +1,352 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_device.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 usb2_com_softc *sc_usb2_com_ptr;
+ struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER];
+};
+
+struct ugensa_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_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 usb2_callback_t ugensa_bulk_write_callback;
+static usb2_callback_t ugensa_bulk_read_callback;
+
+static void ugensa_start_read(struct usb2_com_softc *);
+static void ugensa_stop_read(struct usb2_com_softc *);
+static void ugensa_start_write(struct usb2_com_softc *);
+static void ugensa_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config
+ ugensa_xfer_config[UGENSA_N_TRANSFER] = {
+
+ [UGENSA_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UGENSA_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ugensa_bulk_write_callback,
+ },
+
+ [UGENSA_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UGENSA_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ugensa_bulk_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ugensa_callback = {
+ .usb2_com_start_read = &ugensa_start_read,
+ .usb2_com_stop_read = &ugensa_stop_read,
+ .usb2_com_start_write = &ugensa_start_write,
+ .usb2_com_stop_write = &ugensa_stop_write,
+};
+
+static device_method_t ugensa_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_probe, ugensa_probe),
+ DEVMETHOD(device_attach, ugensa_attach),
+ DEVMETHOD(device_detach, ugensa_detach),
+ {0, 0}
+};
+
+static devclass_t ugensa_devclass;
+
+static driver_t ugensa_driver = {
+ .name = "ugensa",
+ .methods = ugensa_methods,
+ .size = sizeof(struct ugensa_softc),
+};
+
+DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0);
+MODULE_DEPEND(ugensa, ucom, 1, 1, 1);
+MODULE_DEPEND(ugensa, usb, 1, 1, 1);
+
+static const struct usb2_device_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)},
+};
+
+static int
+ugensa_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != 0) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa));
+}
+
+static int
+ugensa_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ugensa_softc *sc = device_get_softc(dev);
+ struct ugensa_sub_softc *ssc;
+ struct usb2_interface *iface;
+ int32_t error;
+ uint8_t iface_index;
+ int x, cnt;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF);
+
+ /* Figure out how many interfaces this device has got */
+ for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) {
+ if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) ||
+ (usb2_get_pipe(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 = usb2_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_usb2_com_ptr = sc->sc_ucom + sc->sc_niface;
+
+ iface_index = (UGENSA_IFACE_INDEX + x);
+ error = usb2_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 */
+ usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+ usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+
+ /* initialize port number */
+ ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface;
+ sc->sc_niface++;
+ if (x != uaa->info.bIfaceIndex)
+ usb2_set_parent_iface(uaa->device, x,
+ uaa->info.bIfaceIndex);
+ }
+ device_printf(dev, "Found %d interfaces.\n", sc->sc_niface);
+
+ error = usb2_com_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;
+ }
+ 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;
+
+ usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface);
+
+ for (x = 0; x < sc->sc_niface; x++) {
+ usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER);
+ }
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+ugensa_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct ugensa_sub_softc *ssc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
+ UGENSA_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ugensa_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct ugensa_sub_softc *ssc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ugensa_start_read(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+}
+
+static void
+ugensa_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+}
+
+static void
+ugensa_start_write(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+}
+
+static void
+ugensa_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/serial/uipaq.c b/sys/dev/usb/serial/uipaq.c
new file mode 100644
index 0000000..e2a88be
--- /dev/null
+++ b/sys/dev/usb/serial/uipaq.c
@@ -0,0 +1,1314 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UIPAQ_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_callback_t uipaq_write_callback;
+static usb2_callback_t uipaq_read_callback;
+
+static void uipaq_start_read(struct usb2_com_softc *);
+static void uipaq_stop_read(struct usb2_com_softc *);
+static void uipaq_start_write(struct usb2_com_softc *);
+static void uipaq_stop_write(struct usb2_com_softc *);
+static void uipaq_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uipaq_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uipaq_cfg_set_break(struct usb2_com_softc *, uint8_t);
+
+static const struct usb2_config uipaq_config_data[UIPAQ_N_TRANSFER] = {
+
+ [UIPAQ_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UIPAQ_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uipaq_write_callback,
+ },
+
+ [UIPAQ_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UIPAQ_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uipaq_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uipaq_callback = {
+ .usb2_com_cfg_set_dtr = &uipaq_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uipaq_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uipaq_cfg_set_break,
+ .usb2_com_start_read = &uipaq_start_read,
+ .usb2_com_stop_read = &uipaq_stop_read,
+ .usb2_com_start_write = &uipaq_start_write,
+ .usb2_com_stop_write = &uipaq_stop_write,
+};
+
+/*
+ * 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 usb2_device_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)},
+ /* "High Tech Computer Corp" */
+ {USB_VPI(USB_VENDOR_HTC, 0x0bce, 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)},
+ /* 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)},
+ /* 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),
+ {0, 0}
+};
+
+static devclass_t uipaq_devclass;
+
+static driver_t uipaq_driver = {
+ .name = "uipaq",
+ .methods = uipaq_methods,
+ .size = sizeof(struct uipaq_softc),
+};
+
+DRIVER_MODULE(uipaq, ushub, uipaq_driver, uipaq_devclass, NULL, 0);
+MODULE_DEPEND(uipaq, ucom, 1, 1, 1);
+MODULE_DEPEND(uipaq, usb, 1, 1, 1);
+
+static int
+uipaq_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UIPAQ_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UIPAQ_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uipaq_devs, sizeof(uipaq_devs), uaa));
+}
+
+static int
+uipaq_attach(device_t dev)
+{
+ struct usb2_device_request req;
+ struct usb2_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_usb2_desc(dev);
+
+ /*
+ * 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 =
+ usb2_do_request_flags(uaa->device, NULL, &req,
+ NULL, 0, NULL, 100);
+ if (error == 0)
+ break;
+ usb2_pause_mtx(NULL, hz / 10);
+ }
+
+ iface_index = UIPAQ_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, uipaq_config_data,
+ UIPAQ_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uipaq_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ uipaq_detach(dev);
+ return (ENXIO);
+}
+
+int
+uipaq_detach(device_t dev)
+{
+ struct uipaq_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UIPAQ_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uipaq_start_read(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+}
+
+static void
+uipaq_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+}
+
+static void
+uipaq_start_write(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+}
+
+static void
+uipaq_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+}
+
+static void
+uipaq_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_write_callback(struct usb2_xfer *xfer)
+{
+ struct uipaq_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UIPAQ_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uipaq_read_callback(struct usb2_xfer *xfer)
+{
+ struct uipaq_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/ulpt.c b/sys/dev/usb/serial/ulpt.c
new file mode 100644
index 0000000..36e082b
--- /dev/null
+++ b/sys/dev/usb/serial/ulpt.c
@@ -0,0 +1,726 @@
+#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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR ulpt_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_parse.h>
+
+#include <sys/syslog.h>
+
+#if USB_DEBUG
+static int ulpt_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt");
+SYSCTL_INT(_hw_usb2_ulpt, OID_AUTO, debug, CTLFLAG_RW,
+ &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 usb2_fifo_sc sc_fifo;
+ struct usb2_fifo_sc sc_fifo_noreset;
+ struct mtx sc_mtx;
+ struct usb2_callout sc_watchdog;
+
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct usb2_fifo *sc_fifo_open[2];
+ struct usb2_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 usb2_callback_t ulpt_write_callback;
+static usb2_callback_t ulpt_read_callback;
+static usb2_callback_t ulpt_status_callback;
+
+static void ulpt_reset(struct ulpt_softc *);
+static void ulpt_watchdog(void *);
+
+static usb2_fifo_close_t ulpt_close;
+static usb2_fifo_cmd_t ulpt_start_read;
+static usb2_fifo_cmd_t ulpt_start_write;
+static usb2_fifo_cmd_t ulpt_stop_read;
+static usb2_fifo_cmd_t ulpt_stop_write;
+static usb2_fifo_ioctl_t ulpt_ioctl;
+static usb2_fifo_open_t ulpt_open;
+static usb2_fifo_open_t unlpt_open;
+
+static struct usb2_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 usb2_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 usb2_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 (usb2_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 (usb2_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 usb2_xfer *xfer)
+{
+ struct ulpt_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_TX];
+ uint32_t actlen;
+
+ 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:
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_fifo_get_data(f, xfer->frbuffers,
+ 0, xfer->max_data_length, &actlen, 0)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+ulpt_read_callback(struct usb2_xfer *xfer)
+{
+ struct ulpt_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_RX];
+
+ 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 (xfer->actlen == 0) {
+
+ if (sc->sc_zlps == 4) {
+ /* enable BULK throttle */
+ xfer->interval = 500; /* ms */
+ } else {
+ sc->sc_zlps++;
+ }
+ } else {
+ /* disable BULK throttle */
+
+ xfer->interval = 0;
+ sc->sc_zlps = 0;
+ }
+
+ usb2_fifo_put_data(f, xfer->frbuffers,
+ 0, xfer->actlen, 1);
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_fifo_put_bytes_max(f) != 0) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ /* disable BULK throttle */
+ xfer->interval = 0;
+ sc->sc_zlps = 0;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+ulpt_status_callback(struct usb2_xfer *xfer)
+{
+ struct ulpt_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint8_t cur_status;
+ uint8_t new_status;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ usb2_copy_out(xfer->frbuffers + 1, 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);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = 1;
+ xfer->nframes = 2;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ DPRINTF("error=%s\n", usb2_errstr(xfer->error));
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* wait for next watchdog timeout */
+ }
+ break;
+ }
+}
+
+static const struct usb2_config ulpt_config[ULPT_N_TRANSFER] = {
+ [ULPT_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = ULPT_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1},
+ .mh.callback = &ulpt_write_callback,
+ },
+
+ [ULPT_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = ULPT_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1},
+ .mh.callback = &ulpt_read_callback,
+ },
+
+ [ULPT_INTR_DT_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request) + 1,
+ .mh.callback = &ulpt_status_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static void
+ulpt_start_read(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]);
+}
+
+static void
+ulpt_stop_read(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]);
+}
+
+static void
+ulpt_start_write(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]);
+}
+
+static void
+ulpt_stop_write(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]);
+}
+
+static int
+ulpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ /* we assume that open is a serial process */
+
+ if (sc->sc_fflags == 0) {
+ ulpt_reset(sc);
+ }
+ return (unlpt_open(fifo, fflags, td));
+}
+
+static int
+unlpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ if (sc->sc_fflags & fflags) {
+ return (EBUSY);
+ }
+ if (fflags & FREAD) {
+ /* clear stall first */
+ mtx_lock(&sc->sc_mtx);
+ usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]);
+ mtx_unlock(&sc->sc_mtx);
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[ULPT_BULK_DT_RD]->max_data_length,
+ 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);
+ usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]);
+ mtx_unlock(&sc->sc_mtx);
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[ULPT_BULK_DT_WR]->max_data_length,
+ 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 usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
+
+ if (fflags & (FREAD | FWRITE)) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+static int
+ulpt_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
+ int fflags, struct thread *td)
+{
+ return (ENODEV);
+}
+
+static int
+ulpt_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if ((uaa->info.bInterfaceClass == UICLASS_PRINTER) &&
+ (uaa->info.bInterfaceSubClass == UISUBCLASS_PRINTER) &&
+ ((uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_UNI) ||
+ (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_BI) ||
+ (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_1284))) {
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+ulpt_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ulpt_softc *sc = device_get_softc(dev);
+ struct usb2_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_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
+
+ /* search through all the descriptors looking for bidir mode */
+
+ id = usb2_get_interface_descriptor(uaa->iface);
+ alt_index = 0 - 1;
+ 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 *)usb2_desc_foreach(
+ usb2_get_config_descriptor(uaa->device), (void *)id);
+ }
+ goto detach;
+
+found:
+
+ DPRINTF("setting alternate "
+ "config number: %d\n", alt_index);
+
+ if (alt_index) {
+
+ error = usb2_set_alt_interface_index
+ (uaa->device, iface_index, alt_index);
+
+ if (error) {
+ DPRINTF("could not set alternate "
+ "config, error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ }
+ sc->sc_iface_no = id->bInterfaceNumber;
+
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error) {
+ DPRINTF("error=%s\n", usb2_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 usb2_config_descriptor *cd = usb2_get_config_descriptor(dev);
+ struct usb2_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 = usb2_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
+
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &ulpt_fifo_methods, &sc->sc_fifo,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &unlpt_fifo_methods, &sc->sc_fifo_noreset,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ 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);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+ usb2_fifo_detach(&sc->sc_fifo_noreset);
+
+ mtx_lock(&sc->sc_mtx);
+ usb2_callout_stop(&sc->sc_watchdog);
+ mtx_unlock(&sc->sc_mtx);
+
+ usb2_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER);
+
+ usb2_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);
+
+ usb2_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]);
+
+ usb2_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),
+ {0, 0}
+};
+
+static driver_t ulpt_driver = {
+ .name = "ulpt",
+ .methods = ulpt_methods,
+ .size = sizeof(struct ulpt_softc),
+};
+
+DRIVER_MODULE(ulpt, ushub, ulpt_driver, ulpt_devclass, NULL, 0);
+MODULE_DEPEND(ulpt, usb, 1, 1, 1);
+MODULE_DEPEND(ulpt, ucom, 1, 1, 1);
diff --git a/sys/dev/usb/serial/umct.c b/sys/dev/usb/serial/umct.c
new file mode 100644
index 0000000..a511e2e
--- /dev/null
+++ b/sys/dev/usb/serial/umct.c
@@ -0,0 +1,579 @@
+#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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.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_INTR_INTERVAL 100
+#define UMCT_IFACE_INDEX 0
+#define UMCT_CONFIG_INDEX 1
+
+enum {
+ UMCT_BULK_DT_WR,
+ UMCT_BULK_DT_RD,
+ UMCT_INTR_DT_RD,
+ UMCT_N_TRANSFER,
+};
+
+struct umct_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[UMCT_N_TRANSFER];
+
+ 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_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t umct_probe;
+static device_attach_t umct_attach;
+static device_detach_t umct_detach;
+
+static usb2_callback_t umct_intr_callback;
+static usb2_callback_t umct_write_callback;
+static usb2_callback_t umct_read_callback;
+
+static void umct_cfg_do_request(struct umct_softc *sc, uint8_t request,
+ uint16_t len, uint32_t value);
+static void umct_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void umct_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void umct_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void umct_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static uint8_t umct_calc_baud(uint32_t);
+static int umct_pre_param(struct usb2_com_softc *, struct termios *);
+static void umct_cfg_param(struct usb2_com_softc *, struct termios *);
+static void umct_start_read(struct usb2_com_softc *);
+static void umct_stop_read(struct usb2_com_softc *);
+static void umct_start_write(struct usb2_com_softc *);
+static void umct_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config umct_config[UMCT_N_TRANSFER] = {
+
+ [UMCT_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &umct_write_callback,
+ },
+
+ [UMCT_BULK_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.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,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umct_intr_callback,
+ .ep_index = 1, /* second interrupt endpoint */
+ },
+};
+
+static const struct usb2_com_callback umct_callback = {
+ .usb2_com_cfg_get_status = &umct_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &umct_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &umct_cfg_set_rts,
+ .usb2_com_cfg_set_break = &umct_cfg_set_break,
+ .usb2_com_cfg_param = &umct_cfg_param,
+ .usb2_com_pre_param = &umct_pre_param,
+ .usb2_com_start_read = &umct_start_read,
+ .usb2_com_stop_read = &umct_stop_read,
+ .usb2_com_start_write = &umct_start_write,
+ .usb2_com_stop_write = &umct_stop_write,
+};
+
+static const struct usb2_device_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),
+ {0, 0}
+};
+
+static devclass_t umct_devclass;
+
+static driver_t umct_driver = {
+ .name = "umct",
+ .methods = umct_methods,
+ .size = sizeof(struct umct_softc),
+};
+
+DRIVER_MODULE(umct, ushub, umct_driver, umct_devclass, NULL, 0);
+MODULE_DEPEND(umct, ucom, 1, 1, 1);
+MODULE_DEPEND(umct, usb, 1, 1, 1);
+
+static int
+umct_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_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 (usb2_lookup_id_by_uaa(umct_devs, sizeof(umct_devs), uaa));
+}
+
+static int
+umct_attach(device_t dev)
+{
+ struct usb2_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_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+
+ iface_index = UMCT_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, umct_config, UMCT_N_TRANSFER, sc, &Giant);
+
+ 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 = UGETW(sc->sc_xfer[UMCT_BULK_DT_RD]->pipe->edesc->wMaxPacketSize);
+ if (maxp == 0x2) {
+
+ /* guessed wrong - switch around endpoints */
+
+ struct usb2_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_xfer[UMCT_BULK_DT_RD]->callback = &umct_read_callback;
+ sc->sc_xfer[UMCT_INTR_DT_RD]->callback = &umct_intr_callback;
+ }
+ sc->sc_obufsize = sc->sc_xfer[UMCT_BULK_DT_WR]->max_data_length;
+
+ if (uaa->info.idProduct == USB_PRODUCT_MCT_SITECOM_USB232) {
+ if (sc->sc_obufsize > 16) {
+ sc->sc_obufsize = 16;
+ }
+ }
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &umct_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMCT_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+umct_cfg_do_request(struct umct_softc *sc, uint8_t request,
+ uint16_t len, uint32_t value)
+{
+ struct usb2_device_request req;
+ usb2_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 = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, temp, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+ return;
+}
+
+static void
+umct_intr_callback(struct usb2_xfer *xfer)
+{
+ struct umct_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 2) {
+ DPRINTF("too short message\n");
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
+
+ sc->sc_msr = buf[0];
+ sc->sc_lsr = buf[1];
+
+ usb2_com_status_change(&sc->sc_ucom);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umct_cfg_get_status(struct usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_softc *ucom, struct termios *t)
+{
+ return (0); /* we accept anything */
+}
+
+static void
+umct_cfg_param(struct usb2_com_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 usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMCT_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]);
+}
+
+static void
+umct_stop_read(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMCT_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]);
+}
+
+static void
+umct_start_write(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]);
+}
+
+static void
+umct_stop_write(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]);
+}
+
+static void
+umct_write_callback(struct usb2_xfer *xfer)
+{
+ struct umct_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ sc->sc_obufsize, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umct_read_callback(struct usb2_xfer *xfer)
+{
+ struct umct_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
+ 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/umodem.c b/sys/dev/usb/serial/umodem.c
new file mode 100644
index 0000000..44813dd
--- /dev/null
+++ b/sys/dev/usb/serial/umodem.c
@@ -0,0 +1,788 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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:
+ * - 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR umodem_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int umodem_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
+SYSCTL_INT(_hw_usb2_umodem, OID_AUTO, debug, CTLFLAG_RW,
+ &umodem_debug, 0, "Debug level");
+#endif
+
+static const struct usb2_device_id umodem_devs[] = {
+ /* Generic Modem class match */
+ {USB_IFACE_CLASS(UICLASS_CDC),
+ USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+ USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
+ /* 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 deivces 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_RD,
+ UMODEM_N_TRANSFER,
+};
+
+#define UMODEM_MODVER 1 /* module version */
+
+struct umodem_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UMODEM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 */
+};
+
+static device_probe_t umodem_probe;
+static device_attach_t umodem_attach;
+static device_detach_t umodem_detach;
+
+static usb2_callback_t umodem_intr_callback;
+static usb2_callback_t umodem_write_callback;
+static usb2_callback_t umodem_read_callback;
+
+static void umodem_start_read(struct usb2_com_softc *);
+static void umodem_stop_read(struct usb2_com_softc *);
+static void umodem_start_write(struct usb2_com_softc *);
+static void umodem_stop_write(struct usb2_com_softc *);
+static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *);
+static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static int umodem_pre_param(struct usb2_com_softc *, struct termios *);
+static void umodem_cfg_param(struct usb2_com_softc *, struct termios *);
+static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int,
+ struct thread *);
+static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t);
+static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t,
+ uint16_t, uint16_t);
+
+static const struct usb2_config umodem_config[UMODEM_N_TRANSFER] = {
+
+ [UMODEM_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 0,
+ .mh.bufsize = UMODEM_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &umodem_write_callback,
+ },
+
+ [UMODEM_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 0,
+ .mh.bufsize = UMODEM_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &umodem_read_callback,
+ },
+
+ [UMODEM_INTR_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 1,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umodem_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback umodem_callback = {
+ .usb2_com_cfg_get_status = &umodem_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &umodem_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &umodem_cfg_set_rts,
+ .usb2_com_cfg_set_break = &umodem_cfg_set_break,
+ .usb2_com_cfg_param = &umodem_cfg_param,
+ .usb2_com_pre_param = &umodem_pre_param,
+ .usb2_com_ioctl = &umodem_ioctl,
+ .usb2_com_start_read = &umodem_start_read,
+ .usb2_com_stop_read = &umodem_stop_read,
+ .usb2_com_start_write = &umodem_start_write,
+ .usb2_com_stop_write = &umodem_stop_write,
+};
+
+static device_method_t umodem_methods[] = {
+ DEVMETHOD(device_probe, umodem_probe),
+ DEVMETHOD(device_attach, umodem_attach),
+ DEVMETHOD(device_detach, umodem_detach),
+ {0, 0}
+};
+
+static devclass_t umodem_devclass;
+
+static driver_t umodem_driver = {
+ .name = "umodem",
+ .methods = umodem_methods,
+ .size = sizeof(struct umodem_softc),
+};
+
+DRIVER_MODULE(umodem, ushub, 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);
+
+static int
+umodem_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ uint8_t cm;
+ uint8_t acm;
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ error = usb2_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
+ if (error) {
+ return (error);
+ }
+ if (uaa->driver_info == NULL) {
+ /* some modems do not have any capabilities */
+ return (error);
+ }
+ umodem_get_caps(uaa, &cm, &acm);
+ if (!(cm & USB_CDC_CM_DOES_CM) ||
+ !(cm & USB_CDC_CM_OVER_DATA) ||
+ !(acm & USB_CDC_ACM_HAS_LINE)) {
+ error = ENXIO;
+ }
+ return (error);
+}
+
+static int
+umodem_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umodem_softc *sc = device_get_softc(dev);
+ struct usb2_cdc_cm_descriptor *cmd;
+ uint8_t i;
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ 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))) {
+ device_printf(dev, "no CM descriptor!\n");
+ goto detach;
+ }
+ 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 usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+
+ iface = usb2_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usb2_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
+ sc->sc_iface_index[0] = i;
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+ break;
+ }
+ } else {
+ device_printf(dev, "no data interface!\n");
+ goto detach;
+ }
+ }
+
+ 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 = usb2_transfer_setup(uaa->device,
+ sc->sc_iface_index, sc->sc_xfer,
+ umodem_config, UMODEM_N_TRANSFER,
+ sc, &Giant);
+ if (error) {
+ goto detach;
+ }
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &umodem_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ umodem_detach(dev);
+ return (ENXIO);
+}
+
+static void
+umodem_start_read(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint, if any */
+ usb2_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
+}
+
+static void
+umodem_stop_read(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint, if any */
+ usb2_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
+}
+
+static void
+umodem_start_write(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
+}
+
+static void
+umodem_stop_write(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
+}
+
+static void
+umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
+{
+ struct usb2_cdc_cm_descriptor *cmd;
+ struct usb2_cdc_acm_descriptor *cad;
+
+ *cm = *acm = 0;
+
+ cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
+ DPRINTF("no CM desc\n");
+ return;
+ }
+ *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");
+ return;
+ }
+ *acm = cad->bmCapabilities;
+}
+
+static void
+umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static int
+umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ return (0); /* we accept anything */
+}
+
+static void
+umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_cdc_line_state ls;
+ struct usb2_device_request req;
+
+ DPRINTF("sc=%p\n", sc);
+
+ bzero(&ls, 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));
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, &ls, 0, 1000);
+}
+
+static int
+umodem_ioctl(struct usb2_com_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 usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ }
+}
+
+static void
+umodem_intr_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_cdc_notification pkt;
+ struct umodem_softc *sc = xfer->priv_sc;
+ uint16_t wLen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < 8) {
+ DPRINTF("received short packet, "
+ "%d bytes\n", xfer->actlen);
+ goto tr_setup;
+ }
+ if (xfer->actlen > sizeof(pkt)) {
+ DPRINTF("truncating message\n");
+ xfer->actlen = sizeof(pkt);
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
+
+ xfer->actlen -= 8;
+
+ wLen = UGETW(pkt.wLength);
+ if (xfer->actlen > wLen) {
+ xfer->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 (xfer->actlen < 2) {
+ DPRINTF("invalid notification "
+ "length, %d bytes!\n", xfer->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;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+ break;
+
+ default:
+ DPRINTF("unknown notify message: 0x%02x\n",
+ pkt.bNotification);
+ break;
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+umodem_write_callback(struct usb2_xfer *xfer)
+{
+ struct umodem_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UMODEM_BUF_SIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umodem_read_callback(struct usb2_xfer *xfer)
+{
+ struct umodem_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen=%d\n", xfer->actlen);
+
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void *
+umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype)
+{
+ return (usb2_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
+ type, 0 - 1, subtype, 0 - 1));
+}
+
+static usb2_error_t
+umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no,
+ uint16_t feature, uint16_t state)
+{
+ struct usb2_device_request req;
+ struct usb2_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 (usb2_do_request(udev, &Giant, &req, &ast));
+}
+
+static int
+umodem_detach(device_t dev)
+{
+ struct umodem_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
+
+ return (0);
+}
diff --git a/sys/dev/usb/serial/umoscom.c b/sys/dev/usb/serial/umoscom.c
new file mode 100644
index 0000000..344f4a1
--- /dev/null
+++ b/sys/dev/usb/serial/umoscom.c
@@ -0,0 +1,672 @@
+/* $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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR umoscom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int umoscom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
+SYSCTL_INT(_hw_usb2_umoscom, OID_AUTO, debug, CTLFLAG_RW,
+ &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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UMOSCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_callback_t umoscom_write_callback;
+static usb2_callback_t umoscom_read_callback;
+static usb2_callback_t umoscom_intr_callback;
+
+static void umoscom_cfg_open(struct usb2_com_softc *);
+static void umoscom_cfg_close(struct usb2_com_softc *);
+static void umoscom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void umoscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void umoscom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static int umoscom_pre_param(struct usb2_com_softc *, struct termios *);
+static void umoscom_cfg_param(struct usb2_com_softc *, struct termios *);
+static void umoscom_cfg_get_status(struct usb2_com_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 usb2_com_softc *);
+static void umoscom_stop_read(struct usb2_com_softc *);
+static void umoscom_start_write(struct usb2_com_softc *);
+static void umoscom_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
+
+ [UMOSCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UMOSCOM_BUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &umoscom_write_callback,
+ },
+
+ [UMOSCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UMOSCOM_BUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &umoscom_read_callback,
+ },
+
+ [UMOSCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umoscom_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback umoscom_callback = {
+ /* configuration callbacks */
+ .usb2_com_cfg_get_status = &umoscom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &umoscom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &umoscom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &umoscom_cfg_set_break,
+ .usb2_com_cfg_param = &umoscom_cfg_param,
+ .usb2_com_cfg_open = &umoscom_cfg_open,
+ .usb2_com_cfg_close = &umoscom_cfg_close,
+
+ /* other callbacks */
+ .usb2_com_pre_param = &umoscom_pre_param,
+ .usb2_com_start_read = &umoscom_start_read,
+ .usb2_com_stop_read = &umoscom_stop_read,
+ .usb2_com_start_write = &umoscom_start_write,
+ .usb2_com_stop_write = &umoscom_stop_write,
+};
+
+static device_method_t umoscom_methods[] = {
+ DEVMETHOD(device_probe, umoscom_probe),
+ DEVMETHOD(device_attach, umoscom_attach),
+ DEVMETHOD(device_detach, umoscom_detach),
+ {0, 0}
+};
+
+static devclass_t umoscom_devclass;
+
+static driver_t umoscom_driver = {
+ .name = "umoscom",
+ .methods = umoscom_methods,
+ .size = sizeof(struct umoscom_softc),
+};
+
+DRIVER_MODULE(umoscom, ushub, umoscom_driver, umoscom_devclass, NULL, 0);
+MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
+MODULE_DEPEND(umoscom, usb, 1, 1, 1);
+
+static const struct usb2_device_id umoscom_devs[] = {
+ {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
+};
+
+static int
+umoscom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_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 (usb2_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
+}
+
+static int
+umoscom_attach(device_t dev)
+{
+ struct usb2_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");
+
+ iface_index = UMOSCOM_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, umoscom_config_data,
+ UMOSCOM_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &umoscom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ device_printf(dev, "attach error: %s\n", usb2_errstr(error));
+ umoscom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+umoscom_detach(device_t dev)
+{
+ struct umoscom_softc *sc = device_get_softc(dev);
+
+ mtx_lock(&Giant);
+
+ mtx_unlock(&Giant);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+umoscom_cfg_open(struct usb2_com_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 usb2_com_softc *ucom)
+{
+ return;
+}
+
+static void
+umoscom_cfg_set_break(struct usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_softc *ucom, struct termios *t)
+{
+ if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
+ return (EINVAL);
+
+ return (0);
+}
+
+static void
+umoscom_cfg_param(struct usb2_com_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 usb2_com_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+ uint8_t lsr;
+ uint8_t msr;
+
+ DPRINTFN(5, "\n");
+
+ /* read status registers */
+
+ 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 usb2_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);
+
+ usb2_com_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 usb2_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);
+
+ usb2_com_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 usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+#if 0
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
+#endif
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+}
+
+static void
+umoscom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt transfer */
+ usb2_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+}
+
+static void
+umoscom_start_write(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+}
+
+static void
+umoscom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+}
+
+static void
+umoscom_write_callback(struct usb2_xfer *xfer)
+{
+ struct umoscom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ DPRINTF("\n");
+
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UMOSCOM_BUFSIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ DPRINTFN(0, "transfer failed\n");
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umoscom_read_callback(struct usb2_xfer *xfer)
+{
+ struct umoscom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("got %d bytes\n", xfer->actlen);
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ DPRINTF("\n");
+
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ DPRINTFN(0, "transfer failed\n");
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umoscom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct umoscom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 2) {
+ DPRINTF("too short message\n");
+ goto tr_setup;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ DPRINTFN(0, "transfer failed\n");
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/uplcom.c b/sys/dev/usb/serial/uplcom.c
new file mode 100644
index 0000000..5f37417
--- /dev/null
+++ b/sys/dev/usb/serial/uplcom.c
@@ -0,0 +1,831 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR uplcom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uplcom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
+SYSCTL_INT(_hw_usb2_uplcom, OID_AUTO, debug, CTLFLAG_RW,
+ &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_PL2303X 1
+
+enum {
+ UPLCOM_BULK_DT_WR,
+ UPLCOM_BULK_DT_RD,
+ UPLCOM_INTR_DT_RD,
+ UPLCOM_N_TRANSFER,
+};
+
+struct uplcom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UPLCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_error_t uplcom_reset(struct uplcom_softc *, struct usb2_device *);
+static int uplcom_pl2303x_init(struct usb2_device *);
+static void uplcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uplcom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uplcom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int uplcom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uplcom_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uplcom_start_read(struct usb2_com_softc *);
+static void uplcom_stop_read(struct usb2_com_softc *);
+static void uplcom_start_write(struct usb2_com_softc *);
+static void uplcom_stop_write(struct usb2_com_softc *);
+static void uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+
+static device_probe_t uplcom_probe;
+static device_attach_t uplcom_attach;
+static device_detach_t uplcom_detach;
+
+static usb2_callback_t uplcom_intr_callback;
+static usb2_callback_t uplcom_write_callback;
+static usb2_callback_t uplcom_read_callback;
+
+static const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
+
+ [UPLCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UPLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uplcom_write_callback,
+ .if_index = 0,
+ },
+
+ [UPLCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UPLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uplcom_read_callback,
+ .if_index = 0,
+ },
+
+ [UPLCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uplcom_intr_callback,
+ .if_index = 1,
+ },
+};
+
+struct usb2_com_callback uplcom_callback = {
+ .usb2_com_cfg_get_status = &uplcom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uplcom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uplcom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uplcom_cfg_set_break,
+ .usb2_com_cfg_param = &uplcom_cfg_param,
+ .usb2_com_pre_param = &uplcom_pre_param,
+ .usb2_com_start_read = &uplcom_start_read,
+ .usb2_com_stop_read = &uplcom_stop_read,
+ .usb2_com_start_write = &uplcom_start_write,
+ .usb2_com_stop_write = &uplcom_stop_write,
+};
+
+#define USB_UPL(v,p,rl,rh,t) \
+ USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl), \
+ USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t)
+
+static const struct usb2_device_id uplcom_devs[] = {
+ /* Belkin F5U257 */
+ {USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)},
+ /* I/O DATA USB-RSAQ */
+ {USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)},
+ /* I/O DATA USB-RSAQ2 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)},
+ /* I/O DATA USB-RSAQ3 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)},
+ /* PLANEX USB-RS232 URS-03 */
+ {USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)},
+ /* TrendNet TU-S9 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)},
+ /* ST Lab USB-SERIAL-4 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)},
+ /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)},
+ /* TDK USB-PHS Adapter UHA6400 */
+ {USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)},
+ /* RATOC REX-USB60 */
+ {USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)},
+ /* ELECOM UC-SGT */
+ {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)},
+ {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)},
+ /* Sagem USB-Serial Controller */
+ {USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)},
+ /* Sony Ericsson USB Cable */
+ {USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)},
+ /* SOURCENEXT KeikaiDenwa 8 */
+ {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)},
+ /* SOURCENEXT KeikaiDenwa 8 with charger */
+ {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)},
+ /* HAL Corporation Crossam2+USB */
+ {USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)},
+ /* Sitecom USB to Serial */
+ {USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)},
+ /* Tripp-Lite U209-000-R */
+ {USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)},
+ {USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)},
+ /* Prolific Pharos */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)},
+ /* Willcom W-SIM */
+ {USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)},
+};
+
+static device_method_t uplcom_methods[] = {
+ DEVMETHOD(device_probe, uplcom_probe),
+ DEVMETHOD(device_attach, uplcom_attach),
+ DEVMETHOD(device_detach, uplcom_detach),
+ {0, 0}
+};
+
+static devclass_t uplcom_devclass;
+
+static driver_t uplcom_driver = {
+ .name = "uplcom",
+ .methods = uplcom_methods,
+ .size = sizeof(struct uplcom_softc),
+};
+
+DRIVER_MODULE(uplcom, ushub, 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);
+
+static int
+uplcom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_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 (usb2_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
+}
+
+static int
+uplcom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uplcom_softc *sc = device_get_softc(dev);
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ device_set_usb2_desc(dev);
+
+ DPRINTF("sc = %p\n", sc);
+
+ sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa);
+ sc->sc_udev = uaa->device;
+
+ DPRINTF("chiptype: %s\n",
+ (sc->sc_chiptype == TYPE_PL2303X) ?
+ "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 = usb2_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
+ if (iface) {
+ id = usb2_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;
+ usb2_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 = usb2_transfer_setup(uaa->device,
+ sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
+ UPLCOM_N_TRANSFER, sc, &Giant);
+ if (error) {
+ DPRINTF("one or more missing USB endpoints, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ error = uplcom_reset(sc, uaa->device);
+ if (error) {
+ device_printf(dev, "reset failed, error=%s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uplcom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ /*
+ * do the initialization during attach so that the system does not
+ * sleep during open:
+ */
+ if (sc->sc_chiptype == TYPE_PL2303X) {
+ if (uplcom_pl2303x_init(uaa->device)) {
+ device_printf(dev, "init failed!\n");
+ goto detach;
+ }
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static usb2_error_t
+uplcom_reset(struct uplcom_softc *sc, struct usb2_device *udev)
+{
+ struct usb2_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 (usb2_do_request(udev, &Giant, &req, NULL));
+}
+
+struct pl2303x_init {
+ uint8_t req_type;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+};
+
+static const struct pl2303x_init pl2303x[] = {
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0},
+};
+
+#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0]))
+
+static int
+uplcom_pl2303x_init(struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t buf[4];
+ uint8_t i;
+
+ for (i = 0; i != N_PL2302X_INIT; i++) {
+ req.bmRequestType = pl2303x[i].req_type;
+ req.bRequest = pl2303x[i].request;
+ USETW(req.wValue, pl2303x[i].value);
+ USETW(req.wIndex, pl2303x[i].index);
+ USETW(req.wLength, pl2303x[i].length);
+
+ err = usb2_do_request(udev, &Giant, &req, buf);
+ if (err) {
+ DPRINTF("error=%s\n", usb2_errstr(err));
+ return (EIO);
+ }
+ }
+ return (0);
+}
+
+static void
+uplcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uplcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uplcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_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);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static const int32_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 (sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
+
+static int
+uplcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ uint8_t i;
+
+ DPRINTF("\n");
+
+ /* check requested baud rate */
+
+ for (i = 0;; i++) {
+
+ if (i != N_UPLCOM_RATES) {
+ if (uplcom_rates[i] == t->c_ospeed) {
+ break;
+ }
+ } else {
+ DPRINTF("invalid baud rate (%d)\n", t->c_ospeed);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+static void
+uplcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_cdc_line_state ls;
+ struct usb2_device_request req;
+
+ DPRINTF("sc = %p\n", sc);
+
+ bzero(&ls, 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);
+
+ usb2_com_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_PL2303X)
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
+ else
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
+ USETW(req.wLength, 0);
+
+ usb2_com_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);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ }
+}
+
+static void
+uplcom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+}
+
+static void
+uplcom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+}
+
+static void
+uplcom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+}
+
+static void
+uplcom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+}
+
+static void
+uplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uplcom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uplcom_softc *sc = xfer->priv_sc;
+ uint8_t buf[9];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen = %u\n", xfer->actlen);
+
+ if (xfer->actlen >= 9) {
+
+ usb2_copy_out(xfer->frbuffers, 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;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uplcom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uplcom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UPLCOM_BULK_BUF_SIZE, &actlen)) {
+
+ DPRINTF("actlen = %d\n", actlen);
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uplcom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uplcom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/usb_serial.c b/sys/dev/usb/serial/usb_serial.c
new file mode 100644
index 0000000..38fd818
--- /dev/null
+++ b/sys/dev/usb/serial/usb_serial.c
@@ -0,0 +1,1127 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR usb2_com_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int usb2_com_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
+SYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW,
+ &usb2_com_debug, 0, "ucom debug level");
+#endif
+
+static usb2_proc_callback_t usb2_com_cfg_start_transfers;
+static usb2_proc_callback_t usb2_com_cfg_open;
+static usb2_proc_callback_t usb2_com_cfg_close;
+static usb2_proc_callback_t usb2_com_cfg_line_state;
+static usb2_proc_callback_t usb2_com_cfg_status_change;
+static usb2_proc_callback_t usb2_com_cfg_param;
+
+static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *);
+static void usb2_com_units_free(uint32_t, uint32_t);
+static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t);
+static void usb2_com_detach_tty(struct usb2_com_softc *);
+static void usb2_com_queue_command(struct usb2_com_softc *,
+ usb2_proc_callback_t *, struct termios *pt,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1);
+static void usb2_com_shutdown(struct usb2_com_softc *);
+static void usb2_com_break(struct usb2_com_softc *, uint8_t);
+static void usb2_com_dtr(struct usb2_com_softc *, uint8_t);
+static void usb2_com_rts(struct usb2_com_softc *, uint8_t);
+
+static tsw_open_t usb2_com_open;
+static tsw_close_t usb2_com_close;
+static tsw_ioctl_t usb2_com_ioctl;
+static tsw_modem_t usb2_com_modem;
+static tsw_param_t usb2_com_param;
+static tsw_outwakeup_t usb2_com_outwakeup;
+static tsw_free_t usb2_com_free;
+
+static struct ttydevsw usb2_com_class = {
+ .tsw_flags = TF_INITLOCK | TF_CALLOUT,
+ .tsw_open = usb2_com_open,
+ .tsw_close = usb2_com_close,
+ .tsw_outwakeup = usb2_com_outwakeup,
+ .tsw_ioctl = usb2_com_ioctl,
+ .tsw_param = usb2_com_param,
+ .tsw_modem = usb2_com_modem,
+ .tsw_free = usb2_com_free,
+};
+
+MODULE_DEPEND(ucom, usb, 1, 1, 1);
+MODULE_VERSION(ucom, 1);
+
+#define UCOM_UNIT_MAX 0x1000 /* exclusive */
+#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */
+
+static uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8];
+
+static uint8_t
+usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
+{
+ uint32_t n;
+ uint32_t o;
+ uint32_t x;
+ uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
+ uint8_t error = 1;
+
+ mtx_lock(&Giant);
+
+ for (n = 0; n < max; n += sub_units) {
+
+ /* check for free consecutive bits */
+
+ for (o = 0; o < sub_units; o++) {
+
+ x = n + o;
+
+ if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) {
+ goto skip;
+ }
+ }
+
+ /* allocate */
+
+ for (o = 0; o < sub_units; o++) {
+
+ x = n + o;
+
+ usb2_com_bitmap[x / 8] |= (1 << (x % 8));
+ }
+
+ error = 0;
+
+ break;
+
+skip: ;
+ }
+
+ mtx_unlock(&Giant);
+
+ /*
+ * Always set the variable pointed to by "p_root_unit" so that
+ * the compiler does not think that it is used uninitialised:
+ */
+ *p_root_unit = n;
+
+ return (error);
+}
+
+static void
+usb2_com_units_free(uint32_t root_unit, uint32_t sub_units)
+{
+ uint32_t x;
+
+ mtx_lock(&Giant);
+
+ while (sub_units--) {
+ x = root_unit + sub_units;
+ usb2_com_bitmap[x / 8] &= ~(1 << (x % 8));
+ }
+
+ mtx_unlock(&Giant);
+}
+
+/*
+ * "N" sub_units are setup at a time. All sub-units will
+ * be given sequential unit numbers. The number of
+ * sub-units can be used to differentiate among
+ * different types of devices.
+ *
+ * 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
+usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
+ uint32_t sub_units, void *parent,
+ const struct usb2_com_callback *callback, struct mtx *mtx)
+{
+ uint32_t n;
+ uint32_t root_unit;
+ int error = 0;
+
+ if ((sc == NULL) ||
+ (sub_units == 0) ||
+ (sub_units > UCOM_SUB_UNIT_MAX) ||
+ (callback == NULL)) {
+ return (EINVAL);
+ }
+
+ /* XXX unit management does not really belong here */
+ if (usb2_com_units_alloc(sub_units, &root_unit)) {
+ return (ENOMEM);
+ }
+
+ error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
+ if (error) {
+ usb2_com_units_free(root_unit, sub_units);
+ return (error);
+ }
+
+ for (n = 0; n != sub_units; n++, sc++) {
+ sc->sc_unit = root_unit + n;
+ sc->sc_local_unit = n;
+ sc->sc_super = ssc;
+ sc->sc_mtx = mtx;
+ sc->sc_parent = parent;
+ sc->sc_callback = callback;
+
+ error = usb2_com_attach_tty(sc, sub_units);
+ if (error) {
+ usb2_com_detach(ssc, sc - n, n);
+ usb2_com_units_free(root_unit + n, sub_units - n);
+ return (error);
+ }
+ sc->sc_flag |= UCOM_FLAG_ATTACHED;
+ }
+ return (0);
+}
+
+/*
+ * NOTE: the following function will do nothing if
+ * the structure pointed to by "ssc" and "sc" is zero.
+ */
+void
+usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
+ uint32_t sub_units)
+{
+ uint32_t n;
+
+ usb2_proc_drain(&ssc->sc_tq);
+
+ for (n = 0; n != sub_units; n++, sc++) {
+ if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
+
+ usb2_com_detach_tty(sc);
+
+ usb2_com_units_free(sc->sc_unit, 1);
+
+ /* avoid duplicate detach: */
+ sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
+ }
+ }
+ usb2_proc_free(&ssc->sc_tq);
+}
+
+static int
+usb2_com_attach_tty(struct usb2_com_softc *sc, uint32_t sub_units)
+{
+ struct tty *tp;
+ int error = 0;
+ char buf[32]; /* temporary TTY device name buffer */
+
+ tp = tty_alloc(&usb2_com_class, sc, sc->sc_mtx);
+ if (tp == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
+
+ buf[0] = 0; /* set some default value */
+
+ /* Check if the client has a custom TTY name */
+ if (sc->sc_callback->usb2_com_tty_name) {
+ sc->sc_callback->usb2_com_tty_name(sc, buf,
+ sizeof(buf), sc->sc_local_unit);
+ }
+ if (buf[0] == 0) {
+ /* Use default TTY name */
+ if (sub_units > 1) {
+ /* multiple modems in one */
+ if (snprintf(buf, sizeof(buf), "U%u.%u",
+ sc->sc_unit - sc->sc_local_unit,
+ sc->sc_local_unit)) {
+ /* ignore */
+ }
+ } else {
+ /* single modem */
+ if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
+ /* ignore */
+ }
+ }
+ }
+ tty_makedev(tp, NULL, "%s", buf);
+
+ sc->sc_tty = tp;
+
+ DPRINTF("ttycreate: %s\n", buf);
+ usb2_cv_init(&sc->sc_cv, "usb2_com");
+
+done:
+ return (error);
+}
+
+static void
+usb2_com_detach_tty(struct usb2_com_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
+
+ /* the config thread has been stopped when we get here */
+
+ mtx_lock(sc->sc_mtx);
+ sc->sc_flag |= UCOM_FLAG_GONE;
+ sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
+ UCOM_FLAG_LL_READY);
+ mtx_unlock(sc->sc_mtx);
+ if (tp) {
+ tty_lock(tp);
+
+ usb2_com_close(tp); /* close, if any */
+
+ tty_rel_gone(tp);
+
+ mtx_lock(sc->sc_mtx);
+ /* Wait for the callback after the TTY is torn down */
+ while (sc->sc_ttyfreed == 0)
+ usb2_cv_wait(&sc->sc_cv, sc->sc_mtx);
+ /*
+ * make sure that read and write transfers are stopped
+ */
+ if (sc->sc_callback->usb2_com_stop_read) {
+ (sc->sc_callback->usb2_com_stop_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_stop_write) {
+ (sc->sc_callback->usb2_com_stop_write) (sc);
+ }
+ mtx_unlock(sc->sc_mtx);
+ }
+ usb2_cv_destroy(&sc->sc_cv);
+}
+
+static void
+usb2_com_queue_command(struct usb2_com_softc *sc,
+ usb2_proc_callback_t *fn, struct termios *pt,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct usb2_com_super_softc *ssc = sc->sc_super;
+ struct usb2_com_param_task *task;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (usb2_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 usb2_com_param_task *)
+ usb2_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 == usb2_com_cfg_close)
+ usb2_proc_mwait(&ssc->sc_tq, t0, t1);
+
+}
+
+static void
+usb2_com_shutdown(struct usb2_com_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ DPRINTF("\n");
+
+ /*
+ * Hang up if necessary:
+ */
+ if (tp->t_termios.c_cflag & HUPCL) {
+ usb2_com_modem(tp, 0, SER_DTR);
+ }
+}
+
+/*
+ * Return values:
+ * 0: normal
+ * else: taskqueue is draining or gone
+ */
+uint8_t
+usb2_com_cfg_is_gone(struct usb2_com_softc *sc)
+{
+ struct usb2_com_super_softc *ssc = sc->sc_super;
+
+ return (usb2_proc_is_gone(&ssc->sc_tq));
+}
+
+static void
+usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_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;
+ }
+ sc->sc_flag |= UCOM_FLAG_GP_DATA;
+
+ if (sc->sc_callback->usb2_com_start_read) {
+ (sc->sc_callback->usb2_com_start_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_start_write) {
+ (sc->sc_callback->usb2_com_start_write) (sc);
+ }
+}
+
+static void
+usb2_com_start_transfers(struct usb2_com_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->usb2_com_start_read) {
+ (sc->sc_callback->usb2_com_start_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_start_write) {
+ (sc->sc_callback->usb2_com_start_write) (sc);
+ }
+}
+
+static void
+usb2_com_cfg_open(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_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->usb2_com_cfg_open) {
+ (sc->sc_callback->usb2_com_cfg_open) (sc);
+
+ /* wait a little */
+ usb2_pause_mtx(sc->sc_mtx, hz / 10);
+ }
+ }
+}
+
+static int
+usb2_com_open(struct tty *tp)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ int error;
+
+ mtx_assert(sc->sc_mtx, 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->usb2_com_pre_open) {
+ /*
+ * give the lower layer a chance to disallow TTY open, for
+ * example if the device is not present:
+ */
+ error = (sc->sc_callback->usb2_com_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;
+
+ usb2_com_queue_command(sc, usb2_com_cfg_open, NULL,
+ &sc->sc_open_task[0].hdr,
+ &sc->sc_open_task[1].hdr);
+
+ /* Queue transfer enable command last */
+ usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
+ &sc->sc_start_task[0].hdr,
+ &sc->sc_start_task[1].hdr);
+
+ usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
+
+ usb2_com_break(sc, 0);
+
+ usb2_com_status_change(sc);
+
+ return (0);
+}
+
+static void
+usb2_com_cfg_close(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+
+ DPRINTF("\n");
+
+ if (sc->sc_flag & UCOM_FLAG_LL_READY) {
+
+ sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
+ UCOM_FLAG_GP_DATA);
+
+ if (sc->sc_callback->usb2_com_cfg_close) {
+ (sc->sc_callback->usb2_com_cfg_close) (sc);
+ }
+ } else {
+ /* already closed */
+ }
+}
+
+static void
+usb2_com_close(struct tty *tp)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ DPRINTF("tp=%p\n", tp);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ DPRINTF("tp=%p already closed\n", tp);
+ return;
+ }
+ usb2_com_shutdown(sc);
+
+ usb2_com_queue_command(sc, usb2_com_cfg_close, NULL,
+ &sc->sc_close_task[0].hdr,
+ &sc->sc_close_task[1].hdr);
+
+ sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
+ UCOM_FLAG_WR_START |
+ UCOM_FLAG_RTS_IFLOW);
+
+ if (sc->sc_callback->usb2_com_stop_read) {
+ (sc->sc_callback->usb2_com_stop_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_stop_write) {
+ (sc->sc_callback->usb2_com_stop_write) (sc);
+ }
+}
+
+static int
+usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ int error;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return (EIO);
+ }
+ DPRINTF("cmd = 0x%08lx\n", cmd);
+
+ switch (cmd) {
+ case TIOCSBRK:
+ usb2_com_break(sc, 1);
+ error = 0;
+ break;
+ case TIOCCBRK:
+ usb2_com_break(sc, 0);
+ error = 0;
+ break;
+ default:
+ if (sc->sc_callback->usb2_com_ioctl) {
+ error = (sc->sc_callback->usb2_com_ioctl)
+ (sc, cmd, data, 0, td);
+ } else {
+ error = ENOIOCTL;
+ }
+ break;
+ }
+ return (error);
+}
+
+static int
+usb2_com_modem(struct tty *tp, int sigon, int sigoff)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ uint8_t onoff;
+
+ mtx_assert(sc->sc_mtx, 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;
+ usb2_com_dtr(sc, onoff);
+
+ onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
+ usb2_com_rts(sc, onoff);
+
+ return (0);
+}
+
+static void
+usb2_com_cfg_line_state(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_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->usb2_com_cfg_set_dtr)
+ mask |= UCOM_LS_DTR;
+ if (sc->sc_callback->usb2_com_cfg_set_rts)
+ mask |= UCOM_LS_RTS;
+ if (sc->sc_callback->usb2_com_cfg_set_break)
+ mask |= UCOM_LS_BREAK;
+
+ /* 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 loose any levels */
+ if (notch_bits & UCOM_LS_DTR)
+ sc->sc_callback->usb2_com_cfg_set_dtr(sc,
+ (prev_value & UCOM_LS_DTR) ? 1 : 0);
+ if (notch_bits & UCOM_LS_RTS)
+ sc->sc_callback->usb2_com_cfg_set_rts(sc,
+ (prev_value & UCOM_LS_RTS) ? 1 : 0);
+ if (notch_bits & UCOM_LS_BREAK)
+ sc->sc_callback->usb2_com_cfg_set_break(sc,
+ (prev_value & UCOM_LS_BREAK) ? 1 : 0);
+
+ /* set last value */
+ if (any_bits & UCOM_LS_DTR)
+ sc->sc_callback->usb2_com_cfg_set_dtr(sc,
+ (last_value & UCOM_LS_DTR) ? 1 : 0);
+ if (any_bits & UCOM_LS_RTS)
+ sc->sc_callback->usb2_com_cfg_set_rts(sc,
+ (last_value & UCOM_LS_RTS) ? 1 : 0);
+ if (any_bits & UCOM_LS_BREAK)
+ sc->sc_callback->usb2_com_cfg_set_break(sc,
+ (last_value & UCOM_LS_BREAK) ? 1 : 0);
+}
+
+static void
+usb2_com_line_state(struct usb2_com_softc *sc,
+ uint8_t set_bits, uint8_t clear_bits)
+{
+ mtx_assert(sc->sc_mtx, 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 */
+ usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL,
+ &sc->sc_line_state_task[0].hdr,
+ &sc->sc_line_state_task[1].hdr);
+}
+
+static void
+usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
+{
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ usb2_com_line_state(sc, UCOM_LS_BREAK, 0);
+ else
+ usb2_com_line_state(sc, 0, UCOM_LS_BREAK);
+}
+
+static void
+usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
+{
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ usb2_com_line_state(sc, UCOM_LS_DTR, 0);
+ else
+ usb2_com_line_state(sc, 0, UCOM_LS_DTR);
+}
+
+static void
+usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
+{
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ usb2_com_line_state(sc, UCOM_LS_RTS, 0);
+ else
+ usb2_com_line_state(sc, 0, UCOM_LS_RTS);
+}
+
+static void
+usb2_com_cfg_status_change(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+ struct tty *tp;
+ uint8_t new_msr;
+ uint8_t new_lsr;
+ uint8_t onoff;
+
+ tp = sc->sc_tty;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+ return;
+ }
+ if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
+ return;
+ }
+ /* get status */
+
+ new_msr = 0;
+ new_lsr = 0;
+
+ (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ /* TTY device closed */
+ return;
+ }
+ onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
+
+ sc->sc_msr = new_msr;
+ sc->sc_lsr = new_lsr;
+
+ if (onoff) {
+
+ onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
+
+ DPRINTF("DCD changed to %d\n", onoff);
+
+ ttydisc_modem(tp, onoff);
+ }
+}
+
+void
+usb2_com_status_change(struct usb2_com_softc *sc)
+{
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return;
+ }
+ DPRINTF("\n");
+
+ usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL,
+ &sc->sc_status_task[0].hdr,
+ &sc->sc_status_task[1].hdr);
+}
+
+static void
+usb2_com_cfg_param(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_param_task *task =
+ (struct usb2_com_param_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+
+ if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+ return;
+ }
+ if (sc->sc_callback->usb2_com_cfg_param == NULL) {
+ return;
+ }
+
+ (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy);
+
+ /* wait a little */
+ usb2_pause_mtx(sc->sc_mtx, hz / 10);
+}
+
+static int
+usb2_com_param(struct tty *tp, struct termios *t)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ uint8_t opened;
+ int error;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ opened = 0;
+ error = 0;
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+
+ /* XXX the TTY layer should call "open()" first! */
+
+ error = usb2_com_open(tp);
+ if (error) {
+ goto done;
+ }
+ opened = 1;
+ }
+ DPRINTF("sc = %p\n", sc);
+
+ /* Check requested parameters. */
+ if (t->c_ospeed < 0) {
+ DPRINTF("negative ospeed\n");
+ error = EINVAL;
+ goto done;
+ }
+ if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
+ DPRINTF("mismatch ispeed and ospeed\n");
+ error = EINVAL;
+ goto done;
+ }
+ t->c_ispeed = t->c_ospeed;
+
+ if (sc->sc_callback->usb2_com_pre_param) {
+ /* Let the lower layer verify the parameters */
+ error = (sc->sc_callback->usb2_com_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 */
+ usb2_com_queue_command(sc, usb2_com_cfg_param, t,
+ &sc->sc_param_task[0].hdr,
+ &sc->sc_param_task[1].hdr);
+
+ /* Queue transfer enable command last */
+ usb2_com_queue_command(sc, usb2_com_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;
+ usb2_com_modem(tp, SER_RTS, 0);
+ }
+done:
+ if (error) {
+ if (opened) {
+ usb2_com_close(tp);
+ }
+ }
+ return (error);
+}
+
+static void
+usb2_com_outwakeup(struct tty *tp)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ DPRINTF("sc = %p\n", sc);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ /* The higher layer is not ready */
+ return;
+ }
+ sc->sc_flag |= UCOM_FLAG_WR_START;
+
+ usb2_com_start_transfers(sc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_com_get_data
+ *
+ * Return values:
+ * 0: No data is available.
+ * Else: Data is available.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
+ uint32_t offset, uint32_t len, uint32_t *actlen)
+{
+ struct usb2_page_search res;
+ struct tty *tp = sc->sc_tty;
+ uint32_t cnt;
+ uint32_t offset_orig;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
+ (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
+ (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
+ actlen[0] = 0;
+ return (0); /* multiport device polling */
+ }
+ offset_orig = offset;
+
+ while (len != 0) {
+
+ usb2_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
+usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
+ uint32_t offset, uint32_t len)
+{
+ struct usb2_page_search res;
+ struct tty *tp = sc->sc_tty;
+ char *buf;
+ uint32_t cnt;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
+ (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
+ return; /* multiport device polling */
+ }
+ if (len == 0)
+ return; /* no data */
+
+ /* set a flag to prevent recursation ? */
+
+ while (len > 0) {
+
+ usb2_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)) {
+ 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 (ttydisc_rint(tp, buf[cnt], 0) == -1) {
+ /* XXX what should we do? */
+
+ DPRINTF("tp=%p, lost %d "
+ "chars\n", tp, res.length - cnt);
+ break;
+ }
+ }
+ }
+ ttydisc_rint_done(tp);
+}
+
+static void
+usb2_com_free(void *xsc)
+{
+ struct usb2_com_softc *sc = xsc;
+
+ mtx_lock(sc->sc_mtx);
+ sc->sc_ttyfreed = 1;
+ usb2_cv_signal(&sc->sc_cv);
+ mtx_unlock(sc->sc_mtx);
+}
diff --git a/sys/dev/usb/serial/usb_serial.h b/sys/dev/usb/serial/usb_serial.h
new file mode 100644
index 0000000..c7d57a0
--- /dev/null
+++ b/sys/dev/usb/serial/usb_serial.h
@@ -0,0 +1,198 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 _USB2_SERIAL_H_
+#define _USB2_SERIAL_H_
+
+#include <sys/tty.h>
+#include <sys/serial.h>
+#include <sys/fcntl.h>
+#include <sys/termios.h>
+
+/* Module interface related macros */
+#define UCOM_MODVER 1
+
+#define UCOM_MINVER 1
+#define UCOM_PREFVER UCOM_MODVER
+#define UCOM_MAXVER 1
+
+struct usb2_device;
+struct usb2_com_softc;
+struct usb2_device_request;
+struct thread;
+
+/*
+ * NOTE: There is no guarantee that "usb2_com_cfg_close()" will
+ * be called after "usb2_com_cfg_open()" if the device is detached
+ * while it is open!
+ */
+struct usb2_com_callback {
+ void (*usb2_com_cfg_get_status) (struct usb2_com_softc *, uint8_t *plsr, uint8_t *pmsr);
+ void (*usb2_com_cfg_set_dtr) (struct usb2_com_softc *, uint8_t);
+ void (*usb2_com_cfg_set_rts) (struct usb2_com_softc *, uint8_t);
+ void (*usb2_com_cfg_set_break) (struct usb2_com_softc *, uint8_t);
+ void (*usb2_com_cfg_param) (struct usb2_com_softc *, struct termios *);
+ void (*usb2_com_cfg_open) (struct usb2_com_softc *);
+ void (*usb2_com_cfg_close) (struct usb2_com_softc *);
+ int (*usb2_com_pre_open) (struct usb2_com_softc *);
+ int (*usb2_com_pre_param) (struct usb2_com_softc *, struct termios *);
+ int (*usb2_com_ioctl) (struct usb2_com_softc *, uint32_t, caddr_t, int, struct thread *);
+ void (*usb2_com_start_read) (struct usb2_com_softc *);
+ void (*usb2_com_stop_read) (struct usb2_com_softc *);
+ void (*usb2_com_start_write) (struct usb2_com_softc *);
+ void (*usb2_com_stop_write) (struct usb2_com_softc *);
+ void (*usb2_com_tty_name) (struct usb2_com_softc *, char *pbuf, uint16_t buflen, uint16_t local_subunit);
+};
+
+/* 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 usb2_com_cfg_task {
+ struct usb2_proc_msg hdr;
+ struct usb2_com_softc *sc;
+};
+
+struct usb2_com_param_task {
+ struct usb2_proc_msg hdr;
+ struct usb2_com_softc *sc;
+ struct termios termios_copy;
+};
+
+struct usb2_com_super_softc {
+ struct usb2_process sc_tq;
+};
+
+struct usb2_com_softc {
+ /*
+ * NOTE: To avoid loosing 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 usb2_com_cfg_task sc_start_task[2];
+ struct usb2_com_cfg_task sc_open_task[2];
+ struct usb2_com_cfg_task sc_close_task[2];
+ struct usb2_com_cfg_task sc_line_state_task[2];
+ struct usb2_com_cfg_task sc_status_task[2];
+ struct usb2_com_param_task sc_param_task[2];
+ struct cv sc_cv;
+ const struct usb2_com_callback *sc_callback;
+ struct usb2_com_super_softc *sc_super;
+ struct tty *sc_tty;
+ struct mtx *sc_mtx;
+ void *sc_parent;
+ uint32_t sc_unit;
+ uint32_t sc_local_unit;
+ uint16_t sc_portno;
+ uint8_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_WR_START 0x10 /* set if write start was issued */
+#define UCOM_FLAG_LL_READY 0x20 /* set if low layer is ready */
+#define UCOM_FLAG_HL_READY 0x40 /* set if high layer is ready */
+ uint8_t sc_lsr;
+ uint8_t sc_msr;
+ uint8_t sc_mcr;
+ uint8_t sc_ttyfreed; /* set when TTY has been freed */
+ /* 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 usb2_com_cfg_do_request(udev,com,req,ptr,flags,timo) \
+ usb2_do_request_proc(udev,&(com)->sc_super->sc_tq,req,ptr,flags,NULL,timo)
+
+int usb2_com_attach(struct usb2_com_super_softc *,
+ struct usb2_com_softc *, uint32_t, void *,
+ const struct usb2_com_callback *callback, struct mtx *);
+void usb2_com_detach(struct usb2_com_super_softc *,
+ struct usb2_com_softc *, uint32_t);
+void usb2_com_status_change(struct usb2_com_softc *);
+uint8_t usb2_com_get_data(struct usb2_com_softc *, struct usb2_page_cache *,
+ uint32_t, uint32_t, uint32_t *);
+void usb2_com_put_data(struct usb2_com_softc *, struct usb2_page_cache *,
+ uint32_t, uint32_t);
+uint8_t usb2_com_cfg_is_gone(struct usb2_com_softc *);
+#endif /* _USB2_SERIAL_H_ */
diff --git a/sys/dev/usb/serial/uslcom.c b/sys/dev/usb/serial/uslcom.c
new file mode 100644
index 0000000..3145151
--- /dev/null
+++ b/sys/dev/usb/serial/uslcom.c
@@ -0,0 +1,539 @@
+/* $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.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR uslcom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uslcom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
+SYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW,
+ &uslcom_debug, 0, "Debug level");
+#endif
+
+#define USLCOM_BULK_BUF_SIZE 1024
+#define USLCOM_CONFIG_INDEX 0
+#define USLCOM_IFACE_INDEX 0
+
+#define USLCOM_SET_DATA_BITS(x) ((x) << 8)
+
+#define USLCOM_WRITE 0x41
+#define USLCOM_READ 0xc1
+
+#define USLCOM_UART 0x00
+#define USLCOM_BAUD_RATE 0x01
+#define USLCOM_DATA 0x03
+#define USLCOM_BREAK 0x05
+#define USLCOM_CTRL 0x07
+
+#define USLCOM_UART_DISABLE 0x00
+#define USLCOM_UART_ENABLE 0x01
+
+#define USLCOM_CTRL_DTR_ON 0x0001
+#define USLCOM_CTRL_DTR_SET 0x0100
+#define USLCOM_CTRL_RTS_ON 0x0002
+#define USLCOM_CTRL_RTS_SET 0x0200
+#define USLCOM_CTRL_CTS 0x0010
+#define USLCOM_CTRL_DSR 0x0020
+#define USLCOM_CTRL_DCD 0x0080
+
+#define USLCOM_BAUD_REF 0x384000
+
+#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_PORT_NO 0xFFFF /* XXX think this should be 0 --hps */
+
+#define USLCOM_BREAK_OFF 0x00
+#define USLCOM_BREAK_ON 0x01
+
+enum {
+ USLCOM_BULK_DT_WR,
+ USLCOM_BULK_DT_RD,
+ USLCOM_N_TRANSFER,
+};
+
+struct uslcom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_msr;
+ uint8_t sc_lsr;
+};
+
+static device_probe_t uslcom_probe;
+static device_attach_t uslcom_attach;
+static device_detach_t uslcom_detach;
+
+static usb2_callback_t uslcom_write_callback;
+static usb2_callback_t uslcom_read_callback;
+
+static void uslcom_open(struct usb2_com_softc *);
+static void uslcom_close(struct usb2_com_softc *);
+static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uslcom_set_rts(struct usb2_com_softc *, uint8_t);
+static void uslcom_set_break(struct usb2_com_softc *, uint8_t);
+static int uslcom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uslcom_param(struct usb2_com_softc *, struct termios *);
+static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *);
+static void uslcom_start_read(struct usb2_com_softc *);
+static void uslcom_stop_read(struct usb2_com_softc *);
+static void uslcom_start_write(struct usb2_com_softc *);
+static void uslcom_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = {
+
+ [USLCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = USLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uslcom_write_callback,
+ },
+
+ [USLCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = USLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uslcom_read_callback,
+ },
+};
+
+struct usb2_com_callback uslcom_callback = {
+ .usb2_com_cfg_open = &uslcom_open,
+ .usb2_com_cfg_close = &uslcom_close,
+ .usb2_com_cfg_get_status = &uslcom_get_status,
+ .usb2_com_cfg_set_dtr = &uslcom_set_dtr,
+ .usb2_com_cfg_set_rts = &uslcom_set_rts,
+ .usb2_com_cfg_set_break = &uslcom_set_break,
+ .usb2_com_cfg_param = &uslcom_param,
+ .usb2_com_pre_param = &uslcom_pre_param,
+ .usb2_com_start_read = &uslcom_start_read,
+ .usb2_com_stop_read = &uslcom_stop_read,
+ .usb2_com_start_write = &uslcom_start_write,
+ .usb2_com_stop_write = &uslcom_stop_write,
+};
+
+static const struct usb2_device_id uslcom_devs[] = {
+ { USB_VPI(USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, 0) },
+ { USB_VPI(USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) },
+ { USB_VPI(USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, 0) },
+ { USB_VPI(USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, 0) },
+ { USB_VPI(USB_VENDOR_USI, USB_PRODUCT_USI_MC60, 0) },
+};
+
+static device_method_t uslcom_methods[] = {
+ DEVMETHOD(device_probe, uslcom_probe),
+ DEVMETHOD(device_attach, uslcom_attach),
+ DEVMETHOD(device_detach, uslcom_detach),
+ {0, 0}
+};
+
+static devclass_t uslcom_devclass;
+
+static driver_t uslcom_driver = {
+ .name = "uslcom",
+ .methods = uslcom_methods,
+ .size = sizeof(struct uslcom_softc),
+};
+
+DRIVER_MODULE(uslcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0);
+MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uslcom, usb, 1, 1, 1);
+MODULE_VERSION(uslcom, 1);
+
+static int
+uslcom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
+}
+
+static int
+uslcom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uslcom_softc *sc = device_get_softc(dev);
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ error = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
+ USLCOM_N_TRANSFER, sc, &Giant);
+ if (error) {
+ DPRINTF("one or more missing USB endpoints, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uslcom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uslcom_open(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_UART;
+ USETW(req.wValue, USLCOM_UART_ENABLE);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("UART enable failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_close(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_UART;
+ USETW(req.wValue, USLCOM_UART_DISABLE);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("UART disable failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t ctl;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
+ ctl |= USLCOM_CTRL_DTR_SET;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_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 usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t ctl;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
+ ctl |= USLCOM_CTRL_RTS_SET;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_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 usb2_com_softc *ucom, struct termios *t)
+{
+ if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
+ return (EINVAL);
+ return (0);
+}
+
+static void
+uslcom_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t data;
+
+ DPRINTF("\n");
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_BAUD_RATE;
+ USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 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_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Set format failed (ignored)\n");
+ }
+ return;
+}
+
+static void
+uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_BREAK;
+ USETW(req.wValue, brk);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Set BREAK failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uslcom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ USLCOM_BULK_BUF_SIZE, &actlen)) {
+
+ DPRINTF("actlen = %d\n", actlen);
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uslcom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uslcom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uslcom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+}
+
+static void
+uslcom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+}
+
+static void
+uslcom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+}
+
+static void
+uslcom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/serial/uvisor.c b/sys/dev/usb/serial/uvisor.c
new file mode 100644
index 0000000..854f972
--- /dev/null
+++ b/sys/dev/usb/serial/uvisor.c
@@ -0,0 +1,610 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uvisor_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uvisor_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor");
+SYSCTL_INT(_hw_usb2_uvisor, OID_AUTO, debug, CTLFLAG_RW,
+ &uvisor_debug, 0, "Debug level");
+#endif
+
+#define UVISOR_CONFIG_INDEX 0
+#define UVISOR_IFACE_INDEX 0
+#define UVISOR_BUFSIZE 1024 /* bytes */
+
+/* From the Linux driver */
+/*
+ * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered 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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UVISOR_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_callback_t uvisor_write_callback;
+static usb2_callback_t uvisor_read_callback;
+
+static usb2_error_t uvisor_init(struct uvisor_softc *, struct usb2_device *,
+ struct usb2_config *);
+static void uvisor_cfg_open(struct usb2_com_softc *);
+static void uvisor_cfg_close(struct usb2_com_softc *);
+static void uvisor_start_read(struct usb2_com_softc *);
+static void uvisor_stop_read(struct usb2_com_softc *);
+static void uvisor_start_write(struct usb2_com_softc *);
+static void uvisor_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config uvisor_config[UVISOR_N_TRANSFER] = {
+
+ [UVISOR_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UVISOR_BUFSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uvisor_write_callback,
+ },
+
+ [UVISOR_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UVISOR_BUFSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uvisor_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uvisor_callback = {
+ .usb2_com_cfg_open = &uvisor_cfg_open,
+ .usb2_com_cfg_close = &uvisor_cfg_close,
+ .usb2_com_start_read = &uvisor_start_read,
+ .usb2_com_stop_read = &uvisor_stop_read,
+ .usb2_com_start_write = &uvisor_start_write,
+ .usb2_com_stop_write = &uvisor_stop_write,
+};
+
+static device_method_t uvisor_methods[] = {
+ DEVMETHOD(device_probe, uvisor_probe),
+ DEVMETHOD(device_attach, uvisor_attach),
+ DEVMETHOD(device_detach, uvisor_detach),
+ {0, 0}
+};
+
+static devclass_t uvisor_devclass;
+
+static driver_t uvisor_driver = {
+ .name = "uvisor",
+ .methods = uvisor_methods,
+ .size = sizeof(struct uvisor_softc),
+};
+
+DRIVER_MODULE(uvisor, ushub, uvisor_driver, uvisor_devclass, NULL, 0);
+MODULE_DEPEND(uvisor, ucom, 1, 1, 1);
+MODULE_DEPEND(uvisor, usb, 1, 1, 1);
+
+static const struct usb2_device_id uvisor_devs[] = {
+ {USB_VPI(USB_VENDOR_ACEECA, USB_PRODUCT_ACEECA_MEZ1000, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_GARMIN, USB_PRODUCT_GARMIN_IQUE_3600, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_FOSSIL, USB_PRODUCT_FOSSIL_WRISTPDA, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR, UVISOR_FLAG_VISOR)},
+ {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M500, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M505, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M515, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_I705, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M125, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M130, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_I500, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40, 0)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35, UVISOR_FLAG_PALM35)},
+/* {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25, UVISOR_FLAG_PALM4 )}, */
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TJ37, UVISOR_FLAG_PALM4)},
+/* {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TH55, UVISOR_FLAG_PALM4 )}, See PR 80935 */
+ {USB_VPI(USB_VENDOR_TAPWAVE, USB_PRODUCT_TAPWAVE_ZODIAC, UVISOR_FLAG_PALM4)},
+};
+
+static int
+uvisor_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_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 (usb2_lookup_id_by_uaa(uvisor_devs, sizeof(uvisor_devs), uaa));
+}
+
+static int
+uvisor_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uvisor_softc *sc = device_get_softc(dev);
+ struct usb2_config uvisor_config_copy[UVISOR_N_TRANSFER];
+ int error;
+
+ DPRINTF("sc=%p\n", sc);
+ bcopy(uvisor_config, uvisor_config_copy,
+ sizeof(uvisor_config_copy));
+ device_set_usb2_desc(dev);
+
+ 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",
+ usb2_errstr(error));
+ goto detach;
+ }
+ error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, uvisor_config_copy, UVISOR_N_TRANSFER,
+ sc, &Giant);
+ if (error) {
+ DPRINTF("could not allocate all pipes\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uvisor_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ 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);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UVISOR_N_TRANSFER);
+
+ return (0);
+}
+
+static usb2_error_t
+uvisor_init(struct uvisor_softc *sc, struct usb2_device *udev, struct usb2_config *config)
+{
+ usb2_error_t err = 0;
+ struct usb2_device_request req;
+ struct uvisor_connection_info coninfo;
+ struct uvisor_palm_connection_info pconinfo;
+ uint16_t actlen;
+ uWord wAvail;
+ 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 = usb2_do_request_flags
+ (udev, &Giant, &req, &coninfo, USB_SHORT_XFER_OK,
+ &actlen, USB_DEFAULT_TIMEOUT);
+
+ if (err) {
+ goto done;
+ }
+ }
+#if 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 = usb2_do_request_flags
+ (udev, &Giant, &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 = usb2_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 = usb2_do_request(udev, &Giant, &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 = usb2_do_request(udev, &Giant, &req, buffer);
+ if (err) {
+ goto done;
+ }
+ }
+ 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 = usb2_do_request(udev, &Giant, &req, &wAvail);
+ if (err) {
+ goto done;
+ }
+ DPRINTF("avail=%d\n", UGETW(wAvail));
+
+ DPRINTF("done\n");
+done:
+ return (err);
+}
+
+static void
+uvisor_cfg_open(struct usb2_com_softc *ucom)
+{
+ return;
+}
+
+static void
+uvisor_cfg_close(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+ uint8_t buffer[UVISOR_CONNECTION_INFO_SIZE];
+ struct usb2_device_request req;
+ usb2_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 = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, buffer, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "close notification failed, error=%s\n",
+ usb2_errstr(err));
+ }
+}
+
+static void
+uvisor_start_read(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+}
+
+static void
+uvisor_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+}
+
+static void
+uvisor_start_write(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+}
+
+static void
+uvisor_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+}
+
+static void
+uvisor_write_callback(struct usb2_xfer *xfer)
+{
+ struct uvisor_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UVISOR_BUFSIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvisor_read_callback(struct usb2_xfer *xfer)
+{
+ struct uvisor_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/uvscom.c b/sys/dev/usb/serial/uvscom.c
new file mode 100644
index 0000000..15a9eba
--- /dev/null
+++ b/sys/dev/usb/serial/uvscom.c
@@ -0,0 +1,707 @@
+/* $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 "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR uvscom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uvscom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
+SYSCTL_INT(_hw_usb2_uvscom, OID_AUTO, debug, CTLFLAG_RW,
+ &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 usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UVSCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ 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 usb2_callback_t uvscom_write_callback;
+static usb2_callback_t uvscom_read_callback;
+static usb2_callback_t uvscom_intr_callback;
+
+static void uvscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uvscom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uvscom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int uvscom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uvscom_cfg_param(struct usb2_com_softc *, struct termios *);
+static int uvscom_pre_open(struct usb2_com_softc *);
+static void uvscom_cfg_open(struct usb2_com_softc *);
+static void uvscom_cfg_close(struct usb2_com_softc *);
+static void uvscom_start_read(struct usb2_com_softc *);
+static void uvscom_stop_read(struct usb2_com_softc *);
+static void uvscom_start_write(struct usb2_com_softc *);
+static void uvscom_stop_write(struct usb2_com_softc *);
+static void uvscom_cfg_get_status(struct usb2_com_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 const struct usb2_config uvscom_config[UVSCOM_N_TRANSFER] = {
+
+ [UVSCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UVSCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uvscom_write_callback,
+ },
+
+ [UVSCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UVSCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uvscom_read_callback,
+ },
+
+ [UVSCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uvscom_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback uvscom_callback = {
+ .usb2_com_cfg_get_status = &uvscom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uvscom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uvscom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uvscom_cfg_set_break,
+ .usb2_com_cfg_param = &uvscom_cfg_param,
+ .usb2_com_cfg_open = &uvscom_cfg_open,
+ .usb2_com_cfg_close = &uvscom_cfg_close,
+ .usb2_com_pre_open = &uvscom_pre_open,
+ .usb2_com_pre_param = &uvscom_pre_param,
+ .usb2_com_start_read = &uvscom_start_read,
+ .usb2_com_stop_read = &uvscom_stop_read,
+ .usb2_com_start_write = &uvscom_start_write,
+ .usb2_com_stop_write = &uvscom_stop_write,
+};
+
+static const struct usb2_device_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),
+ {0, 0}
+};
+
+static devclass_t uvscom_devclass;
+
+static driver_t uvscom_driver = {
+ .name = "uvscom",
+ .methods = uvscom_methods,
+ .size = sizeof(struct uvscom_softc),
+};
+
+DRIVER_MODULE(uvscom, ushub, 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);
+
+static int
+uvscom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_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 (usb2_lookup_id_by_uaa(uvscom_devs, sizeof(uvscom_devs), uaa));
+}
+
+static int
+uvscom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uvscom_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ 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 = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ DPRINTF("could not allocate all USB transfers!\n");
+ goto detach;
+ }
+ sc->sc_line = UVSCOM_LINE_INIT;
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uvscom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ /* start interrupt pipe */
+ mtx_lock(&Giant);
+ usb2_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
+ mtx_unlock(&Giant);
+
+ 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]) {
+ usb2_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
+ }
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uvscom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uvscom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UVSCOM_BULK_BUF_SIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvscom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uvscom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvscom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uvscom_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen >= 2) {
+
+ usb2_copy_out(xfer->frbuffers, 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!
+ */
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvscom_cfg_set_dtr(struct usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_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 usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+}
+
+static void
+uvscom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+}
+
+static void
+uvscom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+}
+
+static void
+uvscom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+}
+
+static void
+uvscom_cfg_get_status(struct usb2_com_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 usb2_device_request req;
+ usb2_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 = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
+
+static uint16_t
+uvscom_cfg_read_status(struct uvscom_softc *sc)
+{
+ struct usb2_device_request req;
+ usb2_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 = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, data, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+ return (data[0] | (data[1] << 8));
+}
OpenPOWER on IntegriCloud