diff options
Diffstat (limited to 'sys/dev/usb/serial')
-rw-r--r-- | sys/dev/usb/serial/u3g.c | 583 | ||||
-rw-r--r-- | sys/dev/usb/serial/uark.c | 407 | ||||
-rw-r--r-- | sys/dev/usb/serial/ubsa.c | 634 | ||||
-rw-r--r-- | sys/dev/usb/serial/ubser.c | 518 | ||||
-rw-r--r-- | sys/dev/usb/serial/uchcom.c | 883 | ||||
-rw-r--r-- | sys/dev/usb/serial/ucycom.c | 564 | ||||
-rw-r--r-- | sys/dev/usb/serial/ufoma.c | 1212 | ||||
-rw-r--r-- | sys/dev/usb/serial/uftdi.c | 784 | ||||
-rw-r--r-- | sys/dev/usb/serial/uftdi_reg.h | 340 | ||||
-rw-r--r-- | sys/dev/usb/serial/ugensa.c | 352 | ||||
-rw-r--r-- | sys/dev/usb/serial/uipaq.c | 1314 | ||||
-rw-r--r-- | sys/dev/usb/serial/ulpt.c | 726 | ||||
-rw-r--r-- | sys/dev/usb/serial/umct.c | 579 | ||||
-rw-r--r-- | sys/dev/usb/serial/umodem.c | 788 | ||||
-rw-r--r-- | sys/dev/usb/serial/umoscom.c | 672 | ||||
-rw-r--r-- | sys/dev/usb/serial/uplcom.c | 831 | ||||
-rw-r--r-- | sys/dev/usb/serial/usb_serial.c | 1127 | ||||
-rw-r--r-- | sys/dev/usb/serial/usb_serial.h | 198 | ||||
-rw-r--r-- | sys/dev/usb/serial/uslcom.c | 539 | ||||
-rw-r--r-- | sys/dev/usb/serial/uvisor.c | 610 | ||||
-rw-r--r-- | sys/dev/usb/serial/uvscom.c | 707 |
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 = ÷rs[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)); +} |