usb - Update bus/u4b
authorMatthew Dillon <dillon@apollo.backplane.com>
Thu, 12 Mar 2015 20:50:38 +0000 (13:50 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Thu, 12 Mar 2015 20:50:38 +0000 (13:50 -0700)
* Update bus/u4b from FreeBSD to commit 3121e258c76aa, 10 March 2015,
  with the following commit message:

      Lock softc before clearing bits.

* Some bits not updated.  Some changes around the MSI handling work
  differently in DFly so I punted on that.  And the serial/tty in FreeBSD
  is a bit different, particular this 'pps' stuff.

* Numerous bits of code currently conditionalized out use ABI features
  from FreeBSD, particularly RWTUN, which we do not yet have.  Currently
  non-critical, we can fix these as the related code gets used (if the
  related code gets used).

Reviewed-by: Markus Pfeiffer
68 files changed:
sys/bus/u4b/controller/dwc_otg_fdt.h [new file with mode: 0644]
sys/bus/u4b/controller/ohci_pci.c
sys/bus/u4b/controller/uhci_pci.c
sys/bus/u4b/controller/usb_controller.c
sys/bus/u4b/controller/xhci.c
sys/bus/u4b/controller/xhci.h
sys/bus/u4b/controller/xhci_pci.c
sys/bus/u4b/input/atp.c
sys/bus/u4b/input/uep.c
sys/bus/u4b/input/uhid.c
sys/bus/u4b/input/ums.c
sys/bus/u4b/misc/udbp.c
sys/bus/u4b/net/if_aue.c
sys/bus/u4b/net/if_axe.c
sys/bus/u4b/net/if_axge.c
sys/bus/u4b/net/if_cdce.c
sys/bus/u4b/net/if_cdcereg.h
sys/bus/u4b/net/if_cue.c
sys/bus/u4b/net/if_ipheth.c
sys/bus/u4b/net/if_kue.c
sys/bus/u4b/net/if_mos.c
sys/bus/u4b/net/if_rue.c
sys/bus/u4b/net/if_udav.c
sys/bus/u4b/net/if_urndis.c
sys/bus/u4b/net/if_usie.c
sys/bus/u4b/net/uhso.c
sys/bus/u4b/quirk/usb_quirk.c
sys/bus/u4b/serial/u3g.c
sys/bus/u4b/serial/ubsa.c
sys/bus/u4b/serial/ubser.c
sys/bus/u4b/serial/uchcom.c
sys/bus/u4b/serial/uftdi.c
sys/bus/u4b/serial/ulpt.c
sys/bus/u4b/serial/umcs.c
sys/bus/u4b/serial/umodem.c
sys/bus/u4b/serial/umoscom.c
sys/bus/u4b/serial/uplcom.c
sys/bus/u4b/serial/usb_serial.c
sys/bus/u4b/serial/usb_serial.h
sys/bus/u4b/serial/uslcom.c
sys/bus/u4b/serial/uvisor.c
sys/bus/u4b/serial/uvscom.c
sys/bus/u4b/storage/urio.c
sys/bus/u4b/storage/ustorage_fs.c
sys/bus/u4b/template/Makefile
sys/bus/u4b/template/usb_template.c
sys/bus/u4b/template/usb_template.h
sys/bus/u4b/template/usb_template_phone.c [new file with mode: 0644]
sys/bus/u4b/template/usb_template_serialnet.c [new file with mode: 0644]
sys/bus/u4b/usb_bus.h
sys/bus/u4b/usb_dev.c
sys/bus/u4b/usb_device.c
sys/bus/u4b/usb_device.h
sys/bus/u4b/usb_freebsd.h
sys/bus/u4b/usb_hub.c
sys/bus/u4b/usb_ioctl.h
sys/bus/u4b/usb_msctest.c
sys/bus/u4b/usb_request.c
sys/bus/u4b/usb_transfer.c
sys/bus/u4b/usbdevs
sys/bus/u4b/video/udl.c [new file with mode: 0644]
sys/bus/u4b/video/udl.h [new file with mode: 0644]
sys/bus/u4b/wlan/if_rum.c
sys/bus/u4b/wlan/if_run.c
sys/bus/u4b/wlan/if_upgt.c
sys/bus/u4b/wlan/if_ural.c
sys/bus/u4b/wlan/if_urtwn.c
sys/bus/u4b/wlan/if_zyd.c

diff --git a/sys/bus/u4b/controller/dwc_otg_fdt.h b/sys/bus/u4b/controller/dwc_otg_fdt.h
new file mode 100644 (file)
index 0000000..d272fb8
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2012 Hans Petter Selasky. 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DWC_OTG_FDT_H_
+#define        _DWC_OTG_FDT_H_
+
+struct dwc_otg_fdt_softc {
+       struct dwc_otg_softc sc_otg;    /* must be first */
+};
+
+extern driver_t dwc_otg_driver;
+
+device_attach_t dwc_otg_attach;
+
+#endif
+/*-
+ * Copyright (c) 2012 Hans Petter Selasky. 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DWC_OTG_FDT_H_
+#define        _DWC_OTG_FDT_H_
+
+struct dwc_otg_fdt_softc {
+       struct dwc_otg_softc sc_otg;    /* must be first */
+};
+
+extern driver_t dwc_otg_driver;
+
+device_attach_t dwc_otg_attach;
+
+#endif
index 18e77da..8016a84 100644 (file)
@@ -169,6 +169,8 @@ ohci_pci_match(device_t self)
 
        case 0x0019106b:
                return ("Apple KeyLargo USB controller");
+       case 0x003f106b:
+               return ("Apple KeyLargo/Intrepid USB controller");
 
        default:
                break;
@@ -205,6 +207,7 @@ ohci_pci_attach(device_t self)
        sc->sc_bus.parent = self;
        sc->sc_bus.devices = sc->sc_devices;
        sc->sc_bus.devices_max = OHCI_MAX_DEVICES;
+       sc->sc_bus.dma_bits = 32;
 
        /* get all DMA memory */
        if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self),
index 0216a41..fe96e7c 100644 (file)
@@ -255,6 +255,7 @@ uhci_pci_attach(device_t self)
        sc->sc_bus.parent = self;
        sc->sc_bus.devices = sc->sc_devices;
        sc->sc_bus.devices_max = UHCI_MAX_DEVICES;
+       sc->sc_bus.dma_bits = 32;
 
        /* get all DMA memory */
        if (usb_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self),
index b160f46..9e96aa8 100644 (file)
@@ -53,6 +53,7 @@
 #include <bus/u4b/usb_busdma.h>
 #include <bus/u4b/usb_dynamic.h>
 #include <bus/u4b/usb_device.h>
+#include <bus/u4b/usb_dev.h>
 #include <bus/u4b/usb_hub.h>
 
 #include <bus/u4b/usb_controller.h>
@@ -77,7 +78,7 @@ static void   usb_attach_sub(device_t, struct usb_bus *);
 static int usb_ctrl_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller");
-SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0,
+SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RWTUN, &usb_ctrl_debug, 0,
     "Debug level");
 #endif
 
@@ -213,6 +214,11 @@ usb_detach(device_t dev)
        usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus),
            &bus->detach_msg[0], &bus->detach_msg[1]);
 
+#if USB_HAVE_UGEN
+       /* Wait for cleanup to complete */
+       usb_proc_mwait(USB_BUS_EXPLORE_PROC(bus),
+           &bus->cleanup_msg[0], &bus->cleanup_msg[1]);
+#endif
        USB_BUS_UNLOCK(bus);
 
 #if USB_HAVE_PER_BUS_PROCESS
@@ -616,6 +622,32 @@ usb_bus_shutdown(struct usb_proc_msg *pm)
        USB_BUS_LOCK(bus);
 }
 
+/*------------------------------------------------------------------------*
+ *     usb_bus_cleanup
+ *
+ * This function is used to cleanup leftover USB character devices.
+ *------------------------------------------------------------------------*/
+#if USB_HAVE_UGEN
+static void
+usb_bus_cleanup(struct usb_proc_msg *pm)
+{
+       struct usb_bus *bus;
+       struct usb_fs_privdata *pd;
+
+       bus = ((struct usb_bus_msg *)pm)->bus;
+
+       while ((pd = LIST_FIRST(&bus->pd_cleanup_list)) != NULL) {
+
+               LIST_REMOVE(pd, pd_next);
+               USB_BUS_UNLOCK(bus);
+
+               usb_destroy_dev_sync(pd);
+
+               USB_BUS_LOCK(bus);
+       }
+}
+#endif
+
 static void
 usb_power_wdog(void *arg)
 {
@@ -796,6 +828,14 @@ usb_attach_sub(device_t dev, struct usb_bus *bus)
        bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown;
        bus->shutdown_msg[1].bus = bus;
 
+#if USB_HAVE_UGEN
+       LIST_INIT(&bus->pd_cleanup_list);
+       bus->cleanup_msg[0].hdr.pm_callback = &usb_bus_cleanup;
+       bus->cleanup_msg[0].bus = bus;
+       bus->cleanup_msg[1].hdr.pm_callback = &usb_bus_cleanup;
+       bus->cleanup_msg[1].bus = bus;
+#endif
+
 #if USB_HAVE_PER_BUS_PROCESS
        /* Create USB explore and callback processes */
 
@@ -895,7 +935,7 @@ usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat,
 
 #if USB_HAVE_BUSDMA
        usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags,
-           dmat, &bus->bus_lock, NULL, 32, USB_BUS_DMA_TAG_MAX);
+           dmat, &bus->bus_lock, NULL, bus->dma_bits, USB_BUS_DMA_TAG_MAX);
 #endif
        if ((bus->devices_max > USB_MAX_DEVICES) ||
            (bus->devices_max < USB_MIN_DEVICES) ||
index a750cc0..3ab9a8e 100644 (file)
@@ -91,6 +91,7 @@ TUNABLE_INT("hw.usb.xhci.streams", &xhcistreams);
 static int xhcidebug = 0;
 static int xhciroute = 0;
 static int xhcipolling = 0;
+static int xhcidma32;
 
 SYSCTL_INT(_hw_usb_xhci, OID_AUTO, debug, CTLFLAG_RW,
     &xhcidebug, 0, "Debug level");
@@ -101,8 +102,14 @@ TUNABLE_INT("hw.usb.xhci.xhci_port_route", &xhciroute);
 SYSCTL_INT(_hw_usb_xhci, OID_AUTO, use_polling, CTLFLAG_RW,
     &xhcipolling, 0, "Set to enable software interrupt polling for XHCI controller");
 TUNABLE_INT("hw.usb.xhci.use_polling", &xhcipolling);
+
+SYSCTL_INT(_hw_usb_xhci, OID_AUTO, dma32, CTLFLAG_RW,
+    &xhcidma32, 0, "Set to only use 32-bit DMA for the XHCI controller");
+TUNABLE_INT("hw.usb.xhci.dma32", &xhcidma32);
+
 #else
 #define        xhciroute 0
+#define        xhcidma32 0
 #endif
 
 #define        XHCI_INTR_ENDPT 1
@@ -316,7 +323,7 @@ xhci_reset_command_queue_locked(struct xhci_softc *sc)
 
        usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
 
-       /* setup command ring control base address */
+       /* set up command ring control base address */
        addr = buf_res.physaddr;
        phwr = buf_res.buffer;
        addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0];
@@ -390,7 +397,7 @@ xhci_start_controller(struct xhci_softc *sc)
        if (sc->sc_noslot > XHCI_MAX_DEVICES)
                sc->sc_noslot = XHCI_MAX_DEVICES;
 
-       /* setup number of device slots */
+       /* set up number of device slots */
 
        DPRINTF("CONFIG=0x%08x -> 0x%08x\n",
            XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot);
@@ -423,7 +430,7 @@ xhci_start_controller(struct xhci_softc *sc)
        /* disable all device notifications */
        XWRITE4(sc, oper, XHCI_DNCTRL, 0);
 
-       /* setup device context base address */
+       /* set up device context base address */
        usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res);
        pdctxa = buf_res.buffer;
        memset(pdctxa, 0, sizeof(*pdctxa));
@@ -502,7 +509,7 @@ xhci_start_controller(struct xhci_softc *sc)
        temp |= XHCI_IMAN_INTR_ENA;
        XWRITE4(sc, runt, XHCI_IMAN(0), temp);
 
-       /* setup command ring control base address */
+       /* set up command ring control base address */
        addr = buf_res.physaddr;
        addr += (uintptr_t)&((struct xhci_hw_root *)0)->hwr_commands[0];
 
@@ -550,10 +557,10 @@ xhci_halt_controller(struct xhci_softc *sc)
 
        DPRINTF("\n");
 
-       sc->sc_capa_off = 0;
-       sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH);
-       sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0xF;
-       sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3;
+       sc->sc_event_ccs = 1;
+       sc->sc_event_idx = 0;
+       sc->sc_command_ccs = 1;
+       sc->sc_command_idx = 0;
 
        /* Halt controller */
        XWRITE4(sc, oper, XHCI_USBCMD, 0);
@@ -573,7 +580,7 @@ xhci_halt_controller(struct xhci_softc *sc)
 }
 
 usb_error_t
-xhci_init(struct xhci_softc *sc, device_t self)
+xhci_init(struct xhci_softc *sc, device_t self, uint8_t dma32)
 {
        uint32_t temp;
 
@@ -588,7 +595,7 @@ xhci_init(struct xhci_softc *sc, device_t self)
        /* set up the bus struct */
        sc->sc_bus.methods = &xhci_bus_methods;
 
-       /* setup devices array */
+       /* set up devices array */
        sc->sc_bus.devices = sc->sc_devices;
        sc->sc_bus.devices_max = XHCI_MAX_DEVICES;
 
@@ -620,7 +627,8 @@ xhci_init(struct xhci_softc *sc, device_t self)
        }
 
        /* get DMA bits */
-       sc->sc_bus.dma_bits = XHCI_HCS0_AC64(temp) ? 64 : 32;
+       sc->sc_bus.dma_bits = (XHCI_HCS0_AC64(temp) &&
+               xhcidma32 == 0 && dma32 == 0) ? 64 : 32;
 
        device_printf(self, "%d bytes context size, %d-bit DMA\n",
            sc->sc_ctx_is_64_byte ? 64 : 32, (int)sc->sc_bus.dma_bits);
@@ -635,6 +643,7 @@ xhci_init(struct xhci_softc *sc, device_t self)
        cv_init(&sc->sc_cmd_cv, "CMDQ");
        lockinit(&sc->sc_cmd_lock, "CMDQ lock", 0, LK_CANRECURSE);
 
+       /* set up command queue mutex and condition varible */
        sc->sc_config_msg[0].hdr.pm_callback = &xhci_configure_msg;
        sc->sc_config_msg[0].bus = &sc->sc_bus;
        sc->sc_config_msg[1].hdr.pm_callback = &xhci_configure_msg;
@@ -1408,6 +1417,13 @@ xhci_set_address(struct usb_device *udev, struct lock *lock, uint16_t address)
 
                pepext = xhci_get_endpoint_ext(udev,
                    &udev->ctrl_ep_desc);
+
+               /* ensure the control endpoint is setup again */
+               USB_BUS_LOCK(udev->bus);
+               pepext->trb_halted = 1;
+               pepext->trb_running = 0;
+               USB_BUS_UNLOCK(udev->bus);
+
                err = xhci_configure_endpoint(udev,
                    &udev->ctrl_ep_desc, pepext,
                    0, 1, 1, 0, mps, mps, USB_EP_MODE_DEFAULT);
@@ -1818,7 +1834,7 @@ restart:
                                npkt_off += buf_res.length;
                        }
 
-                       /* setup npkt */
+                       /* set up npkt */
                        npkt = (len_old - npkt_off + temp->max_packet_size - 1) /
                            temp->max_packet_size;
 
@@ -1944,7 +1960,7 @@ restart:
        if (precompute) {
                precompute = 0;
 
-               /* setup alt next pointer, if any */
+               /* set up alt next pointer, if any */
                if (temp->last_frame) {
                        td_alt_next = NULL;
                } else {
@@ -2123,7 +2139,7 @@ xhci_setup_generic_chain(struct usb_xfer *xfer)
        }
 
        if (x != xfer->nframes) {
-                /* setup page_cache pointer */
+                /* set up page_cache pointer */
                 temp.pc = xfer->frbuffers + x;
                /* set endpoint direction */
                temp.direction = UE_GET_DIR(xfer->endpointno);
@@ -3099,7 +3115,7 @@ xhci_device_generic_enter(struct usb_xfer *xfer)
 {
        DPRINTF("\n");
 
-       /* setup TD's and QH */
+       /* set up TD's and QH */
        xhci_setup_generic_chain(xfer);
 
        xhci_device_generic_multi_enter(xfer->endpoint,
index aeb760f..635f167 100644 (file)
@@ -481,13 +481,13 @@ struct xhci_softc {
        uint32_t                sc_exit_lat_max;
 
        /* offset to operational registers */
-       uint32_t                sc_oper_off;
+       uint32_t                sc_oper_off;
        /* offset to capability registers */
-       uint32_t                sc_capa_off;
+       uint32_t                sc_capa_off;
        /* offset to runtime registers */
-       uint32_t                sc_runt_off;
+       uint32_t                sc_runt_off;
        /* offset to doorbell registers */
-       uint32_t                sc_door_off;
+       uint32_t                sc_door_off;
 
        /* chip specific */
        uint16_t                sc_erst_max;
@@ -523,7 +523,7 @@ struct xhci_softc {
 
 uint8_t        xhci_use_polling(void);
 usb_error_t xhci_halt_controller(struct xhci_softc *);
-usb_error_t xhci_init(struct xhci_softc *, device_t);
+usb_error_t xhci_init(struct xhci_softc *, device_t, uint8_t);
 usb_error_t xhci_start_controller(struct xhci_softc *);
 void   xhci_interrupt(struct xhci_softc *);
 void   xhci_uninit(struct xhci_softc *);
index 90795c6..b25234f 100644 (file)
@@ -177,6 +177,7 @@ xhci_pci_attach(device_t self)
 {
        struct xhci_softc *sc = device_get_softc(self);
        int count, err, rid;
+       uint8_t usedma32;
 
        rid = PCI_XHCI_CBMEM;
        sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
@@ -189,7 +190,17 @@ xhci_pci_attach(device_t self)
        sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
        sc->sc_io_size = rman_get_size(sc->sc_io_res);
 
-       if (xhci_init(sc, self)) {
+       /* check for USB 3.0 controllers which don't support 64-bit DMA */
+       switch (pci_get_devid(self)) {
+       case 0x01941033:        /* NEC uPD720200 USB 3.0 controller */
+               usedma32 = 1;
+               break;
+       default:
+               usedma32 = 0;
+               break;
+       }
+
+       if (xhci_init(sc, self, usedma32)) {
                device_printf(self, "Could not initialize softc\n");
                bus_release_resource(self, SYS_RES_MEMORY, PCI_XHCI_CBMEM,
                    sc->sc_io_res);
index fcdf206..29975fc 100644 (file)
@@ -126,27 +126,33 @@ enum atp_log_level {
 static int atp_debug = ATP_LLEVEL_ERROR; /* the default is to only log errors */
 SYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RW,
     &atp_debug, ATP_LLEVEL_ERROR, "ATP debug level");
+TUNABLE_INT("hw.usb.atp.debug", &atp_debug);
 #endif /* USB_DEBUG */
 
 static u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT;
 SYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW,
     &atp_touch_timeout, 125000, "age threshold (in micros) for a touch");
+TUNABLE_INT("hw.usb.atp.touch_timeout", &atp_touch_timeout);
 
 static u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD;
 SYSCTL_UINT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RW,
     &atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD,
     "maximum time (in micros) between a double-tap");
+TUNABLE_INT("hw.usb.atp.double_tap_threshold", &atp_double_tap_threshold);
 
 static u_int atp_mickeys_scale_factor = ATP_SCALE_FACTOR;
 static int atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS);
 SYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RW,
     &atp_mickeys_scale_factor, sizeof(atp_mickeys_scale_factor),
     atp_sysctl_scale_factor_handler, "IU", "movement scale factor");
+TUNABLE_INT("hw.usb.atp.scale_factor", &atp_mickeys_scale_factor);
 
 static u_int atp_small_movement_threshold = ATP_SCALE_FACTOR >> 3;
 SYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RW,
     &atp_small_movement_threshold, ATP_SCALE_FACTOR >> 3,
     "the small movement black-hole for filtering noise");
+TUNABLE_INT("hw.usb.atp.small_movement", &atp_small_movement_threshold);
+
 /*
  * The movement threshold for a stroke; this is the maximum difference
  * in position which will be resolved as a continuation of a stroke
@@ -165,6 +171,7 @@ static u_int atp_slide_min_movement = (ATP_SCALE_FACTOR >> 3);
 SYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RW,
     &atp_slide_min_movement, (ATP_SCALE_FACTOR >> 3),
     "strokes with at least this amt. of movement are considered slides");
+TUNABLE_INT("hw.usb.atp.slide_min_movement", &atp_slide_min_movement);
 
 /*
  * The minimum age of a stroke for it to be considered mature; this
@@ -174,6 +181,8 @@ static u_int atp_stroke_maturity_threshold = 2;
 SYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RW,
     &atp_stroke_maturity_threshold, 2,
     "the minimum age of a stroke for it to be considered mature");
+TUNABLE_INT("hw.usb.atp.stroke_maturity_threshold",
+               &atp_stroke_maturity_threshold);
 
 /* Accept pressure readings from sensors only if above this value. */
 static u_int atp_sensor_noise_threshold = 2;
index 908cc9d..fa2b70e 100644 (file)
@@ -58,7 +58,7 @@
 static int uep_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uep, CTLFLAG_RW, 0, "USB uep");
-SYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uep, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uep_debug, 0, "Debug level");
 #endif
 
index 352205c..14ea005 100644 (file)
@@ -55,6 +55,7 @@
 #include <sys/priv.h>
 #include <sys/conf.h>
 #include <sys/fcntl.h>
+#include <sys/mutex.h>
 
 #include "usbdevs.h"
 #include <bus/u4b/usb.h>
@@ -73,7 +74,7 @@
 static int uhid_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
-SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uhid, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uhid_debug, 0, "Debug level");
 #endif
 
@@ -512,7 +513,9 @@ uhid_open(struct usb_fifo *fifo, int fflags)
         */
        if (fflags & FREAD) {
                /* reset flags */
+               lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
                sc->sc_flags &= ~UHID_FLAG_IMMED;
+               lockmgr(&sc->sc_lock, LK_RELEASE);
 
                if (usb_fifo_alloc_buffer(fifo,
                    sc->sc_isize + 1, UHID_FRAME_NUM)) {
@@ -728,7 +731,7 @@ uhid_attach(device_t dev)
                if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) {
 
                        sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr);
-                       sc->sc_repdesc_ptr = (void *)&uhid_graphire_report_descr;
+                       sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire_report_descr);
                        sc->sc_flags |= UHID_FLAG_STATIC_DESC;
 
                } else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) {
@@ -749,7 +752,7 @@ uhid_attach(device_t dev)
                                    usbd_errstr(error));
                        }
                        sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr);
-                       sc->sc_repdesc_ptr = (void *)&uhid_graphire3_4x5_report_descr;
+                       sc->sc_repdesc_ptr = __DECONST(void *, &uhid_graphire3_4x5_report_descr);
                        sc->sc_flags |= UHID_FLAG_STATIC_DESC;
                }
        } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) &&
@@ -758,7 +761,7 @@ uhid_attach(device_t dev)
 
                /* the Xbox 360 gamepad has no report descriptor */
                sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr);
-               sc->sc_repdesc_ptr = (void *)&uhid_xb360gp_report_descr;
+               sc->sc_repdesc_ptr = __DECONST(void *, &uhid_xb360gp_report_descr);
                sc->sc_flags |= UHID_FLAG_STATIC_DESC;
        }
        if (sc->sc_repdesc_ptr == NULL) {
index e3e993d..5e4b299 100644 (file)
@@ -71,7 +71,7 @@
 static int ums_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
-SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_ums, OID_AUTO, debug, CTLFLAG_RWTUN,
     &ums_debug, 0, "Debug level");
 #endif
 
index f7b9c86..b0c1734 100644 (file)
@@ -97,7 +97,7 @@ __FBSDID("$FreeBSD$");
 static int udbp_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp");
-SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_udbp, OID_AUTO, debug, CTLFLAG_RWTUN,
     &udbp_debug, 0, "udbp debug level");
 #endif
 
index 4199c40..c2c7823 100644 (file)
 static int aue_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue");
-SYSCTL_INT(_hw_usb_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0,
+SYSCTL_INT(_hw_usb_aue, OID_AUTO, debug, CTLFLAG_RWTUN, &aue_debug, 0,
     "Debug level");
 #endif
 
index dd2d991..23198c9 100644 (file)
 static int axe_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe");
-SYSCTL_INT(_hw_usb_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0,
+SYSCTL_INT(_hw_usb_axe, OID_AUTO, debug, CTLFLAG_RWTUN, &axe_debug, 0,
     "Debug level");
 #endif
 
index 715a7a3..90c79b4 100644 (file)
@@ -133,7 +133,7 @@ static void axge_csum_cfg(struct usb_ether *);
 static int axge_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, axge, CTLFLAG_RW, 0, "USB axge");
-SYSCTL_INT(_hw_usb_axge, OID_AUTO, debug, CTLFLAG_RW, &axge_debug, 0,
+SYSCTL_INT(_hw_usb_axge, OID_AUTO, debug, CTLFLAG_RWTUN, &axge_debug, 0,
     "Debug level");
 #endif
 
index f1bae16..747bd27 100644 (file)
@@ -112,9 +112,9 @@ static int cdce_debug = 0;
 static int cdce_tx_interval = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
-SYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0,
+SYSCTL_INT(_hw_usb_cdce, OID_AUTO, debug, CTLFLAG_RWTUN, &cdce_debug, 0,
     "Debug level");
-SYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RW, &cdce_tx_interval, 0,
+SYSCTL_INT(_hw_usb_cdce, OID_AUTO, interval, CTLFLAG_RWTUN, &cdce_tx_interval, 0,
     "NCM transmit interval in ms");
 #endif
 
@@ -791,8 +791,10 @@ tr_setup:
                ifp->if_oerrors++;
 
                if (error != USB_ERR_CANCELLED) {
-                       /* try to clear stall first */
-                       usbd_xfer_set_stall(xfer);
+                       if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+                               /* try to clear stall first */
+                               usbd_xfer_set_stall(xfer);
+                       }
                        goto tr_setup;
                }
                break;
@@ -832,8 +834,14 @@ cdce_init(struct usb_ether *ue)
        usbd_transfer_start(sc->sc_xfer[CDCE_INTR_RX]);
        usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
 
-       /* stall data write direction, which depends on USB mode */
-       usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]);
+       /*
+        * Stall data write direction, which depends on USB mode.
+        *
+        * Some USB host stacks (e.g. Mac OS X) don't clears stall
+        * bit as it should, so set it in our host mode only.
+        */
+       if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
+               usbd_xfer_set_stall(sc->sc_xfer[CDCE_BULK_TX]);
 
        /* start data transfers */
        cdce_start(ue);
@@ -950,10 +958,12 @@ cdce_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
 
                if (error != USB_ERR_CANCELLED) {
 tr_stall:
-                       /* try to clear stall first */
-                       usbd_xfer_set_stall(xfer);
-                       usbd_xfer_set_frames(xfer, 0);
-                       usbd_transfer_submit(xfer);
+                       if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+                               /* try to clear stall first */
+                               usbd_xfer_set_stall(xfer);
+                               usbd_xfer_set_frames(xfer, 0);
+                               usbd_transfer_submit(xfer);
+                       }
                        break;
                }
 
@@ -966,6 +976,7 @@ tr_stall:
 static void
 cdce_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
+       struct cdce_softc *sc = usbd_xfer_softc(xfer);
        int actlen;
 
        usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
@@ -997,6 +1008,10 @@ tr_setup:
 static void
 cdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
 {
+       struct cdce_softc *sc = usbd_xfer_softc(xfer);
+       struct usb_cdc_notification req;
+       struct usb_page_cache *pc;
+       uint32_t speed;
        int actlen;
 
        usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
@@ -1006,19 +1021,71 @@ cdce_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
 
                DPRINTF("Transferred %d bytes\n", actlen);
 
+               switch (sc->sc_notify_state) {
+               case CDCE_NOTIFY_NETWORK_CONNECTION:
+                       sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
+                       break;
+               case CDCE_NOTIFY_SPEED_CHANGE:
+                       sc->sc_notify_state = CDCE_NOTIFY_DONE;
+                       break;
+               default:
+                       break;
+               }
+
+
                /* FALLTHROUGH */
        case USB_ST_SETUP:
 tr_setup:
-#if 0
-               usbd_xfer_set_frame_len(xfer, 0, XXX);
-               usbd_transfer_submit(xfer);
-#endif
+               /*
+                * Inform host about connection. Required according to USB CDC
+                * specification and communicating to Mac OS X USB host stack.
+                * Some of the values seems ignored by Mac OS X though.
+                */
+               if (sc->sc_notify_state == CDCE_NOTIFY_NETWORK_CONNECTION) {
+                       req.bmRequestType = UCDC_NOTIFICATION;
+                       req.bNotification = UCDC_N_NETWORK_CONNECTION;
+                       req.wIndex[0] = sc->sc_ifaces_index[1];
+                       req.wIndex[1] = 0;
+                       USETW(req.wValue, 1); /* Connected */
+                       USETW(req.wLength, 0);
+
+                       pc = usbd_xfer_get_frame(xfer, 0);
+                       usbd_copy_in(pc, 0, &req, sizeof(req));
+                       usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+                       usbd_xfer_set_frames(xfer, 1);
+                       usbd_transfer_submit(xfer);
+
+               } else if (sc->sc_notify_state == CDCE_NOTIFY_SPEED_CHANGE) {
+                       req.bmRequestType = UCDC_NOTIFICATION;
+                       req.bNotification = UCDC_N_CONNECTION_SPEED_CHANGE;
+                       req.wIndex[0] = sc->sc_ifaces_index[1];
+                       req.wIndex[1] = 0;
+                       USETW(req.wValue, 0);
+                       USETW(req.wLength, 8);
+
+                       /* Peak theoretical bulk trasfer rate in bits/s */
+                       if (usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_FULL)
+                               speed = (13 * 512 * 8 * 1000 * 8);
+                       else
+                               speed = (19 * 64 * 1 * 1000 * 8);
+
+                       USETDW(req.data + 0, speed); /* Upstream bit rate */
+                       USETDW(req.data + 4, speed); /* Downstream bit rate */
+
+                       pc = usbd_xfer_get_frame(xfer, 0);
+                       usbd_copy_in(pc, 0, &req, sizeof(req));
+                       usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+                       usbd_xfer_set_frames(xfer, 1);
+                       usbd_transfer_submit(xfer);
+               }
                break;
 
        default:                        /* Error */
                if (error != USB_ERR_CANCELLED) {
-                       /* start clear stall */
-                       usbd_xfer_set_stall(xfer);
+                       if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+                               /* start clear stall */
+                               usbd_xfer_set_stall(xfer);
+                       }
                        goto tr_setup;
                }
                break;
@@ -1027,9 +1094,30 @@ tr_setup:
 
 static int
 cdce_handle_request(device_t dev,
-    const void *req, void **pptr, uint16_t *plen,
+    const void *preq, void **pptr, uint16_t *plen,
     uint16_t offset, uint8_t *pstate)
 {
+       struct cdce_softc *sc = device_get_softc(dev);
+       const struct usb_device_request *req = preq;
+       uint8_t is_complete = *pstate;
+
+       /*
+        * When Mac OS X resumes after suspending it expects
+        * to be notified again after this request.
+        */
+       if (req->bmRequestType == UT_WRITE_CLASS_INTERFACE && \
+           req->bRequest == UCDC_NCM_SET_ETHERNET_PACKET_FILTER) {
+
+               if (is_complete == 1) {
+                       mtx_lock(&sc->sc_mtx);
+                       sc->sc_notify_state = CDCE_NOTIFY_SPEED_CHANGE;
+                       usbd_transfer_start(sc->sc_xfer[CDCE_INTR_TX]);
+                       mtx_unlock(&sc->sc_mtx);
+               }
+
+               return (0);
+       }
+
        return (ENXIO);                 /* use builtin handler */
 }
 
@@ -1245,10 +1333,12 @@ cdce_ncm_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
                ifp->if_oerrors += 1;
 
                if (error != USB_ERR_CANCELLED) {
-                       /* try to clear stall first */
-                       usbd_xfer_set_stall(xfer);
-                       usbd_xfer_set_frames(xfer, 0);
-                       usbd_transfer_submit(xfer);
+                       if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+                               /* try to clear stall first */
+                               usbd_xfer_set_stall(xfer);
+                               usbd_xfer_set_frames(xfer, 0);
+                               usbd_transfer_submit(xfer);
+                       }
                }
                break;
        }
@@ -1406,10 +1496,12 @@ tr_setup:
 
                if (error != USB_ERR_CANCELLED) {
 tr_stall:
-                       /* try to clear stall first */
-                       usbd_xfer_set_stall(xfer);
-                       usbd_xfer_set_frames(xfer, 0);
-                       usbd_transfer_submit(xfer);
+                       if (usbd_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST) {
+                               /* try to clear stall first */
+                               usbd_xfer_set_stall(xfer);
+                               usbd_xfer_set_frames(xfer, 0);
+                               usbd_transfer_submit(xfer);
+                       }
                }
                break;
        }
index c470b36..1bc2d60 100644 (file)
@@ -93,6 +93,10 @@ struct cdce_softc {
 
        uint8_t sc_eaddr_str_index;
        uint8_t sc_ifaces_index[2];
+       uint8_t sc_notify_state;
+#define        CDCE_NOTIFY_NETWORK_CONNECTION  0
+#define        CDCE_NOTIFY_SPEED_CHANGE        1
+#define        CDCE_NOTIFY_DONE                2
 };
 
 #define        CDCE_LOCK(_sc)                  mtx_lock(&(_sc)->sc_mtx)
index d481404..707f6d6 100644 (file)
@@ -126,7 +126,7 @@ static void cue_reset(struct cue_softc *);
 static int cue_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue");
-SYSCTL_INT(_hw_usb_cue, OID_AUTO, debug, CTLFLAG_RW, &cue_debug, 0,
+SYSCTL_INT(_hw_usb_cue, OID_AUTO, debug, CTLFLAG_RWTUN, &cue_debug, 0,
     "Debug level");
 #endif
 
index a6731a2..4e7f93f 100644 (file)
@@ -83,7 +83,7 @@ static uether_fn_t ipheth_setpromisc;
 static int ipheth_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ipheth, CTLFLAG_RW, 0, "USB iPhone ethernet");
-SYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RW, &ipheth_debug, 0, "Debug level");
+SYSCTL_INT(_hw_usb_ipheth, OID_AUTO, debug, CTLFLAG_RWTUN, &ipheth_debug, 0, "Debug level");
 #endif
 
 static const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = {
index 0c3829b..add3fd4 100644 (file)
@@ -167,7 +167,7 @@ static void kue_reset(struct kue_softc *);
 static int kue_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue");
-SYSCTL_INT(_hw_usb_kue, OID_AUTO, debug, CTLFLAG_RW, &kue_debug, 0,
+SYSCTL_INT(_hw_usb_kue, OID_AUTO, debug, CTLFLAG_RWTUN, &kue_debug, 0,
     "Debug level");
 #endif
 
index 4bf859f..1e79d32 100644 (file)
 static int mos_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, mos, CTLFLAG_RW, 0, "USB mos");
-SYSCTL_INT(_hw_usb_mos, OID_AUTO, debug, CTLFLAG_RW, &mos_debug, 0,
+SYSCTL_INT(_hw_usb_mos, OID_AUTO, debug, CTLFLAG_RWTUN, &mos_debug, 0,
     "Debug level");
 #endif
 
index 3d4ef6c..ebbe087 100644 (file)
@@ -100,7 +100,7 @@ __FBSDID("$FreeBSD$");
 static int rue_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
-SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_rue, OID_AUTO, debug, CTLFLAG_RWTUN,
     &rue_debug, 0, "Debug level");
 #endif
 
index 678fc5b..ba8f7eb 100644 (file)
@@ -191,7 +191,7 @@ static const struct usb_ether_methods udav_ue_methods_nophy = {
 static int udav_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav");
-SYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0,
+SYSCTL_INT(_hw_usb_udav, OID_AUTO, debug, CTLFLAG_RWTUN, &udav_debug, 0,
     "Debug level");
 #endif
 
index 8c4214a..833e7a4 100644 (file)
@@ -85,7 +85,7 @@ static uint32_t urndis_ctrl_init(struct urndis_softc *);
 #ifdef USB_DEBUG
 static int urndis_debug = 0;
 static SYSCTL_NODE(_hw_usb, OID_AUTO, urndis, CTLFLAG_RW, 0, "USB RNDIS-Ethernet");
-SYSCTL_INT(_hw_usb_urndis, OID_AUTO, debug, CTLFLAG_RW, &urndis_debug, 0,
+SYSCTL_INT(_hw_usb_urndis, OID_AUTO, debug, CTLFLAG_RWTUN, &urndis_debug, 0,
     "Debug level");
 #endif
 
@@ -167,10 +167,15 @@ static const struct usb_ether_methods urndis_ue_methods = {
 };
 
 static const STRUCT_USB_HOST_ID urndis_host_devs[] = {
+       /* Generic RNDIS class match */
        {USB_IFACE_CLASS(UICLASS_WIRELESS), USB_IFACE_SUBCLASS(UISUBCLASS_RF),
        USB_IFACE_PROTOCOL(UIPROTO_RNDIS)},
        {USB_IFACE_CLASS(UICLASS_IAD), USB_IFACE_SUBCLASS(UISUBCLASS_SYNC),
        USB_IFACE_PROTOCOL(UIPROTO_ACTIVESYNC)},
+       /* HP-WebOS */
+       {USB_VENDOR(USB_VENDOR_PALM), USB_IFACE_CLASS(UICLASS_CDC),
+               USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+               USB_IFACE_PROTOCOL(0xff)},
 };
 
 static const STRUCT_USB_HOST_ID urndis_non_huawei_host_devs[] = {
index 978cec4..e476dae 100644 (file)
@@ -78,7 +78,7 @@ __FBSDID("$FreeBSD$");
 static int usie_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, usie, CTLFLAG_RW, 0, "sierra USB modem");
-SYSCTL_INT(_hw_usb_usie, OID_AUTO, debug, CTLFLAG_RW, &usie_debug, 0,
+SYSCTL_INT(_hw_usb_usie, OID_AUTO, debug, CTLFLAG_RWTUN, &usie_debug, 0,
     "usie debug level");
 #endif
 
index 285e274..7c4cd9f 100644 (file)
@@ -285,7 +285,7 @@ static const STRUCT_USB_HOST_ID uhso_devs[] = {
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uhso, CTLFLAG_RW, 0, "USB uhso");
 static int uhso_autoswitch = 1;
-SYSCTL_INT(_hw_usb_uhso, OID_AUTO, auto_switch, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uhso, OID_AUTO, auto_switch, CTLFLAG_RWTUN,
     &uhso_autoswitch, 0, "Automatically switch to modem mode");
 
 #ifdef USB_DEBUG
@@ -295,7 +295,7 @@ static int uhso_debug = UHSO_DEBUG;
 static int uhso_debug = -1;
 #endif
 
-SYSCTL_INT(_hw_usb_uhso, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uhso, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uhso_debug, 0, "Debug level");
 
 #define UHSO_DPRINTF(n, x, ...) {\
@@ -592,7 +592,7 @@ uhso_attach(device_t self)
            CTLFLAG_RD, uhso_port[UHSO_IFACE_PORT(sc->sc_type)], 0,
            "Port available at this interface");
        SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "radio",
-           CTLTYPE_INT | CTLFLAG_RW, sc, 0, uhso_radio_sysctl, "I", "Enable radio");
+           CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, uhso_radio_sysctl, "I", "Enable radio");
 
        /*
         * The default interface description on most Option devices isn't
index 1b819d1..8d95429 100644 (file)
@@ -481,6 +481,8 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
        USB_QUIRK(TOSHIBA, TRANSMEMORY, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE,
            UQ_MSC_NO_PREVENT_ALLOW),
        USB_QUIRK(VIALABS, USB30SATABRIDGE, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
+       USB_QUIRK(QUALCOMMINC, ZTE_MF730M, 0x0000, 0xffff, UQ_MSC_NO_GETMAXLUN,
+           UQ_MSC_NO_INQUIRY, UQ_CFG_INDEX_0),
 
        /* Non-standard USB MIDI devices */
        USB_QUIRK(ROLAND, UM1, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS),
index 87aac3d..a038bb0 100644 (file)
@@ -65,7 +65,7 @@
 static int u3g_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB 3g");
-SYSCTL_INT(_hw_usb_u3g, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_u3g, OID_AUTO, debug, CTLFLAG_RWTUN,
     &u3g_debug, 0, "Debug level");
 #endif
 
@@ -213,6 +213,10 @@ static const STRUCT_USB_HOST_ID u3g_devs[] = {
        U3G_DEV(DELL, U5730_2, 0),
        U3G_DEV(DELL, U5730_3, 0),
        U3G_DEV(DELL, U740, 0),
+       U3G_DEV(DLINK, DWR510_CD, U3GINIT_SCSIEJECT),
+       U3G_DEV(DLINK, DWR510, 0),
+       U3G_DEV(DLINK, DWM157_CD, U3GINIT_SCSIEJECT),
+       U3G_DEV(DLINK, DWM157, 0),
        U3G_DEV(DLINK3, DWM652, 0),
        U3G_DEV(HP, EV2200, 0),
        U3G_DEV(HP, HS2300, 0),
@@ -352,8 +356,15 @@ static const STRUCT_USB_HOST_ID u3g_devs[] = {
        U3G_DEV(QISDA, H21_2, 0),
        U3G_DEV(QUALCOMM2, AC8700, 0),
        U3G_DEV(QUALCOMM2, MF330, 0),
+       U3G_DEV(QUALCOMM2, SIM5218, 0),
+       U3G_DEV(QUALCOMM2, WM620, 0),
        U3G_DEV(QUALCOMM2, VW110L, U3GINIT_SCSIEJECT),
+       U3G_DEV(QUALCOMM2, GOBI2000_QDL, 0),
+       U3G_DEV(QUALCOMM2, GOBI2000, 0),
+       U3G_DEV(QUALCOMM2, VT80N, 0),
        U3G_DEV(QUALCOMMINC, AC2726, 0),
+       U3G_DEV(QUALCOMMINC, AC682_INIT, U3GINIT_SCSIEJECT),
+       U3G_DEV(QUALCOMMINC, AC682, 0),
        U3G_DEV(QUALCOMMINC, AC8700, 0),
        U3G_DEV(QUALCOMMINC, AC8710, 0),
        U3G_DEV(QUALCOMMINC, CDMA_MSM, U3GINIT_SCSIEJECT),
@@ -424,6 +435,8 @@ static const STRUCT_USB_HOST_ID u3g_devs[] = {
        U3G_DEV(QUALCOMMINC, MF626, 0),
        U3G_DEV(QUALCOMMINC, MF628, 0),
        U3G_DEV(QUALCOMMINC, MF633R, 0),
+       /* the following is a RNDIS device, no modem features */
+       U3G_DEV(QUALCOMMINC, ZTE_MF730M, U3GINIT_SCSIEJECT),
        U3G_DEV(QUANTA, GKE, 0),
        U3G_DEV(QUANTA, GLE, 0),
        U3G_DEV(QUANTA, GLX, 0),
index 4c6138b..a7f3cf6 100644 (file)
@@ -91,7 +91,7 @@
 static int ubsa_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa");
-SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RWTUN,
     &ubsa_debug, 0, "ubsa debug level");
 #endif
 
index 772d8c1..443d9c4 100644 (file)
 static int ubser_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
-SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RWTUN,
     &ubser_debug, 0, "ubser debug level");
 #endif
 
index 7504747..22ff400 100644 (file)
@@ -99,7 +99,7 @@
 static int uchcom_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom");
-SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uchcom_debug, 0, "uchcom debug level");
 #endif
 
index 0015eeb..c788bca 100644 (file)
@@ -73,6 +73,7 @@ static int uftdi_debug = 0;
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
 SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW,
     &uftdi_debug, 0, "Debug level");
+TUNABLE_INT("hw.usb.uftdi.debug", &uftdi_debug);
 #endif
 
 #define        UFTDI_CONFIG_INDEX      0
index df3e7d0..0d3e585 100644 (file)
@@ -68,7 +68,7 @@
 static int ulpt_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt");
-SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RWTUN,
     &ulpt_debug, 0, "Debug level");
 #endif
 
index 50a2f40..e2ab087 100644 (file)
@@ -75,7 +75,7 @@
 static int umcs_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, umcs, CTLFLAG_RW, 0, "USB umcs quadport serial adapter");
-SYSCTL_INT(_hw_usb_umcs, OID_AUTO, debug, CTLFLAG_RW, &umcs_debug, 0, "Debug level");
+SYSCTL_INT(_hw_usb_umcs, OID_AUTO, debug, CTLFLAG_RWTUN, &umcs_debug, 0, "Debug level");
 #endif                                 /* USB_DEBUG */
 
 
index 2b6aa00..0fcd873 100644 (file)
 #include <bus/u4b/usbhid.h>
 #include <bus/u4b/usb_cdc.h>
 #include "usbdevs.h"
+#include "usb_if.h"
 
 #include <bus/u4b/usb_ioctl.h>
 
 static int umodem_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
-SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RWTUN,
     &umodem_debug, 0, "Debug level");
 #endif
 
-static const STRUCT_USB_HOST_ID umodem_devs[] = {
+static const STRUCT_USB_DUAL_ID umodem_dual_devs[] = {
        /* Generic Modem class match */
        {USB_IFACE_CLASS(UICLASS_CDC),
                USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
@@ -134,6 +135,17 @@ static const STRUCT_USB_HOST_ID umodem_devs[] = {
        {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
 };
 
+static const STRUCT_USB_HOST_ID umodem_host_devs[] = {
+        /* Huawei Modem class match */
+        {USB_VENDOR(USB_VENDOR_HUAWEI),USB_IFACE_CLASS(UICLASS_CDC),
+                USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+                USB_IFACE_PROTOCOL(0xFF)},
+        /* Kyocera AH-K3001V */
+        {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
+        {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
+        {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
+};
+
 /*
  * As speeds for umodem deivces increase, these numbers will need to
  * be increased. They should be good for G3 speeds and below.
@@ -145,6 +157,7 @@ static const STRUCT_USB_HOST_ID umodem_devs[] = {
 enum {
        UMODEM_BULK_WR,
        UMODEM_BULK_RD,
+       UMODEM_INTR_WR,
        UMODEM_INTR_RD,
        UMODEM_N_TRANSFER,
 };
@@ -169,13 +182,17 @@ struct umodem_softc {
        uint8_t sc_cm_over_data;
        uint8_t sc_cm_cap;              /* CM capabilities */
        uint8_t sc_acm_cap;             /* ACM capabilities */
+       uint8_t sc_line_coding[32];     /* used in USB device mode */
+       uint8_t sc_abstract_state[32];  /* used in USB device mode */
 };
 
 static device_probe_t umodem_probe;
 static device_attach_t umodem_attach;
 static device_detach_t umodem_detach;
+static usb_handle_request_t umodem_handle_request;
 
-static usb_callback_t umodem_intr_callback;
+static usb_callback_t umodem_intr_read_callback;
+static usb_callback_t umodem_intr_write_callback;
 static usb_callback_t umodem_write_callback;
 static usb_callback_t umodem_read_callback;
 
@@ -205,31 +222,45 @@ static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = {
        [UMODEM_BULK_WR] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_OUT,
+               .direction = UE_DIR_TX,
                .if_index = 0,
                .bufsize = UMODEM_BUF_SIZE,
                .flags = {.pipe_bof = 1,.force_short_xfer = 1,},
                .callback = &umodem_write_callback,
+               .usb_mode = USB_MODE_DUAL,
        },
 
        [UMODEM_BULK_RD] = {
                .type = UE_BULK,
                .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
+               .direction = UE_DIR_RX,
                .if_index = 0,
                .bufsize = UMODEM_BUF_SIZE,
                .flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
                .callback = &umodem_read_callback,
+               .usb_mode = USB_MODE_DUAL,
+       },
+
+       [UMODEM_INTR_WR] = {
+               .type = UE_INTERRUPT,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_TX,
+               .if_index = 1,
+               .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+               .bufsize = 0,   /* use wMaxPacketSize */
+               .callback = &umodem_intr_write_callback,
+               .usb_mode = USB_MODE_DEVICE,
        },
 
        [UMODEM_INTR_RD] = {
                .type = UE_INTERRUPT,
                .endpoint = UE_ADDR_ANY,
-               .direction = UE_DIR_IN,
+               .direction = UE_DIR_RX,
                .if_index = 1,
                .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
                .bufsize = 0,   /* use wMaxPacketSize */
-               .callback = &umodem_intr_callback,
+               .callback = &umodem_intr_read_callback,
+               .usb_mode = USB_MODE_HOST,
        },
 };
 
@@ -249,6 +280,10 @@ static const struct ucom_callback umodem_callback = {
 };
 
 static device_method_t umodem_methods[] = {
+       /* USB interface */
+       DEVMETHOD(usb_handle_request, umodem_handle_request),
+
+       /* Device interface */
        DEVMETHOD(device_probe, umodem_probe),
        DEVMETHOD(device_attach, umodem_attach),
        DEVMETHOD(device_detach, umodem_detach),
@@ -276,13 +311,14 @@ umodem_probe(device_t dev)
 
        DPRINTFN(11, "\n");
 
-       if (uaa->usb_mode != USB_MODE_HOST)
-               return (ENXIO);
-
-       error = usbd_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
-       if (error)
-               return (error);
-
+       error = usbd_lookup_id_by_uaa(umodem_host_devs,
+           sizeof(umodem_host_devs), uaa);
+       if (error) {
+               error = usbd_lookup_id_by_uaa(umodem_dual_devs,
+                   sizeof(umodem_dual_devs), uaa);
+               if (error)
+                       return (error);
+       }
        return (BUS_PROBE_GENERIC);
 }
 
@@ -396,18 +432,22 @@ umodem_attach(device_t dev)
            umodem_config, UMODEM_N_TRANSFER,
            sc, &sc->sc_lock);
        if (error) {
+               device_printf(dev, "Can't setup transfer\n");
                goto detach;
        }
 
-       /* clear stall at first run */
-       lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
-       usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
-       usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
-       lockmgr(&sc->sc_lock, LK_RELEASE);
+       /* clear stall at first run, if USB host mode */
+       if (uaa->usb_mode == USB_MODE_HOST) {
+               lockmgr(&sc->sc_lock, LK_EXCLUSIVE);
+               usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
+               usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
+               lockmgr(&sc->sc_lock, LK_RELEASE);
+       }
 
        error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
            &umodem_callback, &sc->sc_lock);
        if (error) {
+               device_printf(dev, "Can't attach com\n");
                goto detach;
        }
        ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
@@ -478,6 +518,7 @@ umodem_start_write(struct ucom_softc *ucom)
 {
        struct umodem_softc *sc = ucom->sc_parent;
 
+       usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_WR]);
        usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
 }
 
@@ -486,6 +527,7 @@ umodem_stop_write(struct ucom_softc *ucom)
 {
        struct umodem_softc *sc = ucom->sc_parent;
 
+       usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_WR]);
        usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
 }
 
@@ -680,7 +722,34 @@ umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
 }
 
 static void
-umodem_intr_callback(struct usb_xfer *xfer, usb_error_t error)
+umodem_intr_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       int actlen;
+
+       usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+
+               DPRINTF("Transferred %d bytes\n", actlen);
+
+               /* FALLTHROUGH */
+       case USB_ST_SETUP:
+tr_setup:
+               break;
+
+       default:                        /* Error */
+               if (error != USB_ERR_CANCELLED) {
+                       /* start clear stall */
+                       usbd_xfer_set_stall(xfer);
+                       goto tr_setup;
+               }
+               break;
+       }
+}
+
+static void
+umodem_intr_read_callback(struct usb_xfer *xfer, usb_error_t error)
 {
        struct usb_cdc_notification pkt;
        struct umodem_softc *sc = usbd_xfer_softc(xfer);
@@ -882,3 +951,56 @@ umodem_poll(struct ucom_softc *ucom)
        struct umodem_softc *sc = ucom->sc_parent;
        usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER);
 }
+
+static int
+umodem_handle_request(device_t dev,
+    const void *preq, void **pptr, uint16_t *plen,
+    uint16_t offset, uint8_t *pstate)
+{
+       struct umodem_softc *sc = device_get_softc(dev);
+       const struct usb_device_request *req = preq;
+       uint8_t is_complete = *pstate;
+
+       DPRINTF("sc=%p\n", sc);
+
+       if (!is_complete) {
+               if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+                   (req->bRequest == UCDC_SET_LINE_CODING) &&
+                   (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+                   (req->wIndex[1] == 0x00) &&
+                   (req->wValue[0] == 0x00) &&
+                   (req->wValue[1] == 0x00)) {
+                       if (offset == 0) {
+                               *plen = sizeof(sc->sc_line_coding);
+                               *pptr = &sc->sc_line_coding;
+                       } else {
+                               *plen = 0;
+                       }
+                       return (0);
+               } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+                   (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+                   (req->wIndex[1] == 0x00) &&
+                   (req->bRequest == UCDC_SET_COMM_FEATURE)) {
+                       if (offset == 0) {
+                               *plen = sizeof(sc->sc_abstract_state);
+                               *pptr = &sc->sc_abstract_state;
+                       } else {
+                               *plen = 0;
+                       }
+                       return (0);
+               } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+                   (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+                   (req->wIndex[1] == 0x00) &&
+                   (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) {
+                       *plen = 0;
+                       return (0);
+               } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) &&
+                   (req->wIndex[0] == sc->sc_ctrl_iface_no) &&
+                   (req->wIndex[1] == 0x00) &&
+                   (req->bRequest == UCDC_SEND_BREAK)) {
+                       *plen = 0;
+                       return (0);
+               }
+       }
+       return (ENXIO);                 /* use builtin handler */
+}
index 94e45de..33efefe 100644 (file)
@@ -49,7 +49,7 @@
 static int umoscom_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
-SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &umoscom_debug, 0, "Debug level");
 #endif
 
index 41ba998..081db25 100644 (file)
 static int uplcom_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
-SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uplcom_debug, 0, "Debug level");
 #endif
 
index 130694b..b237cef 100644 (file)
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
 
+static int ucom_pps_mode;
+
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, pps_mode, CTLFLAG_RW,
+    &ucom_pps_mode, 0, "pulse capturing mode - 0/1/2 - disabled/CTS/DCD");
+TUNABLE_INT("hw.usb.ucom.pss_mode", &ucom_pps_mode);
+
+
 #ifdef USB_DEBUG
 static int ucom_debug = 0;
 
-SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_ucom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &ucom_debug, 0, "ucom debug level");
 #endif
 
@@ -1144,6 +1151,8 @@ ucom_dev_ioctl(struct dev_ioctl_args *ap)
                } else {
                        error = ENOIOCTL;
                }
+               if (error == ENOIOCTL)
+                       error = pps_ioctl(cmd, data, &sc->sc_pps);
                break;
        }
        crit_exit();
@@ -1408,7 +1417,7 @@ ucom_cfg_status_change(struct usb_proc_msg *_task)
        struct tty *tp;
        uint8_t new_msr;
        uint8_t new_lsr;
-       uint8_t onoff;
+       uint8_t msr_delta;
        uint8_t lsr_delta;
 
        tp = sc->sc_tty;
@@ -1432,17 +1441,48 @@ ucom_cfg_status_change(struct usb_proc_msg *_task)
                /* TTY device closed */
                return;
        }
-       onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
+       msr_delta = (sc->sc_msr ^ new_msr);
        lsr_delta = (sc->sc_lsr ^ new_lsr);
 
        sc->sc_msr = new_msr;
        sc->sc_lsr = new_lsr;
 
-       if (onoff) {
+#if 0  /* missing pps_capture */
+       /*
+        * Time pulse counting support. Note that both CTS and DCD are
+        * active-low signals. The status bit is high to indicate that
+        * the signal on the line is low, which corresponds to a PPS
+        * clear event.
+        */
+       switch(ucom_pps_mode) {
+       case 1:
+               if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
+                   (msr_delta & SER_CTS)) {
+                       pps_capture(&sc->sc_pps);
+                       pps_event(&sc->sc_pps, (sc->sc_msr & SER_CTS) ?
+                           PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
+               }
+               break;
+       case 2:
+               if ((sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) &&
+                   (msr_delta & SER_DCD)) {
+                       pps_capture(&sc->sc_pps);
+                       pps_event(&sc->sc_pps, (sc->sc_msr & SER_DCD) ?
+                           PPS_CAPTURECLEAR : PPS_CAPTUREASSERT);
+               }
+               break;
+       default:
+               break;
+       }
+#endif
+
+       if (msr_delta & SER_DCD) {
+
+               int onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
 
-               onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
                DPRINTF("DCD changed to %d\n", onoff);
-               (*linesw[tp->t_line].l_modem)(tp, onoff);
+
+               (*linesw[tp->t_line].l_modem)(tp, onoff);
        }
 
        if ((lsr_delta & ULSR_BI) && (sc->sc_lsr & ULSR_BI)) {
index 14267e9..73c1ec9 100644 (file)
@@ -70,6 +70,7 @@
 #include <sys/tty.h>
 #include <sys/fcntl.h>
 #include <sys/sysctl.h>
+#include <sys/timepps.h>
 
 /* We should seriously clean this up! */
 #define SET(t, f)      (t) |= (f)
@@ -166,6 +167,8 @@ struct ucom_softc {
        struct ucom_cfg_task    sc_line_state_task[2];
        struct ucom_cfg_task    sc_status_task[2];
        struct ucom_param_task  sc_param_task[2];
+       /* pulse capturing support, PPS */
+       struct pps_state        sc_pps;
        /* Used to set "UCOM_FLAG_GP_DATA" flag: */
        struct usb_proc_msg     *sc_last_start_xfer;
        const struct ucom_callback *sc_callback;
index a280ec0..eff578e 100644 (file)
@@ -49,7 +49,7 @@
 static int uslcom_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
-SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uslcom_debug, 0, "Debug level");
 #endif
 
index dda188c..2d203bb 100644 (file)
@@ -78,7 +78,7 @@
 static int uvisor_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor");
-SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uvisor, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uvisor_debug, 0, "Debug level");
 #endif
 
index fc69d3e..d536ca4 100644 (file)
@@ -66,7 +66,7 @@
 static int uvscom_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
-SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uvscom_debug, 0, "Debug level");
 #endif
 
index 496e7ce..783f0ce 100644 (file)
@@ -76,7 +76,7 @@
 static int urio_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio");
-SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_urio, OID_AUTO, debug, CTLFLAG_RWTUN,
     &urio_debug, 0, "urio debug level");
 #endif
 
index 46168c5..1ad8690 100644 (file)
@@ -65,7 +65,7 @@
 static int ustorage_fs_debug = 0;
 
 SYSCTL_NODE(_hw_usb, OID_AUTO, ustorage_fs, CTLFLAG_RW, 0, "USB ustorage_fs");
-SYSCTL_INT(_hw_usb_ustorage_fs, OID_AUTO, debug, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb_ustorage_fs, OID_AUTO, debug, CTLFLAG_RWTUN,
     &ustorage_fs_debug, 0, "ustorage_fs debug level");
 #endif
 
index acdcd12..b9f0cd3 100644 (file)
@@ -34,6 +34,7 @@ SRCS= opt_bus.h opt_usb.h device_if.h bus_if.h usb_if.h usbdevs.h \
        usb_template_modem.c \
        usb_template_mouse.c \
        usb_template_msc.c \
-       usb_template_mtp.c
+       usb_template_mtp.c \
+       usb_template_phone.c
 
 .include <bsd.kmod.mk>
index 50db65a..3b0040c 100644 (file)
@@ -1365,6 +1365,9 @@ usb_temp_setup_by_index(struct usb_device *udev, uint16_t index)
        case USB_TEMP_MOUSE:
                err = usb_temp_setup(udev, &usb_template_mouse);
                break;
+       case USB_TEMP_SERIALNET:
+               err = usb_temp_setup(udev, &usb_template_serialnet);
+               break;
        default:
                return (USB_ERR_INVAL);
        }
index 6c94e66..bb32fa1 100644 (file)
@@ -105,6 +105,8 @@ extern const struct usb_temp_device_desc usb_template_modem;
 extern const struct usb_temp_device_desc usb_template_mouse;
 extern const struct usb_temp_device_desc usb_template_msc;
 extern const struct usb_temp_device_desc usb_template_mtp;
+extern const struct usb_temp_device_desc usb_template_phone;
+extern const struct usb_temp_device_desc usb_template_serialnet;
 
 usb_error_t    usb_temp_setup(struct usb_device *,
                    const struct usb_temp_device_desc *);
diff --git a/sys/bus/u4b/template/usb_template_phone.c b/sys/bus/u4b/template/usb_template_phone.c
new file mode 100644 (file)
index 0000000..114409d
--- /dev/null
@@ -0,0 +1,417 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2014 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the USB template for an USB phone device.
+ */
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <bus/u4b/usb.h>
+#include <bus/u4b/usbdi.h>
+#include <bus/u4b/usb_core.h>
+#include <bus/u4b/usb_cdc.h>
+
+#include <bus/u4b/template/usb_template.h>
+#endif                 /* USB_GLOBAL_INCLUDE_FILE */
+
+enum {
+       INDEX_PHONE_LANG,
+       INDEX_PHONE_MIXER,
+       INDEX_PHONE_RECORD,
+       INDEX_PHONE_PLAYBACK,
+       INDEX_PHONE_PRODUCT,
+       INDEX_PHONE_HID,
+       INDEX_PHONE_MAX,
+};
+
+#define        STRING_PHONE_PRODUCT \
+  "U\0S\0B\0 \0P\0h\0o\0n\0e\0 \0D\0e\0v\0i\0c\0e"
+
+#define        STRING_PHONE_MIXER \
+  "M\0i\0x\0e\0r\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e"
+
+#define        STRING_PHONE_RECORD \
+  "R\0e\0c\0o\0r\0d\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e"
+
+#define        STRING_PHONE_PLAYBACK \
+  "P\0l\0a\0y\0b\0a\0c\0k\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e"
+
+#define        STRING_PHONE_HID \
+  "H\0I\0D\0 \0i\0n\0t\0e\0r\0f\0a\0c\0e"
+
+/* make the real string descriptors */
+
+USB_MAKE_STRING_DESC(STRING_PHONE_MIXER, string_phone_mixer);
+USB_MAKE_STRING_DESC(STRING_PHONE_RECORD, string_phone_record);
+USB_MAKE_STRING_DESC(STRING_PHONE_PLAYBACK, string_phone_playback);
+USB_MAKE_STRING_DESC(STRING_PHONE_PRODUCT, string_phone_product);
+USB_MAKE_STRING_DESC(STRING_PHONE_HID, string_phone_hid);
+
+/* prototypes */
+
+/*
+ * Phone Mixer description structures
+ *
+ * Some of the phone descriptors were dumped from no longer in
+ * production Yealink VOIP USB phone adapter:
+ */
+static uint8_t phone_hid_descriptor[] = {
+       0x05, 0x0b, 0x09, 0x01, 0xa1, 0x01, 0x05, 0x09,
+       0x19, 0x01, 0x29, 0x3f, 0x15, 0x00, 0x25, 0x01,
+       0x75, 0x01, 0x95, 0x80, 0x81, 0x00, 0x05, 0x08,
+       0x19, 0x01, 0x29, 0x10, 0x15, 0x00, 0x25, 0x01,
+       0x75, 0x01, 0x95, 0x80, 0x91, 0x00, 0xc0
+};
+
+static const uint8_t phone_raw_desc_0[] = {
+       0x0a, 0x24, 0x01, 0x00, 0x01, 0x4a, 0x00, 0x02,
+       0x01, 0x02
+};
+
+static const uint8_t phone_raw_desc_1[] = {
+       0x0c, 0x24, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01,
+       0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t phone_raw_desc_2[] = {
+       0x0c, 0x24, 0x02, 0x02, 0x01, 0x01, 0x00, 0x01,
+       0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t phone_raw_desc_3[] = {
+       0x09, 0x24, 0x03, 0x03, 0x01, 0x03, 0x00, 0x06,
+       0x00
+};
+
+static const uint8_t phone_raw_desc_4[] = {
+       0x09, 0x24, 0x03, 0x04, 0x01, 0x01, 0x00, 0x05,
+       0x00
+};
+
+static const uint8_t phone_raw_desc_5[] = {
+       0x0b, 0x24, 0x06, 0x05, 0x01, 0x02, 0x03, 0x00,
+       0x03, 0x00, 0x00
+};
+
+static const uint8_t phone_raw_desc_6[] = {
+       0x0b, 0x24, 0x06, 0x06, 0x02, 0x02, 0x03, 0x00,
+       0x03, 0x00, 0x00
+};
+
+static const void *phone_raw_iface_0_desc[] = {
+       phone_raw_desc_0,
+       phone_raw_desc_1,
+       phone_raw_desc_2,
+       phone_raw_desc_3,
+       phone_raw_desc_4,
+       phone_raw_desc_5,
+       phone_raw_desc_6,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc phone_iface_0 = {
+       .ppEndpoints = NULL,            /* no endpoints */
+       .ppRawDesc = phone_raw_iface_0_desc,
+       .bInterfaceClass = 1,
+       .bInterfaceSubClass = 1,
+       .bInterfaceProtocol = 0,
+       .iInterface = INDEX_PHONE_MIXER,
+};
+
+static const uint8_t phone_raw_desc_20[] = {
+       0x07, 0x24, 0x01, 0x04, 0x01, 0x01, 0x00
+};
+
+static const uint8_t phone_raw_desc_21[] = {
+       0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01,
+       /* 8kHz */
+       0x40, 0x1f, 0x00
+};
+
+static const uint8_t phone_raw_desc_22[] = {
+       0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00
+};
+
+static const void *phone_raw_iface_1_desc[] = {
+       phone_raw_desc_20,
+       phone_raw_desc_21,
+       NULL,
+};
+
+static const void *phone_raw_ep_1_desc[] = {
+       phone_raw_desc_22,
+       NULL,
+};
+
+static const struct usb_temp_packet_size phone_isoc_mps = {
+       .mps[USB_SPEED_FULL] = 0x10,
+       .mps[USB_SPEED_HIGH] = 0x10,
+};
+
+static const struct usb_temp_interval phone_isoc_interval = {
+       .bInterval[USB_SPEED_FULL] = 1, /* 1:1 */
+       .bInterval[USB_SPEED_HIGH] = 4, /* 1:8 */
+};
+
+static const struct usb_temp_endpoint_desc phone_isoc_in_ep = {
+       .ppRawDesc = phone_raw_ep_1_desc,
+       .pPacketSize = &phone_isoc_mps,
+       .pIntervals = &phone_isoc_interval,
+       .bEndpointAddress = UE_DIR_IN,
+       .bmAttributes = UE_ISOCHRONOUS,
+};
+
+static const struct usb_temp_endpoint_desc *phone_iface_1_ep[] = {
+       &phone_isoc_in_ep,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc phone_iface_1_alt_0 = {
+       .ppEndpoints = NULL,            /* no endpoints */
+       .ppRawDesc = NULL,              /* no raw descriptors */
+       .bInterfaceClass = 1,
+       .bInterfaceSubClass = 2,
+       .bInterfaceProtocol = 0,
+       .iInterface = INDEX_PHONE_PLAYBACK,
+};
+
+static const struct usb_temp_interface_desc phone_iface_1_alt_1 = {
+       .ppEndpoints = phone_iface_1_ep,
+       .ppRawDesc = phone_raw_iface_1_desc,
+       .bInterfaceClass = 1,
+       .bInterfaceSubClass = 2,
+       .bInterfaceProtocol = 0,
+       .iInterface = INDEX_PHONE_PLAYBACK,
+       .isAltInterface = 1,            /* this is an alternate setting */
+};
+
+static const uint8_t phone_raw_desc_30[] = {
+       0x07, 0x24, 0x01, 0x02, 0x01, 0x01, 0x00
+};
+
+static const uint8_t phone_raw_desc_31[] = {
+       0x0b, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x01,
+       /* 8kHz */
+       0x40, 0x1f, 0x00
+};
+
+static const uint8_t phone_raw_desc_32[] = {
+       0x07, 0x25, 0x01, 0x00, 0x00, 0x00, 0x00
+};
+
+static const void *phone_raw_iface_2_desc[] = {
+       phone_raw_desc_30,
+       phone_raw_desc_31,
+       NULL,
+};
+
+static const void *phone_raw_ep_2_desc[] = {
+       phone_raw_desc_32,
+       NULL,
+};
+
+static const struct usb_temp_endpoint_desc phone_isoc_out_ep = {
+       .ppRawDesc = phone_raw_ep_2_desc,
+       .pPacketSize = &phone_isoc_mps,
+       .pIntervals = &phone_isoc_interval,
+       .bEndpointAddress = UE_DIR_OUT,
+       .bmAttributes = UE_ISOCHRONOUS,
+};
+
+static const struct usb_temp_endpoint_desc *phone_iface_2_ep[] = {
+       &phone_isoc_out_ep,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc phone_iface_2_alt_0 = {
+       .ppEndpoints = NULL,            /* no endpoints */
+       .ppRawDesc = NULL,              /* no raw descriptors */
+       .bInterfaceClass = 1,
+       .bInterfaceSubClass = 2,
+       .bInterfaceProtocol = 0,
+       .iInterface = INDEX_PHONE_RECORD,
+};
+
+static const struct usb_temp_interface_desc phone_iface_2_alt_1 = {
+       .ppEndpoints = phone_iface_2_ep,
+       .ppRawDesc = phone_raw_iface_2_desc,
+       .bInterfaceClass = 1,
+       .bInterfaceSubClass = 2,
+       .bInterfaceProtocol = 0,
+       .iInterface = INDEX_PHONE_RECORD,
+       .isAltInterface = 1,            /* this is an alternate setting */
+};
+
+static const uint8_t phone_hid_raw_desc_0[] = {
+       0x09, 0x21, 0x00, 0x01, 0x00, 0x01, 0x22, sizeof(phone_hid_descriptor),
+       0x00
+};
+
+static const void *phone_hid_desc_0[] = {
+       phone_hid_raw_desc_0,
+       NULL,
+};
+
+static const struct usb_temp_packet_size phone_hid_mps = {
+       .mps[USB_SPEED_FULL] = 0x10,
+       .mps[USB_SPEED_HIGH] = 0x10,
+};
+
+static const struct usb_temp_interval phone_hid_interval = {
+       .bInterval[USB_SPEED_FULL] = 2,         /* 2ms */
+       .bInterval[USB_SPEED_HIGH] = 2,         /* 2ms */
+};
+
+static const struct usb_temp_endpoint_desc phone_hid_in_ep = {
+       .pPacketSize = &phone_hid_mps,
+       .pIntervals = &phone_hid_interval,
+       .bEndpointAddress = UE_DIR_IN,
+       .bmAttributes = UE_INTERRUPT,
+};
+
+static const struct usb_temp_endpoint_desc *phone_iface_3_ep[] = {
+       &phone_hid_in_ep,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc phone_iface_3 = {
+       .ppEndpoints = phone_iface_3_ep,
+       .ppRawDesc = phone_hid_desc_0,
+       .bInterfaceClass = 3,
+       .bInterfaceSubClass = 0,
+       .bInterfaceProtocol = 0,
+       .iInterface = INDEX_PHONE_HID,
+};
+
+static const struct usb_temp_interface_desc *phone_interfaces[] = {
+       &phone_iface_0,
+       &phone_iface_1_alt_0,
+       &phone_iface_1_alt_1,
+       &phone_iface_2_alt_0,
+       &phone_iface_2_alt_1,
+       &phone_iface_3,
+       NULL,
+};
+
+static const struct usb_temp_config_desc phone_config_desc = {
+       .ppIfaceDesc = phone_interfaces,
+       .bmAttributes = UC_BUS_POWERED,
+       .bMaxPower = 25,                /* 50 mA */
+       .iConfiguration = INDEX_PHONE_PRODUCT,
+};
+
+static const struct usb_temp_config_desc *phone_configs[] = {
+       &phone_config_desc,
+       NULL,
+};
+
+static usb_temp_get_string_desc_t phone_get_string_desc;
+static usb_temp_get_vendor_desc_t phone_get_vendor_desc;
+
+const struct usb_temp_device_desc usb_template_phone = {
+       .getStringDesc = &phone_get_string_desc,
+       .getVendorDesc = &phone_get_vendor_desc,
+       .ppConfigDesc = phone_configs,
+       .idVendor = USB_TEMPLATE_VENDOR,
+       .idProduct = 0xb001,
+       .bcdDevice = 0x0100,
+       .bDeviceClass = UDCLASS_IN_INTERFACE,
+       .bDeviceSubClass = 0,
+       .bDeviceProtocol = 0,
+       .iManufacturer = 0,
+       .iProduct = INDEX_PHONE_PRODUCT,
+       .iSerialNumber = 0,
+};
+
+/*------------------------------------------------------------------------*
+ *      phone_get_vendor_desc
+ *
+ * Return values:
+ * NULL: Failure. No such vendor descriptor.
+ * Else: Success. Pointer to vendor descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+phone_get_vendor_desc(const struct usb_device_request *req, uint16_t *plen)
+{
+       if ((req->bmRequestType == 0x81) && (req->bRequest == 0x06) &&
+           (req->wValue[0] == 0x00) && (req->wValue[1] == 0x22) &&
+           (req->wIndex[1] == 0) && (req->wIndex[0] == 3 /* iface */)) {
+
+               *plen = sizeof(phone_hid_descriptor);
+               return (phone_hid_descriptor);
+       }
+       return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ *     phone_get_string_desc
+ *
+ * Return values:
+ * NULL: Failure. No such string.
+ * Else: Success. Pointer to string descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+phone_get_string_desc(uint16_t lang_id, uint8_t string_index)
+{
+       static const void *ptr[INDEX_PHONE_MAX] = {
+               [INDEX_PHONE_LANG] = &usb_string_lang_en,
+               [INDEX_PHONE_MIXER] = &string_phone_mixer,
+               [INDEX_PHONE_RECORD] = &string_phone_record,
+               [INDEX_PHONE_PLAYBACK] = &string_phone_playback,
+               [INDEX_PHONE_PRODUCT] = &string_phone_product,
+               [INDEX_PHONE_HID] = &string_phone_hid,
+       };
+
+       if (string_index == 0) {
+               return (&usb_string_lang_en);
+       }
+       if (lang_id != 0x0409) {
+               return (NULL);
+       }
+       if (string_index < INDEX_PHONE_MAX) {
+               return (ptr[string_index]);
+       }
+       return (NULL);
+}
diff --git a/sys/bus/u4b/template/usb_template_serialnet.c b/sys/bus/u4b/template/usb_template_serialnet.c
new file mode 100644 (file)
index 0000000..beb5e9c
--- /dev/null
@@ -0,0 +1,387 @@
+/*-
+ * Copyright (c) 2015 Ruslan Bukin <br@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * This file contains the USB template for USB Networking and Serial
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#ifdef USB_GLOBAL_INCLUDE_FILE
+#include USB_GLOBAL_INCLUDE_FILE
+#else
+#include <sys/stdint.h>
+#include <sys/stddef.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/sx.h>
+#include <sys/unistd.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/priv.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_cdc.h>
+
+#include <dev/usb/template/usb_template.h>
+#endif         /* USB_GLOBAL_INCLUDE_FILE */
+
+#define        MODEM_IFACE_0 0
+#define        MODEM_IFACE_1 1
+
+enum {
+       STRING_LANG_INDEX,
+       STRING_MODEM_INDEX,
+       STRING_ETH_MAC_INDEX,
+       STRING_ETH_CONTROL_INDEX,
+       STRING_ETH_DATA_INDEX,
+       STRING_ETH_CONFIG_INDEX,
+       STRING_CONFIG_INDEX,
+       STRING_VENDOR_INDEX,
+       STRING_PRODUCT_INDEX,
+       STRING_SERIAL_INDEX,
+       STRING_MAX,
+};
+
+#define        STRING_MODEM \
+  "U\0S\0B\0 \0M\0o\0d\0e\0m\0 \0I\0n\0t\0e\0r\0f\0a\0c\0e"
+
+#define        STRING_ETH_MAC \
+  "2\0A\0002\0003\0004\0005\0006\0007\08\09\0A\0B"
+
+#define        STRING_ETH_CONTROL \
+  "U\0S\0B\0 \0E\0t\0h\0e\0r\0n\0e\0t\0 " \
+  "\0C\0o\0m\0m\0 \0I\0n\0t\0e\0r\0f\0a\0c\0e"
+
+#define        STRING_ETH_DATA \
+  "U\0S\0B\0 \0E\0t\0h\0e\0r\0n\0e\0t\0 \0D\0a\0t\0a\0 " \
+  "\0I\0n\0t\0e\0r\0f\0a\0c\0e"
+
+#define        STRING_CONFIG \
+  "D\0e\0f\0a\0u\0l\0t\0 \0c\0o\0n\0f\0i\0g\0u\0r\0a\0t\0i\0o\0n"
+
+#define        STRING_VENDOR \
+  "T\0h\0e\0 \0F\0r\0e\0e\0B\0S\0D\0 \0P\0r\0o\0j\0e\0c\0t"
+
+#define        STRING_PRODUCT \
+  "S\0E\0R\0I\0A\0L\0N\0E\0T"
+
+#define        STRING_SERIAL \
+  "J\0a\0n\0u\0a\0r\0y\0 \0002\0000\0001\0005"
+
+/* make the real string descriptors */
+
+USB_MAKE_STRING_DESC(STRING_MODEM, string_modem);
+USB_MAKE_STRING_DESC(STRING_ETH_MAC, string_eth_mac);
+USB_MAKE_STRING_DESC(STRING_ETH_CONTROL, string_eth_control);
+USB_MAKE_STRING_DESC(STRING_ETH_DATA, string_eth_data);
+USB_MAKE_STRING_DESC(STRING_CONFIG, string_serialnet_config);
+USB_MAKE_STRING_DESC(STRING_VENDOR, string_serialnet_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, string_serialnet_product);
+USB_MAKE_STRING_DESC(STRING_SERIAL, string_serialnet_serial);
+
+/* prototypes */
+
+static usb_temp_get_string_desc_t serialnet_get_string_desc;
+
+static const struct usb_cdc_union_descriptor eth_union_desc = {
+       .bLength = sizeof(eth_union_desc),
+       .bDescriptorType = UDESC_CS_INTERFACE,
+       .bDescriptorSubtype = UDESCSUB_CDC_UNION,
+       .bMasterInterface = 0,          /* this is automatically updated */
+       .bSlaveInterface[0] = 1,        /* this is automatically updated */
+};
+
+static const struct usb_cdc_header_descriptor eth_header_desc = {
+       .bLength = sizeof(eth_header_desc),
+       .bDescriptorType = UDESC_CS_INTERFACE,
+       .bDescriptorSubtype = UDESCSUB_CDC_HEADER,
+       .bcdCDC[0] = 0x10,
+       .bcdCDC[1] = 0x01,
+};
+
+static const struct usb_cdc_ethernet_descriptor eth_enf_desc = {
+       .bLength = sizeof(eth_enf_desc),
+       .bDescriptorType = UDESC_CS_INTERFACE,
+       .bDescriptorSubtype = UDESCSUB_CDC_ENF,
+       .iMacAddress = STRING_ETH_MAC_INDEX,
+       .bmEthernetStatistics = {0, 0, 0, 0},
+       .wMaxSegmentSize = {0xEA, 0x05},/* 1514 bytes */
+       .wNumberMCFilters = {0, 0},
+       .bNumberPowerFilters = 0,
+};
+
+static const void *eth_control_if_desc[] = {
+       &eth_union_desc,
+       &eth_header_desc,
+       &eth_enf_desc,
+       NULL,
+};
+
+static const struct usb_temp_packet_size bulk_mps = {
+       .mps[USB_SPEED_FULL] = 64,
+       .mps[USB_SPEED_HIGH] = 512,
+};
+
+static const struct usb_temp_packet_size intr_mps = {
+       .mps[USB_SPEED_FULL] = 8,
+       .mps[USB_SPEED_HIGH] = 8,
+};
+
+static const struct usb_temp_endpoint_desc bulk_in_ep = {
+       .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_IN_EP_0
+       .bEndpointAddress = USB_HIP_IN_EP_0,
+#else
+       .bEndpointAddress = UE_DIR_IN,
+#endif
+       .bmAttributes = UE_BULK,
+};
+
+static const struct usb_temp_endpoint_desc bulk_out_ep = {
+       .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_OUT_EP_0
+       .bEndpointAddress = USB_HIP_OUT_EP_0,
+#else
+       .bEndpointAddress = UE_DIR_OUT,
+#endif
+       .bmAttributes = UE_BULK,
+};
+
+static const struct usb_temp_endpoint_desc intr_in_ep = {
+       .pPacketSize = &intr_mps,
+       .bEndpointAddress = UE_DIR_IN,
+       .bmAttributes = UE_INTERRUPT,
+};
+
+static const struct usb_temp_endpoint_desc *eth_intr_endpoints[] = {
+       &intr_in_ep,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc eth_control_interface = {
+       .ppEndpoints = eth_intr_endpoints,
+       .ppRawDesc = eth_control_if_desc,
+       .bInterfaceClass = UICLASS_CDC,
+       .bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL,
+       .bInterfaceProtocol = 0,
+       .iInterface = STRING_ETH_CONTROL_INDEX,
+};
+
+static const struct usb_temp_endpoint_desc *eth_data_endpoints[] = {
+       &bulk_in_ep,
+       &bulk_out_ep,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc eth_data_null_interface = {
+       .ppEndpoints = NULL,            /* no endpoints */
+       .bInterfaceClass = UICLASS_CDC_DATA,
+       .bInterfaceSubClass = 0,
+       .bInterfaceProtocol = 0,
+       .iInterface = STRING_ETH_DATA_INDEX,
+};
+
+static const struct usb_temp_interface_desc eth_data_interface = {
+       .ppEndpoints = eth_data_endpoints,
+       .bInterfaceClass = UICLASS_CDC_DATA,
+       .bInterfaceSubClass = UISUBCLASS_DATA,
+       .bInterfaceProtocol = 0,
+       .iInterface = STRING_ETH_DATA_INDEX,
+       .isAltInterface = 1,            /* this is an alternate setting */
+};
+
+static const struct usb_temp_packet_size modem_bulk_mps = {
+       .mps[USB_SPEED_LOW] = 8,
+       .mps[USB_SPEED_FULL] = 64,
+       .mps[USB_SPEED_HIGH] = 512,
+};
+
+static const struct usb_temp_packet_size modem_intr_mps = {
+       .mps[USB_SPEED_LOW] = 8,
+       .mps[USB_SPEED_FULL] = 8,
+       .mps[USB_SPEED_HIGH] = 8,
+};
+
+static const struct usb_temp_interval modem_intr_interval = {
+       .bInterval[USB_SPEED_LOW] = 8,  /* 8ms */
+       .bInterval[USB_SPEED_FULL] = 8, /* 8ms */
+       .bInterval[USB_SPEED_HIGH] = 7, /* 8ms */
+};
+
+static const struct usb_temp_endpoint_desc modem_ep_0 = {
+       .pPacketSize = &modem_intr_mps,
+       .pIntervals = &modem_intr_interval,
+       .bEndpointAddress = UE_DIR_IN,
+       .bmAttributes = UE_INTERRUPT,
+};
+
+static const struct usb_temp_endpoint_desc modem_ep_1 = {
+       .pPacketSize = &modem_bulk_mps,
+       .bEndpointAddress = UE_DIR_OUT,
+       .bmAttributes = UE_BULK,
+};
+
+static const struct usb_temp_endpoint_desc modem_ep_2 = {
+       .pPacketSize = &modem_bulk_mps,
+       .bEndpointAddress = UE_DIR_IN,
+       .bmAttributes = UE_BULK,
+};
+
+static const struct usb_temp_endpoint_desc *modem_iface_0_ep[] = {
+       &modem_ep_0,
+       NULL,
+};
+
+static const struct usb_temp_endpoint_desc *modem_iface_1_ep[] = {
+       &modem_ep_1,
+       &modem_ep_2,
+       NULL,
+};
+
+static const uint8_t modem_raw_desc_0[] = {
+       0x05, 0x24, 0x00, 0x10, 0x01
+};
+
+static const uint8_t modem_raw_desc_1[] = {
+       0x05, 0x24, 0x06, MODEM_IFACE_0, MODEM_IFACE_1
+};
+
+static const uint8_t modem_raw_desc_2[] = {
+       0x05, 0x24, 0x01, 0x03, MODEM_IFACE_1
+};
+
+static const uint8_t modem_raw_desc_3[] = {
+       0x04, 0x24, 0x02, 0x07
+};
+
+static const void *modem_iface_0_desc[] = {
+       &modem_raw_desc_0,
+       &modem_raw_desc_1,
+       &modem_raw_desc_2,
+       &modem_raw_desc_3,
+       NULL,
+};
+
+static const struct usb_temp_interface_desc modem_iface_0 = {
+       .ppRawDesc = modem_iface_0_desc,
+       .ppEndpoints = modem_iface_0_ep,
+       .bInterfaceClass = 2,
+       .bInterfaceSubClass = 2,
+       .bInterfaceProtocol = 1,
+       .iInterface = STRING_MODEM_INDEX,
+};
+
+static const struct usb_temp_interface_desc modem_iface_1 = {
+       .ppEndpoints = modem_iface_1_ep,
+       .bInterfaceClass = 10,
+       .bInterfaceSubClass = 0,
+       .bInterfaceProtocol = 0,
+       .iInterface = STRING_MODEM_INDEX,
+};
+
+static const struct usb_temp_interface_desc *serialnet_interfaces[] = {
+       &modem_iface_0,
+       &modem_iface_1,
+       &eth_control_interface,
+       &eth_data_null_interface,
+       &eth_data_interface,
+       NULL,
+};
+
+static const struct usb_temp_config_desc serialnet_config_desc = {
+       .ppIfaceDesc = serialnet_interfaces,
+       .bmAttributes = UC_BUS_POWERED,
+       .bMaxPower = 25,                /* 50 mA */
+       .iConfiguration = STRING_CONFIG_INDEX,
+};
+static const struct usb_temp_config_desc *serialnet_configs[] = {
+       &serialnet_config_desc,
+       NULL,
+};
+
+const struct usb_temp_device_desc usb_template_serialnet = {
+       .getStringDesc = &serialnet_get_string_desc,
+       .ppConfigDesc = serialnet_configs,
+       .idVendor = USB_TEMPLATE_VENDOR,
+       .idProduct = 0x0001,
+       .bcdDevice = 0x0100,
+       .bDeviceClass = UDCLASS_COMM,
+       .bDeviceSubClass = 0,
+       .bDeviceProtocol = 0,
+       .iManufacturer = STRING_VENDOR_INDEX,
+       .iProduct = STRING_PRODUCT_INDEX,
+       .iSerialNumber = STRING_SERIAL_INDEX,
+};
+
+/*------------------------------------------------------------------------*
+ *     serialnet_get_string_desc
+ *
+ * Return values:
+ * NULL: Failure. No such string.
+ * Else: Success. Pointer to string descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+serialnet_get_string_desc(uint16_t lang_id, uint8_t string_index)
+{
+       static const void *ptr[STRING_MAX] = {
+               [STRING_LANG_INDEX] = &usb_string_lang_en,
+               [STRING_MODEM_INDEX] = &string_modem,
+               [STRING_ETH_MAC_INDEX] = &string_eth_mac,
+               [STRING_ETH_CONTROL_INDEX] = &string_eth_control,
+               [STRING_ETH_DATA_INDEX] = &string_eth_data,
+               [STRING_CONFIG_INDEX] = &string_serialnet_config,
+               [STRING_VENDOR_INDEX] = &string_serialnet_vendor,
+               [STRING_PRODUCT_INDEX] = &string_serialnet_product,
+               [STRING_SERIAL_INDEX] = &string_serialnet_serial,
+       };
+
+       if (string_index == 0) {
+               return (&usb_string_lang_en);
+       }
+       if (lang_id != 0x0409) {
+               return (NULL);
+       }
+       if (string_index < STRING_MAX) {
+               return (ptr[string_index]);
+       }
+       return (NULL);
+}
index 12ef406..61cd803 100644 (file)
@@ -27,6 +27,8 @@
 #ifndef _USB_BUS_H_
 #define        _USB_BUS_H_
 
+struct usb_fs_privdata;
+
 /*
  * The following structure defines the USB explore message sent to the USB
  * explore process.
@@ -83,6 +85,10 @@ struct usb_bus {
        struct usb_bus_msg resume_msg[2];
        struct usb_bus_msg reset_msg[2];
        struct usb_bus_msg shutdown_msg[2];
+#if USB_HAVE_UGEN
+       struct usb_bus_msg cleanup_msg[2];
+       LIST_HEAD(,usb_fs_privdata) pd_cleanup_list;
+#endif
        /*
         * This mutex protects the USB hardware:
         */
index 1b3df4c..c2f8460 100644 (file)
@@ -295,8 +295,8 @@ error:
                usbd_enum_unlock(cpd->udev);
 
        if (crd->is_uref) {
-               cpd->udev->refcount--;
-               cv_broadcast(&cpd->udev->ref_cv);
+               if (--(cpd->udev->refcount) == 0)
+                       cv_broadcast(&cpd->udev->ref_cv);
        }
        lockmgr(&usb_ref_lock, LK_RELEASE);
        DPRINTFN(2, "fail\n");
@@ -367,8 +367,8 @@ usb_unref_device(struct usb_cdev_privdata *cpd,
        }
        if (crd->is_uref) {
                crd->is_uref = 0;
-               cpd->udev->refcount--;
-               cv_broadcast(&cpd->udev->ref_cv);
+               if (--(cpd->udev->refcount) == 0)
+                       cv_broadcast(&cpd->udev->ref_cv);
        }
        lockmgr(&usb_ref_lock, LK_RELEASE);
 }
@@ -594,12 +594,12 @@ usb_fifo_free(struct usb_fifo *f)
 
        /* decrease refcount */
        f->refcount--;
-       /* prevent any write flush */
-       f->flag_iserror = 1;
        /* need to wait until all callers have exited */
        while (f->refcount != 0) {
                lockmgr(&usb_ref_lock, LK_RELEASE);     /* avoid LOR */
                lockmgr(f->priv_lock, LK_EXCLUSIVE);
+               /* prevent write flush, if any */
+               f->flag_iserror = 1;
                /* get I/O thread out of any sleep state */
                if (f->flag_sleeping) {
                        f->flag_sleeping = 0;
index 6736447..6d68e04 100644 (file)
@@ -446,68 +446,29 @@ usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep)
        return (NULL);
 }
 
-#if USB_HAVE_UGEN
-static uint16_t
-usb_get_refcount(struct usb_device *udev)
-{
-       if (usb_proc_is_called_from(USB_BUS_EXPLORE_PROC(udev->bus)) ||
-           usb_proc_is_called_from(USB_BUS_CONTROL_XFER_PROC(udev->bus)))
-               return (1);
-       return (2);
-}
-#endif
-
 /*------------------------------------------------------------------------*
- *     usb_wait_pending_ref_locked
+ *     usb_wait_pending_refs
  *
  * This function will wait for any USB references to go away before
- * returning and disable further USB device refcounting on the
- * specified USB device. This function is used when detaching a USB
- * device.
+ * returning. This function is used before freeing a USB device.
  *------------------------------------------------------------------------*/
 static void
-usb_wait_pending_ref_locked(struct usb_device *udev)
+usb_wait_pending_refs(struct usb_device *udev)
 {
 #if USB_HAVE_UGEN
-       const uint16_t refcount = usb_get_refcount(udev);
-
-       DPRINTF("Refcount = %d\n", (int)refcount); 
+       DPRINTF("Refcount = %d\n", (int)udev->refcount);
 
+       lockmgr(&usb_ref_lock, LK_EXCLUSIVE);
+       udev->refcount--;
        while (1) {
                /* wait for any pending references to go away */
-               lockmgr(&usb_ref_lock, LK_EXCLUSIVE);
-               if (udev->refcount == refcount) {
-                       /* prevent further refs being taken */
+               if (udev->refcount == 0) {
+                       /* prevent further refs being taken, if any */
                        udev->refcount = USB_DEV_REF_MAX;
-                       lockmgr(&usb_ref_lock, LK_RELEASE);
                        break;
                }
-               usbd_enum_unlock(udev);
                cv_wait(&udev->ref_cv, &usb_ref_lock);
-               lockmgr(&usb_ref_lock, LK_RELEASE);
-               (void) usbd_enum_lock(udev);
        }
-#endif
-}
-
-/*------------------------------------------------------------------------*
- *     usb_ref_restore_locked
- *
- * This function will restore the reference count value after a call
- * to "usb_wait_pending_ref_locked()".
- *------------------------------------------------------------------------*/
-static void
-usb_ref_restore_locked(struct usb_device *udev)
-{
-#if USB_HAVE_UGEN
-       const uint16_t refcount = usb_get_refcount(udev);
-
-       DPRINTF("Refcount = %d\n", (int)refcount); 
-
-       /* restore reference count and wakeup waiters, if any */
-       lockmgr(&usb_ref_lock, LK_EXCLUSIVE);
-       udev->refcount = refcount;
-       cv_broadcast(&udev->ref_cv);
        lockmgr(&usb_ref_lock, LK_RELEASE);
 #endif
 }
@@ -1186,9 +1147,6 @@ usb_detach_device(struct usb_device *udev, uint8_t iface_index,
 
        KKASSERT(lockowned(&udev->enum_lock));
 
-       /* wait for pending refs to go away */
-       usb_wait_pending_ref_locked(udev);
-
        /*
         * First detach the child to give the child's detach routine a
         * chance to detach the sub-devices in the correct order.
@@ -1215,8 +1173,6 @@ usb_detach_device(struct usb_device *udev, uint8_t iface_index,
                usb_detach_device_sub(udev, &iface->subdev,
                    &iface->pnpinfo, flag);
        }
-
-       usb_ref_restore_locked(udev);
 }
 
 /*------------------------------------------------------------------------*
@@ -2034,15 +1990,49 @@ usb_make_dev(struct usb_device *udev, const char *devname, int ep,
        return (pd);
 }
 
+void
+usb_destroy_dev_sync(struct usb_fs_privdata *pd)
+{
+       DPRINTFN(1, "Destroying device at ugen%d.%d\n",
+           pd->bus_index, pd->dev_index);
+
+       /*
+        * Destroy character device synchronously. After this
+        * all system calls are returned. Can block.
+        */
+       destroy_dev(pd->cdev);
+
+       kfree(pd, M_USBDEV);
+}
+
 void
 usb_destroy_dev(struct usb_fs_privdata *pd)
 {
+       struct usb_bus *bus;
+
        if (pd == NULL)
                return;
+       lockmgr(&usb_ref_lock, LK_EXCLUSIVE);
+       bus = devclass_get_softc(usb_devclass_ptr, pd->bus_index);
+       lockmgr(&usb_ref_lock, LK_RELEASE);
 
-       destroy_dev(pd->cdev);
+       if (bus == NULL) {
+               usb_destroy_dev_sync(pd);
+               return;
+       }
 
-       kfree(pd, M_USBDEV);
+#if 0
+       /* XXX what is FreeBSD doing here? */
+       /* make sure we can re-use the device name */
+       delist_dev(pd->cdev);
+#endif
+
+       USB_BUS_LOCK(bus);
+       LIST_INSERT_HEAD(&bus->pd_cleanup_list, pd, pd_next);
+       /* get cleanup going */
+       usb_proc_msignal(USB_BUS_EXPLORE_PROC(bus),
+               &bus->cleanup_msg[0], &bus->cleanup_msg[1]);
+       USB_BUS_UNLOCK(bus);
 }
 
 static void
@@ -2191,6 +2181,9 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
            &udev->cs_msg[0], &udev->cs_msg[1]);
        USB_BUS_UNLOCK(udev->bus);
 
+       /* wait for all references to go away */
+       usb_wait_pending_refs(udev);
+
        lockuninit(&udev->enum_lock);
        lockuninit(&udev->sr_lock);
 
@@ -2680,14 +2673,8 @@ usb_fifo_free_wrap(struct usb_device *udev,
                        /* no need to free this FIFO */
                        continue;
                }
-               /* wait for pending refs to go away */
-               usb_wait_pending_ref_locked(udev);
-
                /* free this FIFO */
                usb_fifo_free(f);
-
-               /* restore refcount */
-               usb_ref_restore_locked(udev);
        }
 }
 #endif
index af9c65f..88f6355 100644 (file)
@@ -291,6 +291,7 @@ struct usb_device *usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
 struct usb_fs_privdata *usb_make_dev(struct usb_device *, const char *,
                    int, int, int, uid_t, gid_t, int);
 void   usb_destroy_dev(struct usb_fs_privdata *);
+void   usb_destroy_dev_sync(struct usb_fs_privdata *);
 #endif
 usb_error_t    usb_probe_and_attach(struct usb_device *udev,
                    uint8_t iface_index);
index e7b2236..f0a47b1 100644 (file)
@@ -87,7 +87,7 @@
 #define        USB_EP0_BUFSIZE         1024    /* bytes */
 #define        USB_CS_RESET_LIMIT      20      /* failures = 20 * 50 ms = 1sec */
 
-#define        USB_MAX_AUTO_QUIRK      4       /* maximum number of dynamic quirks */
+#define        USB_MAX_AUTO_QUIRK      8       /* maximum number of dynamic quirks */
 
 typedef uint32_t usb_timeout_t;                /* milliseconds */
 typedef uint32_t usb_frlength_t;       /* bytes */
index a4811a4..41a1f8f 100644 (file)
@@ -92,6 +92,7 @@ static int usb_power_timeout = 30;    /* seconds */
 
 SYSCTL_INT(_hw_usb, OID_AUTO, power_timeout, CTLFLAG_RW,
     &usb_power_timeout, 0, "USB power timeout");
+TUNABLE_INT("hw.usb.power_timeout", &usb_power_timeout);
 #endif
 
 #if USB_HAVE_DISABLE_ENUM
@@ -1680,10 +1681,19 @@ uhub_child_location_string(device_t parent, device_t child,
                }
                goto done;
        }
-       ksnprintf(buf, buflen, "bus=%u hubaddr=%u port=%u devaddr=%u interface=%u",
-           (res.udev->parent_hub != NULL) ? res.udev->parent_hub->device_index : 0,
-           res.portno, device_get_unit(res.udev->bus->bdev),
-           res.udev->device_index, res.iface_index);
+       ksnprintf(buf, buflen, "bus=%u hubaddr=%u port=%u devaddr=%u"
+               " interface=%u"
+#if USB_HAVE_UGEN
+               " ugen=%s"
+#endif
+               , device_get_unit(res.udev->bus->bdev)
+               , (res.udev->parent_hub != NULL) ?
+               res.udev->parent_hub->device_index : 0
+               , res.portno, res.udev->device_index, res.iface_index
+#if USB_HAVE_UGEN
+               , res.udev->ugen_name
+#endif
+               );
 done:
        return (0);
 }
@@ -1722,7 +1732,7 @@ uhub_child_pnpinfo_string(device_t parent, device_t child,
                    "release=0x%04x "
                    "mode=%s "
                    "intclass=0x%02x intsubclass=0x%02x "
-                   "intprotocol=0x%02x " "%s%s",
+                   "intprotocol=0x%02x" "%s%s",
                    UGETW(res.udev->ddesc.idVendor),
                    UGETW(res.udev->ddesc.idProduct),
                    res.udev->ddesc.bDeviceClass,
index 1f9edd9..593ce1f 100644 (file)
@@ -62,6 +62,7 @@ enum {
        USB_TEMP_KBD,           /* USB Keyboard */
        USB_TEMP_MOUSE,         /* USB Mouse */
        USB_TEMP_PHONE,         /* USB Phone */
+       USB_TEMP_SERIALNET,     /* USB CDC Ethernet and Modem */
        USB_TEMP_MAX,
 };
 
index e2b9bc5..fb970f1 100644 (file)
@@ -107,6 +107,8 @@ static uint8_t scsi_request_sense[] =       { 0x03, 0x00, 0x00, 0x00, 0x12, 0x00,
                                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 static uint8_t scsi_read_capacity[] =  { 0x25, 0x00, 0x00, 0x00, 0x00, 0x00,
                                          0x00, 0x00, 0x00, 0x00 };
+static uint8_t scsi_prevent_removal[] =        { 0x1e, 0, 0, 0, 1, 0 };
+static uint8_t scsi_allow_removal[] =  { 0x1e, 0, 0, 0, 0, 0 };
 
 #ifndef USB_MSCTEST_BULK_SIZE
 #define        USB_MSCTEST_BULK_SIZE   64      /* dummy */
@@ -696,10 +698,28 @@ usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index)
            USB_MS_HZ);
 
        if (err != 0) {
+               if (err != ERR_CSW_FAILED)
+                       goto error;
+               DPRINTF("Test unit ready failed\n");
+       }
 
+       err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0,
+           &scsi_prevent_removal, sizeof(scsi_prevent_removal),
+           USB_MS_HZ);
+
+       if (err == 0) {
+               err = bbb_command_start(sc, DIR_OUT, 0, NULL, 0,
+                   &scsi_allow_removal, sizeof(scsi_allow_removal),
+                   USB_MS_HZ);
+       }
+
+       if (err != 0) {
                if (err != ERR_CSW_FAILED)
                        goto error;
+               DPRINTF("Device doesn't handle prevent and allow removal\n");
+               usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW);
        }
+
        timeout = 1;
 
 retry_sync_cache:
@@ -715,7 +735,6 @@ retry_sync_cache:
                DPRINTF("Device doesn't handle synchronize cache\n");
 
                usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
-
        } else {
 
                /*
@@ -789,6 +808,7 @@ error:
        DPRINTF("Device did not respond, enabling all quirks\n");
 
        usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE);
+       usbd_add_dynamic_quirk(udev, UQ_MSC_NO_PREVENT_ALLOW);
        usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY);
 
        /* Need to re-enumerate the device */
index 070d642..4edfcae 100644 (file)
@@ -69,11 +69,13 @@ static int usb_no_cs_fail;
 
 SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RW,
     &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set");
+TUNABLE_INT("hw.usb.no_cs_fail", &usb_no_cs_fail);
 
 static int usb_full_ddesc;
 
 SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RW,
     &usb_full_ddesc, 0, "USB always read complete device descriptor, if set");
+TUNABLE_INT("hw.usb.full_ddesc", &usb_full_ddesc);
 
 #ifdef USB_DEBUG
 #ifdef USB_REQ_DEBUG
@@ -106,21 +108,21 @@ static struct usb_ctrl_debug usb_ctrl_debug = {
        .bRequest_value = -1,
 };
 
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN,
     &usb_ctrl_debug.bus_index, 0, "USB controller index to fail");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN,
     &usb_ctrl_debug.dev_index, 0, "USB device address to fail");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN,
     &usb_ctrl_debug.ds_fail, 0, "USB fail data stage");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN,
     &usb_ctrl_debug.ss_fail, 0, "USB fail status stage");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN,
     &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN,
     &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN,
     &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail");
-SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RW,
+SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN,
     &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail");
 
 /*------------------------------------------------------------------------*
index bd09721..19f22a3 100644 (file)
@@ -235,7 +235,11 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
                n_obj = 1;
        } else {
                /* compute number of objects per page */
+#ifdef USB_DMA_SINGLE_ALLOC
+               n_obj = 1;
+#else
                n_obj = (USB_PAGE_SIZE / size);
+#endif
                /*
                 * Compute number of DMA chunks, rounded up
                 * to nearest one:
@@ -271,15 +275,33 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
                    &parm->curr_xfer->xroot->dma_parent_tag;
        }
 
-       if (ppc) {
-               *ppc = parm->xfer_page_cache_ptr;
+       if (ppc != NULL) {
+               if (n_obj != 1)
+                       *ppc = parm->xfer_page_cache_ptr;
+               else
+                       *ppc = parm->dma_page_cache_ptr;
        }
        r = count;                      /* set remainder count */
        z = n_obj * size;               /* set allocation size */
        pc = parm->xfer_page_cache_ptr;
        pg = parm->dma_page_ptr;
 
-       for (x = 0; x != n_dma_pc; x++) {
+       if (n_obj == 1) {
+           /*
+            * Avoid mapping memory twice if only a single object
+            * should be allocated per page cache:
+            */
+           for (x = 0; x != n_dma_pc; x++) {
+               if (usb_pc_alloc_mem(parm->dma_page_cache_ptr,
+                   pg, z, align)) {
+                       return (1);     /* failure */
+               }
+               /* Make room for one DMA page cache and "n_dma_pg" pages */
+               parm->dma_page_cache_ptr++;
+               pg += n_dma_pg;
+           }
+       } else {
+           for (x = 0; x != n_dma_pc; x++) {
 
                if (r < n_obj) {
                        /* compute last remainder */
@@ -292,7 +314,7 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
                }
                /* Set beginning of current buffer */
                buf = parm->dma_page_cache_ptr->buffer;
-               /* Make room for one DMA page cache and one page */
+               /* Make room for one DMA page cache and "n_dma_pg" pages */
                parm->dma_page_cache_ptr++;
                pg += n_dma_pg;
 
@@ -312,6 +334,7 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
                        }
                        lockmgr(pc->tag_parent->lock, LK_RELEASE);
                }
+           }
        }
 
        parm->xfer_page_cache_ptr = pc;
@@ -960,8 +983,8 @@ usbd_transfer_setup(struct usb_device *udev,
 #if USB_HAVE_BUSDMA
                        usb_dma_tag_setup(&info->dma_parent_tag,
                            parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag,
-                           xfer_lock, &usb_bdma_done_event, 32,
-                           parm->dma_tag_max);
+                           xfer_lock, &usb_bdma_done_event,
+                           udev->bus->dma_bits, parm->dma_tag_max);
 #endif
 
                        info->bus = udev->bus;
@@ -1448,6 +1471,29 @@ usbd_control_transfer_init(struct usb_xfer *xfer)
            (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT;
 }
 
+/*------------------------------------------------------------------------*
+ *     usbd_control_transfer_did_data
+ *
+ * This function returns non-zero if a control endpoint has
+ * transferred the first DATA packet after the SETUP packet.
+ * Else it returns zero.
+ *------------------------------------------------------------------------*/
+static uint8_t
+usbd_control_transfer_did_data(struct usb_xfer *xfer)
+{
+       struct usb_device_request req;
+
+       /* SETUP packet is not yet sent */
+       if (xfer->flags_int.control_hdr != 0)
+               return (0);
+
+       /* copy out the USB request header */
+       usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
+
+       /* compare remainder to the initial value */
+       return (xfer->flags_int.control_rem != UGETW(req.wLength));
+}
+
 /*------------------------------------------------------------------------*
  *     usbd_setup_ctrl_transfer
  *
@@ -1553,6 +1599,11 @@ usbd_setup_ctrl_transfer(struct usb_xfer *xfer)
                len = (xfer->sumlen - sizeof(struct usb_device_request));
        }
 
+       /* update did data flag */
+
+       xfer->flags_int.control_did_data =
+           usbd_control_transfer_did_data(xfer);
+
        /* check if there is a length mismatch */
 
        if (len > xfer->flags_int.control_rem) {
index ab3bc12..8fc8afa 100644 (file)
@@ -686,6 +686,7 @@ vendor ASUS2                0x1761  ASUS
 vendor SWEEX2          0x177f  Sweex
 vendor METAGEEK                0x1781  MetaGeek
 vendor KAMSTRUP                0x17a8  Kamstrup A/S
+vendor DISPLAYLINK     0x17e9  DisplayLink
 vendor LENOVO          0x17ef  Lenovo
 vendor WAVESENSE       0x17f4  WaveSense
 vendor VAISALA         0x1843  Vaisala
@@ -695,6 +696,7 @@ vendor QCOM         0x18e8  Qcom
 vendor ELV             0x18ef  ELV
 vendor LINKSYS3                0x1915  Linksys
 vendor QUALCOMMINC     0x19d2  Qualcomm, Incorporated
+vendor QUALCOMM3       0x19f5  Qualcomm, Inc.
 vendor BAYER           0x1a79  Bayer
 vendor WCH2            0x1a86  QinHeng Electronics
 vendor STELERA         0x1a8d  Stelera Wireless
@@ -1573,6 +1575,8 @@ product DLINK DSB650              0xabc1  10/100 Ethernet
 product DLINK DUBH7            0xf103  DUB-H7 USB 2.0 7-Port Hub
 product DLINK DWR510_CD                0xa805  DWR-510 CD-ROM Mode
 product DLINK DWR510           0x7e12  DWR-510
+product DLINK DWM157           0x7d02  DWM-157
+product DLINK DWM157_CD                0xa707  DWM-157 CD-ROM Mode
 product DLINK RTL8188CU                0x3308  RTL8188CU
 product        DLINK RTL8192CU_1       0x3307  RTL8192CU
 product        DLINK RTL8192CU_2       0x3309  RTL8192CU
@@ -1599,6 +1603,28 @@ product DLINK2 RT3070_4          0x3c15  RT3070
 product DLINK2 RT3070_5                0x3c16  RT3070
 product DLINK3 DWM652          0x3e04  DWM-652
 
+/* DisplayLink products */
+product DISPLAYLINK LCD4300U   0x01ba  LCD-4300U
+product DISPLAYLINK LCD8000U   0x01bb  LCD-8000U
+product DISPLAYLINK LD220      0x0100  Samsung LD220
+product DISPLAYLINK GUC2020    0x0059  IOGEAR DVI GUC2020
+product DISPLAYLINK VCUD60     0x0136  Rextron DVI
+product DISPLAYLINK CONV       0x0138  StarTech CONV-USB2DVI
+product DISPLAYLINK DLDVI      0x0141  DisplayLink DVI
+product DISPLAYLINK VGA10      0x015a  CMP-USBVGA10
+product DISPLAYLINK WSDVI      0x0198  WS Tech DVI
+product DISPLAYLINK EC008      0x019b  EasyCAP008 DVI
+product DISPLAYLINK HPDOCK     0x01d4  HP USB Docking
+product DISPLAYLINK NL571      0x01d7  HP USB DVI
+product DISPLAYLINK M01061     0x01e2  Lenovo DVI
+product DISPLAYLINK SWDVI      0x024c  SUNWEIT DVI
+product DISPLAYLINK NBDOCK     0x0215  VideoHome NBdock1920
+product DISPLAYLINK LUM70      0x02a9  Lilliput UM-70
+product DISPLAYLINK UM7X0      0x401a  nanovision MiMo
+product DISPLAYLINK LT1421     0x03e0  Lenovo ThinkVision LT1421
+product DISPLAYLINK POLARIS2   0x0117  Polaris2 USB dock
+product DISPLAYLINK PLUGABLE   0x0377  Plugable docking station
+
 /* DMI products */
 product DMI CFSM_RW            0xa109  CF/SM Reader/Writer
 product DMI DISK               0x2bcf  Generic Disk
@@ -2346,6 +2372,8 @@ product INTEL EASYPC_CAMERA       0x0110  Easy PC Camera
 product INTEL TESTBOARD                0x9890  82930 test board
 product INTEL2 IRMH            0x0020  Integrated Rate Matching Hub
 product INTEL2 IRMH2           0x0024  Integrated Rate Matching Hub
+product INTEL2 IRMH3           0x8000  Integrated Rate Matching Hub
+product INTEL2 IRMH4           0x8008  Integrated Rate Matching Hub
 
 /* Interbiometric products */
 product INTERBIOMETRICS IOBOARD                0x1002  FTDI compatible adapter
@@ -3130,7 +3158,7 @@ product NETGEAR EA101X            0x1002  Ethernet
 product NETGEAR FA101          0x1020  Ethernet 10/100, USB1.1
 product NETGEAR FA120          0x1040  USB 2.0 Ethernet
 product NETGEAR M4100          0x1100  M4100/M5300/M7100 series switch
-product NETGEAR WG111V2_2      0x4240  PrismGT USB 2.0 WLAN
+product NETGEAR WG111V1_2      0x4240  PrismGT USB 2.0 WLAN
 product NETGEAR WG111V3                0x4260  WG111v3
 product NETGEAR WG111U         0x4300  WG111U
 product NETGEAR WG111U_NF      0x4301  WG111U (no firmware)
@@ -3506,8 +3534,11 @@ product QUALCOMM2 CDMA_MSM       0x3196  CDMA Technologies MSM modem
 product QUALCOMM2 AC8700       0x6000  AC8700
 product QUALCOMM2 VW110L       0x1000  Vertex Wireless 110L modem
 product QUALCOMM2 SIM5218      0x9000  SIM5218
+product QUALCOMM2 WM620                0x9002  Neoway WM620
 product QUALCOMM2 GOBI2000_QDL 0x9204  Qualcomm Gobi 2000 QDL
 product QUALCOMM2 GOBI2000     0x9205  Qualcomm Gobi 2000 modem
+product QUALCOMM2 VT80N                0x6500  Venus VT80N
+product QUALCOMM3 VFAST2       0x9909  Venus Fast2 modem
 product QUALCOMMINC CDMA_MSM   0x0001  CDMA Technologies MSM modem
 product QUALCOMMINC E0002      0x0002  3G modem
 product QUALCOMMINC E0003      0x0003  3G modem
@@ -3576,9 +3607,14 @@ product QUALCOMMINC E0082        0x0082  3G modem
 product QUALCOMMINC E0086      0x0086  3G modem
 product QUALCOMMINC SURFSTICK  0x0117  1&1 Surf Stick
 product QUALCOMMINC K3772_Z    0x1179  3G modem
+product QUALCOMMINC ZTE_MF730M  0x1420  3G modem
+product QUALCOMMINC MF195E_INIT 0x1514  MF195E initial
+product QUALCOMMINC MF195E      0x1516  MF195E
 product QUALCOMMINC ZTE_STOR   0x2000  USB ZTE Storage
 product QUALCOMMINC E2002      0x2002  3G modem
 product QUALCOMMINC E2003      0x2003  3G modem
+product QUALCOMMINC AC682      0xffdd  CDMA 1xEVDO USB modem
+product QUALCOMMINC AC682_INIT 0xffde  CDMA 1xEVDO USB modem (initial)
 product QUALCOMMINC AC8710     0xfff1  3G modem
 product QUALCOMMINC AC2726     0xfff5  3G modem
 product QUALCOMMINC AC8700     0xfffe  CDMA 1xEVDO USB modem
diff --git a/sys/bus/u4b/video/udl.c b/sys/bus/u4b/video/udl.c
new file mode 100644 (file)
index 0000000..2ef703d
--- /dev/null
@@ -0,0 +1,1097 @@
+/*     $OpenBSD: udl.c,v 1.81 2014/12/09 07:05:06 doug Exp $ */
+/*     $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2015 Hans Petter Selasky <hselasky@freebsd.org>
+ * Copyright (c) 2009 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for the "DisplayLink DL-120 / DL-160" graphic chips based on
+ * the reversed engineered specifications of Florian Echtler
+ * <floe@butterbrot.org>:
+ *
+ *     http://floe.butterbrot.org/displaylink/doku.php
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/callout.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/consio.h>
+#include <sys/fbio.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/syscons/syscons.h>
+
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include "usbdevs.h"
+
+#include <dev/usb/video/udl.h>
+
+#include "fb_if.h"
+
+#undef DPRINTF
+#undef DPRINTFN
+#define        USB_DEBUG_VAR udl_debug
+#include <dev/usb/usb_debug.h>
+
+static SYSCTL_NODE(_hw_usb, OID_AUTO, udl, CTLFLAG_RW, 0, "USB UDL");
+
+#ifdef USB_DEBUG
+static int udl_debug = 0;
+
+SYSCTL_INT(_hw_usb_udl, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &udl_debug, 0, "Debug level");
+#endif
+
+#define        UDL_FPS_MAX     60
+#define        UDL_FPS_MIN     1
+
+static int udl_fps = 25;
+SYSCTL_INT(_hw_usb_udl, OID_AUTO, fps, CTLFLAG_RWTUN,
+    &udl_fps, 0, "Frames Per Second, 1-60");
+
+/*
+ * Prototypes.
+ */
+static usb_callback_t udl_bulk_write_callback;
+
+static device_probe_t udl_probe;
+static device_attach_t udl_attach;
+static device_detach_t udl_detach;
+static fb_getinfo_t udl_fb_getinfo;
+static fb_setblankmode_t udl_fb_setblankmode;
+
+static void udl_select_chip(struct udl_softc *, struct usb_attach_arg *);
+static int udl_init_chip(struct udl_softc *);
+static void udl_select_mode(struct udl_softc *);
+static int udl_init_resolution(struct udl_softc *);
+static void udl_fbmem_alloc(struct udl_softc *);
+static int udl_cmd_write_buf_le16(struct udl_softc *, const uint8_t *, uint32_t, uint8_t, int);
+static int udl_cmd_buf_copy_le16(struct udl_softc *, uint32_t, uint32_t, uint8_t, int);
+static void udl_cmd_insert_int_1(struct udl_cmd_buf *, uint8_t);
+static void udl_cmd_insert_int_3(struct udl_cmd_buf *, uint32_t);
+static void udl_cmd_insert_buf_le16(struct udl_cmd_buf *, const uint8_t *, uint32_t);
+static void udl_cmd_write_reg_1(struct udl_cmd_buf *, uint8_t, uint8_t);
+static void udl_cmd_write_reg_3(struct udl_cmd_buf *, uint8_t, uint32_t);
+static int udl_power_save(struct udl_softc *, int, int);
+
+static const struct usb_config udl_config[UDL_N_TRANSFER] = {
+       [UDL_BULK_WRITE_0] = {
+               .type = UE_BULK,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_TX,
+               .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+               .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES,
+               .callback = &udl_bulk_write_callback,
+               .frames = UDL_CMD_MAX_FRAMES,
+               .timeout = 5000,        /* 5 seconds */
+       },
+       [UDL_BULK_WRITE_1] = {
+               .type = UE_BULK,
+               .endpoint = UE_ADDR_ANY,
+               .direction = UE_DIR_TX,
+               .flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+               .bufsize = UDL_CMD_MAX_DATA_SIZE * UDL_CMD_MAX_FRAMES,
+               .callback = &udl_bulk_write_callback,
+               .frames = UDL_CMD_MAX_FRAMES,
+               .timeout = 5000,        /* 5 seconds */
+       },
+};
+
+/*
+ * Driver glue.
+ */
+static devclass_t udl_devclass;
+
+static device_method_t udl_methods[] = {
+       DEVMETHOD(device_probe, udl_probe),
+       DEVMETHOD(device_attach, udl_attach),
+       DEVMETHOD(device_detach, udl_detach),
+       DEVMETHOD(fb_getinfo, udl_fb_getinfo),
+       DEVMETHOD_END
+};
+
+static driver_t udl_driver = {
+       .name = "udl",
+       .methods = udl_methods,
+       .size = sizeof(struct udl_softc),
+};
+
+DRIVER_MODULE(udl, uhub, udl_driver, udl_devclass, NULL, NULL);
+MODULE_DEPEND(udl, usb, 1, 1, 1);
+MODULE_DEPEND(udl, fbd, 1, 1, 1);
+MODULE_DEPEND(udl, videomode, 1, 1, 1);
+MODULE_VERSION(udl, 1);
+
+/*
+ * Matching devices.
+ */
+static const STRUCT_USB_HOST_ID udl_devs[] = {
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD4300U, DL120)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LCD8000U, DL120)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_GUC2020, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LD220, DL165)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VCUD60, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_DLDVI, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_VGA10, DL120)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_WSDVI, DLUNK)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_EC008, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_HPDOCK, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NL571, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_M01061, DL195)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_NBDOCK, DL165)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_SWDVI, DLUNK)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_UM7X0, DL120)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_CONV, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_PLUGABLE, DL160)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LUM70, DL125)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_POLARIS2, DLUNK)},
+       {USB_VPI(USB_VENDOR_DISPLAYLINK, USB_PRODUCT_DISPLAYLINK_LT1421, DLUNK)}
+};
+
+static uint32_t
+udl_get_fb_size(struct udl_softc *sc)
+{
+       unsigned i = sc->sc_cur_mode;
+
+       return ((uint32_t)udl_modes[i].hdisplay *
+           (uint32_t)udl_modes[i].vdisplay * 2);
+}
+
+static uint32_t
+udl_get_fb_width(struct udl_softc *sc)
+{
+       unsigned i = sc->sc_cur_mode;
+
+       return (udl_modes[i].hdisplay);
+}
+
+static uint32_t
+udl_get_fb_height(struct udl_softc *sc)
+{
+       unsigned i = sc->sc_cur_mode;
+
+       return (udl_modes[i].vdisplay);
+}
+
+static uint32_t
+udl_get_fb_hz(struct udl_softc *sc)
+{
+       unsigned i = sc->sc_cur_mode;
+
+       return (udl_modes[i].hz);
+}
+
+static void
+udl_callout(void *arg)
+{
+       struct udl_softc *sc = arg;
+       const uint32_t max = udl_get_fb_size(sc);
+       int fps;
+
+       if (sc->sc_power_save == 0) {
+               fps = udl_fps;
+
+               /* figure out number of frames per second */
+               if (fps < UDL_FPS_MIN)
+                       fps = UDL_FPS_MIN;
+               else if (fps > UDL_FPS_MAX)
+                       fps = UDL_FPS_MAX;
+
+               if (sc->sc_sync_off >= max)
+                       sc->sc_sync_off = 0;
+               usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]);
+               usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]);
+       } else {
+               fps = 1;
+       }
+       callout_reset(&sc->sc_callout, hz / fps, &udl_callout, sc);
+}
+
+static int
+udl_probe(device_t dev)
+{
+       struct usb_attach_arg *uaa = device_get_ivars(dev);
+
+       if (uaa->usb_mode != USB_MODE_HOST)
+               return (ENXIO);
+       if (uaa->info.bConfigIndex != 0)
+               return (ENXIO);
+       if (uaa->info.bIfaceIndex != 0)
+               return (ENXIO);
+
+       return (usbd_lookup_id_by_uaa(udl_devs, sizeof(udl_devs), uaa));
+}
+
+static int
+udl_attach(device_t dev)
+{
+       struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
+       struct sysctl_oid *tree = device_get_sysctl_tree(dev);
+       struct udl_softc *sc = device_get_softc(dev);
+       struct usb_attach_arg *uaa = device_get_ivars(dev);
+       int error;
+       int i;
+
+       device_set_usb_desc(dev);
+
+       mtx_init(&sc->sc_mtx, "UDL lock", NULL, MTX_DEF);
+       cv_init(&sc->sc_cv, "UDLCV");
+       callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
+       sc->sc_udev = uaa->device;
+
+       error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
+           sc->sc_xfer, udl_config, UDL_N_TRANSFER, sc, &sc->sc_mtx);
+
+       if (error) {
+               DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(error));
+               goto detach;
+       }
+       usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_0], &sc->sc_xfer_head[0]);
+       usbd_xfer_set_priv(sc->sc_xfer[UDL_BULK_WRITE_1], &sc->sc_xfer_head[1]);
+
+       TAILQ_INIT(&sc->sc_xfer_head[0]);
+       TAILQ_INIT(&sc->sc_xfer_head[1]);
+       TAILQ_INIT(&sc->sc_cmd_buf_free);
+       TAILQ_INIT(&sc->sc_cmd_buf_pending);
+
+       sc->sc_def_chip = -1;
+       sc->sc_chip = USB_GET_DRIVER_INFO(uaa);
+       sc->sc_def_mode = -1;
+       sc->sc_cur_mode = UDL_MAX_MODES;
+
+       /* Allow chip ID to be overwritten */
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid_force",
+           CTLFLAG_RWTUN, &sc->sc_def_chip, 0, "chip ID");
+
+       /* Export current chip ID */
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "chipid",
+           CTLFLAG_RD, &sc->sc_chip, 0, "chip ID");
+
+       if (sc->sc_def_chip > -1 && sc->sc_def_chip <= DLMAX) {
+               device_printf(dev, "Forcing chip ID to 0x%04x\n", sc->sc_def_chip);
+               sc->sc_chip = sc->sc_def_chip;
+       }
+       /*
+        * The product might have more than one chip
+        */
+       if (sc->sc_chip == DLUNK)
+               udl_select_chip(sc, uaa);
+
+       for (i = 0; i != UDL_CMD_MAX_BUFFERS; i++) {
+               struct udl_cmd_buf *cb = &sc->sc_cmd_buf_temp[i];
+
+               TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry);
+       }
+
+       /*
+        * Initialize chip.
+        */
+       error = udl_init_chip(sc);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               goto detach;
+
+       /*
+        * Select edid mode.
+        */
+       udl_select_mode(sc);
+
+       /* Allow default mode to be overwritten */
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode_force",
+           CTLFLAG_RWTUN, &sc->sc_def_mode, 0, "mode");
+
+       /* Export current mode */
+       SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mode",
+           CTLFLAG_RD, &sc->sc_cur_mode, 0, "mode");
+
+       i = sc->sc_def_mode;
+       if (i > -1 && i < UDL_MAX_MODES) {
+               if (udl_modes[i].chip <= sc->sc_chip) {
+                       device_printf(dev, "Forcing mode to %d\n", i);
+                       sc->sc_cur_mode = i;
+               }
+       }
+       /* Printout current mode */
+       device_printf(dev, "Mode selected %dx%d @ %dHz\n",
+           (int)udl_get_fb_width(sc),
+           (int)udl_get_fb_height(sc),
+           (int)udl_get_fb_hz(sc));
+
+       udl_init_resolution(sc);
+
+       /* Allocate frame buffer */
+       udl_fbmem_alloc(sc);
+
+       UDL_LOCK(sc);
+       udl_callout(sc);
+       UDL_UNLOCK(sc);
+
+       sc->sc_fb_info.fb_name = device_get_nameunit(dev);
+       sc->sc_fb_info.fb_size = sc->sc_fb_size;
+       sc->sc_fb_info.fb_bpp = 16;
+       sc->sc_fb_info.fb_depth = 16;
+       sc->sc_fb_info.fb_width = udl_get_fb_width(sc);
+       sc->sc_fb_info.fb_height = udl_get_fb_height(sc);
+       sc->sc_fb_info.fb_stride = sc->sc_fb_info.fb_width * 2;
+       sc->sc_fb_info.fb_pbase = 0;
+       sc->sc_fb_info.fb_vbase = (uintptr_t)sc->sc_fb_addr;
+       sc->sc_fb_info.fb_priv = sc;
+       sc->sc_fb_info.setblankmode = &udl_fb_setblankmode;
+
+       sc->sc_fbdev = device_add_child(dev, "fbd", -1);
+       if (sc->sc_fbdev == NULL)
+               goto detach;
+       if (device_probe_and_attach(sc->sc_fbdev) != 0)
+               goto detach;
+
+       return (0);
+
+detach:
+       udl_detach(dev);
+
+       return (ENXIO);
+}
+
+static int
+udl_detach(device_t dev)
+{
+       struct udl_softc *sc = device_get_softc(dev);
+
+       if (sc->sc_fbdev != NULL) {
+               device_t bdev;
+
+               bdev = sc->sc_fbdev;
+               sc->sc_fbdev = NULL;
+               device_detach(bdev);
+               device_delete_child(dev, bdev);
+       }
+       UDL_LOCK(sc);
+       sc->sc_gone = 1;
+       callout_stop(&sc->sc_callout);
+       UDL_UNLOCK(sc);
+
+       usbd_transfer_unsetup(sc->sc_xfer, UDL_N_TRANSFER);
+
+       callout_drain(&sc->sc_callout);
+
+       mtx_destroy(&sc->sc_mtx);
+       cv_destroy(&sc->sc_cv);
+
+       /*
+        * Free framebuffer memory, if any.
+        */
+       free(sc->sc_fb_addr, M_DEVBUF);
+       free(sc->sc_fb_copy, M_DEVBUF);
+
+       return (0);
+}
+
+static struct fb_info *
+udl_fb_getinfo(device_t dev)
+{
+       struct udl_softc *sc = device_get_softc(dev);
+
+       return (&sc->sc_fb_info);
+}
+
+static int
+udl_fb_setblankmode(void *arg, int mode)
+{
+       struct udl_softc *sc = arg;
+
+       switch (mode) {
+       case V_DISPLAY_ON:
+               udl_power_save(sc, 1, M_WAITOK);
+               break;
+       case V_DISPLAY_BLANK:
+               udl_power_save(sc, 1, M_WAITOK);
+               if (sc->sc_fb_addr != 0) {
+                       const uint32_t max = udl_get_fb_size(sc);
+
+                       memset((void *)sc->sc_fb_addr, 0, max);
+               }
+               break;
+       case V_DISPLAY_STAND_BY:
+       case V_DISPLAY_SUSPEND:
+               udl_power_save(sc, 0, M_WAITOK);
+               break;
+       }
+       return (0);
+}
+
+static struct udl_cmd_buf *
+udl_cmd_buf_alloc_locked(struct udl_softc *sc, int flags)
+{
+       struct udl_cmd_buf *cb;
+
+       while ((cb = TAILQ_FIRST(&sc->sc_cmd_buf_free)) == NULL) {
+               if (flags != M_WAITOK)
+                       break;
+               cv_wait(&sc->sc_cv, &sc->sc_mtx);
+       }
+       if (cb != NULL) {
+               TAILQ_REMOVE(&sc->sc_cmd_buf_free, cb, entry);
+               cb->off = 0;
+       }
+       return (cb);
+}
+
+static struct udl_cmd_buf *
+udl_cmd_buf_alloc(struct udl_softc *sc, int flags)
+{
+       struct udl_cmd_buf *cb;
+
+       UDL_LOCK(sc);
+       cb = udl_cmd_buf_alloc_locked(sc, flags);
+       UDL_UNLOCK(sc);
+       return (cb);
+}
+
+static void
+udl_cmd_buf_send(struct udl_softc *sc, struct udl_cmd_buf *cb)
+{
+       UDL_LOCK(sc);
+       if (sc->sc_gone) {
+               TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_free, cb, entry);
+       } else {
+               /* mark end of command stack */
+               udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+               udl_cmd_insert_int_1(cb, UDL_BULK_CMD_EOC);
+
+               TAILQ_INSERT_TAIL(&sc->sc_cmd_buf_pending, cb, entry);
+               usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_0]);
+               usbd_transfer_start(sc->sc_xfer[UDL_BULK_WRITE_1]);
+       }
+       UDL_UNLOCK(sc);
+}
+
+static struct udl_cmd_buf *
+udl_fb_synchronize_locked(struct udl_softc *sc)
+{
+       const uint32_t max = udl_get_fb_size(sc);
+
+       /* check if framebuffer is not ready */
+       if (sc->sc_fb_addr == NULL ||
+           sc->sc_fb_copy == NULL)
+               return (NULL);
+
+       while (sc->sc_sync_off < max) {
+               uint32_t delta = max - sc->sc_sync_off;
+
+               if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2)
+                       delta = UDL_CMD_MAX_PIXEL_COUNT * 2;
+               if (bcmp(sc->sc_fb_addr + sc->sc_sync_off, sc->sc_fb_copy + sc->sc_sync_off, delta) != 0) {
+                       struct udl_cmd_buf *cb;
+
+                       cb = udl_cmd_buf_alloc_locked(sc, M_NOWAIT);
+                       if (cb == NULL)
+                               goto done;
+                       memcpy(sc->sc_fb_copy + sc->sc_sync_off,
+                           sc->sc_fb_addr + sc->sc_sync_off, delta);
+                       udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+                       udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
+                       udl_cmd_insert_int_3(cb, sc->sc_sync_off);
+                       udl_cmd_insert_int_1(cb, delta / 2);
+                       udl_cmd_insert_buf_le16(cb, sc->sc_fb_copy + sc->sc_sync_off, delta);
+                       sc->sc_sync_off += delta;
+                       return (cb);
+               } else {
+                       sc->sc_sync_off += delta;
+               }
+       }
+done:
+       return (NULL);
+}
+
+static void
+udl_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+       struct udl_softc *sc = usbd_xfer_softc(xfer);
+       struct udl_cmd_head *phead = usbd_xfer_get_priv(xfer);
+       struct udl_cmd_buf *cb;
+       unsigned i;
+
+       switch (USB_GET_STATE(xfer)) {
+       case USB_ST_TRANSFERRED:
+               TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry);
+       case USB_ST_SETUP:
+tr_setup:
+               for (i = 0; i != UDL_CMD_MAX_FRAMES; i++) {
+                       cb = TAILQ_FIRST(&sc->sc_cmd_buf_pending);
+                       if (cb == NULL) {
+                               cb = udl_fb_synchronize_locked(sc);
+                               if (cb == NULL)
+                                       break;
+                       } else {
+                               TAILQ_REMOVE(&sc->sc_cmd_buf_pending, cb, entry);
+                       }
+                       TAILQ_INSERT_TAIL(phead, cb, entry);
+                       usbd_xfer_set_frame_data(xfer, i, cb->buf, cb->off);
+               }
+               if (i != 0) {
+                       usbd_xfer_set_frames(xfer, i);
+                       usbd_transfer_submit(xfer);
+               }
+               break;
+       default:
+               TAILQ_CONCAT(&sc->sc_cmd_buf_free, phead, entry);
+               if (error != USB_ERR_CANCELLED) {
+                       /* try clear stall first */
+                       usbd_xfer_set_stall(xfer);
+                       goto tr_setup;
+               }
+               break;
+       }
+       /* wakeup any waiters */
+       cv_signal(&sc->sc_cv);
+}
+
+static int
+udl_power_save(struct udl_softc *sc, int on, int flags)
+{
+       struct udl_cmd_buf *cb;
+
+       /* get new buffer */
+       cb = udl_cmd_buf_alloc(sc, flags);
+       if (cb == NULL)
+               return (EAGAIN);
+
+       DPRINTF("screen %s\n", on ? "ON" : "OFF");
+
+       sc->sc_power_save = on ? 0 : 1;
+
+       if (on)
+               udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
+       else
+               udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_OFF);
+
+       udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
+       udl_cmd_buf_send(sc, cb);
+       return (0);
+}
+
+static int
+udl_ctrl_msg(struct udl_softc *sc, uint8_t rt, uint8_t r,
+    uint16_t index, uint16_t value, uint8_t *buf, size_t len)
+{
+       usb_device_request_t req;
+       int error;
+
+       req.bmRequestType = rt;
+       req.bRequest = r;
+       USETW(req.wIndex, index);
+       USETW(req.wValue, value);
+       USETW(req.wLength, len);
+
+       error = usbd_do_request_flags(sc->sc_udev, NULL,
+           &req, buf, 0, NULL, USB_DEFAULT_TIMEOUT);
+
+       DPRINTF("%s\n", usbd_errstr(error));
+
+       return (error);
+}
+
+static int
+udl_poll(struct udl_softc *sc, uint32_t *buf)
+{
+       uint32_t lbuf;
+       int error;
+
+       error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+           UDL_CTRL_CMD_POLL, 0x0000, 0x0000, (uint8_t *)&lbuf, sizeof(lbuf));
+       if (error == USB_ERR_NORMAL_COMPLETION)
+               *buf = le32toh(lbuf);
+       return (error);
+}
+
+static int
+udl_read_1(struct udl_softc *sc, uint16_t addr, uint8_t *buf)
+{
+       uint8_t lbuf[1];
+       int error;
+
+       error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+           UDL_CTRL_CMD_READ_1, addr, 0x0000, lbuf, 1);
+       if (error == USB_ERR_NORMAL_COMPLETION)
+               *buf = *(uint8_t *)lbuf;
+       return (error);
+}
+
+static int
+udl_write_1(struct udl_softc *sc, uint16_t addr, uint8_t buf)
+{
+       int error;
+
+       error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
+           UDL_CTRL_CMD_WRITE_1, addr, 0x0000, &buf, 1);
+       return (error);
+}
+
+static int
+udl_read_edid(struct udl_softc *sc, uint8_t *buf)
+{
+       uint8_t lbuf[64];
+       uint16_t offset;
+       int error;
+
+       offset = 0;
+
+       error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+           UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               goto fail;
+       bcopy(lbuf + 1, buf + offset, 63);
+       offset += 63;
+
+       error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+           UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 64);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               goto fail;
+       bcopy(lbuf + 1, buf + offset, 63);
+       offset += 63;
+
+       error = udl_ctrl_msg(sc, UT_READ_VENDOR_DEVICE,
+           UDL_CTRL_CMD_READ_EDID, 0x00a1, (offset << 8), lbuf, 3);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               goto fail;
+       bcopy(lbuf + 1, buf + offset, 2);
+fail:
+       return (error);
+}
+
+static uint8_t
+udl_lookup_mode(uint16_t hdisplay, uint16_t vdisplay, uint8_t hz,
+    uint16_t chip, uint32_t clock)
+{
+       uint8_t idx;
+
+       /*
+        * Check first if we have a matching mode with pixelclock
+        */
+       for (idx = 0; idx != UDL_MAX_MODES; idx++) {
+               if ((udl_modes[idx].hdisplay == hdisplay) &&
+                   (udl_modes[idx].vdisplay == vdisplay) &&
+                   (udl_modes[idx].clock == clock) &&
+                   (udl_modes[idx].chip <= chip)) {
+                       return (idx);
+               }
+       }
+
+       /*
+        * If not, check for matching mode with update frequency
+        */
+       for (idx = 0; idx != UDL_MAX_MODES; idx++) {
+               if ((udl_modes[idx].hdisplay == hdisplay) &&
+                   (udl_modes[idx].vdisplay == vdisplay) &&
+                   (udl_modes[idx].hz == hz) &&
+                   (udl_modes[idx].chip <= chip)) {
+                       return (idx);
+               }
+       }
+       return (idx);
+}
+
+static void
+udl_select_chip(struct udl_softc *sc, struct usb_attach_arg *uaa)
+{
+       const char *pserial;
+
+       pserial = usb_get_serial(uaa->device);
+
+       sc->sc_chip = DL120;
+
+       if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) &&
+           (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_WSDVI)) {
+
+               /*
+                * WS Tech DVI is DL120 or DL160. All deviced uses the
+                * same revision (0.04) so iSerialNumber must be used
+                * to determin which chip it is.
+                */
+
+               if (strlen(pserial) > 7) {
+                       if (strncmp(pserial, "0198-13", 7) == 0)
+                               sc->sc_chip = DL160;
+               }
+               DPRINTF("iSerialNumber (%s) used to select chip (%d)\n",
+                   pserial, sc->sc_chip);
+       }
+       if ((uaa->info.idVendor == USB_VENDOR_DISPLAYLINK) &&
+           (uaa->info.idProduct == USB_PRODUCT_DISPLAYLINK_SWDVI)) {
+
+               /*
+                * SUNWEIT DVI is DL160, DL125, DL165 or DL195. Major revision
+                * can be used to differ between DL1x0 and DL1x5. Minor to
+                * differ between DL1x5. iSerialNumber seems not to be uniqe.
+                */
+
+               sc->sc_chip = DL160;
+
+               if (uaa->info.bcdDevice >= 0x100) {
+                       sc->sc_chip = DL165;
+                       if (uaa->info.bcdDevice == 0x104)
+                               sc->sc_chip = DL195;
+                       if (uaa->info.bcdDevice == 0x108)
+                               sc->sc_chip = DL125;
+               }
+               DPRINTF("bcdDevice (%02x) used to select chip (%d)\n",
+                   uaa->info.bcdDevice, sc->sc_chip);
+       }
+}
+
+static int
+udl_set_enc_key(struct udl_softc *sc, uint8_t *buf, uint8_t len)
+{
+       int error;
+
+       error = udl_ctrl_msg(sc, UT_WRITE_VENDOR_DEVICE,
+           UDL_CTRL_CMD_SET_KEY, 0x0000, 0x0000, buf, len);
+       return (error);
+}
+
+static void
+udl_fbmem_alloc(struct udl_softc *sc)
+{
+       uint32_t size;
+
+       size = udl_get_fb_size(sc);
+       size = round_page(size);
+
+       /*
+        * It is assumed that allocations above PAGE_SIZE bytes will
+        * be PAGE_SIZE aligned for use with mmap()
+        */
+       sc->sc_fb_addr = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+       sc->sc_fb_copy = malloc(size, M_DEVBUF, M_WAITOK | M_ZERO);
+       sc->sc_fb_size = size;
+}
+
+static void
+udl_cmd_insert_int_1(struct udl_cmd_buf *cb, uint8_t value)
+{
+
+       cb->buf[cb->off] = value;
+       cb->off += 1;
+}
+
+#if 0
+static void
+udl_cmd_insert_int_2(struct udl_cmd_buf *cb, uint16_t value)
+{
+       uint16_t lvalue;
+
+       lvalue = htobe16(value);
+       bcopy(&lvalue, cb->buf + cb->off, 2);
+
+       cb->off += 2;
+}
+
+#endif
+
+static void
+udl_cmd_insert_int_3(struct udl_cmd_buf *cb, uint32_t value)
+{
+       uint32_t lvalue;
+
+#if BYTE_ORDER == BIG_ENDIAN
+       lvalue = htobe32(value) << 8;
+#else
+       lvalue = htobe32(value) >> 8;
+#endif
+       bcopy(&lvalue, cb->buf + cb->off, 3);
+
+       cb->off += 3;
+}
+
+#if 0
+static void
+udl_cmd_insert_int_4(struct udl_cmd_buf *cb, uint32_t value)
+{
+       uint32_t lvalue;
+
+       lvalue = htobe32(value);
+       bcopy(&lvalue, cb->buf + cb->off, 4);
+
+       cb->off += 4;
+}
+
+#endif
+
+static void
+udl_cmd_insert_buf_le16(struct udl_cmd_buf *cb, const uint8_t *buf, uint32_t len)
+{
+       uint32_t x;
+
+       for (x = 0; x != len; x += 2) {
+               /* byte swap from little endian to big endian */
+               cb->buf[cb->off + x + 0] = buf[x + 1];
+               cb->buf[cb->off + x + 1] = buf[x + 0];
+       }
+       cb->off += len;
+}
+
+static void
+udl_cmd_write_reg_1(struct udl_cmd_buf *cb, uint8_t reg, uint8_t val)
+{
+
+       udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+       udl_cmd_insert_int_1(cb, UDL_BULK_CMD_REG_WRITE_1);
+       udl_cmd_insert_int_1(cb, reg);
+       udl_cmd_insert_int_1(cb, val);
+}
+
+static void
+udl_cmd_write_reg_3(struct udl_cmd_buf *cb, uint8_t reg, uint32_t val)
+{
+
+       udl_cmd_write_reg_1(cb, reg + 0, (val >> 16) & 0xff);
+       udl_cmd_write_reg_1(cb, reg + 1, (val >> 8) & 0xff);
+       udl_cmd_write_reg_1(cb, reg + 2, (val >> 0) & 0xff);
+}
+
+static int
+udl_init_chip(struct udl_softc *sc)
+{
+       uint32_t ui32;
+       uint8_t ui8;
+       int error;
+
+       error = udl_poll(sc, &ui32);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               return (error);
+       DPRINTF("poll=0x%08x\n", ui32);
+
+       /* Some products may use later chip too */
+       switch (ui32 & 0xff) {
+       case 0xf1:                      /* DL1x5 */
+               switch (sc->sc_chip) {
+               case DL120:
+                       sc->sc_chip = DL125;
+                       break;
+               case DL160:
+                       sc->sc_chip = DL165;
+                       break;
+               }
+               break;
+       }
+       DPRINTF("chip 0x%04x\n", sc->sc_chip);
+
+       error = udl_read_1(sc, 0xc484, &ui8);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               return (error);
+       DPRINTF("read 0x%02x from 0xc484\n", ui8);
+
+       error = udl_write_1(sc, 0xc41f, 0x01);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               return (error);
+       DPRINTF("write 0x01 to 0xc41f\n");
+
+       error = udl_read_edid(sc, sc->sc_edid);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               return (error);
+       DPRINTF("read EDID\n");
+
+       error = udl_set_enc_key(sc, __DECONST(void *, udl_null_key_1),
+           sizeof(udl_null_key_1));
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               return (error);
+       DPRINTF("set encryption key\n");
+
+       error = udl_write_1(sc, 0xc40b, 0x00);
+       if (error != USB_ERR_NORMAL_COMPLETION)
+               return (error);
+       DPRINTF("write 0x00 to 0xc40b\n");
+
+       return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+udl_init_fb_offsets(struct udl_cmd_buf *cb, uint32_t start16, uint32_t stride16,
+    uint32_t start8, uint32_t stride8)
+{
+       udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00);
+       udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START16, start16);
+       udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE16, stride16);
+       udl_cmd_write_reg_3(cb, UDL_REG_ADDR_START8, start8);
+       udl_cmd_write_reg_3(cb, UDL_REG_ADDR_STRIDE8, stride8);
+       udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
+}
+
+static int
+udl_init_resolution(struct udl_softc *sc)
+{
+       const uint32_t max = udl_get_fb_size(sc);
+       const uint8_t *buf = udl_modes[sc->sc_cur_mode].mode;
+       struct udl_cmd_buf *cb;
+       uint32_t delta;
+       uint32_t i;
+       int error;
+
+       /* get new buffer */
+       cb = udl_cmd_buf_alloc(sc, M_WAITOK);
+       if (cb == NULL)
+               return (EAGAIN);
+
+       /* write resolution values and set video memory offsets */
+       udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0x00);
+       for (i = 0; i < UDL_MODE_SIZE; i++)
+               udl_cmd_write_reg_1(cb, i, buf[i]);
+       udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
+
+       udl_init_fb_offsets(cb, 0x000000, 0x000a00, 0x555555, 0x000500);
+       udl_cmd_buf_send(sc, cb);
+
+       /* fill screen with black color */
+       for (i = 0; i < max; i += delta) {
+               static const uint8_t udl_black[UDL_CMD_MAX_PIXEL_COUNT * 2] __aligned(4);
+
+               delta = max - i;
+               if (delta > UDL_CMD_MAX_PIXEL_COUNT * 2)
+                       delta = UDL_CMD_MAX_PIXEL_COUNT * 2;
+               if (i == 0)
+                       error = udl_cmd_write_buf_le16(sc, udl_black, i, delta / 2, M_WAITOK);
+               else
+                       error = udl_cmd_buf_copy_le16(sc, 0, i, delta / 2, M_WAITOK);
+               if (error)
+                       return (error);
+       }
+
+       /* get new buffer */
+       cb = udl_cmd_buf_alloc(sc, M_WAITOK);
+       if (cb == NULL)
+               return (EAGAIN);
+
+       /* show framebuffer content */
+       udl_cmd_write_reg_1(cb, UDL_REG_SCREEN, UDL_REG_SCREEN_ON);
+       udl_cmd_write_reg_1(cb, UDL_REG_SYNC, 0xff);
+       udl_cmd_buf_send(sc, cb);
+       return (0);
+}
+
+static void
+udl_select_mode(struct udl_softc *sc)
+{
+       struct udl_mode mode;
+       int index = UDL_MAX_MODES;
+       int i;
+
+       /* try to get the preferred mode from EDID */
+       edid_parse(sc->sc_edid, &sc->sc_edid_info);
+#ifdef USB_DEBUG
+       edid_print(&sc->sc_edid_info);
+#endif
+       if (sc->sc_edid_info.edid_preferred_mode != NULL) {
+               mode.hz =
+                   (sc->sc_edid_info.edid_preferred_mode->dot_clock * 1000) /
+                   (sc->sc_edid_info.edid_preferred_mode->htotal *
+                   sc->sc_edid_info.edid_preferred_mode->vtotal);
+               mode.clock =
+                   sc->sc_edid_info.edid_preferred_mode->dot_clock / 10;
+               mode.hdisplay =
+                   sc->sc_edid_info.edid_preferred_mode->hdisplay;
+               mode.vdisplay =
+                   sc->sc_edid_info.edid_preferred_mode->vdisplay;
+               index = udl_lookup_mode(mode.hdisplay, mode.vdisplay, mode.hz,
+                   sc->sc_chip, mode.clock);
+               sc->sc_cur_mode = index;
+       } else {
+               DPRINTF("no preferred mode found!\n");
+       }
+
+       if (index == UDL_MAX_MODES) {
+               DPRINTF("no mode line found for %dx%d @ %dHz!\n",
+                   mode.hdisplay, mode.vdisplay, mode.hz);
+
+               i = 0;
+               while (i < sc->sc_edid_info.edid_nmodes) {
+                       mode.hz =
+                           (sc->sc_edid_info.edid_modes[i].dot_clock * 1000) /
+                           (sc->sc_edid_info.edid_modes[i].htotal *
+                           sc->sc_edid_info.edid_modes[i].vtotal);
+                       mode.clock =
+                           sc->sc_edid_info.edid_modes[i].dot_clock / 10;
+                       mode.hdisplay =
+                           sc->sc_edid_info.edid_modes[i].hdisplay;
+                       mode.vdisplay =
+                           sc->sc_edid_info.edid_modes[i].vdisplay;
+                       index = udl_lookup_mode(mode.hdisplay, mode.vdisplay,
+                           mode.hz, sc->sc_chip, mode.clock);
+                       if (index < UDL_MAX_MODES)
+                               if ((sc->sc_cur_mode == UDL_MAX_MODES) ||
+                                   (index > sc->sc_cur_mode))
+                                       sc->sc_cur_mode = index;
+                       i++;
+               }
+       }
+       /*
+        * If no mode found use default.
+        */
+       if (sc->sc_cur_mode == UDL_MAX_MODES)
+               sc->sc_cur_mode = udl_lookup_mode(800, 600, 60, sc->sc_chip, 0);
+}
+
+static int
+udl_cmd_write_buf_le16(struct udl_softc *sc, const uint8_t *buf, uint32_t off,
+    uint8_t pixels, int flags)
+{
+       struct udl_cmd_buf *cb;
+
+       cb = udl_cmd_buf_alloc(sc, flags);
+       if (cb == NULL)
+               return (EAGAIN);
+
+       udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+       udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_WRITE | UDL_BULK_CMD_FB_WORD);
+       udl_cmd_insert_int_3(cb, off);
+       udl_cmd_insert_int_1(cb, pixels);
+       udl_cmd_insert_buf_le16(cb, buf, 2 * pixels);
+       udl_cmd_buf_send(sc, cb);
+
+       return (0);
+}
+
+static int
+udl_cmd_buf_copy_le16(struct udl_softc *sc, uint32_t src, uint32_t dst,
+    uint8_t pixels, int flags)
+{
+       struct udl_cmd_buf *cb;
+
+       cb = udl_cmd_buf_alloc(sc, flags);
+       if (cb == NULL)
+               return (EAGAIN);
+
+       udl_cmd_insert_int_1(cb, UDL_BULK_SOC);
+       udl_cmd_insert_int_1(cb, UDL_BULK_CMD_FB_COPY | UDL_BULK_CMD_FB_WORD);
+       udl_cmd_insert_int_3(cb, dst);
+       udl_cmd_insert_int_1(cb, pixels);
+       udl_cmd_insert_int_3(cb, src);
+       udl_cmd_buf_send(sc, cb);
+
+       return (0);
+}
diff --git a/sys/bus/u4b/video/udl.h b/sys/bus/u4b/video/udl.h
new file mode 100644 (file)
index 0000000..1fdae7a
--- /dev/null
@@ -0,0 +1,311 @@
+/*     $OpenBSD: udl.h,v 1.21 2013/04/15 09:23:02 mglocker Exp $ */
+/*     $FreeBSD$       */
+
+/*
+ * Copyright (c) 2009 Marcus Glocker <mglocker@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.
+ */
+
+#ifndef _UDL_H_
+#define        _UDL_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+/*
+ * BULK command transfer structure.
+ */
+#define        UDL_CMD_MAX_FRAMES      64      /* units */
+#define        UDL_CMD_MAX_DATA_SIZE   512     /* bytes */
+#define        UDL_CMD_MAX_HEAD_SIZE   16      /* bytes */
+#define        UDL_CMD_MAX_PIXEL_COUNT ((UDL_CMD_MAX_DATA_SIZE - UDL_CMD_MAX_HEAD_SIZE) / 2)
+#define        UDL_CMD_MAX_BUFFERS     (3 * UDL_CMD_MAX_FRAMES)
+#define        UDL_FONT_HEIGHT         16      /* pixels */
+#define        UDL_MAX_MODES           25      /* units */
+
+struct udl_cmd_buf {
+       TAILQ_ENTRY(udl_cmd_buf) entry;
+       uint32_t off;
+       uint8_t buf[UDL_CMD_MAX_DATA_SIZE] __aligned(4);
+};
+
+TAILQ_HEAD(udl_cmd_head, udl_cmd_buf);
+
+enum {
+       UDL_BULK_WRITE_0,
+       UDL_BULK_WRITE_1,
+       UDL_N_TRANSFER,
+};
+
+/*
+ * Our per device structure.
+ */
+struct udl_softc {
+       struct mtx sc_mtx;
+       struct cv sc_cv;
+       struct callout sc_callout;
+       struct usb_xfer *sc_xfer[UDL_N_TRANSFER];
+       struct usb_device *sc_udev;
+       device_t sc_fbdev;
+       struct fb_info sc_fb_info;
+       uint8_t sc_edid[128];
+       struct edid_info sc_edid_info;
+       struct udl_cmd_head sc_xfer_head[2];
+       struct udl_cmd_head sc_cmd_buf_free;
+       struct udl_cmd_head sc_cmd_buf_pending;
+       struct udl_cmd_buf sc_cmd_buf_temp[UDL_CMD_MAX_BUFFERS];
+       uint32_t sc_sync_off;
+       uint32_t sc_fb_size;
+       uint8_t *sc_fb_addr;
+       uint8_t *sc_fb_copy;
+       int     sc_def_chip;            /* default chip version */
+       int     sc_chip;
+#define        DLALL   0x0000
+#define        DL125   0x0000                  /* max 1280x1024, 1440x900 */
+#define        DL120   0x0001                  /* max 1280x1024, 1440x1050 */
+#define        DL160   0x0002                  /* max 1600x1200, 1680x1050 */
+#define        DL165   0x0003                  /* max 1600x1200, 1920x1080 */
+#define        DL195   0x0004                  /* max 1920x1200, 2048x1152 */
+#define        DLMAX   0x0004
+#define        DLUNK   0x00ff                  /* unknown */
+       int     sc_def_mode;            /* default mode */
+       int     sc_cur_mode;
+       uint8_t sc_power_save;          /* set if power save is enabled */
+       uint8_t sc_gone;
+};
+
+#define        UDL_LOCK(sc)    mtx_lock(&(sc)->sc_mtx)
+#define        UDL_UNLOCK(sc)  mtx_unlock(&(sc)->sc_mtx)
+
+/*
+ * Chip commands.
+ */
+#define        UDL_CTRL_CMD_READ_EDID          0x02
+#define        UDL_CTRL_CMD_WRITE_1            0x03
+#define        UDL_CTRL_CMD_READ_1             0x04
+#define        UDL_CTRL_CMD_POLL               0x06
+#define        UDL_CTRL_CMD_SET_KEY            0x12
+
+#define        UDL_BULK_SOC                    0xaf    /* start of command token */
+
+#define        UDL_BULK_CMD_REG_WRITE_1        0x20    /* write 1 byte to register */
+#define        UDL_BULK_CMD_EOC                0xa0    /* end of command stack */
+#define        UDL_BULK_CMD_DECOMP             0xe0    /* send decompression table */
+
+#define        UDL_BULK_CMD_FB_BASE            0x60
+#define        UDL_BULK_CMD_FB_WORD            0x08
+#define        UDL_BULK_CMD_FB_COMP            0x10
+#define        UDL_BULK_CMD_FB_WRITE           (UDL_BULK_CMD_FB_BASE | 0x00)
+#define        UDL_BULK_CMD_FB_COPY            (UDL_BULK_CMD_FB_BASE | 0x02)
+
+/*
+ * Chip registers.
+ */
+#define        UDL_REG_ADDR_START16            0x20
+#define        UDL_REG_ADDR_STRIDE16           0x23
+#define        UDL_REG_ADDR_START8             0x26
+#define        UDL_REG_ADDR_STRIDE8            0x29
+
+#define        UDL_REG_SCREEN                  0x1f
+#define        UDL_REG_SCREEN_ON               0x00
+#define        UDL_REG_SCREEN_OFF              0x01
+#define        UDL_REG_SYNC                    0xff
+
+#define        UDL_MODE_SIZE 29
+
+/*
+ * Register values for screen resolution initialization.
+ */
+static const uint8_t udl_reg_vals_640x480_60[UDL_MODE_SIZE] = {        /* 25.17 Mhz 59.9 Hz
+                                                                * VESA std */
+       0x00, 0x99, 0x30, 0x26, 0x94, 0x60, 0xa9, 0xce, 0x60, 0x07, 0xb3, 0x0f,
+       0x79, 0xff, 0xff, 0x02, 0x80, 0x83, 0xbc, 0xff, 0xfc, 0xff, 0xff, 0x01,
+       0xe0, 0x01, 0x02, 0xab, 0x13
+};
+static const uint8_t udl_reg_vals_640x480_67[UDL_MODE_SIZE] = {        /* 30.25 MHz 66.6 Hz MAC
+                                                                * std */
+       0x00, 0x1d, 0x33, 0x07, 0xb3, 0x60, 0xa9, 0xce, 0x60, 0xb6, 0xa8, 0xff,
+       0xff, 0xbf, 0x70, 0x02, 0x80, 0x83, 0xbc, 0xff, 0xff, 0xff, 0xf9, 0x01,
+       0xe0, 0x01, 0x02, 0xa2, 0x17
+};
+static const uint8_t udl_reg_vals_640x480_72[UDL_MODE_SIZE] = {        /* 31.50 Mhz 72.8 Hz
+                                                                * VESA std */
+       0x00, 0x2b, 0xeb, 0x35, 0xd3, 0x0a, 0x95, 0xe6, 0x0e, 0x0f, 0xb5, 0x15,
+       0x2a, 0xff, 0xff, 0x02, 0x80, 0xcc, 0x1d, 0xff, 0xf9, 0xff, 0xff, 0x01,
+       0xe0, 0x01, 0x02, 0x9c, 0x18
+};
+static const uint8_t udl_reg_vals_640x480_75[UDL_MODE_SIZE] = {        /* 31.50 Mhz 75.7 Hz
+                                                                * VESA std */
+       0x00, 0xeb, 0xf7, 0xd3, 0x0f, 0x4f, 0x93, 0xfa, 0x47, 0xb5, 0x58, 0xff,
+       0xff, 0xbf, 0x70, 0x02, 0x80, 0xf4, 0x8f, 0xff, 0xff, 0xff, 0xf9, 0x01,
+       0xe0, 0x01, 0x02, 0x9c, 0x18
+};
+static const uint8_t udl_reg_vals_800x480_61[UDL_MODE_SIZE] = {        /* 33.00 MHz 61.9 Hz */
+       0x00, 0x20, 0x3c, 0x7a, 0xc9, 0xf2, 0x6c, 0x48, 0xf9, 0x70, 0x53, 0xff,
+       0xff, 0x21, 0x27, 0x03, 0x20, 0x91, 0xf3, 0xff, 0xff, 0xff, 0xf9, 0x01,
+       0xe0, 0x01, 0x02, 0xc8, 0x19
+};
+static const uint8_t udl_reg_vals_800x600_56[UDL_MODE_SIZE] = {        /* 36.00 MHz 56.2 Hz
+                                                                * VESA std */
+       0x00, 0x65, 0x35, 0x48, 0xf4, 0xf2, 0x6c, 0x19, 0x18, 0xc9, 0x4b, 0xff,
+       0xff, 0x70, 0x35, 0x03, 0x20, 0x32, 0x31, 0xff, 0xff, 0xff, 0xfc, 0x02,
+       0x58, 0x01, 0x02, 0x20, 0x1c
+};
+static const uint8_t udl_reg_vals_800x600_60[UDL_MODE_SIZE] = {        /* 40.00 MHz 60.3 Hz
+                                                                * VESA std */
+       0x00, 0x20, 0x3c, 0x7a, 0xc9, 0x93, 0x60, 0xc8, 0xc7, 0x70, 0x53, 0xff,
+       0xff, 0x21, 0x27, 0x03, 0x20, 0x91, 0x8f, 0xff, 0xff, 0xff, 0xf2, 0x02,
+       0x58, 0x01, 0x02, 0x40, 0x1f
+};
+static const uint8_t udl_reg_vals_800x600_72[UDL_MODE_SIZE] = {        /* 50.00 MHz 72.1 Hz
+                                                                * VESA std */
+       0x00, 0xeb, 0xf7, 0xd1, 0x90, 0x4d, 0x82, 0x23, 0x1f, 0x39, 0xcf, 0xff,
+       0xff, 0x43, 0x21, 0x03, 0x20, 0x62, 0xc5, 0xff, 0xff, 0xff, 0xca, 0x02,
+       0x58, 0x01, 0x02, 0x10, 0x27
+};
+static const uint8_t udl_reg_vals_800x600_74[UDL_MODE_SIZE] = {        /* 50.00 MHz 74.4 Hz */
+       0x00, 0xb3, 0x76, 0x39, 0xcf, 0x60, 0xa9, 0xc7, 0xf4, 0x70, 0x53, 0xff,
+       0xff, 0x35, 0x33, 0x03, 0x20, 0x8f, 0xe9, 0xff, 0xff, 0xff, 0xf9, 0x02,
+       0x58, 0x01, 0x02, 0x10, 0x27
+};
+static const uint8_t udl_reg_vals_800x600_75[UDL_MODE_SIZE] = {        /* 49.50 MHz 75.0 Hz
+                                                                * VESA std */
+       0x00, 0xb3, 0x76, 0x39, 0xcf, 0xf2, 0x6c, 0x19, 0x18, 0x70, 0x53, 0xff,
+       0xff, 0x35, 0x33, 0x03, 0x20, 0x32, 0x31, 0xff, 0xff, 0xff, 0xf9, 0x02,
+       0x58, 0x01, 0x02, 0xac, 0x26
+};
+static const uint8_t udl_reg_vals_1024x768_60[UDL_MODE_SIZE] = {       /* 65.00 MHz 60.0 Hz
+                                                                        * VESA std */
+       0x00, 0x36, 0x18, 0xd5, 0x10, 0x60, 0xa9, 0x7b, 0x33, 0xa1, 0x2b, 0x27,
+       0x32, 0xff, 0xff, 0x04, 0x00, 0xd9, 0x9a, 0xff, 0xca, 0xff, 0xff, 0x03,
+       0x00, 0x04, 0x03, 0xc8, 0x32
+};
+static const uint8_t udl_reg_vals_1024x768_70[UDL_MODE_SIZE] = {       /* 75.00 MHz 70.0 Hz
+                                                                        * VESA std */
+       0x00, 0xb4, 0xed, 0x4c, 0x5e, 0x60, 0xa9, 0x7b, 0x33, 0x10, 0x4d, 0xff,
+       0xff, 0x27, 0x32, 0x04, 0x00, 0xd9, 0x9a, 0xff, 0xff, 0xff, 0xca, 0x03,
+       0x00, 0x04, 0x02, 0x98, 0x3a
+};
+static const uint8_t udl_reg_vals_1024x768_75[UDL_MODE_SIZE] = {       /* 78.75 MHz 75.0 Hz
+                                                                        * VESA std */
+       0x00, 0xec, 0xb4, 0xa0, 0x4c, 0x36, 0x0a, 0x07, 0xb3, 0x5e, 0xd5, 0xff,
+       0xff, 0x0f, 0x79, 0x04, 0x00, 0x0f, 0x66, 0xff, 0xff, 0xff, 0xf9, 0x03,
+       0x00, 0x04, 0x02, 0x86, 0x3d
+};
+static const uint8_t udl_reg_vals_1280x800_60[UDL_MODE_SIZE] = {       /* 83.46 MHz 59.9 MHz */
+       0x00, 0xb2, 0x19, 0x34, 0xdf, 0x93, 0x60, 0x30, 0xfb, 0x9f, 0xca, 0xff,
+       0xff, 0x27, 0x32, 0x05, 0x00, 0x61, 0xf6, 0xff, 0xff, 0xff, 0xf9, 0x03,
+       0x20, 0x04, 0x02, 0x34, 0x41
+};
+static const uint8_t udl_reg_vals_1280x960_60[UDL_MODE_SIZE] = {       /* 108.00 MHz 60.0 Hz
+                                                                        * VESA std */
+       0x00, 0xa6, 0x03, 0x5c, 0x7e, 0x0a, 0x95, 0x48, 0xf4, 0x61, 0xbd, 0xff,
+       0xff, 0x94, 0x43, 0x05, 0x00, 0x91, 0xe8, 0xff, 0xff, 0xff, 0xf9, 0x03,
+       0xc0, 0x04, 0x02, 0x60, 0x54
+};
+static const uint8_t udl_reg_vals_1280x1024_60[UDL_MODE_SIZE] = {      /* 108.00 MHz 60.0 Hz
+                                                                        * VESA std */
+       0x00, 0x98, 0xf8, 0x0d, 0x57, 0x2a, 0x55, 0x4d, 0x54, 0xca, 0x0d, 0xff,
+       0xff, 0x94, 0x43, 0x05, 0x00, 0x9a, 0xa8, 0xff, 0xff, 0xff, 0xf9, 0x04,
+       0x00, 0x04, 0x02, 0x60, 0x54
+};
+static const uint8_t udl_reg_vals_1280x1024_75[UDL_MODE_SIZE] = {      /* 135.00 MHz 75.0 Hz
+                                                                        * VESA std */
+       0x00, 0xce, 0x12, 0x3f, 0x9f, 0x2a, 0x55, 0x4d, 0x54, 0xca, 0x0d, 0xff,
+       0xff, 0x32, 0x60, 0x05, 0x00, 0x9a, 0xa8, 0xff, 0xff, 0xff, 0xf9, 0x04,
+       0x00, 0x04, 0x02, 0x78, 0x69
+};
+static const uint8_t udl_reg_vals_1366x768_60[UDL_MODE_SIZE] = {       /* 90 MHz 60.0 Hz */
+       0x01, 0x19, 0x1e, 0x1f, 0xb0, 0x93, 0x60, 0x40, 0x7b, 0x36, 0xe8, 0x27,
+       0x32, 0xff, 0xff, 0x05, 0x56, 0x03, 0xd9, 0xff, 0xff, 0xfc, 0xa7, 0x03,
+       0x00, 0x04, 0x02, 0x9a, 0x42
+};
+static const uint8_t udl_reg_vals_1440x900_60[UDL_MODE_SIZE] = {       /* 106.47 MHz 59.9 Hz */
+       0x00, 0x24, 0xce, 0xe7, 0x72, 0x36, 0x0a, 0x86, 0xca, 0x1c, 0x10, 0xff,
+       0xff, 0x60, 0x3a, 0x05, 0xa0, 0x0d, 0x94, 0xff, 0xff, 0xff, 0xf9, 0x03,
+       0x84, 0x04, 0x02, 0x2e, 0x53
+};
+static const uint8_t udl_reg_vals_1440x900_59[UDL_MODE_SIZE] = {       /* 106.50 MHz 59.8 Hz */
+       0x00, 0x24, 0xce, 0xe7, 0x72, 0xd8, 0x2a, 0x1b, 0x28, 0x1c, 0x10, 0xff,
+       0xff, 0x60, 0x3a, 0x05, 0xa0, 0x36, 0x50, 0xff, 0xff, 0xff, 0xf9, 0x03,
+       0x84, 0x04, 0x02, 0x34, 0x53
+};
+static const uint8_t udl_reg_vals_1440x900_75[UDL_MODE_SIZE] = {       /* 136.49 MHz 75.0 Hz */
+       0x00, 0x73, 0xa6, 0x14, 0xea, 0x0a, 0x95, 0xca, 0x10, 0x7f, 0x46, 0xff,
+       0xff, 0x60, 0x3a, 0x05, 0xa0, 0x94, 0x20, 0xff, 0xff, 0xff, 0xf9, 0x03,
+       0x84, 0x04, 0x02, 0xa2, 0x6a
+};
+static const uint8_t udl_reg_vals_1680x1050_60[UDL_MODE_SIZE] = {      /* 147.14 MHz 60.0 Hz */
+       0x00, 0x53, 0x43, 0xa6, 0x71, 0xc1, 0x52, 0xd9, 0x29, 0x69, 0x9f, 0xff,
+       0xff, 0xd7, 0xee, 0x06, 0x90, 0xb2, 0x53, 0xff, 0xff, 0xff, 0xf9, 0x04,
+       0x1a, 0x04, 0x02, 0xf4, 0x72
+};
+static const uint8_t udl_reg_vals_1600x1200_60[UDL_MODE_SIZE] = {      /* 162.00 MHz 60.0 Hz
+                                                                        * VESA std */
+       0x00, 0xcf, 0xa4, 0x3c, 0x4e, 0x55, 0x73, 0x71, 0x2b, 0x71, 0x52, 0xff,
+       0xff, 0xee, 0xca, 0x06, 0x40, 0xe2, 0x57, 0xff, 0xff, 0xff, 0xf9, 0x04,
+       0xb0, 0x04, 0x02, 0x90, 0x7e
+};
+static const uint8_t udl_reg_vals_1920x1080_60[UDL_MODE_SIZE] = {      /* 138.50 MHz 59.9 Hz */
+       0x00, 0x73, 0xa6, 0x28, 0xb3, 0x54, 0xaa, 0x41, 0x5d, 0x0d, 0x9f, 0x32,
+       0x60, 0xff, 0xff, 0x07, 0x80, 0x0a, 0xea, 0xff, 0xf9, 0xff, 0xff, 0x04,
+       0x38, 0x04, 0x02, 0xe0, 0x7c
+};
+
+struct udl_mode {
+       uint16_t hdisplay;
+       uint16_t vdisplay;
+       uint8_t hz;
+       uint16_t chip;
+       uint32_t clock;
+       const uint8_t *mode;
+};
+
+static const struct udl_mode udl_modes[UDL_MAX_MODES] = {
+       {640, 480, 60, DLALL, 2520, udl_reg_vals_640x480_60},
+       {640, 480, 67, DLALL, 3025, udl_reg_vals_640x480_67},
+       {640, 480, 72, DLALL, 3150, udl_reg_vals_640x480_72},
+       {640, 480, 75, DLALL, 3150, udl_reg_vals_640x480_75},
+       {800, 480, 59, DLALL, 5000, udl_reg_vals_800x480_61},
+       {800, 480, 61, DLALL, 3300, udl_reg_vals_800x480_61},
+       {800, 600, 56, DLALL, 3600, udl_reg_vals_800x600_56},
+       {800, 600, 60, DLALL, 4000, udl_reg_vals_800x600_60},
+       {800, 600, 72, DLALL, 5000, udl_reg_vals_800x600_72},
+       {800, 600, 74, DLALL, 5000, udl_reg_vals_800x600_74},
+       {800, 600, 75, DLALL, 4950, udl_reg_vals_800x600_75},
+       {1024, 768, 60, DLALL, 6500, udl_reg_vals_1024x768_60},
+       {1024, 768, 70, DLALL, 7500, udl_reg_vals_1024x768_70},
+       {1024, 768, 75, DLALL, 7850, udl_reg_vals_1024x768_75},
+       {1280, 800, 60, DLALL, 8346, udl_reg_vals_1280x800_60},
+       {1280, 960, 60, DLALL, 10800, udl_reg_vals_1280x960_60},
+       {1280, 1024, 60, DLALL, 10800, udl_reg_vals_1280x1024_60},
+       {1280, 1024, 75, DLALL, 13500, udl_reg_vals_1280x1024_75},
+       {1366, 768, 60, DLALL, 9000, udl_reg_vals_1366x768_60},
+       {1440, 900, 59, DL125, 10650, udl_reg_vals_1440x900_59},
+       {1440, 900, 60, DL125, 10647, udl_reg_vals_1440x900_60},
+       {1440, 900, 75, DL125, 13649, udl_reg_vals_1440x900_75},
+       {1680, 1050, 60, DL160, 14714, udl_reg_vals_1680x1050_60},
+       {1600, 1200, 60, DL160, 16200, udl_reg_vals_1600x1200_60},
+       {1920, 1080, 60, DL165, 13850, udl_reg_vals_1920x1080_60}
+};
+
+/*
+ * Encryption.
+ */
+static const uint8_t udl_null_key_1[] = {
+       0x57, 0xcd, 0xdc, 0xa7, 0x1c, 0x88, 0x5e, 0x15, 0x60, 0xfe, 0xc6, 0x97,
+       0x16, 0x3d, 0x47, 0xf2
+};
+
+#endif                                 /* _UDL_H_ */
index d962c27..376c619 100644 (file)
@@ -75,7 +75,7 @@
 static int rum_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum");
-SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0,
+SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RWTUN, &rum_debug, 0,
     "Debug level");
 #endif
 
index 02edb51..b0ba101 100644 (file)
@@ -80,7 +80,7 @@
 #ifdef RUN_DEBUG
 int run_debug = 0;
 static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW, 0, "USB run");
-SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RW, &run_debug, 0,
+SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0,
     "run debug level");
 #endif
 
index 07dc527..b193c25 100644 (file)
@@ -182,7 +182,7 @@ static const STRUCT_USB_HOST_ID upgt_devs[] = {
        UPGT_DEV(FSC,           E5400),
        UPGT_DEV(GLOBESPAN,     PRISM_GT_1),
        UPGT_DEV(GLOBESPAN,     PRISM_GT_2),
-       UPGT_DEV(NETGEAR,       WG111V2_2),
+       UPGT_DEV(NETGEAR,       WG111V1_2),
        UPGT_DEV(INTERSIL,      PRISM_GT),
        UPGT_DEV(SMC,           2862WG),
        UPGT_DEV(USR,           USR5422),
index c88ce92..b0cce4a 100644 (file)
@@ -79,7 +79,7 @@
 static int ural_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural");
-SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0,
+SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RWTUN, &ural_debug, 0,
     "Debug level");
 #endif
 
index c7eeb07..3372eba 100644 (file)
@@ -74,7 +74,7 @@
 static int urtwn_debug = 0;
 
 SYSCTL_NODE(_hw_usb, OID_AUTO, urtwn, CTLFLAG_RW, 0, "USB urtwn");
-SYSCTL_INT(_hw_usb_urtwn, OID_AUTO, debug, CTLFLAG_RW, &urtwn_debug, 0,
+SYSCTL_INT(_hw_usb_urtwn, OID_AUTO, debug, CTLFLAG_RWTUN, &urtwn_debug, 0,
     "Debug level");
 #endif
 
index 302a6f9..20ee5de 100644 (file)
@@ -76,7 +76,7 @@
 static int zyd_debug = 0;
 
 static SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd");
-SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0,
+SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RWTUN, &zyd_debug, 0,
     "zyd debug level");
 
 enum {