aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/usb2/core
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb2/core')
-rw-r--r--sys/dev/usb2/core/README.TXT411
-rw-r--r--sys/dev/usb2/core/usb2_busdma.c1401
-rw-r--r--sys/dev/usb2/core/usb2_busdma.h169
-rw-r--r--sys/dev/usb2/core/usb2_compat_linux.c1659
-rw-r--r--sys/dev/usb2/core/usb2_compat_linux.h465
-rw-r--r--sys/dev/usb2/core/usb2_config_td.c320
-rw-r--r--sys/dev/usb2/core/usb2_config_td.h71
-rw-r--r--sys/dev/usb2/core/usb2_core.c40
-rw-r--r--sys/dev/usb2/core/usb2_core.h448
-rw-r--r--sys/dev/usb2/core/usb2_debug.c153
-rw-r--r--sys/dev/usb2/core/usb2_debug.h70
-rw-r--r--sys/dev/usb2/core/usb2_dev.c2786
-rw-r--r--sys/dev/usb2/core/usb2_dev.h149
-rw-r--r--sys/dev/usb2/core/usb2_device.c2110
-rw-r--r--sys/dev/usb2/core/usb2_device.h162
-rw-r--r--sys/dev/usb2/core/usb2_dynamic.c140
-rw-r--r--sys/dev/usb2/core/usb2_dynamic.h61
-rw-r--r--sys/dev/usb2/core/usb2_error.c44
-rw-r--r--sys/dev/usb2/core/usb2_generic.c2226
-rw-r--r--sys/dev/usb2/core/usb2_generic.h33
-rw-r--r--sys/dev/usb2/core/usb2_handle_request.c750
-rw-r--r--sys/dev/usb2/core/usb2_handle_request.h30
-rw-r--r--sys/dev/usb2/core/usb2_hid.c582
-rw-r--r--sys/dev/usb2/core/usb2_hid.h89
-rw-r--r--sys/dev/usb2/core/usb2_hub.c1330
-rw-r--r--sys/dev/usb2/core/usb2_hub.h75
-rw-r--r--sys/dev/usb2/core/usb2_if.m52
-rw-r--r--sys/dev/usb2/core/usb2_lookup.c134
-rw-r--r--sys/dev/usb2/core/usb2_lookup.h119
-rw-r--r--sys/dev/usb2/core/usb2_mbuf.c77
-rw-r--r--sys/dev/usb2/core/usb2_mbuf.h100
-rw-r--r--sys/dev/usb2/core/usb2_msctest.c612
-rw-r--r--sys/dev/usb2/core/usb2_msctest.h33
-rw-r--r--sys/dev/usb2/core/usb2_parse.c208
-rw-r--r--sys/dev/usb2/core/usb2_parse.h36
-rw-r--r--sys/dev/usb2/core/usb2_process.c480
-rw-r--r--sys/dev/usb2/core/usb2_process.h89
-rw-r--r--sys/dev/usb2/core/usb2_request.c1373
-rw-r--r--sys/dev/usb2/core/usb2_request.h61
-rw-r--r--sys/dev/usb2/core/usb2_sw_transfer.c166
-rw-r--r--sys/dev/usb2/core/usb2_sw_transfer.h61
-rw-r--r--sys/dev/usb2/core/usb2_transfer.c2833
-rw-r--r--sys/dev/usb2/core/usb2_transfer.h123
-rw-r--r--sys/dev/usb2/core/usb2_util.c354
-rw-r--r--sys/dev/usb2/core/usb2_util.h57
-rw-r--r--sys/dev/usb2/core/usbdevs2482
46 files changed, 25224 insertions, 0 deletions
diff --git a/sys/dev/usb2/core/README.TXT b/sys/dev/usb2/core/README.TXT
new file mode 100644
index 000000000000..736723049424
--- /dev/null
+++ b/sys/dev/usb2/core/README.TXT
@@ -0,0 +1,411 @@
+
+$FreeBSD$
+
+DESCRIPTION OF THE NEW USB API
+
+The new USB 2.0 API consists of 5 functions. All transfer types are
+managed using these functions. There is no longer need for separate
+functions to setup INTERRUPT- and ISOCHRONOUS- transfers.
+
++--------------------------------------------------------------+
+| |
+| "usb2_transfer_setup" - This function will allocate all |
+| necessary DMA memory and might |
+| sleep! |
+| |
+| "usb2_transfer_unsetup" - This function will stop the USB |
+| transfer, if it is currently |
+| active, release all DMA |
+| memory and might sleep! |
+| |
+| "usb2_transfer_start" - This function will start an USB |
+| transfer, if not already started.|
+| This function is always |
+| non-blocking. ** |
+| |
+| "usb2_transfer_stop" - This function will stop an USB |
+| transfer, if not already stopped.|
+| The callback function will be |
+| called before this function |
+| returns. This function is always |
+| non-blocking. ** |
+| |
+| "usb2_transfer_drain" - This function will stop an USB |
+| transfer, if not already stopped |
+| and wait for any additional |
+| DMA load operations to complete. |
+| Buffers that are loaded into DMA |
+| using "usb2_set_frame_data" can |
+| safely be freed after that |
+| this function has returned. This |
+| function can block the caller. |
+| |
+| ** These functions must be called with the private driver's |
+| lock locked. |
+| |
+| NOTE: These USB API functions are NULL safe, with regard |
+| to the USB transfer structure pointer. |
++--------------------------------------------------------------+
+
+Reference: /sys/dev/usb2/core/usb2_transfer.c
+
+/*
+ * A simple USB callback state-machine:
+ *
+ * +->-----------------------+
+ * | |
+ * +-<-+-------[tr_setup]--------+-<-+-<-[start/restart]
+ * | |
+ * | |
+ * | |
+ * +------>-[tr_transferred]---------+
+ * | |
+ * +--------->-[tr_error]------------+
+ */
+
+void
+usb2_default_callback(struct usb2_xfer *xfer)
+{
+ /*
+ * NOTE: it is not allowed to return
+ * before "USB_CHECK_STATUS()",
+ * even if the system is tearing down!
+ */
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ /*
+ * Setup xfer->frlengths[], xfer->nframes
+ * and write data to xfer->frbuffers[], if any
+ */
+
+ /**/
+ usb2_start_hardware(xfer);
+ return;
+
+ case USB_ST_TRANSFERRED:
+ /*
+ * Read data from xfer->frbuffers[], if any.
+ * "xfer->frlengths[]" should now have been
+ * updated to the actual length.
+ */
+ return;
+
+ default: /* Error */
+ /* print error message and clear stall for example */
+ return;
+ }
+}
+
+=== Notes for USB control transfers ===
+
+An USB control transfer has three parts. First the SETUP packet, then
+DATA packet(s) and then a STATUS packet. The SETUP packet is always
+pointed to by "xfer->frbuffers[0]" and the length is stored in
+"xfer->frlengths[0]" also if there should not be sent any SETUP
+packet! If an USB control transfer has no DATA stage, then
+"xfer->nframes" should be set to 1. Else the default value is
+"xfer->nframes" equal to 2.
+
+Example1: SETUP + STATUS
+ xfer->nframes = 1;
+ xfer->frlenghts[0] = 8;
+ usb2_start_hardware(xfer);
+
+Example2: SETUP + DATA + STATUS
+ xfer->nframes = 2;
+ xfer->frlenghts[0] = 8;
+ xfer->frlenghts[1] = 1;
+ usb2_start_hardware(xfer);
+
+Example3: SETUP + DATA + STATUS - split
+1st callback:
+ xfer->nframes = 1;
+ xfer->frlenghts[0] = 8;
+ usb2_start_hardware(xfer);
+
+2nd callback:
+ /* IMPORTANT: frbuffer[0] must still point at the setup packet! */
+ xfer->nframes = 2;
+ xfer->frlenghts[0] = 0;
+ xfer->frlenghts[1] = 1;
+ usb2_start_hardware(xfer);
+
+Example4: SETUP + STATUS - split
+1st callback:
+ xfer->nframes = 1;
+ xfer->frlenghts[0] = 8;
+ xfer->flags.manual_status = 1;
+ usb2_start_hardware(xfer);
+
+2nd callback:
+ xfer->nframes = 1;
+ xfer->frlenghts[0] = 0;
+ xfer->flags.manual_status = 0;
+ usb2_start_hardware(xfer);
+
+
+=== General USB transfer notes ===
+
+ 1) Something that one should be aware of is that all USB callbacks support
+recursation. That means one can start/stop whatever transfer from the callback
+of another transfer one desires. Also the transfer that is currently called
+back. Recursion is handled like this that when the callback that wants to
+recurse returns it is called one more time.
+
+ 2) After that the "usb2_start_hardware()" function has been called in
+the callback one can always depend on that "tr_error" or "tr_transferred"
+will get jumped afterwards. Always!
+
+ 3) Sleeping functions can only be called from the attach routine of the
+driver. Else one should not use sleeping functions unless one has to. It is
+very difficult with sleep, because one has to think that the device might have
+detached when the thread returns from sleep.
+
+ 4) Polling.
+
+ use_polling
+ This flag can be used with any callback and will cause the
+ "usb2_transfer_start()" function to wait using "DELAY()",
+ without exiting any mutexes, until the transfer is finished or
+ has timed out. This flag can be changed during operation.
+
+ NOTE: If polling is used the "timeout" field should be non-zero!
+ NOTE: USB_ERR_CANCELLED is returned in case of timeout
+ instead of USB_ERR_TIMEOUT!
+
+
+
+USB device driver examples:
+
+/sys/dev/usb2/ethernet/if_axe.c
+/sys/dev/usb2/ethernet/if_aue.c
+
+QUICK REFERENCE
+===============
+
+
+/*------------------------------------------------------------------------*
+ * usb2_error_t
+ * usb2_transfer_setup(udev, ifaces, pxfer, setup_start,
+ * n_setup, priv_sc, priv_mtx)
+ *------------------------------------------------------------------------*/
+
+- "udev" is a pointer to "struct usb2_device".
+
+- "ifaces" array of interface index numbers to use. See "if_index".
+
+- "pxfer" is a pointer to an array of USB transfer pointers that are
+ initialized to NULL, and then pointed to allocated USB transfers.
+
+- "setup_start" is a pointer to an array of USB config structures.
+
+- "n_setup" is a number telling the USB system how many USB transfers
+ should be setup.
+
+- "priv_sc" is the private softc pointer, which will be used to
+ initialize "xfer->priv_sc".
+
+- "priv_mtx" is the private mutex protecting the transfer structure and
+ the softc. This pointer is used to initialize "xfer->priv_mtx".
+
+/*------------------------------------------------------------------------*
+ * void
+ * usb2_transfer_unsetup(pxfer, n_setup)
+ *------------------------------------------------------------------------*/
+
+- "pxfer" is a pointer to an array of USB transfer pointers, that may
+ be NULL, that should be freed by the USB system.
+
+- "n_setup" is a number telling the USB system how many USB transfers
+ should be unsetup
+
+NOTE: This function can sleep, waiting for active mutexes to become unlocked!
+NOTE: It is not allowed to call "usb2_transfer_unsetup" from the callback
+ of a USB transfer.
+
+/*------------------------------------------------------------------------*
+ * void
+ * usb2_transfer_start(xfer)
+ *------------------------------------------------------------------------*/
+
+- "xfer" is pointer to a USB transfer that should be started
+
+NOTE: this function must be called with "priv_mtx" locked
+
+/*------------------------------------------------------------------------*
+ * void
+ * usb2_transfer_stop(xfer)
+ *------------------------------------------------------------------------*/
+
+- "xfer" is a pointer to a USB transfer that should be stopped
+
+NOTE: this function must be called with "priv_mtx" locked
+
+NOTE: if the transfer was in progress, the callback will called with
+ "xfer->error=USB_ERR_CANCELLED", before this function returns
+
+/*------------------------------------------------------------------------*
+ * struct usb2_config {
+ * type, endpoint, direction, interval, timeout, frames, index
+ * flags, bufsize, callback
+ * };
+ *------------------------------------------------------------------------*/
+
+- The "type" field selects the USB pipe type. Valid values are:
+ UE_INTERRUPT, UE_CONTROL, UE_BULK, UE_ISOCHRONOUS. The special
+ value UE_BULK_INTR will select BULK and INTERRUPT pipes.
+ This field is mandatory.
+
+- The "endpoint" field selects the USB endpoint number. A value of
+ 0xFF, "-1" or "UE_ADDR_ANY" will select the first matching endpoint.
+ This field is mandatory.
+
+- The "direction" field selects the USB endpoint direction. A value of
+ "UE_DIR_ANY" will select the first matching endpoint. Else valid
+ values are: "UE_DIR_IN" and "UE_DIR_OUT". "UE_DIR_IN" and
+ "UE_DIR_OUT" can be binary ORed by "UE_DIR_SID" which means that the
+ direction will be swapped in case of USB_MODE_DEVICE. Note that
+ "UE_DIR_IN" refers to the data transfer direction of the "IN" tokens
+ and "UE_DIR_OUT" refers to the data transfer direction of the "OUT"
+ tokens. This field is mandatory.
+
+- The "interval" field selects the interrupt interval. The value of this
+ field is given in milliseconds and is independent of device speed. Depending
+ on the endpoint type, this field has different meaning:
+
+ UE_INTERRUPT)
+ "0" use the default interrupt interval based on endpoint descriptor.
+ "Else" use the given value for polling rate.
+
+ UE_ISOCHRONOUS)
+ "0" use default.
+ "Else" the value is ignored.
+
+ UE_BULK)
+ UE_CONTROL)
+ "0" no transfer pre-delay.
+ "Else" a delay as given by this field in milliseconds is
+ inserted before the hardware is started when
+ "usb2_start_hardware()" is called.
+ NOTE: The transfer timeout, if any, is started after that
+ the pre-delay has elapsed!
+
+- The "timeout" field, if non-zero, will set the transfer timeout in
+ milliseconds. If the "timeout" field is zero and the transfer type
+ is ISOCHRONOUS a timeout of 250ms will be used.
+
+- The "frames" field sets the maximum number of frames. If zero is
+ specified it will yield the following results:
+
+ UE_BULK)
+ UE_INTERRUPT)
+ xfer->nframes = 1;
+
+ UE_CONTROL)
+ xfer->nframes = 2;
+
+ UE_ISOCHRONOUS)
+ Not allowed. Will cause an error.
+
+- The "ep_index" field allows you to give a number, in case more
+ endpoints match the description, that selects which matching
+ "ep_index" should be used.
+
+- The "if_index" field allows you to select which of the interface
+ numbers in the "ifaces" array parameter passed to "usb2_transfer_setup"
+ that should be used when setting up the given USB transfer.
+
+- The "flags" field has type "struct usb2_xfer_flags" and allows one
+ to set initial flags an USB transfer. Valid flags are:
+
+ force_short_xfer
+ This flag forces the last transmitted USB packet to be short.
+ A short packet has a length of less than "xfer->max_packet_size",
+ which derives from "wMaxPacketSize". This flag can be changed
+ during operation.
+
+ short_xfer_ok
+ This flag allows the received transfer length, "xfer->actlen"
+ to be less than "xfer->sumlen" upon completion of a transfer.
+ This flag can be changed during operation.
+
+ pipe_bof
+ This flag causes a failing USB transfer to remain first
+ in the PIPE queue except in the case of "xfer->error" equal
+ to "USB_ERR_CANCELLED". No other USB transfers in the affected
+ PIPE queue will be started until either:
+
+ 1) The failing USB transfer is stopped using "usb2_transfer_stop()".
+ 2) The failing USB transfer performs a successful transfer.
+
+ The purpose of this flag is to avoid races when multiple
+ transfers are queued for execution on an USB endpoint, and the
+ first executing transfer fails leading to the need for
+ clearing of stall for example. In this case this flag is used
+ to prevent the following USB transfers from being executed at
+ the same time the clear-stall command is executed on the USB
+ control endpoint. This flag can be changed during operation.
+
+ "BOF" is short for "Block On Failure"
+
+ NOTE: This flag should be set on all BULK and INTERRUPT
+ USB transfers which use an endpoint that can be shared
+ between userland and kernel.
+
+ proxy_buffer
+ Setting this flag will cause that the total buffer size will
+ be rounded up to the nearest atomic hardware transfer
+ size. The maximum data length of any USB transfer is always
+ stored in the "xfer->max_data_length". For control transfers
+ the USB kernel will allocate additional space for the 8-bytes
+ of SETUP header. These 8-bytes are not counted by the
+ "xfer->max_data_length" variable. This flag can not be changed
+ during operation.
+
+ ext_buffer
+ Setting this flag will cause that no data buffer will be
+ allocated. Instead the USB client must supply a data buffer.
+ This flag can not be changed during operation.
+
+ manual_status
+ Setting this flag prevents an USB STATUS stage to be appended
+ to the end of the USB control transfer. If no control data is
+ transferred this flag must be cleared. Else an error will be
+ returned to the USB callback. This flag is mostly useful for
+ the USB device side. This flag can be changed during
+ operation.
+
+ no_pipe_ok
+ Setting this flag causes the USB_ERR_NO_PIPE error to be
+ ignored. This flag can not be changed during operation.
+
+ stall_pipe
+ Setting this flag will cause STALL pids to be sent to the
+ endpoint belonging to this transfer before the transfer is
+ started. The transfer is started at the moment the host issues
+ a clear-stall command on the STALL'ed endpoint. This flag can
+ be changed during operation. This flag does only have effect
+ in USB device side mode except for control endpoints. This
+ flag is cleared when the stall command has been executed. This
+ flag can only be changed outside the callback function by
+ using the functions "usb2_transfer_set_stall()" and
+ "usb2_transfer_clear_stall()" !
+
+- The "bufsize" field sets the total buffer size in bytes. If
+ this field is zero, "wMaxPacketSize" will be used, multiplied by the
+ "frames" field if the transfer type is ISOCHRONOUS. This is useful for
+ setting up interrupt pipes. This field is mandatory.
+
+ NOTE: For control transfers "bufsize" includes
+ the length of the request structure.
+
+- The "callback" pointer sets the USB callback. This field is mandatory.
+
+MUTEX NOTE:
+===========
+
+When you create a mutex using "mtx_init()", don't forget to call
+"mtx_destroy()" at detach, else you can get "freed memory accessed"
+panics.
+
+--HPS
diff --git a/sys/dev/usb2/core/usb2_busdma.c b/sys/dev/usb2/core/usb2_busdma.c
new file mode 100644
index 000000000000..1b8c4a1fc3ee
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_busdma.c
@@ -0,0 +1,1401 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_util.h>
+
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+static void usb2_dma_tag_create(struct usb2_dma_tag *udt, uint32_t size, uint32_t align);
+static void usb2_dma_tag_destroy(struct usb2_dma_tag *udt);
+
+#ifdef __FreeBSD__
+static void usb2_dma_lock_cb(void *arg, bus_dma_lock_op_t op);
+static int32_t usb2_m_copy_in_cb(void *arg, void *src, uint32_t count);
+static void usb2_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+static void usb2_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+static void usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error, uint8_t isload);
+
+#endif
+
+#ifdef __NetBSD__
+static int32_t usb2_m_copy_in_cb(void *arg, caddr_t src, uint32_t count);
+static void usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs, int nseg, int error, uint8_t isload);
+
+#endif
+
+/*------------------------------------------------------------------------*
+ * usb2_get_page - lookup DMA-able memory for the given offset
+ *
+ * NOTE: Only call this function when the "page_cache" structure has
+ * been properly initialized !
+ *------------------------------------------------------------------------*/
+void
+usb2_get_page(struct usb2_page_cache *pc, uint32_t offset,
+ struct usb2_page_search *res)
+{
+ struct usb2_page *page;
+
+ if (pc->page_start) {
+
+ /* Case 1 - something has been loaded into DMA */
+
+ if (pc->buffer) {
+
+ /* Case 1a - Kernel Virtual Address */
+
+ res->buffer = USB_ADD_BYTES(pc->buffer, offset);
+ }
+ offset += pc->page_offset_buf;
+
+ /* compute destination page */
+
+ page = pc->page_start;
+
+ if (pc->ismultiseg) {
+
+ page += (offset / USB_PAGE_SIZE);
+
+ offset %= USB_PAGE_SIZE;
+
+ res->length = USB_PAGE_SIZE - offset;
+ res->physaddr = page->physaddr + offset;
+ } else {
+ res->length = 0 - 1;
+ res->physaddr = page->physaddr + offset;
+ }
+ if (!pc->buffer) {
+
+ /* Case 1b - Non Kernel Virtual Address */
+
+ res->buffer = USB_ADD_BYTES(page->buffer, offset);
+ }
+ } else {
+
+ /* Case 2 - Plain PIO */
+
+ res->buffer = USB_ADD_BYTES(pc->buffer, offset);
+ res->length = 0 - 1;
+ res->physaddr = 0;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_copy_in - copy directly to DMA-able memory
+ *------------------------------------------------------------------------*/
+void
+usb2_copy_in(struct usb2_page_cache *cache, uint32_t offset,
+ const void *ptr, uint32_t len)
+{
+ struct usb2_page_search buf_res;
+
+ while (len != 0) {
+
+ usb2_get_page(cache, offset, &buf_res);
+
+ if (buf_res.length > len) {
+ buf_res.length = len;
+ }
+ bcopy(ptr, buf_res.buffer, buf_res.length);
+
+ offset += buf_res.length;
+ len -= buf_res.length;
+ ptr = USB_ADD_BYTES(ptr, buf_res.length);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_copy_in_user - copy directly to DMA-able memory from userland
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+int
+usb2_copy_in_user(struct usb2_page_cache *cache, uint32_t offset,
+ const void *ptr, uint32_t len)
+{
+ struct usb2_page_search buf_res;
+ int error;
+
+ while (len != 0) {
+
+ usb2_get_page(cache, offset, &buf_res);
+
+ if (buf_res.length > len) {
+ buf_res.length = len;
+ }
+ error = copyin(ptr, buf_res.buffer, buf_res.length);
+ if (error)
+ return (error);
+
+ offset += buf_res.length;
+ len -= buf_res.length;
+ ptr = USB_ADD_BYTES(ptr, buf_res.length);
+ }
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_m_copy_in - copy a mbuf chain directly into DMA-able memory
+ *------------------------------------------------------------------------*/
+struct usb2_m_copy_in_arg {
+ struct usb2_page_cache *cache;
+ uint32_t dst_offset;
+};
+
+static int32_t
+#ifdef __FreeBSD__
+usb2_m_copy_in_cb(void *arg, void *src, uint32_t count)
+#else
+usb2_m_copy_in_cb(void *arg, caddr_t src, uint32_t count)
+#endif
+{
+ register struct usb2_m_copy_in_arg *ua = arg;
+
+ usb2_copy_in(ua->cache, ua->dst_offset, src, count);
+ ua->dst_offset += count;
+ return (0);
+}
+
+void
+usb2_m_copy_in(struct usb2_page_cache *cache, uint32_t dst_offset,
+ struct mbuf *m, uint32_t src_offset, uint32_t src_len)
+{
+ struct usb2_m_copy_in_arg arg = {cache, dst_offset};
+ register int error;
+
+ error = m_apply(m, src_offset, src_len, &usb2_m_copy_in_cb, &arg);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_uiomove - factored out code
+ *------------------------------------------------------------------------*/
+int
+usb2_uiomove(struct usb2_page_cache *pc, struct uio *uio,
+ uint32_t pc_offset, uint32_t len)
+{
+ struct usb2_page_search res;
+ int error = 0;
+
+ while (len != 0) {
+
+ usb2_get_page(pc, pc_offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ /*
+ * "uiomove()" can sleep so one needs to make a wrapper,
+ * exiting the mutex and checking things
+ */
+ error = uiomove(res.buffer, res.length, uio);
+
+ if (error) {
+ break;
+ }
+ pc_offset += res.length;
+ len -= res.length;
+ }
+ return (error);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_copy_out - copy directly from DMA-able memory
+ *------------------------------------------------------------------------*/
+void
+usb2_copy_out(struct usb2_page_cache *cache, uint32_t offset,
+ void *ptr, uint32_t len)
+{
+ struct usb2_page_search res;
+
+ while (len != 0) {
+
+ usb2_get_page(cache, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ bcopy(res.buffer, ptr, res.length);
+
+ offset += res.length;
+ len -= res.length;
+ ptr = USB_ADD_BYTES(ptr, res.length);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_copy_out_user - copy directly from DMA-able memory to userland
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+int
+usb2_copy_out_user(struct usb2_page_cache *cache, uint32_t offset,
+ void *ptr, uint32_t len)
+{
+ struct usb2_page_search res;
+ int error;
+
+ while (len != 0) {
+
+ usb2_get_page(cache, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ error = copyout(res.buffer, ptr, res.length);
+ if (error)
+ return (error);
+
+ offset += res.length;
+ len -= res.length;
+ ptr = USB_ADD_BYTES(ptr, res.length);
+ }
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bzero - zero DMA-able memory
+ *------------------------------------------------------------------------*/
+void
+usb2_bzero(struct usb2_page_cache *cache, uint32_t offset, uint32_t len)
+{
+ struct usb2_page_search res;
+
+ while (len != 0) {
+
+ usb2_get_page(cache, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ bzero(res.buffer, res.length);
+
+ offset += res.length;
+ len -= res.length;
+ }
+ return;
+}
+
+
+#ifdef __FreeBSD__
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_lock_cb - dummy callback
+ *------------------------------------------------------------------------*/
+static void
+usb2_dma_lock_cb(void *arg, bus_dma_lock_op_t op)
+{
+ /* we use "mtx_owned()" instead of this function */
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_tag_create - allocate a DMA tag
+ *
+ * NOTE: If the "align" parameter has a value of 1 the DMA-tag will
+ * allow multi-segment mappings. Else all mappings are single-segment.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dma_tag_create(struct usb2_dma_tag *udt,
+ uint32_t size, uint32_t align)
+{
+ bus_dma_tag_t tag;
+
+ if (bus_dma_tag_create
+ ( /* parent */ udt->tag_parent->tag,
+ /* alignment */ align,
+ /* boundary */ USB_PAGE_SIZE,
+ /* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1,
+ /* highaddr */ BUS_SPACE_MAXADDR,
+ /* filter */ NULL,
+ /* filterarg */ NULL,
+ /* maxsize */ size,
+ /* nsegments */ (align == 1) ?
+ (2 + (size / USB_PAGE_SIZE)) : 1,
+ /* maxsegsz */ (align == 1) ?
+ USB_PAGE_SIZE : size,
+ /* flags */ 0,
+ /* lockfn */ &usb2_dma_lock_cb,
+ /* lockarg */ NULL,
+ &tag)) {
+ tag = NULL;
+ }
+ udt->tag = tag;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_tag_free - free a DMA tag
+ *------------------------------------------------------------------------*/
+static void
+usb2_dma_tag_destroy(struct usb2_dma_tag *udt)
+{
+ bus_dma_tag_destroy(udt->tag);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_alloc_mem_cb - BUS-DMA callback function
+ *------------------------------------------------------------------------*/
+static void
+usb2_pc_alloc_mem_cb(void *arg, bus_dma_segment_t *segs,
+ int nseg, int error)
+{
+ usb2_pc_common_mem_cb(arg, segs, nseg, error, 0);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_load_mem_cb - BUS-DMA callback function
+ *------------------------------------------------------------------------*/
+static void
+usb2_pc_load_mem_cb(void *arg, bus_dma_segment_t *segs,
+ int nseg, int error)
+{
+ usb2_pc_common_mem_cb(arg, segs, nseg, error, 1);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_common_mem_cb - BUS-DMA callback function
+ *------------------------------------------------------------------------*/
+static void
+usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs,
+ int nseg, int error, uint8_t isload)
+{
+ struct usb2_dma_parent_tag *uptag;
+ struct usb2_page_cache *pc;
+ struct usb2_page *pg;
+ uint32_t rem;
+ uint8_t owned;
+
+ pc = arg;
+ uptag = pc->tag_parent;
+
+ /*
+ * XXX There is sometimes recursive locking here.
+ * XXX We should try to find a better solution.
+ * XXX Until further the "owned" variable does
+ * XXX the trick.
+ */
+
+ if (error) {
+ goto done;
+ }
+ pg = pc->page_start;
+ pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1);
+ rem = segs->ds_addr & (USB_PAGE_SIZE - 1);
+ pc->page_offset_buf = rem;
+ pc->page_offset_end += rem;
+ nseg--;
+
+ while (nseg > 0) {
+ nseg--;
+ segs++;
+ pg++;
+ pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1);
+ }
+
+done:
+ owned = mtx_owned(uptag->mtx);
+ if (!owned)
+ mtx_lock(uptag->mtx);
+
+ uptag->dma_error = (error ? 1 : 0);
+ if (isload) {
+ (uptag->func) (uptag);
+ } else {
+ usb2_cv_broadcast(uptag->cv);
+ }
+ if (!owned)
+ mtx_unlock(uptag->mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_alloc_mem - allocate DMA'able memory
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg,
+ uint32_t size, uint32_t align)
+{
+ struct usb2_dma_parent_tag *uptag;
+ struct usb2_dma_tag *utag;
+ bus_dmamap_t map;
+ void *ptr;
+ int err;
+
+ uptag = pc->tag_parent;
+
+ if (align != 1) {
+ /*
+ * The alignment must be greater or equal to the
+ * "size" else the object can be split between two
+ * memory pages and we get a problem!
+ */
+ while (align < size) {
+ align *= 2;
+ if (align == 0) {
+ goto error;
+ }
+ }
+#if 1
+ /*
+ * XXX BUS-DMA workaround - FIXME later:
+ *
+ * We assume that that the aligment at this point of
+ * the code is greater than or equal to the size and
+ * less than two times the size, so that if we double
+ * the size, the size will be greater than the
+ * alignment.
+ *
+ * The bus-dma system has a check for "alignment"
+ * being less than "size". If that check fails we end
+ * up using contigmalloc which is page based even for
+ * small allocations. Try to avoid that to save
+ * memory, hence we sometimes to a large number of
+ * small allocations!
+ */
+ if (size <= (USB_PAGE_SIZE / 2)) {
+ size *= 2;
+ }
+#endif
+ }
+ /* get the correct DMA tag */
+ utag = usb2_dma_tag_find(uptag, size, align);
+ if (utag == NULL) {
+ goto error;
+ }
+ /* allocate memory */
+ if (bus_dmamem_alloc(
+ utag->tag, &ptr, (BUS_DMA_WAITOK | BUS_DMA_COHERENT), &map)) {
+ goto error;
+ }
+ /* setup page cache */
+ pc->buffer = ptr;
+ pc->page_start = pg;
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = size;
+ pc->map = map;
+ pc->tag = utag->tag;
+ pc->ismultiseg = (align == 1);
+
+ mtx_lock(uptag->mtx);
+
+ /* load memory into DMA */
+ err = bus_dmamap_load(
+ utag->tag, map, ptr, size, &usb2_pc_alloc_mem_cb,
+ pc, (BUS_DMA_WAITOK | BUS_DMA_COHERENT));
+
+ if (err == EINPROGRESS) {
+ usb2_cv_wait(uptag->cv, uptag->mtx);
+ err = 0;
+ }
+ mtx_unlock(uptag->mtx);
+
+ if (err || uptag->dma_error) {
+ bus_dmamem_free(utag->tag, ptr, map);
+ goto error;
+ }
+ bzero(ptr, size);
+
+ usb2_pc_cpu_flush(pc);
+
+ return (0);
+
+error:
+ /* reset most of the page cache */
+ pc->buffer = NULL;
+ pc->page_start = NULL;
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = 0;
+ pc->map = NULL;
+ pc->tag = NULL;
+ return (1);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_free_mem - free DMA memory
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_free_mem(struct usb2_page_cache *pc)
+{
+ if (pc && pc->buffer) {
+
+ bus_dmamap_unload(pc->tag, pc->map);
+
+ bus_dmamem_free(pc->tag, pc->buffer, pc->map);
+
+ pc->buffer = NULL;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_load_mem - load virtual memory into DMA
+ *
+ * Return values:
+ * 0: Success
+ * Else: Error
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_pc_load_mem(struct usb2_page_cache *pc, uint32_t size, uint8_t sync)
+{
+ /* setup page cache */
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = size;
+ pc->ismultiseg = 1;
+
+ mtx_assert(pc->tag_parent->mtx, MA_OWNED);
+
+ if (size > 0) {
+ if (sync) {
+ struct usb2_dma_parent_tag *uptag;
+ int err;
+
+ uptag = pc->tag_parent;
+
+ /*
+ * Try to load memory into DMA.
+ */
+ err = bus_dmamap_load(
+ pc->tag, pc->map, pc->buffer, size,
+ &usb2_pc_alloc_mem_cb, pc, BUS_DMA_WAITOK);
+ if (err == EINPROGRESS) {
+ usb2_cv_wait(uptag->cv, uptag->mtx);
+ err = 0;
+ }
+ if (err || uptag->dma_error) {
+ return (1);
+ }
+ } else {
+
+ /*
+ * Try to load memory into DMA. The callback
+ * will be called in all cases:
+ */
+ if (bus_dmamap_load(
+ pc->tag, pc->map, pc->buffer, size,
+ &usb2_pc_load_mem_cb, pc, BUS_DMA_WAITOK)) {
+ }
+ }
+ } else {
+ if (!sync) {
+ /*
+ * Call callback so that refcount is decremented
+ * properly:
+ */
+ pc->tag_parent->dma_error = 0;
+ (pc->tag_parent->func) (pc->tag_parent);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_cpu_invalidate - invalidate CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_cpu_invalidate(struct usb2_page_cache *pc)
+{
+ bus_dmamap_sync(pc->tag, pc->map,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_cpu_flush - flush CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_cpu_flush(struct usb2_page_cache *pc)
+{
+ bus_dmamap_sync(pc->tag, pc->map,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_dmamap_create - create a DMA map
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_pc_dmamap_create(struct usb2_page_cache *pc, uint32_t size)
+{
+ struct usb2_xfer_root *info;
+ struct usb2_dma_tag *utag;
+
+ /* get info */
+ info = pc->tag_parent->info;
+
+ /* sanity check */
+ if (info == NULL) {
+ goto error;
+ }
+ utag = usb2_dma_tag_find(pc->tag_parent, size, 1);
+ if (utag == NULL) {
+ goto error;
+ }
+ /* create DMA map */
+ if (bus_dmamap_create(utag->tag, 0, &pc->map)) {
+ goto error;
+ }
+ pc->tag = utag->tag;
+ return 0; /* success */
+
+error:
+ pc->map = NULL;
+ pc->tag = NULL;
+ return 1; /* failure */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_dmamap_destroy
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_dmamap_destroy(struct usb2_page_cache *pc)
+{
+ if (pc && pc->tag) {
+ bus_dmamap_destroy(pc->tag, pc->map);
+ pc->tag = NULL;
+ pc->map = NULL;
+ }
+ return;
+}
+
+#endif
+
+#ifdef __NetBSD__
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_tag_create - allocate a DMA tag
+ *
+ * NOTE: If the "align" parameter has a value of 1 the DMA-tag will
+ * allow multi-segment mappings. Else all mappings are single-segment.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dma_tag_create(struct usb2_dma_tag *udt,
+ uint32_t size, uint32_t align)
+{
+ uint32_t nseg;
+
+ if (align == 1) {
+ nseg = (2 + (size / USB_PAGE_SIZE));
+ } else {
+ nseg = 1;
+ }
+
+ udt->p_seg = malloc(nseg * sizeof(*(udt->p_seg)),
+ M_USB, M_WAITOK | M_ZERO);
+
+ if (udt->p_seg == NULL) {
+ return;
+ }
+ udt->tag = udt->tag_parent->tag;
+ udt->n_seg = nseg;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_tag_free - free a DMA tag
+ *------------------------------------------------------------------------*/
+static void
+usb2_dma_tag_destroy(struct usb2_dma_tag *udt)
+{
+ free(udt->p_seg, M_USB);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_common_mem_cb - BUS-DMA callback function
+ *------------------------------------------------------------------------*/
+static void
+usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs,
+ int nseg, int error, uint8_t isload, uint8_t dolock)
+{
+ struct usb2_dma_parent_tag *uptag;
+ struct usb2_page *pg;
+ uint32_t rem;
+ uint8_t ext_seg; /* extend last segment */
+
+ uptag = pc->tag_parent;
+
+ if (error) {
+ goto done;
+ }
+ pg = pc->page_start;
+ pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1);
+ rem = segs->ds_addr & (USB_PAGE_SIZE - 1);
+ pc->page_offset_buf = rem;
+ pc->page_offset_end += rem;
+ if (nseg < ((pc->page_offset_end +
+ (USB_PAGE_SIZE - 1)) / USB_PAGE_SIZE)) {
+ ext_seg = 1;
+ } else {
+ ext_seg = 0;
+ }
+ nseg--;
+
+ while (nseg > 0) {
+ nseg--;
+ segs++;
+ pg++;
+ pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1);
+ }
+
+ /*
+ * XXX The segments we get from BUS-DMA are not aligned,
+ * XXX so we need to extend the last segment if we are
+ * XXX unaligned and cross the segment boundary!
+ */
+ if (ext_seg && pc->ismultiseg) {
+ (pg + 1)->physaddr = pg->physaddr + USB_PAGE_SIZE;
+ }
+done:
+ if (dolock)
+ mtx_lock(uptag->mtx);
+
+ uptag->dma_error = (error ? 1 : 0);
+ if (isload) {
+ (uptag->func) (uptag);
+ }
+ if (dolock)
+ mtx_unlock(uptag->mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_alloc_mem - allocate DMA'able memory
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg,
+ uint32_t size, uint32_t align)
+{
+ struct usb2_dma_parent_tag *uptag;
+ struct usb2_dma_tag *utag;
+ caddr_t ptr = NULL;
+ bus_dmamap_t map;
+ int seg_count;
+
+ uptag = pc->tag_parent;
+
+ if (align != 1) {
+ /*
+ * The alignment must be greater or equal to the
+ * "size" else the object can be split between two
+ * memory pages and we get a problem!
+ */
+ while (align < size) {
+ align *= 2;
+ if (align == 0) {
+ goto done_5;
+ }
+ }
+ }
+ /* get the correct DMA tag */
+ utag = usb2_dma_tag_find(pc->tag_parent, size, align);
+ if (utag == NULL) {
+ goto done_5;
+ }
+ if (bus_dmamem_alloc(utag->tag, size, align, 0, utag->p_seg,
+ utag->n_seg, &seg_count, BUS_DMA_WAITOK)) {
+ goto done_4;
+ }
+ if (bus_dmamem_map(utag->tag, utag->p_seg, seg_count, size,
+ &ptr, BUS_DMA_WAITOK | BUS_DMA_COHERENT)) {
+ goto done_3;
+ }
+ if (bus_dmamap_create(utag->tag, size, utag->n_seg, (align == 1) ?
+ USB_PAGE_SIZE : size, 0, BUS_DMA_WAITOK, &map)) {
+ goto done_2;
+ }
+ if (bus_dmamap_load(utag->tag, map, ptr, size, NULL,
+ BUS_DMA_WAITOK)) {
+ goto done_1;
+ }
+ pc->p_seg = malloc(seg_count * sizeof(*(pc->p_seg)),
+ M_USB, M_WAITOK | M_ZERO);
+ if (pc->p_seg == NULL) {
+ goto done_0;
+ }
+ /* store number if actual segments used */
+ pc->n_seg = seg_count;
+
+ /* make a copy of the segments */
+ bcopy(utag->p_seg, pc->p_seg,
+ seg_count * sizeof(*(pc->p_seg)));
+
+ /* setup page cache */
+ pc->buffer = ptr;
+ pc->page_start = pg;
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = size;
+ pc->map = map;
+ pc->tag = utag->tag;
+ pc->ismultiseg = (align == 1);
+
+ usb2_pc_common_mem_cb(pc, utag->p_seg, seg_count, 0, 0, 1);
+
+ bzero(ptr, size);
+
+ usb2_pc_cpu_flush(pc);
+
+ return (0);
+
+done_0:
+ bus_dmamap_unload(utag->tag, map);
+done_1:
+ bus_dmamap_destroy(utag->tag, map);
+done_2:
+ bus_dmamem_unmap(utag->tag, ptr, size);
+done_3:
+ bus_dmamem_free(utag->tag, utag->p_seg, seg_count);
+done_4:
+ /* utag is destroyed later */
+done_5:
+ /* reset most of the page cache */
+ pc->buffer = NULL;
+ pc->page_start = NULL;
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = 0;
+ pc->map = NULL;
+ pc->tag = NULL;
+ pc->n_seg = 0;
+ pc->p_seg = NULL;
+ return (1);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_free_mem - free DMA memory
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_free_mem(struct usb2_page_cache *pc)
+{
+ if (pc && pc->buffer) {
+ bus_dmamap_unload(pc->tag, pc->map);
+ bus_dmamap_destroy(pc->tag, pc->map);
+ bus_dmamem_unmap(pc->tag, pc->buffer,
+ pc->page_offset_end - pc->page_offset_buf);
+ bus_dmamem_free(pc->tag, pc->p_seg, pc->n_seg);
+ free(pc->p_seg, M_USB);
+ pc->buffer = NULL;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_load_mem - load virtual memory into DMA
+ *
+ * Return values:
+ * 0: Success
+ * Else: Error
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_pc_load_mem(struct usb2_page_cache *pc, uint32_t size, uint8_t sync)
+{
+ int error;
+
+ /* setup page cache */
+ pc->page_offset_buf = 0;
+ pc->page_offset_end = size;
+ pc->ismultiseg = 1;
+
+ if (size > 0) {
+
+ /* try to load memory into DMA using using no wait option */
+ if (bus_dmamap_load(pc->tag, pc->map, pc->buffer,
+ size, NULL, BUS_DMA_NOWAIT)) {
+ error = ENOMEM;
+ } else {
+ error = 0;
+ }
+
+ usb2_pc_common_mem_cb(pc, pc->map->dm_segs,
+ pc->map->dm_nsegs, error, !sync);
+
+ if (error) {
+ return (1);
+ }
+ } else {
+ if (!sync) {
+ /*
+ * Call callback so that refcount is decremented
+ * properly:
+ */
+ pc->tag_parent->dma_error = 0;
+ (pc->tag_parent->func) (pc->tag_parent);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_cpu_invalidate - invalidate CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_cpu_invalidate(struct usb2_page_cache *pc)
+{
+ uint32_t len;
+
+ len = pc->page_offset_end - pc->page_offset_buf;
+
+ bus_dmamap_sync(pc->tag, pc->map, 0, len,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_cpu_flush - flush CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_cpu_flush(struct usb2_page_cache *pc)
+{
+ uint32_t len;
+
+ len = pc->page_offset_end - pc->page_offset_buf;
+
+ bus_dmamap_sync(pc->tag, pc->map, 0, len,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_dmamap_create - create a DMA map
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_pc_dmamap_create(struct usb2_page_cache *pc, uint32_t size)
+{
+ struct usb2_xfer_root *info;
+ struct usb2_dma_tag *utag;
+
+ /* get info */
+ info = pc->tag_parent->info;
+
+ /* sanity check */
+ if (info == NULL) {
+ goto error;
+ }
+ utag = usb2_dma_tag_find(pc->tag_parent, size, 1);
+ if (utag == NULL) {
+ goto error;
+ }
+ if (bus_dmamap_create(utag->tag, size, utag->n_seg,
+ USB_PAGE_SIZE, 0, BUS_DMA_WAITOK, &pc->map)) {
+ goto error;
+ }
+ pc->tag = utag->tag;
+ pc->p_seg = utag->p_seg;
+ pc->n_seg = utag->n_seg;
+ return 0; /* success */
+
+error:
+ pc->map = NULL;
+ pc->tag = NULL;
+ pc->p_seg = NULL;
+ pc->n_seg = 0;
+ return 1; /* failure */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_dmamap_destroy
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_dmamap_destroy(struct usb2_page_cache *pc)
+{
+ if (pc && pc->tag) {
+ bus_dmamap_destroy(pc->tag, pc->map);
+ pc->tag = NULL;
+ pc->map = NULL;
+ }
+ return;
+}
+
+#endif
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_tag_find - factored out code
+ *------------------------------------------------------------------------*/
+struct usb2_dma_tag *
+usb2_dma_tag_find(struct usb2_dma_parent_tag *udpt,
+ uint32_t size, uint32_t align)
+{
+ struct usb2_dma_tag *udt;
+ uint8_t nudt;
+
+ USB_ASSERT(align > 0, ("Invalid parameter align = 0!\n"));
+ USB_ASSERT(size > 0, ("Invalid parameter size = 0!\n"));
+
+ udt = udpt->utag_first;
+ nudt = udpt->utag_max;
+
+ while (nudt--) {
+
+ if (udt->align == 0) {
+ usb2_dma_tag_create(udt, size, align);
+ if (udt->tag == NULL) {
+ return (NULL);
+ }
+ udt->align = align;
+ udt->size = size;
+ return (udt);
+ }
+ if ((udt->align == align) && (udt->size == size)) {
+ return (udt);
+ }
+ udt++;
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_tag_setup - initialise USB DMA tags
+ *------------------------------------------------------------------------*/
+void
+usb2_dma_tag_setup(struct usb2_dma_parent_tag *udpt,
+ struct usb2_dma_tag *udt, bus_dma_tag_t dmat,
+ struct mtx *mtx, usb2_dma_callback_t *func,
+ struct usb2_xfer_root *info, uint8_t ndmabits,
+ uint8_t nudt)
+{
+ bzero(udpt, sizeof(*udpt));
+
+ /* sanity checking */
+ if ((nudt == 0) ||
+ (ndmabits == 0) ||
+ (mtx == NULL)) {
+ /* something is corrupt */
+ return;
+ }
+#ifdef __FreeBSD__
+ /* initialise condition variable */
+ usb2_cv_init(udpt->cv, "USB DMA CV");
+#endif
+
+ /* store some information */
+ udpt->mtx = mtx;
+ udpt->info = info;
+ udpt->func = func;
+ udpt->tag = dmat;
+ udpt->utag_first = udt;
+ udpt->utag_max = nudt;
+ udpt->dma_bits = ndmabits;
+
+ while (nudt--) {
+ bzero(udt, sizeof(*udt));
+ udt->tag_parent = udpt;
+ udt++;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_tag_unsetup - factored out code
+ *------------------------------------------------------------------------*/
+void
+usb2_dma_tag_unsetup(struct usb2_dma_parent_tag *udpt)
+{
+ struct usb2_dma_tag *udt;
+ uint8_t nudt;
+
+ udt = udpt->utag_first;
+ nudt = udpt->utag_max;
+
+ while (nudt--) {
+
+ if (udt->align) {
+ /* destroy the USB DMA tag */
+ usb2_dma_tag_destroy(udt);
+ udt->align = 0;
+ }
+ udt++;
+ }
+
+ if (udpt->utag_max) {
+#ifdef __FreeBSD__
+ /* destroy the condition variable */
+ usb2_cv_destroy(udpt->cv);
+#endif
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bdma_work_loop
+ *
+ * This function handles loading of virtual buffers into DMA and is
+ * only called when "dma_refcount" is zero.
+ *------------------------------------------------------------------------*/
+void
+usb2_bdma_work_loop(struct usb2_xfer_queue *pq)
+{
+ struct usb2_xfer_root *info;
+ struct usb2_xfer *xfer;
+ uint32_t nframes;
+
+ xfer = pq->curr;
+ info = xfer->usb2_root;
+
+ mtx_assert(info->priv_mtx, MA_OWNED);
+
+ if (xfer->error) {
+ /* some error happened */
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_done(xfer, 0);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ if (!xfer->flags_int.bdma_setup) {
+ struct usb2_page *pg;
+ uint32_t frlength_0;
+ uint8_t isread;
+
+ xfer->flags_int.bdma_setup = 1;
+
+ /* reset BUS-DMA load state */
+
+ info->dma_error = 0;
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ nframes = 1;
+ frlength_0 = xfer->sumlen;
+ } else {
+ /* can be multiple frame buffers */
+ nframes = xfer->nframes;
+ frlength_0 = xfer->frlengths[0];
+ }
+
+ /*
+ * Set DMA direction first. This is needed to
+ * select the correct cache invalidate and cache
+ * flush operations.
+ */
+ isread = USB_GET_DATA_ISREAD(xfer);
+ pg = xfer->dma_page_ptr;
+
+ if (xfer->flags_int.control_xfr &&
+ xfer->flags_int.control_hdr) {
+ /* special case */
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+ /* The device controller writes to memory */
+ xfer->frbuffers[0].isread = 1;
+ } else {
+ /* The host controller reads from memory */
+ xfer->frbuffers[0].isread = 0;
+ }
+ } else {
+ /* default case */
+ xfer->frbuffers[0].isread = isread;
+ }
+
+ /*
+ * Setup the "page_start" pointer which points to an array of
+ * USB pages where information about the physical address of a
+ * page will be stored. Also initialise the "isread" field of
+ * the USB page caches.
+ */
+ xfer->frbuffers[0].page_start = pg;
+
+ info->dma_nframes = nframes;
+ info->dma_currframe = 0;
+ info->dma_frlength_0 = frlength_0;
+
+ pg += (frlength_0 / USB_PAGE_SIZE);
+ pg += 2;
+
+ while (--nframes > 0) {
+ xfer->frbuffers[nframes].isread = isread;
+ xfer->frbuffers[nframes].page_start = pg;
+
+ pg += (xfer->frlengths[nframes] / USB_PAGE_SIZE);
+ pg += 2;
+ }
+
+ }
+ if (info->dma_error) {
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ if (info->dma_currframe != info->dma_nframes) {
+
+ if (info->dma_currframe == 0) {
+ /* special case */
+ usb2_pc_load_mem(xfer->frbuffers,
+ info->dma_frlength_0, 0);
+ } else {
+ /* default case */
+ nframes = info->dma_currframe;
+ usb2_pc_load_mem(xfer->frbuffers + nframes,
+ xfer->frlengths[nframes], 0);
+ }
+
+ /* advance frame index */
+ info->dma_currframe++;
+
+ return;
+ }
+ /* go ahead */
+ usb2_bdma_pre_sync(xfer);
+
+ /* start loading next USB transfer, if any */
+ usb2_command_wrapper(pq, NULL);
+
+ /* finally start the hardware */
+ usb2_pipe_enter(xfer);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bdma_done_event
+ *
+ * This function is called when the BUS-DMA has loaded virtual memory
+ * into DMA, if any.
+ *------------------------------------------------------------------------*/
+void
+usb2_bdma_done_event(struct usb2_dma_parent_tag *udpt)
+{
+ struct usb2_xfer_root *info;
+
+ info = udpt->info;
+
+ mtx_assert(info->priv_mtx, MA_OWNED);
+
+ /* copy error */
+ info->dma_error = udpt->dma_error;
+
+ /* enter workloop again */
+ usb2_command_wrapper(&info->dma_q,
+ info->dma_q.curr);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bdma_pre_sync
+ *
+ * This function handles DMA synchronisation that must be done before
+ * an USB transfer is started.
+ *------------------------------------------------------------------------*/
+void
+usb2_bdma_pre_sync(struct usb2_xfer *xfer)
+{
+ struct usb2_page_cache *pc;
+ uint32_t nframes;
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ nframes = 1;
+ } else {
+ /* can be multiple frame buffers */
+ nframes = xfer->nframes;
+ }
+
+ pc = xfer->frbuffers;
+
+ while (nframes--) {
+
+ if (pc->page_offset_buf != pc->page_offset_end) {
+ if (pc->isread) {
+ usb2_pc_cpu_invalidate(pc);
+ } else {
+ usb2_pc_cpu_flush(pc);
+ }
+ }
+ pc++;
+ }
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bdma_post_sync
+ *
+ * This function handles DMA synchronisation that must be done after
+ * an USB transfer is complete.
+ *------------------------------------------------------------------------*/
+void
+usb2_bdma_post_sync(struct usb2_xfer *xfer)
+{
+ struct usb2_page_cache *pc;
+ uint32_t nframes;
+
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ nframes = 1;
+ } else {
+ /* can be multiple frame buffers */
+ nframes = xfer->nframes;
+ }
+
+ pc = xfer->frbuffers;
+
+ while (nframes--) {
+
+ if (pc->page_offset_buf != pc->page_offset_end) {
+ if (pc->isread) {
+ usb2_pc_cpu_invalidate(pc);
+ }
+ }
+ pc++;
+ }
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_busdma.h b/sys/dev/usb2/core/usb2_busdma.h
new file mode 100644
index 000000000000..0b035995e624
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_busdma.h
@@ -0,0 +1,169 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_BUSDMA_H_
+#define _USB2_BUSDMA_H_
+
+#include <sys/uio.h>
+#include <sys/mbuf.h>
+
+#include <machine/bus.h>
+
+/* defines */
+
+#define USB_PAGE_SIZE PAGE_SIZE /* use system PAGE_SIZE */
+
+#ifdef __FreeBSD__
+#if (__FreeBSD_version >= 700020)
+#define USB_GET_DMA_TAG(dev) bus_get_dma_tag(dev)
+#else
+#define USB_GET_DMA_TAG(dev) NULL /* XXX */
+#endif
+#endif
+
+/* structure prototypes */
+
+struct usb2_xfer_root;
+struct usb2_dma_parent_tag;
+
+/*
+ * The following typedef defines the USB DMA load done callback.
+ */
+
+typedef void (usb2_dma_callback_t)(struct usb2_dma_parent_tag *udpt);
+
+/*
+ * The following structure defines physical and non kernel virtual
+ * address of a memory page having size USB_PAGE_SIZE.
+ */
+struct usb2_page {
+ bus_size_t physaddr;
+ void *buffer; /* non Kernel Virtual Address */
+};
+
+/*
+ * The following structure is used when needing the kernel virtual
+ * pointer and the physical address belonging to an offset in an USB
+ * page cache.
+ */
+struct usb2_page_search {
+ void *buffer;
+ bus_size_t physaddr;
+ uint32_t length;
+};
+
+/*
+ * The following structure is used to keep information about a DMA
+ * memory allocation.
+ */
+struct usb2_page_cache {
+
+#ifdef __FreeBSD__
+ bus_dma_tag_t tag;
+ bus_dmamap_t map;
+#endif
+#ifdef __NetBSD__
+ bus_dma_tag_t tag;
+ bus_dmamap_t map;
+ bus_dma_segment_t *p_seg;
+#endif
+ struct usb2_page *page_start;
+ struct usb2_dma_parent_tag *tag_parent; /* always set */
+ void *buffer; /* virtual buffer pointer */
+#ifdef __NetBSD__
+ int n_seg;
+#endif
+ uint32_t page_offset_buf;
+ uint32_t page_offset_end;
+ uint8_t isread:1; /* set if we are currently reading
+ * from the memory. Else write. */
+ uint8_t ismultiseg:1; /* set if we can have multiple
+ * segments */
+};
+
+/*
+ * The following structure describes the parent USB DMA tag.
+ */
+struct usb2_dma_parent_tag {
+#ifdef __FreeBSD__
+ struct cv cv[1]; /* internal condition variable */
+#endif
+
+ bus_dma_tag_t tag; /* always set */
+
+ struct mtx *mtx; /* private mutex, always set */
+ struct usb2_xfer_root *info; /* used by the callback function */
+ usb2_dma_callback_t *func; /* load complete callback function */
+ struct usb2_dma_tag *utag_first;/* pointer to first USB DMA tag */
+
+ uint8_t dma_error; /* set if DMA load operation failed */
+ uint8_t dma_bits; /* number of DMA address lines */
+ uint8_t utag_max; /* number of USB DMA tags */
+};
+
+/*
+ * The following structure describes an USB DMA tag.
+ */
+struct usb2_dma_tag {
+#ifdef __NetBSD__
+ bus_dma_segment_t *p_seg;
+#endif
+ struct usb2_dma_parent_tag *tag_parent;
+ bus_dma_tag_t tag;
+
+ uint32_t align;
+ uint32_t size;
+#ifdef __NetBSD__
+ uint32_t n_seg;
+#endif
+};
+
+/* function prototypes */
+
+int usb2_uiomove(struct usb2_page_cache *pc, struct uio *uio, uint32_t pc_offset, uint32_t len);
+struct usb2_dma_tag *usb2_dma_tag_find(struct usb2_dma_parent_tag *udpt, uint32_t size, uint32_t align);
+uint8_t usb2_pc_alloc_mem(struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align);
+uint8_t usb2_pc_dmamap_create(struct usb2_page_cache *pc, uint32_t size);
+uint8_t usb2_pc_load_mem(struct usb2_page_cache *pc, uint32_t size, uint8_t sync);
+void usb2_bdma_done_event(struct usb2_dma_parent_tag *udpt);
+void usb2_bdma_post_sync(struct usb2_xfer *xfer);
+void usb2_bdma_pre_sync(struct usb2_xfer *xfer);
+void usb2_bdma_work_loop(struct usb2_xfer_queue *pq);
+void usb2_bzero(struct usb2_page_cache *cache, uint32_t offset, uint32_t len);
+void usb2_copy_in(struct usb2_page_cache *cache, uint32_t offset, const void *ptr, uint32_t len);
+int usb2_copy_in_user(struct usb2_page_cache *cache, uint32_t offset, const void *ptr, uint32_t len);
+void usb2_copy_out(struct usb2_page_cache *cache, uint32_t offset, void *ptr, uint32_t len);
+int usb2_copy_out_user(struct usb2_page_cache *cache, uint32_t offset, void *ptr, uint32_t len);
+void usb2_dma_tag_setup(struct usb2_dma_parent_tag *udpt, struct usb2_dma_tag *udt, bus_dma_tag_t dmat, struct mtx *mtx, usb2_dma_callback_t *func, struct usb2_xfer_root *info, uint8_t ndmabits, uint8_t nudt);
+void usb2_dma_tag_unsetup(struct usb2_dma_parent_tag *udpt);
+void usb2_get_page(struct usb2_page_cache *pc, uint32_t offset, struct usb2_page_search *res);
+void usb2_m_copy_in(struct usb2_page_cache *cache, uint32_t dst_offset, struct mbuf *m, uint32_t src_offset, uint32_t src_len);
+void usb2_pc_cpu_flush(struct usb2_page_cache *pc);
+void usb2_pc_cpu_invalidate(struct usb2_page_cache *pc);
+void usb2_pc_dmamap_destroy(struct usb2_page_cache *pc);
+void usb2_pc_free_mem(struct usb2_page_cache *pc);
+
+#endif /* _USB2_BUSDMA_H_ */
diff --git a/sys/dev/usb2/core/usb2_compat_linux.c b/sys/dev/usb2/core/usb2_compat_linux.c
new file mode 100644
index 000000000000..7b0212d11891
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_compat_linux.c
@@ -0,0 +1,1659 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved.
+ * Copyright (c) 2007 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_ioctl.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_compat_linux.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_parse.h>
+#include <dev/usb2/core/usb2_hub.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_debug.h>
+
+struct usb_linux_softc {
+ LIST_ENTRY(usb_linux_softc) sc_attached_list;
+
+ device_t sc_fbsd_dev;
+ struct usb2_device *sc_fbsd_udev;
+ struct usb_interface *sc_ui;
+ struct usb_driver *sc_udrv;
+};
+
+/* prototypes */
+static device_probe_t usb_linux_probe;
+static device_attach_t usb_linux_attach;
+static device_detach_t usb_linux_detach;
+static device_suspend_t usb_linux_suspend;
+static device_resume_t usb_linux_resume;
+static device_shutdown_t usb_linux_shutdown;
+
+static usb2_callback_t usb_linux_isoc_callback;
+static usb2_callback_t usb_linux_non_isoc_callback;
+
+static usb_complete_t usb_linux_wait_complete;
+
+static uint16_t usb_max_isoc_frames(struct usb_device *dev);
+static int usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint16_t *p_actlen);
+static const struct usb_device_id *usb_linux_lookup_id(const struct usb_device_id *id, struct usb2_attach_arg *uaa);
+static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *sc);
+static struct usb_device *usb_linux_create_usb_device(struct usb2_device *udev, device_t dev);
+static void usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface);
+static void usb_linux_complete(struct usb2_xfer *xfer);
+static int usb_unlink_urb_sub(struct urb *urb, uint8_t drain);
+
+/*------------------------------------------------------------------------*
+ * FreeBSD USB interface
+ *------------------------------------------------------------------------*/
+
+static LIST_HEAD(, usb_linux_softc) usb_linux_attached_list;
+static LIST_HEAD(, usb_driver) usb_linux_driver_list;
+
+static device_method_t usb_linux_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, usb_linux_probe),
+ DEVMETHOD(device_attach, usb_linux_attach),
+ DEVMETHOD(device_detach, usb_linux_detach),
+ DEVMETHOD(device_suspend, usb_linux_suspend),
+ DEVMETHOD(device_resume, usb_linux_resume),
+ DEVMETHOD(device_shutdown, usb_linux_shutdown),
+
+ {0, 0}
+};
+
+static driver_t usb_linux_driver = {
+ .name = "usb_linux",
+ .methods = usb_linux_methods,
+ .size = sizeof(struct usb_linux_softc),
+};
+
+static devclass_t usb_linux_devclass;
+
+DRIVER_MODULE(usb_linux, ushub, usb_linux_driver, usb_linux_devclass, NULL, 0);
+
+/*------------------------------------------------------------------------*
+ * usb_linux_lookup_id
+ *
+ * This functions takes an array of "struct usb_device_id" and tries
+ * to match the entries with the information in "struct usb2_attach_arg".
+ * If it finds a match the matching entry will be returned.
+ * Else "NULL" will be returned.
+ *------------------------------------------------------------------------*/
+static const struct usb_device_id *
+usb_linux_lookup_id(const struct usb_device_id *id, struct usb2_attach_arg *uaa)
+{
+ if (id == NULL) {
+ goto done;
+ }
+ /*
+ * Keep on matching array entries until we find one with
+ * "match_flags" equal to zero, which indicates the end of the
+ * array:
+ */
+ for (; id->match_flags; id++) {
+
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ (id->idVendor != uaa->info.idVendor)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
+ (id->idProduct != uaa->info.idProduct)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
+ (id->bcdDevice_lo > uaa->info.bcdDevice)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
+ (id->bcdDevice_hi < uaa->info.bcdDevice)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass != uaa->info.bDeviceClass)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
+ (id->bDeviceSubClass != uaa->info.bDeviceSubClass)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
+ (id->bDeviceProtocol != uaa->info.bDeviceProtocol)) {
+ continue;
+ }
+ if ((uaa->info.bDeviceClass == 0xFF) &&
+ !(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
+ (id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS |
+ USB_DEVICE_ID_MATCH_INT_PROTOCOL))) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+ (id->bInterfaceClass != uaa->info.bInterfaceClass)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
+ (id->bInterfaceSubClass != uaa->info.bInterfaceSubClass)) {
+ continue;
+ }
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
+ (id->bInterfaceProtocol != uaa->info.bInterfaceProtocol)) {
+ continue;
+ }
+ /* we found a match! */
+ return (id);
+ }
+
+done:
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_probe
+ *
+ * This function is the FreeBSD probe callback. It is called from the
+ * FreeBSD USB stack through the "device_probe_and_attach()" function.
+ *------------------------------------------------------------------------*/
+static int
+usb_linux_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb_driver *udrv;
+ int err = ENXIO;
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ mtx_lock(&Giant);
+ LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) {
+ if (usb_linux_lookup_id(udrv->id_table, uaa)) {
+ err = 0;
+ break;
+ }
+ }
+ mtx_unlock(&Giant);
+
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_get_usb_driver
+ *
+ * This function returns the pointer to the "struct usb_driver" where
+ * the Linux USB device driver "struct usb_device_id" match was found.
+ * We apply a lock before reading out the pointer to avoid races.
+ *------------------------------------------------------------------------*/
+static struct usb_driver *
+usb_linux_get_usb_driver(struct usb_linux_softc *sc)
+{
+ struct usb_driver *udrv;
+
+ mtx_lock(&Giant);
+ udrv = sc->sc_udrv;
+ mtx_unlock(&Giant);
+ return (udrv);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_attach
+ *
+ * This function is the FreeBSD attach callback. It is called from the
+ * FreeBSD USB stack through the "device_probe_and_attach()" function.
+ * This function is called when "usb_linux_probe()" returns zero.
+ *------------------------------------------------------------------------*/
+static int
+usb_linux_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb_linux_softc *sc = device_get_softc(dev);
+ struct usb_driver *udrv;
+ struct usb_device *p_dev;
+ const struct usb_device_id *id = NULL;
+
+ if (sc == NULL) {
+ return (ENOMEM);
+ }
+ mtx_lock(&Giant);
+ LIST_FOREACH(udrv, &usb_linux_driver_list, linux_driver_list) {
+ id = usb_linux_lookup_id(udrv->id_table, uaa);
+ if (id)
+ break;
+ }
+ mtx_unlock(&Giant);
+
+ if (id == NULL) {
+ return (ENXIO);
+ }
+ /*
+ * Save some memory and only create the Linux compat structure when
+ * needed:
+ */
+ p_dev = uaa->device->linux_dev;
+ if (p_dev == NULL) {
+ p_dev = usb_linux_create_usb_device(uaa->device, dev);
+ if (p_dev == NULL) {
+ return (ENOMEM);
+ }
+ uaa->device->linux_dev = p_dev;
+ }
+ device_set_usb2_desc(dev);
+
+ sc->sc_fbsd_udev = uaa->device;
+ sc->sc_fbsd_dev = dev;
+ sc->sc_udrv = udrv;
+ sc->sc_ui = usb_ifnum_to_if(p_dev, uaa->info.bIfaceNum);
+ if (sc->sc_ui == NULL) {
+ return (EINVAL);
+ }
+ if (udrv->probe) {
+ if ((udrv->probe) (sc->sc_ui, id)) {
+ return (ENXIO);
+ }
+ }
+ mtx_lock(&Giant);
+ LIST_INSERT_HEAD(&usb_linux_attached_list, sc, sc_attached_list);
+ mtx_unlock(&Giant);
+
+ /* success */
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_detach
+ *
+ * This function is the FreeBSD detach callback. It is called from the
+ * FreeBSD USB stack through the "device_detach()" function.
+ *------------------------------------------------------------------------*/
+static int
+usb_linux_detach(device_t dev)
+{
+ struct usb_linux_softc *sc = device_get_softc(dev);
+ struct usb_driver *udrv = NULL;
+
+ mtx_lock(&Giant);
+ if (sc->sc_attached_list.le_prev) {
+ LIST_REMOVE(sc, sc_attached_list);
+ sc->sc_attached_list.le_prev = NULL;
+ udrv = sc->sc_udrv;
+ sc->sc_udrv = NULL;
+ }
+ mtx_unlock(&Giant);
+
+ if (udrv && udrv->disconnect) {
+ (udrv->disconnect) (sc->sc_ui);
+ }
+ /*
+ * Make sure that we free all FreeBSD USB transfers belonging to
+ * this Linux "usb_interface", hence they will most likely not be
+ * needed any more.
+ */
+ usb_linux_cleanup_interface(sc->sc_fbsd_udev->linux_dev, sc->sc_ui);
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_suspend
+ *
+ * This function is the FreeBSD suspend callback. Usually it does nothing.
+ *------------------------------------------------------------------------*/
+static int
+usb_linux_suspend(device_t dev)
+{
+ struct usb_linux_softc *sc = device_get_softc(dev);
+ struct usb_driver *udrv = usb_linux_get_usb_driver(sc);
+ int err;
+
+ if (udrv && udrv->suspend) {
+ err = (udrv->suspend) (sc->sc_ui, 0);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_resume
+ *
+ * This function is the FreeBSD resume callback. Usually it does nothing.
+ *------------------------------------------------------------------------*/
+static int
+usb_linux_resume(device_t dev)
+{
+ struct usb_linux_softc *sc = device_get_softc(dev);
+ struct usb_driver *udrv = usb_linux_get_usb_driver(sc);
+ int err;
+
+ if (udrv && udrv->resume) {
+ err = (udrv->resume) (sc->sc_ui);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_shutdown
+ *
+ * This function is the FreeBSD shutdown callback. Usually it does nothing.
+ *------------------------------------------------------------------------*/
+static int
+usb_linux_shutdown(device_t dev)
+{
+ struct usb_linux_softc *sc = device_get_softc(dev);
+ struct usb_driver *udrv = usb_linux_get_usb_driver(sc);
+
+ if (udrv && udrv->shutdown) {
+ (udrv->shutdown) (sc->sc_ui);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * Linux emulation layer
+ *------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------*
+ * usb_max_isoc_frames
+ *
+ * The following function returns the maximum number of isochronous
+ * frames that we support per URB. It is not part of the Linux USB API.
+ *------------------------------------------------------------------------*/
+static uint16_t
+usb_max_isoc_frames(struct usb_device *dev)
+{
+ return ((usb2_get_speed(dev->bsd_udev) == USB_SPEED_HIGH) ?
+ USB_MAX_HIGH_SPEED_ISOC_FRAMES : USB_MAX_FULL_SPEED_ISOC_FRAMES);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_submit_urb
+ *
+ * This function is used to queue an URB after that it has been
+ * initialized. If it returns non-zero, it means that the URB was not
+ * queued.
+ *------------------------------------------------------------------------*/
+int
+usb_submit_urb(struct urb *urb, uint16_t mem_flags)
+{
+ struct usb_host_endpoint *uhe;
+
+ if (urb == NULL) {
+ return (-EINVAL);
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (urb->pipe == NULL) {
+ return (-EINVAL);
+ }
+ uhe = urb->pipe;
+
+ /*
+ * Check that we have got a FreeBSD USB transfer that will dequeue
+ * the URB structure and do the real transfer. If there are no USB
+ * transfers, then we return an error.
+ */
+ if (uhe->bsd_xfer[0] ||
+ uhe->bsd_xfer[1]) {
+ /* we are ready! */
+
+ TAILQ_INSERT_HEAD(&uhe->bsd_urb_list, urb, bsd_urb_list);
+
+ urb->status = -EINPROGRESS;
+
+ usb2_transfer_start(uhe->bsd_xfer[0]);
+ usb2_transfer_start(uhe->bsd_xfer[1]);
+ } else {
+ /* no pipes have been setup yet! */
+ urb->status = -EINVAL;
+ return (-EINVAL);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_unlink_urb
+ *
+ * This function is used to stop an URB after that it is been
+ * submitted, but before the "complete" callback has been called. On
+ *------------------------------------------------------------------------*/
+int
+usb_unlink_urb(struct urb *urb)
+{
+ return (usb_unlink_urb_sub(urb, 0));
+}
+
+static void
+usb_unlink_bsd(struct usb2_xfer *xfer,
+ struct urb *urb, uint8_t drain)
+{
+ if (xfer &&
+ usb2_transfer_pending(xfer) &&
+ (xfer->priv_fifo == (void *)urb)) {
+ if (drain) {
+ mtx_unlock(&Giant);
+ usb2_transfer_drain(xfer);
+ mtx_lock(&Giant);
+ } else {
+ usb2_transfer_stop(xfer);
+ }
+ usb2_transfer_start(xfer);
+ }
+ return;
+}
+
+static int
+usb_unlink_urb_sub(struct urb *urb, uint8_t drain)
+{
+ struct usb_host_endpoint *uhe;
+ uint16_t x;
+
+ if (urb == NULL) {
+ return (-EINVAL);
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (urb->pipe == NULL) {
+ return (-EINVAL);
+ }
+ uhe = urb->pipe;
+
+ if (urb->bsd_urb_list.tqe_prev) {
+
+ /* not started yet, just remove it from the queue */
+ TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list);
+ urb->bsd_urb_list.tqe_prev = NULL;
+ urb->status = -ECONNRESET;
+ urb->actual_length = 0;
+
+ for (x = 0; x < urb->number_of_packets; x++) {
+ urb->iso_frame_desc[x].actual_length = 0;
+ }
+
+ if (urb->complete) {
+ (urb->complete) (urb);
+ }
+ } else {
+
+ /*
+ * If the URB is not on the URB list, then check if one of
+ * the FreeBSD USB transfer are processing the current URB.
+ * If so, re-start that transfer, which will lead to the
+ * termination of that URB:
+ */
+ usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain);
+ usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_clear_halt
+ *
+ * This function must always be used to clear the stall. Stall is when
+ * an USB endpoint returns a stall message to the USB host controller.
+ * Until the stall is cleared, no data can be transferred.
+ *------------------------------------------------------------------------*/
+int
+usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe)
+{
+ struct usb2_config cfg[1];
+ struct usb2_pipe *pipe;
+ uint8_t type;
+ uint8_t addr;
+
+ if (uhe == NULL)
+ return (-EINVAL);
+
+ type = uhe->desc.bmAttributes & UE_XFERTYPE;
+ addr = uhe->desc.bEndpointAddress;
+
+ bzero(cfg, sizeof(cfg));
+
+ cfg[0].type = type;
+ cfg[0].endpoint = addr & UE_ADDR;
+ cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN);
+
+ pipe = usb2_get_pipe(dev->bsd_udev, uhe->bsd_iface_index, cfg);
+ if (pipe == NULL)
+ return (-EINVAL);
+
+ usb2_clear_data_toggle(dev->bsd_udev, pipe);
+
+ return (usb_control_msg(dev, &dev->ep0,
+ UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT,
+ UF_ENDPOINT_HALT, addr, NULL, 0, 1000));
+}
+
+/*------------------------------------------------------------------------*
+ * usb_start_wait_urb
+ *
+ * This is an internal function that is used to perform synchronous
+ * Linux USB transfers.
+ *------------------------------------------------------------------------*/
+static int
+usb_start_wait_urb(struct urb *urb, uint32_t timeout, uint16_t *p_actlen)
+{
+ int err;
+
+ /* you must have a timeout! */
+ if (timeout == 0) {
+ timeout = 1;
+ }
+ urb->complete = &usb_linux_wait_complete;
+ urb->timeout = timeout;
+ urb->transfer_flags |= URB_WAIT_WAKEUP;
+ urb->transfer_flags &= ~URB_IS_SLEEPING;
+
+ err = usb_submit_urb(urb, 0);
+ if (err)
+ goto done;
+
+ /*
+ * the URB might have completed before we get here, so check that by
+ * using some flags!
+ */
+ while (urb->transfer_flags & URB_WAIT_WAKEUP) {
+ urb->transfer_flags |= URB_IS_SLEEPING;
+ usb2_cv_wait(&urb->cv_wait, &Giant);
+ urb->transfer_flags &= ~URB_IS_SLEEPING;
+ }
+
+ err = urb->status;
+
+done:
+ if (err) {
+ *p_actlen = 0;
+ } else {
+ *p_actlen = urb->actual_length;
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_control_msg
+ *
+ * The following function performs a control transfer sequence one any
+ * control, bulk or interrupt endpoint, specified by "uhe". A control
+ * transfer means that you transfer an 8-byte header first followed by
+ * a data-phase as indicated by the 8-byte header. The "timeout" is
+ * given in milliseconds.
+ *
+ * Return values:
+ * 0: Success
+ * < 0: Failure
+ * > 0: Acutal length
+ *------------------------------------------------------------------------*/
+int
+usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *uhe,
+ uint8_t request, uint8_t requesttype,
+ uint16_t value, uint16_t index, void *data,
+ uint16_t size, uint32_t timeout)
+{
+ struct usb2_device_request req;
+ struct urb *urb;
+ int err;
+ uint16_t actlen;
+ uint8_t type;
+ uint8_t addr;
+
+ req.bmRequestType = requesttype;
+ req.bRequest = request;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, size);
+
+ if (uhe == NULL) {
+ return (-EINVAL);
+ }
+ type = (uhe->desc.bmAttributes & UE_XFERTYPE);
+ addr = (uhe->desc.bEndpointAddress & UE_ADDR);
+
+ if (type != UE_CONTROL) {
+ return (-EINVAL);
+ }
+ if (addr == 0) {
+ /*
+ * The FreeBSD USB stack supports standard control
+ * transfers on control endpoint zero:
+ */
+ err = usb2_do_request_flags(dev->bsd_udev,
+ &Giant, &req, data, USB_SHORT_XFER_OK,
+ &actlen, timeout);
+ if (err) {
+ err = -EPIPE;
+ } else {
+ err = actlen;
+ }
+ return (err);
+ }
+ if (dev->bsd_udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return (-EINVAL);
+ }
+ err = usb_setup_endpoint(dev, uhe, 1 /* dummy */ );
+
+ /*
+ * NOTE: we need to allocate real memory here so that we don't
+ * transfer data to/from the stack!
+ *
+ * 0xFFFF is a FreeBSD specific magic value.
+ */
+ urb = usb_alloc_urb(0xFFFF, size);
+ if (urb == NULL)
+ return (-ENOMEM);
+
+ urb->dev = dev;
+ urb->pipe = uhe;
+
+ bcopy(&req, urb->setup_packet, sizeof(req));
+
+ if (size && (!(req.bmRequestType & UT_READ))) {
+ /* move the data to a real buffer */
+ bcopy(data, USB_ADD_BYTES(urb->setup_packet,
+ sizeof(req)), size);
+ }
+ err = usb_start_wait_urb(urb, timeout, &actlen);
+
+ if (req.bmRequestType & UT_READ) {
+ if (actlen) {
+ bcopy(USB_ADD_BYTES(urb->setup_packet,
+ sizeof(req)), data, actlen);
+ }
+ }
+ usb_free_urb(urb);
+
+ if (err == 0) {
+ err = actlen;
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_set_interface
+ *
+ * The following function will select which alternate setting of an
+ * USB interface you plan to use. By default alternate setting with
+ * index zero is selected. Note that "iface_no" is not the interface
+ * index, but rather the value of "bInterfaceNumber".
+ *------------------------------------------------------------------------*/
+int
+usb_set_interface(struct usb_device *dev, uint8_t iface_no, uint8_t alt_index)
+{
+ struct usb_interface *p_ui = usb_ifnum_to_if(dev, iface_no);
+ int err;
+
+ if (p_ui == NULL)
+ return (-EINVAL);
+ if (alt_index >= p_ui->num_altsetting)
+ return (-EINVAL);
+ usb_linux_cleanup_interface(dev, p_ui);
+ err = -usb2_set_alt_interface_index(dev->bsd_udev,
+ p_ui->bsd_iface_index, alt_index);
+ if (err == 0) {
+ p_ui->cur_altsetting = p_ui->altsetting + alt_index;
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_setup_endpoint
+ *
+ * The following function is an extension to the Linux USB API that
+ * allows you to set a maximum buffer size for a given USB endpoint.
+ * The maximum buffer size is per URB. If you don't call this function
+ * to set a maximum buffer size, the endpoint will not be functional.
+ * Note that for isochronous endpoints the maximum buffer size must be
+ * a non-zero dummy, hence this function will base the maximum buffer
+ * size on "wMaxPacketSize".
+ *------------------------------------------------------------------------*/
+int
+usb_setup_endpoint(struct usb_device *dev,
+ struct usb_host_endpoint *uhe, uint32_t bufsize)
+{
+ struct usb2_config cfg[2];
+ uint8_t type = uhe->desc.bmAttributes & UE_XFERTYPE;
+ uint8_t addr = uhe->desc.bEndpointAddress;
+
+ if (uhe->fbsd_buf_size == bufsize) {
+ /* optimize */
+ return (0);
+ }
+ usb2_transfer_unsetup(uhe->bsd_xfer, 2);
+
+ uhe->fbsd_buf_size = bufsize;
+
+ if (bufsize == 0) {
+ return (0);
+ }
+ bzero(cfg, sizeof(cfg));
+
+ if (type == UE_ISOCHRONOUS) {
+
+ /*
+ * Isochronous transfers are special in that they don't fit
+ * into the BULK/INTR/CONTROL transfer model.
+ */
+
+ cfg[0].type = type;
+ cfg[0].endpoint = addr & UE_ADDR;
+ cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN);
+ cfg[0].mh.callback = &usb_linux_isoc_callback;
+ cfg[0].mh.bufsize = 0; /* use wMaxPacketSize */
+ cfg[0].mh.frames = usb_max_isoc_frames(dev);
+ cfg[0].mh.flags.proxy_buffer = 1;
+#if 0
+ /*
+ * The Linux USB API allows non back-to-back
+ * isochronous frames which we do not support. If the
+ * isochronous frames are not back-to-back we need to
+ * do a copy, and then we need a buffer for
+ * that. Enable this at your own risk.
+ */
+ cfg[0].mh.flags.ext_buffer = 1;
+#endif
+ cfg[0].mh.flags.short_xfer_ok = 1;
+
+ bcopy(cfg, cfg + 1, sizeof(*cfg));
+
+ /* Allocate and setup two generic FreeBSD USB transfers */
+
+ if (usb2_transfer_setup(dev->bsd_udev, &uhe->bsd_iface_index,
+ uhe->bsd_xfer, cfg, 2, uhe, &Giant)) {
+ return (-EINVAL);
+ }
+ } else {
+ if (bufsize > (1 << 22)) {
+ /* limit buffer size */
+ bufsize = (1 << 22);
+ }
+ /* Allocate and setup one generic FreeBSD USB transfer */
+
+ cfg[0].type = type;
+ cfg[0].endpoint = addr & UE_ADDR;
+ cfg[0].direction = addr & (UE_DIR_OUT | UE_DIR_IN);
+ cfg[0].mh.callback = &usb_linux_non_isoc_callback;
+ cfg[0].mh.bufsize = bufsize;
+ cfg[0].mh.flags.ext_buffer = 1; /* enable zero-copy */
+ cfg[0].mh.flags.proxy_buffer = 1;
+ cfg[0].mh.flags.short_xfer_ok = 1;
+
+ if (usb2_transfer_setup(dev->bsd_udev, &uhe->bsd_iface_index,
+ uhe->bsd_xfer, cfg, 1, uhe, &Giant)) {
+ return (-EINVAL);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_create_usb_device
+ *
+ * The following function is used to build up a per USB device
+ * structure tree, that mimics the Linux one. The root structure
+ * is returned by this function.
+ *------------------------------------------------------------------------*/
+static struct usb_device *
+usb_linux_create_usb_device(struct usb2_device *udev, device_t dev)
+{
+ struct usb2_config_descriptor *cd = usb2_get_config_descriptor(udev);
+ struct usb2_descriptor *desc;
+ struct usb2_interface_descriptor *id;
+ struct usb2_endpoint_descriptor *ed;
+ struct usb_device *p_ud = NULL;
+ struct usb_interface *p_ui = NULL;
+ struct usb_host_interface *p_uhi = NULL;
+ struct usb_host_endpoint *p_uhe = NULL;
+ uint32_t size;
+ uint16_t niface_total;
+ uint16_t nedesc;
+ uint16_t iface_no_curr;
+ uint16_t iface_index;
+ uint8_t pass;
+ uint8_t iface_no;
+
+ /*
+ * We do two passes. One pass for computing necessary memory size
+ * and one pass to initialize all the allocated memory structures.
+ */
+ for (pass = 0; pass < 2; pass++) {
+
+ iface_no_curr = 0 - 1;
+ niface_total = 0;
+ iface_index = 0;
+ nedesc = 0;
+ desc = NULL;
+
+ /*
+ * Iterate over all the USB descriptors. Use the USB config
+ * descriptor pointer provided by the FreeBSD USB stack.
+ */
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ /*
+ * Build up a tree according to the descriptors we
+ * find:
+ */
+ switch (desc->bDescriptorType) {
+ case UDESC_DEVICE:
+ break;
+
+ case UDESC_ENDPOINT:
+ ed = (void *)desc;
+ if ((ed->bLength < sizeof(*ed)) ||
+ (iface_index == 0))
+ break;
+ if (p_uhe) {
+ bcopy(ed, &p_uhe->desc, sizeof(p_uhe->desc));
+ p_uhe->bsd_iface_index = iface_index - 1;
+ p_uhe++;
+ }
+ if (p_uhi) {
+ (p_uhi - 1)->desc.bNumEndpoints++;
+ }
+ nedesc++;
+ break;
+
+ case UDESC_INTERFACE:
+ id = (void *)desc;
+ if (id->bLength < sizeof(*id))
+ break;
+ if (p_uhi) {
+ bcopy(id, &p_uhi->desc, sizeof(p_uhi->desc));
+ p_uhi->desc.bNumEndpoints = 0;
+ p_uhi->endpoint = p_uhe;
+ p_uhi->string = "";
+ p_uhi->bsd_iface_index = iface_index;
+ p_uhi++;
+ }
+ iface_no = id->bInterfaceNumber;
+ niface_total++;
+ if (iface_no_curr != iface_no) {
+ if (p_ui) {
+ p_ui->altsetting = p_uhi - 1;
+ p_ui->cur_altsetting = p_uhi - 1;
+ p_ui->num_altsetting = 1;
+ p_ui->bsd_iface_index = iface_index;
+ p_ui->linux_udev = p_ud;
+ p_ui++;
+ }
+ iface_no_curr = iface_no;
+ iface_index++;
+ } else {
+ if (p_ui) {
+ (p_ui - 1)->num_altsetting++;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (pass == 0) {
+
+ size = ((sizeof(*p_ud) * 1) +
+ (sizeof(*p_uhe) * nedesc) +
+ (sizeof(*p_ui) * iface_index) +
+ (sizeof(*p_uhi) * niface_total));
+
+ p_ud = malloc(size, M_USBDEV, M_WAITOK | M_ZERO);
+ if (p_ud == NULL) {
+ goto done;
+ }
+ p_uhe = (void *)(p_ud + 1);
+ p_ui = (void *)(p_uhe + nedesc);
+ p_uhi = (void *)(p_ui + iface_index);
+
+ p_ud->product = "";
+ p_ud->manufacturer = "";
+ p_ud->serial = "";
+ p_ud->speed = usb2_get_speed(udev);
+ p_ud->bsd_udev = udev;
+ p_ud->bsd_iface_start = p_ui;
+ p_ud->bsd_iface_end = p_ui + iface_index;
+ p_ud->bsd_endpoint_start = p_uhe;
+ p_ud->bsd_endpoint_end = p_uhe + nedesc;
+ p_ud->devnum = device_get_unit(dev);
+ bcopy(&udev->ddesc, &p_ud->descriptor,
+ sizeof(p_ud->descriptor));
+ bcopy(udev->default_pipe.edesc, &p_ud->ep0.desc,
+ sizeof(p_ud->ep0.desc));
+ }
+ }
+done:
+ return (p_ud);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_alloc_urb
+ *
+ * This function should always be used when you allocate an URB for
+ * use with the USB Linux stack. In case of an isochronous transfer
+ * you must specifiy the maximum number of "iso_packets" which you
+ * plan to transfer per URB. This function is always blocking, and
+ * "mem_flags" are not regarded like on Linux.
+ *------------------------------------------------------------------------*/
+struct urb *
+usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags)
+{
+ struct urb *urb;
+ uint32_t size;
+
+ if (iso_packets == 0xFFFF) {
+ /*
+ * FreeBSD specific magic value to ask for control transfer
+ * memory allocation:
+ */
+ size = sizeof(*urb) + sizeof(struct usb2_device_request) + mem_flags;
+ } else {
+ size = sizeof(*urb) + (iso_packets * sizeof(urb->iso_frame_desc[0]));
+ }
+
+ urb = malloc(size, M_USBDEV, M_WAITOK | M_ZERO);
+ if (urb) {
+
+ usb2_cv_init(&urb->cv_wait, "URBWAIT");
+ if (iso_packets == 0xFFFF) {
+ urb->setup_packet = (void *)(urb + 1);
+ urb->transfer_buffer = (void *)(urb->setup_packet +
+ sizeof(struct usb2_device_request));
+ } else {
+ urb->number_of_packets = iso_packets;
+ }
+ }
+ return (urb);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_find_host_endpoint
+ *
+ * The following function will return the Linux USB host endpoint
+ * structure that matches the given endpoint type and endpoint
+ * value. If no match is found, NULL is returned. This function is not
+ * part of the Linux USB API and is only used internally.
+ *------------------------------------------------------------------------*/
+struct usb_host_endpoint *
+usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep)
+{
+ struct usb_host_endpoint *uhe;
+ struct usb_host_endpoint *uhe_end;
+ struct usb_host_interface *uhi;
+ struct usb_interface *ui;
+ uint8_t ea;
+ uint8_t at;
+ uint8_t mask;
+
+ if (dev == NULL) {
+ return (NULL);
+ }
+ if (type == UE_CONTROL) {
+ mask = UE_ADDR;
+ } else {
+ mask = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR);
+ }
+
+ ep &= mask;
+
+ /*
+ * Iterate over all the interfaces searching the selected alternate
+ * setting only, and all belonging endpoints.
+ */
+ for (ui = dev->bsd_iface_start;
+ ui != dev->bsd_iface_end;
+ ui++) {
+ uhi = ui->cur_altsetting;
+ if (uhi) {
+ uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints;
+ for (uhe = uhi->endpoint;
+ uhe != uhe_end;
+ uhe++) {
+ ea = uhe->desc.bEndpointAddress;
+ at = uhe->desc.bmAttributes;
+
+ if (((ea & mask) == ep) &&
+ ((at & UE_XFERTYPE) == type)) {
+ return (uhe);
+ }
+ }
+ }
+ }
+
+ if ((type == UE_CONTROL) && ((ep & UE_ADDR) == 0)) {
+ return (&dev->ep0);
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_altnum_to_altsetting
+ *
+ * The following function returns a pointer to an alternate setting by
+ * index given a "usb_interface" pointer. If the alternate setting by
+ * index does not exist, NULL is returned. And alternate setting is a
+ * variant of an interface, but usually with slightly different
+ * characteristics.
+ *------------------------------------------------------------------------*/
+struct usb_host_interface *
+usb_altnum_to_altsetting(const struct usb_interface *intf, uint8_t alt_index)
+{
+ if (alt_index >= intf->num_altsetting) {
+ return (NULL);
+ }
+ return (intf->altsetting + alt_index);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_ifnum_to_if
+ *
+ * The following function searches up an USB interface by
+ * "bInterfaceNumber". If no match is found, NULL is returned.
+ *------------------------------------------------------------------------*/
+struct usb_interface *
+usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no)
+{
+ struct usb_interface *p_ui;
+
+ for (p_ui = dev->bsd_iface_start;
+ p_ui != dev->bsd_iface_end;
+ p_ui++) {
+ if ((p_ui->num_altsetting > 0) &&
+ (p_ui->altsetting->desc.bInterfaceNumber == iface_no)) {
+ return (p_ui);
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_buffer_alloc
+ *------------------------------------------------------------------------*/
+void *
+usb_buffer_alloc(struct usb_device *dev, uint32_t size, uint16_t mem_flags, uint8_t *dma_addr)
+{
+ return (malloc(size, M_USBDEV, M_WAITOK | M_ZERO));
+}
+
+/*------------------------------------------------------------------------*
+ * usb_get_intfdata
+ *------------------------------------------------------------------------*/
+void *
+usb_get_intfdata(struct usb_interface *intf)
+{
+ return (intf->bsd_priv_sc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_register
+ *
+ * The following function is used by the "USB_DRIVER_EXPORT()" macro,
+ * and is used to register a Linux USB driver, so that its
+ * "usb_device_id" structures gets searched a probe time. This
+ * function is not part of the Linux USB API, and is for internal use
+ * only.
+ *------------------------------------------------------------------------*/
+void
+usb_linux_register(void *arg)
+{
+ struct usb_driver *drv = arg;
+
+ mtx_lock(&Giant);
+ LIST_INSERT_HEAD(&usb_linux_driver_list, drv, linux_driver_list);
+ mtx_unlock(&Giant);
+
+ usb2_needs_explore_all();
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_deregister
+ *
+ * The following function is used by the "USB_DRIVER_EXPORT()" macro,
+ * and is used to deregister a Linux USB driver. This function will
+ * ensure that all driver instances belonging to the Linux USB device
+ * driver in question, gets detached before the driver is
+ * unloaded. This function is not part of the Linux USB API, and is
+ * for internal use only.
+ *------------------------------------------------------------------------*/
+void
+usb_linux_deregister(void *arg)
+{
+ struct usb_driver *drv = arg;
+ struct usb_linux_softc *sc;
+
+repeat:
+ mtx_lock(&Giant);
+ LIST_FOREACH(sc, &usb_linux_attached_list, sc_attached_list) {
+ if (sc->sc_udrv == drv) {
+ mtx_unlock(&Giant);
+ device_detach(sc->sc_fbsd_dev);
+ goto repeat;
+ }
+ }
+ LIST_REMOVE(drv, linux_driver_list);
+ mtx_unlock(&Giant);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_free_device
+ *
+ * The following function is only used by the FreeBSD USB stack, to
+ * cleanup and free memory after that a Linux USB device was attached.
+ *------------------------------------------------------------------------*/
+void
+usb_linux_free_device(struct usb_device *dev)
+{
+ struct usb_host_endpoint *uhe;
+ struct usb_host_endpoint *uhe_end;
+ int err;
+
+ uhe = dev->bsd_endpoint_start;
+ uhe_end = dev->bsd_endpoint_end;
+ while (uhe != uhe_end) {
+ err = usb_setup_endpoint(dev, uhe, 0);
+ uhe++;
+ }
+ err = usb_setup_endpoint(dev, &dev->ep0, 0);
+ free(dev, M_USBDEV);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_buffer_free
+ *------------------------------------------------------------------------*/
+void
+usb_buffer_free(struct usb_device *dev, uint32_t size,
+ void *addr, uint8_t dma_addr)
+{
+ free(addr, M_USBDEV);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_free_urb
+ *------------------------------------------------------------------------*/
+void
+usb_free_urb(struct urb *urb)
+{
+ if (urb == NULL) {
+ return;
+ }
+ /* make sure that the current URB is not active */
+ usb_kill_urb(urb);
+
+ /* destroy condition variable */
+ usb2_cv_destroy(&urb->cv_wait);
+
+ /* just free it */
+ free(urb, M_USBDEV);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_init_urb
+ *
+ * The following function can be used to initialize a custom URB. It
+ * is not recommended to use this function. Use "usb_alloc_urb()"
+ * instead.
+ *------------------------------------------------------------------------*/
+void
+usb_init_urb(struct urb *urb)
+{
+ if (urb == NULL) {
+ return;
+ }
+ bzero(urb, sizeof(*urb));
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_kill_urb
+ *------------------------------------------------------------------------*/
+void
+usb_kill_urb(struct urb *urb)
+{
+ if (usb_unlink_urb_sub(urb, 1)) {
+ /* ignore */
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_set_intfdata
+ *
+ * The following function sets the per Linux USB interface private
+ * data pointer. It is used by most Linux USB device drivers.
+ *------------------------------------------------------------------------*/
+void
+usb_set_intfdata(struct usb_interface *intf, void *data)
+{
+ intf->bsd_priv_sc = data;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_cleanup_interface
+ *
+ * The following function will release all FreeBSD USB transfers
+ * associated with a Linux USB interface. It is for internal use only.
+ *------------------------------------------------------------------------*/
+static void
+usb_linux_cleanup_interface(struct usb_device *dev, struct usb_interface *iface)
+{
+ struct usb_host_interface *uhi;
+ struct usb_host_interface *uhi_end;
+ struct usb_host_endpoint *uhe;
+ struct usb_host_endpoint *uhe_end;
+ int err;
+
+ uhi = iface->altsetting;
+ uhi_end = iface->altsetting + iface->num_altsetting;
+ while (uhi != uhi_end) {
+ uhe = uhi->endpoint;
+ uhe_end = uhi->endpoint + uhi->desc.bNumEndpoints;
+ while (uhe != uhe_end) {
+ err = usb_setup_endpoint(dev, uhe, 0);
+ uhe++;
+ }
+ uhi++;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_wait_complete
+ *
+ * The following function is used by "usb_start_wait_urb()" to wake it
+ * up, when an USB transfer has finished.
+ *------------------------------------------------------------------------*/
+static void
+usb_linux_wait_complete(struct urb *urb)
+{
+ if (urb->transfer_flags & URB_IS_SLEEPING) {
+ usb2_cv_signal(&urb->cv_wait);
+ }
+ urb->transfer_flags &= ~URB_WAIT_WAKEUP;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_complete
+ *------------------------------------------------------------------------*/
+static void
+usb_linux_complete(struct usb2_xfer *xfer)
+{
+ struct urb *urb;
+
+ urb = xfer->priv_fifo;
+ xfer->priv_fifo = NULL;
+ if (urb->complete) {
+ (urb->complete) (urb);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_isoc_callback
+ *
+ * The following is the FreeBSD isochronous USB callback. Isochronous
+ * frames are USB packets transferred 1000 or 8000 times per second,
+ * depending on whether a full- or high- speed USB transfer is
+ * used.
+ *------------------------------------------------------------------------*/
+static void
+usb_linux_isoc_callback(struct usb2_xfer *xfer)
+{
+ uint32_t max_frame = xfer->max_frame_size;
+ uint32_t offset;
+ uint16_t x;
+ struct urb *urb = xfer->priv_fifo;
+ struct usb_host_endpoint *uhe = xfer->priv_sc;
+ struct usb_iso_packet_descriptor *uipd;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (urb->bsd_isread) {
+
+ /* copy in data with regard to the URB */
+
+ offset = 0;
+
+ for (x = 0; x < urb->number_of_packets; x++) {
+ uipd = urb->iso_frame_desc + x;
+ uipd->actual_length = xfer->frlengths[x];
+ uipd->status = 0;
+ if (!xfer->flags.ext_buffer) {
+ usb2_copy_out(xfer->frbuffers, offset,
+ USB_ADD_BYTES(urb->transfer_buffer,
+ uipd->offset), uipd->actual_length);
+ }
+ offset += max_frame;
+ }
+ } else {
+ for (x = 0; x < urb->number_of_packets; x++) {
+ uipd = urb->iso_frame_desc + x;
+ uipd->actual_length = xfer->frlengths[x];
+ uipd->status = 0;
+ }
+ }
+
+ urb->actual_length = xfer->actlen;
+
+ /* check for short transfer */
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ urb->status = -EPIPE; /* XXX should be
+ * EREMOTEIO */
+ } else {
+ urb->status = 0;
+ }
+ } else {
+ /* success */
+ urb->status = 0;
+ }
+
+ /* call callback */
+ usb_linux_complete(xfer);
+
+ case USB_ST_SETUP:
+tr_setup:
+
+ if (xfer->priv_fifo == NULL) {
+
+ /* get next transfer */
+ urb = TAILQ_FIRST(&uhe->bsd_urb_list);
+ if (urb == NULL) {
+ /* nothing to do */
+ return;
+ }
+ TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list);
+ urb->bsd_urb_list.tqe_prev = NULL;
+
+ x = xfer->max_frame_count;
+ if (urb->number_of_packets > x) {
+ /* XXX simply truncate the transfer */
+ urb->number_of_packets = x;
+ }
+ } else {
+ DPRINTF("Already got a transfer\n");
+
+ /* already got a transfer (should not happen) */
+ urb = xfer->priv_fifo;
+ }
+
+ urb->bsd_isread = (uhe->desc.bEndpointAddress & UE_DIR_IN) ? 1 : 0;
+
+ if (!(urb->bsd_isread)) {
+
+ /* copy out data with regard to the URB */
+
+ offset = 0;
+
+ for (x = 0; x < urb->number_of_packets; x++) {
+ uipd = urb->iso_frame_desc + x;
+ xfer->frlengths[x] = uipd->length;
+ if (!xfer->flags.ext_buffer) {
+ usb2_copy_in(xfer->frbuffers, offset,
+ USB_ADD_BYTES(urb->transfer_buffer,
+ uipd->offset), uipd->length);
+ }
+ offset += uipd->length;
+ }
+ } else {
+
+ /*
+ * compute the transfer length into the "offset"
+ * variable
+ */
+
+ offset = urb->number_of_packets * max_frame;
+
+ /* setup "frlengths" array */
+
+ for (x = 0; x < urb->number_of_packets; x++) {
+ uipd = urb->iso_frame_desc + x;
+ xfer->frlengths[x] = max_frame;
+ }
+ }
+
+ if (xfer->flags.ext_buffer) {
+ /* set virtual address to load */
+ usb2_set_frame_data(xfer,
+ urb->transfer_buffer, 0);
+ }
+ xfer->priv_fifo = urb;
+ xfer->flags.force_short_xfer = 0;
+ xfer->timeout = urb->timeout;
+ xfer->nframes = urb->number_of_packets;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ urb->status = -ECONNRESET;
+ } else {
+ urb->status = -EPIPE; /* stalled */
+ }
+
+ /* Set zero for "actual_length" */
+ urb->actual_length = 0;
+
+ /* Set zero for "actual_length" */
+ for (x = 0; x < urb->number_of_packets; x++) {
+ urb->iso_frame_desc[x].actual_length = 0;
+ }
+
+ /* call callback */
+ usb_linux_complete(xfer);
+
+ if (xfer->error == USB_ERR_CANCELLED) {
+ /* we need to return in this case */
+ return;
+ }
+ goto tr_setup;
+
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb_linux_non_isoc_callback
+ *
+ * The following is the FreeBSD BULK/INTERRUPT and CONTROL USB
+ * callback. It dequeues Linux USB stack compatible URB's, transforms
+ * the URB fields into a FreeBSD USB transfer, and defragments the USB
+ * transfer as required. When the transfer is complete the "complete"
+ * callback is called.
+ *------------------------------------------------------------------------*/
+static void
+usb_linux_non_isoc_callback(struct usb2_xfer *xfer)
+{
+ enum {
+ REQ_SIZE = sizeof(struct usb2_device_request)
+ };
+ struct urb *urb = xfer->priv_fifo;
+ struct usb_host_endpoint *uhe = xfer->priv_sc;
+ uint8_t *ptr;
+ uint32_t max_bulk = xfer->max_data_length;
+ uint8_t data_frame = xfer->flags_int.control_xfr ? 1 : 0;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->flags_int.control_xfr) {
+
+ /* don't transfer the setup packet again: */
+
+ xfer->frlengths[0] = 0;
+ }
+ if (urb->bsd_isread && (!xfer->flags.ext_buffer)) {
+ /* copy in data with regard to the URB */
+ usb2_copy_out(xfer->frbuffers + data_frame, 0,
+ urb->bsd_data_ptr, xfer->frlengths[data_frame]);
+ }
+ urb->bsd_length_rem -= xfer->frlengths[data_frame];
+ urb->bsd_data_ptr += xfer->frlengths[data_frame];
+ urb->actual_length += xfer->frlengths[data_frame];
+
+ /* check for short transfer */
+ if (xfer->actlen < xfer->sumlen) {
+ urb->bsd_length_rem = 0;
+
+ /* short transfer */
+ if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+ urb->status = -EPIPE;
+ } else {
+ urb->status = 0;
+ }
+ } else {
+ /* check remainder */
+ if (urb->bsd_length_rem > 0) {
+ goto setup_bulk;
+ }
+ /* success */
+ urb->status = 0;
+ }
+
+ /* call callback */
+ usb_linux_complete(xfer);
+
+ case USB_ST_SETUP:
+tr_setup:
+ /* get next transfer */
+ urb = TAILQ_FIRST(&uhe->bsd_urb_list);
+ if (urb == NULL) {
+ /* nothing to do */
+ return;
+ }
+ TAILQ_REMOVE(&uhe->bsd_urb_list, urb, bsd_urb_list);
+ urb->bsd_urb_list.tqe_prev = NULL;
+
+ xfer->priv_fifo = urb;
+ xfer->flags.force_short_xfer = 0;
+ xfer->timeout = urb->timeout;
+
+ if (xfer->flags_int.control_xfr) {
+
+ /*
+ * USB control transfers need special handling.
+ * First copy in the header, then copy in data!
+ */
+ if (!xfer->flags.ext_buffer) {
+ usb2_copy_in(xfer->frbuffers, 0,
+ urb->setup_packet, REQ_SIZE);
+ } else {
+ /* set virtual address to load */
+ usb2_set_frame_data(xfer,
+ urb->setup_packet, 0);
+ }
+
+ xfer->frlengths[0] = REQ_SIZE;
+
+ ptr = urb->setup_packet;
+
+ /* setup data transfer direction and length */
+ urb->bsd_isread = (ptr[0] & UT_READ) ? 1 : 0;
+ urb->bsd_length_rem = ptr[6] | (ptr[7] << 8);
+
+ } else {
+
+ /* setup data transfer direction */
+
+ urb->bsd_length_rem = urb->transfer_buffer_length;
+ urb->bsd_isread = (uhe->desc.bEndpointAddress &
+ UE_DIR_IN) ? 1 : 0;
+ }
+
+ urb->bsd_data_ptr = urb->transfer_buffer;
+ urb->actual_length = 0;
+
+setup_bulk:
+ if (max_bulk > urb->bsd_length_rem) {
+ max_bulk = urb->bsd_length_rem;
+ }
+ /* check if we need to force a short transfer */
+
+ if ((max_bulk == urb->bsd_length_rem) &&
+ (urb->transfer_flags & URB_ZERO_PACKET) &&
+ (!xfer->flags_int.control_xfr)) {
+ xfer->flags.force_short_xfer = 1;
+ }
+ /* check if we need to copy in data */
+
+ if (xfer->flags.ext_buffer) {
+ /* set virtual address to load */
+ usb2_set_frame_data(xfer, urb->bsd_data_ptr,
+ data_frame);
+ } else if (!urb->bsd_isread) {
+ /* copy out data with regard to the URB */
+ usb2_copy_in(xfer->frbuffers + data_frame, 0,
+ urb->bsd_data_ptr, max_bulk);
+ }
+ xfer->frlengths[data_frame] = max_bulk;
+ if (xfer->flags_int.control_xfr) {
+ if (max_bulk > 0) {
+ xfer->nframes = 2;
+ } else {
+ xfer->nframes = 1;
+ }
+ } else {
+ xfer->nframes = 1;
+ }
+ usb2_start_hardware(xfer);
+ return;
+
+ default:
+ if (xfer->error == USB_ERR_CANCELLED) {
+ urb->status = -ECONNRESET;
+ } else {
+ urb->status = -EPIPE;
+ }
+
+ /* Set zero for "actual_length" */
+ urb->actual_length = 0;
+
+ /* call callback */
+ usb_linux_complete(xfer);
+
+ if (xfer->error == USB_ERR_CANCELLED) {
+ /* we need to return in this case */
+ return;
+ }
+ goto tr_setup;
+ }
+}
diff --git a/sys/dev/usb2/core/usb2_compat_linux.h b/sys/dev/usb2/core/usb2_compat_linux.h
new file mode 100644
index 000000000000..b8ddaf4fe93f
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_compat_linux.h
@@ -0,0 +1,465 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2007 Luigi Rizzo - Universita` di Pisa. All rights reserved.
+ * Copyright (c) 2007 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.
+ */
+
+#ifndef _USB_COMPAT_LINUX_H
+#define _USB_COMPAT_LINUX_H
+
+struct usb_device;
+struct usb_interface;
+struct usb_driver;
+struct urb;
+
+typedef void *pm_message_t;
+typedef void (usb_complete_t)(struct urb *);
+
+#define USB_MAX_FULL_SPEED_ISOC_FRAMES (60 * 1)
+#define USB_MAX_HIGH_SPEED_ISOC_FRAMES (60 * 8)
+
+/*
+ * Linux compatible USB device drivers put their device information
+ * into the "usb_device_id" structure using the "USB_DEVICE()" macro.
+ * The "MODULE_DEVICE_TABLE()" macro can be used to export this
+ * information to userland.
+ */
+struct usb_device_id {
+ /* which fields to match against */
+ uint16_t match_flags;
+#define USB_DEVICE_ID_MATCH_VENDOR 0x0001
+#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002
+#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004
+#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008
+#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010
+#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020
+#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040
+#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080
+#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100
+#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
+
+ /* Used for product specific matches; the BCD range is inclusive */
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice_lo;
+ uint16_t bcdDevice_hi;
+
+ /* Used for device class matches */
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+
+ /* Used for interface class matches */
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+
+ /* Hook for driver specific information */
+ unsigned long driver_info;
+};
+
+#define USB_DEVICE_ID_MATCH_DEVICE \
+ (USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT)
+
+#define USB_DEVICE(vend,prod) \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = (vend), \
+ .idProduct = (prod)
+
+/* The "usb_driver" structure holds the Linux USB device driver
+ * callbacks, and a pointer to device ID's which this entry should
+ * match against. Usually this entry is exposed to the USB emulation
+ * layer using the "USB_DRIVER_EXPORT()" macro, which is defined
+ * below.
+ */
+struct usb_driver {
+ const char *name;
+
+ int (*probe) (struct usb_interface *intf,
+ const struct usb_device_id *id);
+
+ void (*disconnect) (struct usb_interface *intf);
+
+ int (*ioctl) (struct usb_interface *intf, unsigned int code,
+ void *buf);
+
+ int (*suspend) (struct usb_interface *intf, pm_message_t message);
+ int (*resume) (struct usb_interface *intf);
+
+ const struct usb_device_id *id_table;
+
+ void (*shutdown) (struct usb_interface *intf);
+
+ LIST_ENTRY(usb_driver) linux_driver_list;
+};
+
+#define USB_DRIVER_EXPORT(id,p_usb_drv) \
+ SYSINIT(id,SI_SUB_KLD,SI_ORDER_FIRST,usb_linux_register,p_usb_drv); \
+ SYSUNINIT(id,SI_SUB_KLD,SI_ORDER_ANY,usb_linux_deregister,p_usb_drv)
+
+/*
+ * The following structure is the same as "usb_device_descriptor_t"
+ * except that 16-bit values are "uint16_t" and not an array of "uint8_t".
+ * It is used by Linux USB device drivers.
+ */
+struct usb_device_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+
+ uint16_t bcdUSB;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bMaxPacketSize0;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+ uint8_t bNumConfigurations;
+} __packed;
+
+/*
+ * The following structure is the same as
+ * "usb_interface_descriptor_t". It is used by
+ * Linux USB device drivers.
+ */
+struct usb_interface_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+} __packed;
+
+/*
+ * The following structure is the same as "usb_endpoint_descriptor_t"
+ * except that 16-bit values are "uint16_t" and not an array of "uint8_t".
+ * It is used by Linux USB device drivers.
+ */
+struct usb_endpoint_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+
+ /* extension for audio endpoints only: */
+ uint8_t bRefresh;
+ uint8_t bSynchAddress;
+} __packed;
+
+#define USB_DT_ENDPOINT_SIZE 7
+#define USB_DT_ENDPOINT_AUDIO_SIZE 9
+
+/*
+ * Endpoints
+ */
+#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
+#define USB_ENDPOINT_DIR_MASK 0x80
+
+#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
+#define USB_ENDPOINT_XFER_CONTROL 0
+#define USB_ENDPOINT_XFER_ISOC 1
+#define USB_ENDPOINT_XFER_BULK 2
+#define USB_ENDPOINT_XFER_INT 3
+#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
+
+/* CONTROL REQUEST SUPPORT */
+
+/*
+ * Definition of direction mask for
+ * "bEndpointAddress" and "bmRequestType":
+ */
+#define USB_DIR_MASK 0x80
+#define USB_DIR_OUT 0x00 /* write to USB device */
+#define USB_DIR_IN 0x80 /* read from USB device */
+
+/*
+ * Definition of type mask for
+ * "bmRequestType":
+ */
+#define USB_TYPE_MASK (0x03 << 5)
+#define USB_TYPE_STANDARD (0x00 << 5)
+#define USB_TYPE_CLASS (0x01 << 5)
+#define USB_TYPE_VENDOR (0x02 << 5)
+#define USB_TYPE_RESERVED (0x03 << 5)
+
+/*
+ * Definition of receiver mask for
+ * "bmRequestType":
+ */
+#define USB_RECIP_MASK 0x1f
+#define USB_RECIP_DEVICE 0x00
+#define USB_RECIP_INTERFACE 0x01
+#define USB_RECIP_ENDPOINT 0x02
+#define USB_RECIP_OTHER 0x03
+
+/*
+ * Definition of standard request values for
+ * "bRequest":
+ */
+#define USB_REQ_GET_STATUS 0x00
+#define USB_REQ_CLEAR_FEATURE 0x01
+#define USB_REQ_SET_FEATURE 0x03
+#define USB_REQ_SET_ADDRESS 0x05
+#define USB_REQ_GET_DESCRIPTOR 0x06
+#define USB_REQ_SET_DESCRIPTOR 0x07
+#define USB_REQ_GET_CONFIGURATION 0x08
+#define USB_REQ_SET_CONFIGURATION 0x09
+#define USB_REQ_GET_INTERFACE 0x0A
+#define USB_REQ_SET_INTERFACE 0x0B
+#define USB_REQ_SYNCH_FRAME 0x0C
+
+#define USB_REQ_SET_ENCRYPTION 0x0D /* Wireless USB */
+#define USB_REQ_GET_ENCRYPTION 0x0E
+#define USB_REQ_SET_HANDSHAKE 0x0F
+#define USB_REQ_GET_HANDSHAKE 0x10
+#define USB_REQ_SET_CONNECTION 0x11
+#define USB_REQ_SET_SECURITY_DATA 0x12
+#define USB_REQ_GET_SECURITY_DATA 0x13
+#define USB_REQ_SET_WUSB_DATA 0x14
+#define USB_REQ_LOOPBACK_DATA_WRITE 0x15
+#define USB_REQ_LOOPBACK_DATA_READ 0x16
+#define USB_REQ_SET_INTERFACE_DS 0x17
+
+/*
+ * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and
+ * are read as a bit array returned by USB_REQ_GET_STATUS. (So there
+ * are at most sixteen features of each type.)
+ */
+#define USB_DEVICE_SELF_POWERED 0 /* (read only) */
+#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */
+#define USB_DEVICE_TEST_MODE 2 /* (wired high speed only) */
+#define USB_DEVICE_BATTERY 2 /* (wireless) */
+#define USB_DEVICE_B_HNP_ENABLE 3 /* (otg) dev may initiate HNP */
+#define USB_DEVICE_WUSB_DEVICE 3 /* (wireless) */
+#define USB_DEVICE_A_HNP_SUPPORT 4 /* (otg) RH port supports HNP */
+#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* (otg) other RH port does */
+#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */
+
+#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */
+
+#define PIPE_ISOCHRONOUS 0x01 /* UE_ISOCHRONOUS */
+#define PIPE_INTERRUPT 0x03 /* UE_INTERRUPT */
+#define PIPE_CONTROL 0x00 /* UE_CONTROL */
+#define PIPE_BULK 0x02 /* UE_BULK */
+
+/* Whenever Linux references an USB endpoint:
+ * a) to initialize "urb->pipe"
+ * b) second argument passed to "usb_control_msg()"
+ *
+ * Then it uses one of the following macros. The "endpoint" argument
+ * is the physical endpoint value masked by 0xF. The "dev" argument
+ * is a pointer to "struct usb_device".
+ */
+#define usb_sndctrlpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_CONTROL, (endpoint) | USB_DIR_OUT)
+
+#define usb_rcvctrlpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_CONTROL, (endpoint) | USB_DIR_IN)
+
+#define usb_sndisocpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_ISOCHRONOUS, (endpoint) | USB_DIR_OUT)
+
+#define usb_rcvisocpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_ISOCHRONOUS, (endpoint) | USB_DIR_IN)
+
+#define usb_sndbulkpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_BULK, (endpoint) | USB_DIR_OUT)
+
+#define usb_rcvbulkpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_BULK, (endpoint) | USB_DIR_IN)
+
+#define usb_sndintpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_INTERRUPT, (endpoint) | USB_DIR_OUT)
+
+#define usb_rcvintpipe(dev,endpoint) \
+ usb_find_host_endpoint(dev, PIPE_INTERRUPT, (endpoint) | USB_DIR_IN)
+
+/* The following four structures makes up a tree, where we have the
+ * leaf structure, "usb_host_endpoint", first, and the root structure,
+ * "usb_device", last. The four structures below mirror the structure
+ * of the USB descriptors belonging to an USB configuration. Please
+ * refer to the USB specification for a definition of "endpoints" and
+ * "interfaces".
+ */
+struct usb_host_endpoint {
+ struct usb_endpoint_descriptor desc;
+
+ TAILQ_HEAD(, urb) bsd_urb_list;
+
+ struct usb2_xfer *bsd_xfer[2];
+
+ uint8_t *extra; /* Extra descriptors */
+
+ uint32_t fbsd_buf_size;
+
+ uint16_t extralen;
+
+ uint8_t bsd_iface_index;
+} __aligned(USB_HOST_ALIGN);
+
+struct usb_host_interface {
+ struct usb_interface_descriptor desc;
+
+ /* the following array has size "desc.bNumEndpoint" */
+ struct usb_host_endpoint *endpoint;
+
+ const char *string; /* iInterface string, if present */
+ uint8_t *extra; /* Extra descriptors */
+
+ uint16_t extralen;
+
+ uint8_t bsd_iface_index;
+} __aligned(USB_HOST_ALIGN);
+
+struct usb_interface {
+ /* array of alternate settings for this interface */
+ struct usb_host_interface *altsetting;
+ struct usb_host_interface *cur_altsetting;
+ struct usb_device *linux_udev;
+ void *bsd_priv_sc; /* device specific information */
+
+ uint8_t num_altsetting; /* number of alternate settings */
+ uint8_t bsd_iface_index;
+} __aligned(USB_HOST_ALIGN);
+
+struct usb_device {
+ struct usb_device_descriptor descriptor;
+ struct usb_host_endpoint ep0;
+
+ struct usb2_device *bsd_udev;
+ struct usb_interface *bsd_iface_start;
+ struct usb_interface *bsd_iface_end;
+ struct usb_host_endpoint *bsd_endpoint_start;
+ struct usb_host_endpoint *bsd_endpoint_end;
+
+ /* static strings from the device */
+ const char *product; /* iProduct string, if present */
+ const char *manufacturer; /* iManufacturer string, if present */
+ const char *serial; /* iSerialNumber string, if present */
+
+ uint16_t devnum;
+
+ uint8_t speed; /* USB_SPEED_XXX */
+} __aligned(USB_HOST_ALIGN);
+
+/*
+ * The following structure is used to extend "struct urb" when we are
+ * dealing with an isochronous endpoint. It contains information about
+ * the data offset and data length of an isochronous packet.
+ * The "actual_length" field is updated before the "complete"
+ * callback in the "urb" structure is called.
+ */
+struct usb_iso_packet_descriptor {
+ uint32_t offset; /* depreciated buffer offset (the
+ * packets are usually back to back) */
+ uint16_t length; /* expected length */
+ uint16_t actual_length;
+ uint16_t status;
+};
+
+/*
+ * The following structure holds various information about an USB
+ * transfer. This structure is used for all kinds of USB transfers.
+ *
+ * URB is short for USB Request Block.
+ */
+struct urb {
+ TAILQ_ENTRY(urb) bsd_urb_list;
+ struct cv cv_wait;
+
+ struct usb_device *dev; /* (in) pointer to associated device */
+ struct usb_host_endpoint *pipe; /* (in) pipe pointer */
+ uint8_t *setup_packet; /* (in) setup packet (control only) */
+ uint8_t *bsd_data_ptr;
+ void *transfer_buffer; /* (in) associated data buffer */
+ void *context; /* (in) context for completion */
+ usb_complete_t *complete; /* (in) completion routine */
+
+ uint32_t transfer_buffer_length;/* (in) data buffer length */
+ uint32_t actual_length; /* (return) actual transfer length */
+ uint32_t bsd_length_rem;
+ uint32_t timeout; /* FreeBSD specific */
+
+ uint16_t transfer_flags; /* (in) */
+#define URB_SHORT_NOT_OK 0x0001 /* report short transfers like errors */
+#define URB_ISO_ASAP 0x0002 /* ignore "start_frame" field */
+#define URB_ZERO_PACKET 0x0004 /* the USB transfer ends with a short
+ * packet */
+#define URB_NO_TRANSFER_DMA_MAP 0x0008 /* "transfer_dma" is valid on submit */
+#define URB_WAIT_WAKEUP 0x0010 /* custom flags */
+#define URB_IS_SLEEPING 0x0020 /* custom flags */
+
+ uint16_t start_frame; /* (modify) start frame (ISO) */
+ uint16_t number_of_packets; /* (in) number of ISO packets */
+ uint16_t interval; /* (modify) transfer interval
+ * (INT/ISO) */
+ uint16_t error_count; /* (return) number of ISO errors */
+ int16_t status; /* (return) status */
+
+ uint8_t setup_dma; /* (in) not used on FreeBSD */
+ uint8_t transfer_dma; /* (in) not used on FreeBSD */
+ uint8_t bsd_isread;
+
+ struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */
+};
+
+/* various prototypes */
+
+int usb_submit_urb(struct urb *urb, uint16_t mem_flags);
+int usb_unlink_urb(struct urb *urb);
+int usb_clear_halt(struct usb_device *dev, struct usb_host_endpoint *uhe);
+int usb_control_msg(struct usb_device *dev, struct usb_host_endpoint *pipe, uint8_t request, uint8_t requesttype, uint16_t value, uint16_t index, void *data, uint16_t size, uint32_t timeout);
+int usb_set_interface(struct usb_device *dev, uint8_t ifnum, uint8_t alternate);
+int usb_setup_endpoint(struct usb_device *dev, struct usb_host_endpoint *uhe, uint32_t bufsize);
+
+struct usb_host_endpoint *usb_find_host_endpoint(struct usb_device *dev, uint8_t type, uint8_t ep);
+struct urb *usb_alloc_urb(uint16_t iso_packets, uint16_t mem_flags);
+struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf, uint8_t alt_index);
+struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, uint8_t iface_no);
+
+void *usb_buffer_alloc(struct usb_device *dev, uint32_t size, uint16_t mem_flags, uint8_t *dma_addr);
+void *usb_get_intfdata(struct usb_interface *intf);
+
+void usb_buffer_free(struct usb_device *dev, uint32_t size, void *addr, uint8_t dma_addr);
+void usb_free_urb(struct urb *urb);
+void usb_init_urb(struct urb *urb);
+void usb_kill_urb(struct urb *urb);
+void usb_set_intfdata(struct usb_interface *intf, void *data);
+void usb_linux_register(void *arg);
+void usb_linux_deregister(void *arg);
+
+#define interface_to_usbdev(intf) (intf)->linux_udev
+#define interface_to_bsddev(intf) (intf)->linux_udev->bsd_udev
+
+#endif /* _USB_COMPAT_LINUX_H */
diff --git a/sys/dev/usb2/core/usb2_config_td.c b/sys/dev/usb2/core/usb2_config_td.c
new file mode 100644
index 000000000000..ac652fd256db
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_config_td.c
@@ -0,0 +1,320 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_mfunc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_config_td.h>
+#include <dev/usb2/core/usb2_debug.h>
+
+static void usb2_config_td_sync_cb(struct usb2_config_td_softc *sc, struct usb2_config_td_cc *cc, uint16_t ref);
+
+static void
+usb2_config_td_dispatch(struct usb2_proc_msg *pm)
+{
+ struct usb2_config_td_item *pi = (void *)pm;
+ struct usb2_config_td *ctd = pi->p_ctd;
+
+ DPRINTF("\n");
+
+ (pi->command_func) (ctd->p_softc, (void *)(pi + 1), pi->command_ref);
+
+ if (TAILQ_NEXT(pm, pm_qentry) == NULL) {
+ /* last command */
+ if (ctd->p_end_of_commands) {
+ (ctd->p_end_of_commands) (ctd->p_softc);
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_setup
+ *
+ * NOTE: the structure pointed to by "ctd" must be zeroed before calling
+ * this function!
+ *
+ * Return values:
+ * 0: success
+ * Else: failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_config_td_setup(struct usb2_config_td *ctd, void *priv_sc,
+ struct mtx *priv_mtx,
+ usb2_config_td_end_of_commands_t *p_func_eoc,
+ uint16_t item_size, uint16_t item_count)
+{
+ struct usb2_config_td_item *pi;
+ uint16_t n;
+
+ DPRINTF(" size=%u, count=%u \n", item_size, item_count);
+
+ if (item_count >= 256) {
+ DPRINTFN(0, "too many items!\n");
+ return (1);
+ }
+ ctd->p_softc = priv_sc;
+ ctd->p_end_of_commands = p_func_eoc;
+ ctd->msg_count = (2 * item_count);
+ ctd->msg_size =
+ (sizeof(struct usb2_config_td_item) + item_size);
+ ctd->p_msgs =
+ malloc(ctd->msg_size * ctd->msg_count, M_USBDEV, M_WAITOK | M_ZERO);
+ if (ctd->p_msgs == NULL) {
+ return (1);
+ }
+ if (usb2_proc_setup(&ctd->usb2_proc, priv_mtx, USB_PRI_MED)) {
+ free(ctd->p_msgs, M_USBDEV);
+ ctd->p_msgs = NULL;
+ return (1);
+ }
+ /* initialise messages */
+ pi = USB_ADD_BYTES(ctd->p_msgs, 0);
+ for (n = 0; n != ctd->msg_count; n++) {
+ pi->hdr.pm_callback = &usb2_config_td_dispatch;
+ pi->p_ctd = ctd;
+ pi = USB_ADD_BYTES(pi, ctd->msg_size);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_drain
+ *
+ * This function will tear down an USB config thread, waiting for the
+ * currently executing command to return.
+ *
+ * NOTE: If the structure pointed to by "ctd" is all zero,
+ * this function does nothing.
+ *------------------------------------------------------------------------*/
+void
+usb2_config_td_drain(struct usb2_config_td *ctd)
+{
+ DPRINTF("\n");
+ if (ctd->p_msgs) {
+ usb2_proc_drain(&ctd->usb2_proc);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_unsetup
+ *
+ * NOTE: If the structure pointed to by "ctd" is all zero,
+ * this function does nothing.
+ *------------------------------------------------------------------------*/
+void
+usb2_config_td_unsetup(struct usb2_config_td *ctd)
+{
+ DPRINTF("\n");
+
+ usb2_config_td_drain(ctd);
+
+ if (ctd->p_msgs) {
+ usb2_proc_unsetup(&ctd->usb2_proc);
+ free(ctd->p_msgs, M_USBDEV);
+ ctd->p_msgs = NULL;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_queue_command
+ *
+ * This function will enter a command into the config thread queue for
+ * execution. The "command_sync" field was previously used to indicate
+ * the queue count which is now fixed at two elements. If the
+ * "command_sync" field is equal to "USB2_CONFIG_TD_SYNC" the command
+ * will be executed synchronously from the config thread. The
+ * "command_ref" argument is the reference count for the current
+ * command which is passed on to the "command_post_func"
+ * function. This parameter can be used to make a command
+ * unique. "command_pre_func" is called from this function when we
+ * have the final queue element. "command_post_func" is called from
+ * the USB config thread when the command reaches the beginning of the
+ * USB config thread queue. This function must be called locked.
+ *------------------------------------------------------------------------*/
+void
+usb2_config_td_queue_command(struct usb2_config_td *ctd,
+ usb2_config_td_command_t *command_pre_func,
+ usb2_config_td_command_t *command_post_func,
+ uint16_t command_sync,
+ uint16_t command_ref)
+{
+ struct usb2_config_td_item *pi;
+ struct usb2_config_td_item *pi_0;
+ struct usb2_config_td_item *pi_1;
+ uint16_t n;
+
+ if (usb2_config_td_is_gone(ctd)) {
+ DPRINTF("gone\n");
+ /* nothing more to do */
+ return;
+ }
+ DPRINTF("\n");
+
+ pi = USB_ADD_BYTES(ctd->p_msgs, 0);
+ for (n = 0;; n += 2) {
+ if (n == ctd->msg_count) {
+ /* should not happen */
+ panic("%s:%d: out of memory!\n",
+ __FUNCTION__, __LINE__);
+ return;
+ }
+ if (pi->command_func == NULL) {
+ /* reserve our entry */
+ pi->command_func = command_post_func;
+ pi->command_ref = command_ref;
+ pi_0 = pi;
+ pi = USB_ADD_BYTES(pi, ctd->msg_size);
+ pi->command_func = command_post_func;
+ pi->command_ref = command_ref;
+ pi_1 = pi;
+ break;
+ }
+ if ((pi->command_func == command_post_func) &&
+ (pi->command_ref == command_ref)) {
+ /* found an entry */
+ pi_0 = pi;
+ pi = USB_ADD_BYTES(pi, ctd->msg_size);
+ pi_1 = pi;
+ break;
+ }
+ pi = USB_ADD_BYTES(pi, (2 * ctd->msg_size));
+ }
+
+ /*
+ * We have two message structures. One of them will get
+ * queued:
+ */
+ pi = usb2_proc_msignal(&ctd->usb2_proc, pi_0, pi_1);
+
+ /*
+ * The job of the post-command function is to finish the command in
+ * a separate context to allow calls to sleeping functions
+ * basically. Queue the post command before calling the pre command.
+ * That way commands queued by the pre command will be queued after
+ * the current command.
+ */
+
+ /*
+ * The job of the pre-command function is to copy the needed
+ * configuration to the provided structure and to execute other
+ * commands that must happen immediately
+ */
+ if (command_pre_func) {
+ (command_pre_func) (ctd->p_softc, (void *)(pi + 1), command_ref);
+ }
+ if (command_sync == USB2_CONFIG_TD_SYNC) {
+ usb2_proc_mwait(&ctd->usb2_proc, pi_0, pi_1);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_is_gone
+ *
+ * Return values:
+ * 0: config thread is running
+ * Else: config thread is gone
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_config_td_is_gone(struct usb2_config_td *ctd)
+{
+ return (usb2_proc_is_gone(&ctd->usb2_proc));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_sleep
+ *
+ * NOTE: this function can only be called from the config thread
+ *
+ * Return values:
+ * 0: normal delay
+ * Else: config thread is gone
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_config_td_sleep(struct usb2_config_td *ctd, uint32_t timeout)
+{
+ uint8_t is_gone = usb2_config_td_is_gone(ctd);
+
+ if (is_gone) {
+ goto done;
+ }
+ if (timeout == 0) {
+ /*
+ * Zero means no timeout, so avoid that by setting
+ * timeout to one:
+ */
+ timeout = 1;
+ }
+ mtx_unlock(ctd->usb2_proc.up_mtx);
+
+ if (pause("USBWAIT", timeout)) {
+ /* ignore */
+ }
+ mtx_lock(ctd->usb2_proc.up_mtx);
+
+ is_gone = usb2_config_td_is_gone(ctd);
+done:
+ return (is_gone);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_config_td_sync
+ *
+ * This function will wait until all commands have been executed on
+ * the config thread. This function must be called locked and can
+ * sleep.
+ *
+ * Return values:
+ * 0: success
+ * Else: config thread is gone
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_config_td_sync(struct usb2_config_td *ctd)
+{
+ if (usb2_config_td_is_gone(ctd)) {
+ return (1);
+ }
+ usb2_config_td_queue_command(ctd, NULL,
+ &usb2_config_td_sync_cb, USB2_CONFIG_TD_SYNC, 0);
+
+ if (usb2_config_td_is_gone(ctd)) {
+ return (1);
+ }
+ return (0);
+}
+
+static void
+usb2_config_td_sync_cb(struct usb2_config_td_softc *sc,
+ struct usb2_config_td_cc *cc, uint16_t ref)
+{
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_config_td.h b/sys/dev/usb2/core/usb2_config_td.h
new file mode 100644
index 000000000000..1f7d378b0bf0
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_config_td.h
@@ -0,0 +1,71 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_CONFIG_TD_H_
+#define _USB2_CONFIG_TD_H_
+
+struct usb2_config_td_softc;
+struct usb2_config_td_cc;
+
+#define USB2_CONFIG_TD_SYNC 0xFFFF /* magic value */
+
+typedef void (usb2_config_td_command_t)(struct usb2_config_td_softc *sc, struct usb2_config_td_cc *cc, uint16_t reference);
+typedef void (usb2_config_td_end_of_commands_t)(struct usb2_config_td_softc *sc);
+
+/*
+ * The following structure defines a command that should be executed
+ * using the USB config thread system.
+ */
+struct usb2_config_td_item {
+ struct usb2_proc_msg hdr;
+ struct usb2_config_td *p_ctd;
+ usb2_config_td_command_t *command_func;
+ uint16_t command_ref;
+} __aligned(USB_HOST_ALIGN);
+
+/*
+ * The following structure defines an USB config thread.
+ */
+struct usb2_config_td {
+ struct usb2_process usb2_proc;
+ struct usb2_config_td_softc *p_softc;
+ usb2_config_td_end_of_commands_t *p_end_of_commands;
+ void *p_msgs;
+ uint16_t msg_size;
+ uint16_t msg_count;
+};
+
+/* prototypes */
+
+uint8_t usb2_config_td_setup(struct usb2_config_td *ctd, void *priv_sc, struct mtx *priv_mtx, usb2_config_td_end_of_commands_t *p_func_eoc, uint16_t item_size, uint16_t item_count);
+void usb2_config_td_drain(struct usb2_config_td *ctd);
+void usb2_config_td_unsetup(struct usb2_config_td *ctd);
+void usb2_config_td_queue_command(struct usb2_config_td *ctd, usb2_config_td_command_t *pre_func, usb2_config_td_command_t *post_func, uint16_t command_sync, uint16_t command_ref);
+uint8_t usb2_config_td_is_gone(struct usb2_config_td *ctd);
+uint8_t usb2_config_td_sleep(struct usb2_config_td *ctd, uint32_t timeout);
+uint8_t usb2_config_td_sync(struct usb2_config_td *ctd);
+
+#endif /* _USB2_CONFIG_TD_H_ */
diff --git a/sys/dev/usb2/core/usb2_core.c b/sys/dev/usb2/core/usb2_core.c
new file mode 100644
index 000000000000..b96f53c71225
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_core.c
@@ -0,0 +1,40 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * USB specifications and other documentation can be found at
+ * http://www.usb.org/developers/docs/ and
+ * http://www.usb.org/developers/devclass_docs/
+ */
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_mbuf.h>
+
+MALLOC_DEFINE(M_USB, "USB", "USB");
+MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device");
+MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller");
+
+MODULE_VERSION(usb2_core, 1);
diff --git a/sys/dev/usb2/core/usb2_core.h b/sys/dev/usb2/core/usb2_core.h
new file mode 100644
index 000000000000..c0cec7a1725e
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_core.h
@@ -0,0 +1,448 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * Including this file is mandatory for all USB related c-files in the
+ * kernel.
+ */
+
+#ifndef _USB2_CORE_H_
+#define _USB2_CORE_H_
+
+/* Default USB configuration */
+
+#ifndef USB_NO_POLL
+#define USB_NO_POLL 0
+#endif
+
+#ifndef USB_USE_CONDVAR
+#define USB_USE_CONDVAR 0
+#endif
+
+#ifndef USB_TD_GET_PROC
+#define USB_TD_GET_PROC(td) (td)->td_proc
+#endif
+
+#ifndef USB_PROC_GET_GID
+#define USB_PROC_GET_GID(td) (td)->p_pgid
+#endif
+
+#ifndef USB_VNOPS_FO_CLOSE
+#define USB_VNOPS_FO_CLOSE(fp, td, perr) do { \
+ (td)->td_fpop = (fp); \
+ *(perr) = vnops.fo_close(fp, td); \
+ (td)->td_fpop = NULL; \
+} while (0)
+#endif
+
+#ifndef USB_VNOPS_FO_STAT
+#define USB_VNOPS_FO_STAT(fp, sb, cred, td) \
+ vnops.fo_stat(fp, sb, cred, td)
+#endif
+
+#ifndef USB_VNOPS_FO_TRUNCATE
+#define USB_VNOPS_FO_TRUNCATE(fp, length, cred, td) \
+ vnops.fo_truncate(fp, length, cred, td)
+#endif
+
+/* Include files */
+
+#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/linker_set.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/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_revision.h>
+
+#include "usb2_if.h"
+#include "opt_usb.h"
+#include "opt_bus.h"
+
+#define USB_STACK_VERSION 2000 /* 2.0 */
+
+#define USB_HOST_ALIGN 8 /* bytes, must be power of two */
+
+#define USB_ROOT_HUB_ADDR 1 /* value */
+
+#define USB_ISOC_TIME_MAX 128 /* ms */
+#define USB_FS_ISOC_UFRAME_MAX 4 /* exclusive unit */
+
+#if (USB_FS_ISOC_UFRAME_MAX > 6)
+#error "USB_FS_ISOC_UFRAME_MAX cannot be set higher than 6"
+#endif
+
+#define USB_MAX_FS_ISOC_FRAMES_PER_XFER (120) /* units */
+#define USB_MAX_HS_ISOC_FRAMES_PER_XFER (8*120) /* units */
+
+#define USB_MAX_IPACKET 8 /* maximum size of the initial USB
+ * data packet */
+#ifndef USB_VERBOSE
+#define USB_VERBOSE 1
+#endif
+
+#define USB_HUB_MAX_DEPTH 5
+
+/* USB transfer states */
+
+#define USB_ST_SETUP 0
+#define USB_ST_TRANSFERRED 1
+#define USB_ST_ERROR 2
+
+/*
+ * The following macro will return the current state of an USB
+ * transfer like defined by the "USB_ST_XXX" enums.
+ */
+#define USB_GET_STATE(xfer) ((xfer)->usb2_state)
+
+/*
+ * The following macro will tell if an USB transfer is currently
+ * receiving or transferring data.
+ */
+#define USB_GET_DATA_ISREAD(xfer) (((xfer)->flags_int.usb2_mode == \
+ USB_MODE_DEVICE) ? ((xfer->endpoint & UE_DIR_IN) ? 0 : 1) : \
+ ((xfer->endpoint & UE_DIR_IN) ? 1 : 0))
+
+/*
+ * The following macros are used used to convert milliseconds into
+ * HZ. We use 1024 instead of 1000 milliseconds per second to save a
+ * full division.
+ */
+#define USB_MS_HZ 1024
+
+#define USB_MS_TO_TICKS(ms) \
+ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ)
+
+/* macros */
+
+#define usb2_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f)
+#define usb2_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d)
+#define usb2_callout_stop(c) callout_stop(&(c)->co)
+#define usb2_callout_drain(c) callout_drain(&(c)->co)
+#define usb2_callout_pending(c) callout_pending(&(c)->co)
+
+/* structure prototypes */
+
+struct file;
+struct usb2_bus;
+struct usb2_device;
+struct usb2_page;
+struct usb2_page_cache;
+struct usb2_xfer;
+struct usb2_xfer_root;
+
+/* typedefs */
+
+typedef uint8_t usb2_error_t;
+
+typedef void (usb2_callback_t)(struct usb2_xfer *);
+
+/* structures */
+
+/*
+ * This structure contains permissions.
+ */
+
+struct usb2_perm {
+ uint32_t uid;
+ uint32_t gid;
+ uint16_t mode;
+};
+
+/*
+ * Common queue structure for USB transfers.
+ */
+struct usb2_xfer_queue {
+ TAILQ_HEAD(, usb2_xfer) head;
+ struct usb2_xfer *curr; /* current USB transfer processed */
+ void (*command) (struct usb2_xfer_queue *pq);
+ uint8_t recurse_1:1;
+ uint8_t recurse_2:1;
+};
+
+/*
+ * The following is a wrapper for the callout structure to ease
+ * porting the code to other platforms.
+ */
+struct usb2_callout {
+ struct callout co;
+};
+
+/*
+ * The following structure defines a set of USB transfer flags.
+ */
+struct usb2_xfer_flags {
+ uint8_t force_short_xfer:1; /* force a short transmit transfer
+ * last */
+ uint8_t short_xfer_ok:1; /* allow short receive transfers */
+ uint8_t short_frames_ok:1; /* allow short frames */
+ uint8_t pipe_bof:1; /* block pipe on failure */
+ uint8_t proxy_buffer:1; /* makes buffer size a factor of
+ * "max_frame_size" */
+ uint8_t ext_buffer:1; /* uses external DMA buffer */
+ uint8_t manual_status:1; /* non automatic status stage on
+ * control transfers */
+ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can
+ * be ignored */
+ uint8_t stall_pipe:1; /* set if the endpoint belonging to
+ * this USB transfer should be stalled
+ * before starting this transfer! */
+};
+
+/*
+ * The following structure defines a set of internal USB transfer
+ * flags.
+ */
+struct usb2_xfer_flags_int {
+ uint16_t control_rem; /* remainder in bytes */
+
+ uint8_t open:1; /* set if USB pipe has been opened */
+ uint8_t transferring:1; /* set if an USB transfer is in
+ * progress */
+ uint8_t did_dma_delay:1; /* set if we waited for HW DMA */
+ uint8_t did_close:1; /* set if we closed the USB transfer */
+ uint8_t draining:1; /* set if we are draining an USB
+ * transfer */
+ uint8_t started:1; /* keeps track of started or stopped */
+ uint8_t bandwidth_reclaimed:1;
+ uint8_t control_xfr:1; /* set if control transfer */
+ uint8_t control_hdr:1; /* set if control header should be
+ * sent */
+ uint8_t control_act:1; /* set if control transfer is active */
+
+ uint8_t short_frames_ok:1; /* filtered version */
+ uint8_t short_xfer_ok:1; /* filtered version */
+ uint8_t bdma_enable:1; /* filtered version (only set if
+ * hardware supports DMA) */
+ uint8_t bdma_no_post_sync:1; /* set if the USB callback wrapper
+ * should not do the BUS-DMA post sync
+ * operation */
+ uint8_t bdma_setup:1; /* set if BUS-DMA has been setup */
+ uint8_t isochronous_xfr:1; /* set if isochronous transfer */
+ uint8_t usb2_mode:1; /* shadow copy of "udev->usb2_mode" */
+ uint8_t curr_dma_set:1; /* used by USB HC/DC driver */
+ uint8_t can_cancel_immed:1; /* set if USB transfer can be
+ * cancelled immediately */
+};
+
+/*
+ * The following structure defines the symmetric part of an USB config
+ * structure.
+ */
+struct usb2_config_sub {
+ usb2_callback_t *callback; /* USB transfer callback */
+ uint32_t bufsize; /* total pipe buffer size in bytes */
+ uint32_t frames; /* maximum number of USB frames */
+ uint16_t interval; /* interval in milliseconds */
+#define USB_DEFAULT_INTERVAL 0
+ uint16_t timeout; /* transfer timeout in milliseconds */
+ struct usb2_xfer_flags flags; /* transfer flags */
+};
+
+/*
+ * The following structure define an USB configuration, that basically
+ * is used when setting up an USB transfer.
+ */
+struct usb2_config {
+ struct usb2_config_sub mh; /* parameters for USB_MODE_HOST */
+ struct usb2_config_sub md; /* parameters for USB_MODE_DEVICE */
+ uint8_t type; /* pipe type */
+ uint8_t endpoint; /* pipe number */
+ uint8_t direction; /* pipe direction */
+ uint8_t ep_index; /* pipe index match to use */
+ uint8_t if_index; /* "ifaces" index to use */
+};
+
+/*
+ * The following structure defines an USB transfer.
+ */
+struct usb2_xfer {
+ struct usb2_callout timeout_handle;
+ TAILQ_ENTRY(usb2_xfer) wait_entry; /* used at various places */
+
+ struct usb2_page_cache *buf_fixup; /* fixup buffer(s) */
+ struct usb2_xfer_queue *wait_queue; /* pointer to queue that we
+ * are waiting on */
+ struct usb2_page *dma_page_ptr;
+ struct usb2_pipe *pipe; /* our USB pipe */
+ struct usb2_device *udev;
+ struct mtx *priv_mtx; /* cannot be changed during operation */
+ struct mtx *usb2_mtx; /* used by HC driver */
+ struct usb2_xfer_root *usb2_root; /* used by HC driver */
+ void *usb2_sc; /* used by HC driver */
+ void *qh_start[2]; /* used by HC driver */
+ void *td_start[2]; /* used by HC driver */
+ void *td_transfer_first; /* used by HC driver */
+ void *td_transfer_last; /* used by HC driver */
+ void *td_transfer_cache; /* used by HC driver */
+ void *priv_sc; /* device driver data pointer 1 */
+ void *priv_fifo; /* device driver data pointer 2 */
+ void *local_buffer;
+ uint32_t *frlengths;
+ struct usb2_page_cache *frbuffers;
+ usb2_callback_t *callback;
+
+ uint32_t max_usb2_frame_size;
+ uint32_t max_data_length;
+ uint32_t sumlen; /* sum of all lengths in bytes */
+ uint32_t actlen; /* actual length in bytes */
+ uint32_t timeout; /* milliseconds */
+#define USB_NO_TIMEOUT 0
+#define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */
+
+ uint32_t max_frame_count; /* initial value of "nframes" after
+ * setup */
+ uint32_t nframes; /* number of USB frames to transfer */
+ uint32_t aframes; /* actual number of USB frames
+ * transferred */
+
+ uint16_t max_packet_size;
+ uint16_t max_frame_size;
+ uint16_t qh_pos;
+ uint16_t isoc_time_complete; /* in ms */
+ uint16_t interval; /* milliseconds */
+
+ uint8_t address; /* physical USB address */
+ uint8_t endpoint; /* physical USB endpoint */
+ uint8_t max_packet_count;
+ uint8_t usb2_smask;
+ uint8_t usb2_cmask;
+ uint8_t usb2_uframe;
+ uint8_t usb2_state;
+
+ usb2_error_t error;
+
+ struct usb2_xfer_flags flags;
+ struct usb2_xfer_flags_int flags_int;
+};
+
+/*
+ * The following structure keeps information that is used to match
+ * against an array of "usb2_device_id" elements.
+ */
+struct usb2_lookup_info {
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t bIfaceIndex;
+ uint8_t bIfaceNum;
+ uint8_t bConfigIndex;
+ uint8_t bConfigNum;
+};
+
+/* Structure used by probe and attach */
+
+struct usb2_attach_arg {
+ struct usb2_lookup_info info;
+ device_t temp_dev; /* for internal use */
+ const void *driver_info; /* for internal use */
+ struct usb2_device *device; /* current device */
+ struct usb2_interface *iface; /* current interface */
+ uint8_t usb2_mode; /* see USB_MODE_XXX */
+ uint8_t port;
+ uint8_t use_generic; /* hint for generic drivers */
+};
+
+/* Structure used when referring an USB device */
+
+struct usb2_location {
+ struct usb2_bus *bus;
+ struct usb2_device *udev;
+ struct usb2_interface *iface;
+ struct usb2_fifo *rxfifo;
+ struct usb2_fifo *txfifo;
+ uint32_t devloc; /* original devloc */
+ uint16_t bus_index;
+ uint8_t dev_index;
+ uint8_t iface_index;
+ uint8_t ep_index;
+ uint8_t is_read;
+ uint8_t is_write;
+ uint8_t is_uref;
+};
+
+/* external variables */
+
+MALLOC_DECLARE(M_USB);
+MALLOC_DECLARE(M_USBDEV);
+MALLOC_DECLARE(M_USBHC);
+
+extern struct mtx usb2_ref_lock;
+
+/* typedefs */
+
+typedef struct malloc_type *usb2_malloc_type;
+
+/* prototypes */
+
+const char *usb2_errstr(usb2_error_t error);
+struct usb2_config_descriptor *usb2_get_config_descriptor(struct usb2_device *udev);
+struct usb2_device_descriptor *usb2_get_device_descriptor(struct usb2_device *udev);
+struct usb2_interface *usb2_get_iface(struct usb2_device *udev, uint8_t iface_index);
+struct usb2_interface_descriptor *usb2_get_interface_descriptor(struct usb2_interface *iface);
+uint8_t usb2_clear_stall_callback(struct usb2_xfer *xfer1, struct usb2_xfer *xfer2);
+uint8_t usb2_get_interface_altindex(struct usb2_interface *iface);
+usb2_error_t usb2_set_alt_interface_index(struct usb2_device *udev, uint8_t iface_index, uint8_t alt_index);
+uint8_t usb2_get_speed(struct usb2_device *udev);
+usb2_error_t usb2_transfer_setup(struct usb2_device *udev, const uint8_t *ifaces, struct usb2_xfer **pxfer, const struct usb2_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx);
+void usb2_set_frame_data(struct usb2_xfer *xfer, void *ptr, uint32_t frindex);
+void usb2_set_frame_offset(struct usb2_xfer *xfer, uint32_t offset, uint32_t frindex);
+void usb2_start_hardware(struct usb2_xfer *xfer);
+void usb2_transfer_clear_stall(struct usb2_xfer *xfer);
+void usb2_transfer_drain(struct usb2_xfer *xfer);
+void usb2_transfer_set_stall(struct usb2_xfer *xfer);
+void usb2_transfer_start(struct usb2_xfer *xfer);
+void usb2_transfer_stop(struct usb2_xfer *xfer);
+void usb2_transfer_unsetup(struct usb2_xfer **pxfer, uint16_t n_setup);
+usb2_error_t usb2_ref_device(struct file *fp, struct usb2_location *ploc, uint32_t devloc);
+void usb2_unref_device(struct usb2_location *ploc);
+void usb2_set_parent_iface(struct usb2_device *udev, uint8_t iface_index, uint8_t parent_index);
+void usb2_set_iface_perm(struct usb2_device *udev, uint8_t iface_index, uint32_t uid, uint32_t gid, uint16_t mode);
+uint8_t usb2_get_bus_index(struct usb2_device *udev);
+uint8_t usb2_get_device_index(struct usb2_device *udev);
+
+#endif /* _USB2_CORE_H_ */
diff --git a/sys/dev/usb2/core/usb2_debug.c b/sys/dev/usb2/core/usb2_debug.c
new file mode 100644
index 000000000000..7969662387c7
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_debug.c
@@ -0,0 +1,153 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_defs.h>
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+
+/*
+ * Define this unconditionally in case a kernel module is loaded that
+ * has been compiled with debugging options.
+ */
+int usb2_debug = 0;
+
+SYSCTL_NODE(_hw, OID_AUTO, usb2, CTLFLAG_RW, 0, "USB debugging");
+SYSCTL_INT(_hw_usb2, OID_AUTO, debug, CTLFLAG_RW,
+ &usb2_debug, 0, "Debug level");
+
+/*------------------------------------------------------------------------*
+ * usb2_dump_iface
+ *
+ * This function dumps information about an USB interface.
+ *------------------------------------------------------------------------*/
+void
+usb2_dump_iface(struct usb2_interface *iface)
+{
+ printf("usb2_dump_iface: iface=%p\n", iface);
+ if (iface == NULL) {
+ return;
+ }
+ printf(" iface=%p idesc=%p altindex=%d\n",
+ iface, iface->idesc, iface->alt_index);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dump_device
+ *
+ * This function dumps information about an USB device.
+ *------------------------------------------------------------------------*/
+void
+usb2_dump_device(struct usb2_device *udev)
+{
+ printf("usb2_dump_device: dev=%p\n", udev);
+ if (udev == NULL) {
+ return;
+ }
+ printf(" bus=%p \n"
+ " address=%d config=%d depth=%d speed=%d self_powered=%d\n"
+ " power=%d langid=%d\n",
+ udev->bus,
+ udev->address, udev->curr_config_no, udev->depth, udev->speed,
+ udev->flags.self_powered, udev->power, udev->langid);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dump_queue
+ *
+ * This function dumps the USB transfer that are queued up on an USB pipe.
+ *------------------------------------------------------------------------*/
+void
+usb2_dump_queue(struct usb2_pipe *pipe)
+{
+ struct usb2_xfer *xfer;
+
+ printf("usb2_dump_queue: pipe=%p xfer: ", pipe);
+ TAILQ_FOREACH(xfer, &pipe->pipe_q.head, wait_entry) {
+ printf(" %p", xfer);
+ }
+ printf("\n");
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dump_pipe
+ *
+ * This function dumps information about an USB pipe.
+ *------------------------------------------------------------------------*/
+void
+usb2_dump_pipe(struct usb2_pipe *pipe)
+{
+ if (pipe) {
+ printf("usb2_dump_pipe: pipe=%p", pipe);
+
+ printf(" edesc=%p isoc_next=%d toggle_next=%d",
+ pipe->edesc, pipe->isoc_next, pipe->toggle_next);
+
+ if (pipe->edesc) {
+ printf(" bEndpointAddress=0x%02x",
+ pipe->edesc->bEndpointAddress);
+ }
+ printf("\n");
+ usb2_dump_queue(pipe);
+ } else {
+ printf("usb2_dump_pipe: pipe=NULL\n");
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dump_xfer
+ *
+ * This function dumps information about an USB transfer.
+ *------------------------------------------------------------------------*/
+void
+usb2_dump_xfer(struct usb2_xfer *xfer)
+{
+ printf("usb2_dump_xfer: xfer=%p\n", xfer);
+ if (xfer == NULL) {
+ return;
+ }
+ if (xfer->pipe == NULL) {
+ printf("xfer %p: pipe=NULL\n",
+ xfer);
+ return;
+ }
+ printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d "
+ "pipe=%p ep=0x%02x attr=0x%02x\n",
+ xfer, xfer->udev,
+ UGETW(xfer->udev->ddesc.idVendor),
+ UGETW(xfer->udev->ddesc.idProduct),
+ xfer->udev->address, xfer->pipe,
+ xfer->pipe->edesc->bEndpointAddress,
+ xfer->pipe->edesc->bmAttributes);
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_debug.h b/sys/dev/usb2/core/usb2_debug.h
new file mode 100644
index 000000000000..92dcbd5b9c2c
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_debug.h
@@ -0,0 +1,70 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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 various factored out debug macros. */
+
+#ifndef _USB2_DEBUG_H_
+#define _USB2_DEBUG_H_
+
+/* Declare parent SYSCTL USB node. */
+SYSCTL_DECL(_hw_usb2);
+
+/* Declare global USB debug variable. */
+extern int usb2_debug;
+
+/* Force debugging until further */
+#ifndef USB_DEBUG
+#define USB_DEBUG 1
+#endif
+
+/* Check if USB debugging is enabled. */
+#ifdef USB_DEBUG_VAR
+#if (USB_DEBUG != 0)
+#define DPRINTFN(n,fmt,...) do { \
+ if ((USB_DEBUG_VAR) >= (n)) { \
+ printf("%s:%u: " fmt, \
+ __FUNCTION__, __LINE__,## __VA_ARGS__); \
+ } \
+} while (0)
+#define DPRINTF(...) DPRINTFN(1, __VA_ARGS__)
+#else
+#define DPRINTF(...) do { } while (0)
+#define DPRINTFN(...) do { } while (0)
+#endif
+#endif
+
+struct usb2_interface;
+struct usb2_device;
+struct usb2_pipe;
+struct usb2_xfer;
+
+void usb2_dump_iface(struct usb2_interface *iface);
+void usb2_dump_device(struct usb2_device *udev);
+void usb2_dump_queue(struct usb2_pipe *pipe);
+void usb2_dump_pipe(struct usb2_pipe *pipe);
+void usb2_dump_xfer(struct usb2_xfer *xfer);
+
+#endif /* _USB2_DEBUG_H_ */
diff --git a/sys/dev/usb2/core/usb2_dev.c b/sys/dev/usb2/core/usb2_dev.c
new file mode 100644
index 000000000000..1afe83e1f9ee
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_dev.c
@@ -0,0 +1,2786 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2006-2008 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.
+ *
+ *
+ * usb2_dev.c - An abstraction layer for creating devices under /dev/...
+ */
+
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_ioctl.h>
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+
+#define USB_DEBUG_VAR usb2_fifo_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_mbuf.h>
+#include <dev/usb2/core/usb2_dev.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_generic.h>
+#include <dev/usb2/core/usb2_dynamic.h>
+#include <dev/usb2/core/usb2_util.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+#include <sys/filio.h>
+#include <sys/ttycom.h>
+#include <sys/syscallsubr.h>
+
+#include <machine/stdarg.h>
+
+#if USB_DEBUG
+static int usb2_fifo_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device");
+SYSCTL_INT(_hw_usb2_dev, OID_AUTO, debug, CTLFLAG_RW,
+ &usb2_fifo_debug, 0, "Debug Level");
+#endif
+
+#if ((__FreeBSD_version >= 700001) || (__FreeBSD_version == 0) || \
+ ((__FreeBSD_version >= 600034) && (__FreeBSD_version < 700000)))
+#define USB_UCRED struct ucred *ucred,
+#else
+#define USB_UCRED
+#endif
+
+/* prototypes */
+
+static uint32_t usb2_path_convert_one(const char **pp);
+static uint32_t usb2_path_convert(const char *path);
+static int usb2_check_access(int fflags, struct usb2_perm *puser);
+static int usb2_fifo_open(struct usb2_fifo *f, struct file *fp, struct thread *td, int fflags);
+static void usb2_fifo_close(struct usb2_fifo *f, struct thread *td, int fflags);
+static void usb2_dev_init(void *arg);
+static void usb2_dev_init_post(void *arg);
+static void usb2_dev_uninit(void *arg);
+static int usb2_fifo_uiomove(struct usb2_fifo *f, void *cp, int n, struct uio *uio);
+static void usb2_fifo_check_methods(struct usb2_fifo_methods *pm);
+static void usb2_clone(void *arg, USB_UCRED char *name, int namelen, struct cdev **dev);
+static struct usb2_fifo *usb2_fifo_alloc(void);
+static struct usb2_pipe *usb2_dev_get_pipe(struct usb2_device *udev, uint8_t iface_index, uint8_t ep_index, uint8_t dir);
+
+static d_fdopen_t usb2_fdopen;
+static d_close_t usb2_close;
+static d_ioctl_t usb2_ioctl;
+
+static fo_rdwr_t usb2_read_f;
+static fo_rdwr_t usb2_write_f;
+
+#if __FreeBSD_version > 800009
+static fo_truncate_t usb2_truncate_f;
+
+#endif
+static fo_ioctl_t usb2_ioctl_f;
+static fo_poll_t usb2_poll_f;
+static fo_kqfilter_t usb2_kqfilter_f;
+static fo_stat_t usb2_stat_f;
+static fo_close_t usb2_close_f;
+
+static usb2_fifo_open_t usb2_fifo_dummy_open;
+static usb2_fifo_close_t usb2_fifo_dummy_close;
+static usb2_fifo_ioctl_t usb2_fifo_dummy_ioctl;
+static usb2_fifo_cmd_t usb2_fifo_dummy_cmd;
+
+static struct usb2_perm usb2_perm = {
+ .uid = UID_ROOT,
+ .gid = GID_OPERATOR,
+ .mode = 0660,
+};
+
+static struct cdevsw usb2_devsw = {
+ .d_version = D_VERSION,
+ .d_fdopen = usb2_fdopen,
+ .d_close = usb2_close,
+ .d_ioctl = usb2_ioctl,
+ .d_name = "usb",
+ .d_flags = D_TRACKCLOSE,
+};
+
+static struct fileops usb2_ops_f = {
+ .fo_read = usb2_read_f,
+ .fo_write = usb2_write_f,
+#if __FreeBSD_version > 800009
+ .fo_truncate = usb2_truncate_f,
+#endif
+ .fo_ioctl = usb2_ioctl_f,
+ .fo_poll = usb2_poll_f,
+ .fo_kqfilter = usb2_kqfilter_f,
+ .fo_stat = usb2_stat_f,
+ .fo_close = usb2_close_f,
+ .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
+};
+
+static const dev_clone_fn usb2_clone_ptr = &usb2_clone;
+static struct cdev *usb2_dev;
+static uint32_t usb2_last_devloc = 0 - 1;
+static eventhandler_tag usb2_clone_tag;
+static void *usb2_old_f_data;
+static struct fileops *usb2_old_f_ops;
+static TAILQ_HEAD(, usb2_symlink) usb2_sym_head;
+static struct sx usb2_sym_lock;
+
+struct mtx usb2_ref_lock;
+
+static uint32_t
+usb2_path_convert_one(const char **pp)
+{
+ const char *ptr;
+ uint32_t temp = 0;
+
+ ptr = *pp;
+
+ while ((*ptr >= '0') && (*ptr <= '9')) {
+ temp *= 10;
+ temp += (*ptr - '0');
+ if (temp >= 1000000) {
+ /* catch overflow early */
+ return (0 - 1);
+ }
+ ptr++;
+ }
+
+ if (*ptr == '.') {
+ /* skip dot */
+ ptr++;
+ }
+ *pp = ptr;
+
+ return (temp);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_path_convert
+ *
+ * Path format: "/dev/usb<bus>.<dev>.<iface>.<fifo>"
+ *
+ * Returns: Path converted into numerical format.
+ *------------------------------------------------------------------------*/
+static uint32_t
+usb2_path_convert(const char *path)
+{
+ uint32_t temp;
+ uint32_t devloc;
+
+ devloc = 0;
+
+ temp = usb2_path_convert_one(&path);
+
+ if (temp >= USB_BUS_MAX) {
+ return (0 - 1);
+ }
+ devloc += temp;
+
+ temp = usb2_path_convert_one(&path);
+
+ if (temp >= USB_DEV_MAX) {
+ return (0 - 1);
+ }
+ devloc += (temp * USB_BUS_MAX);
+
+ temp = usb2_path_convert_one(&path);
+
+ if (temp >= USB_IFACE_MAX) {
+ return (0 - 1);
+ }
+ devloc += (temp * USB_DEV_MAX * USB_BUS_MAX);
+
+ temp = usb2_path_convert_one(&path);
+
+ if (temp >= ((USB_FIFO_MAX / 2) + (USB_EP_MAX / 2))) {
+ return (0 - 1);
+ }
+ devloc += (temp * USB_IFACE_MAX * USB_DEV_MAX * USB_BUS_MAX);
+
+ return (devloc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_iface_perm
+ *
+ * This function will set the interface permissions.
+ *------------------------------------------------------------------------*/
+void
+usb2_set_iface_perm(struct usb2_device *udev, uint8_t iface_index,
+ uint32_t uid, uint32_t gid, uint16_t mode)
+{
+ struct usb2_interface *iface;
+
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface && iface->idesc) {
+ mtx_lock(&usb2_ref_lock);
+ iface->perm.uid = uid;
+ iface->perm.gid = gid;
+ iface->perm.mode = mode;
+ mtx_unlock(&usb2_ref_lock);
+
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_perm
+ *
+ * This function will set the permissions at the given level.
+ *
+ * Return values:
+ * 0: Success.
+ * Else: Failure.
+ *------------------------------------------------------------------------*/
+static int
+usb2_set_perm(struct usb2_dev_perm *psrc, uint8_t level)
+{
+ struct usb2_location loc;
+ struct usb2_perm *pdst;
+ uint32_t devloc;
+ int error;
+
+ /* check if the current thread can change USB permissions. */
+ error = priv_check(curthread, PRIV_ROOT);
+ if (error) {
+ return (error);
+ }
+ /* range check device location */
+ if ((psrc->bus_index >= USB_BUS_MAX) ||
+ (psrc->dev_index >= USB_DEV_MAX) ||
+ (psrc->iface_index >= USB_IFACE_MAX)) {
+ return (EINVAL);
+ }
+ if (level == 1)
+ devloc = USB_BUS_MAX; /* use root-HUB to access bus */
+ else
+ devloc = 0;
+ switch (level) {
+ case 3:
+ devloc += psrc->iface_index *
+ USB_DEV_MAX * USB_BUS_MAX;
+ /* FALLTHROUGH */
+ case 2:
+ devloc += psrc->dev_index *
+ USB_BUS_MAX;
+ /* FALLTHROUGH */
+ case 1:
+ devloc += psrc->bus_index;
+ break;
+ default:
+ break;
+ }
+
+ if ((level > 0) && (level < 4)) {
+ error = usb2_ref_device(NULL, &loc, devloc);
+ if (error) {
+ return (error);
+ }
+ }
+ switch (level) {
+ case 3:
+ if (loc.iface == NULL) {
+ usb2_unref_device(&loc);
+ return (EINVAL);
+ }
+ pdst = &loc.iface->perm;
+ break;
+ case 2:
+ pdst = &loc.udev->perm;
+ break;
+ case 1:
+ pdst = &loc.bus->perm;
+ break;
+ default:
+ pdst = &usb2_perm;
+ break;
+ }
+
+ /* all permissions are protected by "usb2_ref_lock" */
+ mtx_lock(&usb2_ref_lock);
+ pdst->uid = psrc->user_id;
+ pdst->gid = psrc->group_id;
+ pdst->mode = psrc->mode;
+ mtx_unlock(&usb2_ref_lock);
+
+ if ((level > 0) && (level < 4)) {
+ usb2_unref_device(&loc);
+ }
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_perm
+ *
+ * This function will get the permissions at the given level.
+ *
+ * Return values:
+ * 0: Success.
+ * Else: Failure.
+ *------------------------------------------------------------------------*/
+static int
+usb2_get_perm(struct usb2_dev_perm *pdst, uint8_t level)
+{
+ struct usb2_location loc;
+ struct usb2_perm *psrc;
+ uint32_t devloc;
+ int error;
+
+ if ((pdst->bus_index >= USB_BUS_MAX) ||
+ (pdst->dev_index >= USB_DEV_MAX) ||
+ (pdst->iface_index >= USB_IFACE_MAX)) {
+ return (EINVAL);
+ }
+ if (level == 1)
+ devloc = USB_BUS_MAX; /* use root-HUB to access bus */
+ else
+ devloc = 0;
+ switch (level) {
+ case 3:
+ devloc += pdst->iface_index *
+ USB_DEV_MAX * USB_BUS_MAX;
+ /* FALLTHROUGH */
+ case 2:
+ devloc += pdst->dev_index *
+ USB_BUS_MAX;
+ /* FALLTHROUGH */
+ case 1:
+ devloc += pdst->bus_index;
+ break;
+ default:
+ break;
+ }
+
+ if ((level > 0) && (level < 4)) {
+ error = usb2_ref_device(NULL, &loc, devloc);
+ if (error) {
+ return (error);
+ }
+ }
+ switch (level) {
+ case 3:
+ if (loc.iface == NULL) {
+ usb2_unref_device(&loc);
+ return (EINVAL);
+ }
+ psrc = &loc.iface->perm;
+ break;
+ case 2:
+ psrc = &loc.udev->perm;
+ break;
+ case 1:
+ psrc = &loc.bus->perm;
+ break;
+ default:
+ psrc = &usb2_perm;
+ break;
+ }
+
+ /* all permissions are protected by "usb2_ref_lock" */
+ mtx_lock(&usb2_ref_lock);
+ if (psrc->mode != 0) {
+ pdst->user_id = psrc->uid;
+ pdst->group_id = psrc->gid;
+ pdst->mode = psrc->mode;
+ } else {
+ /* access entry at this level and location is not active */
+ pdst->user_id = 0;
+ pdst->group_id = 0;
+ pdst->mode = 0;
+ }
+ mtx_unlock(&usb2_ref_lock);
+
+ if ((level > 0) && (level < 4)) {
+ usb2_unref_device(&loc);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_check_access
+ *
+ * This function will verify the given access information.
+ *
+ * Return values:
+ * 0: Access granted.
+ * Else: No access granted.
+ *------------------------------------------------------------------------*/
+static int
+usb2_check_access(int fflags, struct usb2_perm *puser)
+{
+ mode_t accmode;
+
+ if ((fflags & (FWRITE | FREAD)) && (puser->mode != 0)) {
+ /* continue */
+ } else {
+ return (EPERM); /* no access */
+ }
+
+ accmode = 0;
+ if (fflags & FWRITE)
+ accmode |= VWRITE;
+ if (fflags & FREAD)
+ accmode |= VREAD;
+
+ return (vaccess(VCHR, puser->mode, puser->uid,
+ puser->gid, accmode, curthread->td_ucred, NULL));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_ref_device
+ *
+ * This function is used to atomically refer an USB device by its
+ * device location. If this function returns success the USB device
+ * will not dissappear until the USB device is unreferenced.
+ *
+ * Return values:
+ * 0: Success, refcount incremented on the given USB device.
+ * Else: Failure.
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_ref_device(struct file *fp, struct usb2_location *ploc, uint32_t devloc)
+{
+ struct usb2_fifo **ppf;
+ struct usb2_fifo *f;
+ int fflags;
+ uint8_t need_uref;
+
+ if (fp) {
+ /* check if we need uref hint */
+ need_uref = devloc ? 0 : 1;
+ /* get devloc - already verified */
+ devloc = USB_P2U(fp->f_data);
+ /* get file flags */
+ fflags = fp->f_flag;
+ /* only ref FIFO */
+ ploc->is_uref = 0;
+ /* devloc should be valid */
+ } else {
+ /* we need uref */
+ need_uref = 1;
+ /* only ref device */
+ fflags = 0;
+ /* search for FIFO */
+ ploc->is_uref = 1;
+ /* check "devloc" */
+ if (devloc >= (USB_BUS_MAX * USB_DEV_MAX *
+ USB_IFACE_MAX * ((USB_EP_MAX / 2) + (USB_FIFO_MAX / 2)))) {
+ return (USB_ERR_INVAL);
+ }
+ }
+
+ /* store device location */
+ ploc->devloc = devloc;
+ ploc->bus_index = devloc % USB_BUS_MAX;
+ ploc->dev_index = (devloc / USB_BUS_MAX) % USB_DEV_MAX;
+ ploc->iface_index = (devloc / (USB_BUS_MAX *
+ USB_DEV_MAX)) % USB_IFACE_MAX;
+ ploc->ep_index = (devloc / (USB_BUS_MAX * USB_DEV_MAX *
+ USB_IFACE_MAX));
+
+ mtx_lock(&usb2_ref_lock);
+ ploc->bus = devclass_get_softc(usb2_devclass_ptr, ploc->bus_index);
+ if (ploc->bus == NULL) {
+ DPRINTFN(2, "no bus at %u\n", ploc->bus_index);
+ goto error;
+ }
+ if (ploc->dev_index >= ploc->bus->devices_max) {
+ DPRINTFN(2, "invalid dev index, %u\n", ploc->dev_index);
+ goto error;
+ }
+ ploc->udev = ploc->bus->devices[ploc->dev_index];
+ if (ploc->udev == NULL) {
+ DPRINTFN(2, "no device at %u\n", ploc->dev_index);
+ goto error;
+ }
+ if (ploc->udev->refcount == USB_DEV_REF_MAX) {
+ DPRINTFN(2, "no dev ref\n");
+ goto error;
+ }
+ ploc->iface = usb2_get_iface(ploc->udev, ploc->iface_index);
+ if (ploc->ep_index != 0) {
+ /* non control endpoint - we need an interface */
+ if (ploc->iface == NULL) {
+ DPRINTFN(2, "no iface\n");
+ goto error;
+ }
+ if (ploc->iface->idesc == NULL) {
+ DPRINTFN(2, "no idesc\n");
+ goto error;
+ }
+ }
+ /* check if we are doing an open */
+ if (fp == NULL) {
+ /* set defaults */
+ ploc->txfifo = NULL;
+ ploc->rxfifo = NULL;
+ ploc->is_write = 0;
+ ploc->is_read = 0;
+ } else {
+ /* check for write */
+ if (fflags & FWRITE) {
+ ppf = ploc->udev->fifo;
+ f = ppf[ploc->ep_index + USB_FIFO_TX];
+ ploc->txfifo = f;
+ ploc->is_write = 1; /* ref */
+ if ((f == NULL) ||
+ (f->refcount == USB_FIFO_REF_MAX) ||
+ (f->curr_file != fp)) {
+ goto error;
+ }
+ } else {
+ ploc->txfifo = NULL;
+ ploc->is_write = 0; /* no ref */
+ }
+
+ /* check for read */
+ if (fflags & FREAD) {
+ ppf = ploc->udev->fifo;
+ f = ppf[ploc->ep_index + USB_FIFO_RX];
+ ploc->rxfifo = f;
+ ploc->is_read = 1; /* ref */
+ if ((f == NULL) ||
+ (f->refcount == USB_FIFO_REF_MAX) ||
+ (f->curr_file != fp)) {
+ goto error;
+ }
+ } else {
+ ploc->rxfifo = NULL;
+ ploc->is_read = 0; /* no ref */
+ }
+ }
+
+ /* when everything is OK we increment the refcounts */
+ if (ploc->is_write) {
+ DPRINTFN(2, "ref write\n");
+ ploc->txfifo->refcount++;
+ if (ploc->txfifo->flag_no_uref == 0) {
+ /* we need extra locking */
+ ploc->is_uref = 1;
+ }
+ }
+ if (ploc->is_read) {
+ DPRINTFN(2, "ref read\n");
+ ploc->rxfifo->refcount++;
+ if (ploc->rxfifo->flag_no_uref == 0) {
+ /* we need extra locking */
+ ploc->is_uref = 1;
+ }
+ }
+ if (ploc->is_uref) {
+ if (need_uref) {
+ DPRINTFN(2, "ref udev - needed\n");
+ ploc->udev->refcount++;
+ } else {
+ DPRINTFN(2, "ref udev - not needed\n");
+ ploc->is_uref = 0;
+ }
+ }
+ mtx_unlock(&usb2_ref_lock);
+
+ if (ploc->is_uref) {
+ /*
+ * We are about to alter the bus-state. Apply the
+ * required locks.
+ */
+ sx_xlock(ploc->udev->default_sx + 1);
+ mtx_lock(&Giant); /* XXX */
+ }
+ return (0);
+
+error:
+ mtx_unlock(&usb2_ref_lock);
+ DPRINTFN(2, "fail\n");
+ return (USB_ERR_INVAL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_unref_device
+ *
+ * This function will release the reference count by one unit for the
+ * given USB device.
+ *------------------------------------------------------------------------*/
+void
+usb2_unref_device(struct usb2_location *ploc)
+{
+ if (ploc->is_uref) {
+ mtx_unlock(&Giant); /* XXX */
+ sx_unlock(ploc->udev->default_sx + 1);
+ }
+ mtx_lock(&usb2_ref_lock);
+ if (ploc->is_read) {
+ if (--(ploc->rxfifo->refcount) == 0) {
+ usb2_cv_signal(&ploc->rxfifo->cv_drain);
+ }
+ }
+ if (ploc->is_write) {
+ if (--(ploc->txfifo->refcount) == 0) {
+ usb2_cv_signal(&ploc->txfifo->cv_drain);
+ }
+ }
+ if (ploc->is_uref) {
+ if (--(ploc->udev->refcount) == 0) {
+ usb2_cv_signal(ploc->udev->default_cv + 1);
+ }
+ }
+ mtx_unlock(&usb2_ref_lock);
+ return;
+}
+
+static struct usb2_fifo *
+usb2_fifo_alloc(void)
+{
+ struct usb2_fifo *f;
+
+ f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
+ if (f) {
+ usb2_cv_init(&f->cv_io, "FIFO-IO");
+ usb2_cv_init(&f->cv_drain, "FIFO-DRAIN");
+ f->refcount = 1;
+ }
+ return (f);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_create
+ *------------------------------------------------------------------------*/
+static int
+usb2_fifo_create(struct usb2_location *ploc, uint32_t *pdevloc, int fflags)
+{
+ struct usb2_device *udev = ploc->udev;
+ struct usb2_fifo *f;
+ struct usb2_pipe *pipe;
+ uint8_t iface_index = ploc->iface_index;
+ uint8_t dev_ep_index = ploc->ep_index;
+ uint8_t n;
+ uint8_t is_tx;
+ uint8_t is_rx;
+ uint8_t no_null;
+ uint8_t is_busy;
+
+ is_tx = (fflags & FWRITE) ? 1 : 0;
+ is_rx = (fflags & FREAD) ? 1 : 0;
+ no_null = 1;
+ is_busy = 0;
+
+ /* search for a free FIFO slot */
+
+ for (n = 0;; n += 2) {
+
+ if (n == USB_FIFO_MAX) {
+ if (no_null) {
+ no_null = 0;
+ n = 0;
+ } else {
+ /* end of FIFOs reached */
+ return (ENOMEM);
+ }
+ }
+ /* Check for TX FIFO */
+ if (is_tx) {
+ f = udev->fifo[n + USB_FIFO_TX];
+ if (f != NULL) {
+ if (f->dev_ep_index != dev_ep_index) {
+ /* wrong endpoint index */
+ continue;
+ }
+ if ((dev_ep_index != 0) &&
+ (f->iface_index != iface_index)) {
+ /* wrong interface index */
+ continue;
+ }
+ if (f->curr_file != NULL) {
+ /* FIFO is opened */
+ is_busy = 1;
+ continue;
+ }
+ } else if (no_null) {
+ continue;
+ }
+ }
+ /* Check for RX FIFO */
+ if (is_rx) {
+ f = udev->fifo[n + USB_FIFO_RX];
+ if (f != NULL) {
+ if (f->dev_ep_index != dev_ep_index) {
+ /* wrong endpoint index */
+ continue;
+ }
+ if ((dev_ep_index != 0) &&
+ (f->iface_index != iface_index)) {
+ /* wrong interface index */
+ continue;
+ }
+ if (f->curr_file != NULL) {
+ /* FIFO is opened */
+ is_busy = 1;
+ continue;
+ }
+ } else if (no_null) {
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (no_null == 0) {
+ if (dev_ep_index >= (USB_EP_MAX / 2)) {
+ /* we don't create any endpoints in this range */
+ return (is_busy ? EBUSY : EINVAL);
+ }
+ }
+ /* Check TX FIFO */
+ if (is_tx &&
+ (udev->fifo[n + USB_FIFO_TX] == NULL)) {
+ pipe = usb2_dev_get_pipe(udev,
+ iface_index, dev_ep_index, USB_FIFO_TX);
+ if (pipe == NULL) {
+ return (EINVAL);
+ }
+ f = usb2_fifo_alloc();
+ if (f == NULL) {
+ return (ENOMEM);
+ }
+ /* update some fields */
+ f->fifo_index = n + USB_FIFO_TX;
+ f->dev_ep_index = dev_ep_index;
+ f->priv_mtx = udev->default_mtx;
+ f->priv_sc0 = pipe;
+ f->methods = &usb2_ugen_methods;
+ f->iface_index = iface_index;
+ f->udev = udev;
+ if (dev_ep_index != 0) {
+ f->flag_no_uref = 1;
+ }
+ mtx_lock(&usb2_ref_lock);
+ udev->fifo[n + USB_FIFO_TX] = f;
+ mtx_unlock(&usb2_ref_lock);
+ }
+ /* Check RX FIFO */
+ if (is_rx &&
+ (udev->fifo[n + USB_FIFO_RX] == NULL)) {
+
+ pipe = usb2_dev_get_pipe(udev,
+ iface_index, dev_ep_index, USB_FIFO_RX);
+ if (pipe == NULL) {
+ return (EINVAL);
+ }
+ f = usb2_fifo_alloc();
+ if (f == NULL) {
+ return (ENOMEM);
+ }
+ /* update some fields */
+ f->fifo_index = n + USB_FIFO_RX;
+ f->dev_ep_index = dev_ep_index;
+ f->priv_mtx = udev->default_mtx;
+ f->priv_sc0 = pipe;
+ f->methods = &usb2_ugen_methods;
+ f->iface_index = iface_index;
+ f->udev = udev;
+ if (dev_ep_index != 0) {
+ f->flag_no_uref = 1;
+ }
+ mtx_lock(&usb2_ref_lock);
+ udev->fifo[n + USB_FIFO_RX] = f;
+ mtx_unlock(&usb2_ref_lock);
+ }
+ if (is_tx) {
+ ploc->txfifo = udev->fifo[n + USB_FIFO_TX];
+ }
+ if (is_rx) {
+ ploc->rxfifo = udev->fifo[n + USB_FIFO_RX];
+ }
+ /* replace endpoint index by FIFO index */
+
+ (*pdevloc) %= (USB_BUS_MAX * USB_DEV_MAX * USB_IFACE_MAX);
+ (*pdevloc) += (USB_BUS_MAX * USB_DEV_MAX * USB_IFACE_MAX) * n;
+
+ /* complete */
+
+ return (0);
+}
+
+void
+usb2_fifo_free(struct usb2_fifo *f)
+{
+ uint8_t n;
+
+ if (f == NULL) {
+ /* be NULL safe */
+ return;
+ }
+ /* destroy symlink devices, if any */
+ for (n = 0; n != 2; n++) {
+ if (f->symlink[n]) {
+ usb2_free_symlink(f->symlink[n]);
+ f->symlink[n] = NULL;
+ }
+ }
+ mtx_lock(&usb2_ref_lock);
+
+ /* delink ourselves to stop calls from userland */
+ if ((f->fifo_index < USB_FIFO_MAX) &&
+ (f->udev != NULL) &&
+ (f->udev->fifo[f->fifo_index] == f)) {
+ f->udev->fifo[f->fifo_index] = NULL;
+ } else {
+ DPRINTFN(0, "USB FIFO %p has not been linked!\n", 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) {
+ mtx_unlock(&usb2_ref_lock); /* avoid LOR */
+ mtx_lock(f->priv_mtx);
+ /* get I/O thread out of any sleep state */
+ if (f->flag_sleeping) {
+ f->flag_sleeping = 0;
+ usb2_cv_broadcast(&f->cv_io);
+ }
+ mtx_unlock(f->priv_mtx);
+ mtx_lock(&usb2_ref_lock);
+
+ /* wait for sync */
+ usb2_cv_wait(&f->cv_drain, &usb2_ref_lock);
+ }
+ mtx_unlock(&usb2_ref_lock);
+
+ /* take care of closing the device here, if any */
+ usb2_fifo_close(f, curthread, 0);
+
+ usb2_cv_destroy(&f->cv_io);
+ usb2_cv_destroy(&f->cv_drain);
+
+ free(f, M_USBDEV);
+ return;
+}
+
+static struct usb2_pipe *
+usb2_dev_get_pipe(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t ep_index, uint8_t dir)
+{
+ struct usb2_pipe *pipe;
+ uint8_t ep_dir;
+
+ if (ep_index == 0) {
+ pipe = &udev->default_pipe;
+ } else {
+ if (dir == USB_FIFO_RX) {
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ ep_dir = UE_DIR_IN;
+ } else {
+ ep_dir = UE_DIR_OUT;
+ }
+ } else {
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ ep_dir = UE_DIR_OUT;
+ } else {
+ ep_dir = UE_DIR_IN;
+ }
+ }
+ pipe = usb2_get_pipe_by_addr(udev, ep_index | ep_dir);
+ }
+
+ if (pipe == NULL) {
+ /* if the pipe does not exist then return */
+ return (NULL);
+ }
+ if (pipe->edesc == NULL) {
+ /* invalid pipe */
+ return (NULL);
+ }
+ if (ep_index != 0) {
+ if (pipe->iface_index != iface_index) {
+ /*
+ * Permissions violation - trying to access a
+ * pipe that does not belong to the interface.
+ */
+ return (NULL);
+ }
+ }
+ return (pipe); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_open
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static int
+usb2_fifo_open(struct usb2_fifo *f, struct file *fp, struct thread *td,
+ int fflags)
+{
+ int err;
+
+ if (f == NULL) {
+ /* no FIFO there */
+ DPRINTFN(2, "no FIFO\n");
+ return (ENXIO);
+ }
+ /* remove FWRITE and FREAD flags */
+ fflags &= ~(FWRITE | FREAD);
+
+ /* set correct file flags */
+ if ((f->fifo_index & 1) == USB_FIFO_TX) {
+ fflags |= FWRITE;
+ } else {
+ fflags |= FREAD;
+ }
+
+ /* check if we are already opened */
+ /* we don't need any locks when checking this variable */
+ if (f->curr_file) {
+ err = EBUSY;
+ goto done;
+ }
+ /* call open method */
+ err = (f->methods->f_open) (f, fflags, td);
+ if (err) {
+ goto done;
+ }
+ mtx_lock(f->priv_mtx);
+
+ /* reset sleep flag */
+ f->flag_sleeping = 0;
+
+ /* reset error flag */
+ f->flag_iserror = 0;
+
+ /* reset complete flag */
+ f->flag_iscomplete = 0;
+
+ /* reset select flag */
+ f->flag_isselect = 0;
+
+ /* reset flushing flag */
+ f->flag_flushing = 0;
+
+ /* reset ASYNC proc flag */
+ f->async_p = NULL;
+
+ /* set which file we belong to */
+ mtx_lock(&usb2_ref_lock);
+ f->curr_file = fp;
+ mtx_unlock(&usb2_ref_lock);
+
+ /* reset queue */
+ usb2_fifo_reset(f);
+
+ mtx_unlock(f->priv_mtx);
+done:
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_reset
+ *------------------------------------------------------------------------*/
+void
+usb2_fifo_reset(struct usb2_fifo *f)
+{
+ struct usb2_mbuf *m;
+
+ if (f == NULL) {
+ return;
+ }
+ while (1) {
+ USB_IF_DEQUEUE(&f->used_q, m);
+ if (m) {
+ USB_IF_ENQUEUE(&f->free_q, m);
+ } else {
+ break;
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_close
+ *------------------------------------------------------------------------*/
+static void
+usb2_fifo_close(struct usb2_fifo *f, struct thread *td, int fflags)
+{
+ int err;
+
+ /* check if we are not opened */
+ if (!f->curr_file) {
+ /* nothing to do - already closed */
+ return;
+ }
+ mtx_lock(f->priv_mtx);
+
+ /* clear current file flag */
+ f->curr_file = NULL;
+
+ /* check if we are selected */
+ if (f->flag_isselect) {
+ selwakeup(&f->selinfo);
+ f->flag_isselect = 0;
+ }
+ /* check if a thread wants SIGIO */
+ if (f->async_p != NULL) {
+ PROC_LOCK(f->async_p);
+ psignal(f->async_p, SIGIO);
+ PROC_UNLOCK(f->async_p);
+ f->async_p = NULL;
+ }
+ /* remove FWRITE and FREAD flags */
+ fflags &= ~(FWRITE | FREAD);
+
+ /* flush written data, if any */
+ if ((f->fifo_index & 1) == USB_FIFO_TX) {
+
+ if (!f->flag_iserror) {
+
+ /* set flushing flag */
+ f->flag_flushing = 1;
+
+ /* start write transfer, if not already started */
+ (f->methods->f_start_write) (f);
+
+ /* check if flushed already */
+ while (f->flag_flushing &&
+ (!f->flag_iserror)) {
+ /* wait until all data has been written */
+ f->flag_sleeping = 1;
+ err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx);
+ if (err) {
+ DPRINTF("signal received\n");
+ break;
+ }
+ }
+ }
+ fflags |= FWRITE;
+
+ /* stop write transfer, if not already stopped */
+ (f->methods->f_stop_write) (f);
+ } else {
+ fflags |= FREAD;
+
+ /* stop write transfer, if not already stopped */
+ (f->methods->f_stop_read) (f);
+ }
+
+ /* check if we are sleeping */
+ if (f->flag_sleeping) {
+ DPRINTFN(2, "Sleeping at close!\n");
+ }
+ mtx_unlock(f->priv_mtx);
+
+ /* call close method */
+ (f->methods->f_close) (f, fflags, td);
+
+ DPRINTF("closed\n");
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_check_thread_perm
+ *
+ * Returns:
+ * 0: Has permission.
+ * Else: No permission.
+ *------------------------------------------------------------------------*/
+int
+usb2_check_thread_perm(struct usb2_device *udev, struct thread *td,
+ int fflags, uint8_t iface_index, uint8_t ep_index)
+{
+ struct usb2_interface *iface;
+ int err;
+
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface == NULL) {
+ return (EINVAL);
+ }
+ if (iface->idesc == NULL) {
+ return (EINVAL);
+ }
+ /* scan down the permissions tree */
+ if ((ep_index != 0) && iface &&
+ (usb2_check_access(fflags, &iface->perm) == 0)) {
+ /* we got access through the interface */
+ err = 0;
+ } else if (udev &&
+ (usb2_check_access(fflags, &udev->perm) == 0)) {
+ /* we got access through the device */
+ err = 0;
+ } else if (udev->bus &&
+ (usb2_check_access(fflags, &udev->bus->perm) == 0)) {
+ /* we got access through the USB bus */
+ err = 0;
+ } else if (usb2_check_access(fflags, &usb2_perm) == 0) {
+ /* we got general access */
+ err = 0;
+ } else {
+ /* no access */
+ err = EPERM;
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fdopen - cdev callback
+ *------------------------------------------------------------------------*/
+static int
+usb2_fdopen(struct cdev *dev, int xxx_oflags, struct thread *td,
+ struct file *fp)
+{
+ struct usb2_location loc;
+ uint32_t devloc;
+ int err;
+ int fflags;
+
+ DPRINTFN(2, "oflags=0x%08x\n", xxx_oflags);
+
+ devloc = usb2_last_devloc;
+ usb2_last_devloc = (0 - 1); /* reset "usb2_last_devloc" */
+
+ if (fp == NULL) {
+ DPRINTFN(2, "fp == NULL\n");
+ return (ENXIO);
+ }
+ if (usb2_old_f_data != fp->f_data) {
+ if (usb2_old_f_data != NULL) {
+ DPRINTFN(0, "File data mismatch!\n");
+ return (ENXIO);
+ }
+ usb2_old_f_data = fp->f_data;
+ }
+ if (usb2_old_f_ops != fp->f_ops) {
+ if (usb2_old_f_ops != NULL) {
+ DPRINTFN(0, "File ops mismatch!\n");
+ return (ENXIO);
+ }
+ usb2_old_f_ops = fp->f_ops;
+ }
+ fflags = fp->f_flag;
+ DPRINTFN(2, "fflags=0x%08x\n", fflags);
+
+ if (!(fflags & (FREAD | FWRITE))) {
+ /* should not happen */
+ return (EPERM);
+ }
+ if (devloc == (uint32_t)(0 - 2)) {
+ /* tried to open "/dev/usb" */
+ return (0);
+ } else if (devloc == (uint32_t)(0 - 1)) {
+ /* tried to open "/dev/usb " */
+ DPRINTFN(2, "no devloc\n");
+ return (ENXIO);
+ }
+ err = usb2_ref_device(NULL, &loc, devloc);
+ if (err) {
+ DPRINTFN(2, "cannot ref device\n");
+ return (ENXIO);
+ }
+ err = usb2_check_thread_perm(loc.udev, td, fflags,
+ loc.iface_index, loc.ep_index);
+
+ /* check for error */
+ if (err) {
+ usb2_unref_device(&loc);
+ return (err);
+ }
+ /* create FIFOs, if any */
+ err = usb2_fifo_create(&loc, &devloc, fflags);
+ /* check for error */
+ if (err) {
+ usb2_unref_device(&loc);
+ return (err);
+ }
+ if (fflags & FREAD) {
+ err = usb2_fifo_open(loc.rxfifo, fp, td, fflags);
+ if (err) {
+ DPRINTFN(2, "read open failed\n");
+ usb2_unref_device(&loc);
+ return (err);
+ }
+ }
+ if (fflags & FWRITE) {
+ err = usb2_fifo_open(loc.txfifo, fp, td, fflags);
+ if (err) {
+ DPRINTFN(2, "write open failed\n");
+ if (fflags & FREAD) {
+ usb2_fifo_close(loc.rxfifo, td,
+ fflags);
+ }
+ usb2_unref_device(&loc);
+ return (err);
+ }
+ }
+ /*
+ * Take over the file so that we get all the callbacks
+ * directly and don't have to create another device:
+ */
+ finit(fp, fp->f_flag, DTYPE_VNODE,
+ ((uint8_t *)0) + devloc, &usb2_ops_f);
+
+ usb2_unref_device(&loc);
+
+ DPRINTFN(2, "error=%d\n", err);
+
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_close - cdev callback
+ *------------------------------------------------------------------------*/
+static int
+usb2_close(struct cdev *dev, int flag, int mode, struct thread *p)
+{
+ DPRINTF("\n");
+ return (0); /* nothing to do */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_close - cdev callback
+ *------------------------------------------------------------------------*/
+static int
+usb2_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int fflag, struct thread *td)
+{
+ union {
+ struct usb2_read_dir *urd;
+ struct usb2_dev_perm *udp;
+ void *data;
+ } u;
+ int err;
+
+ u.data = data;
+
+ switch (cmd) {
+ case USB_READ_DIR:
+ err = usb2_read_symlink(u.urd->urd_data,
+ u.urd->urd_startentry, u.urd->urd_maxlen);
+ break;
+ case USB_SET_IFACE_PERM:
+ err = usb2_set_perm(u.udp, 3);
+ break;
+ case USB_SET_DEVICE_PERM:
+ err = usb2_set_perm(u.udp, 2);
+ break;
+ case USB_SET_BUS_PERM:
+ err = usb2_set_perm(u.udp, 1);
+ break;
+ case USB_SET_ROOT_PERM:
+ err = usb2_set_perm(u.udp, 0);
+ break;
+ case USB_GET_IFACE_PERM:
+ err = usb2_get_perm(u.udp, 3);
+ break;
+ case USB_GET_DEVICE_PERM:
+ err = usb2_get_perm(u.udp, 2);
+ break;
+ case USB_GET_BUS_PERM:
+ err = usb2_get_perm(u.udp, 1);
+ break;
+ case USB_GET_ROOT_PERM:
+ err = usb2_get_perm(u.udp, 0);
+ break;
+ case USB_DEV_QUIRK_GET:
+ case USB_QUIRK_NAME_GET:
+ case USB_DEV_QUIRK_ADD:
+ case USB_DEV_QUIRK_REMOVE:
+ err = usb2_quirk_ioctl_p(cmd, data, fflag, td);
+ break;
+ default:
+ err = ENOTTY;
+ break;
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_clone - cdev callback
+ *
+ * This function is the kernel clone callback for "/dev/usbX.Y".
+ *
+ * NOTE: This function assumes that the clone and device open
+ * operation is atomic.
+ *------------------------------------------------------------------------*/
+static void
+usb2_clone(void *arg, USB_UCRED char *name, int namelen, struct cdev **dev)
+{
+ enum {
+ USB_DNAME_LEN = sizeof(USB_DEVICE_NAME) - 1,
+ USB_GNAME_LEN = sizeof(USB_GENERIC_NAME) - 1,
+ };
+
+ if (*dev) {
+ /* someone else has created a device */
+ return;
+ }
+ /* reset device location */
+ usb2_last_devloc = (uint32_t)(0 - 1);
+
+ /*
+ * Check if we are matching "usb", "ugen" or an internal
+ * symbolic link:
+ */
+ if ((namelen >= USB_DNAME_LEN) &&
+ (bcmp(name, USB_DEVICE_NAME, USB_DNAME_LEN) == 0)) {
+ if (namelen == USB_DNAME_LEN) {
+ /* USB management device location */
+ usb2_last_devloc = (uint32_t)(0 - 2);
+ } else {
+ /* USB endpoint */
+ usb2_last_devloc =
+ usb2_path_convert(name + USB_DNAME_LEN);
+ }
+ } else if ((namelen >= USB_GNAME_LEN) &&
+ (bcmp(name, USB_GENERIC_NAME, USB_GNAME_LEN) == 0)) {
+ if (namelen == USB_GNAME_LEN) {
+ /* USB management device location */
+ usb2_last_devloc = (uint32_t)(0 - 2);
+ } else {
+ /* USB endpoint */
+ usb2_last_devloc =
+ usb2_path_convert(name + USB_GNAME_LEN);
+ }
+ }
+ if (usb2_last_devloc == (uint32_t)(0 - 1)) {
+ /* Search for symbolic link */
+ usb2_last_devloc =
+ usb2_lookup_symlink(name, namelen);
+ }
+ if (usb2_last_devloc == (uint32_t)(0 - 1)) {
+ /* invalid location */
+ return;
+ }
+ dev_ref(usb2_dev);
+ *dev = usb2_dev;
+ return;
+}
+
+static void
+usb2_dev_init(void *arg)
+{
+ mtx_init(&usb2_ref_lock, "USB ref mutex", NULL, MTX_DEF);
+ sx_init(&usb2_sym_lock, "USB sym mutex");
+ TAILQ_INIT(&usb2_sym_head);
+
+ /* check the UGEN methods */
+ usb2_fifo_check_methods(&usb2_ugen_methods);
+ return;
+}
+
+SYSINIT(usb2_dev_init, SI_SUB_KLD, SI_ORDER_FIRST, usb2_dev_init, NULL);
+
+static void
+usb2_dev_init_post(void *arg)
+{
+ /*
+ * Create a dummy device so that we are visible. This device
+ * should never be opened. Therefore a space character is
+ * appended after the USB device name.
+ *
+ * NOTE: The permissions of this device is 0777, because we
+ * check the permissions again in the open routine against the
+ * real USB permissions which are not 0777. Else USB access
+ * will be limited to one user and one group.
+ */
+ usb2_dev = make_dev(&usb2_devsw, 0, UID_ROOT, GID_OPERATOR,
+ 0777, USB_DEVICE_NAME " ");
+ if (usb2_dev == NULL) {
+ DPRINTFN(0, "Could not create usb bus device!\n");
+ }
+ usb2_clone_tag = EVENTHANDLER_REGISTER(dev_clone, usb2_clone_ptr, NULL, 1000);
+ if (usb2_clone_tag == NULL) {
+ DPRINTFN(0, "Registering clone handler failed!\n");
+ }
+ return;
+}
+
+SYSINIT(usb2_dev_init_post, SI_SUB_KICK_SCHEDULER, SI_ORDER_FIRST, usb2_dev_init_post, NULL);
+
+static void
+usb2_dev_uninit(void *arg)
+{
+ if (usb2_clone_tag) {
+ EVENTHANDLER_DEREGISTER(dev_clone, usb2_clone_tag);
+ usb2_clone_tag = NULL;
+ }
+ if (usb2_dev) {
+ destroy_dev(usb2_dev);
+ usb2_dev = NULL;
+ }
+ mtx_destroy(&usb2_ref_lock);
+ sx_destroy(&usb2_sym_lock);
+ return;
+}
+
+SYSUNINIT(usb2_dev_uninit, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_dev_uninit, NULL);
+
+static int
+usb2_close_f(struct file *fp, struct thread *td)
+{
+ struct usb2_location loc;
+ int fflags;
+ int err;
+
+ fflags = fp->f_flag;
+
+ DPRINTFN(2, "fflags=%u\n", fflags);
+
+ err = usb2_ref_device(fp, &loc, 0);;
+
+ /* restore some file variables */
+ fp->f_ops = usb2_old_f_ops;
+ fp->f_data = usb2_old_f_data;
+
+ /* check for error */
+ if (err) {
+ DPRINTFN(2, "could not ref\n");
+ goto done;
+ }
+ if (fflags & FREAD) {
+ usb2_fifo_close(loc.rxfifo, td, fflags);
+ }
+ if (fflags & FWRITE) {
+ usb2_fifo_close(loc.txfifo, td, fflags);
+ }
+ usb2_unref_device(&loc);
+
+done:
+ /* call old close method */
+ USB_VNOPS_FO_CLOSE(fp, td, &err);
+
+ return (err);
+}
+
+static int
+usb2_ioctl_f_sub(struct usb2_fifo *f, u_long cmd, void *addr,
+ struct thread *td)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case FIODTYPE:
+ *(int *)addr = 0; /* character device */
+ break;
+
+ case FIONBIO:
+ /* handled by upper FS layer */
+ break;
+
+ case FIOASYNC:
+ if (*(int *)addr) {
+ if (f->async_p != NULL) {
+ error = EBUSY;
+ break;
+ }
+ f->async_p = USB_TD_GET_PROC(td);
+ } else {
+ f->async_p = NULL;
+ }
+ break;
+
+ /* XXX this is not the most general solution */
+ case TIOCSPGRP:
+ if (f->async_p == NULL) {
+ error = EINVAL;
+ break;
+ }
+ if (*(int *)addr != USB_PROC_GET_GID(f->async_p)) {
+ error = EPERM;
+ break;
+ }
+ break;
+ default:
+ return (ENOTTY);
+ }
+ return (error);
+}
+
+static int
+usb2_ioctl_f(struct file *fp, u_long cmd, void *addr,
+ struct ucred *cred, struct thread *td)
+{
+ struct usb2_location loc;
+ int fflags;
+ int err_rx;
+ int err_tx;
+ int err;
+ uint8_t is_common = 0;
+
+ err = usb2_ref_device(fp, &loc, 0);;
+ if (err) {
+ return (ENXIO);
+ }
+ fflags = fp->f_flag;
+
+ DPRINTFN(2, "fflags=%u, cmd=0x%lx\n", fflags, cmd);
+
+ if (fflags & FREAD) {
+ if (fflags & FWRITE) {
+ /*
+ * Make sure that the IOCTL is not
+ * duplicated:
+ */
+ is_common = 1;
+ }
+ err_rx = usb2_ioctl_f_sub(loc.rxfifo, cmd, addr, td);
+ if (err_rx == ENOTTY) {
+ err_rx = (loc.rxfifo->methods->f_ioctl) (
+ loc.rxfifo, cmd, addr,
+ is_common ? fflags : (fflags & ~FWRITE), td);
+ }
+ } else {
+ err_rx = 0;
+ }
+ if (fflags & FWRITE) {
+ err_tx = usb2_ioctl_f_sub(loc.txfifo, cmd, addr, td);
+ if (err_tx == ENOTTY) {
+ if (is_common)
+ err_tx = 0; /* already handled this IOCTL */
+ else
+ err_tx = (loc.txfifo->methods->f_ioctl) (
+ loc.txfifo, cmd, addr, fflags & ~FREAD, td);
+ }
+ } else {
+ err_tx = 0;
+ }
+
+ if (err_rx) {
+ err = err_rx;
+ } else if (err_tx) {
+ err = err_tx;
+ } else {
+ err = 0; /* no error */
+ }
+ usb2_unref_device(&loc);
+ return (err);
+}
+
+/* ARGSUSED */
+static int
+usb2_kqfilter_f(struct file *fp, struct knote *kn)
+{
+ return (ENXIO);
+}
+
+/* ARGSUSED */
+static int
+usb2_poll_f(struct file *fp, int events,
+ struct ucred *cred, struct thread *td)
+{
+ struct usb2_location loc;
+ struct usb2_fifo *f;
+ struct usb2_mbuf *m;
+ int fflags;
+ int revents;
+ uint8_t usbfs_active = 0;
+
+ revents = usb2_ref_device(fp, &loc, 1 /* no uref */ );;
+ if (revents) {
+ return (POLLHUP);
+ }
+ fflags = fp->f_flag;
+
+ /* figure out if the USB File System is active */
+
+ if (fflags & FWRITE) {
+ f = loc.txfifo;
+ if (f->fs_ep_max != 0) {
+ usbfs_active = 1;
+ }
+ }
+ if (fflags & FREAD) {
+ f = loc.rxfifo;
+ if (f->fs_ep_max != 0) {
+ usbfs_active = 1;
+ }
+ }
+ /* Figure out who needs service */
+
+ if ((events & (POLLOUT | POLLWRNORM)) &&
+ (fflags & FWRITE)) {
+
+ f = loc.txfifo;
+
+ mtx_lock(f->priv_mtx);
+
+ if (!usbfs_active) {
+ if (f->flag_iserror) {
+ /* we got an error */
+ m = (void *)1;
+ } else {
+ if (f->queue_data == NULL) {
+ /*
+ * start write transfer, if not
+ * already started
+ */
+ (f->methods->f_start_write) (f);
+ }
+ /* check if any packets are available */
+ USB_IF_POLL(&f->free_q, m);
+ }
+ } else {
+ if (f->flag_iscomplete) {
+ m = (void *)1;
+ } else {
+ m = NULL;
+ }
+ }
+
+ if (m) {
+ revents |= events & (POLLOUT | POLLWRNORM);
+ } else {
+ f->flag_isselect = 1;
+ selrecord(td, &f->selinfo);
+ }
+
+ mtx_unlock(f->priv_mtx);
+ }
+ if ((events & (POLLIN | POLLRDNORM)) &&
+ (fflags & FREAD)) {
+
+ f = loc.rxfifo;
+
+ mtx_lock(f->priv_mtx);
+
+ if (!usbfs_active) {
+ if (f->flag_iserror) {
+ /* we have and error */
+ m = (void *)1;
+ } else {
+ if (f->queue_data == NULL) {
+ /*
+ * start read transfer, if not
+ * already started
+ */
+ (f->methods->f_start_read) (f);
+ }
+ /* check if any packets are available */
+ USB_IF_POLL(&f->used_q, m);
+ }
+ } else {
+ if (f->flag_iscomplete) {
+ m = (void *)1;
+ } else {
+ m = NULL;
+ }
+ }
+
+ if (m) {
+ revents |= events & (POLLIN | POLLRDNORM);
+ } else {
+ f->flag_isselect = 1;
+ selrecord(td, &f->selinfo);
+
+ /* start reading data */
+ (f->methods->f_start_read) (f);
+ }
+
+ mtx_unlock(f->priv_mtx);
+ }
+ usb2_unref_device(&loc);
+ return (revents);
+}
+
+/* ARGSUSED */
+static int
+usb2_read_f(struct file *fp, struct uio *uio, struct ucred *cred,
+ int flags, struct thread *td)
+{
+ struct usb2_location loc;
+ struct usb2_fifo *f;
+ struct usb2_mbuf *m;
+ int fflags;
+ int resid;
+ int io_len;
+ int err;
+ uint8_t tr_data = 0;
+
+ DPRINTFN(2, "\n");
+
+ fflags = fp->f_flag & (O_NONBLOCK | O_DIRECT | FREAD | FWRITE);
+ if (fflags & O_DIRECT)
+ fflags |= IO_DIRECT;
+
+ err = usb2_ref_device(fp, &loc, 1 /* no uref */ );
+ if (err) {
+ return (ENXIO);
+ }
+ f = loc.rxfifo;
+ if (f == NULL) {
+ /* should not happen */
+ return (EPERM);
+ }
+ resid = uio->uio_resid;
+
+ if ((flags & FOF_OFFSET) == 0)
+ uio->uio_offset = fp->f_offset;
+
+ mtx_lock(f->priv_mtx);
+
+ if (f->flag_iserror) {
+ err = EIO;
+ goto done;
+ }
+ while (uio->uio_resid > 0) {
+
+ if (f->fs_ep_max == 0) {
+ USB_IF_DEQUEUE(&f->used_q, m);
+ } else {
+ /*
+ * The queue is used for events that should be
+ * retrieved using the "USB_FS_COMPLETE"
+ * ioctl.
+ */
+ m = NULL;
+ }
+
+ if (m == NULL) {
+
+ /* start read transfer, if not already started */
+
+ (f->methods->f_start_read) (f);
+
+ if (fflags & O_NONBLOCK) {
+ if (tr_data) {
+ /* return length before error */
+ break;
+ }
+ err = EWOULDBLOCK;
+ break;
+ }
+ DPRINTF("sleeping\n");
+
+ err = usb2_fifo_wait(f);
+ if (err) {
+ break;
+ }
+ continue;
+ } else {
+ tr_data = 1;
+ }
+
+ io_len = MIN(m->cur_data_len, uio->uio_resid);
+
+ DPRINTFN(2, "transfer %d bytes from %p\n",
+ io_len, m->cur_data_ptr);
+
+ err = usb2_fifo_uiomove(f,
+ m->cur_data_ptr, io_len, uio);
+
+ m->cur_data_len -= io_len;
+ m->cur_data_ptr += io_len;
+
+ if (m->cur_data_len == 0) {
+
+ uint8_t last_packet;
+
+ last_packet = m->last_packet;
+
+ USB_IF_ENQUEUE(&f->free_q, m);
+
+ if (last_packet) {
+ /* keep framing */
+ break;
+ }
+ } else {
+ USB_IF_PREPEND(&f->used_q, m);
+ }
+
+ if (err) {
+ break;
+ }
+ }
+done:
+ mtx_unlock(f->priv_mtx);
+
+ usb2_unref_device(&loc);
+
+ if ((flags & FOF_OFFSET) == 0)
+ fp->f_offset = uio->uio_offset;
+ fp->f_nextoff = uio->uio_offset;
+ return (err);
+}
+
+static int
+usb2_stat_f(struct file *fp, struct stat *sb, struct ucred *cred, struct thread *td)
+{
+ return (USB_VNOPS_FO_STAT(fp, sb, cred, td));
+}
+
+#if __FreeBSD_version > 800009
+static int
+usb2_truncate_f(struct file *fp, off_t length, struct ucred *cred, struct thread *td)
+{
+ return (USB_VNOPS_FO_TRUNCATE(fp, length, cred, td));
+}
+
+#endif
+
+/* ARGSUSED */
+static int
+usb2_write_f(struct file *fp, struct uio *uio, struct ucred *cred,
+ int flags, struct thread *td)
+{
+ struct usb2_location loc;
+ struct usb2_fifo *f;
+ struct usb2_mbuf *m;
+ int fflags;
+ int resid;
+ int io_len;
+ int err;
+ uint8_t tr_data = 0;
+
+ DPRINTFN(2, "\n");
+
+ fflags = fp->f_flag & (O_NONBLOCK | O_DIRECT |
+ FREAD | FWRITE | O_FSYNC);
+ if (fflags & O_DIRECT)
+ fflags |= IO_DIRECT;
+
+ err = usb2_ref_device(fp, &loc, 1 /* no uref */ );
+ if (err) {
+ return (ENXIO);
+ }
+ f = loc.txfifo;
+ if (f == NULL) {
+ /* should not happen */
+ usb2_unref_device(&loc);
+ return (EPERM);
+ }
+ resid = uio->uio_resid;
+
+ if ((flags & FOF_OFFSET) == 0)
+ uio->uio_offset = fp->f_offset;
+
+ mtx_lock(f->priv_mtx);
+
+ if (f->flag_iserror) {
+ err = EIO;
+ goto done;
+ }
+ if ((f->queue_data == NULL) && (f->fs_ep_max == 0)) {
+ /* start write transfer, if not already started */
+ (f->methods->f_start_write) (f);
+ }
+ /* we allow writing zero length data */
+ do {
+ if (f->fs_ep_max == 0) {
+ USB_IF_DEQUEUE(&f->free_q, m);
+ } else {
+ /*
+ * The queue is used for events that should be
+ * retrieved using the "USB_FS_COMPLETE"
+ * ioctl.
+ */
+ m = NULL;
+ }
+
+ if (m == NULL) {
+
+ if (fflags & O_NONBLOCK) {
+ if (tr_data) {
+ /* return length before error */
+ break;
+ }
+ err = EWOULDBLOCK;
+ break;
+ }
+ DPRINTF("sleeping\n");
+
+ err = usb2_fifo_wait(f);
+ if (err) {
+ break;
+ }
+ continue;
+ } else {
+ tr_data = 1;
+ }
+
+ USB_MBUF_RESET(m);
+
+ io_len = MIN(m->cur_data_len, uio->uio_resid);
+
+ m->cur_data_len = io_len;
+
+ DPRINTFN(2, "transfer %d bytes to %p\n",
+ io_len, m->cur_data_ptr);
+
+ err = usb2_fifo_uiomove(f,
+ m->cur_data_ptr, io_len, uio);
+
+ if (err) {
+ USB_IF_ENQUEUE(&f->free_q, m);
+ break;
+ } else {
+ USB_IF_ENQUEUE(&f->used_q, m);
+ (f->methods->f_start_write) (f);
+ }
+ } while (uio->uio_resid > 0);
+done:
+ mtx_unlock(f->priv_mtx);
+
+ usb2_unref_device(&loc);
+
+ if ((flags & FOF_OFFSET) == 0)
+ fp->f_offset = uio->uio_offset;
+ fp->f_nextoff = uio->uio_offset;
+
+ return (err);
+}
+
+static int
+usb2_fifo_uiomove(struct usb2_fifo *f, void *cp,
+ int n, struct uio *uio)
+{
+ int error;
+
+ mtx_unlock(f->priv_mtx);
+
+ /*
+ * "uiomove()" can sleep so one needs to make a wrapper,
+ * exiting the mutex and checking things:
+ */
+ error = uiomove(cp, n, uio);
+
+ mtx_lock(f->priv_mtx);
+
+ return (error);
+}
+
+int
+usb2_fifo_wait(struct usb2_fifo *f)
+{
+ int err;
+
+ mtx_assert(f->priv_mtx, MA_OWNED);
+
+ if (f->flag_iserror) {
+ /* we are gone */
+ return (EIO);
+ }
+ f->flag_sleeping = 1;
+
+ err = usb2_cv_wait_sig(&f->cv_io, f->priv_mtx);
+
+ if (f->flag_iserror) {
+ /* we are gone */
+ err = EIO;
+ }
+ return (err);
+}
+
+void
+usb2_fifo_signal(struct usb2_fifo *f)
+{
+ if (f->flag_sleeping) {
+ f->flag_sleeping = 0;
+ usb2_cv_broadcast(&f->cv_io);
+ }
+ return;
+}
+
+void
+usb2_fifo_wakeup(struct usb2_fifo *f)
+{
+ usb2_fifo_signal(f);
+
+ if (f->flag_isselect) {
+ selwakeup(&f->selinfo);
+ f->flag_isselect = 0;
+ }
+ if (f->async_p != NULL) {
+ PROC_LOCK(f->async_p);
+ psignal(f->async_p, SIGIO);
+ PROC_UNLOCK(f->async_p);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_opened
+ *
+ * Returns:
+ * 0: FIFO not opened.
+ * Else: FIFO is opened.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_fifo_opened(struct usb2_fifo *f)
+{
+ uint8_t temp;
+ uint8_t do_unlock;
+
+ if (f == NULL) {
+ return (0); /* be NULL safe */
+ }
+ if (mtx_owned(f->priv_mtx)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ mtx_lock(f->priv_mtx);
+ }
+ temp = f->curr_file ? 1 : 0;
+ if (do_unlock) {
+ mtx_unlock(f->priv_mtx);
+ }
+ return (temp);
+}
+
+
+static int
+usb2_fifo_dummy_open(struct usb2_fifo *fifo,
+ int fflags, struct thread *td)
+{
+ return (0);
+}
+
+static void
+usb2_fifo_dummy_close(struct usb2_fifo *fifo,
+ int fflags, struct thread *td)
+{
+ return;
+}
+
+static int
+usb2_fifo_dummy_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr,
+ int fflags, struct thread *td)
+{
+ return (ENOTTY);
+}
+
+static void
+usb2_fifo_dummy_cmd(struct usb2_fifo *fifo)
+{
+ fifo->flag_flushing = 0; /* not flushing */
+ return;
+}
+
+static void
+usb2_fifo_check_methods(struct usb2_fifo_methods *pm)
+{
+ /* check that all callback functions are OK */
+
+ if (pm->f_open == NULL)
+ pm->f_open = &usb2_fifo_dummy_open;
+
+ if (pm->f_close == NULL)
+ pm->f_close = &usb2_fifo_dummy_close;
+
+ if (pm->f_ioctl == NULL)
+ pm->f_ioctl = &usb2_fifo_dummy_ioctl;
+
+ if (pm->f_start_read == NULL)
+ pm->f_start_read = &usb2_fifo_dummy_cmd;
+
+ if (pm->f_stop_read == NULL)
+ pm->f_stop_read = &usb2_fifo_dummy_cmd;
+
+ if (pm->f_start_write == NULL)
+ pm->f_start_write = &usb2_fifo_dummy_cmd;
+
+ if (pm->f_stop_write == NULL)
+ pm->f_stop_write = &usb2_fifo_dummy_cmd;
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_attach
+ *
+ * The following function will create a duplex FIFO.
+ *
+ * Return values:
+ * 0: Success.
+ * Else: Failure.
+ *------------------------------------------------------------------------*/
+int
+usb2_fifo_attach(struct usb2_device *udev, void *priv_sc,
+ struct mtx *priv_mtx, struct usb2_fifo_methods *pm,
+ struct usb2_fifo_sc *f_sc, uint16_t unit, uint16_t subunit,
+ uint8_t iface_index)
+{
+ struct usb2_fifo *f_tx;
+ struct usb2_fifo *f_rx;
+ char buf[32];
+ char src[32];
+ uint8_t n;
+
+ f_sc->fp[USB_FIFO_TX] = NULL;
+ f_sc->fp[USB_FIFO_RX] = NULL;
+
+ if (pm == NULL)
+ return (EINVAL);
+
+ /* check the methods */
+ usb2_fifo_check_methods(pm);
+
+ if (priv_mtx == NULL)
+ priv_mtx = &Giant;
+
+ /* search for a free FIFO slot */
+ for (n = 0;; n += 2) {
+
+ if (n == USB_FIFO_MAX) {
+ /* end of FIFOs reached */
+ return (ENOMEM);
+ }
+ /* Check for TX FIFO */
+ if (udev->fifo[n + USB_FIFO_TX] != NULL) {
+ continue;
+ }
+ /* Check for RX FIFO */
+ if (udev->fifo[n + USB_FIFO_RX] != NULL) {
+ continue;
+ }
+ break;
+ }
+
+ f_tx = usb2_fifo_alloc();
+ f_rx = usb2_fifo_alloc();
+
+ if ((f_tx == NULL) || (f_rx == NULL)) {
+ usb2_fifo_free(f_tx);
+ usb2_fifo_free(f_rx);
+ return (ENOMEM);
+ }
+ /* initialise FIFO structures */
+
+ f_tx->fifo_index = n + USB_FIFO_TX;
+ f_tx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2);
+ f_tx->priv_mtx = priv_mtx;
+ f_tx->priv_sc0 = priv_sc;
+ f_tx->methods = pm;
+ f_tx->iface_index = iface_index;
+ f_tx->udev = udev;
+ f_tx->flag_no_uref = 1;
+
+ f_rx->fifo_index = n + USB_FIFO_RX;
+ f_rx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2);
+ f_rx->priv_mtx = priv_mtx;
+ f_rx->priv_sc0 = priv_sc;
+ f_rx->methods = pm;
+ f_rx->iface_index = iface_index;
+ f_rx->udev = udev;
+ f_rx->flag_no_uref = 1;
+
+ f_sc->fp[USB_FIFO_TX] = f_tx;
+ f_sc->fp[USB_FIFO_RX] = f_rx;
+
+ mtx_lock(&usb2_ref_lock);
+ udev->fifo[f_tx->fifo_index] = f_tx;
+ udev->fifo[f_rx->fifo_index] = f_rx;
+ mtx_unlock(&usb2_ref_lock);
+
+ if (snprintf(src, sizeof(src),
+ USB_DEVICE_NAME "%u.%u.%u.%u",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index,
+ iface_index,
+ f_tx->dev_ep_index)) {
+ /* ignore */
+ }
+ for (n = 0; n != 4; n++) {
+
+ if (pm->basename[n] == NULL) {
+ continue;
+ }
+ if (subunit == 0xFFFF) {
+ if (snprintf(buf, sizeof(buf),
+ "%s%u%s", pm->basename[n],
+ unit, pm->postfix[n] ?
+ pm->postfix[n] : "")) {
+ /* ignore */
+ }
+ } else {
+ if (snprintf(buf, sizeof(buf),
+ "%s%u.%u%s", pm->basename[n],
+ unit, subunit, pm->postfix[n] ?
+ pm->postfix[n] : "")) {
+ /* ignore */
+ }
+ }
+
+ /*
+ * Distribute the symbolic links into two FIFO structures:
+ */
+ if (n & 1) {
+ f_rx->symlink[n / 2] =
+ usb2_alloc_symlink(src, "%s", buf);
+ } else {
+ f_tx->symlink[n / 2] =
+ usb2_alloc_symlink(src, "%s", buf);
+ }
+ printf("Symlink: %s -> %s\n", buf, src);
+ }
+
+ DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx);
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_alloc_buffer
+ *
+ * Return values:
+ * 0: Success
+ * Else failure
+ *------------------------------------------------------------------------*/
+int
+usb2_fifo_alloc_buffer(struct usb2_fifo *f, uint32_t bufsize,
+ uint16_t nbuf)
+{
+ usb2_fifo_free_buffer(f);
+
+ /* allocate an endpoint */
+ f->free_q.ifq_maxlen = nbuf;
+ f->used_q.ifq_maxlen = nbuf;
+
+ f->queue_data = usb2_alloc_mbufs(
+ M_USBDEV, &f->free_q, bufsize, nbuf);
+
+ if ((f->queue_data == NULL) && bufsize && nbuf) {
+ return (ENOMEM);
+ }
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_free_buffer
+ *
+ * This function will free the buffers associated with a FIFO. This
+ * function can be called multiple times in a row.
+ *------------------------------------------------------------------------*/
+void
+usb2_fifo_free_buffer(struct usb2_fifo *f)
+{
+ if (f->queue_data) {
+ /* free old buffer */
+ free(f->queue_data, M_USBDEV);
+ f->queue_data = NULL;
+ }
+ /* reset queues */
+
+ bzero(&f->free_q, sizeof(f->free_q));
+ bzero(&f->used_q, sizeof(f->used_q));
+ return;
+}
+
+void
+usb2_fifo_detach(struct usb2_fifo_sc *f_sc)
+{
+ if (f_sc == NULL) {
+ return;
+ }
+ usb2_fifo_free(f_sc->fp[USB_FIFO_TX]);
+ usb2_fifo_free(f_sc->fp[USB_FIFO_RX]);
+
+ f_sc->fp[USB_FIFO_TX] = NULL;
+ f_sc->fp[USB_FIFO_RX] = NULL;
+
+ DPRINTFN(2, "detached %p\n", f_sc);
+
+ return;
+}
+
+uint32_t
+usb2_fifo_put_bytes_max(struct usb2_fifo *f)
+{
+ struct usb2_mbuf *m;
+ uint32_t len;
+
+ USB_IF_POLL(&f->free_q, m);
+
+ if (m) {
+ len = m->max_data_len;
+ } else {
+ len = 0;
+ }
+ return (len);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_put_data
+ *
+ * what:
+ * 0 - normal operation
+ * 1 - set last packet flag to enforce framing
+ *------------------------------------------------------------------------*/
+void
+usb2_fifo_put_data(struct usb2_fifo *f, struct usb2_page_cache *pc,
+ uint32_t offset, uint32_t len, uint8_t what)
+{
+ struct usb2_mbuf *m;
+ uint32_t io_len;
+
+ while (len || (what == 1)) {
+
+ USB_IF_DEQUEUE(&f->free_q, m);
+
+ if (m) {
+ USB_MBUF_RESET(m);
+
+ io_len = MIN(len, m->cur_data_len);
+
+ usb2_copy_out(pc, offset, m->cur_data_ptr, io_len);
+
+ m->cur_data_len = io_len;
+ offset += io_len;
+ len -= io_len;
+
+ if ((len == 0) && (what == 1)) {
+ m->last_packet = 1;
+ }
+ USB_IF_ENQUEUE(&f->used_q, m);
+
+ usb2_fifo_wakeup(f);
+
+ if ((len == 0) || (what == 1)) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return;
+}
+
+void
+usb2_fifo_put_data_linear(struct usb2_fifo *f, void *ptr,
+ uint32_t len, uint8_t what)
+{
+ struct usb2_mbuf *m;
+ uint32_t io_len;
+
+ while (len || (what == 1)) {
+
+ USB_IF_DEQUEUE(&f->free_q, m);
+
+ if (m) {
+ USB_MBUF_RESET(m);
+
+ io_len = MIN(len, m->cur_data_len);
+
+ bcopy(ptr, m->cur_data_ptr, io_len);
+
+ m->cur_data_len = io_len;
+ ptr = USB_ADD_BYTES(ptr, io_len);
+ len -= io_len;
+
+ if ((len == 0) && (what == 1)) {
+ m->last_packet = 1;
+ }
+ USB_IF_ENQUEUE(&f->used_q, m);
+
+ usb2_fifo_wakeup(f);
+
+ if ((len == 0) || (what == 1)) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return;
+}
+
+uint8_t
+usb2_fifo_put_data_buffer(struct usb2_fifo *f, void *ptr, uint32_t len)
+{
+ struct usb2_mbuf *m;
+
+ USB_IF_DEQUEUE(&f->free_q, m);
+
+ if (m) {
+ m->cur_data_len = len;
+ m->cur_data_ptr = ptr;
+ USB_IF_ENQUEUE(&f->used_q, m);
+ usb2_fifo_wakeup(f);
+ return (1);
+ }
+ return (0);
+}
+
+void
+usb2_fifo_put_data_error(struct usb2_fifo *f)
+{
+ f->flag_iserror = 1;
+ usb2_fifo_wakeup(f);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_get_data
+ *
+ * what:
+ * 0 - normal operation
+ * 1 - only get one "usb2_mbuf"
+ *
+ * returns:
+ * 0 - no more data
+ * 1 - data in buffer
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_fifo_get_data(struct usb2_fifo *f, struct usb2_page_cache *pc,
+ uint32_t offset, uint32_t len, uint32_t *actlen,
+ uint8_t what)
+{
+ struct usb2_mbuf *m;
+ uint32_t io_len;
+ uint8_t tr_data = 0;
+
+ actlen[0] = 0;
+
+ while (1) {
+
+ USB_IF_DEQUEUE(&f->used_q, m);
+
+ if (m) {
+
+ tr_data = 1;
+
+ io_len = MIN(len, m->cur_data_len);
+
+ usb2_copy_in(pc, offset, m->cur_data_ptr, io_len);
+
+ len -= io_len;
+ offset += io_len;
+ actlen[0] += io_len;
+ m->cur_data_ptr += io_len;
+ m->cur_data_len -= io_len;
+
+ if ((m->cur_data_len == 0) || (what == 1)) {
+ USB_IF_ENQUEUE(&f->free_q, m);
+
+ usb2_fifo_wakeup(f);
+
+ if (what == 1) {
+ break;
+ }
+ } else {
+ USB_IF_PREPEND(&f->used_q, m);
+ }
+ } else {
+
+ if (tr_data) {
+ /* wait for data to be written out */
+ break;
+ }
+ if (f->flag_flushing) {
+ f->flag_flushing = 0;
+ usb2_fifo_wakeup(f);
+ }
+ break;
+ }
+ if (len == 0) {
+ break;
+ }
+ }
+ return (tr_data);
+}
+
+uint8_t
+usb2_fifo_get_data_linear(struct usb2_fifo *f, void *ptr,
+ uint32_t len, uint32_t *actlen, uint8_t what)
+{
+ struct usb2_mbuf *m;
+ uint32_t io_len;
+ uint8_t tr_data = 0;
+
+ actlen[0] = 0;
+
+ while (1) {
+
+ USB_IF_DEQUEUE(&f->used_q, m);
+
+ if (m) {
+
+ tr_data = 1;
+
+ io_len = MIN(len, m->cur_data_len);
+
+ bcopy(m->cur_data_ptr, ptr, io_len);
+
+ len -= io_len;
+ ptr = USB_ADD_BYTES(ptr, io_len);
+ actlen[0] += io_len;
+ m->cur_data_ptr += io_len;
+ m->cur_data_len -= io_len;
+
+ if ((m->cur_data_len == 0) || (what == 1)) {
+ USB_IF_ENQUEUE(&f->free_q, m);
+
+ usb2_fifo_wakeup(f);
+
+ if (what == 1) {
+ break;
+ }
+ } else {
+ USB_IF_PREPEND(&f->used_q, m);
+ }
+ } else {
+
+ if (tr_data) {
+ /* wait for data to be written out */
+ break;
+ }
+ if (f->flag_flushing) {
+ f->flag_flushing = 0;
+ usb2_fifo_wakeup(f);
+ }
+ break;
+ }
+ if (len == 0) {
+ break;
+ }
+ }
+ return (tr_data);
+}
+
+uint8_t
+usb2_fifo_get_data_buffer(struct usb2_fifo *f, void **pptr, uint32_t *plen)
+{
+ struct usb2_mbuf *m;
+
+ USB_IF_DEQUEUE(&f->used_q, m);
+
+ if (m) {
+ *plen = m->cur_data_len;
+ *pptr = m->cur_data_ptr;
+
+ USB_IF_PREPEND(&f->used_q, m);
+ return (1);
+ }
+ return (0);
+}
+
+void
+usb2_fifo_get_data_next(struct usb2_fifo *f)
+{
+ struct usb2_mbuf *m;
+
+ USB_IF_DEQUEUE(&f->used_q, m);
+
+ if (m) {
+ USB_IF_ENQUEUE(&f->free_q, m);
+ usb2_fifo_wakeup(f);
+ }
+ return;
+}
+
+void
+usb2_fifo_get_data_error(struct usb2_fifo *f)
+{
+ f->flag_iserror = 1;
+ usb2_fifo_wakeup(f);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_alloc_symlink
+ *
+ * Return values:
+ * NULL: Failure
+ * Else: Pointer to symlink entry
+ *------------------------------------------------------------------------*/
+struct usb2_symlink *
+usb2_alloc_symlink(const char *target, const char *fmt,...)
+{
+ struct usb2_symlink *ps;
+ va_list ap;
+
+ ps = malloc(sizeof(*ps), M_USBDEV, M_WAITOK);
+ if (ps == NULL) {
+ return (ps);
+ }
+ strlcpy(ps->dst_path, target, sizeof(ps->dst_path));
+ ps->dst_len = strlen(ps->dst_path);
+
+ va_start(ap, fmt);
+ vsnrprintf(ps->src_path,
+ sizeof(ps->src_path), 32, fmt, ap);
+ va_end(ap);
+ ps->src_len = strlen(ps->src_path);
+
+ sx_xlock(&usb2_sym_lock);
+ TAILQ_INSERT_TAIL(&usb2_sym_head, ps, sym_entry);
+ sx_unlock(&usb2_sym_lock);
+ return (ps);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_free_symlink
+ *------------------------------------------------------------------------*/
+void
+usb2_free_symlink(struct usb2_symlink *ps)
+{
+ if (ps == NULL) {
+ return;
+ }
+ sx_xlock(&usb2_sym_lock);
+ TAILQ_REMOVE(&usb2_sym_head, ps, sym_entry);
+ sx_unlock(&usb2_sym_lock);
+
+ free(ps, M_USBDEV);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_lookup_symlink
+ *
+ * Return value:
+ * Numerical device location
+ *------------------------------------------------------------------------*/
+uint32_t
+usb2_lookup_symlink(const char *src_ptr, uint8_t src_len)
+{
+ enum {
+ USB_DNAME_LEN = sizeof(USB_DEVICE_NAME) - 1,
+ };
+ struct usb2_symlink *ps;
+ uint32_t temp;
+
+ sx_xlock(&usb2_sym_lock);
+
+ TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) {
+
+ if (src_len != ps->src_len)
+ continue;
+
+ if (memcmp(ps->src_path, src_ptr, src_len))
+ continue;
+
+ if (USB_DNAME_LEN > ps->dst_len)
+ continue;
+
+ if (memcmp(ps->dst_path, USB_DEVICE_NAME, USB_DNAME_LEN))
+ continue;
+
+ temp = usb2_path_convert(ps->dst_path + USB_DNAME_LEN);
+ sx_unlock(&usb2_sym_lock);
+
+ return (temp);
+ }
+ sx_unlock(&usb2_sym_lock);
+ return (0 - 1);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_read_symlink
+ *
+ * Return value:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+int
+usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
+{
+ struct usb2_symlink *ps;
+ uint32_t temp;
+ uint32_t delta = 0;
+ uint8_t len;
+ int error = 0;
+
+ sx_xlock(&usb2_sym_lock);
+
+ TAILQ_FOREACH(ps, &usb2_sym_head, sym_entry) {
+
+ /*
+ * Compute total length of source and destination symlink
+ * strings pluss one length byte and two NUL bytes:
+ */
+ temp = ps->src_len + ps->dst_len + 3;
+
+ if (temp > 255) {
+ /*
+ * Skip entry because this length cannot fit
+ * into one byte:
+ */
+ continue;
+ }
+ if (startentry != 0) {
+ /* decrement read offset */
+ startentry--;
+ continue;
+ }
+ if (temp > user_len) {
+ /* out of buffer space */
+ break;
+ }
+ len = temp;
+
+ /* copy out total length */
+
+ error = copyout(&len,
+ USB_ADD_BYTES(user_ptr, delta), 1);
+ if (error) {
+ break;
+ }
+ delta += 1;
+
+ /* copy out source string */
+
+ error = copyout(ps->src_path,
+ USB_ADD_BYTES(user_ptr, delta), ps->src_len);
+ if (error) {
+ break;
+ }
+ len = 0;
+ delta += ps->src_len;
+ error = copyout(&len,
+ USB_ADD_BYTES(user_ptr, delta), 1);
+ if (error) {
+ break;
+ }
+ delta += 1;
+
+ /* copy out destination string */
+
+ error = copyout(ps->dst_path,
+ USB_ADD_BYTES(user_ptr, delta), ps->dst_len);
+ if (error) {
+ break;
+ }
+ len = 0;
+ delta += ps->dst_len;
+ error = copyout(&len,
+ USB_ADD_BYTES(user_ptr, delta), 1);
+ if (error) {
+ break;
+ }
+ delta += 1;
+
+ user_len -= temp;
+ }
+
+ /* a zero length entry indicates the end */
+
+ if ((user_len != 0) && (error == 0)) {
+
+ len = 0;
+
+ error = copyout(&len,
+ USB_ADD_BYTES(user_ptr, delta), 1);
+ }
+ sx_unlock(&usb2_sym_lock);
+ return (error);
+}
diff --git a/sys/dev/usb2/core/usb2_dev.h b/sys/dev/usb2/core/usb2_dev.h
new file mode 100644
index 000000000000..859dd4ebb728
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_dev.h
@@ -0,0 +1,149 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_DEV_H_
+#define _USB2_DEV_H_
+
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/poll.h>
+#include <sys/signalvar.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+
+#define USB_FIFO_TX 0
+#define USB_FIFO_RX 1
+
+struct usb2_fifo;
+
+typedef int (usb2_fifo_open_t)(struct usb2_fifo *fifo, int fflags, struct thread *td);
+typedef void (usb2_fifo_close_t)(struct usb2_fifo *fifo, int fflags, struct thread *td);
+typedef int (usb2_fifo_ioctl_t)(struct usb2_fifo *fifo, u_long cmd, void *addr, int fflags, struct thread *td);
+typedef void (usb2_fifo_cmd_t)(struct usb2_fifo *fifo);
+
+struct usb2_symlink {
+ TAILQ_ENTRY(usb2_symlink) sym_entry;
+ char src_path[32]; /* Source path - including terminating
+ * zero */
+ char dst_path[32]; /* Destination path - including
+ * terminating zero */
+ uint8_t src_len; /* String length */
+ uint8_t dst_len; /* String length */
+};
+
+/*
+ * Locking note for the following functions. All the
+ * "usb2_fifo_cmd_t" functions are called locked. The others are
+ * called unlocked.
+ */
+struct usb2_fifo_methods {
+ usb2_fifo_open_t *f_open;
+ usb2_fifo_close_t *f_close;
+ usb2_fifo_ioctl_t *f_ioctl;
+ usb2_fifo_cmd_t *f_start_read;
+ usb2_fifo_cmd_t *f_stop_read;
+ usb2_fifo_cmd_t *f_start_write;
+ usb2_fifo_cmd_t *f_stop_write;
+ const char *basename[4];
+ const char *postfix[4];
+};
+
+/*
+ * Most of the fields in the "usb2_fifo" structure are used by the
+ * generic USB access layer.
+ */
+struct usb2_fifo {
+ struct usb2_ifqueue free_q;
+ struct usb2_ifqueue used_q;
+ struct selinfo selinfo;
+ struct cv cv_io;
+ struct cv cv_drain;
+ struct usb2_fifo_methods *methods;
+ struct usb2_symlink *symlink[2];/* our symlinks */
+ struct proc *async_p; /* process that wants SIGIO */
+ struct usb2_fs_endpoint *fs_ep_ptr;
+ struct usb2_device *udev;
+ struct usb2_xfer *xfer[2];
+ struct usb2_xfer **fs_xfer;
+ struct mtx *priv_mtx; /* client data */
+ struct file *curr_file; /* set if FIFO is opened by a FILE */
+ void *priv_sc0; /* client data */
+ void *priv_sc1; /* client data */
+ void *queue_data;
+ uint32_t timeout; /* timeout in milliseconds */
+ uint32_t bufsize; /* BULK and INTERRUPT buffer size */
+ uint16_t nframes; /* for isochronous mode */
+ uint16_t dev_ep_index; /* our device endpoint index */
+ uint8_t flag_no_uref; /* set if FIFO is not control endpoint */
+ uint8_t flag_sleeping; /* set if FIFO is sleeping */
+ uint8_t flag_iscomplete; /* set if a USB transfer is complete */
+ uint8_t flag_iserror; /* set if FIFO error happened */
+ uint8_t flag_isselect; /* set if FIFO is selected */
+ uint8_t flag_flushing; /* set if FIFO is flushing data */
+ uint8_t flag_short; /* set if short_ok or force_short
+ * transfer flags should be set */
+ uint8_t flag_stall; /* set if clear stall should be run */
+ uint8_t iface_index; /* set to the interface we belong to */
+ uint8_t fifo_index; /* set to the FIFO index in "struct
+ * usb2_device" */
+ uint8_t fs_ep_max;
+ uint8_t fifo_zlp; /* zero length packet count */
+ uint8_t refcount;
+#define USB_FIFO_REF_MAX 0xFF
+};
+
+struct usb2_fifo_sc {
+ struct usb2_fifo *fp[2];
+};
+
+int usb2_fifo_wait(struct usb2_fifo *fifo);
+void usb2_fifo_signal(struct usb2_fifo *fifo);
+int usb2_fifo_alloc_buffer(struct usb2_fifo *f, uint32_t bufsize, uint16_t nbuf);
+void usb2_fifo_free_buffer(struct usb2_fifo *f);
+int usb2_fifo_attach(struct usb2_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb2_fifo_methods *pm, struct usb2_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, uint8_t iface_index);
+void usb2_fifo_detach(struct usb2_fifo_sc *f_sc);
+uint32_t usb2_fifo_put_bytes_max(struct usb2_fifo *fifo);
+void usb2_fifo_put_data(struct usb2_fifo *fifo, struct usb2_page_cache *pc, uint32_t offset, uint32_t len, uint8_t what);
+void usb2_fifo_put_data_linear(struct usb2_fifo *fifo, void *ptr, uint32_t len, uint8_t what);
+uint8_t usb2_fifo_put_data_buffer(struct usb2_fifo *f, void *ptr, uint32_t len);
+void usb2_fifo_put_data_error(struct usb2_fifo *fifo);
+uint8_t usb2_fifo_get_data(struct usb2_fifo *fifo, struct usb2_page_cache *pc, uint32_t offset, uint32_t len, uint32_t *actlen, uint8_t what);
+uint8_t usb2_fifo_get_data_linear(struct usb2_fifo *fifo, void *ptr, uint32_t len, uint32_t *actlen, uint8_t what);
+uint8_t usb2_fifo_get_data_buffer(struct usb2_fifo *f, void **pptr, uint32_t *plen);
+void usb2_fifo_get_data_next(struct usb2_fifo *f);
+void usb2_fifo_get_data_error(struct usb2_fifo *fifo);
+uint8_t usb2_fifo_opened(struct usb2_fifo *fifo);
+void usb2_fifo_free(struct usb2_fifo *f);
+void usb2_fifo_reset(struct usb2_fifo *f);
+int usb2_check_thread_perm(struct usb2_device *udev, struct thread *td, int fflags, uint8_t iface_index, uint8_t ep_index);
+void usb2_fifo_wakeup(struct usb2_fifo *f);
+struct usb2_symlink *usb2_alloc_symlink(const char *target, const char *fmt,...);
+void usb2_free_symlink(struct usb2_symlink *ps);
+uint32_t usb2_lookup_symlink(const char *src_ptr, uint8_t src_len);
+int usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len);
+
+#endif /* _USB2_DEV_H_ */
diff --git a/sys/dev/usb2/core/usb2_device.c b/sys/dev/usb2/core/usb2_device.c
new file mode 100644
index 000000000000..934886525f55
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_device.c
@@ -0,0 +1,2110 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_ioctl.h>
+#include <dev/usb2/include/usb2_devid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_parse.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_dynamic.h>
+#include <dev/usb2/core/usb2_hub.h>
+#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_mbuf.h>
+#include <dev/usb2/core/usb2_dev.h>
+#include <dev/usb2/core/usb2_msctest.h>
+
+#include <dev/usb2/quirk/usb2_quirk.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+/* function prototypes */
+
+static void usb2_fill_pipe_data(struct usb2_device *udev, uint8_t iface_index, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe);
+static void usb2_free_pipe_data(struct usb2_device *udev, uint8_t iface_index, uint8_t iface_mask);
+static void usb2_free_iface_data(struct usb2_device *udev);
+static void usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev, uint8_t free_subdev);
+static uint8_t usb2_probe_and_attach_sub(struct usb2_device *udev, struct usb2_attach_arg *uaa);
+static void usb2_init_attach_arg(struct usb2_device *udev, struct usb2_attach_arg *uaa);
+static void usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspend);
+static void usb2_clear_stall_proc(struct usb2_proc_msg *_pm);
+static void usb2_check_strings(struct usb2_device *udev);
+static usb2_error_t usb2_fill_iface_data(struct usb2_device *udev, uint8_t iface_index, uint8_t alt_index);
+static void usb2_notify_addq(const char *type, struct usb2_device *udev);
+static void usb2_fifo_free_wrap(struct usb2_device *udev, uint8_t iface_index, uint8_t free_all);
+
+/* static structures */
+
+static const uint8_t usb2_hub_speed_combs[USB_SPEED_MAX][USB_SPEED_MAX] = {
+ /* HUB *//* subdevice */
+ [USB_SPEED_HIGH][USB_SPEED_HIGH] = 1,
+ [USB_SPEED_HIGH][USB_SPEED_FULL] = 1,
+ [USB_SPEED_HIGH][USB_SPEED_LOW] = 1,
+ [USB_SPEED_FULL][USB_SPEED_FULL] = 1,
+ [USB_SPEED_FULL][USB_SPEED_LOW] = 1,
+ [USB_SPEED_LOW][USB_SPEED_LOW] = 1,
+};
+
+/* This variable is global to allow easy access to it: */
+
+int usb2_template = 0;
+
+SYSCTL_INT(_hw_usb2, OID_AUTO, template, CTLFLAG_RW,
+ &usb2_template, 0, "Selected USB device side template");
+
+
+/*------------------------------------------------------------------------*
+ * usb2_get_pipe_by_addr
+ *
+ * This function searches for an USB pipe by endpoint address and
+ * direction.
+ *
+ * Returns:
+ * NULL: Failure
+ * Else: Success
+ *------------------------------------------------------------------------*/
+struct usb2_pipe *
+usb2_get_pipe_by_addr(struct usb2_device *udev, uint8_t ea_val)
+{
+ struct usb2_pipe *pipe = udev->pipes;
+ struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX;
+ enum {
+ EA_MASK = (UE_DIR_IN | UE_DIR_OUT | UE_ADDR),
+ };
+
+ /*
+ * According to the USB specification not all bits are used
+ * for the endpoint address. Keep defined bits only:
+ */
+ ea_val &= EA_MASK;
+
+ /*
+ * Iterate accross all the USB pipes searching for a match
+ * based on the endpoint address:
+ */
+ for (; pipe != pipe_end; pipe++) {
+
+ if (pipe->edesc == NULL) {
+ continue;
+ }
+ /* do the mask and check the value */
+ if ((pipe->edesc->bEndpointAddress & EA_MASK) == ea_val) {
+ goto found;
+ }
+ }
+
+ /*
+ * The default pipe is always present and is checked separately:
+ */
+ if ((udev->default_pipe.edesc) &&
+ ((udev->default_pipe.edesc->bEndpointAddress & EA_MASK) == ea_val)) {
+ pipe = &udev->default_pipe;
+ goto found;
+ }
+ return (NULL);
+
+found:
+ return (pipe);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_pipe
+ *
+ * This function searches for an USB pipe based on the information
+ * given by the passed "struct usb2_config" pointer.
+ *
+ * Return values:
+ * NULL: No match.
+ * Else: Pointer to "struct usb2_pipe".
+ *------------------------------------------------------------------------*/
+struct usb2_pipe *
+usb2_get_pipe(struct usb2_device *udev, uint8_t iface_index,
+ const struct usb2_config *setup)
+{
+ struct usb2_pipe *pipe = udev->pipes;
+ struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX;
+ uint8_t index = setup->ep_index;
+ uint8_t ea_mask;
+ uint8_t ea_val;
+ uint8_t type_mask;
+ uint8_t type_val;
+
+ DPRINTFN(10, "udev=%p iface_index=%d address=0x%x "
+ "type=0x%x dir=0x%x index=%d\n",
+ udev, iface_index, setup->endpoint,
+ setup->type, setup->direction, setup->ep_index);
+
+ /* setup expected endpoint direction mask and value */
+
+ if (setup->direction == UE_DIR_ANY) {
+ /* match any endpoint direction */
+ ea_mask = 0;
+ ea_val = 0;
+ } else {
+ /* match the given endpoint direction */
+ ea_mask = (UE_DIR_IN | UE_DIR_OUT);
+ ea_val = (setup->direction & (UE_DIR_IN | UE_DIR_OUT));
+ }
+
+ /* setup expected endpoint address */
+
+ if (setup->endpoint == UE_ADDR_ANY) {
+ /* match any endpoint address */
+ } else {
+ /* match the given endpoint address */
+ ea_mask |= UE_ADDR;
+ ea_val |= (setup->endpoint & UE_ADDR);
+ }
+
+ /* setup expected endpoint type */
+
+ if (setup->type == UE_BULK_INTR) {
+ /* this will match BULK and INTERRUPT endpoints */
+ type_mask = 2;
+ type_val = 2;
+ } else if (setup->type == UE_TYPE_ANY) {
+ /* match any endpoint type */
+ type_mask = 0;
+ type_val = 0;
+ } else {
+ /* match the given endpoint type */
+ type_mask = UE_XFERTYPE;
+ type_val = (setup->type & UE_XFERTYPE);
+ }
+
+ /*
+ * Iterate accross all the USB pipes searching for a match
+ * based on the endpoint address. Note that we are searching
+ * the pipes from the beginning of the "udev->pipes" array.
+ */
+ for (; pipe != pipe_end; pipe++) {
+
+ if ((pipe->edesc == NULL) ||
+ (pipe->iface_index != iface_index)) {
+ continue;
+ }
+ /* do the masks and check the values */
+
+ if (((pipe->edesc->bEndpointAddress & ea_mask) == ea_val) &&
+ ((pipe->edesc->bmAttributes & type_mask) == type_val)) {
+ if (!index--) {
+ goto found;
+ }
+ }
+ }
+
+ /*
+ * Match against default pipe last, so that "any pipe", "any
+ * address" and "any direction" returns the first pipe of the
+ * interface. "iface_index" and "direction" is ignored:
+ */
+ if ((udev->default_pipe.edesc) &&
+ ((udev->default_pipe.edesc->bEndpointAddress & ea_mask) == ea_val) &&
+ ((udev->default_pipe.edesc->bmAttributes & type_mask) == type_val) &&
+ (!index)) {
+ pipe = &udev->default_pipe;
+ goto found;
+ }
+ return (NULL);
+
+found:
+ return (pipe);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_interface_count
+ *
+ * This function stores the number of USB interfaces excluding
+ * alternate settings, which the USB config descriptor reports into
+ * the unsigned 8-bit integer pointed to by "count".
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_interface_count(struct usb2_device *udev, uint8_t *count)
+{
+ if (udev->cdesc == NULL) {
+ *count = 0;
+ return (USB_ERR_NOT_CONFIGURED);
+ }
+ *count = udev->cdesc->bNumInterface;
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+
+/*------------------------------------------------------------------------*
+ * usb2_fill_pipe_data
+ *
+ * This function will initialise the USB pipe structure pointed to by
+ * the "pipe" argument.
+ *------------------------------------------------------------------------*/
+static void
+usb2_fill_pipe_data(struct usb2_device *udev, uint8_t iface_index,
+ struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe)
+{
+ bzero(pipe, sizeof(*pipe));
+
+ (udev->bus->methods->pipe_init) (udev, edesc, pipe);
+
+ if (pipe->methods == NULL) {
+ /* the pipe is invalid: just return */
+ return;
+ }
+ /* initialise USB pipe structure */
+ pipe->edesc = edesc;
+ pipe->iface_index = iface_index;
+ TAILQ_INIT(&pipe->pipe_q.head);
+ pipe->pipe_q.command = &usb2_pipe_start;
+
+ /* clear stall, if any */
+ if (udev->bus->methods->clear_stall) {
+ mtx_lock(&udev->bus->mtx);
+ (udev->bus->methods->clear_stall) (udev, pipe);
+ mtx_unlock(&udev->bus->mtx);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_free_pipe_data
+ *
+ * This function will free USB pipe data for the given interface
+ * index. Hence we do not have any dynamic allocations we simply clear
+ * "pipe->edesc" to indicate that the USB pipe structure can be
+ * reused. The pipes belonging to the given interface should not be in
+ * use when this function is called and no check is performed to
+ * prevent this.
+ *------------------------------------------------------------------------*/
+static void
+usb2_free_pipe_data(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t iface_mask)
+{
+ struct usb2_pipe *pipe = udev->pipes;
+ struct usb2_pipe *pipe_end = udev->pipes + USB_EP_MAX;
+
+ while (pipe != pipe_end) {
+ if ((pipe->iface_index & iface_mask) == iface_index) {
+ /* free pipe */
+ pipe->edesc = NULL;
+ }
+ pipe++;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fill_iface_data
+ *
+ * This function will fill in interface data and allocate USB pipes
+ * for all the endpoints that belong to the given interface. This
+ * function is typically called when setting the configuration or when
+ * setting an alternate interface.
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_fill_iface_data(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t alt_index)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_pipe *pipe;
+ struct usb2_pipe *pipe_end;
+ struct usb2_interface_descriptor *id;
+ struct usb2_endpoint_descriptor *ed = NULL;
+ struct usb2_descriptor *desc;
+ uint8_t nendpt;
+
+ if (iface == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ DPRINTFN(5, "iface_index=%d alt_index=%d\n",
+ iface_index, alt_index);
+
+ sx_assert(udev->default_sx + 1, SA_LOCKED);
+
+ pipe = udev->pipes;
+ pipe_end = udev->pipes + USB_EP_MAX;
+
+ /*
+ * Check if any USB pipes on the given USB interface are in
+ * use:
+ */
+ while (pipe != pipe_end) {
+ if ((pipe->edesc != NULL) &&
+ (pipe->iface_index == iface_index) &&
+ (pipe->refcount != 0)) {
+ return (USB_ERR_IN_USE);
+ }
+ pipe++;
+ }
+
+ pipe = &udev->pipes[0];
+
+ id = usb2_find_idesc(udev->cdesc, iface_index, alt_index);
+ if (id == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ /*
+ * Free old pipes after we know that an interface descriptor exists,
+ * if any.
+ */
+ usb2_free_pipe_data(udev, iface_index, 0 - 1);
+
+ /* Setup USB interface structure */
+ iface->idesc = id;
+ iface->alt_index = alt_index;
+ iface->parent_iface_index = USB_IFACE_INDEX_ANY;
+
+ nendpt = id->bNumEndpoints;
+ DPRINTFN(5, "found idesc nendpt=%d\n", nendpt);
+
+ desc = (void *)id;
+
+ while (nendpt--) {
+ DPRINTFN(11, "endpt=%d\n", nendpt);
+
+ while ((desc = usb2_desc_foreach(udev->cdesc, desc))) {
+ if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
+ (desc->bLength >= sizeof(*ed))) {
+ goto found;
+ }
+ if (desc->bDescriptorType == UDESC_INTERFACE) {
+ break;
+ }
+ }
+ goto error;
+
+found:
+ ed = (void *)desc;
+
+ /* find a free pipe */
+ while (pipe != pipe_end) {
+ if (pipe->edesc == NULL) {
+ /* pipe is free */
+ usb2_fill_pipe_data(udev, iface_index, ed, pipe);
+ break;
+ }
+ pipe++;
+ }
+ }
+ return (USB_ERR_NORMAL_COMPLETION);
+
+error:
+ /* passed end, or bad desc */
+ DPRINTFN(0, "%s: bad descriptor(s), addr=%d!\n",
+ __FUNCTION__, udev->address);
+
+ /* free old pipes if any */
+ usb2_free_pipe_data(udev, iface_index, 0 - 1);
+ return (USB_ERR_INVAL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_free_iface_data
+ *
+ * This function will free all USB interfaces and USB pipes belonging
+ * to an USB device.
+ *------------------------------------------------------------------------*/
+static void
+usb2_free_iface_data(struct usb2_device *udev)
+{
+ struct usb2_interface *iface = udev->ifaces;
+ struct usb2_interface *iface_end = udev->ifaces + USB_IFACE_MAX;
+
+ /* mtx_assert() */
+
+ /* free Linux compat device, if any */
+ if (udev->linux_dev) {
+ usb_linux_free_device(udev->linux_dev);
+ udev->linux_dev = NULL;
+ }
+ /* free all pipes, if any */
+ usb2_free_pipe_data(udev, 0, 0);
+
+ /* free all interfaces, if any */
+ while (iface != iface_end) {
+ iface->idesc = NULL;
+ iface->alt_index = 0;
+ iface->parent_iface_index = USB_IFACE_INDEX_ANY;
+ iface->perm.mode = 0; /* disable permissions */
+ iface++;
+ }
+
+ /* free "cdesc" after "ifaces", if any */
+ if (udev->cdesc) {
+ free(udev->cdesc, M_USB);
+ udev->cdesc = NULL;
+ }
+ /* set unconfigured state */
+ udev->curr_config_no = USB_UNCONFIG_NO;
+ udev->curr_config_index = USB_UNCONFIG_INDEX;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_config_index
+ *
+ * This function selects configuration by index, independent of the
+ * actual configuration number. This function should not be used by
+ * USB drivers.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_set_config_index(struct usb2_device *udev, uint8_t index)
+{
+ struct usb2_status ds;
+ struct usb2_hub_descriptor hd;
+ struct usb2_config_descriptor *cdp;
+ uint16_t power;
+ uint16_t max_power;
+ uint8_t nifc;
+ uint8_t selfpowered;
+ uint8_t do_unlock;
+ usb2_error_t err;
+
+ DPRINTFN(6, "udev=%p index=%d\n", udev, index);
+
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+
+ /* detach all interface drivers */
+ usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 1);
+
+ /* free all FIFOs except control endpoint FIFOs */
+ usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 0);
+
+ /* free all configuration data structures */
+ usb2_free_iface_data(udev);
+
+ if (index == USB_UNCONFIG_INDEX) {
+ /*
+ * Leave unallocated when unconfiguring the
+ * device. "usb2_free_iface_data()" will also reset
+ * the current config number and index.
+ */
+ err = usb2_req_set_config(udev, &Giant, USB_UNCONFIG_NO);
+ goto done;
+ }
+ /* get the full config descriptor */
+ err = usb2_req_get_config_desc_full(udev,
+ &Giant, &cdp, M_USB, index);
+ if (err) {
+ goto done;
+ }
+ /* set the new config descriptor */
+
+ udev->cdesc = cdp;
+
+ if (cdp->bNumInterface > USB_IFACE_MAX) {
+ DPRINTFN(0, "too many interfaces: %d\n", cdp->bNumInterface);
+ cdp->bNumInterface = USB_IFACE_MAX;
+ }
+ /* Figure out if the device is self or bus powered. */
+ selfpowered = 0;
+ if ((!udev->flags.uq_bus_powered) &&
+ (cdp->bmAttributes & UC_SELF_POWERED) &&
+ (udev->flags.usb2_mode == USB_MODE_HOST)) {
+ /* May be self powered. */
+ if (cdp->bmAttributes & UC_BUS_POWERED) {
+ /* Must ask device. */
+ if (udev->flags.uq_power_claim) {
+ /*
+ * HUB claims to be self powered, but isn't.
+ * It seems that the power status can be
+ * determined by the HUB characteristics.
+ */
+ err = usb2_req_get_hub_descriptor
+ (udev, &Giant, &hd, 1);
+ if (err) {
+ DPRINTFN(0, "could not read "
+ "HUB descriptor: %s\n",
+ usb2_errstr(err));
+
+ } else if (UGETW(hd.wHubCharacteristics) &
+ UHD_PWR_INDIVIDUAL) {
+ selfpowered = 1;
+ }
+ DPRINTF("characteristics=0x%04x\n",
+ UGETW(hd.wHubCharacteristics));
+ } else {
+ err = usb2_req_get_device_status
+ (udev, &Giant, &ds);
+ if (err) {
+ DPRINTFN(0, "could not read "
+ "device status: %s\n",
+ usb2_errstr(err));
+ } else if (UGETW(ds.wStatus) & UDS_SELF_POWERED) {
+ selfpowered = 1;
+ }
+ DPRINTF("status=0x%04x \n",
+ UGETW(ds.wStatus));
+ }
+ } else
+ selfpowered = 1;
+ }
+ DPRINTF("udev=%p cdesc=%p (addr %d) cno=%d attr=0x%02x, "
+ "selfpowered=%d, power=%d\n",
+ udev, cdp,
+ cdp->bConfigurationValue, udev->address, cdp->bmAttributes,
+ selfpowered, cdp->bMaxPower * 2);
+
+ /* Check if we have enough power. */
+ power = cdp->bMaxPower * 2;
+
+ if (udev->parent_hub) {
+ max_power = udev->parent_hub->hub->portpower;
+ } else {
+ max_power = USB_MAX_POWER;
+ }
+
+ if (power > max_power) {
+ DPRINTFN(0, "power exceeded %d > %d\n", power, max_power);
+ err = USB_ERR_NO_POWER;
+ goto done;
+ }
+ /* Only update "self_powered" in USB Host Mode */
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ udev->flags.self_powered = selfpowered;
+ }
+ udev->power = power;
+ udev->curr_config_no = cdp->bConfigurationValue;
+ udev->curr_config_index = index;
+
+ /* Set the actual configuration value. */
+ err = usb2_req_set_config(udev, &Giant, cdp->bConfigurationValue);
+ if (err) {
+ goto done;
+ }
+ /* Allocate and fill interface data. */
+ nifc = cdp->bNumInterface;
+ while (nifc--) {
+ err = usb2_fill_iface_data(udev, nifc, 0);
+ if (err) {
+ goto done;
+ }
+ }
+
+done:
+ DPRINTF("error=%s\n", usb2_errstr(err));
+ if (err) {
+ usb2_free_iface_data(udev);
+ }
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_alt_interface_index
+ *
+ * This function will select an alternate interface index for the
+ * given interface index. The interface should not be in use when this
+ * function is called. That means there should be no open USB
+ * transfers. Else an error is returned.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_set_alt_interface_index(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t alt_index)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ usb2_error_t err;
+ uint8_t do_unlock;
+
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+ if (iface == NULL) {
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+ if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
+ usb2_detach_device(udev, iface_index, 1);
+ }
+ /* free all FIFOs for this interface */
+ usb2_fifo_free_wrap(udev, iface_index, 0);
+
+ err = usb2_fill_iface_data(udev, iface_index, alt_index);
+ if (err) {
+ goto done;
+ }
+ err = usb2_req_set_alt_interface_no
+ (udev, &Giant, iface_index,
+ iface->idesc->bAlternateSetting);
+
+done:
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_endpoint_stall
+ *
+ * This function is used to make a BULK or INTERRUPT endpoint
+ * send STALL tokens.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_set_endpoint_stall(struct usb2_device *udev, struct usb2_pipe *pipe,
+ uint8_t do_stall)
+{
+ struct usb2_xfer *xfer;
+ uint8_t et;
+ uint8_t was_stalled;
+
+ if (pipe == NULL) {
+ /* nothing to do */
+ DPRINTF("Cannot find endpoint\n");
+ /*
+ * Pretend that the clear or set stall request is
+ * successful else some USB host stacks can do
+ * strange things, especially when a control endpoint
+ * stalls.
+ */
+ return (0);
+ }
+ et = (pipe->edesc->bmAttributes & UE_XFERTYPE);
+
+ if ((et != UE_BULK) &&
+ (et != UE_INTERRUPT)) {
+ /*
+ * Should not stall control
+ * nor isochronous endpoints.
+ */
+ DPRINTF("Invalid endpoint\n");
+ return (0);
+ }
+ mtx_lock(&udev->bus->mtx);
+
+ /* store current stall state */
+ was_stalled = pipe->is_stalled;
+
+ /* check for no change */
+ if (was_stalled && do_stall) {
+ /* if the pipe is already stalled do nothing */
+ mtx_unlock(&udev->bus->mtx);
+ DPRINTF("No change\n");
+ return (0);
+ }
+ /* set stalled state */
+ pipe->is_stalled = 1;
+
+ if (do_stall || (!was_stalled)) {
+ if (!was_stalled) {
+ /* lookup the current USB transfer, if any */
+ xfer = pipe->pipe_q.curr;
+ } else {
+ xfer = NULL;
+ }
+
+ /*
+ * If "xfer" is non-NULL the "set_stall" method will
+ * complete the USB transfer like in case of a timeout
+ * setting the error code "USB_ERR_STALLED".
+ */
+ (udev->bus->methods->set_stall) (udev, xfer, pipe);
+ }
+ if (!do_stall) {
+ pipe->toggle_next = 0; /* reset data toggle */
+ pipe->is_stalled = 0; /* clear stalled state */
+
+ (udev->bus->methods->clear_stall) (udev, pipe);
+
+ /* start up the current or next transfer, if any */
+ usb2_command_wrapper(&pipe->pipe_q, pipe->pipe_q.curr);
+ }
+ mtx_unlock(&udev->bus->mtx);
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_reset_iface_endpoints - used in USB device side mode
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_reset_iface_endpoints(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_pipe *pipe;
+ struct usb2_pipe *pipe_end;
+ usb2_error_t err;
+
+ pipe = udev->pipes;
+ pipe_end = udev->pipes + USB_EP_MAX;
+
+ for (; pipe != pipe_end; pipe++) {
+
+ if ((pipe->edesc == NULL) ||
+ (pipe->iface_index != iface_index)) {
+ continue;
+ }
+ /* simulate a clear stall from the peer */
+ err = usb2_set_endpoint_stall(udev, pipe, 0);
+ if (err) {
+ /* just ignore */
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_detach_device_sub
+ *
+ * This function will try to detach an USB device. If it fails a panic
+ * will result.
+ *------------------------------------------------------------------------*/
+static void
+usb2_detach_device_sub(struct usb2_device *udev, device_t *ppdev,
+ uint8_t free_subdev)
+{
+ device_t dev;
+ int err;
+
+ if (!free_subdev) {
+
+ *ppdev = NULL;
+
+ } else if (*ppdev) {
+
+ /*
+ * NOTE: It is important to clear "*ppdev" before deleting
+ * the child due to some device methods being called late
+ * during the delete process !
+ */
+ dev = *ppdev;
+ *ppdev = NULL;
+
+ device_printf(dev, "at %s, port %d, addr %d "
+ "(disconnected)\n",
+ device_get_nameunit(udev->parent_dev),
+ udev->port_no, udev->address);
+
+ if (device_is_attached(dev)) {
+ if (udev->flags.suspended) {
+ err = DEVICE_RESUME(dev);
+ if (err) {
+ device_printf(dev, "Resume failed!\n");
+ }
+ }
+ if (device_detach(dev)) {
+ goto error;
+ }
+ }
+ if (device_delete_child(udev->parent_dev, dev)) {
+ goto error;
+ }
+ }
+ return;
+
+error:
+ /* Detach is not allowed to fail in the USB world */
+ panic("An USB driver would not detach!\n");
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_detach_device
+ *
+ * The following function will detach the matching interfaces.
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_detach_device(struct usb2_device *udev, uint8_t iface_index,
+ uint8_t free_subdev)
+{
+ struct usb2_interface *iface;
+ uint8_t i;
+ uint8_t do_unlock;
+
+ if (udev == NULL) {
+ /* nothing to do */
+ return;
+ }
+ DPRINTFN(4, "udev=%p\n", udev);
+
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+
+ /*
+ * First detach the child to give the child's detach routine a
+ * chance to detach the sub-devices in the correct order.
+ * Then delete the child using "device_delete_child()" which
+ * will detach all sub-devices from the bottom and upwards!
+ */
+ if (iface_index != USB_IFACE_INDEX_ANY) {
+ i = iface_index;
+ iface_index = i + 1;
+ } else {
+ i = 0;
+ iface_index = USB_IFACE_MAX;
+ }
+
+ /* do the detach */
+
+ for (; i != iface_index; i++) {
+
+ iface = usb2_get_iface(udev, i);
+ if (iface == NULL) {
+ /* looks like the end of the USB interfaces */
+ break;
+ }
+ usb2_detach_device_sub(udev, &iface->subdev, free_subdev);
+ }
+
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_probe_and_attach_sub
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_probe_and_attach_sub(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ struct usb2_interface *iface;
+ device_t dev;
+ int err;
+
+ iface = uaa->iface;
+ if (iface->parent_iface_index != USB_IFACE_INDEX_ANY) {
+ /* leave interface alone */
+ return (0);
+ }
+ dev = iface->subdev;
+ if (dev) {
+
+ /* clean up after module unload */
+
+ if (device_is_attached(dev)) {
+ /* already a device there */
+ return (0);
+ }
+ /* clear "iface->subdev" as early as possible */
+
+ iface->subdev = NULL;
+
+ if (device_delete_child(udev->parent_dev, dev)) {
+
+ /*
+ * Panic here, else one can get a double call
+ * to device_detach(). USB devices should
+ * never fail on detach!
+ */
+ panic("device_delete_child() failed!\n");
+ }
+ }
+ if (uaa->temp_dev == NULL) {
+
+ /* create a new child */
+ uaa->temp_dev = device_add_child(udev->parent_dev, NULL, -1);
+ if (uaa->temp_dev == NULL) {
+ device_printf(udev->parent_dev,
+ "Device creation failed!\n");
+ return (1); /* failure */
+ }
+ device_set_ivars(uaa->temp_dev, uaa);
+ device_quiet(uaa->temp_dev);
+ }
+ /*
+ * Set "subdev" before probe and attach so that "devd" gets
+ * the information it needs.
+ */
+ iface->subdev = uaa->temp_dev;
+
+ if (device_probe_and_attach(iface->subdev) == 0) {
+ /*
+ * The USB attach arguments are only available during probe
+ * and attach !
+ */
+ uaa->temp_dev = NULL;
+ device_set_ivars(iface->subdev, NULL);
+
+ if (udev->flags.suspended) {
+ err = DEVICE_SUSPEND(iface->subdev);
+ device_printf(iface->subdev, "Suspend failed\n");
+ }
+ return (0); /* success */
+ } else {
+ /* No USB driver found */
+ iface->subdev = NULL;
+ }
+ return (1); /* failure */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_parent_iface
+ *
+ * Using this function will lock the alternate interface setting on an
+ * interface. It is typically used for multi interface drivers. In USB
+ * device side mode it is assumed that the alternate interfaces all
+ * have the same endpoint descriptors. The default parent index value
+ * is "USB_IFACE_INDEX_ANY". Then the alternate setting value is not
+ * locked.
+ *------------------------------------------------------------------------*/
+void
+usb2_set_parent_iface(struct usb2_device *udev, uint8_t iface_index,
+ uint8_t parent_index)
+{
+ struct usb2_interface *iface;
+
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface) {
+ iface->parent_iface_index = parent_index;
+ }
+ return;
+}
+
+static void
+usb2_init_attach_arg(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ bzero(uaa, sizeof(*uaa));
+
+ uaa->device = udev;
+ uaa->usb2_mode = udev->flags.usb2_mode;
+ uaa->port = udev->port_no;
+
+ uaa->info.idVendor = UGETW(udev->ddesc.idVendor);
+ uaa->info.idProduct = UGETW(udev->ddesc.idProduct);
+ uaa->info.bcdDevice = UGETW(udev->ddesc.bcdDevice);
+ uaa->info.bDeviceClass = udev->ddesc.bDeviceClass;
+ uaa->info.bDeviceSubClass = udev->ddesc.bDeviceSubClass;
+ uaa->info.bDeviceProtocol = udev->ddesc.bDeviceProtocol;
+ uaa->info.bConfigIndex = udev->curr_config_index;
+ uaa->info.bConfigNum = udev->curr_config_no;
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_probe_and_attach
+ *
+ * This function is called from "uhub_explore_sub()",
+ * "usb2_handle_set_config()" and "usb2_handle_request()".
+ *
+ * Returns:
+ * 0: Success
+ * Else: A control transfer failed
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_probe_and_attach(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_attach_arg uaa;
+ struct usb2_interface *iface;
+ uint8_t i;
+ uint8_t j;
+ uint8_t do_unlock;
+
+ if (udev == NULL) {
+ DPRINTF("udev == NULL\n");
+ return (USB_ERR_INVAL);
+ }
+ /* automatic locking */
+ if (sx_xlocked(udev->default_sx + 1)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ sx_xlock(udev->default_sx + 1);
+ }
+
+ if (udev->curr_config_index == USB_UNCONFIG_INDEX) {
+ /* do nothing - no configuration has been set */
+ goto done;
+ }
+ /* setup USB attach arguments */
+
+ usb2_init_attach_arg(udev, &uaa);
+
+ /* Check if only one interface should be probed: */
+ if (iface_index != USB_IFACE_INDEX_ANY) {
+ i = iface_index;
+ j = i + 1;
+ } else {
+ i = 0;
+ j = USB_IFACE_MAX;
+ }
+
+ /* Do the probe and attach */
+ for (; i != j; i++) {
+
+ iface = usb2_get_iface(udev, i);
+ if (iface == NULL) {
+ /*
+ * Looks like the end of the USB
+ * interfaces !
+ */
+ DPRINTFN(2, "end of interfaces "
+ "at %u\n", i);
+ break;
+ }
+ if (iface->idesc == NULL) {
+ /* no interface descriptor */
+ continue;
+ }
+ uaa.iface = iface;
+
+ uaa.info.bInterfaceClass =
+ iface->idesc->bInterfaceClass;
+ uaa.info.bInterfaceSubClass =
+ iface->idesc->bInterfaceSubClass;
+ uaa.info.bInterfaceProtocol =
+ iface->idesc->bInterfaceProtocol;
+ uaa.info.bIfaceIndex = i;
+ uaa.info.bIfaceNum =
+ iface->idesc->bInterfaceNumber;
+ uaa.use_generic = 0;
+
+ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n",
+ uaa.info.bInterfaceClass,
+ uaa.info.bInterfaceSubClass,
+ uaa.info.bInterfaceProtocol,
+ uaa.info.bIfaceIndex,
+ uaa.info.bIfaceNum);
+
+ /* try specific interface drivers first */
+
+ if (usb2_probe_and_attach_sub(udev, &uaa)) {
+ /* ignore */
+ }
+ /* try generic interface drivers last */
+
+ uaa.use_generic = 1;
+
+ if (usb2_probe_and_attach_sub(udev, &uaa)) {
+ /* ignore */
+ }
+ }
+
+ if (uaa.temp_dev) {
+ /* remove the last created child; it is unused */
+
+ if (device_delete_child(udev->parent_dev, uaa.temp_dev)) {
+ DPRINTFN(0, "device delete child failed!\n");
+ }
+ }
+done:
+ if (do_unlock) {
+ sx_unlock(udev->default_sx + 1);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_suspend_resume_sub
+ *
+ * This function is called when the suspend or resume methods should
+ * be executed on an USB device.
+ *------------------------------------------------------------------------*/
+static void
+usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspend)
+{
+ int err;
+
+ if (dev == NULL) {
+ return;
+ }
+ if (!device_is_attached(dev)) {
+ return;
+ }
+ if (do_suspend) {
+ err = DEVICE_SUSPEND(dev);
+ } else {
+ err = DEVICE_RESUME(dev);
+ }
+ if (err) {
+ device_printf(dev, "%s failed!\n",
+ do_suspend ? "Suspend" : "Resume");
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_suspend_resume_device
+ *
+ * The following function will suspend or resume the USB device.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_suspend_resume(struct usb2_device *udev, uint8_t do_suspend)
+{
+ struct usb2_interface *iface;
+ uint8_t i;
+
+ if (udev == NULL) {
+ /* nothing to do */
+ return (0);
+ }
+ DPRINTFN(4, "udev=%p do_suspend=%d\n", udev, do_suspend);
+
+ sx_assert(udev->default_sx + 1, SA_LOCKED);
+
+ mtx_lock(&udev->bus->mtx);
+ /* filter the suspend events */
+ if (udev->flags.suspended == do_suspend) {
+ mtx_unlock(&udev->bus->mtx);
+ /* nothing to do */
+ return (0);
+ }
+ udev->flags.suspended = do_suspend;
+ mtx_unlock(&udev->bus->mtx);
+
+ /* do the suspend or resume */
+
+ for (i = 0; i != USB_IFACE_MAX; i++) {
+
+ iface = usb2_get_iface(udev, i);
+ if (iface == NULL) {
+ /* looks like the end of the USB interfaces */
+ break;
+ }
+ usb2_suspend_resume_sub(udev, iface->subdev, do_suspend);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_clear_stall_proc
+ *
+ * This function performs generic USB clear stall operations.
+ *------------------------------------------------------------------------*/
+static void
+usb2_clear_stall_proc(struct usb2_proc_msg *_pm)
+{
+ struct usb2_clear_stall_msg *pm = (void *)_pm;
+ struct usb2_device *udev = pm->udev;
+
+ /* Change lock */
+ mtx_unlock(&udev->bus->mtx);
+ mtx_lock(udev->default_mtx);
+
+ /* Start clear stall callback */
+ usb2_transfer_start(udev->default_xfer[1]);
+
+ /* Change lock */
+ mtx_unlock(udev->default_mtx);
+ mtx_lock(&udev->bus->mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_alloc_device
+ *
+ * This function allocates a new USB device. This function is called
+ * when a new device has been put in the powered state, but not yet in
+ * the addressed state. Get initial descriptor, set the address, get
+ * full descriptor and get strings.
+ *
+ * Return values:
+ * 0: Failure
+ * Else: Success
+ *------------------------------------------------------------------------*/
+struct usb2_device *
+usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus,
+ struct usb2_device *parent_hub, uint8_t depth,
+ uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb2_mode)
+{
+ struct usb2_attach_arg uaa;
+ struct usb2_device *udev;
+ struct usb2_device *adev;
+ struct usb2_device *hub;
+ uint8_t *scratch_ptr;
+ uint32_t scratch_size;
+ usb2_error_t err;
+ uint8_t device_index;
+
+ DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, "
+ "port_index=%u, port_no=%u, speed=%u, usb2_mode=%u\n",
+ parent_dev, bus, parent_hub, depth, port_index, port_no,
+ speed, usb2_mode);
+
+ /*
+ * Find an unused device index. In USB Host mode this is the
+ * same as the device address.
+ *
+ * NOTE: Index 1 is reserved for the Root HUB.
+ */
+ for (device_index = USB_ROOT_HUB_ADDR; device_index !=
+ USB_MAX_DEVICES; device_index++) {
+ if (bus->devices[device_index] == NULL)
+ break;
+ }
+
+ if (device_index == USB_MAX_DEVICES) {
+ device_printf(bus->bdev,
+ "No free USB device index for new device!\n");
+ return (NULL);
+ }
+ if (depth > 0x10) {
+ device_printf(bus->bdev,
+ "Invalid device depth!\n");
+ return (NULL);
+ }
+ udev = malloc(sizeof(*udev), M_USB, M_WAITOK | M_ZERO);
+ if (udev == NULL) {
+ return (NULL);
+ }
+ /* initialise our SX-lock */
+ sx_init(udev->default_sx, "0123456789ABCDEF - USB device SX lock" + depth);
+
+ /* initialise our SX-lock */
+ sx_init(udev->default_sx + 1, "0123456789ABCDEF - USB config SX lock" + depth);
+
+ usb2_cv_init(udev->default_cv, "WCTRL");
+ usb2_cv_init(udev->default_cv + 1, "UGONE");
+
+ /* initialise our mutex */
+ mtx_init(udev->default_mtx, "USB device mutex", NULL, MTX_DEF);
+
+ /* initialise generic clear stall */
+ udev->cs_msg[0].hdr.pm_callback = &usb2_clear_stall_proc;
+ udev->cs_msg[0].udev = udev;
+ udev->cs_msg[1].hdr.pm_callback = &usb2_clear_stall_proc;
+ udev->cs_msg[1].udev = udev;
+
+ /* initialise some USB device fields */
+ udev->parent_hub = parent_hub;
+ udev->parent_dev = parent_dev;
+ udev->port_index = port_index;
+ udev->port_no = port_no;
+ udev->depth = depth;
+ udev->bus = bus;
+ udev->address = USB_START_ADDR; /* default value */
+ udev->plugtime = (uint32_t)ticks;
+ udev->power_mode = USB_POWER_MODE_ON;
+
+ /* we are not ready yet */
+ udev->refcount = 1;
+
+ /* set up default endpoint descriptor */
+ udev->default_ep_desc.bLength = sizeof(udev->default_ep_desc);
+ udev->default_ep_desc.bDescriptorType = UDESC_ENDPOINT;
+ udev->default_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT;
+ udev->default_ep_desc.bmAttributes = UE_CONTROL;
+ udev->default_ep_desc.wMaxPacketSize[0] = USB_MAX_IPACKET;
+ udev->default_ep_desc.wMaxPacketSize[1] = 0;
+ udev->default_ep_desc.bInterval = 0;
+ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
+
+ udev->speed = speed;
+ udev->flags.usb2_mode = usb2_mode;
+
+ /* check speed combination */
+
+ hub = udev->parent_hub;
+ if (hub) {
+ if (usb2_hub_speed_combs[hub->speed][speed] == 0) {
+#if USB_DEBUG
+ printf("%s: the selected subdevice and HUB speed "
+ "combination is not supported %d/%d.\n",
+ __FUNCTION__, speed, hub->speed);
+#endif
+ /* reject this combination */
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+ }
+ /* search for our High Speed USB HUB, if any */
+
+ adev = udev;
+ hub = udev->parent_hub;
+
+ while (hub) {
+ if (hub->speed == USB_SPEED_HIGH) {
+ udev->hs_hub_addr = hub->address;
+ udev->hs_port_no = adev->port_no;
+ break;
+ }
+ adev = hub;
+ hub = hub->parent_hub;
+ }
+
+ /* init the default pipe */
+ usb2_fill_pipe_data(udev, 0,
+ &udev->default_ep_desc,
+ &udev->default_pipe);
+
+ /* set device index */
+ udev->device_index = device_index;
+
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+
+ err = usb2_req_set_address(udev, &Giant, device_index);
+
+ /* This is the new USB device address from now on */
+
+ udev->address = device_index;
+
+ /*
+ * We ignore any set-address errors, hence there are
+ * buggy USB devices out there that actually receive
+ * the SETUP PID, but manage to set the address before
+ * the STATUS stage is ACK'ed. If the device responds
+ * to the subsequent get-descriptor at the new
+ * address, then we know that the set-address command
+ * was successful.
+ */
+ if (err) {
+ DPRINTFN(0, "set address %d failed "
+ "(ignored)\n", udev->address);
+ }
+ /* allow device time to set new address */
+ usb2_pause_mtx(&Giant, USB_SET_ADDRESS_SETTLE);
+ } else {
+ /* We are not self powered */
+ udev->flags.self_powered = 0;
+
+ /* Set unconfigured state */
+ udev->curr_config_no = USB_UNCONFIG_NO;
+ udev->curr_config_index = USB_UNCONFIG_INDEX;
+
+ /* Setup USB descriptors */
+ err = (usb2_temp_setup_by_index_p) (udev, usb2_template);
+ if (err) {
+ DPRINTFN(0, "setting up USB template failed maybe the USB "
+ "template module has not been loaded\n");
+ goto done;
+ }
+ }
+
+ /*
+ * Get the first 8 bytes of the device descriptor !
+ *
+ * NOTE: "usb2_do_request" will check the device descriptor
+ * next time we do a request to see if the maximum packet size
+ * changed! The 8 first bytes of the device descriptor
+ * contains the maximum packet size to use on control endpoint
+ * 0. If this value is different from "USB_MAX_IPACKET" a new
+ * USB control request will be setup!
+ */
+ err = usb2_req_get_desc(udev, &Giant, &udev->ddesc,
+ USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
+ if (err) {
+ DPRINTFN(0, "getting device descriptor "
+ "at addr %d failed!\n", udev->address);
+ goto done;
+ }
+ DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
+ "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
+ udev->address, UGETW(udev->ddesc.bcdUSB),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->ddesc.bDeviceProtocol,
+ udev->ddesc.bMaxPacketSize,
+ udev->ddesc.bLength,
+ udev->speed);
+
+ /* get the full device descriptor */
+ err = usb2_req_get_device_desc(udev, &Giant, &udev->ddesc);
+ if (err) {
+ DPRINTF("addr=%d, getting full desc failed\n",
+ udev->address);
+ goto done;
+ }
+ /*
+ * Setup temporary USB attach args so that we can figure out some
+ * basic quirks for this device.
+ */
+ usb2_init_attach_arg(udev, &uaa);
+
+ if (usb2_test_quirk(&uaa, UQ_BUS_POWERED)) {
+ udev->flags.uq_bus_powered = 1;
+ }
+ if (usb2_test_quirk(&uaa, UQ_POWER_CLAIM)) {
+ udev->flags.uq_power_claim = 1;
+ }
+ if (usb2_test_quirk(&uaa, UQ_NO_STRINGS)) {
+ udev->flags.no_strings = 1;
+ }
+ /*
+ * Workaround for buggy USB devices.
+ *
+ * It appears that some string-less USB chips will crash and
+ * disappear if any attempts are made to read any string
+ * descriptors.
+ *
+ * Try to detect such chips by checking the strings in the USB
+ * device descriptor. If no strings are present there we
+ * simply disable all USB strings.
+ */
+ scratch_ptr = udev->bus->scratch[0].data;
+ scratch_size = sizeof(udev->bus->scratch[0].data);
+
+ if (udev->ddesc.iManufacturer ||
+ udev->ddesc.iProduct ||
+ udev->ddesc.iSerialNumber) {
+ /* read out the language ID string */
+ err = usb2_req_get_string_desc(udev, &Giant,
+ (char *)scratch_ptr, 4, scratch_size,
+ USB_LANGUAGE_TABLE);
+ } else {
+ err = USB_ERR_INVAL;
+ }
+
+ if (err || (scratch_ptr[0] < 4)) {
+ udev->flags.no_strings = 1;
+ } else {
+ /* pick the first language as the default */
+ udev->langid = UGETW(scratch_ptr + 2);
+ }
+
+ /* assume 100mA bus powered for now. Changed when configured. */
+ udev->power = USB_MIN_POWER;
+
+ /* get serial number string */
+ err = usb2_req_get_string_any
+ (udev, &Giant, (char *)scratch_ptr,
+ scratch_size, udev->ddesc.iSerialNumber);
+
+ strlcpy(udev->serial, (char *)scratch_ptr, sizeof(udev->serial));
+
+ /* get manufacturer string */
+ err = usb2_req_get_string_any
+ (udev, &Giant, (char *)scratch_ptr,
+ scratch_size, udev->ddesc.iManufacturer);
+
+ strlcpy(udev->manufacturer, (char *)scratch_ptr, sizeof(udev->manufacturer));
+
+ /* get product string */
+ err = usb2_req_get_string_any
+ (udev, &Giant, (char *)scratch_ptr,
+ scratch_size, udev->ddesc.iProduct);
+
+ strlcpy(udev->product, (char *)scratch_ptr, sizeof(udev->product));
+
+ /* finish up all the strings */
+ usb2_check_strings(udev);
+
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ uint8_t config_index;
+ uint8_t config_quirk;
+
+ /*
+ * Most USB devices should attach to config index 0 by
+ * default
+ */
+ if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_0)) {
+ config_index = 0;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_1)) {
+ config_index = 1;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_2)) {
+ config_index = 2;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_3)) {
+ config_index = 3;
+ config_quirk = 1;
+ } else if (usb2_test_quirk(&uaa, UQ_CFG_INDEX_4)) {
+ config_index = 4;
+ config_quirk = 1;
+ } else {
+ config_index = 0;
+ config_quirk = 0;
+ }
+
+repeat_set_config:
+
+ DPRINTF("setting config %u\n", config_index);
+
+ /* get the USB device configured */
+ sx_xlock(udev->default_sx + 1);
+ err = usb2_set_config_index(udev, config_index);
+ sx_unlock(udev->default_sx + 1);
+ if (err) {
+ DPRINTFN(0, "Failure selecting "
+ "configuration index %u: %s, port %u, addr %u\n",
+ config_index, usb2_errstr(err), udev->port_no,
+ udev->address);
+
+ } else if (config_quirk) {
+ /* user quirk selects configuration index */
+ } else if ((config_index + 1) < udev->ddesc.bNumConfigurations) {
+
+ if ((udev->cdesc->bNumInterface < 2) &&
+ (usb2_get_no_endpoints(udev->cdesc) == 0)) {
+ DPRINTFN(0, "Found no endpoints "
+ "(trying next config)!\n");
+ config_index++;
+ goto repeat_set_config;
+ }
+ if (config_index == 0) {
+ /*
+ * Try to figure out if we have an
+ * auto-install disk there:
+ */
+ if (usb2_test_autoinstall(udev, 0) == 0) {
+ DPRINTFN(0, "Found possible auto-install "
+ "disk (trying next config)\n");
+ config_index++;
+ goto repeat_set_config;
+ }
+ }
+ } else if (UGETW(udev->ddesc.idVendor) == USB_VENDOR_HUAWEI) {
+ if (usb2_test_huawei(udev, 0) == 0) {
+ DPRINTFN(0, "Found Huawei auto-install disk!\n");
+ err = USB_ERR_STALLED; /* fake an error */
+ }
+ }
+ } else {
+ err = 0; /* set success */
+ }
+
+ DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n",
+ udev->address, udev, udev->parent_hub);
+
+ /* register our device - we are ready */
+ usb2_bus_port_set_device(bus, parent_hub ?
+ parent_hub->hub->ports + port_index : NULL, udev, device_index);
+
+ /* make a symlink for UGEN */
+ if (snprintf((char *)scratch_ptr, scratch_size,
+ USB_DEVICE_NAME "%u.%u.0.0",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index)) {
+ /* ignore */
+ }
+ udev->ugen_symlink =
+ usb2_alloc_symlink((char *)scratch_ptr, "ugen%u.%u",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index);
+
+ printf("ugen%u.%u: <%s> at %s\n",
+ device_get_unit(udev->bus->bdev),
+ udev->device_index, udev->manufacturer,
+ device_get_nameunit(udev->bus->bdev));
+
+ usb2_notify_addq("+", udev);
+done:
+ if (err) {
+ /* free device */
+ usb2_free_device(udev);
+ udev = NULL;
+ }
+ return (udev);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_free_device
+ *
+ * This function is NULL safe and will free an USB device.
+ *------------------------------------------------------------------------*/
+void
+usb2_free_device(struct usb2_device *udev)
+{
+ struct usb2_bus *bus;
+
+ if (udev == NULL) {
+ /* already freed */
+ return;
+ }
+ DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no);
+
+ usb2_notify_addq("-", udev);
+
+ bus = udev->bus;
+
+ /*
+ * Destroy UGEN symlink, if any
+ */
+ if (udev->ugen_symlink) {
+ usb2_free_symlink(udev->ugen_symlink);
+ udev->ugen_symlink = NULL;
+ }
+ /*
+ * Unregister our device first which will prevent any further
+ * references:
+ */
+ usb2_bus_port_set_device(bus, udev->parent_hub ?
+ udev->parent_hub->hub->ports + udev->port_index : NULL,
+ NULL, USB_ROOT_HUB_ADDR);
+
+ /* wait for all pending references to go away: */
+
+ mtx_lock(&usb2_ref_lock);
+ udev->refcount--;
+ while (udev->refcount != 0) {
+ usb2_cv_wait(udev->default_cv + 1, &usb2_ref_lock);
+ }
+ mtx_unlock(&usb2_ref_lock);
+
+ if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
+ /* stop receiving any control transfers (Device Side Mode) */
+ usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX);
+ }
+ /* free all FIFOs */
+ usb2_fifo_free_wrap(udev, USB_IFACE_INDEX_ANY, 1);
+
+ /*
+ * Free all interface related data and FIFOs, if any.
+ */
+ usb2_free_iface_data(udev);
+
+ /* unsetup any leftover default USB transfers */
+ usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX);
+
+ (usb2_temp_unsetup_p) (udev);
+
+ sx_destroy(udev->default_sx);
+ sx_destroy(udev->default_sx + 1);
+
+ usb2_cv_destroy(udev->default_cv);
+ usb2_cv_destroy(udev->default_cv + 1);
+
+ mtx_destroy(udev->default_mtx);
+
+ /* free device */
+ free(udev, M_USB);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_iface
+ *
+ * This function is the safe way to get the USB interface structure
+ * pointer by interface index.
+ *
+ * Return values:
+ * NULL: Interface not present.
+ * Else: Pointer to USB interface structure.
+ *------------------------------------------------------------------------*/
+struct usb2_interface *
+usb2_get_iface(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_interface *iface = udev->ifaces + iface_index;
+
+ if ((iface < udev->ifaces) ||
+ (iface_index >= USB_IFACE_MAX) ||
+ (udev->cdesc == NULL) ||
+ (iface_index >= udev->cdesc->bNumInterface)) {
+ return (NULL);
+ }
+ return (iface);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_find_descriptor
+ *
+ * This function will lookup the first descriptor that matches the
+ * criteria given by the arguments "type" and "subtype". Descriptors
+ * will only be searched within the interface having the index
+ * "iface_index". If the "id" argument points to an USB descriptor,
+ * it will be skipped before the search is started. This allows
+ * searching for multiple descriptors using the same criteria. Else
+ * the search is started after the interface descriptor.
+ *
+ * Return values:
+ * NULL: End of descriptors
+ * Else: A descriptor matching the criteria
+ *------------------------------------------------------------------------*/
+void *
+usb2_find_descriptor(struct usb2_device *udev, void *id, uint8_t iface_index,
+ uint8_t type, uint8_t type_mask,
+ uint8_t subtype, uint8_t subtype_mask)
+{
+ struct usb2_descriptor *desc;
+ struct usb2_config_descriptor *cd;
+ struct usb2_interface *iface;
+
+ cd = usb2_get_config_descriptor(udev);
+ if (cd == NULL) {
+ return (NULL);
+ }
+ if (id == NULL) {
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface == NULL) {
+ return (NULL);
+ }
+ id = usb2_get_interface_descriptor(iface);
+ if (id == NULL) {
+ return (NULL);
+ }
+ }
+ desc = (void *)id;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ if (desc->bDescriptorType == UDESC_INTERFACE) {
+ break;
+ }
+ if (((desc->bDescriptorType & type_mask) == type) &&
+ ((desc->bDescriptorSubtype & subtype_mask) == subtype)) {
+ return (desc);
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_devinfo
+ *
+ * This function will dump information from the device descriptor
+ * belonging to the USB device pointed to by "udev", to the string
+ * pointed to by "dst_ptr" having a maximum length of "dst_len" bytes
+ * including the terminating zero.
+ *------------------------------------------------------------------------*/
+void
+usb2_devinfo(struct usb2_device *udev, char *dst_ptr, uint16_t dst_len)
+{
+ struct usb2_device_descriptor *udd = &udev->ddesc;
+ uint16_t bcdDevice;
+ uint16_t bcdUSB;
+
+ bcdUSB = UGETW(udd->bcdUSB);
+ bcdDevice = UGETW(udd->bcdDevice);
+
+ if (udd->bDeviceClass != 0xFF) {
+ snprintf(dst_ptr, dst_len, "%s %s, class %d/%d, rev %x.%02x/"
+ "%x.%02x, addr %d", udev->manufacturer, udev->product,
+ udd->bDeviceClass, udd->bDeviceSubClass,
+ (bcdUSB >> 8), bcdUSB & 0xFF,
+ (bcdDevice >> 8), bcdDevice & 0xFF,
+ udev->address);
+ } else {
+ snprintf(dst_ptr, dst_len, "%s %s, rev %x.%02x/"
+ "%x.%02x, addr %d", udev->manufacturer, udev->product,
+ (bcdUSB >> 8), bcdUSB & 0xFF,
+ (bcdDevice >> 8), bcdDevice & 0xFF,
+ udev->address);
+ }
+ return;
+}
+
+#if USB_VERBOSE
+/*
+ * Descriptions of of known vendors and devices ("products").
+ */
+struct usb_knowndev {
+ uint16_t vendor;
+ uint16_t product;
+ uint32_t flags;
+ const char *vendorname;
+ const char *productname;
+};
+
+#define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */
+
+#include <dev/usb2/include/usb2_devid.h>
+#include <dev/usb2/include/usb2_devtable.h>
+#endif /* USB_VERBOSE */
+
+/*------------------------------------------------------------------------*
+ * usb2_check_strings
+ *
+ * This function checks the manufacturer and product strings and will
+ * fill in defaults for missing strings.
+ *------------------------------------------------------------------------*/
+static void
+usb2_check_strings(struct usb2_device *udev)
+{
+ struct usb2_device_descriptor *udd = &udev->ddesc;
+ const char *vendor;
+ const char *product;
+
+#if USB_VERBOSE
+ const struct usb_knowndev *kdp;
+
+#endif
+ uint16_t vendor_id;
+ uint16_t product_id;
+
+ usb2_trim_spaces(udev->manufacturer);
+ usb2_trim_spaces(udev->product);
+
+ if (udev->manufacturer[0]) {
+ vendor = udev->manufacturer;
+ } else {
+ vendor = NULL;
+ }
+
+ if (udev->product[0]) {
+ product = udev->product;
+ } else {
+ product = NULL;
+ }
+
+ vendor_id = UGETW(udd->idVendor);
+ product_id = UGETW(udd->idProduct);
+
+#if USB_VERBOSE
+ if (vendor == NULL || product == NULL) {
+
+ for (kdp = usb_knowndevs;
+ kdp->vendorname != NULL;
+ kdp++) {
+ if (kdp->vendor == vendor_id &&
+ (kdp->product == product_id ||
+ (kdp->flags & USB_KNOWNDEV_NOPROD) != 0))
+ break;
+ }
+ if (kdp->vendorname != NULL) {
+ if (vendor == NULL)
+ vendor = kdp->vendorname;
+ if (product == NULL)
+ product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ?
+ kdp->productname : NULL;
+ }
+ }
+#endif
+ if (vendor && *vendor) {
+ if (udev->manufacturer != vendor) {
+ strlcpy(udev->manufacturer, vendor,
+ sizeof(udev->manufacturer));
+ }
+ } else {
+ snprintf(udev->manufacturer,
+ sizeof(udev->manufacturer), "vendor 0x%04x", vendor_id);
+ }
+
+ if (product && *product) {
+ if (udev->product != product) {
+ strlcpy(udev->product, product,
+ sizeof(udev->product));
+ }
+ } else {
+ snprintf(udev->product,
+ sizeof(udev->product), "product 0x%04x", product_id);
+ }
+ return;
+}
+
+uint8_t
+usb2_get_speed(struct usb2_device *udev)
+{
+ return (udev->speed);
+}
+
+struct usb2_device_descriptor *
+usb2_get_device_descriptor(struct usb2_device *udev)
+{
+ if (udev == NULL)
+ return (NULL); /* be NULL safe */
+ return (&udev->ddesc);
+}
+
+struct usb2_config_descriptor *
+usb2_get_config_descriptor(struct usb2_device *udev)
+{
+ if (udev == NULL)
+ return (NULL); /* be NULL safe */
+ return (udev->cdesc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_test_quirk - test a device for a given quirk
+ *
+ * Return values:
+ * 0: The USB device does not have the given quirk.
+ * Else: The USB device has the given quirk.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_test_quirk(const struct usb2_attach_arg *uaa, uint16_t quirk)
+{
+ uint8_t found;
+
+ found = (usb2_test_quirk_p) (&uaa->info, quirk);
+ return (found);
+}
+
+struct usb2_interface_descriptor *
+usb2_get_interface_descriptor(struct usb2_interface *iface)
+{
+ if (iface == NULL)
+ return (NULL); /* be NULL safe */
+ return (iface->idesc);
+}
+
+uint8_t
+usb2_get_interface_altindex(struct usb2_interface *iface)
+{
+ return (iface->alt_index);
+}
+
+uint8_t
+usb2_get_bus_index(struct usb2_device *udev)
+{
+ return ((uint8_t)device_get_unit(udev->bus->bdev));
+}
+
+uint8_t
+usb2_get_device_index(struct usb2_device *udev)
+{
+ return (udev->device_index);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_notify_addq
+ *
+ * This function will generate events for dev.
+ *------------------------------------------------------------------------*/
+static void
+usb2_notify_addq(const char *type, struct usb2_device *udev)
+{
+ char *data = NULL;
+ struct malloc_type *mt;
+
+ mtx_lock(&malloc_mtx);
+ mt = malloc_desc2type("bus"); /* XXX M_BUS */
+ mtx_unlock(&malloc_mtx);
+ if (mt == NULL)
+ return;
+
+ data = malloc(512, mt, M_NOWAIT);
+ if (data == NULL)
+ return;
+
+ /* String it all together. */
+ if (udev->parent_hub) {
+ snprintf(data, 1024,
+ "%s"
+ "ugen%u.%u "
+ "vendor=0x%04x "
+ "product=0x%04x "
+ "devclass=0x%02x "
+ "devsubclass=0x%02x "
+ "sernum=\"%s\" "
+ "at "
+ "port=%u "
+ "on "
+ "ugen%u.%u\n",
+ type,
+ device_get_unit(udev->bus->bdev),
+ udev->device_index,
+ UGETW(udev->ddesc.idVendor),
+ UGETW(udev->ddesc.idProduct),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->serial,
+ udev->port_no,
+ device_get_unit(udev->bus->bdev),
+ udev->parent_hub->device_index);
+ } else {
+ snprintf(data, 1024,
+ "%s"
+ "ugen%u.%u "
+ "vendor=0x%04x "
+ "product=0x%04x "
+ "devclass=0x%02x "
+ "devsubclass=0x%02x "
+ "sernum=\"%s\" "
+ "at port=%u "
+ "on "
+ "%s\n",
+ type,
+ device_get_unit(udev->bus->bdev),
+ udev->device_index,
+ UGETW(udev->ddesc.idVendor),
+ UGETW(udev->ddesc.idProduct),
+ udev->ddesc.bDeviceClass,
+ udev->ddesc.bDeviceSubClass,
+ udev->serial,
+ udev->port_no,
+ device_get_nameunit(device_get_parent(udev->bus->bdev)));
+ }
+ devctl_queue_data(data);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_free_wrap
+ *
+ * The function will free the FIFOs.
+ *------------------------------------------------------------------------*/
+static void
+usb2_fifo_free_wrap(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t free_all)
+{
+ struct usb2_fifo *f;
+ struct usb2_pipe *pipe;
+ uint16_t i;
+
+ /*
+ * Free any USB FIFOs on the given interface:
+ */
+ for (i = 0; i != USB_FIFO_MAX; i++) {
+ f = udev->fifo[i];
+ if (f == NULL) {
+ continue;
+ }
+ pipe = f->priv_sc0;
+ if ((pipe == &udev->default_pipe) && (free_all == 0)) {
+ /* don't free UGEN control endpoint yet */
+ continue;
+ }
+ /* Check if the interface index matches */
+ if ((iface_index == f->iface_index) ||
+ (iface_index == USB_IFACE_INDEX_ANY)) {
+ usb2_fifo_free(f);
+ }
+ }
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_device.h b/sys/dev/usb2/core/usb2_device.h
new file mode 100644
index 000000000000..3c5225c226d3
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_device.h
@@ -0,0 +1,162 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_DEVICE_H_
+#define _USB2_DEVICE_H_
+
+struct usb2_symlink;
+
+#define USB_DEFAULT_XFER_MAX 2
+
+struct usb2_clear_stall_msg {
+ struct usb2_proc_msg hdr;
+ struct usb2_device *udev;
+};
+
+/*
+ * The following structure defines an USB pipe which is equal to an
+ * USB endpoint.
+ */
+struct usb2_pipe {
+ struct usb2_xfer_queue pipe_q; /* queue of USB transfers */
+
+ struct usb2_xfer *xfer_block; /* blocking USB transfer */
+ struct usb2_endpoint_descriptor *edesc;
+ struct usb2_pipe_methods *methods; /* set by HC driver */
+
+ uint16_t isoc_next;
+ uint16_t refcount;
+
+ uint8_t toggle_next:1; /* next data toggle value */
+ uint8_t is_stalled:1; /* set if pipe is stalled */
+ uint8_t is_synced:1; /* set if we a synchronised */
+ uint8_t unused:5;
+ uint8_t iface_index; /* not used by "default pipe" */
+};
+
+/*
+ * The following structure defines an USB interface.
+ */
+struct usb2_interface {
+ struct usb2_perm perm; /* interface permissions */
+ struct usb2_interface_descriptor *idesc;
+ device_t subdev;
+ uint8_t alt_index;
+ uint8_t parent_iface_index;
+};
+
+/*
+ * The following structure defines the USB device flags.
+ */
+struct usb2_device_flags {
+ uint8_t usb2_mode:1; /* USB mode (see USB_MODE_XXX) */
+ uint8_t self_powered:1; /* set if USB device is self powered */
+ uint8_t suspended:1; /* set if USB device is suspended */
+ uint8_t no_strings:1; /* set if USB device does not support
+ * strings */
+ uint8_t remote_wakeup:1; /* set if remote wakeup is enabled */
+ uint8_t uq_bus_powered:1; /* set if BUS powered quirk is present */
+ uint8_t uq_power_claim:1; /* set if power claim quirk is present */
+};
+
+/*
+ * The following structure defines an USB device. There exists one of
+ * these structures for every USB device.
+ */
+struct usb2_device {
+ struct usb2_clear_stall_msg cs_msg[2]; /* generic clear stall
+ * messages */
+ struct usb2_perm perm;
+ struct sx default_sx[2];
+ struct mtx default_mtx[1];
+ struct cv default_cv[2];
+ struct usb2_interface ifaces[USB_IFACE_MAX];
+ struct usb2_pipe default_pipe; /* Control Endpoint 0 */
+ struct usb2_pipe pipes[USB_EP_MAX];
+
+ struct usb2_bus *bus; /* our USB BUS */
+ device_t parent_dev; /* parent device */
+ struct usb2_device *parent_hub;
+ struct usb2_config_descriptor *cdesc; /* full config descr */
+ struct usb2_hub *hub; /* only if this is a hub */
+ struct usb_device *linux_dev;
+ struct usb2_xfer *default_xfer[USB_DEFAULT_XFER_MAX];
+ struct usb2_temp_data *usb2_template_ptr;
+ struct usb2_pipe *pipe_curr; /* current clear stall pipe */
+ struct usb2_fifo *fifo[USB_FIFO_MAX];
+ struct usb2_symlink *ugen_symlink; /* our generic symlink */
+
+ uint32_t plugtime; /* copy of "ticks" */
+
+ uint16_t refcount;
+#define USB_DEV_REF_MAX 0xffff
+
+ uint16_t power; /* mA the device uses */
+ uint16_t langid; /* language for strings */
+
+ uint8_t address; /* device addess */
+ uint8_t device_index; /* device index in "bus->devices" */
+ uint8_t curr_config_index; /* current configuration index */
+ uint8_t curr_config_no; /* current configuration number */
+ uint8_t depth; /* distance from root HUB */
+ uint8_t speed; /* low/full/high speed */
+ uint8_t port_index; /* parent HUB port index */
+ uint8_t port_no; /* parent HUB port number */
+ uint8_t hs_hub_addr; /* high-speed HUB address */
+ uint8_t hs_port_no; /* high-speed HUB port number */
+ uint8_t driver_added_refcount; /* our driver added generation count */
+ uint8_t power_mode; /* see USB_POWER_XXX */
+
+ /* the "flags" field is write-protected by "bus->mtx" */
+
+ struct usb2_device_flags flags;
+
+ struct usb2_endpoint_descriptor default_ep_desc; /* for pipe 0 */
+ struct usb2_device_descriptor ddesc; /* device descriptor */
+
+ char serial[64]; /* serial number */
+ char manufacturer[64]; /* manufacturer string */
+ char product[64]; /* product string */
+};
+
+/* function prototypes */
+
+struct usb2_device *usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, struct usb2_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, uint8_t speed, uint8_t usb2_mode);
+struct usb2_pipe *usb2_get_pipe(struct usb2_device *udev, uint8_t iface_index, const struct usb2_config *setup);
+struct usb2_pipe *usb2_get_pipe_by_addr(struct usb2_device *udev, uint8_t ea_val);
+usb2_error_t usb2_interface_count(struct usb2_device *udev, uint8_t *count);
+usb2_error_t usb2_probe_and_attach(struct usb2_device *udev, uint8_t iface_index);
+usb2_error_t usb2_reset_iface_endpoints(struct usb2_device *udev, uint8_t iface_index);
+usb2_error_t usb2_set_config_index(struct usb2_device *udev, uint8_t index);
+usb2_error_t usb2_set_endpoint_stall(struct usb2_device *udev, struct usb2_pipe *pipe, uint8_t do_stall);
+usb2_error_t usb2_suspend_resume(struct usb2_device *udev, uint8_t do_suspend);
+void usb2_detach_device(struct usb2_device *udev, uint8_t iface_index, uint8_t free_subdev);
+void usb2_devinfo(struct usb2_device *udev, char *dst_ptr, uint16_t dst_len);
+void usb2_free_device(struct usb2_device *udev);
+void *usb2_find_descriptor(struct usb2_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask);
+void usb_linux_free_device(struct usb_device *dev);
+
+#endif /* _USB2_DEVICE_H_ */
diff --git a/sys/dev/usb2/core/usb2_dynamic.c b/sys/dev/usb2/core/usb2_dynamic.c
new file mode 100644
index 000000000000..2bd4b01331ce
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_dynamic.c
@@ -0,0 +1,140 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_dynamic.h>
+
+/* function prototypes */
+static usb2_temp_get_desc_t usb2_temp_get_desc_w;
+static usb2_temp_setup_by_index_t usb2_temp_setup_by_index_w;
+static usb2_temp_unsetup_t usb2_temp_unsetup_w;
+static usb2_test_quirk_t usb2_test_quirk_w;
+static usb2_quirk_ioctl_t usb2_quirk_ioctl_w;
+
+/* global variables */
+usb2_temp_get_desc_t *usb2_temp_get_desc_p = &usb2_temp_get_desc_w;
+usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index_w;
+usb2_temp_unsetup_t *usb2_temp_unsetup_p = &usb2_temp_unsetup_w;
+usb2_test_quirk_t *usb2_test_quirk_p = &usb2_test_quirk_w;
+usb2_quirk_ioctl_t *usb2_quirk_ioctl_p = &usb2_quirk_ioctl_w;
+devclass_t usb2_devclass_ptr = NULL;
+
+static usb2_error_t
+usb2_temp_setup_by_index_w(struct usb2_device *udev, uint16_t index)
+{
+ return (USB_ERR_INVAL);
+}
+
+static uint8_t
+usb2_test_quirk_w(const struct usb2_lookup_info *info, uint16_t quirk)
+{
+ return (0); /* no match */
+}
+
+static int
+usb2_quirk_ioctl_w(unsigned long cmd, caddr_t data, int fflag, struct thread *td)
+{
+ return (ENOIOCTL);
+}
+
+static void
+usb2_temp_get_desc_w(struct usb2_device *udev, struct usb2_device_request *req, const void **pPtr, uint16_t *pLength)
+{
+ /* stall */
+ *pPtr = NULL;
+ *pLength = 0;
+ return;
+}
+
+static void
+usb2_temp_unsetup_w(struct usb2_device *udev)
+{
+ if (udev->usb2_template_ptr) {
+
+ free(udev->usb2_template_ptr, M_USB);
+
+ udev->usb2_template_ptr = NULL;
+ }
+ return;
+}
+
+void
+usb2_quirk_unload(void *arg)
+{
+ /* reset function pointers */
+
+ usb2_test_quirk_p = &usb2_test_quirk_w;
+ usb2_quirk_ioctl_p = &usb2_quirk_ioctl_w;
+
+ /* wait for CPU to exit the loaded functions, if any */
+
+ /* XXX this is a tradeoff */
+
+ pause("WAIT", hz);
+
+ return;
+}
+
+void
+usb2_temp_unload(void *arg)
+{
+ /* reset function pointers */
+
+ usb2_temp_get_desc_p = &usb2_temp_get_desc_w;
+ usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index_w;
+ usb2_temp_unsetup_p = &usb2_temp_unsetup_w;
+
+ /* wait for CPU to exit the loaded functions, if any */
+
+ /* XXX this is a tradeoff */
+
+ pause("WAIT", hz);
+
+ return;
+}
+
+void
+usb2_bus_unload(void *arg)
+{
+ /* reset function pointers */
+
+ usb2_devclass_ptr = NULL;
+
+ /* wait for CPU to exit the loaded functions, if any */
+
+ /* XXX this is a tradeoff */
+
+ pause("WAIT", hz);
+
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_dynamic.h b/sys/dev/usb2/core/usb2_dynamic.h
new file mode 100644
index 000000000000..ca59f96e2d00
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_dynamic.h
@@ -0,0 +1,61 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_DYNAMIC_H_
+#define _USB2_DYNAMIC_H_
+
+/* prototypes */
+
+struct usb2_device;
+struct usb2_lookup_info;
+struct usb2_device_request;
+
+/* typedefs */
+
+typedef usb2_error_t (usb2_temp_setup_by_index_t)(struct usb2_device *udev, uint16_t index);
+typedef uint8_t (usb2_test_quirk_t)(const struct usb2_lookup_info *info, uint16_t quirk);
+typedef int (usb2_quirk_ioctl_t)(unsigned long cmd, caddr_t data, int fflag, struct thread *td);
+typedef void (usb2_temp_get_desc_t)(struct usb2_device *udev, struct usb2_device_request *req, const void **pPtr, uint16_t *pLength);
+typedef void (usb2_temp_unsetup_t)(struct usb2_device *udev);
+
+/* global function pointers */
+
+extern usb2_temp_get_desc_t *usb2_temp_get_desc_p;
+extern usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p;
+extern usb2_temp_unsetup_t *usb2_temp_unsetup_p;
+extern usb2_test_quirk_t *usb2_test_quirk_p;
+extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p;
+extern devclass_t usb2_devclass_ptr;
+
+/* function prototypes */
+
+void usb2_temp_unload(void *);
+void usb2_quirk_unload(void *);
+void usb2_bus_unload(void *);
+
+uint8_t usb2_test_quirk(const struct usb2_attach_arg *uaa, uint16_t quirk);
+
+#endif /* _USB2_DYNAMIC_H_ */
diff --git a/sys/dev/usb2/core/usb2_error.c b/sys/dev/usb2/core/usb2_error.c
new file mode 100644
index 000000000000..51668599f2d8
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_error.c
@@ -0,0 +1,44 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+
+#include <dev/usb2/core/usb2_core.h>
+
+USB_MAKE_DEBUG_TABLE(USB_ERR);
+
+/*------------------------------------------------------------------------*
+ * usb2_errstr
+ *
+ * This function converts an USB error code into a string.
+ *------------------------------------------------------------------------*/
+const char *
+usb2_errstr(usb2_error_t err)
+{
+ return ((err < USB_ERR_MAX) ?
+ USB_ERR[err] : "USB_ERR_UNKNOWN");
+}
diff --git a/sys/dev/usb2/core/usb2_generic.c b/sys/dev/usb2/core/usb2_generic.c
new file mode 100644
index 000000000000..56ada335bad6
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_generic.c
@@ -0,0 +1,2226 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_ioctl.h>
+#include <dev/usb2/include/usb2_error.h>
+
+#define USB_DEBUG_VAR ugen_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_mbuf.h>
+#include <dev/usb2/core/usb2_dev.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_hub.h>
+#include <dev/usb2/core/usb2_generic.h>
+#include <dev/usb2/core/usb2_transfer.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+/* defines */
+
+#define UGEN_BULK_FS_BUFFER_SIZE (64*32) /* bytes */
+#define UGEN_BULK_HS_BUFFER_SIZE (1024*32) /* bytes */
+#define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */
+
+/* function prototypes */
+
+static usb2_callback_t ugen_read_clear_stall_callback;
+static usb2_callback_t ugen_write_clear_stall_callback;
+static usb2_callback_t ugen_default_read_callback;
+static usb2_callback_t ugen_default_write_callback;
+static usb2_callback_t ugen_isoc_read_callback;
+static usb2_callback_t ugen_isoc_write_callback;
+static usb2_callback_t ugen_default_fs_callback;
+
+static usb2_fifo_open_t ugen_open;
+static usb2_fifo_close_t ugen_close;
+static usb2_fifo_ioctl_t ugen_ioctl;
+static usb2_fifo_cmd_t ugen_start_read;
+static usb2_fifo_cmd_t ugen_start_write;
+static usb2_fifo_cmd_t ugen_stop_io;
+
+static int ugen_transfer_setup(struct usb2_fifo *f, const struct usb2_config *setup, uint8_t n_setup);
+static int ugen_open_pipe_write(struct usb2_fifo *f);
+static int ugen_open_pipe_read(struct usb2_fifo *f);
+static int ugen_set_config(struct usb2_fifo *f, uint8_t index);
+static int ugen_set_interface(struct usb2_fifo *f, uint8_t iface_index, uint8_t alt_index);
+static int ugen_get_cdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *pgd);
+static int ugen_get_sdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd);
+static int usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di);
+static int ugen_re_enumerate(struct usb2_fifo *f);
+static int ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags);
+static int ugen_ctrl_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags);
+static int ugen_fs_uninit(struct usb2_fifo *f);
+static uint8_t ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex);
+
+
+/* structures */
+
+struct usb2_fifo_methods usb2_ugen_methods = {
+ .f_open = &ugen_open,
+ .f_close = &ugen_close,
+ .f_ioctl = &ugen_ioctl,
+ .f_start_read = &ugen_start_read,
+ .f_stop_read = &ugen_stop_io,
+ .f_start_write = &ugen_start_write,
+ .f_stop_write = &ugen_stop_io,
+};
+
+#if USB_DEBUG
+static int ugen_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic");
+SYSCTL_INT(_hw_usb2_ugen, OID_AUTO, debug, CTLFLAG_RW, &ugen_debug,
+ 0, "Debug level");
+#endif
+
+
+/* prototypes */
+
+static int
+ugen_transfer_setup(struct usb2_fifo *f,
+ const struct usb2_config *setup, uint8_t n_setup)
+{
+ struct usb2_pipe *pipe = f->priv_sc0;
+ struct usb2_device *udev = f->udev;
+ uint8_t iface_index = pipe->iface_index;
+ int error;
+
+ mtx_unlock(f->priv_mtx);
+
+ /*
+ * "usb2_transfer_setup()" can sleep so one needs to make a wrapper,
+ * exiting the mutex and checking things
+ */
+ error = usb2_transfer_setup(udev, &iface_index, f->xfer,
+ setup, n_setup, f, f->priv_mtx);
+ if (error == 0) {
+
+ if (f->xfer[0]->nframes == 1) {
+ error = usb2_fifo_alloc_buffer(f,
+ f->xfer[0]->max_data_length, 2);
+ } else {
+ error = usb2_fifo_alloc_buffer(f,
+ f->xfer[0]->max_frame_size,
+ 2 * f->xfer[0]->nframes);
+ }
+ if (error) {
+ usb2_transfer_unsetup(f->xfer, n_setup);
+ }
+ }
+ mtx_lock(f->priv_mtx);
+
+ return (error);
+}
+
+static int
+ugen_open(struct usb2_fifo *f, int fflags, struct thread *td)
+{
+ struct usb2_pipe *pipe = f->priv_sc0;
+ struct usb2_endpoint_descriptor *ed = pipe->edesc;
+ uint8_t type;
+
+ DPRINTFN(6, "flag=0x%x\n", fflags);
+
+ mtx_lock(f->priv_mtx);
+ if (usb2_get_speed(f->udev) == USB_SPEED_HIGH) {
+ f->nframes = UGEN_HW_FRAMES * 8;
+ f->bufsize = UGEN_BULK_HS_BUFFER_SIZE;
+ } else {
+ f->nframes = UGEN_HW_FRAMES;
+ f->bufsize = UGEN_BULK_FS_BUFFER_SIZE;
+ }
+
+ type = ed->bmAttributes & UE_XFERTYPE;
+ if (type == UE_INTERRUPT) {
+ f->bufsize = 0; /* use "wMaxPacketSize" */
+ }
+ f->timeout = USB_NO_TIMEOUT;
+ f->flag_short = 0;
+ f->fifo_zlp = 0;
+ mtx_unlock(f->priv_mtx);
+
+ return (0);
+}
+
+static void
+ugen_close(struct usb2_fifo *f, int fflags, struct thread *td)
+{
+ DPRINTFN(6, "flag=0x%x\n", fflags);
+
+ /* cleanup */
+
+ mtx_lock(f->priv_mtx);
+ usb2_transfer_stop(f->xfer[0]);
+ usb2_transfer_stop(f->xfer[1]);
+ mtx_unlock(f->priv_mtx);
+
+ usb2_transfer_unsetup(f->xfer, 2);
+ usb2_fifo_free_buffer(f);
+
+ if (ugen_fs_uninit(f)) {
+ /* ignore any errors - we are closing */
+ }
+ return;
+}
+
+static int
+ugen_open_pipe_write(struct usb2_fifo *f)
+{
+ struct usb2_config usb2_config[2];
+ struct usb2_pipe *pipe = f->priv_sc0;
+ struct usb2_endpoint_descriptor *ed = pipe->edesc;
+
+ mtx_assert(f->priv_mtx, MA_OWNED);
+
+ if (f->xfer[0] || f->xfer[1]) {
+ /* transfers are already opened */
+ return (0);
+ }
+ if (f->fs_xfer) {
+ /* should not happen */
+ return (EINVAL);
+ }
+ bzero(usb2_config, sizeof(usb2_config));
+
+ usb2_config[1].type = UE_CONTROL;
+ usb2_config[1].endpoint = 0;
+ usb2_config[1].direction = UE_DIR_ANY;
+ usb2_config[1].mh.timeout = 1000; /* 1 second */
+ usb2_config[1].mh.interval = 50;/* 50 milliseconds */
+ usb2_config[1].mh.bufsize = sizeof(struct usb2_device_request);
+ usb2_config[1].mh.callback = &ugen_write_clear_stall_callback;
+
+ usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE;
+ usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
+ usb2_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN);
+ usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL;
+ usb2_config[0].mh.flags.proxy_buffer = 1;
+
+ switch (ed->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ case UE_BULK:
+ if (f->flag_short) {
+ usb2_config[0].mh.flags.force_short_xfer = 1;
+ }
+ usb2_config[0].mh.callback = &ugen_default_write_callback;
+ usb2_config[0].mh.timeout = f->timeout;
+ usb2_config[0].mh.frames = 1;
+ usb2_config[0].mh.bufsize = f->bufsize;
+ usb2_config[0].md = usb2_config[0].mh; /* symmetric config */
+ if (ugen_transfer_setup(f, usb2_config, 2)) {
+ return (EIO);
+ }
+ /* first transfer does not clear stall */
+ f->flag_stall = 0;
+ break;
+
+ case UE_ISOCHRONOUS:
+ usb2_config[0].mh.flags.short_xfer_ok = 1;
+ usb2_config[0].mh.bufsize = 0; /* use default */
+ usb2_config[0].mh.frames = f->nframes;
+ usb2_config[0].mh.callback = &ugen_isoc_write_callback;
+ usb2_config[0].mh.timeout = 0;
+ usb2_config[0].md = usb2_config[0].mh; /* symmetric config */
+
+ /* clone configuration */
+ usb2_config[1] = usb2_config[0];
+
+ if (ugen_transfer_setup(f, usb2_config, 2)) {
+ return (EIO);
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+ugen_open_pipe_read(struct usb2_fifo *f)
+{
+ struct usb2_config usb2_config[2];
+ struct usb2_pipe *pipe = f->priv_sc0;
+ struct usb2_endpoint_descriptor *ed = pipe->edesc;
+
+ mtx_assert(f->priv_mtx, MA_OWNED);
+
+ if (f->xfer[0] || f->xfer[1]) {
+ /* transfers are already opened */
+ return (0);
+ }
+ if (f->fs_xfer) {
+ /* should not happen */
+ return (EINVAL);
+ }
+ bzero(usb2_config, sizeof(usb2_config));
+
+ usb2_config[1].type = UE_CONTROL;
+ usb2_config[1].endpoint = 0;
+ usb2_config[1].direction = UE_DIR_ANY;
+ usb2_config[1].mh.timeout = 1000; /* 1 second */
+ usb2_config[1].mh.interval = 50;/* 50 milliseconds */
+ usb2_config[1].mh.bufsize = sizeof(struct usb2_device_request);
+ usb2_config[1].mh.callback = &ugen_read_clear_stall_callback;
+
+ usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE;
+ usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
+ usb2_config[0].direction = UE_DIR_IN;
+ usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL;
+ usb2_config[0].mh.flags.proxy_buffer = 1;
+
+ switch (ed->bmAttributes & UE_XFERTYPE) {
+ case UE_INTERRUPT:
+ case UE_BULK:
+ if (f->flag_short) {
+ usb2_config[0].mh.flags.short_xfer_ok = 1;
+ }
+ usb2_config[0].mh.timeout = f->timeout;
+ usb2_config[0].mh.frames = 1;
+ usb2_config[0].mh.callback = &ugen_default_read_callback;
+ usb2_config[0].mh.bufsize = f->bufsize;
+ usb2_config[0].md = usb2_config[0].mh; /* symmetric config */
+
+ if (ugen_transfer_setup(f, usb2_config, 2)) {
+ return (EIO);
+ }
+ /* first transfer does not clear stall */
+ f->flag_stall = 0;
+ break;
+
+ case UE_ISOCHRONOUS:
+ usb2_config[0].mh.flags.short_xfer_ok = 1;
+ usb2_config[0].mh.bufsize = 0; /* use default */
+ usb2_config[0].mh.frames = f->nframes;
+ usb2_config[0].mh.callback = &ugen_isoc_read_callback;
+ usb2_config[0].mh.timeout = 0;
+ usb2_config[0].md = usb2_config[0].mh; /* symmetric config */
+
+ /* clone configuration */
+ usb2_config[1] = usb2_config[0];
+
+ if (ugen_transfer_setup(f, usb2_config, 2)) {
+ return (EIO);
+ }
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static void
+ugen_start_read(struct usb2_fifo *f)
+{
+ /* check that pipes are open */
+ if (ugen_open_pipe_read(f)) {
+ /* signal error */
+ usb2_fifo_put_data_error(f);
+ }
+ /* start transfers */
+ usb2_transfer_start(f->xfer[0]);
+ usb2_transfer_start(f->xfer[1]);
+ return;
+}
+
+static void
+ugen_start_write(struct usb2_fifo *f)
+{
+ /* check that pipes are open */
+ if (ugen_open_pipe_write(f)) {
+ /* signal error */
+ usb2_fifo_get_data_error(f);
+ }
+ /* start transfers */
+ usb2_transfer_start(f->xfer[0]);
+ usb2_transfer_start(f->xfer[1]);
+ return;
+}
+
+static void
+ugen_stop_io(struct usb2_fifo *f)
+{
+ /* stop transfers */
+ usb2_transfer_stop(f->xfer[0]);
+ usb2_transfer_stop(f->xfer[1]);
+ return;
+}
+
+static void
+ugen_default_read_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_fifo *f = xfer->priv_sc;
+ struct usb2_mbuf *m;
+
+ DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen == 0) {
+ if (f->fifo_zlp != 4) {
+ f->fifo_zlp++;
+ } else {
+ /*
+ * Throttle a little bit we have multiple ZLPs
+ * in a row!
+ */
+ xfer->interval = 64; /* ms */
+ }
+ } else {
+ /* clear throttle */
+ xfer->interval = 0;
+ f->fifo_zlp = 0;
+ }
+ usb2_fifo_put_data(f, xfer->frbuffers, 0,
+ xfer->actlen, 1);
+
+ case USB_ST_SETUP:
+ if (f->flag_stall) {
+ usb2_transfer_start(f->xfer[1]);
+ break;
+ }
+ USB_IF_POLL(&f->free_q, m);
+ if (m) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ f->flag_stall = 1;
+ f->fifo_zlp = 0;
+ usb2_transfer_start(f->xfer[1]);
+ }
+ break;
+ }
+ return;
+}
+
+static void
+ugen_default_write_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_fifo *f = xfer->priv_sc;
+ uint32_t actlen;
+
+ DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+ /*
+ * If writing is in stall, just jump to clear stall
+ * callback and solve the situation.
+ */
+ if (f->flag_stall) {
+ usb2_transfer_start(f->xfer[1]);
+ break;
+ }
+ /*
+ * Write data, setup and perform hardware transfer.
+ */
+ if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
+ xfer->max_data_length, &actlen, 0)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ f->flag_stall = 1;
+ usb2_transfer_start(f->xfer[1]);
+ }
+ break;
+ }
+ return;
+}
+
+static void
+ugen_read_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_fifo *f = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = f->xfer[0];
+
+ if (f->flag_stall == 0) {
+ /* nothing to do */
+ return;
+ }
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTFN(5, "f=%p: stall cleared\n", f);
+ f->flag_stall = 0;
+ usb2_transfer_start(xfer_other);
+ }
+ return;
+}
+
+static void
+ugen_write_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_fifo *f = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = f->xfer[0];
+
+ if (f->flag_stall == 0) {
+ /* nothing to do */
+ return;
+ }
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTFN(5, "f=%p: stall cleared\n", f);
+ f->flag_stall = 0;
+ usb2_transfer_start(xfer_other);
+ }
+ return;
+}
+
+static void
+ugen_isoc_read_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_fifo *f = xfer->priv_sc;
+ uint32_t offset;
+ uint16_t n;
+
+ DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTFN(6, "actlen=%d\n", xfer->actlen);
+
+ offset = 0;
+
+ for (n = 0; n != xfer->aframes; n++) {
+ usb2_fifo_put_data(f, xfer->frbuffers, offset,
+ xfer->frlengths[n], 1);
+ offset += xfer->max_frame_size;
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ for (n = 0; n != xfer->nframes; n++) {
+ /* setup size for next transfer */
+ xfer->frlengths[n] = xfer->max_frame_size;
+ }
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ goto tr_setup;
+ }
+ return;
+}
+
+static void
+ugen_isoc_write_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_fifo *f = xfer->priv_sc;
+ uint32_t actlen;
+ uint32_t offset;
+ uint16_t n;
+
+ DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ offset = 0;
+ for (n = 0; n != xfer->nframes; n++) {
+ if (usb2_fifo_get_data(f, xfer->frbuffers, offset,
+ xfer->max_frame_size, &actlen, 1)) {
+ xfer->frlengths[n] = actlen;
+ offset += actlen;
+ } else {
+ break;
+ }
+ }
+
+ for (; n != xfer->nframes; n++) {
+ /* fill in zero frames */
+ xfer->frlengths[n] = 0;
+ }
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ goto tr_setup;
+ }
+ return;
+}
+
+static int
+ugen_set_config(struct usb2_fifo *f, uint8_t index)
+{
+ DPRINTFN(2, "index %u\n", index);
+
+ if (f->flag_no_uref) {
+ /* not the control endpoint - just forget it */
+ return (EINVAL);
+ }
+ if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not possible in device side mode */
+ return (ENOTTY);
+ }
+ if (f->udev->curr_config_index == index) {
+ /* no change needed */
+ return (0);
+ }
+ /* change setting - will free generic FIFOs, if any */
+ if (usb2_set_config_index(f->udev, index)) {
+ return (EIO);
+ }
+ /* probe and attach */
+ if (usb2_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ugen_set_interface(struct usb2_fifo *f,
+ uint8_t iface_index, uint8_t alt_index)
+{
+ DPRINTFN(2, "%u, %u\n", iface_index, alt_index);
+
+ if (f->flag_no_uref) {
+ /* not the control endpoint - just forget it */
+ return (EINVAL);
+ }
+ if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not possible in device side mode */
+ return (ENOTTY);
+ }
+ /* change setting - will free generic FIFOs, if any */
+ if (usb2_set_alt_interface_index(f->udev, iface_index, alt_index)) {
+ return (EIO);
+ }
+ /* probe and attach */
+ if (usb2_probe_and_attach(f->udev, iface_index)) {
+ return (EIO);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ugen_get_cdesc
+ *
+ * This function will retrieve the complete configuration descriptor
+ * at the given index.
+ *------------------------------------------------------------------------*/
+static int
+ugen_get_cdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd)
+{
+ struct usb2_config_descriptor *cdesc;
+ struct usb2_device *udev = f->udev;
+ int error;
+ uint16_t len;
+ uint8_t free_data;
+
+ DPRINTFN(6, "\n");
+
+ if (f->flag_no_uref) {
+ /* control endpoint only */
+ return (EINVAL);
+ }
+ if (ugd->ugd_data == NULL) {
+ /* userland pointer should not be zero */
+ return (EINVAL);
+ }
+ if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) ||
+ (ugd->ugd_config_index == udev->curr_config_index)) {
+ cdesc = usb2_get_config_descriptor(udev);
+ if (cdesc == NULL) {
+ return (ENXIO);
+ }
+ free_data = 0;
+
+ } else {
+ if (usb2_req_get_config_desc_full(udev,
+ &Giant, &cdesc, M_USBDEV,
+ ugd->ugd_config_index)) {
+ return (ENXIO);
+ }
+ free_data = 1;
+ }
+
+ len = UGETW(cdesc->wTotalLength);
+ if (len > ugd->ugd_maxlen) {
+ len = ugd->ugd_maxlen;
+ }
+ DPRINTFN(6, "len=%u\n", len);
+
+ ugd->ugd_actlen = len;
+ ugd->ugd_offset = 0;
+
+ error = copyout(cdesc, ugd->ugd_data, len);
+
+ if (free_data) {
+ free(cdesc, M_USBDEV);
+ }
+ return (error);
+}
+
+static int
+ugen_get_sdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd)
+{
+ void *ptr = f->udev->bus->scratch[0].data;
+ uint16_t size = sizeof(f->udev->bus->scratch[0].data);
+ int error;
+
+ if (f->flag_no_uref) {
+ /* control endpoint only */
+ return (EINVAL);
+ }
+ if (usb2_req_get_string_desc(f->udev, &Giant, ptr,
+ size, ugd->ugd_lang_id, ugd->ugd_string_index)) {
+ error = EINVAL;
+ } else {
+
+ if (size > ((uint8_t *)ptr)[0]) {
+ size = ((uint8_t *)ptr)[0];
+ }
+ if (size > ugd->ugd_maxlen) {
+ size = ugd->ugd_maxlen;
+ }
+ ugd->ugd_actlen = size;
+ ugd->ugd_offset = 0;
+
+ error = copyout(ptr, ugd->ugd_data, size);
+ }
+ return (error);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_gen_fill_devicenames
+ *
+ * This function dumps information about an USB device names to
+ * userland.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static int
+usb2_gen_fill_devicenames(struct usb2_fifo *f, struct usb2_device_names *dn)
+{
+ struct usb2_interface *iface;
+ const char *ptr;
+ char *dst;
+ char buf[32];
+ int error = 0;
+ int len;
+ int max_len;
+ uint8_t i;
+ uint8_t first = 1;
+
+ if (f->flag_no_uref) {
+ /* control endpoint only */
+ return (EINVAL);
+ }
+ max_len = dn->udn_devnames_len;
+ dst = dn->udn_devnames_ptr;
+
+ if (max_len == 0) {
+ return (EINVAL);
+ }
+ /* put a zero there */
+ error = copyout("", dst, 1);
+ if (error) {
+ return (error);
+ }
+ for (i = 0;; i++) {
+ iface = usb2_get_iface(f->udev, i);
+ if (iface == NULL) {
+ break;
+ }
+ if ((iface->subdev != NULL) &&
+ device_is_attached(iface->subdev)) {
+ ptr = device_get_nameunit(iface->subdev);
+ if (!first) {
+ strlcpy(buf, ", ", sizeof(buf));
+ } else {
+ buf[0] = 0;
+ }
+ strlcat(buf, ptr, sizeof(buf));
+ len = strlen(buf) + 1;
+ if (len > max_len) {
+ break;
+ }
+ error = copyout(buf, dst, len);
+ if (error) {
+ return (error);
+ }
+ len--;
+ dst += len;
+ max_len -= len;
+ first = 0;
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_gen_fill_deviceinfo
+ *
+ * This function dumps information about an USB device to the
+ * structure pointed to by the "di" argument.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static int
+usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di)
+{
+ struct usb2_device *udev;
+ struct usb2_device *hub;
+
+ if (f->flag_no_uref) {
+ /* control endpoint only */
+ return (EINVAL);
+ }
+ udev = f->udev;
+
+ bzero(di, sizeof(di[0]));
+
+ di->udi_bus = device_get_unit(udev->bus->bdev);
+ di->udi_addr = udev->address;
+ di->udi_index = udev->device_index;
+ strlcpy(di->udi_serial, udev->serial,
+ sizeof(di->udi_serial));
+ strlcpy(di->udi_vendor, udev->manufacturer,
+ sizeof(di->udi_vendor));
+ strlcpy(di->udi_product, udev->product,
+ sizeof(di->udi_product));
+ usb2_printBCD(di->udi_release, sizeof(di->udi_release),
+ UGETW(udev->ddesc.bcdDevice));
+ di->udi_vendorNo = UGETW(udev->ddesc.idVendor);
+ di->udi_productNo = UGETW(udev->ddesc.idProduct);
+ di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice);
+ di->udi_class = udev->ddesc.bDeviceClass;
+ di->udi_subclass = udev->ddesc.bDeviceSubClass;
+ di->udi_protocol = udev->ddesc.bDeviceProtocol;
+ di->udi_config_no = udev->curr_config_no;
+ di->udi_config_index = udev->curr_config_index;
+ di->udi_power = udev->flags.self_powered ? 0 : udev->power;
+ di->udi_speed = udev->speed;
+ di->udi_mode = udev->flags.usb2_mode;
+ di->udi_power_mode = udev->power_mode;
+ if (udev->flags.suspended) {
+ di->udi_suspended = 1;
+ } else {
+ di->udi_suspended = 0;
+ }
+
+ hub = udev->parent_hub;
+ if (hub) {
+ di->udi_hubaddr = hub->address;
+ di->udi_hubindex = hub->device_index;
+ di->udi_hubport = udev->port_no;
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ugen_check_request
+ *
+ * Return values:
+ * 0: Access allowed
+ * Else: No access
+ *------------------------------------------------------------------------*/
+static int
+ugen_check_request(struct usb2_device *udev, struct usb2_device_request *req)
+{
+ struct usb2_pipe *pipe;
+ int error;
+
+ /*
+ * Avoid requests that would damage the bus integrity:
+ */
+ if (((req->bmRequestType == UT_WRITE_DEVICE) &&
+ (req->bRequest == UR_SET_ADDRESS)) ||
+ ((req->bmRequestType == UT_WRITE_DEVICE) &&
+ (req->bRequest == UR_SET_CONFIG)) ||
+ ((req->bmRequestType == UT_WRITE_INTERFACE) &&
+ (req->bRequest == UR_SET_INTERFACE))) {
+ /*
+ * These requests can be useful for testing USB drivers.
+ */
+ error = priv_check(curthread, PRIV_DRIVER);
+ if (error) {
+ return (error);
+ }
+ }
+ /*
+ * Special case - handle clearing of stall
+ */
+ if (req->bmRequestType == UT_WRITE_ENDPOINT) {
+
+ pipe = usb2_get_pipe_by_addr(udev, req->wIndex[0]);
+ if (pipe == NULL) {
+ return (EINVAL);
+ }
+ if (usb2_check_thread_perm(udev, curthread, FREAD | FWRITE,
+ pipe->iface_index, req->wIndex[0] & UE_ADDR)) {
+ return (EPERM);
+ }
+ if ((req->bRequest == UR_CLEAR_FEATURE) &&
+ (UGETW(req->wValue) == UF_ENDPOINT_HALT)) {
+ usb2_clear_data_toggle(udev, pipe);
+ }
+ }
+ /* TODO: add more checks to verify the interface index */
+
+ return (0);
+}
+
+int
+ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur)
+{
+ int error;
+ uint16_t len;
+ uint16_t actlen;
+
+ if (f->flag_no_uref) {
+ /* control endpoint only */
+ return (EINVAL);
+ }
+ if (ugen_check_request(f->udev, &ur->ucr_request)) {
+ return (EPERM);
+ }
+ len = UGETW(ur->ucr_request.wLength);
+
+ /* check if "ucr_data" is valid */
+ if (len != 0) {
+ if (ur->ucr_data == NULL) {
+ return (EFAULT);
+ }
+ }
+ /* do the USB request */
+ error = usb2_do_request_flags
+ (f->udev, NULL, &ur->ucr_request, ur->ucr_data,
+ (ur->ucr_flags & USB_SHORT_XFER_OK) |
+ USB_USER_DATA_PTR, &actlen,
+ USB_DEFAULT_TIMEOUT);
+
+ ur->ucr_actlen = actlen;
+
+ if (error) {
+ error = EIO;
+ }
+ return (error);
+}
+
+/*------------------------------------------------------------------------
+ * ugen_re_enumerate
+ *------------------------------------------------------------------------*/
+static int
+ugen_re_enumerate(struct usb2_fifo *f)
+{
+ struct usb2_device *udev = f->udev;
+ int error;
+
+ if (f->flag_no_uref) {
+ /* control endpoint only */
+ return (EINVAL);
+ }
+ /*
+ * This request can be useful for testing USB drivers:
+ */
+ error = priv_check(curthread, PRIV_DRIVER);
+ if (error) {
+ return (error);
+ }
+ mtx_lock(f->priv_mtx);
+ error = usb2_req_re_enumerate(udev, f->priv_mtx);
+ mtx_unlock(f->priv_mtx);
+
+ if (error) {
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static int
+ugen_fs_uninit(struct usb2_fifo *f)
+{
+ if (f->fs_xfer == NULL) {
+ return (EINVAL);
+ }
+ usb2_transfer_unsetup(f->fs_xfer, f->fs_ep_max);
+ free(f->fs_xfer, M_USB);
+ f->fs_xfer = NULL;
+ f->fs_ep_max = 0;
+ f->fs_ep_ptr = NULL;
+ f->flag_iscomplete = 0;
+ f->flag_no_uref = 0; /* restore operation */
+ usb2_fifo_free_buffer(f);
+ return (0);
+}
+
+static uint8_t
+ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex)
+{
+ struct usb2_mbuf *m;
+
+ USB_IF_DEQUEUE(&f->used_q, m);
+
+ if (m) {
+ *pindex = *((uint8_t *)(m->cur_data_ptr));
+
+ USB_IF_ENQUEUE(&f->free_q, m);
+
+ return (0); /* success */
+ } else {
+
+ *pindex = 0; /* fix compiler warning */
+
+ f->flag_iscomplete = 0;
+ }
+ return (1); /* failure */
+}
+
+static void
+ugen_fs_set_complete(struct usb2_fifo *f, uint8_t index)
+{
+ struct usb2_mbuf *m;
+
+ USB_IF_DEQUEUE(&f->free_q, m);
+
+ if (m == NULL) {
+ /* can happen during close */
+ DPRINTF("out of buffers\n");
+ return;
+ }
+ USB_MBUF_RESET(m);
+
+ *((uint8_t *)(m->cur_data_ptr)) = index;
+
+ USB_IF_ENQUEUE(&f->used_q, m);
+
+ f->flag_iscomplete = 1;
+
+ usb2_fifo_wakeup(f);
+
+ return;
+}
+
+static int
+ugen_fs_copy_in(struct usb2_fifo *f, uint8_t ep_index)
+{
+ struct usb2_device_request *req;
+ struct usb2_xfer *xfer;
+ struct usb2_fs_endpoint fs_ep;
+ void *uaddr; /* userland pointer */
+ void *kaddr;
+ uint32_t offset;
+ uint32_t length;
+ uint32_t n;
+ uint32_t rem;
+ int error;
+ uint8_t isread;
+
+ if (ep_index >= f->fs_ep_max) {
+ return (EINVAL);
+ }
+ xfer = f->fs_xfer[ep_index];
+ if (xfer == NULL) {
+ return (EINVAL);
+ }
+ mtx_lock(f->priv_mtx);
+ if (usb2_transfer_pending(xfer)) {
+ mtx_unlock(f->priv_mtx);
+ return (EBUSY); /* should not happen */
+ }
+ mtx_unlock(f->priv_mtx);
+
+ error = copyin(f->fs_ep_ptr +
+ ep_index, &fs_ep, sizeof(fs_ep));
+ if (error) {
+ return (error);
+ }
+ /* security checks */
+
+ if (fs_ep.nFrames > xfer->max_frame_count) {
+ xfer->error = USB_ERR_INVAL;
+ goto complete;
+ }
+ if (fs_ep.nFrames == 0) {
+ xfer->error = USB_ERR_INVAL;
+ goto complete;
+ }
+ error = copyin(fs_ep.ppBuffer,
+ &uaddr, sizeof(uaddr));
+ if (error) {
+ return (error);
+ }
+ /* reset first frame */
+ usb2_set_frame_offset(xfer, 0, 0);
+
+ if (xfer->flags_int.control_xfr) {
+
+ req = xfer->frbuffers[0].buffer;
+
+ error = copyin(fs_ep.pLength,
+ &length, sizeof(length));
+ if (error) {
+ return (error);
+ }
+ if (length >= sizeof(*req)) {
+ xfer->error = USB_ERR_INVAL;
+ goto complete;
+ }
+ if (length != 0) {
+ error = copyin(uaddr, req, length);
+ if (error) {
+ return (error);
+ }
+ }
+ if (ugen_check_request(f->udev, req)) {
+ xfer->error = USB_ERR_INVAL;
+ goto complete;
+ }
+ xfer->frlengths[0] = length;
+
+ /* Host mode only ! */
+ if ((req->bmRequestType &
+ (UT_READ | UT_WRITE)) == UT_READ) {
+ isread = 1;
+ } else {
+ isread = 0;
+ }
+ n = 1;
+ offset = sizeof(*req);
+
+ } else {
+ /* Device and Host mode */
+ if (USB_GET_DATA_ISREAD(xfer)) {
+ isread = 1;
+ } else {
+ isread = 0;
+ }
+ n = 0;
+ offset = 0;
+ }
+
+ rem = xfer->max_data_length;
+ xfer->nframes = fs_ep.nFrames;
+ xfer->timeout = fs_ep.timeout;
+ if (xfer->timeout > 65535) {
+ xfer->timeout = 65535;
+ }
+ if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK)
+ xfer->flags.short_xfer_ok = 1;
+ else
+ xfer->flags.short_xfer_ok = 0;
+
+ if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK)
+ xfer->flags.short_frames_ok = 1;
+ else
+ xfer->flags.short_frames_ok = 0;
+
+ if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT)
+ xfer->flags.force_short_xfer = 1;
+ else
+ xfer->flags.force_short_xfer = 0;
+
+ if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL)
+ xfer->flags.stall_pipe = 1;
+ else
+ xfer->flags.stall_pipe = 0;
+
+ for (; n != xfer->nframes; n++) {
+
+ error = copyin(fs_ep.pLength + n,
+ &length, sizeof(length));
+ if (error) {
+ break;
+ }
+ xfer->frlengths[n] = length;
+
+ if (length > rem) {
+ xfer->error = USB_ERR_INVAL;
+ goto complete;
+ }
+ rem -= length;
+
+ if (!isread) {
+
+ /* we need to know the source buffer */
+ error = copyin(fs_ep.ppBuffer + n,
+ &uaddr, sizeof(uaddr));
+ if (error) {
+ break;
+ }
+ if (xfer->flags_int.isochronous_xfr) {
+ /* get kernel buffer address */
+ kaddr = xfer->frbuffers[0].buffer;
+ kaddr = USB_ADD_BYTES(kaddr, offset);
+ } else {
+ /* set current frame offset */
+ usb2_set_frame_offset(xfer, offset, n);
+
+ /* get kernel buffer address */
+ kaddr = xfer->frbuffers[n].buffer;
+ }
+
+ /* move data */
+ error = copyin(uaddr, kaddr, length);
+ if (error) {
+ break;
+ }
+ }
+ offset += length;
+ }
+ return (error);
+
+complete:
+ mtx_lock(f->priv_mtx);
+ ugen_fs_set_complete(f, ep_index);
+ mtx_unlock(f->priv_mtx);
+ return (0);
+}
+
+static int
+ugen_fs_copy_out(struct usb2_fifo *f, uint8_t ep_index)
+{
+ struct usb2_device_request *req;
+ struct usb2_xfer *xfer;
+ struct usb2_fs_endpoint fs_ep;
+ struct usb2_fs_endpoint *fs_ep_uptr; /* userland ptr */
+ void *uaddr; /* userland ptr */
+ void *kaddr;
+ uint32_t offset;
+ uint32_t length;
+ uint32_t temp;
+ uint32_t n;
+ uint32_t rem;
+ int error;
+ uint8_t isread;
+
+ if (ep_index >= f->fs_ep_max) {
+ return (EINVAL);
+ }
+ xfer = f->fs_xfer[ep_index];
+ if (xfer == NULL) {
+ return (EINVAL);
+ }
+ mtx_lock(f->priv_mtx);
+ if (usb2_transfer_pending(xfer)) {
+ mtx_unlock(f->priv_mtx);
+ return (EBUSY); /* should not happen */
+ }
+ mtx_unlock(f->priv_mtx);
+
+ fs_ep_uptr = f->fs_ep_ptr + ep_index;
+ error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep));
+ if (error) {
+ return (error);
+ }
+ fs_ep.status = xfer->error;
+ fs_ep.aFrames = xfer->aframes;
+ fs_ep.isoc_time_complete = xfer->isoc_time_complete;
+ if (xfer->error) {
+ goto complete;
+ }
+ if (xfer->flags_int.control_xfr) {
+ req = xfer->frbuffers[0].buffer;
+
+ /* Host mode only ! */
+ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) {
+ isread = 1;
+ } else {
+ isread = 0;
+ }
+ if (xfer->nframes == 0)
+ n = 0; /* should never happen */
+ else
+ n = 1;
+ } else {
+ /* Device and Host mode */
+ if (USB_GET_DATA_ISREAD(xfer)) {
+ isread = 1;
+ } else {
+ isread = 0;
+ }
+ n = 0;
+ }
+
+ /* Update lengths and copy out data */
+
+ rem = xfer->max_data_length;
+ offset = 0;
+
+ for (; n != xfer->nframes; n++) {
+
+ /* get initial length into "temp" */
+ error = copyin(fs_ep.pLength + n,
+ &temp, sizeof(temp));
+ if (error) {
+ return (error);
+ }
+ if (temp > rem) {
+ /* the userland length has been corrupted */
+ DPRINTF("corrupt userland length "
+ "%u > %u\n", temp, rem);
+ fs_ep.status = USB_ERR_INVAL;
+ goto complete;
+ }
+ rem -= temp;
+
+ /* get actual transfer length */
+ length = xfer->frlengths[n];
+ if (length > temp) {
+ /* data overflow */
+ fs_ep.status = USB_ERR_INVAL;
+ DPRINTF("data overflow %u > %u\n",
+ length, temp);
+ goto complete;
+ }
+ if (isread) {
+
+ /* we need to know the destination buffer */
+ error = copyin(fs_ep.ppBuffer + n,
+ &uaddr, sizeof(uaddr));
+ if (error) {
+ return (error);
+ }
+ if (xfer->flags_int.isochronous_xfr) {
+ /* only one frame buffer */
+ kaddr = USB_ADD_BYTES(
+ xfer->frbuffers[0].buffer, offset);
+ } else {
+ /* multiple frame buffers */
+ kaddr = xfer->frbuffers[n].buffer;
+ }
+
+ /* move data */
+ error = copyout(kaddr, uaddr, length);
+ if (error) {
+ return (error);
+ }
+ }
+ /*
+ * Update offset according to initial length, which is
+ * needed by isochronous transfers!
+ */
+ offset += temp;
+
+ /* update length */
+ error = copyout(&length,
+ fs_ep.pLength + n, sizeof(length));
+ if (error) {
+ return (error);
+ }
+ }
+
+complete:
+ /* update "aFrames" */
+ error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames,
+ sizeof(fs_ep.aFrames));
+ if (error)
+ goto done;
+
+ /* update "isoc_time_complete" */
+ error = copyout(&fs_ep.isoc_time_complete,
+ &fs_ep_uptr->isoc_time_complete,
+ sizeof(fs_ep.isoc_time_complete));
+ if (error)
+ goto done;
+ /* update "status" */
+ error = copyout(&fs_ep.status, &fs_ep_uptr->status,
+ sizeof(fs_ep.status));
+done:
+ return (error);
+}
+
+static uint8_t
+ugen_fifo_in_use(struct usb2_fifo *f, int fflags)
+{
+ struct usb2_fifo *f_rx;
+ struct usb2_fifo *f_tx;
+
+ f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX];
+ f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX];
+
+ if ((fflags & FREAD) && f_rx &&
+ (f_rx->xfer[0] || f_rx->xfer[1])) {
+ return (1); /* RX FIFO in use */
+ }
+ if ((fflags & FWRITE) && f_tx &&
+ (f_tx->xfer[0] || f_tx->xfer[1])) {
+ return (1); /* TX FIFO in use */
+ }
+ return (0); /* not in use */
+}
+
+static int
+ugen_fs_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags)
+{
+ struct usb2_config usb2_config[1];
+ struct usb2_device_request req;
+ union {
+ struct usb2_fs_complete *pcomp;
+ struct usb2_fs_start *pstart;
+ struct usb2_fs_stop *pstop;
+ struct usb2_fs_init *pinit;
+ struct usb2_fs_uninit *puninit;
+ struct usb2_fs_open *popen;
+ struct usb2_fs_close *pclose;
+ struct usb2_fs_clear_stall_sync *pstall;
+ void *addr;
+ } u;
+ struct usb2_pipe *pipe;
+ struct usb2_endpoint_descriptor *ed;
+ int error = 0;
+ uint8_t iface_index;
+ uint8_t isread;
+ uint8_t ep_index;
+
+ u.addr = addr;
+
+ switch (cmd) {
+ case USB_FS_COMPLETE:
+ mtx_lock(f->priv_mtx);
+ error = ugen_fs_get_complete(f, &ep_index);
+ mtx_unlock(f->priv_mtx);
+
+ if (error) {
+ error = EBUSY;
+ break;
+ }
+ u.pcomp->ep_index = ep_index;
+ error = ugen_fs_copy_out(f, u.pcomp->ep_index);
+ break;
+
+ case USB_FS_START:
+ error = ugen_fs_copy_in(f, u.pstart->ep_index);
+ if (error) {
+ break;
+ }
+ mtx_lock(f->priv_mtx);
+ usb2_transfer_start(f->fs_xfer[u.pstart->ep_index]);
+ mtx_unlock(f->priv_mtx);
+ break;
+
+ case USB_FS_STOP:
+ if (u.pstop->ep_index >= f->fs_ep_max) {
+ error = EINVAL;
+ break;
+ }
+ mtx_lock(f->priv_mtx);
+ usb2_transfer_stop(f->fs_xfer[u.pstop->ep_index]);
+ mtx_unlock(f->priv_mtx);
+ break;
+
+ case USB_FS_INIT:
+ /* verify input parameters */
+ if (u.pinit->pEndpoints == NULL) {
+ error = EINVAL;
+ break;
+ }
+ if (u.pinit->ep_index_max > 127) {
+ error = EINVAL;
+ break;
+ }
+ if (u.pinit->ep_index_max == 0) {
+ error = EINVAL;
+ break;
+ }
+ if (f->fs_xfer != NULL) {
+ error = EBUSY;
+ break;
+ }
+ if (f->flag_no_uref) {
+ error = EINVAL;
+ break;
+ }
+ if (f->dev_ep_index != 0) {
+ error = EINVAL;
+ break;
+ }
+ if (ugen_fifo_in_use(f, fflags)) {
+ error = EBUSY;
+ break;
+ }
+ error = usb2_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max);
+ if (error) {
+ break;
+ }
+ f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) *
+ u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO);
+ if (f->fs_xfer == NULL) {
+ usb2_fifo_free_buffer(f);
+ error = ENOMEM;
+ break;
+ }
+ f->fs_ep_max = u.pinit->ep_index_max;
+ f->fs_ep_ptr = u.pinit->pEndpoints;
+ break;
+
+ case USB_FS_UNINIT:
+ if (u.puninit->dummy != 0) {
+ error = EINVAL;
+ break;
+ }
+ error = ugen_fs_uninit(f);
+ break;
+
+ case USB_FS_OPEN:
+ if (u.popen->ep_index >= f->fs_ep_max) {
+ error = EINVAL;
+ break;
+ }
+ if (f->fs_xfer[u.popen->ep_index] != NULL) {
+ error = EBUSY;
+ break;
+ }
+ if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) {
+ u.popen->max_bufsize = USB_FS_MAX_BUFSIZE;
+ }
+ if (u.popen->max_frames > USB_FS_MAX_FRAMES) {
+ u.popen->max_frames = USB_FS_MAX_FRAMES;
+ break;
+ }
+ if (u.popen->max_frames == 0) {
+ error = EINVAL;
+ break;
+ }
+ pipe = usb2_get_pipe_by_addr(f->udev, u.popen->ep_no);
+ if (pipe == NULL) {
+ error = EINVAL;
+ break;
+ }
+ ed = pipe->edesc;
+ if (ed == NULL) {
+ error = ENXIO;
+ break;
+ }
+ iface_index = pipe->iface_index;
+
+ error = usb2_check_thread_perm(f->udev, curthread, fflags,
+ iface_index, u.popen->ep_no);
+ if (error) {
+ break;
+ }
+ bzero(usb2_config, sizeof(usb2_config));
+
+ usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE;
+ usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
+ usb2_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN);
+ usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL;
+ usb2_config[0].mh.flags.proxy_buffer = 1;
+ usb2_config[0].mh.callback = &ugen_default_fs_callback;
+ usb2_config[0].mh.timeout = 0; /* no timeout */
+ usb2_config[0].mh.frames = u.popen->max_frames;
+ usb2_config[0].mh.bufsize = u.popen->max_bufsize;
+ usb2_config[0].md = usb2_config[0].mh; /* symmetric config */
+
+ if (usb2_config[0].type == UE_CONTROL) {
+ if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+ error = EINVAL;
+ break;
+ }
+ } else {
+
+ isread = ((usb2_config[0].endpoint &
+ (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN);
+
+ if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+ isread = !isread;
+ }
+ /* check permissions */
+ if (isread) {
+ if (!(fflags & FREAD)) {
+ error = EPERM;
+ break;
+ }
+ } else {
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ }
+ }
+ error = usb2_transfer_setup(f->udev, &iface_index,
+ f->fs_xfer + u.popen->ep_index, usb2_config, 1,
+ f, f->priv_mtx);
+ if (error == 0) {
+ /* update maximums */
+ u.popen->max_packet_length =
+ f->fs_xfer[u.popen->ep_index]->max_frame_size;
+ u.popen->max_bufsize =
+ f->fs_xfer[u.popen->ep_index]->max_data_length;
+ f->fs_xfer[u.popen->ep_index]->priv_fifo =
+ ((uint8_t *)0) + u.popen->ep_index;
+ /*
+ * Increase performance by dropping locks we
+ * don't need:
+ */
+ f->flag_no_uref = 1;
+ } else {
+ error = ENOMEM;
+ }
+ break;
+
+ case USB_FS_CLOSE:
+ if (u.pclose->ep_index >= f->fs_ep_max) {
+ error = EINVAL;
+ break;
+ }
+ if (f->fs_xfer[u.pclose->ep_index] == NULL) {
+ error = EINVAL;
+ break;
+ }
+ usb2_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1);
+ break;
+
+ case USB_FS_CLEAR_STALL_SYNC:
+ if (u.pstall->ep_index >= f->fs_ep_max) {
+ error = EINVAL;
+ break;
+ }
+ if (f->fs_xfer[u.pstall->ep_index] == NULL) {
+ error = EINVAL;
+ break;
+ }
+ if (f->udev->flags.usb2_mode != USB_MODE_HOST) {
+ error = EINVAL;
+ break;
+ }
+ mtx_lock(f->priv_mtx);
+ error = usb2_transfer_pending(f->fs_xfer[u.pstall->ep_index]);
+ mtx_unlock(f->priv_mtx);
+
+ if (error) {
+ return (EBUSY);
+ }
+ pipe = f->fs_xfer[u.pstall->ep_index]->pipe;
+
+ /* setup a clear-stall packet */
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ req.wIndex[0] = pipe->edesc->bEndpointAddress;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ error = usb2_do_request(f->udev, NULL, &req, NULL);
+ if (error == 0) {
+ usb2_clear_data_toggle(f->udev, pipe);
+ } else {
+ error = ENXIO;
+ }
+ break;
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+static int
+ugen_set_short_xfer(struct usb2_fifo *f, void *addr)
+{
+ uint8_t t;
+
+ if (*(int *)addr)
+ t = 1;
+ else
+ t = 0;
+
+ if (f->flag_short == t) {
+ /* same value like before - accept */
+ return (0);
+ }
+ if (f->xfer[0] || f->xfer[1]) {
+ /* cannot change this during transfer */
+ return (EBUSY);
+ }
+ f->flag_short = t;
+ return (0);
+}
+
+static int
+ugen_set_timeout(struct usb2_fifo *f, void *addr)
+{
+ f->timeout = *(int *)addr;
+ if (f->timeout > 65535) {
+ /* limit user input */
+ f->timeout = 65535;
+ }
+ return (0);
+}
+
+static int
+ugen_get_frame_size(struct usb2_fifo *f, void *addr)
+{
+ if (f->xfer[0]) {
+ *(int *)addr = f->xfer[0]->max_frame_size;
+ } else {
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+ugen_set_buffer_size(struct usb2_fifo *f, void *addr)
+{
+ uint32_t t;
+
+ if (*(int *)addr < 1024)
+ t = 1024;
+ else if (*(int *)addr < (256 * 1024))
+ t = *(int *)addr;
+ else
+ t = 256 * 1024;
+
+ if (f->bufsize == t) {
+ /* same value like before - accept */
+ return (0);
+ }
+ if (f->xfer[0] || f->xfer[1]) {
+ /* cannot change this during transfer */
+ return (EBUSY);
+ }
+ f->bufsize = t;
+ return (0);
+}
+
+static int
+ugen_get_buffer_size(struct usb2_fifo *f, void *addr)
+{
+ *(int *)addr = f->bufsize;
+ return (0);
+}
+
+static int
+ugen_get_iface_desc(struct usb2_fifo *f,
+ struct usb2_interface_descriptor *idesc)
+{
+ struct usb2_interface *iface;
+
+ iface = usb2_get_iface(f->udev, f->iface_index);
+ if (iface && iface->idesc) {
+ *idesc = *(iface->idesc);
+ } else {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ugen_get_endpoint_desc(struct usb2_fifo *f,
+ struct usb2_endpoint_descriptor *ed)
+{
+ struct usb2_pipe *pipe;
+
+ pipe = f->priv_sc0;
+
+ if (pipe && pipe->edesc) {
+ *ed = *pipe->edesc;
+ } else {
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+ugen_set_power_mode(struct usb2_fifo *f, int mode)
+{
+ struct usb2_device *udev = f->udev;
+ int err;
+
+ if ((udev == NULL) ||
+ (udev->parent_hub == NULL)) {
+ return (EINVAL);
+ }
+ err = priv_check(curthread, PRIV_ROOT);
+ if (err) {
+ return (err);
+ }
+ switch (mode) {
+ case USB_POWER_MODE_OFF:
+ /* clear suspend */
+ err = usb2_req_clear_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ if (err)
+ break;
+
+ /* clear port enable */
+ err = usb2_req_clear_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_ENABLE);
+ break;
+
+ case USB_POWER_MODE_ON:
+ /* enable port */
+ err = usb2_req_set_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_ENABLE);
+
+ /* FALLTHROUGH */
+
+ case USB_POWER_MODE_SAVE:
+ case USB_POWER_MODE_RESUME:
+ /* TODO: implement USB power save */
+ err = usb2_req_clear_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ break;
+
+ case USB_POWER_MODE_SUSPEND:
+ /* TODO: implement USB power save */
+ err = usb2_req_set_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (err)
+ return (ENXIO); /* I/O failure */
+
+ udev->power_mode = mode; /* update copy of power mode */
+
+ return (0); /* success */
+}
+
+static int
+ugen_get_power_mode(struct usb2_fifo *f)
+{
+ struct usb2_device *udev = f->udev;
+
+ if ((udev == NULL) ||
+ (udev->parent_hub == NULL)) {
+ return (USB_POWER_MODE_ON);
+ }
+ return (udev->power_mode);
+}
+
+static int
+ugen_do_port_feature(struct usb2_fifo *f, uint8_t port_no,
+ uint8_t set, uint16_t feature)
+{
+ struct usb2_device *udev = f->udev;
+ struct usb2_hub *hub;
+ int err;
+
+ err = priv_check(curthread, PRIV_ROOT);
+ if (err) {
+ return (err);
+ }
+ if (port_no == 0) {
+ return (EINVAL);
+ }
+ if ((udev == NULL) ||
+ (udev->hub == NULL)) {
+ return (EINVAL);
+ }
+ hub = udev->hub;
+
+ if (port_no > hub->nports) {
+ return (EINVAL);
+ }
+ if (set)
+ err = usb2_req_set_port_feature(udev,
+ NULL, port_no, feature);
+ else
+ err = usb2_req_clear_port_feature(udev,
+ NULL, port_no, feature);
+
+ if (err)
+ return (ENXIO); /* failure */
+
+ return (0); /* success */
+}
+
+static int
+ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags)
+{
+ struct usb2_fifo *f_rx;
+ struct usb2_fifo *f_tx;
+ int error = 0;
+
+ f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX];
+ f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX];
+
+ switch (cmd) {
+ case USB_SET_RX_SHORT_XFER:
+ if (fflags & FREAD) {
+ error = ugen_set_short_xfer(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_TX_FORCE_SHORT:
+ if (fflags & FWRITE) {
+ error = ugen_set_short_xfer(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_RX_TIMEOUT:
+ if (fflags & FREAD) {
+ error = ugen_set_timeout(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_TX_TIMEOUT:
+ if (fflags & FWRITE) {
+ error = ugen_set_timeout(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_RX_FRAME_SIZE:
+ if (fflags & FREAD) {
+ error = ugen_get_frame_size(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_TX_FRAME_SIZE:
+ if (fflags & FWRITE) {
+ error = ugen_get_frame_size(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_RX_BUFFER_SIZE:
+ if (fflags & FREAD) {
+ error = ugen_set_buffer_size(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_TX_BUFFER_SIZE:
+ if (fflags & FWRITE) {
+ error = ugen_set_buffer_size(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_RX_BUFFER_SIZE:
+ if (fflags & FREAD) {
+ error = ugen_get_buffer_size(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_TX_BUFFER_SIZE:
+ if (fflags & FWRITE) {
+ error = ugen_get_buffer_size(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_RX_INTERFACE_DESC:
+ if (fflags & FREAD) {
+ error = ugen_get_iface_desc(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_TX_INTERFACE_DESC:
+ if (fflags & FWRITE) {
+ error = ugen_get_iface_desc(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_RX_ENDPOINT_DESC:
+ if (fflags & FREAD) {
+ error = ugen_get_endpoint_desc(f_rx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_GET_TX_ENDPOINT_DESC:
+ if (fflags & FWRITE) {
+ error = ugen_get_endpoint_desc(f_tx, addr);
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_RX_STALL_FLAG:
+ if ((fflags & FREAD) && (*(int *)addr)) {
+ f_rx->flag_stall = 1;
+ }
+ break;
+
+ case USB_SET_TX_STALL_FLAG:
+ if ((fflags & FWRITE) && (*(int *)addr)) {
+ f_tx->flag_stall = 1;
+ }
+ break;
+
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
+
+static int
+ugen_ctrl_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags)
+{
+ union {
+ struct usb2_interface_descriptor *idesc;
+ struct usb2_alt_interface *ai;
+ struct usb2_device_descriptor *ddesc;
+ struct usb2_config_descriptor *cdesc;
+ struct usb2_device_stats *stat;
+ uint32_t *ptime;
+ void *addr;
+ int *pint;
+ } u;
+ struct usb2_device_descriptor *dtemp;
+ struct usb2_config_descriptor *ctemp;
+ struct usb2_interface *iface;
+ int error = 0;
+ uint8_t n;
+
+ u.addr = addr;
+
+ switch (cmd) {
+ case USB_DISCOVER:
+ usb2_needs_explore_all();
+ break;
+
+ case USB_SETDEBUG:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ usb2_debug = *(int *)addr;
+ break;
+
+ case USB_GET_CONFIG:
+ *(int *)addr = f->udev->curr_config_index;
+ break;
+
+ case USB_SET_CONFIG:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ error = ugen_set_config(f, *(int *)addr);
+ break;
+
+ case USB_GET_ALTINTERFACE:
+ iface = usb2_get_iface(f->udev,
+ u.ai->uai_interface_index);
+ if (iface && iface->idesc) {
+ u.ai->uai_alt_index = iface->alt_index;
+ } else {
+ error = EINVAL;
+ }
+ break;
+
+ case USB_SET_ALTINTERFACE:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ error = ugen_set_interface(f,
+ u.ai->uai_interface_index, u.ai->uai_alt_index);
+ break;
+
+ case USB_GET_DEVICE_DESC:
+ dtemp = usb2_get_device_descriptor(f->udev);
+ if (!dtemp) {
+ error = EIO;
+ break;
+ }
+ *u.ddesc = *dtemp;
+ break;
+
+ case USB_GET_CONFIG_DESC:
+ ctemp = usb2_get_config_descriptor(f->udev);
+ if (!ctemp) {
+ error = EIO;
+ break;
+ }
+ *u.cdesc = *ctemp;
+ break;
+
+ case USB_GET_FULL_DESC:
+ error = ugen_get_cdesc(f, addr);
+ break;
+
+ case USB_GET_STRING_DESC:
+ error = ugen_get_sdesc(f, addr);
+ break;
+
+ case USB_REQUEST:
+ case USB_DO_REQUEST:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ error = ugen_do_request(f, addr);
+ break;
+
+ case USB_DEVICEINFO:
+ case USB_GET_DEVICEINFO:
+ error = usb2_gen_fill_deviceinfo(f, addr);
+ break;
+
+ case USB_GET_DEVICENAMES:
+ error = usb2_gen_fill_devicenames(f, addr);
+ break;
+
+ case USB_DEVICESTATS:
+ for (n = 0; n != 4; n++) {
+
+ u.stat->uds_requests_fail[n] =
+ f->udev->bus->stats_err.uds_requests[n];
+
+ u.stat->uds_requests_ok[n] =
+ f->udev->bus->stats_ok.uds_requests[n];
+ }
+ break;
+
+ case USB_DEVICEENUMERATE:
+ error = ugen_re_enumerate(f);
+ break;
+
+ case USB_GET_PLUGTIME:
+ *u.ptime = f->udev->plugtime;
+ break;
+
+ case USB_CLAIM_INTERFACE:
+ case USB_RELEASE_INTERFACE:
+ /* TODO */
+ break;
+
+ case USB_IFACE_DRIVER_ACTIVE:
+ /* TODO */
+ *u.pint = 0;
+ break;
+
+ case USB_IFACE_DRIVER_DETACH:
+ /* TODO */
+ error = priv_check(curthread, PRIV_DRIVER);
+ if (error) {
+ break;
+ }
+ error = EINVAL;
+ break;
+
+ case USB_SET_POWER_MODE:
+ error = ugen_set_power_mode(f, *u.pint);
+ break;
+
+ case USB_GET_POWER_MODE:
+ *u.pint = ugen_get_power_mode(f);
+ break;
+
+ case USB_SET_PORT_ENABLE:
+ error = ugen_do_port_feature(f,
+ *u.pint, 1, UHF_PORT_ENABLE);
+ break;
+
+ case USB_SET_PORT_DISABLE:
+ error = ugen_do_port_feature(f,
+ *u.pint, 0, UHF_PORT_ENABLE);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static int
+ugen_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags,
+ struct thread *td)
+{
+ int error;
+
+ DPRINTFN(6, "cmd=%08lx\n", cmd);
+ error = ugen_fs_ioctl(f, cmd, addr, fflags);
+ if (error == ENOTTY) {
+ if (f->flag_no_uref) {
+ mtx_lock(f->priv_mtx);
+ error = ugen_iface_ioctl(f, cmd, addr, fflags);
+ mtx_unlock(f->priv_mtx);
+ } else {
+ error = ugen_ctrl_ioctl(f, cmd, addr, fflags);
+ }
+ }
+ DPRINTFN(6, "error=%d\n", error);
+ return (error);
+}
+
+static void
+ugen_default_fs_callback(struct usb2_xfer *xfer)
+{
+ ; /* workaround for a bug in "indent" */
+
+ DPRINTF("st=%u alen=%u aframes=%u\n",
+ USB_GET_STATE(xfer), xfer->actlen, xfer->aframes);
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ usb2_start_hardware(xfer);
+ break;
+ default:
+ ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo));
+ break;
+ }
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_generic.h b/sys/dev/usb2/core/usb2_generic.h
new file mode 100644
index 000000000000..3a4e7c940ea4
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_generic.h
@@ -0,0 +1,33 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_GENERIC_H_
+#define _USB2_GENERIC_H_
+
+extern struct usb2_fifo_methods usb2_ugen_methods;
+int ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur);
+
+#endif /* _USB2_GENERIC_H_ */
diff --git a/sys/dev/usb2/core/usb2_handle_request.c b/sys/dev/usb2/core/usb2_handle_request.c
new file mode 100644
index 000000000000..7dc82c072699
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_handle_request.c
@@ -0,0 +1,750 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_dynamic.h>
+#include <dev/usb2/core/usb2_hub.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+/* enum */
+
+enum {
+ ST_DATA,
+ ST_POST_STATUS,
+};
+
+/* function prototypes */
+
+static uint8_t usb2_handle_get_stall(struct usb2_device *udev, uint8_t ea_val);
+static usb2_error_t usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on);
+static usb2_error_t usb2_handle_request(struct usb2_xfer *xfer);
+static usb2_error_t usb2_handle_set_config(struct usb2_xfer *xfer, uint8_t conf_no);
+static usb2_error_t usb2_handle_set_stall(struct usb2_xfer *xfer, uint8_t ep, uint8_t do_stall);
+static usb2_error_t usb2_handle_iface_request(struct usb2_xfer *xfer, void **ppdata, uint16_t *plen, struct usb2_device_request req, uint16_t off, uint8_t state);
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_request_callback
+ *
+ * This function is the USB callback for generic USB Device control
+ * transfers.
+ *------------------------------------------------------------------------*/
+void
+usb2_handle_request_callback(struct usb2_xfer *xfer)
+{
+ usb2_error_t err;
+
+ /* check the current transfer state */
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+
+ /* handle the request */
+ err = usb2_handle_request(xfer);
+
+ if (err) {
+
+ if (err == USB_ERR_BAD_CONTEXT) {
+ /* we need to re-setup the control transfer */
+ usb2_needs_explore(xfer->udev->bus, 0);
+ break;
+ }
+ /*
+ * If no control transfer is active,
+ * receive the next SETUP message:
+ */
+ goto tr_restart;
+ }
+ usb2_start_hardware(xfer);
+ break;
+
+ default:
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* should not happen - try stalling */
+ goto tr_restart;
+ }
+ break;
+ }
+ return;
+
+tr_restart:
+ xfer->frlengths[0] = sizeof(struct usb2_device_request);
+ xfer->nframes = 1;
+ xfer->flags.manual_status = 1;
+ xfer->flags.force_short_xfer = 0;
+ xfer->flags.stall_pipe = 1; /* cancel previous transfer, if any */
+ usb2_start_hardware(xfer);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_set_config
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_handle_set_config(struct usb2_xfer *xfer, uint8_t conf_no)
+{
+ usb2_error_t err = 0;
+
+ /*
+ * We need to protect against other threads doing probe and
+ * attach:
+ */
+ mtx_unlock(xfer->priv_mtx);
+ mtx_lock(&Giant); /* XXX */
+ sx_xlock(xfer->udev->default_sx + 1);
+
+ if (conf_no == USB_UNCONFIG_NO) {
+ conf_no = USB_UNCONFIG_INDEX;
+ } else {
+ /*
+ * The relationship between config number and config index
+ * is very simple in our case:
+ */
+ conf_no--;
+ }
+
+ if (usb2_set_config_index(xfer->udev, conf_no)) {
+ DPRINTF("set config %d failed\n", conf_no);
+ err = USB_ERR_STALLED;
+ goto done;
+ }
+ if (usb2_probe_and_attach(xfer->udev, USB_IFACE_INDEX_ANY)) {
+ DPRINTF("probe and attach failed\n");
+ err = USB_ERR_STALLED;
+ goto done;
+ }
+done:
+ mtx_unlock(&Giant); /* XXX */
+ sx_unlock(xfer->udev->default_sx + 1);
+ mtx_lock(xfer->priv_mtx);
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_iface_request
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_handle_iface_request(struct usb2_xfer *xfer,
+ void **ppdata, uint16_t *plen,
+ struct usb2_device_request req, uint16_t off, uint8_t state)
+{
+ struct usb2_interface *iface;
+ struct usb2_interface *iface_parent; /* parent interface */
+ struct usb2_device *udev = xfer->udev;
+ int error;
+ uint8_t iface_index;
+
+ if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
+ iface_index = req.wIndex[0]; /* unicast */
+ } else {
+ iface_index = 0; /* broadcast */
+ }
+
+ /*
+ * We need to protect against other threads doing probe and
+ * attach:
+ */
+ mtx_unlock(xfer->priv_mtx);
+ mtx_lock(&Giant); /* XXX */
+ sx_xlock(udev->default_sx + 1);
+
+ error = ENXIO;
+
+tr_repeat:
+ iface = usb2_get_iface(udev, iface_index);
+ if ((iface == NULL) ||
+ (iface->idesc == NULL)) {
+ /* end of interfaces non-existing interface */
+ goto tr_stalled;
+ }
+ /* forward request to interface, if any */
+
+ if ((error != 0) &&
+ (error != ENOTTY) &&
+ (iface->subdev != NULL) &&
+ device_is_attached(iface->subdev)) {
+#if 0
+ DEVMETHOD(usb2_handle_request, NULL); /* dummy */
+#endif
+ error = USB2_HANDLE_REQUEST(iface->subdev,
+ &req, ppdata, plen,
+ off, (state == ST_POST_STATUS));
+ }
+ iface_parent = usb2_get_iface(udev, iface->parent_iface_index);
+
+ if ((iface_parent == NULL) ||
+ (iface_parent->idesc == NULL)) {
+ /* non-existing interface */
+ iface_parent = NULL;
+ }
+ /* forward request to parent interface, if any */
+
+ if ((error != 0) &&
+ (error != ENOTTY) &&
+ (iface_parent != NULL) &&
+ (iface_parent->subdev != NULL) &&
+ ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
+ (iface_parent->subdev != iface->subdev) &&
+ device_is_attached(iface_parent->subdev)) {
+ error = USB2_HANDLE_REQUEST(iface_parent->subdev,
+ &req, ppdata, plen, off,
+ (state == ST_POST_STATUS));
+ }
+ if (error == 0) {
+ /* negativly adjust pointer and length */
+ *ppdata = ((uint8_t *)(*ppdata)) - off;
+ *plen += off;
+ goto tr_valid;
+ } else if (error == ENOTTY) {
+ goto tr_stalled;
+ }
+ if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
+ iface_index++; /* iterate */
+ goto tr_repeat;
+ }
+ if (state == ST_POST_STATUS) {
+ /* we are complete */
+ goto tr_valid;
+ }
+ switch (req.bmRequestType) {
+ case UT_WRITE_INTERFACE:
+ switch (req.bRequest) {
+ case UR_SET_INTERFACE:
+ /*
+ * Handle special case. If we have parent interface
+ * we just reset the endpoints, because this is a
+ * multi interface device and re-attaching only a
+ * part of the device is not possible. Also if the
+ * alternate setting is the same like before we just
+ * reset the interface endoints.
+ */
+ if ((iface_parent != NULL) ||
+ (iface->alt_index == req.wValue[0])) {
+ error = usb2_reset_iface_endpoints(udev,
+ iface_index);
+ if (error) {
+ DPRINTF("alt setting failed %s\n",
+ usb2_errstr(error));
+ goto tr_stalled;
+ }
+ break;
+ }
+ error = usb2_set_alt_interface_index(udev,
+ iface_index, req.wValue[0]);
+ if (error) {
+ DPRINTF("alt setting failed %s\n",
+ usb2_errstr(error));
+ goto tr_stalled;
+ }
+ error = usb2_probe_and_attach(udev,
+ iface_index);
+ if (error) {
+ DPRINTF("alt setting probe failed\n");
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (req.bRequest) {
+ case UR_GET_INTERFACE:
+ *ppdata = &iface->alt_index;
+ *plen = 1;
+ break;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+tr_valid:
+ mtx_unlock(&Giant);
+ sx_unlock(udev->default_sx + 1);
+ mtx_lock(xfer->priv_mtx);
+ return (0);
+
+tr_stalled:
+ mtx_unlock(&Giant);
+ sx_unlock(udev->default_sx + 1);
+ mtx_lock(xfer->priv_mtx);
+ return (USB_ERR_STALLED);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_stall
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_handle_set_stall(struct usb2_xfer *xfer, uint8_t ep, uint8_t do_stall)
+{
+ usb2_error_t err;
+
+ mtx_unlock(xfer->priv_mtx);
+ err = usb2_set_endpoint_stall(xfer->udev,
+ usb2_get_pipe_by_addr(xfer->udev, ep), do_stall);
+ mtx_lock(xfer->priv_mtx);
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_get_stall
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_handle_get_stall(struct usb2_device *udev, uint8_t ea_val)
+{
+ struct usb2_pipe *pipe;
+ uint8_t halted;
+
+ pipe = usb2_get_pipe_by_addr(udev, ea_val);
+ if (pipe == NULL) {
+ /* nothing to do */
+ return (0);
+ }
+ mtx_lock(&udev->bus->mtx);
+ halted = pipe->is_stalled;
+ mtx_unlock(&udev->bus->mtx);
+
+ return (halted);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_remote_wakeup
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on)
+{
+ struct usb2_device *udev;
+ struct usb2_bus *bus;
+
+ udev = xfer->udev;
+ bus = udev->bus;
+
+ mtx_lock(&bus->mtx);
+
+ if (is_on) {
+ udev->flags.remote_wakeup = 1;
+ } else {
+ udev->flags.remote_wakeup = 0;
+ }
+
+ (bus->methods->rem_wakeup_set) (xfer->udev, is_on);
+
+ mtx_unlock(&bus->mtx);
+
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_handle_request
+ *
+ * Internal state sequence:
+ *
+ * ST_DATA -> ST_POST_STATUS
+ *
+ * Returns:
+ * 0: Ready to start hardware
+ * Else: Stall current transfer, if any
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_handle_request(struct usb2_xfer *xfer)
+{
+ struct usb2_device_request req;
+ struct usb2_device *udev;
+ const void *src_zcopy; /* zero-copy source pointer */
+ const void *src_mcopy; /* non zero-copy source pointer */
+ uint16_t off; /* data offset */
+ uint16_t rem; /* data remainder */
+ uint16_t max_len; /* max fragment length */
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint8_t state;
+ usb2_error_t err;
+ union {
+ uWord wStatus;
+ uint8_t buf[2];
+ } temp;
+
+ /*
+ * Filter the USB transfer state into
+ * something which we understand:
+ */
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ state = ST_DATA;
+
+ if (!xfer->flags_int.control_act) {
+ /* nothing to do */
+ goto tr_stalled;
+ }
+ break;
+
+ default: /* USB_ST_TRANSFERRED */
+ if (!xfer->flags_int.control_act) {
+ state = ST_POST_STATUS;
+ } else {
+ state = ST_DATA;
+ }
+ break;
+ }
+
+ /* reset frame stuff */
+
+ xfer->frlengths[0] = 0;
+
+ usb2_set_frame_offset(xfer, 0, 0);
+ usb2_set_frame_offset(xfer, sizeof(req), 1);
+
+ /* get the current request, if any */
+
+ usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
+
+ if (xfer->flags_int.control_rem == 0xFFFF) {
+ /* first time - not initialised */
+ rem = UGETW(req.wLength);
+ off = 0;
+ } else {
+ /* not first time - initialised */
+ rem = xfer->flags_int.control_rem;
+ off = UGETW(req.wLength) - rem;
+ }
+
+ /* set some defaults */
+
+ max_len = 0;
+ src_zcopy = NULL;
+ src_mcopy = NULL;
+ udev = xfer->udev;
+
+ /* get some request fields decoded */
+
+ wValue = UGETW(req.wValue);
+ wIndex = UGETW(req.wIndex);
+
+ DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
+ "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
+ req.bRequest, wValue, wIndex, off, rem, state);
+
+ /* demultiplex the control request */
+
+ switch (req.bmRequestType) {
+ case UT_READ_DEVICE:
+ if (state != ST_DATA) {
+ break;
+ }
+ switch (req.bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_descriptor;
+ case UR_GET_CONFIG:
+ goto tr_handle_get_config;
+ case UR_GET_STATUS:
+ goto tr_handle_get_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_DEVICE:
+ switch (req.bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ switch (wValue) {
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (wValue) {
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (wValue) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (wValue) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (req.bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ /* we use "USB_ADD_BYTES" to de-const the src_zcopy */
+ err = usb2_handle_iface_request(xfer,
+ USB_ADD_BYTES(&src_zcopy, 0),
+ &max_len, req, off, state);
+ if (err == 0) {
+ goto tr_valid;
+ }
+ /*
+ * Reset zero-copy pointer and max length
+ * variable in case they were unintentionally
+ * set:
+ */
+ src_zcopy = NULL;
+ max_len = 0;
+
+ /*
+ * Check if we have a vendor specific
+ * descriptor:
+ */
+ goto tr_handle_get_descriptor;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ (usb2_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
+ if (src_zcopy == NULL) {
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_config:
+ temp.buf[0] = udev->curr_config_no;
+ src_mcopy = temp.buf;
+ max_len = 1;
+ goto tr_valid;
+
+tr_handle_get_status:
+
+ wValue = 0;
+
+ mtx_lock(&udev->bus->mtx);
+ if (udev->flags.remote_wakeup) {
+ wValue |= UDS_REMOTE_WAKEUP;
+ }
+ if (udev->flags.self_powered) {
+ wValue |= UDS_SELF_POWERED;
+ }
+ mtx_unlock(&udev->bus->mtx);
+
+ USETW(temp.wStatus, wValue);
+ src_mcopy = temp.wStatus;
+ max_len = sizeof(temp.wStatus);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (state == ST_DATA) {
+ if (wValue >= 0x80) {
+ /* invalid value */
+ goto tr_stalled;
+ } else if (udev->curr_config_no != 0) {
+ /* we are configured ! */
+ goto tr_stalled;
+ }
+ } else if (state == ST_POST_STATUS) {
+ udev->address = (wValue & 0x7F);
+ goto tr_bad_context;
+ }
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (state == ST_DATA) {
+ if (usb2_handle_set_config(xfer, req.wValue[0])) {
+ goto tr_stalled;
+ }
+ }
+ goto tr_valid;
+
+tr_handle_clear_halt:
+ if (state == ST_DATA) {
+ if (usb2_handle_set_stall(xfer, req.wIndex[0], 0)) {
+ goto tr_stalled;
+ }
+ }
+ goto tr_valid;
+
+tr_handle_clear_wakeup:
+ if (state == ST_DATA) {
+ if (usb2_handle_remote_wakeup(xfer, 0)) {
+ goto tr_stalled;
+ }
+ }
+ goto tr_valid;
+
+tr_handle_set_halt:
+ if (state == ST_DATA) {
+ if (usb2_handle_set_stall(xfer, req.wIndex[0], 1)) {
+ goto tr_stalled;
+ }
+ }
+ goto tr_valid;
+
+tr_handle_set_wakeup:
+ if (state == ST_DATA) {
+ if (usb2_handle_remote_wakeup(xfer, 1)) {
+ goto tr_stalled;
+ }
+ }
+ goto tr_valid;
+
+tr_handle_get_ep_status:
+ if (state == ST_DATA) {
+ temp.wStatus[0] =
+ usb2_handle_get_stall(udev, req.wIndex[0]);
+ temp.wStatus[1] = 0;
+ src_mcopy = temp.wStatus;
+ max_len = sizeof(temp.wStatus);
+ }
+ goto tr_valid;
+
+tr_valid:
+ if (state == ST_POST_STATUS) {
+ goto tr_stalled;
+ }
+ /* subtract offset from length */
+
+ max_len -= off;
+
+ /* Compute the real maximum data length */
+
+ if (max_len > xfer->max_data_length) {
+ max_len = xfer->max_data_length;
+ }
+ if (max_len > rem) {
+ max_len = rem;
+ }
+ /*
+ * If the remainder is greater than the maximum data length,
+ * we need to truncate the value for the sake of the
+ * comparison below:
+ */
+ if (rem > xfer->max_data_length) {
+ rem = xfer->max_data_length;
+ }
+ if (rem != max_len) {
+ /*
+ * If we don't transfer the data we can transfer, then
+ * the transfer is short !
+ */
+ xfer->flags.force_short_xfer = 1;
+ xfer->nframes = 2;
+ } else {
+ /*
+ * Default case
+ */
+ xfer->flags.force_short_xfer = 0;
+ xfer->nframes = max_len ? 2 : 1;
+ }
+ if (max_len > 0) {
+ if (src_mcopy) {
+ src_mcopy = USB_ADD_BYTES(src_mcopy, off);
+ usb2_copy_in(xfer->frbuffers + 1, 0,
+ src_mcopy, max_len);
+ } else {
+ usb2_set_frame_data(xfer,
+ USB_ADD_BYTES(src_zcopy, off), 1);
+ }
+ xfer->frlengths[1] = max_len;
+ } else {
+ /* the end is reached, send status */
+ xfer->flags.manual_status = 0;
+ xfer->frlengths[1] = 0;
+ }
+ DPRINTF("success\n");
+ return (0); /* success */
+
+tr_stalled:
+ DPRINTF("%s\n", (state == ST_POST_STATUS) ?
+ "complete" : "stalled");
+ return (USB_ERR_STALLED);
+
+tr_bad_context:
+ DPRINTF("bad context\n");
+ return (USB_ERR_BAD_CONTEXT);
+}
diff --git a/sys/dev/usb2/core/usb2_handle_request.h b/sys/dev/usb2/core/usb2_handle_request.h
new file mode 100644
index 000000000000..6cc050321371
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_handle_request.h
@@ -0,0 +1,30 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_HANDLE_REQUEST_H_
+#define _USB2_HANDLE_REQUEST_H_
+
+#endif /* _USB2_HANDLE_REQUEST_H_ */
diff --git a/sys/dev/usb2/core/usb2_hid.c b/sys/dev/usb2/core/usb2_hid.c
new file mode 100644
index 000000000000..04d7d4d0c0d7
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_hid.c
@@ -0,0 +1,582 @@
+/* $NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_hid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_parse.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_hid.h>
+
+static void hid_clear_local(struct hid_item *);
+
+#define MAXUSAGE 100
+struct hid_data {
+ const uint8_t *start;
+ const uint8_t *end;
+ const uint8_t *p;
+ struct hid_item cur;
+ int32_t usages[MAXUSAGE];
+ int nu;
+ int minset;
+ int multi;
+ int multimax;
+ int kindset;
+};
+
+/*------------------------------------------------------------------------*
+ * hid_clear_local
+ *------------------------------------------------------------------------*/
+static void
+hid_clear_local(struct hid_item *c)
+{
+
+ c->usage = 0;
+ c->usage_minimum = 0;
+ c->usage_maximum = 0;
+ c->designator_index = 0;
+ c->designator_minimum = 0;
+ c->designator_maximum = 0;
+ c->string_index = 0;
+ c->string_minimum = 0;
+ c->string_maximum = 0;
+ c->set_delimiter = 0;
+}
+
+/*------------------------------------------------------------------------*
+ * hid_start_parse
+ *------------------------------------------------------------------------*/
+struct hid_data *
+hid_start_parse(const void *d, int len, int kindset)
+{
+ struct hid_data *s;
+
+ s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO);
+ s->start = s->p = d;
+ s->end = ((const uint8_t *)d) + len;
+ s->kindset = kindset;
+ return (s);
+}
+
+/*------------------------------------------------------------------------*
+ * hid_end_parse
+ *------------------------------------------------------------------------*/
+void
+hid_end_parse(struct hid_data *s)
+{
+
+ while (s->cur.next != NULL) {
+ struct hid_item *hi = s->cur.next->next;
+
+ free(s->cur.next, M_TEMP);
+ s->cur.next = hi;
+ }
+ free(s, M_TEMP);
+}
+
+/*------------------------------------------------------------------------*
+ * hid_get_item
+ *------------------------------------------------------------------------*/
+int
+hid_get_item(struct hid_data *s, struct hid_item *h)
+{
+ struct hid_item *c = &s->cur;
+ unsigned int bTag, bType, bSize;
+ uint32_t oldpos;
+ const uint8_t *data;
+ int32_t dval;
+ const uint8_t *p;
+ struct hid_item *hi;
+ int i;
+
+top:
+ if (s->multimax != 0) {
+ if (s->multi < s->multimax) {
+ c->usage = s->usages[MIN(s->multi, s->nu - 1)];
+ s->multi++;
+ *h = *c;
+ c->loc.pos += c->loc.size;
+ h->next = 0;
+ return (1);
+ } else {
+ c->loc.count = s->multimax;
+ s->multimax = 0;
+ s->nu = 0;
+ hid_clear_local(c);
+ }
+ }
+ for (;;) {
+ p = s->p;
+ if (p >= s->end)
+ return (0);
+
+ bSize = *p++;
+ if (bSize == 0xfe) {
+ /* long item */
+ bSize = *p++;
+ bSize |= *p++ << 8;
+ bTag = *p++;
+ data = p;
+ p += bSize;
+ bType = 0xff; /* XXX what should it be */
+ } else {
+ /* short item */
+ bTag = bSize >> 4;
+ bType = (bSize >> 2) & 3;
+ bSize &= 3;
+ if (bSize == 3)
+ bSize = 4;
+ data = p;
+ p += bSize;
+ }
+ s->p = p;
+ switch (bSize) {
+ case 0:
+ dval = 0;
+ break;
+ case 1:
+ dval = (int8_t)*data++;
+ break;
+ case 2:
+ dval = *data++;
+ dval |= *data++ << 8;
+ dval = (int16_t)dval;
+ break;
+ case 4:
+ dval = *data++;
+ dval |= *data++ << 8;
+ dval |= *data++ << 16;
+ dval |= *data++ << 24;
+ break;
+ default:
+ printf("BAD LENGTH %d\n", bSize);
+ continue;
+ }
+
+ switch (bType) {
+ case 0: /* Main */
+ switch (bTag) {
+ case 8: /* Input */
+ if (!(s->kindset & (1 << hid_input))) {
+ if (s->nu > 0)
+ s->nu--;
+ continue;
+ }
+ c->kind = hid_input;
+ c->flags = dval;
+ ret:
+ if (c->flags & HIO_VARIABLE) {
+ s->multimax = c->loc.count;
+ s->multi = 0;
+ c->loc.count = 1;
+ if (s->minset) {
+ for (i = c->usage_minimum;
+ i <= c->usage_maximum;
+ i++) {
+ s->usages[s->nu] = i;
+ if (s->nu < MAXUSAGE - 1)
+ s->nu++;
+ }
+ s->minset = 0;
+ }
+ goto top;
+ } else {
+ *h = *c;
+ h->next = 0;
+ c->loc.pos +=
+ c->loc.size * c->loc.count;
+ hid_clear_local(c);
+ s->minset = 0;
+ return (1);
+ }
+ case 9: /* Output */
+ if (!(s->kindset & (1 << hid_output))) {
+ if (s->nu > 0)
+ s->nu--;
+ continue;
+ }
+ c->kind = hid_output;
+ c->flags = dval;
+ goto ret;
+ case 10: /* Collection */
+ c->kind = hid_collection;
+ c->collection = dval;
+ c->collevel++;
+ *h = *c;
+ hid_clear_local(c);
+ s->nu = 0;
+ return (1);
+ case 11: /* Feature */
+ if (!(s->kindset & (1 << hid_feature))) {
+ if (s->nu > 0)
+ s->nu--;
+ continue;
+ }
+ c->kind = hid_feature;
+ c->flags = dval;
+ goto ret;
+ case 12: /* End collection */
+ c->kind = hid_endcollection;
+ c->collevel--;
+ *h = *c;
+ hid_clear_local(c);
+ s->nu = 0;
+ return (1);
+ default:
+ printf("Main bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ case 1: /* Global */
+ switch (bTag) {
+ case 0:
+ c->_usage_page = dval << 16;
+ break;
+ case 1:
+ c->logical_minimum = dval;
+ break;
+ case 2:
+ c->logical_maximum = dval;
+ break;
+ case 3:
+ c->physical_minimum = dval;
+ break;
+ case 4:
+ c->physical_maximum = dval;
+ break;
+ case 5:
+ c->unit_exponent = dval;
+ break;
+ case 6:
+ c->unit = dval;
+ break;
+ case 7:
+ c->loc.size = dval;
+ break;
+ case 8:
+ c->report_ID = dval;
+ break;
+ case 9:
+ c->loc.count = dval;
+ break;
+ case 10: /* Push */
+ hi = malloc(sizeof *hi, M_TEMP, M_WAITOK);
+ *hi = s->cur;
+ c->next = hi;
+ break;
+ case 11: /* Pop */
+ hi = c->next;
+ oldpos = c->loc.pos;
+ s->cur = *hi;
+ c->loc.pos = oldpos;
+ free(hi, M_TEMP);
+ break;
+ default:
+ printf("Global bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ case 2: /* Local */
+ switch (bTag) {
+ case 0:
+ if (bSize == 1)
+ dval = c->_usage_page | (dval & 0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval & 0xffff);
+ c->usage = dval;
+ if (s->nu < MAXUSAGE)
+ s->usages[s->nu++] = dval;
+ /* else XXX */
+ break;
+ case 1:
+ s->minset = 1;
+ if (bSize == 1)
+ dval = c->_usage_page | (dval & 0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval & 0xffff);
+ c->usage_minimum = dval;
+ break;
+ case 2:
+ if (bSize == 1)
+ dval = c->_usage_page | (dval & 0xff);
+ else if (bSize == 2)
+ dval = c->_usage_page | (dval & 0xffff);
+ c->usage_maximum = dval;
+ break;
+ case 3:
+ c->designator_index = dval;
+ break;
+ case 4:
+ c->designator_minimum = dval;
+ break;
+ case 5:
+ c->designator_maximum = dval;
+ break;
+ case 7:
+ c->string_index = dval;
+ break;
+ case 8:
+ c->string_minimum = dval;
+ break;
+ case 9:
+ c->string_maximum = dval;
+ break;
+ case 10:
+ c->set_delimiter = dval;
+ break;
+ default:
+ printf("Local bTag=%d\n", bTag);
+ break;
+ }
+ break;
+ default:
+ printf("default bType=%d\n", bType);
+ break;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * hid_report_size
+ *------------------------------------------------------------------------*/
+int
+hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t *idp)
+{
+ struct hid_data *d;
+ struct hid_item h;
+ int hi, lo, size, id;
+
+ id = 0;
+ hi = lo = -1;
+ for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);)
+ if (h.kind == k) {
+ if (h.report_ID != 0 && !id)
+ id = h.report_ID;
+ if (h.report_ID == id) {
+ if (lo < 0)
+ lo = h.loc.pos;
+ hi = h.loc.pos + h.loc.size * h.loc.count;
+ }
+ }
+ hid_end_parse(d);
+ size = hi - lo;
+ if (id != 0) {
+ size += 8;
+ *idp = id; /* XXX wrong */
+ } else
+ *idp = 0;
+ return ((size + 7) / 8);
+}
+
+/*------------------------------------------------------------------------*
+ * hid_locate
+ *------------------------------------------------------------------------*/
+int
+hid_locate(const void *desc, int size, uint32_t u, enum hid_kind k,
+ struct hid_location *loc, uint32_t *flags)
+{
+ struct hid_data *d;
+ struct hid_item h;
+
+ for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) {
+ if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
+ if (loc != NULL)
+ *loc = h.loc;
+ if (flags != NULL)
+ *flags = h.flags;
+ hid_end_parse(d);
+ return (1);
+ }
+ }
+ hid_end_parse(d);
+ loc->size = 0;
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * hid_get_data
+ *------------------------------------------------------------------------*/
+uint32_t
+hid_get_data(const uint8_t *buf, uint32_t len, struct hid_location *loc)
+{
+ uint32_t hpos = loc->pos;
+ uint32_t hsize = loc->size;
+ uint32_t data;
+ int i, s, t;
+
+ DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize);
+
+ if (hsize == 0)
+ return (0);
+
+ data = 0;
+ s = hpos / 8;
+ for (i = hpos; i < (hpos + hsize); i += 8) {
+ t = (i / 8);
+ if (t < len) {
+ data |= buf[t] << ((t - s) * 8);
+ }
+ }
+ data >>= hpos % 8;
+ data &= (1 << hsize) - 1;
+ hsize = 32 - hsize;
+ /* Sign extend */
+ data = ((int32_t)data << hsize) >> hsize;
+ DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n",
+ loc->pos, loc->size, (long)data);
+ return (data);
+}
+
+/*------------------------------------------------------------------------*
+ * hid_is_collection
+ *------------------------------------------------------------------------*/
+int
+hid_is_collection(const void *desc, int size, uint32_t usage)
+{
+ struct hid_data *hd;
+ struct hid_item hi;
+ int err;
+
+ hd = hid_start_parse(desc, size, hid_input);
+ if (hd == NULL)
+ return (0);
+
+ err = hid_get_item(hd, &hi) &&
+ hi.kind == hid_collection &&
+ hi.usage == usage;
+ hid_end_parse(hd);
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * hid_get_descriptor_from_usb
+ *
+ * This function will search for a HID descriptor between two USB
+ * interface descriptors.
+ *
+ * Return values:
+ * NULL: No more HID descriptors.
+ * Else: Pointer to HID descriptor.
+ *------------------------------------------------------------------------*/
+struct usb2_hid_descriptor *
+hid_get_descriptor_from_usb(struct usb2_config_descriptor *cd,
+ struct usb2_interface_descriptor *id)
+{
+ struct usb2_descriptor *desc = (void *)id;
+
+ if (desc == NULL) {
+ return (NULL);
+ }
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+ if ((desc->bDescriptorType == UDESC_HID) &&
+ (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) {
+ return (void *)desc;
+ }
+ if (desc->bDescriptorType == UDESC_INTERFACE) {
+ break;
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_hid_desc
+ *
+ * This function will read out an USB report descriptor from the USB
+ * device.
+ *
+ * Return values:
+ * NULL: Failure.
+ * Else: Success. The pointer should eventually be passed to free().
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_hid_desc(struct usb2_device *udev, struct mtx *mtx,
+ void **descp, uint16_t *sizep,
+ usb2_malloc_type mem, uint8_t iface_index)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_hid_descriptor *hid;
+ usb2_error_t err;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ hid = hid_get_descriptor_from_usb
+ (usb2_get_config_descriptor(udev), iface->idesc);
+
+ if (hid == NULL) {
+ return (USB_ERR_IOERROR);
+ }
+ *sizep = UGETW(hid->descrs[0].wDescriptorLength);
+ if (*sizep == 0) {
+ return (USB_ERR_IOERROR);
+ }
+ if (mtx)
+ mtx_unlock(mtx);
+
+ *descp = malloc(*sizep, mem, M_ZERO | M_WAITOK);
+
+ if (mtx)
+ mtx_lock(mtx);
+
+ if (*descp == NULL) {
+ return (USB_ERR_NOMEM);
+ }
+ err = usb2_req_get_report_descriptor
+ (udev, mtx, *descp, *sizep, iface_index);
+
+ if (err) {
+ free(*descp, mem);
+ *descp = NULL;
+ return (err);
+ }
+ return (USB_ERR_NORMAL_COMPLETION);
+}
diff --git a/sys/dev/usb2/core/usb2_hid.h b/sys/dev/usb2/core/usb2_hid.h
new file mode 100644
index 000000000000..9809cde9bb3b
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_hid.h
@@ -0,0 +1,89 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
+ * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _USB2_CORE_HID_H_
+#define _USB2_CORE_HID_H_
+
+struct usb2_hid_descriptor;
+struct usb2_config_descriptor;
+
+enum hid_kind {
+ hid_input, hid_output, hid_feature, hid_collection, hid_endcollection
+};
+
+struct hid_location {
+ uint32_t size;
+ uint32_t count;
+ uint32_t pos;
+};
+
+struct hid_item {
+ /* Global */
+ int32_t _usage_page;
+ int32_t logical_minimum;
+ int32_t logical_maximum;
+ int32_t physical_minimum;
+ int32_t physical_maximum;
+ int32_t unit_exponent;
+ int32_t unit;
+ int32_t report_ID;
+ /* Local */
+ int32_t usage;
+ int32_t usage_minimum;
+ int32_t usage_maximum;
+ int32_t designator_index;
+ int32_t designator_minimum;
+ int32_t designator_maximum;
+ int32_t string_index;
+ int32_t string_minimum;
+ int32_t string_maximum;
+ int32_t set_delimiter;
+ /* Misc */
+ int32_t collection;
+ int collevel;
+ enum hid_kind kind;
+ uint32_t flags;
+ /* Location */
+ struct hid_location loc;
+ /* */
+ struct hid_item *next;
+};
+
+/* prototypes from "usb2_hid.c" */
+
+struct hid_data *hid_start_parse(const void *d, int len, int kindset);
+void hid_end_parse(struct hid_data *s);
+int hid_get_item(struct hid_data *s, struct hid_item *h);
+int hid_report_size(const void *buf, int len, enum hid_kind k, uint8_t *id);
+int hid_locate(const void *desc, int size, uint32_t usage, enum hid_kind kind, struct hid_location *loc, uint32_t *flags);
+uint32_t hid_get_data(const uint8_t *buf, uint32_t len, struct hid_location *loc);
+int hid_is_collection(const void *desc, int size, uint32_t usage);
+struct usb2_hid_descriptor *hid_get_descriptor_from_usb(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id);
+usb2_error_t usb2_req_get_hid_desc(struct usb2_device *udev, struct mtx *mtx, void **descp, uint16_t *sizep, usb2_malloc_type mem, uint8_t iface_index);
+
+#endif /* _USB2_CORE_HID_H_ */
diff --git a/sys/dev/usb2/core/usb2_hub.c b/sys/dev/usb2/core/usb2_hub.c
new file mode 100644
index 000000000000..b36085583a63
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_hub.c
@@ -0,0 +1,1330 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
+ * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+#define USB_DEBUG_VAR uhub_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_hub.h>
+#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_dynamic.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+#define UHUB_INTR_INTERVAL 250 /* ms */
+
+#if USB_DEBUG
+static int uhub_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB");
+SYSCTL_INT(_hw_usb2_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0,
+ "Debug level");
+#endif
+
+struct uhub_current_state {
+ uint16_t port_change;
+ uint16_t port_status;
+};
+
+struct uhub_softc {
+ struct uhub_current_state sc_st;/* current state */
+ device_t sc_dev; /* base device */
+ struct usb2_device *sc_udev; /* USB device */
+ struct usb2_xfer *sc_xfer[2]; /* interrupt xfer */
+ uint8_t sc_flags;
+#define UHUB_FLAG_INTR_STALL 0x02
+ char sc_name[32];
+};
+
+#define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol)
+#define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
+#define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
+
+/* prototypes for type checking: */
+
+static device_probe_t uhub_probe;
+static device_attach_t uhub_attach;
+static device_detach_t uhub_detach;
+
+static bus_driver_added_t uhub_driver_added;
+static bus_child_location_str_t uhub_child_location_string;
+static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string;
+
+static usb2_callback_t uhub_intr_callback;
+static usb2_callback_t uhub_intr_clear_stall_callback;
+
+static const struct usb2_config uhub_config[2] = {
+
+ [0] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_ANY,
+ .mh.timeout = 0,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uhub_intr_callback,
+ .mh.interval = UHUB_INTR_INTERVAL,
+ },
+
+ [1] = {
+ .type = UE_CONTROL,
+ .endpoint = 0,
+ .direction = UE_DIR_ANY,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ .mh.flags = {},
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.callback = &uhub_intr_clear_stall_callback,
+ },
+};
+
+/*
+ * driver instance for "hub" connected to "usb"
+ * and "hub" connected to "hub"
+ */
+static devclass_t uhub_devclass;
+
+static driver_t uhub_driver =
+{
+ .name = "ushub",
+ .methods = (device_method_t[]){
+ DEVMETHOD(device_probe, uhub_probe),
+ DEVMETHOD(device_attach, uhub_attach),
+ DEVMETHOD(device_detach, uhub_detach),
+
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ DEVMETHOD(bus_child_location_str, uhub_child_location_string),
+ DEVMETHOD(bus_child_pnpinfo_str, uhub_child_pnpinfo_string),
+ DEVMETHOD(bus_driver_added, uhub_driver_added),
+ {0, 0}
+ },
+ .size = sizeof(struct uhub_softc)
+};
+
+DRIVER_MODULE(ushub, usbus, uhub_driver, uhub_devclass, 0, 0);
+DRIVER_MODULE(ushub, ushub, uhub_driver, uhub_devclass, NULL, 0);
+
+static void
+uhub_intr_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct uhub_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[0];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~UHUB_FLAG_INTR_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+ return;
+}
+
+static void
+uhub_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uhub_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(2, "\n");
+ /*
+ * This is an indication that some port
+ * has changed status. Notify the bus
+ * event handler thread that we need
+ * to be explored again:
+ */
+ usb2_needs_explore(sc->sc_udev->bus, 0);
+
+ case USB_ST_SETUP:
+ if (sc->sc_flags & UHUB_FLAG_INTR_STALL) {
+ usb2_transfer_start(sc->sc_xfer[1]);
+ } else {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* start clear stall */
+ sc->sc_flags |= UHUB_FLAG_INTR_STALL;
+ usb2_transfer_start(sc->sc_xfer[1]);
+ }
+ return;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * uhub_explore_sub - subroutine
+ *
+ * Return values:
+ * 0: Success
+ * Else: A control transaction failed
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+uhub_explore_sub(struct uhub_softc *sc, struct usb2_port *up)
+{
+ struct usb2_bus *bus;
+ struct usb2_device *child;
+ uint8_t refcount;
+ usb2_error_t err;
+
+ bus = sc->sc_udev->bus;
+ err = 0;
+
+ /* get driver added refcount from USB bus */
+ refcount = bus->driver_added_refcount;
+
+ /* get device assosiated with the given port */
+ child = usb2_bus_port_get_device(bus, up);
+ if (child == NULL) {
+ /* nothing to do */
+ goto done;
+ }
+ /* check if probe and attach should be done */
+
+ if (child->driver_added_refcount != refcount) {
+ child->driver_added_refcount = refcount;
+ err = usb2_probe_and_attach(child,
+ USB_IFACE_INDEX_ANY);
+ if (err) {
+ goto done;
+ }
+ }
+ /* start control transfer, if device mode */
+
+ if (child->flags.usb2_mode == USB_MODE_DEVICE) {
+ usb2_default_transfer_setup(child);
+ }
+ /* if a HUB becomes present, do a recursive HUB explore */
+
+ if (child->hub) {
+ err = (child->hub->explore) (child);
+ }
+done:
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * uhub_read_port_status - factored out code
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+uhub_read_port_status(struct uhub_softc *sc, uint8_t portno)
+{
+ struct usb2_port_status ps;
+ usb2_error_t err;
+
+ err = usb2_req_get_port_status(
+ sc->sc_udev, &Giant, &ps, portno);
+
+ /* update status regardless of error */
+
+ sc->sc_st.port_status = UGETW(ps.wPortStatus);
+ sc->sc_st.port_change = UGETW(ps.wPortChange);
+
+ /* debugging print */
+
+ DPRINTFN(4, "port %d, wPortStatus=0x%04x, "
+ "wPortChange=0x%04x, err=%s\n",
+ portno, sc->sc_st.port_status,
+ sc->sc_st.port_change, usb2_errstr(err));
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * uhub_reattach_port
+ *
+ * Returns:
+ * 0: Success
+ * Else: A control transaction failed
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+uhub_reattach_port(struct uhub_softc *sc, uint8_t portno)
+{
+ struct usb2_device *child;
+ struct usb2_device *udev;
+ usb2_error_t err;
+ uint8_t timeout;
+ uint8_t speed;
+ uint8_t usb2_mode;
+
+ DPRINTF("reattaching port %d\n", portno);
+
+ err = 0;
+ timeout = 0;
+ udev = sc->sc_udev;
+ child = usb2_bus_port_get_device(udev->bus,
+ udev->hub->ports + portno - 1);
+
+repeat:
+
+ /* first clear the port connection change bit */
+
+ err = usb2_req_clear_port_feature
+ (udev, &Giant, portno, UHF_C_PORT_CONNECTION);
+
+ if (err) {
+ goto error;
+ }
+ /* detach any existing devices */
+
+ if (child) {
+ usb2_detach_device(child, USB_IFACE_INDEX_ANY, 1);
+ usb2_free_device(child);
+ child = NULL;
+ }
+ /* get fresh status */
+
+ err = uhub_read_port_status(sc, portno);
+ if (err) {
+ goto error;
+ }
+ /* check if nothing is connected to the port */
+
+ if (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS)) {
+ goto error;
+ }
+ /* check if there is no power on the port and print a warning */
+
+ if (!(sc->sc_st.port_status & UPS_PORT_POWER)) {
+ DPRINTF("WARNING: strange, connected port %d "
+ "has no power\n", portno);
+ }
+ /* check if the device is in Host Mode */
+
+ if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) {
+
+ DPRINTF("Port %d is in Host Mode\n", portno);
+
+ /* USB Host Mode */
+
+ /* wait for maximum device power up time */
+
+ usb2_pause_mtx(&Giant, USB_PORT_POWERUP_DELAY);
+
+ /* reset port, which implies enabling it */
+
+ err = usb2_req_reset_port
+ (udev, &Giant, portno);
+
+ if (err) {
+ DPRINTFN(0, "port %d reset "
+ "failed, error=%s\n",
+ portno, usb2_errstr(err));
+ goto error;
+ }
+ /* get port status again, it might have changed during reset */
+
+ err = uhub_read_port_status(sc, portno);
+ if (err) {
+ goto error;
+ }
+ /* check if something changed during port reset */
+
+ if ((sc->sc_st.port_change & UPS_C_CONNECT_STATUS) ||
+ (!(sc->sc_st.port_status & UPS_CURRENT_CONNECT_STATUS))) {
+ if (timeout) {
+ DPRINTFN(0, "giving up port reset "
+ "- device vanished!\n");
+ goto error;
+ }
+ timeout = 1;
+ goto repeat;
+ }
+ } else {
+ DPRINTF("Port %d is in Device Mode\n", portno);
+ }
+
+ /*
+ * Figure out the device speed
+ */
+ speed =
+ (sc->sc_st.port_status & UPS_HIGH_SPEED) ? USB_SPEED_HIGH :
+ (sc->sc_st.port_status & UPS_LOW_SPEED) ? USB_SPEED_LOW : USB_SPEED_FULL;
+
+ /*
+ * Figure out the device mode
+ *
+ * NOTE: This part is currently FreeBSD specific.
+ */
+ usb2_mode =
+ (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE) ?
+ USB_MODE_DEVICE : USB_MODE_HOST;
+
+ /* need to create a new child */
+
+ child = usb2_alloc_device(sc->sc_dev, udev->bus, udev,
+ udev->depth + 1, portno - 1, portno, speed, usb2_mode);
+ if (child == NULL) {
+ DPRINTFN(0, "could not allocate new device!\n");
+ goto error;
+ }
+ return (0); /* success */
+
+error:
+ if (child) {
+ usb2_detach_device(child, USB_IFACE_INDEX_ANY, 1);
+ usb2_free_device(child);
+ child = NULL;
+ }
+ if (err == 0) {
+ if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
+ err = usb2_req_clear_port_feature
+ (sc->sc_udev, &Giant,
+ portno, UHF_PORT_ENABLE);
+ }
+ }
+ if (err) {
+ DPRINTFN(0, "device problem (%s), "
+ "disabling port %d\n", usb2_errstr(err), portno);
+ }
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * uhub_suspend_resume_port
+ *
+ * Returns:
+ * 0: Success
+ * Else: A control transaction failed
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno)
+{
+ struct usb2_device *child;
+ struct usb2_device *udev;
+ uint8_t is_suspend;
+ usb2_error_t err;
+
+ DPRINTF("port %d\n", portno);
+
+ udev = sc->sc_udev;
+ child = usb2_bus_port_get_device(udev->bus,
+ udev->hub->ports + portno - 1);
+
+ /* first clear the port suspend change bit */
+
+ err = usb2_req_clear_port_feature
+ (udev, &Giant, portno, UHF_C_PORT_SUSPEND);
+
+ if (err) {
+ goto done;
+ }
+ /* get fresh status */
+
+ err = uhub_read_port_status(sc, portno);
+ if (err) {
+ goto done;
+ }
+ /* get current state */
+
+ if (sc->sc_st.port_status & UPS_SUSPEND) {
+ is_suspend = 1;
+ } else {
+ is_suspend = 0;
+ }
+ /* do the suspend or resume */
+
+ if (child) {
+ sx_xlock(child->default_sx + 1);
+ err = usb2_suspend_resume(child, is_suspend);
+ sx_unlock(child->default_sx + 1);
+ }
+done:
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * uhub_explore
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+uhub_explore(struct usb2_device *udev)
+{
+ struct usb2_hub *hub;
+ struct uhub_softc *sc;
+ struct usb2_port *up;
+ usb2_error_t err;
+ uint8_t portno;
+ uint8_t x;
+
+ hub = udev->hub;
+ sc = hub->hubsoftc;
+
+ DPRINTFN(11, "udev=%p addr=%d\n", udev, udev->address);
+
+ /* ignore hubs that are too deep */
+ if (udev->depth > USB_HUB_MAX_DEPTH) {
+ return (USB_ERR_TOO_DEEP);
+ }
+ for (x = 0; x != hub->nports; x++) {
+ up = hub->ports + x;
+ portno = x + 1;
+
+ err = uhub_read_port_status(sc, portno);
+ if (err) {
+ /* most likely the HUB is gone */
+ break;
+ }
+ if (sc->sc_st.port_change & UPS_C_PORT_ENABLED) {
+ err = usb2_req_clear_port_feature(
+ udev, &Giant, portno, UHF_C_PORT_ENABLE);
+ if (err) {
+ /* most likely the HUB is gone */
+ break;
+ }
+ if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) {
+ /*
+ * Ignore the port error if the device
+ * has vanished !
+ */
+ } else if (sc->sc_st.port_status & UPS_PORT_ENABLED) {
+ DPRINTFN(0, "illegal enable change, "
+ "port %d\n", portno);
+ } else {
+
+ if (up->restartcnt == USB_RESTART_MAX) {
+ /* XXX could try another speed ? */
+ DPRINTFN(0, "port error, giving up "
+ "port %d\n", portno);
+ } else {
+ sc->sc_st.port_change |= UPS_C_CONNECT_STATUS;
+ up->restartcnt++;
+ }
+ }
+ }
+ if (sc->sc_st.port_change & UPS_C_CONNECT_STATUS) {
+ err = uhub_reattach_port(sc, portno);
+ if (err) {
+ /* most likely the HUB is gone */
+ break;
+ }
+ }
+ if (sc->sc_st.port_change & UPS_C_SUSPEND) {
+ err = uhub_suspend_resume_port(sc, portno);
+ if (err) {
+ /* most likely the HUB is gone */
+ break;
+ }
+ }
+ err = uhub_explore_sub(sc, up);
+ if (err) {
+ /* no device(s) present */
+ continue;
+ }
+ /* explore succeeded - reset restart counter */
+ up->restartcnt = 0;
+ }
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static int
+uhub_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ /*
+ * The subclass for USB HUBs is ignored because it is 0 for
+ * some and 1 for others.
+ */
+ if ((uaa->info.bConfigIndex == 0) &&
+ (uaa->info.bDeviceClass == UDCLASS_HUB)) {
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+uhub_attach(device_t dev)
+{
+ struct uhub_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_device *udev = uaa->device;
+ struct usb2_device *parent_hub = udev->parent_hub;
+ struct usb2_hub *hub;
+ struct usb2_hub_descriptor hubdesc;
+ uint16_t pwrdly;
+ uint8_t x;
+ uint8_t nports;
+ uint8_t portno;
+ uint8_t removable;
+ uint8_t iface_index;
+ usb2_error_t err;
+
+ if (sc == NULL) {
+ return (ENOMEM);
+ }
+ sc->sc_udev = udev;
+ sc->sc_dev = dev;
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
+ device_get_nameunit(dev));
+
+ device_set_usb2_desc(dev);
+
+ DPRINTFN(2, "depth=%d selfpowered=%d, parent=%p, "
+ "parent->selfpowered=%d\n",
+ udev->depth,
+ udev->flags.self_powered,
+ parent_hub,
+ parent_hub ?
+ parent_hub->flags.self_powered : 0);
+
+ if (udev->depth > USB_HUB_MAX_DEPTH) {
+ DPRINTFN(0, "hub depth, %d, exceeded. HUB ignored!\n",
+ USB_HUB_MAX_DEPTH);
+ goto error;
+ }
+ if (!udev->flags.self_powered && parent_hub &&
+ (!parent_hub->flags.self_powered)) {
+ DPRINTFN(0, "bus powered HUB connected to "
+ "bus powered HUB. HUB ignored!\n");
+ goto error;
+ }
+ /* get HUB descriptor */
+
+ DPRINTFN(2, "getting HUB descriptor\n");
+
+ /* assuming that there is one port */
+ err = usb2_req_get_hub_descriptor(udev, &Giant, &hubdesc, 1);
+
+ nports = hubdesc.bNbrPorts;
+
+ if (!err && (nports >= 8)) {
+ /* get complete HUB descriptor */
+ err = usb2_req_get_hub_descriptor(udev, &Giant, &hubdesc, nports);
+ }
+ if (err) {
+ DPRINTFN(0, "getting hub descriptor failed,"
+ "error=%s\n", usb2_errstr(err));
+ goto error;
+ }
+ if (hubdesc.bNbrPorts != nports) {
+ DPRINTFN(0, "number of ports changed!\n");
+ goto error;
+ }
+ if (nports == 0) {
+ DPRINTFN(0, "portless HUB!\n");
+ goto error;
+ }
+ hub = malloc(sizeof(hub[0]) + (sizeof(hub->ports[0]) * nports),
+ M_USBDEV, M_WAITOK | M_ZERO);
+
+ if (hub == NULL) {
+ goto error;
+ }
+ udev->hub = hub;
+
+ /* init FULL-speed ISOCHRONOUS schedule */
+ usb2_fs_isoc_schedule_init_all(hub->fs_isoc_schedule);
+
+ /* initialize HUB structure */
+ hub->hubsoftc = sc;
+ hub->explore = &uhub_explore;
+ hub->nports = hubdesc.bNbrPorts;
+ hub->hubudev = udev;
+
+ /* if self powered hub, give ports maximum current */
+ if (udev->flags.self_powered) {
+ hub->portpower = USB_MAX_POWER;
+ } else {
+ hub->portpower = USB_MIN_POWER;
+ }
+
+ /* set up interrupt pipe */
+ iface_index = 0;
+ err = usb2_transfer_setup(udev, &iface_index, sc->sc_xfer,
+ uhub_config, 2, sc, &Giant);
+ if (err) {
+ DPRINTFN(0, "cannot setup interrupt transfer, "
+ "errstr=%s!\n", usb2_errstr(err));
+ goto error;
+ }
+ /* wait with power off for a while */
+ usb2_pause_mtx(&Giant, USB_POWER_DOWN_TIME);
+
+ /*
+ * To have the best chance of success we do things in the exact same
+ * order as Windoze98. This should not be necessary, but some
+ * devices do not follow the USB specs to the letter.
+ *
+ * These are the events on the bus when a hub is attached:
+ * Get device and config descriptors (see attach code)
+ * Get hub descriptor (see above)
+ * For all ports
+ * turn on power
+ * wait for power to become stable
+ * (all below happens in explore code)
+ * For all ports
+ * clear C_PORT_CONNECTION
+ * For all ports
+ * get port status
+ * if device connected
+ * wait 100 ms
+ * turn on reset
+ * wait
+ * clear C_PORT_RESET
+ * get port status
+ * proceed with device attachment
+ */
+
+ /* XXX should check for none, individual, or ganged power? */
+
+ removable = 0;
+ pwrdly = ((hubdesc.bPwrOn2PwrGood * UHD_PWRON_FACTOR) +
+ USB_EXTRA_POWER_UP_TIME);
+
+ for (x = 0; x != nports; x++) {
+ /* set up data structures */
+ struct usb2_port *up = hub->ports + x;
+
+ up->device_index = 0;
+ up->restartcnt = 0;
+ portno = x + 1;
+
+ /* check if port is removable */
+ if (!UHD_NOT_REMOV(&hubdesc, portno)) {
+ removable++;
+ }
+ if (!err) {
+ /* turn the power on */
+ err = usb2_req_set_port_feature
+ (udev, &Giant, portno, UHF_PORT_POWER);
+ }
+ if (err) {
+ DPRINTFN(0, "port %d power on failed, %s\n",
+ portno, usb2_errstr(err));
+ }
+ DPRINTF("turn on port %d power\n",
+ portno);
+
+ /* wait for stable power */
+ usb2_pause_mtx(&Giant, pwrdly);
+ }
+
+ device_printf(dev, "%d port%s with %d "
+ "removable, %s powered\n", nports, (nports != 1) ? "s" : "",
+ removable, udev->flags.self_powered ? "self" : "bus");
+
+ /* start the interrupt endpoint */
+
+ mtx_lock(sc->sc_xfer[0]->priv_mtx);
+ usb2_transfer_start(sc->sc_xfer[0]);
+ mtx_unlock(sc->sc_xfer[0]->priv_mtx);
+
+ return (0);
+
+error:
+ usb2_transfer_unsetup(sc->sc_xfer, 2);
+
+ if (udev->hub) {
+ free(udev->hub, M_USBDEV);
+ udev->hub = NULL;
+ }
+ return (ENXIO);
+}
+
+/*
+ * Called from process context when the hub is gone.
+ * Detach all devices on active ports.
+ */
+static int
+uhub_detach(device_t dev)
+{
+ struct uhub_softc *sc = device_get_softc(dev);
+ struct usb2_hub *hub = sc->sc_udev->hub;
+ struct usb2_device *child;
+ uint8_t x;
+
+ /* detach all children first */
+ bus_generic_detach(dev);
+
+ if (hub == NULL) { /* must be partially working */
+ return (0);
+ }
+ for (x = 0; x != hub->nports; x++) {
+
+ child = usb2_bus_port_get_device(sc->sc_udev->bus, hub->ports + x);
+
+ if (child == NULL) {
+ continue;
+ }
+ /*
+ * Subdevices are not freed, because the caller of
+ * uhub_detach() will do that.
+ */
+ usb2_detach_device(child, USB_IFACE_INDEX_ANY, 0);
+ usb2_free_device(child);
+ child = NULL;
+ }
+
+ usb2_transfer_unsetup(sc->sc_xfer, 2);
+
+ free(hub, M_USBDEV);
+ sc->sc_udev->hub = NULL;
+ return (0);
+}
+
+static void
+uhub_driver_added(device_t dev, driver_t *driver)
+{
+ usb2_needs_explore_all();
+ return;
+}
+
+struct hub_result {
+ struct usb2_device *udev;
+ uint8_t portno;
+ uint8_t iface_index;
+};
+
+static void
+uhub_find_iface_index(struct usb2_hub *hub, device_t child,
+ struct hub_result *res)
+{
+ struct usb2_interface *iface;
+ struct usb2_device *udev;
+ uint8_t nports;
+ uint8_t x;
+ uint8_t i;
+
+ nports = hub->nports;
+ for (x = 0; x != nports; x++) {
+ udev = usb2_bus_port_get_device(hub->hubudev->bus,
+ hub->ports + x);
+ if (!udev) {
+ continue;
+ }
+ for (i = 0; i != USB_IFACE_MAX; i++) {
+ iface = usb2_get_iface(udev, i);
+ if (iface &&
+ (iface->subdev == child)) {
+ res->iface_index = i;
+ res->udev = udev;
+ res->portno = x + 1;
+ return;
+ }
+ }
+ }
+ res->iface_index = 0;
+ res->udev = NULL;
+ res->portno = 0;
+ return;
+}
+
+static int
+uhub_child_location_string(device_t parent, device_t child,
+ char *buf, size_t buflen)
+{
+ struct uhub_softc *sc = device_get_softc(parent);
+ struct usb2_hub *hub = sc->sc_udev->hub;
+ struct hub_result res;
+
+ mtx_lock(&Giant);
+ uhub_find_iface_index(hub, child, &res);
+ if (!res.udev) {
+ DPRINTF("device not on hub\n");
+ if (buflen) {
+ buf[0] = '\0';
+ }
+ goto done;
+ }
+ snprintf(buf, buflen, "port=%u interface=%u",
+ res.portno, res.iface_index);
+done:
+ mtx_unlock(&Giant);
+
+ return (0);
+}
+
+static int
+uhub_child_pnpinfo_string(device_t parent, device_t child,
+ char *buf, size_t buflen)
+{
+ struct uhub_softc *sc = device_get_softc(parent);
+ struct usb2_hub *hub = sc->sc_udev->hub;
+ struct usb2_interface *iface;
+ struct hub_result res;
+
+ mtx_lock(&Giant);
+ uhub_find_iface_index(hub, child, &res);
+ if (!res.udev) {
+ DPRINTF("device not on hub\n");
+ if (buflen) {
+ buf[0] = '\0';
+ }
+ goto done;
+ }
+ iface = usb2_get_iface(res.udev, res.iface_index);
+ if (iface && iface->idesc) {
+ snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
+ "devclass=0x%02x devsubclass=0x%02x "
+ "sernum=\"%s\" "
+ "intclass=0x%02x intsubclass=0x%02x",
+ UGETW(res.udev->ddesc.idVendor),
+ UGETW(res.udev->ddesc.idProduct),
+ res.udev->ddesc.bDeviceClass,
+ res.udev->ddesc.bDeviceSubClass,
+ res.udev->serial,
+ iface->idesc->bInterfaceClass,
+ iface->idesc->bInterfaceSubClass);
+ } else {
+ if (buflen) {
+ buf[0] = '\0';
+ }
+ goto done;
+ }
+done:
+ mtx_unlock(&Giant);
+
+ return (0);
+}
+
+/*
+ * The USB Transaction Translator:
+ * ===============================
+ *
+ * When doing LOW- and FULL-speed USB transfers accross a HIGH-speed
+ * USB HUB, bandwidth must be allocated for ISOCHRONOUS and INTERRUPT
+ * USB transfers. To utilize bandwidth dynamically the "scatter and
+ * gather" principle must be applied. This means that bandwidth must
+ * be divided into equal parts of bandwidth. With regard to USB all
+ * data is transferred in smaller packets with length
+ * "wMaxPacketSize". The problem however is that "wMaxPacketSize" is
+ * not a constant!
+ *
+ * The bandwidth scheduler which I have implemented will simply pack
+ * the USB transfers back to back until there is no more space in the
+ * schedule. Out of the 8 microframes which the USB 2.0 standard
+ * provides, only 6 are available for non-HIGH-speed devices. I have
+ * reserved the first 4 microframes for ISOCHRONOUS transfers. The
+ * last 2 microframes I have reserved for INTERRUPT transfers. Without
+ * this division, it is very difficult to allocate and free bandwidth
+ * dynamically.
+ *
+ * NOTE about the Transaction Translator in USB HUBs:
+ *
+ * USB HUBs have a very simple Transaction Translator, that will
+ * simply pipeline all the SPLIT transactions. That means that the
+ * transactions will be executed in the order they are queued!
+ *
+ */
+
+/*------------------------------------------------------------------------*
+ * usb2_intr_find_best_slot
+ *
+ * Return value:
+ * The best Transaction Translation slot for an interrupt endpoint.
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_intr_find_best_slot(uint32_t *ptr, uint8_t start, uint8_t end)
+{
+ uint32_t max = 0xffffffff;
+ uint8_t x;
+ uint8_t y;
+
+ y = 0;
+
+ /* find the last slot with lesser used bandwidth */
+
+ for (x = start; x < end; x++) {
+ if (max >= ptr[x]) {
+ max = ptr[x];
+ y = x;
+ }
+ }
+ return (y);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_intr_schedule_adjust
+ *
+ * This function will update the bandwith usage for the microframe
+ * having index "slot" by "len" bytes. "len" can be negative. If the
+ * "slot" argument is greater or equal to "USB_HS_MICRO_FRAMES_MAX"
+ * the "slot" argument will be replaced by the slot having least used
+ * bandwidth.
+ *
+ * Returns:
+ * The slot on which the bandwidth update was done.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_intr_schedule_adjust(struct usb2_device *udev, int16_t len, uint8_t slot)
+{
+ struct usb2_bus *bus = udev->bus;
+ struct usb2_hub *hub;
+
+ mtx_assert(&bus->mtx, MA_OWNED);
+
+ if (usb2_get_speed(udev) == USB_SPEED_HIGH) {
+ if (slot >= USB_HS_MICRO_FRAMES_MAX) {
+ slot = usb2_intr_find_best_slot(bus->uframe_usage, 0,
+ USB_HS_MICRO_FRAMES_MAX);
+ }
+ bus->uframe_usage[slot] += len;
+ } else {
+ if (usb2_get_speed(udev) == USB_SPEED_LOW) {
+ len *= 8;
+ }
+ /*
+ * The Host Controller Driver should have
+ * performed checks so that the lookup
+ * below does not result in a NULL pointer
+ * access.
+ */
+
+ hub = bus->devices[udev->hs_hub_addr]->hub;
+ if (slot >= USB_HS_MICRO_FRAMES_MAX) {
+ slot = usb2_intr_find_best_slot(hub->uframe_usage,
+ USB_FS_ISOC_UFRAME_MAX, 6);
+ }
+ hub->uframe_usage[slot] += len;
+ bus->uframe_usage[slot] += len;
+ }
+ return (slot);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fs_isoc_schedule_init_sub
+ *
+ * This function initialises an USB FULL speed isochronous schedule
+ * entry.
+ *------------------------------------------------------------------------*/
+static void
+usb2_fs_isoc_schedule_init_sub(struct usb2_fs_isoc_schedule *fss)
+{
+ fss->total_bytes = (USB_FS_ISOC_UFRAME_MAX *
+ USB_FS_BYTES_PER_HS_UFRAME);
+ fss->frame_bytes = (USB_FS_BYTES_PER_HS_UFRAME);
+ fss->frame_slot = 0;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fs_isoc_schedule_init_all
+ *
+ * This function will reset the complete USB FULL speed isochronous
+ * bandwidth schedule.
+ *------------------------------------------------------------------------*/
+void
+usb2_fs_isoc_schedule_init_all(struct usb2_fs_isoc_schedule *fss)
+{
+ struct usb2_fs_isoc_schedule *fss_end = fss + USB_ISOC_TIME_MAX;
+
+ while (fss != fss_end) {
+ usb2_fs_isoc_schedule_init_sub(fss);
+ fss++;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_isoc_time_expand
+ *
+ * This function will expand the time counter from 7-bit to 16-bit.
+ *
+ * Returns:
+ * 16-bit isochronous time counter.
+ *------------------------------------------------------------------------*/
+uint16_t
+usb2_isoc_time_expand(struct usb2_bus *bus, uint16_t isoc_time_curr)
+{
+ uint16_t rem;
+
+ mtx_assert(&bus->mtx, MA_OWNED);
+
+ rem = bus->isoc_time_last & (USB_ISOC_TIME_MAX - 1);
+
+ isoc_time_curr &= (USB_ISOC_TIME_MAX - 1);
+
+ if (isoc_time_curr < rem) {
+ /* the time counter wrapped around */
+ bus->isoc_time_last += USB_ISOC_TIME_MAX;
+ }
+ /* update the remainder */
+
+ bus->isoc_time_last &= ~(USB_ISOC_TIME_MAX - 1);
+ bus->isoc_time_last |= isoc_time_curr;
+
+ return (bus->isoc_time_last);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fs_isoc_schedule_isoc_time_expand
+ *
+ * This function does multiple things. First of all it will expand the
+ * passed isochronous time, which is the return value. Then it will
+ * store where the current FULL speed isochronous schedule is
+ * positioned in time and where the end is. See "pp_start" and
+ * "pp_end" arguments.
+ *
+ * Returns:
+ * Expanded version of "isoc_time".
+ *
+ * NOTE: This function depends on being called regularly with
+ * intervals less than "USB_ISOC_TIME_MAX".
+ *------------------------------------------------------------------------*/
+uint16_t
+usb2_fs_isoc_schedule_isoc_time_expand(struct usb2_device *udev,
+ struct usb2_fs_isoc_schedule **pp_start,
+ struct usb2_fs_isoc_schedule **pp_end,
+ uint16_t isoc_time)
+{
+ struct usb2_fs_isoc_schedule *fss_end;
+ struct usb2_fs_isoc_schedule *fss_a;
+ struct usb2_fs_isoc_schedule *fss_b;
+ struct usb2_hub *hs_hub;
+
+ isoc_time = usb2_isoc_time_expand(udev->bus, isoc_time);
+
+ hs_hub = udev->bus->devices[udev->hs_hub_addr]->hub;
+
+ if (hs_hub != NULL) {
+
+ fss_a = hs_hub->fs_isoc_schedule +
+ (hs_hub->isoc_last_time % USB_ISOC_TIME_MAX);
+
+ hs_hub->isoc_last_time = isoc_time;
+
+ fss_b = hs_hub->fs_isoc_schedule +
+ (isoc_time % USB_ISOC_TIME_MAX);
+
+ fss_end = hs_hub->fs_isoc_schedule + USB_ISOC_TIME_MAX;
+
+ *pp_start = hs_hub->fs_isoc_schedule;
+ *pp_end = fss_end;
+
+ while (fss_a != fss_b) {
+ if (fss_a == fss_end) {
+ fss_a = hs_hub->fs_isoc_schedule;
+ continue;
+ }
+ usb2_fs_isoc_schedule_init_sub(fss_a);
+ fss_a++;
+ }
+
+ } else {
+
+ *pp_start = NULL;
+ *pp_end = NULL;
+ }
+ return (isoc_time);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fs_isoc_schedule_alloc
+ *
+ * This function will allocate bandwidth for an isochronous FULL speed
+ * transaction in the FULL speed schedule. The microframe slot where
+ * the transaction should be started is stored in the byte pointed to
+ * by "pstart". The "len" argument specifies the length of the
+ * transaction in bytes.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Error
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_fs_isoc_schedule_alloc(struct usb2_fs_isoc_schedule *fss,
+ uint8_t *pstart, uint16_t len)
+{
+ uint8_t slot = fss->frame_slot;
+
+ /* Compute overhead and bit-stuffing */
+
+ len += 8;
+
+ len *= 7;
+ len /= 6;
+
+ if (len > fss->total_bytes) {
+ *pstart = 0; /* set some dummy value */
+ return (1); /* error */
+ }
+ if (len > 0) {
+
+ fss->total_bytes -= len;
+
+ while (len >= fss->frame_bytes) {
+ len -= fss->frame_bytes;
+ fss->frame_bytes = USB_FS_BYTES_PER_HS_UFRAME;
+ fss->frame_slot++;
+ }
+
+ fss->frame_bytes -= len;
+ }
+ *pstart = slot;
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_port_get_device
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+struct usb2_device *
+usb2_bus_port_get_device(struct usb2_bus *bus, struct usb2_port *up)
+{
+ if ((bus == NULL) || (up == NULL)) {
+ /* be NULL safe */
+ return (NULL);
+ }
+ if (up->device_index == 0) {
+ /* nothing to do */
+ return (NULL);
+ }
+ return (bus->devices[up->device_index]);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_port_set_device
+ *
+ * This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_port_set_device(struct usb2_bus *bus, struct usb2_port *up,
+ struct usb2_device *udev, uint8_t device_index)
+{
+ if (bus == NULL) {
+ /* be NULL safe */
+ return;
+ }
+ /*
+ * There is only one case where we don't
+ * have an USB port, and that is the Root Hub!
+ */
+ if (up) {
+ if (udev) {
+ up->device_index = device_index;
+ } else {
+ device_index = up->device_index;
+ up->device_index = 0;
+ }
+ }
+ /*
+ * Make relationships to our new device
+ */
+ if (device_index != 0) {
+ mtx_lock(&usb2_ref_lock);
+ bus->devices[device_index] = udev;
+ mtx_unlock(&usb2_ref_lock);
+ }
+ /*
+ * Debug print
+ */
+ DPRINTFN(2, "bus %p devices[%u] = %p\n", bus, device_index, udev);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_needs_explore
+ *
+ * This functions is called when the USB event thread needs to run.
+ *------------------------------------------------------------------------*/
+void
+usb2_needs_explore(struct usb2_bus *bus, uint8_t do_probe)
+{
+ DPRINTF("\n");
+
+ if (bus == NULL) {
+ DPRINTF("No bus pointer!\n");
+ return;
+ }
+ mtx_lock(&bus->mtx);
+ if (do_probe) {
+ bus->do_probe = 1;
+ }
+ if (usb2_proc_msignal(&bus->explore_proc,
+ &bus->explore_msg[0], &bus->explore_msg[1])) {
+ /* ignore */
+ }
+ mtx_unlock(&bus->mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_needs_explore_all
+ *
+ * This function is called whenever a new driver is loaded and will
+ * cause that all USB busses are re-explored.
+ *------------------------------------------------------------------------*/
+void
+usb2_needs_explore_all(void)
+{
+ struct usb2_bus *bus;
+ devclass_t dc;
+ device_t dev;
+ int max;
+
+ DPRINTFN(3, "\n");
+
+ dc = usb2_devclass_ptr;
+ if (dc == NULL) {
+ DPRINTFN(0, "no devclass\n");
+ return;
+ }
+ /*
+ * Explore all USB busses in parallell.
+ */
+ max = devclass_get_maxunit(dc);
+ while (max >= 0) {
+ dev = devclass_get_device(dc, max);
+ if (dev) {
+ bus = device_get_softc(dev);
+ if (bus) {
+ usb2_needs_explore(bus, 1);
+ }
+ }
+ max--;
+ }
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_hub.h b/sys/dev/usb2/core/usb2_hub.h
new file mode 100644
index 000000000000..183c9e02d854
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_hub.h
@@ -0,0 +1,75 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_HUB_H_
+#define _USB2_HUB_H_
+
+/*
+ * The following structure defines an USB port.
+ */
+struct usb2_port {
+ uint8_t restartcnt;
+#define USB_RESTART_MAX 5
+ uint8_t device_index; /* zero means not valid */
+ uint8_t usb2_mode:1; /* current USB mode */
+ uint8_t unused:7;
+};
+
+/*
+ * The following structure defines how many bytes are
+ * left in an 1ms USB time slot.
+ */
+struct usb2_fs_isoc_schedule {
+ uint16_t total_bytes;
+ uint8_t frame_bytes;
+ uint8_t frame_slot;
+};
+
+/*
+ * The following structure defines an USB HUB.
+ */
+struct usb2_hub {
+ struct usb2_fs_isoc_schedule fs_isoc_schedule[USB_ISOC_TIME_MAX];
+ struct usb2_device *hubudev; /* the HUB device */
+ usb2_error_t (*explore) (struct usb2_device *hub);
+ void *hubsoftc;
+ uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX];
+ uint16_t portpower; /* mA per USB port */
+ uint8_t isoc_last_time;
+ uint8_t nports;
+ struct usb2_port ports[0];
+};
+
+/* function prototypes */
+
+uint8_t usb2_intr_schedule_adjust(struct usb2_device *udev, int16_t len, uint8_t slot);
+void usb2_fs_isoc_schedule_init_all(struct usb2_fs_isoc_schedule *fss);
+void usb2_bus_port_set_device(struct usb2_bus *bus, struct usb2_port *up, struct usb2_device *udev, uint8_t device_index);
+struct usb2_device *usb2_bus_port_get_device(struct usb2_bus *bus, struct usb2_port *up);
+void usb2_needs_explore(struct usb2_bus *bus, uint8_t do_probe);
+void usb2_needs_explore_all(void);
+
+#endif /* _USB2_HUB_H_ */
diff --git a/sys/dev/usb2/core/usb2_if.m b/sys/dev/usb2/core/usb2_if.m
new file mode 100644
index 000000000000..d0db8d4445f9
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_if.m
@@ -0,0 +1,52 @@
+#-
+# Copyright (c) 2008 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,
+# without modification, immediately at the beginning of the file.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# $FreeBSD$
+#
+
+# USB interface description
+#
+
+#include <sys/bus.h>
+
+INTERFACE usb2;
+
+# The device received a control request
+#
+# Return values:
+# 0: Success
+# ENOTTY: Transaction stalled
+# Else: Use builtin request handler
+#
+METHOD int handle_request {
+ device_t dev;
+ const void *req; /* pointer to the device request */
+ void **pptr; /* data pointer */
+ uint16_t *plen; /* maximum transfer length */
+ uint16_t offset; /* data offset */
+ uint8_t is_complete; /* set if transfer is complete */
+};
+
diff --git a/sys/dev/usb2/core/usb2_lookup.c b/sys/dev/usb2/core/usb2_lookup.c
new file mode 100644
index 000000000000..eaab8a3d6b12
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_lookup.c
@@ -0,0 +1,134 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_lookup.h>
+
+/*------------------------------------------------------------------------*
+ * usb2_lookup_id_by_info
+ *
+ * This functions takes an array of "struct usb2_device_id" and tries
+ * to match the entries with the information in "struct usb2_lookup_info".
+ *
+ * NOTE: The "sizeof_id" parameter must be a multiple of the
+ * usb2_device_id structure size. Else the behaviour of this function
+ * is undefined.
+ *
+ * Return values:
+ * NULL: No match found.
+ * Else: Pointer to matching entry.
+ *------------------------------------------------------------------------*/
+const struct usb2_device_id *
+usb2_lookup_id_by_info(const struct usb2_device_id *id, uint32_t sizeof_id,
+ const struct usb2_lookup_info *info)
+{
+ const struct usb2_device_id *id_end;
+
+ if (id == NULL) {
+ goto done;
+ }
+ id_end = (const void *)(((const uint8_t *)id) + sizeof_id);
+
+ /*
+ * Keep on matching array entries until we find a match or
+ * until we reach the end of the matching array:
+ */
+ for (; id != id_end; id++) {
+
+ if ((id->match_flag_vendor) &&
+ (id->idVendor != info->idVendor)) {
+ continue;
+ }
+ if ((id->match_flag_product) &&
+ (id->idProduct != info->idProduct)) {
+ continue;
+ }
+ if ((id->match_flag_dev_lo) &&
+ (id->bcdDevice_lo > info->bcdDevice)) {
+ continue;
+ }
+ if ((id->match_flag_dev_hi) &&
+ (id->bcdDevice_hi < info->bcdDevice)) {
+ continue;
+ }
+ if ((id->match_flag_dev_class) &&
+ (id->bDeviceClass != info->bDeviceClass)) {
+ continue;
+ }
+ if ((id->match_flag_dev_subclass) &&
+ (id->bDeviceSubClass != info->bDeviceSubClass)) {
+ continue;
+ }
+ if ((id->match_flag_dev_protocol) &&
+ (id->bDeviceProtocol != info->bDeviceProtocol)) {
+ continue;
+ }
+ if ((info->bDeviceClass == 0xFF) &&
+ (!(id->match_flag_vendor)) &&
+ ((id->match_flag_int_class) ||
+ (id->match_flag_int_subclass) ||
+ (id->match_flag_int_protocol))) {
+ continue;
+ }
+ if ((id->match_flag_int_class) &&
+ (id->bInterfaceClass != info->bInterfaceClass)) {
+ continue;
+ }
+ if ((id->match_flag_int_subclass) &&
+ (id->bInterfaceSubClass != info->bInterfaceSubClass)) {
+ continue;
+ }
+ if ((id->match_flag_int_protocol) &&
+ (id->bInterfaceProtocol != info->bInterfaceProtocol)) {
+ continue;
+ }
+ /* We found a match! */
+ return (id);
+ }
+
+done:
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_lookup_id_by_uaa - factored out code
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+int
+usb2_lookup_id_by_uaa(const struct usb2_device_id *id, uint32_t sizeof_id,
+ struct usb2_attach_arg *uaa)
+{
+ id = usb2_lookup_id_by_info(id, sizeof_id, &uaa->info);
+ if (id) {
+ /* copy driver info */
+ uaa->driver_info = id->driver_info;
+ return (0);
+ }
+ return (ENXIO);
+}
diff --git a/sys/dev/usb2/core/usb2_lookup.h b/sys/dev/usb2/core/usb2_lookup.h
new file mode 100644
index 000000000000..d4e39fd53b6e
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_lookup.h
@@ -0,0 +1,119 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_LOOKUP_H_
+#define _USB2_LOOKUP_H_
+
+struct usb2_attach_arg;
+
+/*
+ * The following structure is used when looking up an USB driver for
+ * an USB device. It is inspired by the Linux structure called
+ * "usb2_device_id".
+ */
+struct usb2_device_id {
+
+ /* Hook for driver specific information */
+ const void *driver_info;
+
+ /* Used for product specific matches; the BCD range is inclusive */
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice_lo;
+ uint16_t bcdDevice_hi;
+
+ /* Used for device class matches */
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+
+ /* Used for interface class matches */
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+
+ /* Select which fields to match against */
+ uint8_t match_flag_vendor:1;
+ uint8_t match_flag_product:1;
+ uint8_t match_flag_dev_lo:1;
+ uint8_t match_flag_dev_hi:1;
+ uint8_t match_flag_dev_class:1;
+ uint8_t match_flag_dev_subclass:1;
+ uint8_t match_flag_dev_protocol:1;
+ uint8_t match_flag_int_class:1;
+ uint8_t match_flag_int_subclass:1;
+ uint8_t match_flag_int_protocol:1;
+};
+
+#define USB_VENDOR(vend) \
+ .match_flag_vendor = 1, .idVendor = (vend)
+
+#define USB_PRODUCT(prod) \
+ .match_flag_product = 1, .idProduct = (prod)
+
+#define USB_VP(vend,prod) \
+ USB_VENDOR(vend), USB_PRODUCT(prod)
+
+#define USB_VPI(vend,prod,info) \
+ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info)
+
+#define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \
+ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo)
+
+#define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \
+ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi)
+
+#define USB_DEV_CLASS(dc) \
+ .match_flag_dev_class = 1, .bDeviceClass = (dc)
+
+#define USB_DEV_SUBCLASS(dsc) \
+ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc)
+
+#define USB_DEV_PROTOCOL(dp) \
+ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp)
+
+#define USB_IFACE_CLASS(ic) \
+ .match_flag_int_class = 1, .bInterfaceClass = (ic)
+
+#define USB_IFACE_SUBCLASS(isc) \
+ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc)
+
+#define USB_IFACE_PROTOCOL(ip) \
+ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip)
+
+#define USB_IF_CSI(class,subclass,info) \
+ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info)
+
+#define USB_DRIVER_INFO(ptr) \
+ .driver_info = ((const void *)(ptr))
+
+#define USB_GET_DRIVER_INFO(did) \
+ (((const uint8_t *)((did)->driver_info)) - ((const uint8_t *)0))
+
+const struct usb2_device_id *usb2_lookup_id_by_info(const struct usb2_device_id *id, uint32_t sizeof_id, const struct usb2_lookup_info *info);
+int usb2_lookup_id_by_uaa(const struct usb2_device_id *id, uint32_t sizeof_id, struct usb2_attach_arg *uaa);
+
+#endif /* _USB2_LOOKUP_H_ */
diff --git a/sys/dev/usb2/core/usb2_mbuf.c b/sys/dev/usb2/core/usb2_mbuf.c
new file mode 100644
index 000000000000..1f06f0f0f0dd
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_mbuf.c
@@ -0,0 +1,77 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_mbuf.h>
+
+/*------------------------------------------------------------------------*
+ * usb2_alloc_mbufs - allocate mbufs to an usbd interface queue
+ *
+ * Returns:
+ * A pointer that should be passed to "free()" when the buffer(s)
+ * should be released.
+ *------------------------------------------------------------------------*/
+void *
+usb2_alloc_mbufs(struct malloc_type *type, struct usb2_ifqueue *ifq,
+ uint32_t block_size, uint16_t nblocks)
+{
+ struct usb2_mbuf *m_ptr;
+ uint8_t *data_ptr;
+ void *free_ptr = NULL;
+ uint32_t alloc_size;
+
+ /* align data */
+ block_size += ((-block_size) & (USB_HOST_ALIGN - 1));
+
+ if (nblocks && block_size) {
+
+ alloc_size = (block_size + sizeof(struct usb2_mbuf)) * nblocks;
+
+ free_ptr = malloc(alloc_size, type, M_WAITOK | M_ZERO);
+
+ if (free_ptr == NULL) {
+ goto done;
+ }
+ m_ptr = free_ptr;
+ data_ptr = (void *)(m_ptr + nblocks);
+
+ while (nblocks--) {
+
+ m_ptr->cur_data_ptr =
+ m_ptr->min_data_ptr = data_ptr;
+
+ m_ptr->cur_data_len =
+ m_ptr->max_data_len = block_size;
+
+ USB_IF_ENQUEUE(ifq, m_ptr);
+
+ m_ptr++;
+ data_ptr += block_size;
+ }
+ }
+done:
+ return (free_ptr);
+}
diff --git a/sys/dev/usb2/core/usb2_mbuf.h b/sys/dev/usb2/core/usb2_mbuf.h
new file mode 100644
index 000000000000..521263ecab02
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_mbuf.h
@@ -0,0 +1,100 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_MBUF_H_
+#define _USB2_MBUF_H_
+
+/*
+ * The following structure defines a minimum re-implementation of the
+ * mbuf system in the kernel.
+ */
+struct usb2_mbuf {
+ uint8_t *cur_data_ptr;
+ uint8_t *min_data_ptr;
+ struct usb2_mbuf *usb2_nextpkt;
+ struct usb2_mbuf *usb2_next;
+
+ uint32_t cur_data_len;
+ uint32_t max_data_len:31;
+ uint32_t last_packet:1;
+};
+
+/*
+ * The following structure defines a minimum re-implementation of the
+ * ifqueue structure in the kernel.
+ */
+struct usb2_ifqueue {
+ struct usb2_mbuf *ifq_head;
+ struct usb2_mbuf *ifq_tail;
+
+ uint32_t ifq_len;
+ uint32_t ifq_maxlen;
+};
+
+#define USB_IF_ENQUEUE(ifq, m) do { \
+ (m)->usb2_nextpkt = NULL; \
+ if ((ifq)->ifq_tail == NULL) \
+ (ifq)->ifq_head = (m); \
+ else \
+ (ifq)->ifq_tail->usb2_nextpkt = (m); \
+ (ifq)->ifq_tail = (m); \
+ (ifq)->ifq_len++; \
+ } while (0)
+
+#define USB_IF_DEQUEUE(ifq, m) do { \
+ (m) = (ifq)->ifq_head; \
+ if (m) { \
+ if (((ifq)->ifq_head = (m)->usb2_nextpkt) == NULL) { \
+ (ifq)->ifq_tail = NULL; \
+ } \
+ (m)->usb2_nextpkt = NULL; \
+ (ifq)->ifq_len--; \
+ } \
+ } while (0)
+
+#define USB_IF_PREPEND(ifq, m) do { \
+ (m)->usb2_nextpkt = (ifq)->ifq_head; \
+ if ((ifq)->ifq_tail == NULL) { \
+ (ifq)->ifq_tail = (m); \
+ } \
+ (ifq)->ifq_head = (m); \
+ (ifq)->ifq_len++; \
+ } while (0)
+
+#define USB_IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen)
+#define USB_IF_QLEN(ifq) ((ifq)->ifq_len)
+#define USB_IF_POLL(ifq, m) ((m) = (ifq)->ifq_head)
+
+#define USB_MBUF_RESET(m) do { \
+ (m)->cur_data_ptr = (m)->min_data_ptr; \
+ (m)->cur_data_len = (m)->max_data_len; \
+ (m)->last_packet = 0; \
+ } while (0)
+
+/* prototypes */
+void *usb2_alloc_mbufs(struct malloc_type *type, struct usb2_ifqueue *ifq, uint32_t block_size, uint16_t nblocks);
+
+#endif /* _USB2_MBUF_H_ */
diff --git a/sys/dev/usb2/core/usb2_msctest.c b/sys/dev/usb2/core/usb2_msctest.c
new file mode 100644
index 000000000000..9db204a1523c
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_msctest.c
@@ -0,0 +1,612 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * The following file contains code that will detect USB autoinstall
+ * disks.
+ *
+ * TODO: Potentially we could add code to automatically detect USB
+ * mass storage quirks for not supported SCSI commands!
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_msctest.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_util.h>
+
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+enum {
+ ST_COMMAND,
+ ST_DATA_RD,
+ ST_DATA_RD_CS,
+ ST_DATA_WR,
+ ST_DATA_WR_CS,
+ ST_STATUS,
+ ST_MAX,
+};
+
+enum {
+ DIR_IN,
+ DIR_OUT,
+ DIR_NONE,
+};
+
+#define BULK_SIZE 64 /* dummy */
+
+
+/* Command Block Wrapper */
+struct bbb_cbw {
+ uDWord dCBWSignature;
+#define CBWSIGNATURE 0x43425355
+ uDWord dCBWTag;
+ uDWord dCBWDataTransferLength;
+ uByte bCBWFlags;
+#define CBWFLAGS_OUT 0x00
+#define CBWFLAGS_IN 0x80
+ uByte bCBWLUN;
+ uByte bCDBLength;
+#define CBWCDBLENGTH 16
+ uByte CBWCDB[CBWCDBLENGTH];
+} __packed;
+
+/* Command Status Wrapper */
+struct bbb_csw {
+ uDWord dCSWSignature;
+#define CSWSIGNATURE 0x53425355
+ uDWord dCSWTag;
+ uDWord dCSWDataResidue;
+ uByte bCSWStatus;
+#define CSWSTATUS_GOOD 0x0
+#define CSWSTATUS_FAILED 0x1
+#define CSWSTATUS_PHASE 0x2
+} __packed;
+
+struct bbb_transfer {
+ struct mtx mtx;
+ struct cv cv;
+ struct bbb_cbw cbw;
+ struct bbb_csw csw;
+
+ struct usb2_xfer *xfer[ST_MAX];
+
+ uint8_t *data_ptr;
+
+ uint32_t data_len; /* bytes */
+ uint32_t data_rem; /* bytes */
+ uint32_t data_timeout; /* ms */
+ uint32_t actlen; /* bytes */
+
+ uint8_t cmd_len; /* bytes */
+ uint8_t dir;
+ uint8_t lun;
+ uint8_t state;
+ uint8_t error;
+ uint8_t status_try;
+
+ uint8_t buffer[256];
+};
+
+static usb2_callback_t bbb_command_callback;
+static usb2_callback_t bbb_data_read_callback;
+static usb2_callback_t bbb_data_rd_cs_callback;
+static usb2_callback_t bbb_data_write_callback;
+static usb2_callback_t bbb_data_wr_cs_callback;
+static usb2_callback_t bbb_status_callback;
+
+static const struct usb2_config bbb_config[ST_MAX] = {
+
+ [ST_COMMAND] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = sizeof(struct bbb_cbw),
+ .mh.flags = {},
+ .mh.callback = &bbb_command_callback,
+ .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */
+ },
+
+ [ST_DATA_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .mh.callback = &bbb_data_read_callback,
+ .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */
+ },
+
+ [ST_DATA_RD_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &bbb_data_rd_cs_callback,
+ .mh.timeout = 1 * USB_MS_HZ, /* 1 second */
+ },
+
+ [ST_DATA_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .mh.callback = &bbb_data_write_callback,
+ .mh.timeout = 4 * USB_MS_HZ, /* 4 seconds */
+ },
+
+ [ST_DATA_WR_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &bbb_data_wr_cs_callback,
+ .mh.timeout = 1 * USB_MS_HZ, /* 1 second */
+ },
+
+ [ST_STATUS] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = sizeof(struct bbb_csw),
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &bbb_status_callback,
+ .mh.timeout = 1 * USB_MS_HZ, /* 1 second */
+ },
+};
+
+static void
+bbb_done(struct bbb_transfer *sc, uint8_t error)
+{
+ struct usb2_xfer *xfer;
+
+ xfer = sc->xfer[sc->state];
+
+ /* verify the error code */
+
+ if (error) {
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+ error = 1;
+ break;
+ default:
+ error = 2;
+ break;
+ }
+ }
+ sc->error = error;
+ sc->state = ST_COMMAND;
+ sc->status_try = 1;
+ usb2_cv_signal(&sc->cv);
+ return;
+}
+
+static void
+bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
+{
+ sc->state = xfer_index;
+ usb2_transfer_start(sc->xfer[xfer_index]);
+ return;
+}
+
+static void
+bbb_data_clear_stall_callback(struct usb2_xfer *xfer,
+ uint8_t next_xfer, uint8_t stall_xfer)
+{
+ struct bbb_transfer *sc = xfer->priv_sc;
+
+ if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+ bbb_transfer_start(sc, next_xfer);
+ break;
+ default:
+ bbb_done(sc, 1);
+ break;
+ }
+ }
+ return;
+}
+
+static void
+bbb_command_callback(struct usb2_xfer *xfer)
+{
+ struct bbb_transfer *sc = xfer->priv_sc;
+ uint32_t tag;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ bbb_transfer_start
+ (sc, ((sc->dir == DIR_IN) ? ST_DATA_RD :
+ (sc->dir == DIR_OUT) ? ST_DATA_WR :
+ ST_STATUS));
+ break;
+
+ case USB_ST_SETUP:
+ sc->status_try = 0;
+ tag = UGETDW(sc->cbw.dCBWTag) + 1;
+ USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
+ USETDW(sc->cbw.dCBWTag, tag);
+ USETDW(sc->cbw.dCBWDataTransferLength, sc->data_len);
+ sc->cbw.bCBWFlags = ((sc->dir == DIR_IN) ? CBWFLAGS_IN : CBWFLAGS_OUT);
+ sc->cbw.bCBWLUN = sc->lun;
+ if (sc->cbw.bCDBLength > sizeof(sc->cbw.CBWCDB)) {
+ sc->cbw.bCDBLength = sizeof(sc->cbw.CBWCDB);
+ DPRINTFN(0, "Truncating long command!\n");
+ }
+ xfer->frlengths[0] = sizeof(sc->cbw);
+
+ usb2_set_frame_data(xfer, &sc->cbw, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ bbb_done(sc, 1);
+ break;
+ }
+ return;
+}
+
+static void
+bbb_data_read_callback(struct usb2_xfer *xfer)
+{
+ struct bbb_transfer *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->data_rem -= xfer->actlen;
+ sc->data_ptr += xfer->actlen;
+ sc->actlen += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->data_rem = 0;
+ }
+ case USB_ST_SETUP:
+ DPRINTF("max_bulk=%d, data_rem=%d\n",
+ max_bulk, sc->data_rem);
+
+ if (sc->data_rem == 0) {
+ bbb_transfer_start(sc, ST_STATUS);
+ break;
+ }
+ if (max_bulk > sc->data_rem) {
+ max_bulk = sc->data_rem;
+ }
+ xfer->timeout = sc->data_timeout;
+ xfer->frlengths[0] = max_bulk;
+
+ usb2_set_frame_data(xfer, sc->data_ptr, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ bbb_done(sc, 1);
+ } else {
+ bbb_transfer_start(sc, ST_DATA_RD_CS);
+ }
+ break;
+ }
+ return;
+}
+
+static void
+bbb_data_rd_cs_callback(struct usb2_xfer *xfer)
+{
+ bbb_data_clear_stall_callback(xfer, ST_STATUS,
+ ST_DATA_RD);
+ return;
+}
+
+static void
+bbb_data_write_callback(struct usb2_xfer *xfer)
+{
+ struct bbb_transfer *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->data_rem -= xfer->actlen;
+ sc->data_ptr += xfer->actlen;
+ sc->actlen += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->data_rem = 0;
+ }
+ case USB_ST_SETUP:
+ DPRINTF("max_bulk=%d, data_rem=%d\n",
+ max_bulk, sc->data_rem);
+
+ if (sc->data_rem == 0) {
+ bbb_transfer_start(sc, ST_STATUS);
+ return;
+ }
+ if (max_bulk > sc->data_rem) {
+ max_bulk = sc->data_rem;
+ }
+ xfer->timeout = sc->data_timeout;
+ xfer->frlengths[0] = max_bulk;
+
+ usb2_set_frame_data(xfer, sc->data_ptr, 0);
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ bbb_done(sc, 1);
+ } else {
+ bbb_transfer_start(sc, ST_DATA_WR_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+bbb_data_wr_cs_callback(struct usb2_xfer *xfer)
+{
+ bbb_data_clear_stall_callback(xfer, ST_STATUS,
+ ST_DATA_WR);
+ return;
+}
+
+static void
+bbb_status_callback(struct usb2_xfer *xfer)
+{
+ struct bbb_transfer *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ /* very simple status check */
+
+ if (xfer->actlen < sizeof(sc->csw)) {
+ bbb_done(sc, 1);/* error */
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
+ bbb_done(sc, 0);/* success */
+ } else {
+ bbb_done(sc, 1);/* error */
+ }
+ break;
+
+ case USB_ST_SETUP:
+ xfer->frlengths[0] = sizeof(sc->csw);
+
+ usb2_set_frame_data(xfer, &sc->csw, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default:
+ DPRINTFN(0, "Failed to read CSW: %s, try %d\n",
+ usb2_errstr(xfer->error), sc->status_try);
+
+ if ((xfer->error == USB_ERR_CANCELLED) ||
+ (sc->status_try)) {
+ bbb_done(sc, 1);
+ } else {
+ sc->status_try = 1;
+ bbb_transfer_start(sc, ST_DATA_RD_CS);
+ }
+ break;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * bbb_command_start - execute a SCSI command synchronously
+ *
+ * Return values
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
+ void *data_ptr, uint32_t data_len, uint8_t cmd_len,
+ uint32_t data_timeout)
+{
+ sc->lun = lun;
+ sc->dir = data_len ? dir : DIR_NONE;
+ sc->data_ptr = data_ptr;
+ sc->data_len = data_len;
+ sc->data_rem = data_len;
+ sc->data_timeout = (data_timeout + USB_MS_HZ);
+ sc->actlen = 0;
+ sc->cmd_len = cmd_len;
+
+ usb2_transfer_start(sc->xfer[sc->state]);
+
+ while (usb2_transfer_pending(sc->xfer[sc->state])) {
+ usb2_cv_wait(&sc->cv, &sc->mtx);
+ }
+ return (sc->error);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_test_autoinstall
+ *
+ * Return values:
+ * 0: This interface is an auto install disk (CD-ROM)
+ * Else: Not an auto install disk.
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ usb2_error_t err;
+ uint8_t timeout;
+ uint8_t sid_type;
+ struct bbb_transfer *sc;
+
+ if (udev == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ id = iface->idesc;
+ if (id == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ if (id->bInterfaceClass != UICLASS_MASS) {
+ return (USB_ERR_INVAL);
+ }
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_SCSI:
+ case UISUBCLASS_UFI:
+ break;
+ default:
+ return (USB_ERR_INVAL);
+ }
+
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_BBB_OLD:
+ case UIPROTO_MASS_BBB:
+ break;
+ default:
+ return (USB_ERR_INVAL);
+ }
+
+ sc = malloc(sizeof(*sc), M_USB, M_WAITOK | M_ZERO);
+ if (sc == NULL) {
+ return (USB_ERR_NOMEM);
+ }
+ mtx_init(&sc->mtx, "USB autoinstall", NULL, MTX_DEF);
+ usb2_cv_init(&sc->cv, "WBBB");
+
+ err = usb2_transfer_setup(udev,
+ &iface_index, sc->xfer, bbb_config,
+ ST_MAX, sc, &sc->mtx);
+
+ if (err) {
+ goto done;
+ }
+ mtx_lock(&sc->mtx);
+
+ timeout = 4; /* tries */
+
+repeat_inquiry:
+
+ sc->cbw.CBWCDB[0] = 0x12; /* INQUIRY */
+
+ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 256, 6, USB_MS_HZ);
+ if (err) {
+ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 256, 12, USB_MS_HZ);
+ if (err) {
+ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, 256, 16, USB_MS_HZ);
+ }
+ }
+ if ((sc->actlen != 0) && (err == 0)) {
+ sid_type = sc->buffer[0] & 0x1F;
+ if (sid_type == 0x05) {
+ /* CD-ROM */
+ /* XXX could investigate more */
+ return (0);
+ }
+ } else if (--timeout) {
+ usb2_pause_mtx(&sc->mtx, USB_MS_HZ);
+ goto repeat_inquiry;
+ }
+ err = USB_ERR_INVAL;
+ goto done;
+
+done:
+ mtx_unlock(&sc->mtx);
+ usb2_transfer_unsetup(sc->xfer, ST_MAX);
+ mtx_destroy(&sc->mtx);
+ usb2_cv_destroy(&sc->cv);
+ free(sc, M_USB);
+ return (err);
+}
+
+/*
+ * Huawei Exxx radio devices have a built in flash disk which is their
+ * default power up configuration. This allows the device to carry its
+ * own installation software.
+ *
+ * Instead of following the USB spec, and create multiple
+ * configuration descriptors for this, the devices expects the driver
+ * to send UF_DEVICE_REMOTE_WAKEUP to endpoint 2 to reset the device,
+ * so it reprobes, now with the radio exposed.
+ */
+
+usb2_error_t
+usb2_test_huawei(struct usb2_device *udev, uint8_t iface_index)
+{
+ struct usb2_device_request req;
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ usb2_error_t err;
+
+ if (udev == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ id = iface->idesc;
+ if (id == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ if (id->bInterfaceClass != UICLASS_MASS) {
+ return (USB_ERR_INVAL);
+ }
+ /* Bend it like Beckham */
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+ USETW(req.wIndex, 2);
+ USETW(req.wLength, 0);
+
+ /* We get error at return, but it works */
+ err = usb2_do_request_flags(udev, NULL, &req, NULL, 0, NULL, 1 * USB_MS_HZ);
+
+ return (0); /* success */
+}
diff --git a/sys/dev/usb2/core/usb2_msctest.h b/sys/dev/usb2/core/usb2_msctest.h
new file mode 100644
index 000000000000..19aac3697bd1
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_msctest.h
@@ -0,0 +1,33 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_MSCTEST_H_
+#define _USB2_MSCTEST_H_
+
+usb2_error_t usb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index);
+usb2_error_t usb2_test_huawei(struct usb2_device *udev, uint8_t iface_index);
+
+#endif /* _USB2_MSCTEST_H_ */
diff --git a/sys/dev/usb2/core/usb2_parse.c b/sys/dev/usb2/core/usb2_parse.c
new file mode 100644
index 000000000000..223b3c2634da
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_parse.c
@@ -0,0 +1,208 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_parse.h>
+
+/*------------------------------------------------------------------------*
+ * usb2_desc_foreach
+ *
+ * This function is the safe way to iterate across the USB config
+ * descriptor. It contains several checks against invalid
+ * descriptors. If the "desc" argument passed to this function is
+ * "NULL" the first descriptor, if any, will be returned.
+ *
+ * Return values:
+ * NULL: End of descriptors
+ * Else: Next descriptor after "desc"
+ *------------------------------------------------------------------------*/
+struct usb2_descriptor *
+usb2_desc_foreach(struct usb2_config_descriptor *cd, struct usb2_descriptor *desc)
+{
+ void *end;
+
+ if (cd == NULL) {
+ return (NULL);
+ }
+ end = USB_ADD_BYTES(cd, UGETW(cd->wTotalLength));
+
+ if (desc == NULL) {
+ desc = USB_ADD_BYTES(cd, 0);
+ } else {
+ desc = USB_ADD_BYTES(desc, desc->bLength);
+ }
+ return (((((void *)desc) >= ((void *)cd)) &&
+ (((void *)desc) < end) &&
+ (USB_ADD_BYTES(desc, desc->bLength) >= ((void *)cd)) &&
+ (USB_ADD_BYTES(desc, desc->bLength) <= end) &&
+ (desc->bLength >= sizeof(*desc))) ? desc : NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_find_idesc
+ *
+ * This function will return the interface descriptor, if any, that
+ * has index "iface_index" and alternate index "alt_index".
+ *
+ * Return values:
+ * NULL: End of descriptors
+ * Else: A valid interface descriptor
+ *------------------------------------------------------------------------*/
+struct usb2_interface_descriptor *
+usb2_find_idesc(struct usb2_config_descriptor *cd,
+ uint8_t iface_index, uint8_t alt_index)
+{
+ struct usb2_descriptor *desc = NULL;
+ struct usb2_interface_descriptor *id;
+ uint8_t curidx = 0;
+ uint8_t lastidx = 0;
+ uint8_t curaidx = 0;
+ uint8_t first = 1;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+ if ((desc->bDescriptorType == UDESC_INTERFACE) &&
+ (desc->bLength >= sizeof(*id))) {
+ id = (void *)desc;
+
+ if (first) {
+ first = 0;
+ lastidx = id->bInterfaceNumber;
+
+ } else if (id->bInterfaceNumber != lastidx) {
+
+ lastidx = id->bInterfaceNumber;
+ curidx++;
+ curaidx = 0;
+
+ } else {
+ curaidx++;
+ }
+
+ if ((iface_index == curidx) && (alt_index == curaidx)) {
+ return (id);
+ }
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_find_edesc
+ *
+ * This function will return the endpoint descriptor for the passed
+ * interface index, alternate index and endpoint index.
+ *
+ * Return values:
+ * NULL: End of descriptors
+ * Else: A valid endpoint descriptor
+ *------------------------------------------------------------------------*/
+struct usb2_endpoint_descriptor *
+usb2_find_edesc(struct usb2_config_descriptor *cd,
+ uint8_t iface_index, uint8_t alt_index, uint8_t ep_index)
+{
+ struct usb2_descriptor *desc = NULL;
+ struct usb2_interface_descriptor *d;
+ uint8_t curidx = 0;
+
+ d = usb2_find_idesc(cd, iface_index, alt_index);
+ if (d == NULL)
+ return (NULL);
+
+ if (ep_index >= d->bNumEndpoints) /* quick exit */
+ return (NULL);
+
+ desc = ((void *)d);
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ if (desc->bDescriptorType == UDESC_INTERFACE) {
+ break;
+ }
+ if (desc->bDescriptorType == UDESC_ENDPOINT) {
+ if (curidx == ep_index) {
+ if (desc->bLength <
+ sizeof(struct usb2_endpoint_descriptor)) {
+ /* endpoint index is invalid */
+ break;
+ }
+ return ((void *)desc);
+ }
+ curidx++;
+ }
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_no_endpoints
+ *
+ * This function will count the total number of endpoints available.
+ *------------------------------------------------------------------------*/
+uint16_t
+usb2_get_no_endpoints(struct usb2_config_descriptor *cd)
+{
+ struct usb2_descriptor *desc = NULL;
+ uint16_t count = 0;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+ if (desc->bDescriptorType == UDESC_ENDPOINT) {
+ count++;
+ }
+ }
+ return (count);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_no_alts
+ *
+ * Return value:
+ * Number of alternate settings for the given "ifaceno".
+ *
+ * NOTE: The returned can be larger than the actual number of
+ * alternate settings.
+ *------------------------------------------------------------------------*/
+uint16_t
+usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno)
+{
+ struct usb2_descriptor *desc = NULL;
+ struct usb2_interface_descriptor *id;
+ uint16_t n = 0;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ if ((desc->bDescriptorType == UDESC_INTERFACE) &&
+ (desc->bLength >= sizeof(*id))) {
+ id = (void *)desc;
+ if (id->bInterfaceNumber == ifaceno) {
+ n++;
+ }
+ }
+ }
+ return (n);
+}
diff --git a/sys/dev/usb2/core/usb2_parse.h b/sys/dev/usb2/core/usb2_parse.h
new file mode 100644
index 000000000000..f367f1ad757b
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_parse.h
@@ -0,0 +1,36 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_PARSE_H_
+#define _USB2_PARSE_H_
+
+struct usb2_descriptor *usb2_desc_foreach(struct usb2_config_descriptor *cd, struct usb2_descriptor *desc);
+struct usb2_interface_descriptor *usb2_find_idesc(struct usb2_config_descriptor *cd, uint8_t iface_index, uint8_t alt_index);
+struct usb2_endpoint_descriptor *usb2_find_edesc(struct usb2_config_descriptor *cd, uint8_t iface_index, uint8_t alt_index, uint8_t ep_index);
+uint16_t usb2_get_no_endpoints(struct usb2_config_descriptor *cd);
+uint16_t usb2_get_no_alts(struct usb2_config_descriptor *cd, uint8_t ifaceno);
+
+#endif /* _USB2_PARSE_H_ */
diff --git a/sys/dev/usb2/core/usb2_process.c b/sys/dev/usb2/core/usb2_process.c
new file mode 100644
index 000000000000..97365722e269
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_process.c
@@ -0,0 +1,480 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#define USB_DEBUG_VAR usb2_proc_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_util.h>
+
+#include <sys/proc.h>
+#include <sys/kthread.h>
+#include <sys/sched.h>
+
+#if (__FreeBSD_version < 700000)
+#define thread_lock(td) mtx_lock_spin(&sched_lock)
+#define thread_unlock(td) mtx_unlock_spin(&sched_lock)
+#endif
+
+#if (__FreeBSD_version >= 800000)
+#define USB_THREAD_CREATE(f, s, p, ...) \
+ kproc_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__)
+#define USB_THREAD_SUSPEND(p) kproc_suspend(p,0)
+#define USB_THREAD_EXIT(err) kproc_exit(err)
+#else
+#define USB_THREAD_CREATE(f, s, p, ...) \
+ kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__)
+#define USB_THREAD_SUSPEND(p) kthread_suspend(p,0)
+#define USB_THREAD_EXIT(err) kthread_exit(err)
+#endif
+
+#if USB_DEBUG
+static int usb2_proc_debug;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, proc, CTLFLAG_RW, 0, "USB process");
+SYSCTL_INT(_hw_usb2_proc, OID_AUTO, debug, CTLFLAG_RW, &usb2_proc_debug, 0,
+ "Debug level");
+#endif
+
+/*------------------------------------------------------------------------*
+ * usb2_process
+ *
+ * This function is the USB process dispatcher.
+ *------------------------------------------------------------------------*/
+static void
+usb2_process(void *arg)
+{
+ struct usb2_process *up = arg;
+ struct usb2_proc_msg *pm;
+ struct thread *td;
+
+ /* adjust priority */
+ td = curthread;
+ thread_lock(td);
+ sched_prio(td, up->up_prio);
+ thread_unlock(td);
+
+ mtx_lock(up->up_mtx);
+
+ up->up_curtd = td;
+
+ while (1) {
+
+ if (up->up_gone) {
+ break;
+ }
+ /*
+ * NOTE to reimplementors: dequeueing a command from the
+ * "used" queue and executing it must be atomic, with regard
+ * to the "up_mtx" mutex. That means any attempt to queue a
+ * command by another thread must be blocked until either:
+ *
+ * 1) the command sleeps
+ *
+ * 2) the command returns
+ *
+ * Here is a practical example that shows how this helps
+ * solving a problem:
+ *
+ * Assume that you want to set the baud rate on a USB serial
+ * device. During the programming of the device you don't
+ * want to receive nor transmit any data, because it will be
+ * garbage most likely anyway. The programming of our USB
+ * device takes 20 milliseconds and it needs to call
+ * functions that sleep.
+ *
+ * Non-working solution: Before we queue the programming
+ * command, we stop transmission and reception of data. Then
+ * we queue a programming command. At the end of the
+ * programming command we enable transmission and reception
+ * of data.
+ *
+ * Problem: If a second programming command is queued while the
+ * first one is sleeping, we end up enabling transmission
+ * and reception of data too early.
+ *
+ * Working solution: Before we queue the programming command,
+ * we stop transmission and reception of data. Then we queue
+ * a programming command. Then we queue a second command
+ * that only enables transmission and reception of data.
+ *
+ * Why it works: If a second programming command is queued
+ * while the first one is sleeping, then the queueing of a
+ * second command to enable the data transfers, will cause
+ * the previous one, which is still on the queue, to be
+ * removed from the queue, and re-inserted after the last
+ * baud rate programming command, which then gives the
+ * desired result.
+ */
+ pm = TAILQ_FIRST(&up->up_qhead);
+
+ if (pm) {
+ DPRINTF("Message pm=%p, cb=%p (enter)\n",
+ pm, pm->pm_callback);
+
+ (pm->pm_callback) (pm);
+
+ if (pm == TAILQ_FIRST(&up->up_qhead)) {
+ /* nothing changed */
+ TAILQ_REMOVE(&up->up_qhead, pm, pm_qentry);
+ pm->pm_qentry.tqe_prev = NULL;
+ }
+ DPRINTF("Message pm=%p (leave)\n", pm);
+
+ continue;
+ }
+ /* end if messages - check if anyone is waiting for sync */
+ if (up->up_dsleep) {
+ up->up_dsleep = 0;
+ usb2_cv_broadcast(&up->up_drain);
+ }
+ up->up_msleep = 1;
+ usb2_cv_wait(&up->up_cv, up->up_mtx);
+ }
+
+ up->up_ptr = NULL;
+ usb2_cv_signal(&up->up_cv);
+ mtx_unlock(up->up_mtx);
+
+ USB_THREAD_EXIT(0);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_setup
+ *
+ * This function will create a process using the given "prio" that can
+ * execute callbacks. The mutex pointed to by "p_mtx" will be applied
+ * before calling the callbacks and released after that the callback
+ * has returned. The structure pointed to by "up" is assumed to be
+ * zeroed before this function is called.
+ *
+ * Return values:
+ * 0: success
+ * Else: failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_proc_setup(struct usb2_process *up, struct mtx *p_mtx, uint8_t prio)
+{
+ up->up_mtx = p_mtx;
+ up->up_prio = prio;
+
+ TAILQ_INIT(&up->up_qhead);
+
+ usb2_cv_init(&up->up_cv, "WMSG");
+ usb2_cv_init(&up->up_drain, "DMSG");
+
+ if (USB_THREAD_CREATE(&usb2_process, up,
+ &up->up_ptr, "USBPROC")) {
+ DPRINTFN(0, "Unable to create USB process.");
+ up->up_ptr = NULL;
+ goto error;
+ }
+ return (0);
+
+error:
+ usb2_proc_unsetup(up);
+ return (1);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_unsetup
+ *
+ * NOTE: If the structure pointed to by "up" is all zero, this
+ * function does nothing.
+ *
+ * NOTE: Messages that are pending on the process queue will not be
+ * removed nor called.
+ *------------------------------------------------------------------------*/
+void
+usb2_proc_unsetup(struct usb2_process *up)
+{
+ if (!(up->up_mtx)) {
+ /* not initialised */
+ return;
+ }
+ usb2_proc_drain(up);
+
+ usb2_cv_destroy(&up->up_cv);
+ usb2_cv_destroy(&up->up_drain);
+
+ /* make sure that we do not enter here again */
+ up->up_mtx = NULL;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_msignal
+ *
+ * This function will queue one of the passed USB process messages on
+ * the USB process queue. The first message that is not already queued
+ * will get queued. If both messages are already queued the one queued
+ * last will be removed from the queue and queued in the end. The USB
+ * process mutex must be locked when calling this function. This
+ * function exploits the fact that a process can only do one callback
+ * at a time. The message that was queued is returned.
+ *------------------------------------------------------------------------*/
+void *
+usb2_proc_msignal(struct usb2_process *up, void *_pm0, void *_pm1)
+{
+ struct usb2_proc_msg *pm0 = _pm0;
+ struct usb2_proc_msg *pm1 = _pm1;
+ struct usb2_proc_msg *pm2;
+ uint32_t d;
+ uint8_t t;
+
+ mtx_assert(up->up_mtx, MA_OWNED);
+
+ t = 0;
+
+ if (pm0->pm_qentry.tqe_prev) {
+ t |= 1;
+ }
+ if (pm1->pm_qentry.tqe_prev) {
+ t |= 2;
+ }
+ if (t == 0) {
+ /*
+ * No entries are queued. Queue "pm0" and use the existing
+ * message number.
+ */
+ pm2 = pm0;
+ } else if (t == 1) {
+ /* Check if we need to increment the message number. */
+ if (pm0->pm_num == up->up_msg_num) {
+ up->up_msg_num++;
+ }
+ pm2 = pm1;
+ } else if (t == 2) {
+ /* Check if we need to increment the message number. */
+ if (pm1->pm_num == up->up_msg_num) {
+ up->up_msg_num++;
+ }
+ pm2 = pm0;
+ } else if (t == 3) {
+ /*
+ * Both entries are queued. Re-queue the entry closest to
+ * the end.
+ */
+ d = (pm1->pm_num - pm0->pm_num);
+
+ /* Check sign after subtraction */
+ if (d & 0x80000000) {
+ pm2 = pm0;
+ } else {
+ pm2 = pm1;
+ }
+
+ TAILQ_REMOVE(&up->up_qhead, pm2, pm_qentry);
+ } else {
+ pm2 = NULL; /* panic - should not happen */
+ }
+
+ DPRINTF(" t=%u, num=%u\n", t, up->up_msg_num);
+
+ /* Put message last on queue */
+
+ pm2->pm_num = up->up_msg_num;
+ TAILQ_INSERT_TAIL(&up->up_qhead, pm2, pm_qentry);
+
+ /* Check if we need to wakeup the USB process. */
+
+ if (up->up_msleep) {
+ up->up_msleep = 0; /* save "cv_signal()" calls */
+ usb2_cv_signal(&up->up_cv);
+ }
+ return (pm2);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_is_gone
+ *
+ * Return values:
+ * 0: USB process is running
+ * Else: USB process is tearing down
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_proc_is_gone(struct usb2_process *up)
+{
+ mtx_assert(up->up_mtx, MA_OWNED);
+
+ return (up->up_gone ? 1 : 0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_mwait
+ *
+ * This function will return when the USB process message pointed to
+ * by "pm" is no longer on a queue. This function must be called
+ * having "up->up_mtx" locked.
+ *------------------------------------------------------------------------*/
+void
+usb2_proc_mwait(struct usb2_process *up, void *_pm0, void *_pm1)
+{
+ struct usb2_proc_msg *pm0 = _pm0;
+ struct usb2_proc_msg *pm1 = _pm1;
+
+ mtx_assert(up->up_mtx, MA_OWNED);
+
+ if (up->up_curtd == curthread) {
+ /* Just remove the messages from the queue. */
+ if (pm0->pm_qentry.tqe_prev) {
+ TAILQ_REMOVE(&up->up_qhead, pm0, pm_qentry);
+ pm0->pm_qentry.tqe_prev = NULL;
+ }
+ if (pm1->pm_qentry.tqe_prev) {
+ TAILQ_REMOVE(&up->up_qhead, pm1, pm_qentry);
+ pm1->pm_qentry.tqe_prev = NULL;
+ }
+ } else
+ while (pm0->pm_qentry.tqe_prev ||
+ pm1->pm_qentry.tqe_prev) {
+ /* check if config thread is gone */
+ if (up->up_gone)
+ break;
+ up->up_dsleep = 1;
+ usb2_cv_wait(&up->up_drain, up->up_mtx);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_drain
+ *
+ * This function will tear down an USB process, waiting for the
+ * currently executing command to return.
+ *
+ * NOTE: If the structure pointed to by "up" is all zero,
+ * this function does nothing.
+ *------------------------------------------------------------------------*/
+void
+usb2_proc_drain(struct usb2_process *up)
+{
+ if (!(up->up_mtx)) {
+ /* not initialised */
+ return;
+ }
+ if (up->up_mtx != &Giant) {
+ mtx_assert(up->up_mtx, MA_NOTOWNED);
+ }
+ mtx_lock(up->up_mtx);
+
+ /* Set the gone flag */
+
+ up->up_gone = 1;
+
+ while (up->up_ptr) {
+
+ /* Check if we need to wakeup the USB process */
+
+ if (up->up_msleep || up->up_csleep) {
+ up->up_msleep = 0;
+ up->up_csleep = 0;
+ usb2_cv_signal(&up->up_cv);
+ }
+ /* Check if we are still cold booted */
+
+ if (cold) {
+ USB_THREAD_SUSPEND(up->up_ptr);
+ printf("WARNING: A USB process has been left suspended!\n");
+ break;
+ }
+ usb2_cv_wait(&up->up_cv, up->up_mtx);
+ }
+ /* Check if someone is waiting - should not happen */
+
+ if (up->up_dsleep) {
+ up->up_dsleep = 0;
+ usb2_cv_broadcast(&up->up_drain);
+ DPRINTF("WARNING: Someone is waiting "
+ "for USB process drain!\n");
+ }
+ mtx_unlock(up->up_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_cwait
+ *
+ * This function will suspend the current process until
+ * "usb2_proc_signal()" or "usb2_proc_drain()" is called. The
+ * "timeout" parameter defines the maximum wait time in system
+ * ticks. If "timeout" is zero that means no timeout.
+ *
+ * NOTE: This function can only be called from within an USB process.
+ *
+ * Return values:
+ * USB_PROC_WAIT_TIMEOUT: Timeout
+ * USB_PROC_WAIT_NORMAL: Success
+ * Else: USB process is tearing down
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_proc_cwait(struct usb2_process *up, int timeout)
+{
+ int error;
+
+ mtx_assert(up->up_mtx, MA_OWNED);
+
+ if (up->up_gone) {
+ return (USB_PROC_WAIT_DRAIN);
+ }
+ up->up_csleep = 1;
+
+ if (timeout == 0) {
+ usb2_cv_wait(&up->up_cv, up->up_mtx);
+ error = 0;
+ } else {
+ error = usb2_cv_timedwait(&up->up_cv, up->up_mtx, timeout);
+ }
+
+ up->up_csleep = 0;
+
+ if (up->up_gone) {
+ return (USB_PROC_WAIT_DRAIN);
+ }
+ if (error == EWOULDBLOCK) {
+ return (USB_PROC_WAIT_TIMEOUT);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_csignal
+ *
+ * This function will wakeup the given USB process.
+ *------------------------------------------------------------------------*/
+void
+usb2_proc_csignal(struct usb2_process *up)
+{
+ mtx_assert(up->up_mtx, MA_OWNED);
+
+ if (up->up_csleep) {
+ up->up_csleep = 0;
+ usb2_cv_signal(&up->up_cv);
+ }
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_process.h b/sys/dev/usb2/core/usb2_process.h
new file mode 100644
index 000000000000..7019735f3c14
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_process.h
@@ -0,0 +1,89 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_PROCESS_H_
+#define _USB2_PROCESS_H_
+
+#include <sys/priority.h>
+
+/* defines */
+#define USB_PRI_HIGH PI_NET
+#define USB_PRI_MED PI_DISK
+
+#define USB_PROC_WAIT_TIMEOUT 2
+#define USB_PROC_WAIT_DRAIN 1
+#define USB_PROC_WAIT_NORMAL 0
+
+/* structure prototypes */
+
+struct usb2_proc_msg;
+
+/* typedefs */
+
+typedef void (usb2_proc_callback_t)(struct usb2_proc_msg *hdr);
+
+/*
+ * The following structure defines the USB process message header.
+ */
+struct usb2_proc_msg {
+ TAILQ_ENTRY(usb2_proc_msg) pm_qentry;
+ usb2_proc_callback_t *pm_callback;
+ uint32_t pm_num;
+};
+
+/*
+ * The following structure defines the USB process.
+ */
+struct usb2_process {
+ TAILQ_HEAD(, usb2_proc_msg) up_qhead;
+ struct cv up_cv;
+ struct cv up_drain;
+
+ struct proc *up_ptr;
+ struct thread *up_curtd;
+ struct mtx *up_mtx;
+
+ uint32_t up_msg_num;
+
+ uint8_t up_prio;
+ uint8_t up_gone;
+ uint8_t up_msleep;
+ uint8_t up_csleep;
+ uint8_t up_dsleep;
+};
+
+/* prototypes */
+
+uint8_t usb2_proc_cwait(struct usb2_process *up, int timeout);
+uint8_t usb2_proc_is_gone(struct usb2_process *up);
+uint8_t usb2_proc_setup(struct usb2_process *up, struct mtx *p_mtx, uint8_t prio);
+void usb2_proc_csignal(struct usb2_process *up);
+void usb2_proc_drain(struct usb2_process *up);
+void usb2_proc_mwait(struct usb2_process *up, void *pm0, void *pm1);
+void usb2_proc_unsetup(struct usb2_process *up);
+void *usb2_proc_msignal(struct usb2_process *up, void *pm0, void *pm1);
+
+#endif /* _USB2_PROCESS_H_ */
diff --git a/sys/dev/usb2/core/usb2_request.c b/sys/dev/usb2/core/usb2_request.c
new file mode 100644
index 000000000000..72d9f175e555
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_request.c
@@ -0,0 +1,1373 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
+ * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_ioctl.h>
+#include <dev/usb2/include/usb2_hid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_dynamic.h>
+
+#include <sys/ctype.h>
+
+#if USB_DEBUG
+static int usb2_pr_poll_delay = USB_PORT_RESET_DELAY;
+static int usb2_pr_recovery_delay = USB_PORT_RESET_RECOVERY;
+static int usb2_ss_delay = 0;
+
+SYSCTL_INT(_hw_usb2, OID_AUTO, pr_poll_delay, CTLFLAG_RW,
+ &usb2_pr_poll_delay, 0, "USB port reset poll delay in ms");
+SYSCTL_INT(_hw_usb2, OID_AUTO, pr_recovery_delay, CTLFLAG_RW,
+ &usb2_pr_recovery_delay, 0, "USB port reset recovery delay in ms");
+SYSCTL_INT(_hw_usb2, OID_AUTO, ss_delay, CTLFLAG_RW,
+ &usb2_ss_delay, 0, "USB status stage delay in ms");
+#endif
+
+/*------------------------------------------------------------------------*
+ * usb2_do_request_callback
+ *
+ * This function is the USB callback for generic USB Host control
+ * transfers.
+ *------------------------------------------------------------------------*/
+void
+usb2_do_request_callback(struct usb2_xfer *xfer)
+{
+ ; /* workaround for a bug in "indent" */
+
+ DPRINTF("st=%u\n", USB_GET_STATE(xfer));
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ usb2_start_hardware(xfer);
+ break;
+ default:
+ usb2_cv_signal(xfer->udev->default_cv);
+ break;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_do_clear_stall_callback
+ *
+ * This function is the USB callback for generic clear stall requests.
+ *------------------------------------------------------------------------*/
+void
+usb2_do_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_device_request req;
+ struct usb2_pipe *pipe;
+ struct usb2_pipe *pipe_end;
+ struct usb2_pipe *pipe_first;
+ uint8_t to = USB_EP_MAX;
+
+ mtx_lock(xfer->usb2_mtx);
+
+ /* round robin pipe clear stall */
+
+ pipe = xfer->udev->pipe_curr;
+ pipe_end = xfer->udev->pipes + USB_EP_MAX;
+ pipe_first = xfer->udev->pipes;
+ if (pipe == NULL) {
+ pipe = pipe_first;
+ }
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (pipe->edesc &&
+ pipe->is_stalled) {
+ pipe->toggle_next = 0;
+ pipe->is_stalled = 0;
+ /* start up the current or next transfer, if any */
+ usb2_command_wrapper(&pipe->pipe_q,
+ pipe->pipe_q.curr);
+ }
+ pipe++;
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (pipe == pipe_end) {
+ pipe = pipe_first;
+ }
+ if (pipe->edesc &&
+ pipe->is_stalled) {
+
+ /* setup a clear-stall packet */
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ req.wIndex[0] = pipe->edesc->bEndpointAddress;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ /* copy in the transfer */
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ /* set length */
+ xfer->frlengths[0] = sizeof(req);
+ xfer->nframes = 1;
+ mtx_unlock(xfer->usb2_mtx);
+
+ usb2_start_hardware(xfer);
+
+ mtx_lock(xfer->usb2_mtx);
+ break;
+ }
+ pipe++;
+ if (--to)
+ goto tr_setup;
+ break;
+
+ default:
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ goto tr_setup;
+ }
+
+ /* store current pipe */
+ xfer->udev->pipe_curr = pipe;
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_do_request_flags and usb2_do_request
+ *
+ * Description of arguments passed to these functions:
+ *
+ * "udev" - this is the "usb2_device" structure pointer on which the
+ * request should be performed. It is possible to call this function
+ * in both Host Side mode and Device Side mode.
+ *
+ * "mtx" - if this argument is non-NULL the mutex pointed to by it
+ * will get dropped and picked up during the execution of this
+ * function, hence this function sometimes needs to sleep. If this
+ * argument is NULL it has no effect.
+ *
+ * "req" - this argument must always be non-NULL and points to an
+ * 8-byte structure holding the USB request to be done. The USB
+ * request structure has a bit telling the direction of the USB
+ * request, if it is a read or a write.
+ *
+ * "data" - if the "wLength" part of the structure pointed to by "req"
+ * is non-zero this argument must point to a valid kernel buffer which
+ * can hold at least "wLength" bytes. If "wLength" is zero "data" can
+ * be NULL.
+ *
+ * "flags" - here is a list of valid flags:
+ *
+ * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than
+ * specified
+ *
+ * o USB_USE_POLLING: forces the transfer to complete from the
+ * current context by polling the interrupt handler. This flag can be
+ * used to perform USB transfers after that the kernel has crashed.
+ *
+ * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed
+ * at a later point in time. This is tunable by the "hw.usb.ss_delay"
+ * sysctl. This flag is mostly useful for debugging.
+ *
+ * o USB_USER_DATA_PTR: treat the "data" pointer like a userland
+ * pointer.
+ *
+ * "actlen" - if non-NULL the actual transfer length will be stored in
+ * the 16-bit unsigned integer pointed to by "actlen". This
+ * information is mostly useful when the "USB_SHORT_XFER_OK" flag is
+ * used.
+ *
+ * "timeout" - gives the timeout for the control transfer in
+ * milliseconds. A "timeout" value less than 50 milliseconds is
+ * treated like a 50 millisecond timeout. A "timeout" value greater
+ * than 30 seconds is treated like a 30 second timeout. This USB stack
+ * does not allow control requests without a timeout.
+ *
+ * NOTE: This function is thread safe. All calls to
+ * "usb2_do_request_flags" will be serialised by the use of an
+ * internal "sx_lock".
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_do_request_flags(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_device_request *req, void *data, uint32_t flags,
+ uint16_t *actlen, uint32_t timeout)
+{
+ struct usb2_xfer *xfer;
+ const void *desc;
+ int err = 0;
+ uint32_t start_ticks;
+ uint32_t delta_ticks;
+ uint32_t max_ticks;
+ uint16_t length;
+ uint16_t temp;
+
+ if (timeout < 50) {
+ /* timeout is too small */
+ timeout = 50;
+ }
+ if (timeout > 30000) {
+ /* timeout is too big */
+ timeout = 30000;
+ }
+ length = UGETW(req->wLength);
+
+ DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x "
+ "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n",
+ udev, req->bmRequestType, req->bRequest,
+ req->wValue[1], req->wValue[0],
+ req->wIndex[1], req->wIndex[0],
+ req->wLength[1], req->wLength[0]);
+
+ /*
+ * Set "actlen" to a known value in case the caller does not
+ * check the return value:
+ */
+ if (actlen) {
+ *actlen = 0;
+ }
+ if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
+ DPRINTF("USB device mode\n");
+ (usb2_temp_get_desc_p) (udev, req, &desc, &temp);
+ if (length > temp) {
+ if (!(flags & USB_SHORT_XFER_OK)) {
+ return (USB_ERR_SHORT_XFER);
+ }
+ length = temp;
+ }
+ if (actlen) {
+ *actlen = length;
+ }
+ if (length > 0) {
+ if (flags & USB_USER_DATA_PTR) {
+ if (copyout(desc, data, length)) {
+ return (USB_ERR_INVAL);
+ }
+ } else {
+ bcopy(desc, data, length);
+ }
+ }
+ return (0); /* success */
+ }
+ if (mtx) {
+ mtx_unlock(mtx);
+ if (mtx != &Giant) {
+ mtx_assert(mtx, MA_NOTOWNED);
+ }
+ }
+ /*
+ * Grab the default sx-lock so that serialisation
+ * is achieved when multiple threads are involved:
+ */
+
+ sx_xlock(udev->default_sx);
+
+ /*
+ * Setup a new USB transfer or use the existing one, if any:
+ */
+ usb2_default_transfer_setup(udev);
+
+ xfer = udev->default_xfer[0];
+ if (xfer == NULL) {
+ /* most likely out of memory */
+ err = USB_ERR_NOMEM;
+ goto done;
+ }
+ mtx_lock(xfer->priv_mtx);
+
+ if (flags & USB_DELAY_STATUS_STAGE) {
+ xfer->flags.manual_status = 1;
+ } else {
+ xfer->flags.manual_status = 0;
+ }
+
+ xfer->timeout = timeout;
+
+ start_ticks = ticks;
+
+ max_ticks = USB_MS_TO_TICKS(timeout);
+
+ usb2_copy_in(xfer->frbuffers, 0, req, sizeof(*req));
+
+ xfer->frlengths[0] = sizeof(*req);
+ xfer->nframes = 2;
+
+ while (1) {
+ temp = length;
+ if (temp > xfer->max_data_length) {
+ temp = xfer->max_data_length;
+ }
+ xfer->frlengths[1] = temp;
+
+ if (temp > 0) {
+ if (!(req->bmRequestType & UT_READ)) {
+ if (flags & USB_USER_DATA_PTR) {
+ mtx_unlock(xfer->priv_mtx);
+ err = usb2_copy_in_user(xfer->frbuffers + 1,
+ 0, data, temp);
+ mtx_lock(xfer->priv_mtx);
+ if (err) {
+ err = USB_ERR_INVAL;
+ break;
+ }
+ } else {
+ usb2_copy_in(xfer->frbuffers + 1, 0, data, temp);
+ }
+ }
+ xfer->nframes = 2;
+ } else {
+ if (xfer->frlengths[0] == 0) {
+ if (xfer->flags.manual_status) {
+#if USB_DEBUG
+ int temp;
+
+ temp = usb2_ss_delay;
+ if (temp > 5000) {
+ temp = 5000;
+ }
+ if (temp > 0) {
+ usb2_pause_mtx(
+ xfer->priv_mtx, temp);
+ }
+#endif
+ xfer->flags.manual_status = 0;
+ } else {
+ break;
+ }
+ }
+ xfer->nframes = 1;
+ }
+
+ usb2_transfer_start(xfer);
+
+ while (usb2_transfer_pending(xfer)) {
+ if ((flags & USB_USE_POLLING) || cold) {
+ usb2_do_poll(udev->default_xfer, USB_DEFAULT_XFER_MAX);
+ } else {
+ usb2_cv_wait(xfer->udev->default_cv, xfer->priv_mtx);
+ }
+ }
+
+ err = xfer->error;
+
+ if (err) {
+ break;
+ }
+ /* subtract length of SETUP packet, if any */
+
+ if (xfer->aframes > 0) {
+ xfer->actlen -= xfer->frlengths[0];
+ } else {
+ xfer->actlen = 0;
+ }
+
+ /* check for short packet */
+
+ if (temp > xfer->actlen) {
+ temp = xfer->actlen;
+ if (!(flags & USB_SHORT_XFER_OK)) {
+ err = USB_ERR_SHORT_XFER;
+ }
+ length = temp;
+ }
+ if (temp > 0) {
+ if (req->bmRequestType & UT_READ) {
+ if (flags & USB_USER_DATA_PTR) {
+ mtx_unlock(xfer->priv_mtx);
+ err = usb2_copy_out_user(xfer->frbuffers + 1,
+ 0, data, temp);
+ mtx_lock(xfer->priv_mtx);
+ if (err) {
+ err = USB_ERR_INVAL;
+ break;
+ }
+ } else {
+ usb2_copy_out(xfer->frbuffers + 1,
+ 0, data, temp);
+ }
+ }
+ }
+ /*
+ * Clear "frlengths[0]" so that we don't send the setup
+ * packet again:
+ */
+ xfer->frlengths[0] = 0;
+
+ /* update length and data pointer */
+ length -= temp;
+ data = USB_ADD_BYTES(data, temp);
+
+ if (actlen) {
+ (*actlen) += temp;
+ }
+ /* check for timeout */
+
+ delta_ticks = ticks - start_ticks;
+ if (delta_ticks > max_ticks) {
+ if (!err) {
+ err = USB_ERR_TIMEOUT;
+ }
+ }
+ if (err) {
+ break;
+ }
+ }
+
+ if (err) {
+ /*
+ * Make sure that the control endpoint is no longer
+ * blocked in case of a non-transfer related error:
+ */
+ usb2_transfer_stop(xfer);
+ }
+ mtx_unlock(xfer->priv_mtx);
+
+done:
+ sx_xunlock(udev->default_sx);
+
+ if (mtx) {
+ mtx_lock(mtx);
+ }
+ return ((usb2_error_t)err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_reset_port
+ *
+ * This function will instruct an USB HUB to perform a reset sequence
+ * on the specified port number.
+ *
+ * Returns:
+ * 0: Success. The USB device should now be at address zero.
+ * Else: Failure. No USB device is present and the USB port should be
+ * disabled.
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_reset_port(struct usb2_device *udev, struct mtx *mtx, uint8_t port)
+{
+ struct usb2_port_status ps;
+ usb2_error_t err;
+ uint16_t n;
+
+#if USB_DEBUG
+ uint16_t pr_poll_delay;
+ uint16_t pr_recovery_delay;
+
+#endif
+ err = usb2_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET);
+ if (err) {
+ goto done;
+ }
+#if USB_DEBUG
+ /* range check input parameters */
+ pr_poll_delay = usb2_pr_poll_delay;
+ if (pr_poll_delay < 1) {
+ pr_poll_delay = 1;
+ } else if (pr_poll_delay > 1000) {
+ pr_poll_delay = 1000;
+ }
+ pr_recovery_delay = usb2_pr_recovery_delay;
+ if (pr_recovery_delay > 1000) {
+ pr_recovery_delay = 1000;
+ }
+#endif
+ n = 0;
+ while (1) {
+#if USB_DEBUG
+ /* wait for the device to recover from reset */
+ usb2_pause_mtx(mtx, pr_poll_delay);
+ n += pr_poll_delay;
+#else
+ /* wait for the device to recover from reset */
+ usb2_pause_mtx(mtx, USB_PORT_RESET_DELAY);
+ n += USB_PORT_RESET_DELAY;
+#endif
+ err = usb2_req_get_port_status(udev, mtx, &ps, port);
+ if (err) {
+ goto done;
+ }
+ /* if the device disappeared, just give up */
+ if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
+ goto done;
+ }
+ /* check if reset is complete */
+ if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) {
+ break;
+ }
+ /* check for timeout */
+ if (n > 1000) {
+ n = 0;
+ break;
+ }
+ }
+
+ /* clear port reset first */
+ err = usb2_req_clear_port_feature(
+ udev, mtx, port, UHF_C_PORT_RESET);
+ if (err) {
+ goto done;
+ }
+ /* check for timeout */
+ if (n == 0) {
+ err = USB_ERR_TIMEOUT;
+ goto done;
+ }
+#if USB_DEBUG
+ /* wait for the device to recover from reset */
+ usb2_pause_mtx(mtx, pr_recovery_delay);
+#else
+ /* wait for the device to recover from reset */
+ usb2_pause_mtx(mtx, USB_PORT_RESET_RECOVERY);
+#endif
+
+done:
+ DPRINTFN(2, "port %d reset returning error=%s\n",
+ port, usb2_errstr(err));
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_desc
+ *
+ * This function can be used to retrieve USB descriptors. It contains
+ * some additional logic like zeroing of missing descriptor bytes and
+ * retrying an USB descriptor in case of failure. The "min_len"
+ * argument specifies the minimum descriptor length. The "max_len"
+ * argument specifies the maximum descriptor length. If the real
+ * descriptor length is less than the minimum length the missing
+ * byte(s) will be zeroed. The length field, first byte, of the USB
+ * descriptor will get overwritten in case it indicates a length that
+ * is too big. Also the type field, second byte, of the USB descriptor
+ * will get forced to the correct type.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_desc(struct usb2_device *udev, struct mtx *mtx, void *desc,
+ uint16_t min_len, uint16_t max_len,
+ uint16_t id, uint8_t type, uint8_t index,
+ uint8_t retries)
+{
+ struct usb2_device_request req;
+ uint8_t *buf;
+ usb2_error_t err;
+
+ DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n",
+ id, type, index, max_len);
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, type, index);
+ USETW(req.wIndex, id);
+
+ while (1) {
+
+ if ((min_len < 2) || (max_len < 2)) {
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+ USETW(req.wLength, min_len);
+
+ err = usb2_do_request(udev, mtx, &req, desc);
+
+ if (err) {
+ if (!retries) {
+ goto done;
+ }
+ retries--;
+
+ usb2_pause_mtx(mtx, 200);
+
+ continue;
+ }
+ buf = desc;
+
+ if (min_len == max_len) {
+
+ /* enforce correct type and length */
+
+ if (buf[0] > min_len) {
+ buf[0] = min_len;
+ }
+ buf[1] = type;
+
+ goto done;
+ }
+ /* range check */
+
+ if (max_len > buf[0]) {
+ max_len = buf[0];
+ }
+ /* zero minimum data */
+
+ while (min_len > max_len) {
+ min_len--;
+ buf[min_len] = 0;
+ }
+
+ /* set new minimum length */
+
+ min_len = max_len;
+ }
+done:
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_string_any
+ *
+ * This function will return the string given by "string_index"
+ * using the first language ID. The maximum length "len" includes
+ * the terminating zero. The "len" argument should be twice as
+ * big pluss 2 bytes, compared with the actual maximum string length !
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_string_any(struct usb2_device *udev, struct mtx *mtx, char *buf,
+ uint16_t len, uint8_t string_index)
+{
+ char *s;
+ uint8_t *temp;
+ uint16_t i;
+ uint16_t n;
+ uint16_t c;
+ uint8_t swap;
+ usb2_error_t err;
+
+ if (len == 0) {
+ /* should not happen */
+ return (USB_ERR_NORMAL_COMPLETION);
+ }
+ buf[0] = 0;
+
+ if (string_index == 0) {
+ /* this is the language table */
+ return (USB_ERR_INVAL);
+ }
+ if (udev->flags.no_strings) {
+ return (USB_ERR_STALLED);
+ }
+ err = usb2_req_get_string_desc
+ (udev, mtx, buf, len, udev->langid, string_index);
+ if (err) {
+ return (err);
+ }
+ temp = (uint8_t *)buf;
+
+ if (temp[0] < 2) {
+ /* string length is too short */
+ return (USB_ERR_INVAL);
+ }
+ /* reserve one byte for terminating zero */
+ len--;
+
+ /* find maximum length */
+ s = buf;
+ n = (temp[0] / 2) - 1;
+ if (n > len) {
+ n = len;
+ }
+ /* skip descriptor header */
+ temp += 2;
+
+ /* reset swap state */
+ swap = 3;
+
+ /* convert and filter */
+ for (i = 0; (i != n); i++) {
+ c = UGETW(temp + (2 * i));
+
+ /* convert from Unicode, handle buggy strings */
+ if (((c & 0xff00) == 0) && (swap & 1)) {
+ /* Little Endian, default */
+ *s = c;
+ swap = 1;
+ } else if (((c & 0x00ff) == 0) && (swap & 2)) {
+ /* Big Endian */
+ *s = c >> 8;
+ swap = 2;
+ } else {
+ *s = '.';
+ }
+
+ /*
+ * Filter by default - we don't allow greater and less than
+ * signs because they might confuse the dmesg printouts!
+ */
+ if ((*s == '<') || (*s == '>') || (!isprint(*s))) {
+ *s = '.';
+ }
+ s++;
+ }
+ *s = 0;
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_string_desc
+ *
+ * If you don't know the language ID, consider using
+ * "usb2_req_get_string_any()".
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_string_desc(struct usb2_device *udev, struct mtx *mtx, void *sdesc,
+ uint16_t max_len, uint16_t lang_id,
+ uint8_t string_index)
+{
+ return (usb2_req_get_desc(udev, mtx, sdesc, 2, max_len, lang_id,
+ UDESC_STRING, string_index, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_config_desc
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_config_desc(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_config_descriptor *d, uint8_t conf_index)
+{
+ usb2_error_t err;
+
+ DPRINTFN(4, "confidx=%d\n", conf_index);
+
+ err = usb2_req_get_desc(udev, mtx, d, sizeof(*d),
+ sizeof(*d), 0, UDESC_CONFIG, conf_index, 0);
+ if (err) {
+ goto done;
+ }
+ /* Extra sanity checking */
+ if (UGETW(d->wTotalLength) < sizeof(*d)) {
+ err = USB_ERR_INVAL;
+ }
+done:
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_config_desc_full
+ *
+ * This function gets the complete USB configuration descriptor and
+ * ensures that "wTotalLength" is correct.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_config_desc_full(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_config_descriptor **ppcd, struct malloc_type *mtype,
+ uint8_t index)
+{
+ struct usb2_config_descriptor cd;
+ struct usb2_config_descriptor *cdesc;
+ uint16_t len;
+ usb2_error_t err;
+
+ DPRINTFN(4, "index=%d\n", index);
+
+ *ppcd = NULL;
+
+ err = usb2_req_get_config_desc(udev, mtx, &cd, index);
+ if (err) {
+ return (err);
+ }
+ /* get full descriptor */
+ len = UGETW(cd.wTotalLength);
+ if (len < sizeof(*cdesc)) {
+ /* corrupt descriptor */
+ return (USB_ERR_INVAL);
+ }
+ cdesc = malloc(len, mtype, M_WAITOK);
+ if (cdesc == NULL) {
+ return (USB_ERR_NOMEM);
+ }
+ err = usb2_req_get_desc(udev, mtx, cdesc, len, len, 0,
+ UDESC_CONFIG, index, 3);
+ if (err) {
+ free(cdesc, mtype);
+ return (err);
+ }
+ /* make sure that the device is not fooling us: */
+ USETW(cdesc->wTotalLength, len);
+
+ *ppcd = cdesc;
+
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_device_desc
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_device_desc(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_device_descriptor *d)
+{
+ DPRINTFN(4, "\n");
+ return (usb2_req_get_desc(udev, mtx, d, sizeof(*d),
+ sizeof(*d), 0, UDESC_DEVICE, 0, 3));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_alt_interface_no
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_alt_interface_no(struct usb2_device *udev, struct mtx *mtx,
+ uint8_t *alt_iface_no, uint8_t iface_index)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ req.bmRequestType = UT_READ_INTERFACE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 1);
+ return (usb2_do_request(udev, mtx, &req, alt_iface_no));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_alt_interface_no
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_alt_interface_no(struct usb2_device *udev, struct mtx *mtx,
+ uint8_t iface_index, uint8_t alt_no)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ req.bmRequestType = UT_WRITE_INTERFACE;
+ req.bRequest = UR_SET_INTERFACE;
+ req.wValue[0] = alt_no;
+ req.wValue[1] = 0;
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_device_status
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_device_status(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_status *st)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(*st));
+ return (usb2_do_request(udev, mtx, &req, st));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_hub_descriptor
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_hub_descriptor(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_hub_descriptor *hd, uint8_t nports)
+{
+ struct usb2_device_request req;
+ uint16_t len = (nports + 7 + (8 * 8)) / 8;
+
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_HUB, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+ return (usb2_do_request(udev, mtx, &req, hd));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_hub_status
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_hub_status(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_hub_status *st)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_CLASS_DEVICE;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(struct usb2_hub_status));
+ return (usb2_do_request(udev, mtx, &req, st));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_address
+ *
+ * This function is used to set the address for an USB device. After
+ * port reset the USB device will respond at address zero.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_address(struct usb2_device *udev, struct mtx *mtx, uint16_t addr)
+{
+ struct usb2_device_request req;
+
+ DPRINTFN(6, "setting device address=%d\n", addr);
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ /* Setting the address should not take more than 1 second ! */
+ return (usb2_do_request_flags(udev, mtx, &req, NULL,
+ USB_DELAY_STATUS_STAGE, NULL, 1000));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_port_status
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_port_status(struct usb2_device *udev, struct mtx *mtx,
+ struct usb2_port_status *ps, uint8_t port)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_CLASS_OTHER;
+ req.bRequest = UR_GET_STATUS;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = port;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, sizeof *ps);
+ return (usb2_do_request(udev, mtx, &req, ps));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_clear_hub_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_clear_hub_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_hub_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_hub_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_clear_port_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_clear_port_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint8_t port, uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, sel);
+ req.wIndex[0] = port;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_port_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_port_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint8_t port, uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, sel);
+ req.wIndex[0] = port;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_protocol
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_protocol(struct usb2_device *udev, struct mtx *mtx,
+ uint8_t iface_index, uint16_t report)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n",
+ iface, report, iface->idesc->bInterfaceNumber);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_PROTOCOL;
+ USETW(req.wValue, report);
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_report
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len,
+ uint8_t iface_index, uint8_t type, uint8_t id)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ DPRINTFN(5, "len=%d\n", len);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, type, id);
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, len);
+ return (usb2_do_request(udev, mtx, &req, data));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_report
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_report(struct usb2_device *udev, struct mtx *mtx, void *data,
+ uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) {
+ return (USB_ERR_INVAL);
+ }
+ DPRINTFN(5, "len=%d\n", len);
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_REPORT;
+ USETW2(req.wValue, type, id);
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, len);
+ return (usb2_do_request(udev, mtx, &req, data));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_idle
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_idle(struct usb2_device *udev, struct mtx *mtx,
+ uint8_t iface_index, uint8_t duration, uint8_t id)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ DPRINTFN(5, "%d %d\n", duration, id);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_IDLE;
+ USETW2(req.wValue, duration, id);
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_report_descriptor
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_report_descriptor(struct usb2_device *udev, struct mtx *mtx,
+ void *d, uint16_t size, uint8_t iface_index)
+{
+ struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
+ struct usb2_device_request req;
+
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ return (USB_ERR_INVAL);
+ }
+ req.bmRequestType = UT_READ_INTERFACE;
+ req.bRequest = UR_GET_DESCRIPTOR;
+ USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
+ req.wIndex[0] = iface->idesc->bInterfaceNumber;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, size);
+ return (usb2_do_request(udev, mtx, &req, d));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_set_config
+ *
+ * This function is used to select the current configuration number in
+ * both USB device side mode and USB host side mode. When setting the
+ * configuration the function of the interfaces can change.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_config(struct usb2_device *udev, struct mtx *mtx, uint8_t conf)
+{
+ struct usb2_device_request req;
+
+ DPRINTF("setting config %d\n", conf);
+
+ /* do "set configuration" request */
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_CONFIG;
+ req.wValue[0] = conf;
+ req.wValue[1] = 0;
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ return (usb2_do_request(udev, mtx, &req, 0));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_get_config
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_get_config(struct usb2_device *udev, struct mtx *mtx, uint8_t *pconf)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ return (usb2_do_request(udev, mtx, &req, pconf));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_re_enumerate
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx)
+{
+ struct usb2_device_descriptor ddesc;
+ struct usb2_device *parent_hub;
+ usb2_error_t err;
+ uint8_t old_addr;
+
+ old_addr = udev->address;
+ parent_hub = udev->parent_hub;
+ if (parent_hub == NULL) {
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+ err = usb2_req_reset_port(parent_hub, mtx, udev->port_no);
+ if (err) {
+ DPRINTFN(0, "addr=%d, port reset failed\n", old_addr);
+ goto done;
+ }
+ /*
+ * After that the port has been reset our device should be at
+ * address zero:
+ */
+ udev->address = USB_START_ADDR;
+
+ /*
+ * Restore device address:
+ */
+ err = usb2_req_set_address(udev, mtx, old_addr);
+ if (err) {
+ /* XXX ignore any errors! */
+ DPRINTFN(0, "addr=%d, set address failed\n",
+ old_addr);
+ err = 0;
+ }
+ /* restore device address */
+ udev->address = old_addr;
+
+ /* allow device time to set new address */
+ usb2_pause_mtx(mtx, USB_SET_ADDRESS_SETTLE);
+
+ /* get the device descriptor */
+ err = usb2_req_get_device_desc(udev, mtx, &ddesc);
+ if (err) {
+ DPRINTFN(0, "addr=%d, getting device "
+ "descriptor failed!\n", old_addr);
+ goto done;
+ }
+done:
+ /* restore address */
+ udev->address = old_addr;
+
+ if (err == 0) {
+ /* restore configuration */
+ err = usb2_req_set_config(udev, mtx, udev->curr_config_no);
+ /* wait a little bit, just in case */
+ usb2_pause_mtx(mtx, 10);
+ }
+ return (err);
+}
diff --git a/sys/dev/usb2/core/usb2_request.h b/sys/dev/usb2/core/usb2_request.h
new file mode 100644
index 000000000000..f2b642291135
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_request.h
@@ -0,0 +1,61 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_REQUEST_H_
+#define _USB2_REQUEST_H_
+
+usb2_error_t usb2_do_request_flags(struct usb2_device *udev, struct mtx *mtx, struct usb2_device_request *req, void *data, uint32_t flags, uint16_t *actlen, uint32_t timeout);
+usb2_error_t usb2_req_clear_hub_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel);
+usb2_error_t usb2_req_clear_port_feature(struct usb2_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel);
+usb2_error_t usb2_req_get_alt_interface_no(struct usb2_device *udev, struct mtx *mtx, uint8_t *alt_iface_no, uint8_t iface_index);
+usb2_error_t usb2_req_get_config(struct usb2_device *udev, struct mtx *mtx, uint8_t *pconf);
+usb2_error_t usb2_req_get_config_desc(struct usb2_device *udev, struct mtx *mtx, struct usb2_config_descriptor *d, uint8_t conf_index);
+usb2_error_t usb2_req_get_config_desc_full(struct usb2_device *udev, struct mtx *mtx, struct usb2_config_descriptor **ppcd, struct malloc_type *mtype, uint8_t conf_index);
+usb2_error_t usb2_req_get_desc(struct usb2_device *udev, struct mtx *mtx, void *desc, uint16_t min_len, uint16_t max_len, uint16_t id, uint8_t type, uint8_t index, uint8_t retries);
+usb2_error_t usb2_req_get_device_desc(struct usb2_device *udev, struct mtx *mtx, struct usb2_device_descriptor *d);
+usb2_error_t usb2_req_get_device_status(struct usb2_device *udev, struct mtx *mtx, struct usb2_status *st);
+usb2_error_t usb2_req_get_hub_descriptor(struct usb2_device *udev, struct mtx *mtx, struct usb2_hub_descriptor *hd, uint8_t nports);
+usb2_error_t usb2_req_get_hub_status(struct usb2_device *udev, struct mtx *mtx, struct usb2_hub_status *st);
+usb2_error_t usb2_req_get_port_status(struct usb2_device *udev, struct mtx *mtx, struct usb2_port_status *ps, uint8_t port);
+usb2_error_t usb2_req_get_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id);
+usb2_error_t usb2_req_get_report_descriptor(struct usb2_device *udev, struct mtx *mtx, void *d, uint16_t size, uint8_t iface_index);
+usb2_error_t usb2_req_get_string_any(struct usb2_device *udev, struct mtx *mtx, char *buf, uint16_t len, uint8_t string_index);
+usb2_error_t usb2_req_get_string_desc(struct usb2_device *udev, struct mtx *mtx, void *sdesc, uint16_t max_len, uint16_t lang_id, uint8_t string_index);
+usb2_error_t usb2_req_reset_port(struct usb2_device *udev, struct mtx *mtx, uint8_t port);
+usb2_error_t usb2_req_set_address(struct usb2_device *udev, struct mtx *mtx, uint16_t addr);
+usb2_error_t usb2_req_set_alt_interface_no(struct usb2_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t alt_no);
+usb2_error_t usb2_req_set_config(struct usb2_device *udev, struct mtx *mtx, uint8_t conf);
+usb2_error_t usb2_req_set_hub_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel);
+usb2_error_t usb2_req_set_idle(struct usb2_device *udev, struct mtx *mtx, uint8_t iface_index, uint8_t duration, uint8_t id);
+usb2_error_t usb2_req_set_port_feature(struct usb2_device *udev, struct mtx *mtx, uint8_t port, uint16_t sel);
+usb2_error_t usb2_req_set_protocol(struct usb2_device *udev, struct mtx *mtx, uint8_t iface_index, uint16_t report);
+usb2_error_t usb2_req_set_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id);
+usb2_error_t usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx);
+
+#define usb2_do_request(u,m,r,d) \
+ usb2_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT)
+
+#endif /* _USB2_REQUEST_H_ */
diff --git a/sys/dev/usb2/core/usb2_sw_transfer.c b/sys/dev/usb2/core/usb2_sw_transfer.c
new file mode 100644
index 000000000000..bb825028a907
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_sw_transfer.c
@@ -0,0 +1,166 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_error.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_sw_transfer.h>
+#include <dev/usb2/core/usb2_debug.h>
+
+/*------------------------------------------------------------------------*
+ * usb2_sw_transfer - factored out code
+ *
+ * This function is basically used for the Virtual Root HUB, and can
+ * emulate control, bulk and interrupt endpoints. Data is exchanged
+ * using the "std->ptr" and "std->len" fields, that allows kernel
+ * virtual memory to be transferred. All state is kept in the
+ * structure pointed to by the "std" argument passed to this
+ * function. The "func" argument points to a function that is called
+ * back in the various states, so that the application using this
+ * function can get a chance to select the outcome. The "func"
+ * function is allowed to sleep, exiting all mutexes. If this function
+ * will sleep the "enter" and "start" methods must be marked
+ * non-cancelable, hence there is no extra cancelled checking in this
+ * function.
+ *------------------------------------------------------------------------*/
+void
+usb2_sw_transfer(struct usb2_sw_transfer *std,
+ usb2_sw_transfer_func_t *func)
+{
+ struct usb2_xfer *xfer;
+ uint32_t len;
+ uint8_t shortpkt = 0;
+
+ xfer = std->xfer;
+ if (xfer == NULL) {
+ /* the transfer is gone */
+ DPRINTF("xfer gone\n");
+ return;
+ }
+ mtx_assert(xfer->usb2_mtx, MA_OWNED);
+
+ std->xfer = NULL;
+
+ /* check for control transfer */
+ if (xfer->flags_int.control_xfr) {
+ /* check if we are transferring the SETUP packet */
+ if (xfer->flags_int.control_hdr) {
+
+ /* copy out the USB request */
+
+ if (xfer->frlengths[0] == sizeof(std->req)) {
+ usb2_copy_out(xfer->frbuffers, 0,
+ &std->req, sizeof(std->req));
+ } else {
+ std->err = USB_ERR_INVAL;
+ goto done;
+ }
+
+ xfer->aframes = 1;
+
+ std->err = 0;
+ std->state = USB_SW_TR_SETUP;
+
+ (func) (xfer, std);
+
+ if (std->err) {
+ goto done;
+ }
+ } else {
+ /* skip the first frame in this case */
+ xfer->aframes = 1;
+ }
+ }
+ std->err = 0;
+ std->state = USB_SW_TR_PRE_DATA;
+
+ (func) (xfer, std);
+
+ if (std->err) {
+ goto done;
+ }
+ /* Transfer data. Iterate accross all frames. */
+ while (xfer->aframes != xfer->nframes) {
+
+ len = xfer->frlengths[xfer->aframes];
+
+ if (len > std->len) {
+ len = std->len;
+ shortpkt = 1;
+ }
+ if (len > 0) {
+ if ((xfer->endpoint & (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN) {
+ usb2_copy_in(xfer->frbuffers + xfer->aframes, 0,
+ std->ptr, len);
+ } else {
+ usb2_copy_out(xfer->frbuffers + xfer->aframes, 0,
+ std->ptr, len);
+ }
+ }
+ std->ptr += len;
+ std->len -= len;
+ xfer->frlengths[xfer->aframes] = len;
+ xfer->aframes++;
+
+ if (shortpkt) {
+ break;
+ }
+ }
+
+ std->err = 0;
+ std->state = USB_SW_TR_POST_DATA;
+
+ (func) (xfer, std);
+
+ if (std->err) {
+ goto done;
+ }
+ /* check if the control transfer is complete */
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ std->err = 0;
+ std->state = USB_SW_TR_STATUS;
+
+ (func) (xfer, std);
+
+ if (std->err) {
+ goto done;
+ }
+ }
+done:
+ DPRINTF("done err=%s\n", usb2_errstr(std->err));
+ std->state = USB_SW_TR_PRE_CALLBACK;
+ (func) (xfer, std);
+ return;
+}
diff --git a/sys/dev/usb2/core/usb2_sw_transfer.h b/sys/dev/usb2/core/usb2_sw_transfer.h
new file mode 100644
index 000000000000..c90ba3663f77
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_sw_transfer.h
@@ -0,0 +1,61 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_SW_TRANSFER_H_
+#define _USB2_SW_TRANSFER_H_
+
+/* Software transfer function state argument values */
+
+enum {
+ USB_SW_TR_SETUP,
+ USB_SW_TR_STATUS,
+ USB_SW_TR_PRE_DATA,
+ USB_SW_TR_POST_DATA,
+ USB_SW_TR_PRE_CALLBACK,
+};
+
+struct usb2_sw_transfer;
+
+typedef void (usb2_sw_transfer_func_t)(struct usb2_xfer *, struct usb2_sw_transfer *);
+
+/*
+ * The following structure is used to keep the state of a standard
+ * root transfer.
+ */
+struct usb2_sw_transfer {
+ struct usb2_device_request req;
+ struct usb2_xfer *xfer;
+ uint8_t *ptr;
+ uint16_t len;
+ uint8_t state;
+ usb2_error_t err;
+};
+
+/* prototypes */
+
+void usb2_sw_transfer(struct usb2_sw_transfer *std, usb2_sw_transfer_func_t *func);
+
+#endif /* _USB2_SW_TRANSFER_H_ */
diff --git a/sys/dev/usb2/core/usb2_transfer.c b/sys/dev/usb2/core/usb2_transfer.c
new file mode 100644
index 000000000000..21676243e6eb
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_transfer.c
@@ -0,0 +1,2833 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+#include <dev/usb2/include/usb2_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_busdma.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_transfer.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_debug.h>
+#include <dev/usb2/core/usb2_util.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+struct usb2_std_packet_size {
+ struct {
+ uint16_t min; /* inclusive */
+ uint16_t max; /* inclusive */
+ } range;
+
+ uint16_t fixed[4];
+};
+
+/*
+ * This table stores the all the allowed packet sizes based on
+ * endpoint type and USB speed:
+ */
+static const struct usb2_std_packet_size
+ usb2_std_packet_size[4][USB_SPEED_MAX] = {
+
+ [UE_INTERRUPT] = {
+ [USB_SPEED_LOW] = {.range = {0, 8}},
+ [USB_SPEED_FULL] = {.range = {0, 64}},
+ [USB_SPEED_HIGH] = {.range = {0, 1024}},
+ [USB_SPEED_VARIABLE] = {.range = {0, 1024}},
+ },
+
+ [UE_CONTROL] = {
+ [USB_SPEED_LOW] = {.fixed = {8, 8, 8, 8}},
+ [USB_SPEED_FULL] = {.fixed = {8, 16, 32, 64}},
+ [USB_SPEED_HIGH] = {.fixed = {64, 64, 64, 64}},
+ [USB_SPEED_VARIABLE] = {.fixed = {512, 512, 512, 512}},
+ },
+
+ [UE_BULK] = {
+ [USB_SPEED_LOW] = {.fixed = {0, 0, 0, 0}}, /* invalid */
+ [USB_SPEED_FULL] = {.fixed = {8, 16, 32, 64}},
+ [USB_SPEED_HIGH] = {.fixed = {512, 512, 512, 512}},
+ [USB_SPEED_VARIABLE] = {.fixed = {512, 512, 1024, 1536}},
+ },
+
+ [UE_ISOCHRONOUS] = {
+ [USB_SPEED_LOW] = {.fixed = {0, 0, 0, 0}}, /* invalid */
+ [USB_SPEED_FULL] = {.range = {0, 1023}},
+ [USB_SPEED_HIGH] = {.range = {0, 1024}},
+ [USB_SPEED_VARIABLE] = {.range = {0, 3584}},
+ },
+};
+
+static const struct usb2_config usb2_control_ep_cfg[USB_DEFAULT_XFER_MAX] = {
+
+ /* This transfer is used for generic control endpoint transfers */
+
+ [0] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control endpoint */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = 1024, /* bytes */
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .mh.callback = &usb2_do_request_callback,
+ .md.bufsize = 1024, /* bytes */
+ .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 0,},
+ .md.callback = &usb2_handle_request_callback,
+ },
+
+ /* This transfer is used for generic clear stall only */
+
+ [1] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &usb2_do_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+};
+
+/* function prototypes */
+
+static void usb2_update_max_frame_size(struct usb2_xfer *xfer);
+static uint32_t usb2_get_dma_delay(struct usb2_bus *bus);
+static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *info, uint8_t needs_delay);
+static void usb2_control_transfer_init(struct usb2_xfer *xfer);
+static uint8_t usb2_start_hardware_sub(struct usb2_xfer *xfer);
+static void usb2_callback_proc(struct usb2_proc_msg *_pm);
+static void usb2_callback_ss_done_defer(struct usb2_xfer *xfer);
+static void usb2_callback_wrapper(struct usb2_xfer_queue *pq);
+static void usb2_dma_delay_done_cb(void *arg);
+static void usb2_transfer_start_cb(void *arg);
+static uint8_t usb2_callback_wrapper_sub(struct usb2_xfer *xfer);
+
+/*------------------------------------------------------------------------*
+ * usb2_update_max_frame_size
+ *
+ * This function updates the maximum frame size, hence high speed USB
+ * can transfer multiple consecutive packets.
+ *------------------------------------------------------------------------*/
+static void
+usb2_update_max_frame_size(struct usb2_xfer *xfer)
+{
+ /* compute maximum frame size */
+
+ if (xfer->max_packet_count == 2) {
+ xfer->max_frame_size = 2 * xfer->max_packet_size;
+ } else if (xfer->max_packet_count == 3) {
+ xfer->max_frame_size = 3 * xfer->max_packet_size;
+ } else {
+ xfer->max_frame_size = xfer->max_packet_size;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_dma_delay
+ *
+ * The following function is called when we need to
+ * synchronize with DMA hardware.
+ *
+ * Returns:
+ * 0: no DMA delay required
+ * Else: milliseconds of DMA delay
+ *------------------------------------------------------------------------*/
+static uint32_t
+usb2_get_dma_delay(struct usb2_bus *bus)
+{
+ uint32_t temp = 0;
+
+ if (bus->methods->get_dma_delay) {
+ (bus->methods->get_dma_delay) (bus, &temp);
+ /*
+ * Round up and convert to milliseconds. Note that we use
+ * 1024 milliseconds per second. to save a division.
+ */
+ temp += 0x3FF;
+ temp /= 0x400;
+ }
+ return (temp);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_setup_sub_malloc
+ *
+ * This function will allocate one or more DMA'able memory chunks
+ * according to "size", "align" and "count" arguments. "ppc" is
+ * pointed to a linear array of USB page caches afterwards.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_transfer_setup_sub_malloc(struct usb2_setup_params *parm,
+ struct usb2_page_cache **ppc, uint32_t size, uint32_t align,
+ uint32_t count)
+{
+ struct usb2_page_cache *pc;
+ struct usb2_page *pg;
+ void *buf;
+ uint32_t n_dma_pc;
+ uint32_t n_obj;
+ uint32_t x;
+ uint32_t y;
+ uint32_t r;
+ uint32_t z;
+
+ USB_ASSERT(align > 1, ("Invalid alignment, 0x%08x!\n",
+ align));
+ USB_ASSERT(size > 0, ("Invalid size = 0!\n"));
+
+ if (count == 0) {
+ return (0); /* nothing to allocate */
+ }
+ /*
+ * Make sure that the size is aligned properly.
+ */
+ size = -((-size) & (-align));
+
+ /*
+ * Try multi-allocation chunks to reduce the number of DMA
+ * allocations, hence DMA allocations are slow.
+ */
+ if (size >= PAGE_SIZE) {
+ n_dma_pc = count;
+ n_obj = 1;
+ } else {
+ /* compute number of objects per page */
+ n_obj = (PAGE_SIZE / size);
+ /*
+ * Compute number of DMA chunks, rounded up
+ * to nearest one:
+ */
+ n_dma_pc = ((count + n_obj - 1) / n_obj);
+ }
+
+ if (parm->buf == NULL) {
+ /* for the future */
+ parm->dma_page_ptr += n_dma_pc;
+ parm->dma_page_cache_ptr += n_dma_pc;
+ parm->dma_page_ptr += count;
+ parm->xfer_page_cache_ptr += count;
+ return (0);
+ }
+ for (x = 0; x != n_dma_pc; x++) {
+ /* need to initialize the page cache */
+ parm->dma_page_cache_ptr[x].tag_parent =
+ &parm->curr_xfer->usb2_root->dma_parent_tag;
+ }
+ for (x = 0; x != count; x++) {
+ /* need to initialize the page cache */
+ parm->xfer_page_cache_ptr[x].tag_parent =
+ &parm->curr_xfer->usb2_root->dma_parent_tag;
+ }
+
+ if (ppc) {
+ *ppc = parm->xfer_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 (r < n_obj) {
+ /* compute last remainder */
+ z = r * size;
+ n_obj = r;
+ }
+ if (usb2_pc_alloc_mem(parm->dma_page_cache_ptr,
+ pg, z, align)) {
+ return (1); /* failure */
+ }
+ /* Set beginning of current buffer */
+ buf = parm->dma_page_cache_ptr->buffer;
+ /* Make room for one DMA page cache and one page */
+ parm->dma_page_cache_ptr++;
+ pg++;
+
+ for (y = 0; (y != n_obj); y++, r--, pc++, pg++) {
+
+ /* Load sub-chunk into DMA */
+ if (usb2_pc_dmamap_create(pc, size)) {
+ return (1); /* failure */
+ }
+ pc->buffer = USB_ADD_BYTES(buf, y * size);
+ pc->page_start = pg;
+
+ mtx_lock(pc->tag_parent->mtx);
+ if (usb2_pc_load_mem(pc, size, 1 /* synchronous */ )) {
+ mtx_unlock(pc->tag_parent->mtx);
+ return (1); /* failure */
+ }
+ mtx_unlock(pc->tag_parent->mtx);
+ }
+ }
+
+ parm->xfer_page_cache_ptr = pc;
+ parm->dma_page_ptr = pg;
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_setup_sub - transfer setup subroutine
+ *
+ * This function must be called from the "xfer_setup" callback of the
+ * USB Host or Device controller driver when setting up an USB
+ * transfer. This function will setup correct packet sizes, buffer
+ * sizes, flags and more, that are stored in the "usb2_xfer"
+ * structure.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_setup_sub(struct usb2_setup_params *parm)
+{
+ enum {
+ REQ_SIZE = 8,
+ MIN_PKT = 8,
+ };
+ struct usb2_xfer *xfer = parm->curr_xfer;
+ const struct usb2_config_sub *setup_sub = parm->curr_setup_sub;
+ struct usb2_endpoint_descriptor *edesc;
+ struct usb2_std_packet_size std_size;
+ uint32_t n_frlengths;
+ uint32_t n_frbuffers;
+ uint32_t x;
+ uint8_t type;
+ uint8_t zmps;
+
+ /*
+ * Sanity check. The following parameters must be initialized before
+ * calling this function.
+ */
+ if ((parm->hc_max_packet_size == 0) ||
+ (parm->hc_max_packet_count == 0) ||
+ (parm->hc_max_frame_size == 0)) {
+ parm->err = USB_ERR_INVAL;
+ goto done;
+ }
+ edesc = xfer->pipe->edesc;
+
+ type = (edesc->bmAttributes & UE_XFERTYPE);
+
+ xfer->flags = setup_sub->flags;
+ xfer->nframes = setup_sub->frames;
+ xfer->timeout = setup_sub->timeout;
+ xfer->callback = setup_sub->callback;
+ xfer->interval = setup_sub->interval;
+ xfer->endpoint = edesc->bEndpointAddress;
+ xfer->max_packet_size = UGETW(edesc->wMaxPacketSize);
+ xfer->max_packet_count = 1;
+ /* make a shadow copy: */
+ xfer->flags_int.usb2_mode = parm->udev->flags.usb2_mode;
+
+ parm->bufsize = setup_sub->bufsize;
+
+ if (parm->speed == USB_SPEED_HIGH) {
+ xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3;
+ xfer->max_packet_size &= 0x7FF;
+ }
+ /* range check "max_packet_count" */
+
+ if (xfer->max_packet_count > parm->hc_max_packet_count) {
+ xfer->max_packet_count = parm->hc_max_packet_count;
+ }
+ /* filter "wMaxPacketSize" according to HC capabilities */
+
+ if ((xfer->max_packet_size > parm->hc_max_packet_size) ||
+ (xfer->max_packet_size == 0)) {
+ xfer->max_packet_size = parm->hc_max_packet_size;
+ }
+ /* filter "wMaxPacketSize" according to standard sizes */
+
+ std_size = usb2_std_packet_size[type][parm->speed];
+
+ if (std_size.range.min || std_size.range.max) {
+
+ if (xfer->max_packet_size < std_size.range.min) {
+ xfer->max_packet_size = std_size.range.min;
+ }
+ if (xfer->max_packet_size > std_size.range.max) {
+ xfer->max_packet_size = std_size.range.max;
+ }
+ } else {
+
+ if (xfer->max_packet_size >= std_size.fixed[3]) {
+ xfer->max_packet_size = std_size.fixed[3];
+ } else if (xfer->max_packet_size >= std_size.fixed[2]) {
+ xfer->max_packet_size = std_size.fixed[2];
+ } else if (xfer->max_packet_size >= std_size.fixed[1]) {
+ xfer->max_packet_size = std_size.fixed[1];
+ } else {
+ /* only one possibility left */
+ xfer->max_packet_size = std_size.fixed[0];
+ }
+ }
+
+ /* compute "max_frame_size" */
+
+ usb2_update_max_frame_size(xfer);
+
+ /* check interrupt interval and transfer pre-delay */
+
+ if (type == UE_ISOCHRONOUS) {
+
+ uint32_t frame_limit;
+
+ xfer->interval = 0; /* not used, must be zero */
+ xfer->flags_int.isochronous_xfr = 1; /* set flag */
+
+ if (xfer->timeout == 0) {
+ /*
+ * set a default timeout in
+ * case something goes wrong!
+ */
+ xfer->timeout = 1000 / 4;
+ }
+ if (parm->speed == USB_SPEED_HIGH) {
+ frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER;
+ } else {
+ frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER;
+ }
+
+ if (xfer->nframes > frame_limit) {
+ /*
+ * this is not going to work
+ * cross hardware
+ */
+ parm->err = USB_ERR_INVAL;
+ goto done;
+ }
+ if (xfer->nframes == 0) {
+ /*
+ * this is not a valid value
+ */
+ parm->err = USB_ERR_ZERO_NFRAMES;
+ goto done;
+ }
+ } else {
+
+ /*
+ * if a value is specified use that else check the endpoint
+ * descriptor
+ */
+ if (xfer->interval == 0) {
+
+ if (type == UE_INTERRUPT) {
+
+ xfer->interval = edesc->bInterval;
+
+ if (parm->speed == USB_SPEED_HIGH) {
+ xfer->interval /= 8; /* 125us -> 1ms */
+ }
+ if (xfer->interval == 0) {
+ /*
+ * one millisecond is the smallest
+ * interval
+ */
+ xfer->interval = 1;
+ }
+ }
+ }
+ }
+
+ /*
+ * NOTE: we do not allow "max_packet_size" or "max_frame_size"
+ * to be equal to zero when setting up USB transfers, hence
+ * this leads to alot of extra code in the USB kernel.
+ */
+
+ if ((xfer->max_frame_size == 0) ||
+ (xfer->max_packet_size == 0)) {
+
+ zmps = 1;
+
+ if ((parm->bufsize <= MIN_PKT) &&
+ (type != UE_CONTROL) &&
+ (type != UE_BULK)) {
+
+ /* workaround */
+ xfer->max_packet_size = MIN_PKT;
+ xfer->max_packet_count = 1;
+ parm->bufsize = 0; /* automatic setup length */
+ usb2_update_max_frame_size(xfer);
+
+ } else {
+ parm->err = USB_ERR_ZERO_MAXP;
+ goto done;
+ }
+
+ } else {
+ zmps = 0;
+ }
+
+ /*
+ * check if we should setup a default
+ * length:
+ */
+
+ if (parm->bufsize == 0) {
+
+ parm->bufsize = xfer->max_frame_size;
+
+ if (type == UE_ISOCHRONOUS) {
+ parm->bufsize *= xfer->nframes;
+ }
+ }
+ /*
+ * check if we are about to setup a proxy
+ * type of buffer:
+ */
+
+ if (xfer->flags.proxy_buffer) {
+
+ /* round bufsize up */
+
+ parm->bufsize += (xfer->max_frame_size - 1);
+
+ if (parm->bufsize < xfer->max_frame_size) {
+ /* length wrapped around */
+ parm->err = USB_ERR_INVAL;
+ goto done;
+ }
+ /* subtract remainder */
+
+ parm->bufsize -= (parm->bufsize % xfer->max_frame_size);
+
+ /* add length of USB device request structure, if any */
+
+ if (type == UE_CONTROL) {
+ parm->bufsize += REQ_SIZE; /* SETUP message */
+ }
+ }
+ xfer->max_data_length = parm->bufsize;
+
+ /* Setup "n_frlengths" and "n_frbuffers" */
+
+ if (type == UE_ISOCHRONOUS) {
+ n_frlengths = xfer->nframes;
+ n_frbuffers = 1;
+ } else {
+
+ if (type == UE_CONTROL) {
+ xfer->flags_int.control_xfr = 1;
+ if (xfer->nframes == 0) {
+ if (parm->bufsize <= REQ_SIZE) {
+ /*
+ * there will never be any data
+ * stage
+ */
+ xfer->nframes = 1;
+ } else {
+ xfer->nframes = 2;
+ }
+ }
+ } else {
+ if (xfer->nframes == 0) {
+ xfer->nframes = 1;
+ }
+ }
+
+ n_frlengths = xfer->nframes;
+ n_frbuffers = xfer->nframes;
+ }
+
+ /*
+ * check if we have room for the
+ * USB device request structure:
+ */
+
+ if (type == UE_CONTROL) {
+
+ if (xfer->max_data_length < REQ_SIZE) {
+ /* length wrapped around or too small bufsize */
+ parm->err = USB_ERR_INVAL;
+ goto done;
+ }
+ xfer->max_data_length -= REQ_SIZE;
+ }
+ /* setup "frlengths" */
+
+ xfer->frlengths = parm->xfer_length_ptr;
+
+ parm->xfer_length_ptr += n_frlengths;
+
+ /* setup "frbuffers" */
+
+ xfer->frbuffers = parm->xfer_page_cache_ptr;
+
+ parm->xfer_page_cache_ptr += n_frbuffers;
+
+ /*
+ * check if we need to setup
+ * a local buffer:
+ */
+
+ if (!xfer->flags.ext_buffer) {
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ if (parm->buf) {
+
+ xfer->local_buffer =
+ USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ usb2_set_frame_offset(xfer, 0, 0);
+
+ if ((type == UE_CONTROL) && (n_frbuffers > 1)) {
+ usb2_set_frame_offset(xfer, REQ_SIZE, 1);
+ }
+ }
+ parm->size[0] += parm->bufsize;
+
+ /* align data again */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+ }
+ /*
+ * Compute maximum buffer size
+ */
+
+ if (parm->bufsize_max < parm->bufsize) {
+ parm->bufsize_max = parm->bufsize;
+ }
+ if (xfer->flags_int.bdma_enable) {
+ /*
+ * Setup "dma_page_ptr".
+ *
+ * Proof for formula below:
+ *
+ * Assume there are three USB frames having length "a", "b" and
+ * "c". These USB frames will at maximum need "z"
+ * "usb2_page" structures. "z" is given by:
+ *
+ * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) +
+ * ((c / USB_PAGE_SIZE) + 2);
+ *
+ * Constraining "a", "b" and "c" like this:
+ *
+ * (a + b + c) <= parm->bufsize
+ *
+ * We know that:
+ *
+ * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2));
+ *
+ * Here is the general formula:
+ */
+ xfer->dma_page_ptr = parm->dma_page_ptr;
+ parm->dma_page_ptr += (2 * n_frbuffers);
+ parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE);
+ }
+ if (zmps) {
+ /* correct maximum data length */
+ xfer->max_data_length = 0;
+ }
+ /* subtract USB frame remainder from "hc_max_frame_size" */
+
+ xfer->max_usb2_frame_size =
+ (parm->hc_max_frame_size -
+ (parm->hc_max_frame_size % xfer->max_frame_size));
+
+ if (xfer->max_usb2_frame_size == 0) {
+ parm->err = USB_ERR_INVAL;
+ goto done;
+ }
+ /* initialize max frame count */
+
+ xfer->max_frame_count = xfer->nframes;
+
+ /* initialize frame buffers */
+
+ if (parm->buf) {
+ for (x = 0; x != n_frbuffers; x++) {
+ xfer->frbuffers[x].tag_parent =
+ &xfer->usb2_root->dma_parent_tag;
+
+ if (xfer->flags_int.bdma_enable &&
+ (parm->bufsize_max > 0)) {
+
+ if (usb2_pc_dmamap_create(
+ xfer->frbuffers + x,
+ parm->bufsize_max)) {
+ parm->err = USB_ERR_NOMEM;
+ goto done;
+ }
+ }
+ }
+ }
+done:
+ if (parm->err) {
+ /*
+ * Set some dummy values so that we avoid division by zero:
+ */
+ xfer->max_usb2_frame_size = 1;
+ xfer->max_frame_size = 1;
+ xfer->max_packet_size = 1;
+ xfer->max_data_length = 0;
+ xfer->nframes = 0;
+ xfer->max_frame_count = 0;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_setup - setup an array of USB transfers
+ *
+ * NOTE: You must always call "usb2_transfer_unsetup" after calling
+ * "usb2_transfer_setup" if success was returned.
+ *
+ * The idea is that the USB device driver should pre-allocate all its
+ * transfers by one call to this function.
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_transfer_setup(struct usb2_device *udev,
+ const uint8_t *ifaces, struct usb2_xfer **ppxfer,
+ const struct usb2_config *setup_start, uint16_t n_setup,
+ void *priv_sc, struct mtx *priv_mtx)
+{
+ struct usb2_xfer dummy;
+ struct usb2_setup_params parm;
+ const struct usb2_config *setup_end = setup_start + n_setup;
+ const struct usb2_config *setup;
+ struct usb2_pipe *pipe;
+ struct usb2_xfer_root *info;
+ struct usb2_xfer *xfer;
+ void *buf = NULL;
+ uint16_t n;
+ uint16_t refcount;
+
+ parm.err = 0;
+ refcount = 0;
+ info = NULL;
+
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
+ "usb2_transfer_setup can sleep!");
+
+ /* do some checking first */
+
+ if (n_setup == 0) {
+ DPRINTFN(6, "setup array has zero length!\n");
+ return (USB_ERR_INVAL);
+ }
+ if (ifaces == 0) {
+ DPRINTFN(6, "ifaces array is NULL!\n");
+ return (USB_ERR_INVAL);
+ }
+ if (priv_mtx == NULL) {
+ DPRINTFN(6, "using global lock\n");
+ priv_mtx = &Giant;
+ }
+ /* sanity checks */
+ for (setup = setup_start, n = 0;
+ setup != setup_end; setup++, n++) {
+ if ((setup->mh.bufsize == 0xffffffff) ||
+ (setup->md.bufsize == 0xffffffff)) {
+ parm.err = USB_ERR_BAD_BUFSIZE;
+ DPRINTF("invalid bufsize\n");
+ }
+ if ((setup->mh.callback == NULL) &&
+ (setup->md.callback == NULL)) {
+ parm.err = USB_ERR_NO_CALLBACK;
+ DPRINTF("no callback\n");
+ }
+ ppxfer[n] = NULL;
+ }
+
+ if (parm.err) {
+ goto done;
+ }
+ bzero(&parm, sizeof(parm));
+
+ parm.udev = udev;
+ parm.speed = usb2_get_speed(udev);
+ parm.hc_max_packet_count = 1;
+
+ if (parm.speed >= USB_SPEED_MAX) {
+ parm.err = USB_ERR_INVAL;
+ goto done;
+ }
+ /* setup all transfers */
+
+ while (1) {
+
+ if (buf) {
+ /*
+ * Initialize the "usb2_xfer_root" structure,
+ * which is common for all our USB transfers.
+ */
+ info = USB_ADD_BYTES(buf, 0);
+
+ info->memory_base = buf;
+ info->memory_size = parm.size[0];
+
+ info->dma_page_cache_start = USB_ADD_BYTES(buf, parm.size[4]);
+ info->dma_page_cache_end = USB_ADD_BYTES(buf, parm.size[5]);
+ info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm.size[5]);
+ info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm.size[2]);
+
+ usb2_cv_init(&info->cv_drain, "WDRAIN");
+
+ info->usb2_mtx = &udev->bus->mtx;
+ info->priv_mtx = priv_mtx;
+
+ usb2_dma_tag_setup(&info->dma_parent_tag,
+ parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag,
+ priv_mtx, &usb2_bdma_done_event, info, 32, parm.dma_tag_max);
+
+ info->bus = udev->bus;
+
+ TAILQ_INIT(&info->done_q.head);
+ info->done_q.command = &usb2_callback_wrapper;
+
+ TAILQ_INIT(&info->dma_q.head);
+ info->dma_q.command = &usb2_bdma_work_loop;
+
+ info->done_m[0].hdr.pm_callback = &usb2_callback_proc;
+ info->done_m[0].usb2_root = info;
+ info->done_m[1].hdr.pm_callback = &usb2_callback_proc;
+ info->done_m[1].usb2_root = info;
+
+ /* create a callback thread */
+
+ if (usb2_proc_setup(&info->done_p,
+ &udev->bus->mtx, USB_PRI_HIGH)) {
+ parm.err = USB_ERR_NO_INTR_THREAD;
+ goto done;
+ }
+ }
+ /* reset sizes */
+
+ parm.size[0] = 0;
+ parm.buf = buf;
+ parm.size[0] += sizeof(info[0]);
+
+ for (setup = setup_start, n = 0;
+ setup != setup_end; setup++, n++) {
+
+ /* select mode specific structure */
+ if (udev->flags.usb2_mode == USB_MODE_HOST) {
+ parm.curr_setup_sub = &setup->mh;
+ } else {
+ parm.curr_setup_sub = &setup->md;
+ }
+ /* skip USB transfers without callbacks: */
+ if (parm.curr_setup_sub->callback == NULL) {
+ continue;
+ }
+ /* see if there is a matching endpoint */
+ pipe = usb2_get_pipe(udev,
+ ifaces[setup->if_index], setup);
+
+ if (!pipe) {
+ if (parm.curr_setup_sub->flags.no_pipe_ok) {
+ continue;
+ }
+ parm.err = USB_ERR_NO_PIPE;
+ goto done;
+ }
+ /* store current setup pointer */
+ parm.curr_setup = setup;
+
+ /* align data properly */
+ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+
+ if (buf) {
+
+ /*
+ * Common initialization of the
+ * "usb2_xfer" structure.
+ */
+ xfer = USB_ADD_BYTES(buf, parm.size[0]);
+
+ ppxfer[n] = xfer;
+ xfer->udev = udev;
+ xfer->address = udev->address;
+ xfer->priv_sc = priv_sc;
+ xfer->priv_mtx = priv_mtx;
+ xfer->usb2_mtx = &udev->bus->mtx;
+ xfer->usb2_root = info;
+ info->setup_refcount++;
+
+ usb2_callout_init_mtx(&xfer->timeout_handle, xfer->usb2_mtx,
+ CALLOUT_RETURNUNLOCKED);
+ } else {
+ /*
+ * Setup a dummy xfer, hence we are
+ * writing to the "usb2_xfer"
+ * structure pointed to by "xfer"
+ * before we have allocated any
+ * memory:
+ */
+ xfer = &dummy;
+ bzero(&dummy, sizeof(dummy));
+ refcount++;
+ }
+
+ parm.size[0] += sizeof(xfer[0]);
+
+ xfer->pipe = pipe;
+
+ if (buf) {
+ /*
+ * Increment the pipe refcount. This
+ * basically prevents setting a new
+ * configuration and alternate setting
+ * when USB transfers are in use on
+ * the given interface. Search the USB
+ * code for "pipe->refcount" if you
+ * want more information.
+ */
+ xfer->pipe->refcount++;
+ }
+ parm.methods = xfer->pipe->methods;
+ parm.curr_xfer = xfer;
+
+ /*
+ * Call the Host or Device controller transfer setup
+ * routine:
+ */
+ (udev->bus->methods->xfer_setup) (&parm);
+
+ if (parm.err) {
+ goto done;
+ }
+ }
+
+ if (buf || parm.err) {
+ goto done;
+ }
+ if (refcount == 0) {
+ /* no transfers - nothing to do ! */
+ goto done;
+ }
+ /* align data properly */
+ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+
+ /* store offset temporarily */
+ parm.size[1] = parm.size[0];
+
+ /*
+ * The number of DMA tags required depends on
+ * the number of endpoints. The current estimate
+ * for maximum number of DMA tags per endpoint
+ * is two.
+ */
+ parm.dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX);
+
+ /*
+ * DMA tags for QH, TD, Data and more.
+ */
+ parm.dma_tag_max += 8;
+
+ parm.dma_tag_p += parm.dma_tag_max;
+
+ parm.size[0] += ((uint8_t *)parm.dma_tag_p) -
+ ((uint8_t *)0);
+
+ /* align data properly */
+ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+
+ /* store offset temporarily */
+ parm.size[3] = parm.size[0];
+
+ parm.size[0] += ((uint8_t *)parm.dma_page_ptr) -
+ ((uint8_t *)0);
+
+ /* align data properly */
+ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+
+ /* store offset temporarily */
+ parm.size[4] = parm.size[0];
+
+ parm.size[0] += ((uint8_t *)parm.dma_page_cache_ptr) -
+ ((uint8_t *)0);
+
+ /* store end offset temporarily */
+ parm.size[5] = parm.size[0];
+
+ parm.size[0] += ((uint8_t *)parm.xfer_page_cache_ptr) -
+ ((uint8_t *)0);
+
+ /* store end offset temporarily */
+
+ parm.size[2] = parm.size[0];
+
+ /* align data properly */
+ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+
+ parm.size[6] = parm.size[0];
+
+ parm.size[0] += ((uint8_t *)parm.xfer_length_ptr) -
+ ((uint8_t *)0);
+
+ /* align data properly */
+ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1));
+
+ /* allocate zeroed memory */
+ buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO);
+
+ if (buf == NULL) {
+ parm.err = USB_ERR_NOMEM;
+ DPRINTFN(0, "cannot allocate memory block for "
+ "configuration (%d bytes)\n",
+ parm.size[0]);
+ goto done;
+ }
+ parm.dma_tag_p = USB_ADD_BYTES(buf, parm.size[1]);
+ parm.dma_page_ptr = USB_ADD_BYTES(buf, parm.size[3]);
+ parm.dma_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[4]);
+ parm.xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[5]);
+ parm.xfer_length_ptr = USB_ADD_BYTES(buf, parm.size[6]);
+ }
+
+done:
+ if (buf) {
+ if (info->setup_refcount == 0) {
+ /*
+ * "usb2_transfer_unsetup_sub" will unlock
+ * "usb2_mtx" before returning !
+ */
+ mtx_lock(info->usb2_mtx);
+
+ /* something went wrong */
+ usb2_transfer_unsetup_sub(info, 0);
+ }
+ }
+ if (parm.err) {
+ usb2_transfer_unsetup(ppxfer, n_setup);
+ }
+ return (parm.err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_unsetup_sub - factored out code
+ *------------------------------------------------------------------------*/
+static void
+usb2_transfer_unsetup_sub(struct usb2_xfer_root *info, uint8_t needs_delay)
+{
+ struct usb2_page_cache *pc;
+ uint32_t temp;
+
+ mtx_assert(info->usb2_mtx, MA_OWNED);
+
+ /* wait for any outstanding DMA operations */
+
+ if (needs_delay) {
+ temp = usb2_get_dma_delay(info->bus);
+ usb2_pause_mtx(info->usb2_mtx, temp);
+ }
+ mtx_unlock(info->usb2_mtx);
+
+ /* wait for interrupt thread to exit */
+ usb2_proc_unsetup(&info->done_p);
+
+ /* free DMA'able memory, if any */
+ pc = info->dma_page_cache_start;
+ while (pc != info->dma_page_cache_end) {
+ usb2_pc_free_mem(pc);
+ pc++;
+ }
+
+ /* free DMA maps in all "xfer->frbuffers" */
+ pc = info->xfer_page_cache_start;
+ while (pc != info->xfer_page_cache_end) {
+ usb2_pc_dmamap_destroy(pc);
+ pc++;
+ }
+
+ /* free all DMA tags */
+ usb2_dma_tag_unsetup(&info->dma_parent_tag);
+
+ usb2_cv_destroy(&info->cv_drain);
+
+ /*
+ * free the "memory_base" last, hence the "info" structure is
+ * contained within the "memory_base"!
+ */
+ free(info->memory_base, M_USB);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_unsetup - unsetup/free an array of USB transfers
+ *
+ * NOTE: All USB transfers in progress will get called back passing
+ * the error code "USB_ERR_CANCELLED" before this function
+ * returns.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_unsetup(struct usb2_xfer **pxfer, uint16_t n_setup)
+{
+ struct usb2_xfer *xfer;
+ struct usb2_xfer_root *info;
+ uint8_t needs_delay = 0;
+
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
+ "usb2_transfer_unsetup can sleep!");
+
+ while (n_setup--) {
+ xfer = pxfer[n_setup];
+
+ if (xfer) {
+ if (xfer->pipe) {
+ mtx_lock(xfer->priv_mtx);
+ mtx_lock(xfer->usb2_mtx);
+
+ /*
+ * HINT: when you start/stop a transfer, it
+ * might be a good idea to directly use the
+ * "pxfer[]" structure:
+ *
+ * usb2_transfer_start(sc->pxfer[0]);
+ * usb2_transfer_stop(sc->pxfer[0]);
+ *
+ * That way, if your code has many parts that
+ * will not stop running under the same
+ * lock, in other words "priv_mtx", the
+ * usb2_transfer_start and
+ * usb2_transfer_stop functions will simply
+ * return when they detect a NULL pointer
+ * argument.
+ *
+ * To avoid any races we clear the "pxfer[]"
+ * pointer while holding the private mutex
+ * of the driver:
+ */
+ pxfer[n_setup] = NULL;
+
+ mtx_unlock(xfer->usb2_mtx);
+ mtx_unlock(xfer->priv_mtx);
+
+ usb2_transfer_drain(xfer);
+
+ if (xfer->flags_int.bdma_enable) {
+ needs_delay = 1;
+ }
+ /*
+ * NOTE: default pipe does not have an
+ * interface, even if pipe->iface_index == 0
+ */
+ xfer->pipe->refcount--;
+
+ } else {
+ /* clear the transfer pointer */
+ pxfer[n_setup] = NULL;
+ }
+
+ usb2_callout_drain(&xfer->timeout_handle);
+
+ if (xfer->usb2_root) {
+ info = xfer->usb2_root;
+
+ mtx_lock(info->usb2_mtx);
+
+ USB_ASSERT(info->setup_refcount != 0,
+ ("Invalid setup "
+ "reference count!\n"));
+
+ info->setup_refcount--;
+
+ if (info->setup_refcount == 0) {
+ usb2_transfer_unsetup_sub(info,
+ needs_delay);
+ } else {
+ mtx_unlock(info->usb2_mtx);
+ }
+ }
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_control_transfer_init - factored out code
+ *
+ * In USB Device Mode we have to wait for the SETUP packet which
+ * containst the "struct usb2_device_request" structure, before we can
+ * transfer any data. In USB Host Mode we already have the SETUP
+ * packet at the moment the USB transfer is started. This leads us to
+ * having to setup the USB transfer at two different places in
+ * time. This function just contains factored out control transfer
+ * initialisation code, so that we don't duplicate the code.
+ *------------------------------------------------------------------------*/
+static void
+usb2_control_transfer_init(struct usb2_xfer *xfer)
+{
+ struct usb2_device_request req;
+
+ /* copy out the USB request header */
+
+ usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
+
+ /* setup remainder */
+
+ xfer->flags_int.control_rem = UGETW(req.wLength);
+
+ /* copy direction to endpoint variable */
+
+ xfer->endpoint &= ~(UE_DIR_IN | UE_DIR_OUT);
+ xfer->endpoint |=
+ (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT;
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_start_hardware_sub
+ *
+ * This function handles initialisation of control transfers. Control
+ * transfers are special in that regard that they can both transmit
+ * and receive data.
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_start_hardware_sub(struct usb2_xfer *xfer)
+{
+ uint32_t len;
+
+ /* Check for control endpoint stall */
+ if (xfer->flags.stall_pipe) {
+ /* no longer active */
+ xfer->flags_int.control_act = 0;
+ }
+ /*
+ * Check if there is a control
+ * transfer in progress:
+ */
+ if (xfer->flags_int.control_act) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ /* clear send header flag */
+
+ xfer->flags_int.control_hdr = 0;
+
+ /* setup control transfer */
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+ usb2_control_transfer_init(xfer);
+ }
+ }
+ /* get data length */
+
+ len = xfer->sumlen;
+
+ } else {
+
+ /* the size of the SETUP structure is hardcoded ! */
+
+ if (xfer->frlengths[0] != sizeof(struct usb2_device_request)) {
+ DPRINTFN(0, "Wrong framelength %u != %zu\n",
+ xfer->frlengths[0], sizeof(struct
+ usb2_device_request));
+ goto error;
+ }
+ /* check USB mode */
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+
+ /* check number of frames */
+ if (xfer->nframes != 1) {
+ /*
+ * We need to receive the setup
+ * message first so that we know the
+ * data direction!
+ */
+ DPRINTF("Misconfigured transfer\n");
+ goto error;
+ }
+ /*
+ * Set a dummy "control_rem" value. This
+ * variable will be overwritten later by a
+ * call to "usb2_control_transfer_init()" !
+ */
+ xfer->flags_int.control_rem = 0xFFFF;
+ } else {
+
+ /* setup "endpoint" and "control_rem" */
+
+ usb2_control_transfer_init(xfer);
+ }
+
+ /* set transfer-header flag */
+
+ xfer->flags_int.control_hdr = 1;
+
+ /* get data length */
+
+ len = (xfer->sumlen - sizeof(struct usb2_device_request));
+ }
+
+ /* check if there is a length mismatch */
+
+ if (len > xfer->flags_int.control_rem) {
+ DPRINTFN(0, "Length greater than remaining length!\n");
+ goto error;
+ }
+ /* check if we are doing a short transfer */
+
+ if (xfer->flags.force_short_xfer) {
+ xfer->flags_int.control_rem = 0;
+ } else {
+ if ((len != xfer->max_data_length) &&
+ (len != xfer->flags_int.control_rem) &&
+ (xfer->nframes != 1)) {
+ DPRINTFN(0, "Short control transfer without "
+ "force_short_xfer set!\n");
+ goto error;
+ }
+ xfer->flags_int.control_rem -= len;
+ }
+
+ /* the status part is executed when "control_act" is 0 */
+
+ if ((xfer->flags_int.control_rem > 0) ||
+ (xfer->flags.manual_status)) {
+ /* don't execute the STATUS stage yet */
+ xfer->flags_int.control_act = 1;
+
+ /* sanity check */
+ if ((!xfer->flags_int.control_hdr) &&
+ (xfer->nframes == 1)) {
+ /*
+ * This is not a valid operation!
+ */
+ DPRINTFN(0, "Invalid parameter "
+ "combination\n");
+ goto error;
+ }
+ } else {
+ /* time to execute the STATUS stage */
+ xfer->flags_int.control_act = 0;
+ }
+ return (0); /* success */
+
+error:
+ return (1); /* failure */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_start_hardware - start USB hardware for the given transfer
+ *
+ * This function should only be called from the USB callback.
+ *------------------------------------------------------------------------*/
+void
+usb2_start_hardware(struct usb2_xfer *xfer)
+{
+ uint32_t x;
+
+ DPRINTF("xfer=%p, pipe=%p, nframes=%d, dir=%s\n",
+ xfer, xfer->pipe, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ?
+ "read" : "write");
+
+#if USB_DEBUG
+ if (USB_DEBUG_VAR > 0) {
+ mtx_lock(xfer->usb2_mtx);
+
+ usb2_dump_pipe(xfer->pipe);
+
+ mtx_unlock(xfer->usb2_mtx);
+ }
+#endif
+
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+ mtx_assert(xfer->usb2_mtx, MA_NOTOWNED);
+
+ /* Only open the USB transfer once! */
+ if (!xfer->flags_int.open) {
+ xfer->flags_int.open = 1;
+
+ DPRINTF("open\n");
+
+ mtx_lock(xfer->usb2_mtx);
+ (xfer->pipe->methods->open) (xfer);
+ mtx_unlock(xfer->usb2_mtx);
+ }
+ /* set "transferring" flag */
+ xfer->flags_int.transferring = 1;
+
+ /*
+ * Check if the transfer is waiting on a queue, most
+ * frequently the "done_q":
+ */
+ if (xfer->wait_queue) {
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_dequeue(xfer);
+ mtx_unlock(xfer->usb2_mtx);
+ }
+ /* clear "did_dma_delay" flag */
+ xfer->flags_int.did_dma_delay = 0;
+
+ /* clear "did_close" flag */
+ xfer->flags_int.did_close = 0;
+
+ /* clear "bdma_setup" flag */
+ xfer->flags_int.bdma_setup = 0;
+
+ /* by default we cannot cancel any USB transfer immediately */
+ xfer->flags_int.can_cancel_immed = 0;
+
+ /* clear lengths and frame counts by default */
+ xfer->sumlen = 0;
+ xfer->actlen = 0;
+ xfer->aframes = 0;
+
+ /* clear any previous errors */
+ xfer->error = 0;
+
+ /* sanity check */
+
+ if (xfer->nframes == 0) {
+ if (xfer->flags.stall_pipe) {
+ /*
+ * Special case - want to stall without transferring
+ * any data:
+ */
+ DPRINTF("xfer=%p nframes=0: stall "
+ "or clear stall!\n", xfer);
+ mtx_lock(xfer->usb2_mtx);
+ xfer->flags_int.can_cancel_immed = 1;
+ /* start the transfer */
+ usb2_command_wrapper(&xfer->pipe->pipe_q, xfer);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_done(xfer, USB_ERR_INVAL);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ /* compute total transfer length */
+
+ for (x = 0; x != xfer->nframes; x++) {
+ xfer->sumlen += xfer->frlengths[x];
+ if (xfer->sumlen < xfer->frlengths[x]) {
+ /* length wrapped around */
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_done(xfer, USB_ERR_INVAL);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ }
+
+ /* clear some internal flags */
+
+ xfer->flags_int.short_xfer_ok = 0;
+ xfer->flags_int.short_frames_ok = 0;
+
+ /* check if this is a control transfer */
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (usb2_start_hardware_sub(xfer)) {
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_done(xfer, USB_ERR_STALLED);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ }
+ /*
+ * Setup filtered version of some transfer flags,
+ * in case of data read direction
+ */
+ if (USB_GET_DATA_ISREAD(xfer)) {
+
+ if (xfer->flags_int.control_xfr) {
+
+ /*
+ * Control transfers do not support reception
+ * of multiple short USB frames !
+ */
+
+ if (xfer->flags.short_xfer_ok) {
+ xfer->flags_int.short_xfer_ok = 1;
+ }
+ } else {
+
+ if (xfer->flags.short_frames_ok) {
+ xfer->flags_int.short_xfer_ok = 1;
+ xfer->flags_int.short_frames_ok = 1;
+ } else if (xfer->flags.short_xfer_ok) {
+ xfer->flags_int.short_xfer_ok = 1;
+ }
+ }
+ }
+ /*
+ * Check if BUS-DMA support is enabled and try to load virtual
+ * buffers into DMA, if any:
+ */
+ if (xfer->flags_int.bdma_enable) {
+ /* insert the USB transfer last in the BUS-DMA queue */
+ usb2_command_wrapper(&xfer->usb2_root->dma_q, xfer);
+ return;
+ }
+ /*
+ * Enter the USB transfer into the Host Controller or
+ * Device Controller schedule:
+ */
+ usb2_pipe_enter(xfer);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pipe_enter - factored out code
+ *------------------------------------------------------------------------*/
+void
+usb2_pipe_enter(struct usb2_xfer *xfer)
+{
+ struct usb2_pipe *pipe;
+
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+
+ mtx_lock(xfer->usb2_mtx);
+
+ pipe = xfer->pipe;
+
+ DPRINTF("enter\n");
+
+ /* enter the transfer */
+ (pipe->methods->enter) (xfer);
+
+ /* check cancelability */
+ if (pipe->methods->enter_is_cancelable) {
+ xfer->flags_int.can_cancel_immed = 1;
+ /* check for transfer error */
+ if (xfer->error) {
+ /* some error has happened */
+ usb2_transfer_done(xfer, 0);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+ }
+ } else {
+ xfer->flags_int.can_cancel_immed = 0;
+ }
+
+ /* start the transfer */
+ usb2_command_wrapper(&pipe->pipe_q, xfer);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_start - start an USB transfer
+ *
+ * NOTE: Calling this function more than one time will only
+ * result in a single transfer start, until the USB transfer
+ * completes.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_start(struct usb2_xfer *xfer)
+{
+ if (xfer == NULL) {
+ /* transfer is gone */
+ return;
+ }
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+
+ /* mark the USB transfer started */
+
+ if (!xfer->flags_int.started) {
+ xfer->flags_int.started = 1;
+ }
+ /* check if the USB transfer callback is already transferring */
+
+ if (xfer->flags_int.transferring) {
+ return;
+ }
+ mtx_lock(xfer->usb2_mtx);
+ /* call the USB transfer callback */
+ usb2_callback_ss_done_defer(xfer);
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_stop - stop an USB transfer
+ *
+ * NOTE: Calling this function more than one time will only
+ * result in a single transfer stop.
+ * NOTE: When this function returns it is not safe to free nor
+ * reuse any DMA buffers. See "usb2_transfer_drain()".
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_stop(struct usb2_xfer *xfer)
+{
+ struct usb2_pipe *pipe;
+
+ if (xfer == NULL) {
+ /* transfer is gone */
+ return;
+ }
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+
+ /* check if the USB transfer was ever opened */
+
+ if (!xfer->flags_int.open) {
+ /* nothing to do except clearing the "started" flag */
+ xfer->flags_int.started = 0;
+ return;
+ }
+ /* try to stop the current USB transfer */
+
+ mtx_lock(xfer->usb2_mtx);
+ xfer->error = USB_ERR_CANCELLED;/* override any previous error */
+ /*
+ * Clear "open" and "started" when both private and USB lock
+ * is locked so that we don't get a race updating "flags_int"
+ */
+ xfer->flags_int.open = 0;
+ xfer->flags_int.started = 0;
+
+ /*
+ * Check if we can cancel the USB transfer immediately.
+ */
+ if (xfer->flags_int.transferring) {
+ if (xfer->flags_int.can_cancel_immed &&
+ (!xfer->flags_int.did_close)) {
+ DPRINTF("close\n");
+ /*
+ * The following will lead to an USB_ERR_CANCELLED
+ * error code being passed to the USB callback.
+ */
+ (xfer->pipe->methods->close) (xfer);
+ /* only close once */
+ xfer->flags_int.did_close = 1;
+ } else {
+ /* need to wait for the next done callback */
+ }
+ } else {
+ DPRINTF("close\n");
+
+ /* close here and now */
+ (xfer->pipe->methods->close) (xfer);
+
+ /*
+ * Any additional DMA delay is done by
+ * "usb2_transfer_unsetup()".
+ */
+
+ /*
+ * Special case. Check if we need to restart a blocked
+ * pipe.
+ */
+ pipe = xfer->pipe;
+
+ /*
+ * If the current USB transfer is completing we need
+ * to start the next one:
+ */
+ if (pipe->pipe_q.curr == xfer) {
+ usb2_command_wrapper(&pipe->pipe_q, NULL);
+ }
+ }
+
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_pending
+ *
+ * This function will check if an USB transfer is pending which is a
+ * little bit complicated!
+ * Return values:
+ * 0: Not pending
+ * 1: Pending: The USB transfer will receive a callback in the future.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_transfer_pending(struct usb2_xfer *xfer)
+{
+ struct usb2_xfer_root *info;
+ struct usb2_xfer_queue *pq;
+
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+
+ if (xfer->flags_int.transferring) {
+ /* trivial case */
+ return (1);
+ }
+ mtx_lock(xfer->usb2_mtx);
+ if (xfer->wait_queue) {
+ /* we are waiting on a queue somewhere */
+ mtx_unlock(xfer->usb2_mtx);
+ return (1);
+ }
+ info = xfer->usb2_root;
+ pq = &info->done_q;
+
+ if (pq->curr == xfer) {
+ /* we are currently scheduled for callback */
+ mtx_unlock(xfer->usb2_mtx);
+ return (1);
+ }
+ /* we are not pending */
+ mtx_unlock(xfer->usb2_mtx);
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_drain
+ *
+ * This function will stop the USB transfer and wait for any
+ * additional BUS-DMA and HW-DMA operations to complete. Buffers that
+ * are loaded into DMA can safely be freed or reused after that this
+ * function has returned.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_drain(struct usb2_xfer *xfer)
+{
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
+ "usb2_transfer_drain can sleep!");
+
+ if (xfer == NULL) {
+ /* transfer is gone */
+ return;
+ }
+ if (xfer->priv_mtx != &Giant) {
+ mtx_assert(xfer->priv_mtx, MA_NOTOWNED);
+ }
+ mtx_lock(xfer->priv_mtx);
+
+ usb2_transfer_stop(xfer);
+
+ while (usb2_transfer_pending(xfer)) {
+ xfer->flags_int.draining = 1;
+ /*
+ * Wait until the current outstanding USB
+ * transfer is complete !
+ */
+ usb2_cv_wait(&xfer->usb2_root->cv_drain, xfer->priv_mtx);
+ }
+ mtx_unlock(xfer->priv_mtx);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_frame_data
+ *
+ * This function sets the pointer of the buffer that should
+ * loaded directly into DMA for the given USB frame. Passing "ptr"
+ * equal to NULL while the corresponding "frlength" is greater
+ * than zero gives undefined results!
+ *------------------------------------------------------------------------*/
+void
+usb2_set_frame_data(struct usb2_xfer *xfer, void *ptr, uint32_t frindex)
+{
+ /* set virtual address to load and length */
+ xfer->frbuffers[frindex].buffer = ptr;
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_frame_offset
+ *
+ * This function sets the frame data buffer offset relative to the beginning
+ * of the USB DMA buffer allocated for this USB transfer.
+ *------------------------------------------------------------------------*/
+void
+usb2_set_frame_offset(struct usb2_xfer *xfer, uint32_t offset,
+ uint32_t frindex)
+{
+ USB_ASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame "
+ "when the USB buffer is external!\n"));
+
+ /* set virtual address to load */
+ xfer->frbuffers[frindex].buffer =
+ USB_ADD_BYTES(xfer->local_buffer, offset);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_callback_proc - factored out code
+ *
+ * This function performs USB callbacks.
+ *------------------------------------------------------------------------*/
+static void
+usb2_callback_proc(struct usb2_proc_msg *_pm)
+{
+ struct usb2_done_msg *pm = (void *)_pm;
+ struct usb2_xfer_root *info = pm->usb2_root;
+
+ /* Change locking order */
+ mtx_unlock(info->usb2_mtx);
+
+ /*
+ * We exploit the fact that the mutex is the same for all
+ * callbacks that will be called from this thread:
+ */
+ mtx_lock(info->priv_mtx);
+ mtx_lock(info->usb2_mtx);
+
+ /* Continue where we lost track */
+ usb2_command_wrapper(&info->done_q,
+ info->done_q.curr);
+
+ mtx_unlock(info->priv_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_callback_ss_done_defer
+ *
+ * This function will defer the start, stop and done callback to the
+ * correct thread.
+ *------------------------------------------------------------------------*/
+static void
+usb2_callback_ss_done_defer(struct usb2_xfer *xfer)
+{
+ struct usb2_xfer_root *info = xfer->usb2_root;
+ struct usb2_xfer_queue *pq = &info->done_q;
+
+ if (!mtx_owned(xfer->usb2_mtx)) {
+ panic("%s: called unlocked!\n", __FUNCTION__);
+ }
+ if (pq->curr != xfer) {
+ usb2_transfer_enqueue(pq, xfer);
+ }
+ if (!pq->recurse_1) {
+
+ /*
+ * We have to postpone the callback due to the fact we
+ * will have a Lock Order Reversal, LOR, if we try to
+ * proceed !
+ */
+ if (usb2_proc_msignal(&info->done_p,
+ &info->done_m[0], &info->done_m[1])) {
+ /* ignore */
+ }
+ } else {
+ /* clear second recurse flag */
+ pq->recurse_2 = 0;
+ }
+ return;
+
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_callback_wrapper
+ *
+ * This is a wrapper for USB callbacks. This wrapper does some
+ * auto-magic things like figuring out if we can call the callback
+ * directly from the current context or if we need to wakeup the
+ * interrupt process.
+ *------------------------------------------------------------------------*/
+static void
+usb2_callback_wrapper(struct usb2_xfer_queue *pq)
+{
+ struct usb2_xfer *xfer = pq->curr;
+ struct usb2_xfer_root *info = xfer->usb2_root;
+
+ if (!mtx_owned(xfer->usb2_mtx)) {
+ panic("%s: called unlocked!\n", __FUNCTION__);
+ }
+ if (!mtx_owned(xfer->priv_mtx)) {
+ /*
+ * Cases that end up here:
+ *
+ * 5) HW interrupt done callback or other source.
+ */
+ DPRINTFN(3, "case 5\n");
+
+ /*
+ * We have to postpone the callback due to the fact we
+ * will have a Lock Order Reversal, LOR, if we try to
+ * proceed !
+ */
+ if (usb2_proc_msignal(&info->done_p,
+ &info->done_m[0], &info->done_m[1])) {
+ /* ignore */
+ }
+ return;
+ }
+ /*
+ * Cases that end up here:
+ *
+ * 1) We are starting a transfer
+ * 2) We are prematurely calling back a transfer
+ * 3) We are stopping a transfer
+ * 4) We are doing an ordinary callback
+ */
+ DPRINTFN(3, "case 1-4\n");
+ /* get next USB transfer in the queue */
+ info->done_q.curr = NULL;
+
+ mtx_unlock(xfer->usb2_mtx);
+ mtx_assert(xfer->usb2_mtx, MA_NOTOWNED);
+
+ /* set correct USB state for callback */
+ if (!xfer->flags_int.transferring) {
+ xfer->usb2_state = USB_ST_SETUP;
+ if (!xfer->flags_int.started) {
+ /* we got stopped before we even got started */
+ mtx_lock(xfer->usb2_mtx);
+ goto done;
+ }
+ } else {
+
+ if (usb2_callback_wrapper_sub(xfer)) {
+ /* the callback has been deferred */
+ mtx_lock(xfer->usb2_mtx);
+ goto done;
+ }
+ xfer->flags_int.transferring = 0;
+
+ if (xfer->error) {
+ xfer->usb2_state = USB_ST_ERROR;
+ } else {
+ /* set transferred state */
+ xfer->usb2_state = USB_ST_TRANSFERRED;
+
+ /* sync DMA memory, if any */
+ if (xfer->flags_int.bdma_enable &&
+ (!xfer->flags_int.bdma_no_post_sync)) {
+ usb2_bdma_post_sync(xfer);
+ }
+ }
+ }
+
+ /* call processing routine */
+ (xfer->callback) (xfer);
+
+ /* pickup the USB mutex again */
+ mtx_lock(xfer->usb2_mtx);
+
+ /*
+ * Check if we got started after that we got cancelled, but
+ * before we managed to do the callback. Check if we are
+ * draining.
+ */
+ if ((!xfer->flags_int.open) &&
+ (xfer->flags_int.started) &&
+ (xfer->usb2_state == USB_ST_ERROR)) {
+ /* try to loop, but not recursivly */
+ usb2_command_wrapper(&info->done_q, xfer);
+ return;
+ } else if (xfer->flags_int.draining &&
+ (!xfer->flags_int.transferring)) {
+ /* "usb2_transfer_drain()" is waiting for end of transfer */
+ xfer->flags_int.draining = 0;
+ usb2_cv_broadcast(&xfer->usb2_root->cv_drain);
+ }
+done:
+ /* do the next callback, if any */
+ usb2_command_wrapper(&info->done_q,
+ info->done_q.curr);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dma_delay_done_cb
+ *
+ * This function is called when the DMA delay has been exectuded, and
+ * will make sure that the callback is called to complete the USB
+ * transfer. This code path is ususally only used when there is an USB
+ * error like USB_ERR_CANCELLED.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dma_delay_done_cb(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ mtx_assert(xfer->usb2_mtx, MA_OWNED);
+
+ DPRINTFN(3, "Completed %p\n", xfer);
+
+ /* queue callback for execution, again */
+ usb2_transfer_done(xfer, 0);
+
+ mtx_unlock(xfer->usb2_mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_dequeue
+ *
+ * - This function is used to remove an USB transfer from a USB
+ * transfer queue.
+ *
+ * - This function can be called multiple times in a row.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_dequeue(struct usb2_xfer *xfer)
+{
+ struct usb2_xfer_queue *pq;
+
+ pq = xfer->wait_queue;
+ if (pq) {
+ TAILQ_REMOVE(&pq->head, xfer, wait_entry);
+ xfer->wait_queue = NULL;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_enqueue
+ *
+ * - This function is used to insert an USB transfer into a USB *
+ * transfer queue.
+ *
+ * - This function can be called multiple times in a row.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_enqueue(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer)
+{
+ /*
+ * Insert the USB transfer into the queue, if it is not
+ * already on a USB transfer queue:
+ */
+ if (xfer->wait_queue == NULL) {
+ xfer->wait_queue = pq;
+ TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_done
+ *
+ * - This function is used to remove an USB transfer from the busdma,
+ * pipe or interrupt queue.
+ *
+ * - This function is used to queue the USB transfer on the done
+ * queue.
+ *
+ * - This function is used to stop any USB transfer timeouts.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ struct usb2_xfer_queue *pq;
+
+ mtx_assert(xfer->usb2_mtx, MA_OWNED);
+
+ DPRINTF("err=%s\n", usb2_errstr(error));
+
+ /*
+ * If we are not transferring then just return.
+ * This can happen during transfer cancel.
+ */
+ if (!xfer->flags_int.transferring) {
+ DPRINTF("not transferring\n");
+ return;
+ }
+ /* only set transfer error if not already set */
+ if (!xfer->error) {
+ xfer->error = error;
+ }
+ /* stop any callouts */
+ usb2_callout_stop(&xfer->timeout_handle);
+
+ /*
+ * If we are waiting on a queue, just remove the USB transfer
+ * from the queue, if any. We should have the required locks
+ * locked to do the remove when this function is called.
+ */
+ usb2_transfer_dequeue(xfer);
+
+ if (mtx_owned(xfer->priv_mtx)) {
+ /*
+ * If the private USB lock is not locked, then we assume
+ * that the BUS-DMA load stage has been passed:
+ */
+ pq = &xfer->usb2_root->dma_q;
+
+ if (pq->curr == xfer) {
+ /* start the next BUS-DMA load, if any */
+ usb2_command_wrapper(pq, NULL);
+ }
+ }
+ /* keep some statistics */
+ if (xfer->error) {
+ xfer->udev->bus->stats_err.uds_requests
+ [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++;
+ } else {
+ xfer->udev->bus->stats_ok.uds_requests
+ [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++;
+ }
+
+ /* call the USB transfer callback */
+ usb2_callback_ss_done_defer(xfer);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_start_cb
+ *
+ * This function is called to start the USB transfer when
+ * "xfer->interval" is greater than zero, and and the endpoint type is
+ * BULK or CONTROL.
+ *------------------------------------------------------------------------*/
+static void
+usb2_transfer_start_cb(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+ struct usb2_pipe *pipe = xfer->pipe;
+
+ mtx_assert(xfer->usb2_mtx, MA_OWNED);
+
+ DPRINTF("start\n");
+
+ /* start the transfer */
+ (pipe->methods->start) (xfer);
+
+ /* check cancelability */
+ if (pipe->methods->start_is_cancelable) {
+ xfer->flags_int.can_cancel_immed = 1;
+ if (xfer->error) {
+ /* some error has happened */
+ usb2_transfer_done(xfer, 0);
+ }
+ } else {
+ xfer->flags_int.can_cancel_immed = 0;
+ }
+ mtx_unlock(xfer->usb2_mtx);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_set_stall
+ *
+ * This function is used to set the stall flag outside the
+ * callback. This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_set_stall(struct usb2_xfer *xfer)
+{
+ if (xfer == NULL) {
+ /* tearing down */
+ return;
+ }
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+
+ /* avoid any races by locking the USB mutex */
+ mtx_lock(xfer->usb2_mtx);
+
+ xfer->flags.stall_pipe = 1;
+
+ mtx_unlock(xfer->usb2_mtx);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_clear_stall
+ *
+ * This function is used to clear the stall flag outside the
+ * callback. This function is NULL safe.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_clear_stall(struct usb2_xfer *xfer)
+{
+ if (xfer == NULL) {
+ /* tearing down */
+ return;
+ }
+ mtx_assert(xfer->priv_mtx, MA_OWNED);
+
+ /* avoid any races by locking the USB mutex */
+ mtx_lock(xfer->usb2_mtx);
+
+ xfer->flags.stall_pipe = 0;
+
+ mtx_unlock(xfer->usb2_mtx);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pipe_start
+ *
+ * This function is used to add an USB transfer to the pipe transfer list.
+ *------------------------------------------------------------------------*/
+void
+usb2_pipe_start(struct usb2_xfer_queue *pq)
+{
+ struct usb2_pipe *pipe;
+ struct usb2_xfer *xfer;
+ uint8_t type;
+
+ xfer = pq->curr;
+ pipe = xfer->pipe;
+
+ mtx_assert(xfer->usb2_mtx, MA_OWNED);
+
+ /*
+ * If the pipe is already stalled we do nothing !
+ */
+ if (pipe->is_stalled) {
+ return;
+ }
+ /*
+ * Check if we are supposed to stall the pipe:
+ */
+ if (xfer->flags.stall_pipe) {
+ /* clear stall command */
+ xfer->flags.stall_pipe = 0;
+
+ /*
+ * Only stall BULK and INTERRUPT endpoints.
+ */
+ type = (pipe->edesc->bmAttributes & UE_XFERTYPE);
+ if ((type == UE_BULK) ||
+ (type == UE_INTERRUPT)) {
+ struct usb2_device *udev;
+ struct usb2_xfer_root *info;
+
+ udev = xfer->udev;
+ pipe->is_stalled = 1;
+
+ if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
+ (udev->bus->methods->set_stall) (
+ udev, NULL, pipe);
+ } else if (udev->default_xfer[1]) {
+ info = udev->default_xfer[1]->usb2_root;
+ if (usb2_proc_msignal(&info->done_p,
+ &udev->cs_msg[0], &udev->cs_msg[1])) {
+ /* ignore */
+ }
+ } else {
+ /* should not happen */
+ DPRINTFN(0, "No stall handler!\n");
+ }
+ /*
+ * We get started again when the stall is cleared!
+ */
+ return;
+ }
+ }
+ /* Set or clear stall complete - special case */
+ if (xfer->nframes == 0) {
+ /* we are complete */
+ xfer->aframes = 0;
+ usb2_transfer_done(xfer, 0);
+ return;
+ }
+ /*
+ * Handled cases:
+ *
+ * 1) Start the first transfer queued.
+ *
+ * 2) Re-start the current USB transfer.
+ */
+ /*
+ * Check if there should be any
+ * pre transfer start delay:
+ */
+ if (xfer->interval > 0) {
+ type = (pipe->edesc->bmAttributes & UE_XFERTYPE);
+ if ((type == UE_BULK) ||
+ (type == UE_CONTROL)) {
+ usb2_transfer_timeout_ms(xfer,
+ &usb2_transfer_start_cb,
+ xfer->interval);
+ return;
+ }
+ }
+ DPRINTF("start\n");
+
+ /* start USB transfer */
+ (pipe->methods->start) (xfer);
+
+ /* check cancelability */
+ if (pipe->methods->start_is_cancelable) {
+ xfer->flags_int.can_cancel_immed = 1;
+ if (xfer->error) {
+ /* some error has happened */
+ usb2_transfer_done(xfer, 0);
+ }
+ } else {
+ xfer->flags_int.can_cancel_immed = 0;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_timeout_ms
+ *
+ * This function is used to setup a timeout on the given USB
+ * transfer. If the timeout has been deferred the callback given by
+ * "cb" will get called after "ms" milliseconds.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_timeout_ms(struct usb2_xfer *xfer,
+ void (*cb) (void *arg), uint32_t ms)
+{
+ mtx_assert(xfer->usb2_mtx, MA_OWNED);
+
+ /* defer delay */
+ usb2_callout_reset(&xfer->timeout_handle,
+ USB_MS_TO_TICKS(ms), cb, xfer);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_callback_wrapper_sub
+ *
+ * - This function will update variables in an USB transfer after
+ * that the USB transfer is complete.
+ *
+ * - This function is used to start the next USB transfer on the
+ * pipe transfer queue, if any.
+ *
+ * NOTE: In some special cases the USB transfer will not be removed from
+ * the pipe queue, but remain first. To enforce USB transfer removal call
+ * this function passing the error code "USB_ERR_CANCELLED".
+ *
+ * Return values:
+ * 0: Success.
+ * Else: The callback has been deferred.
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_callback_wrapper_sub(struct usb2_xfer *xfer)
+{
+ struct usb2_pipe *pipe;
+ uint32_t x;
+
+ if ((!xfer->flags_int.open) &&
+ (!xfer->flags_int.did_close)) {
+ DPRINTF("close\n");
+ mtx_lock(xfer->usb2_mtx);
+ (xfer->pipe->methods->close) (xfer);
+ mtx_unlock(xfer->usb2_mtx);
+ /* only close once */
+ xfer->flags_int.did_close = 1;
+ return (1); /* wait for new callback */
+ }
+ /*
+ * If we have a non-hardware induced error we
+ * need to do the DMA delay!
+ */
+ if (((xfer->error == USB_ERR_CANCELLED) ||
+ (xfer->error == USB_ERR_TIMEOUT)) &&
+ (!xfer->flags_int.did_dma_delay)) {
+
+ uint32_t temp;
+
+ /* only delay once */
+ xfer->flags_int.did_dma_delay = 1;
+
+ /* we can not cancel this delay */
+ xfer->flags_int.can_cancel_immed = 0;
+
+ temp = usb2_get_dma_delay(xfer->udev->bus);
+
+ DPRINTFN(3, "DMA delay, %u ms, "
+ "on %p\n", temp, xfer);
+
+ if (temp != 0) {
+ mtx_lock(xfer->usb2_mtx);
+ usb2_transfer_timeout_ms(xfer,
+ &usb2_dma_delay_done_cb, temp);
+ mtx_unlock(xfer->usb2_mtx);
+ return (1); /* wait for new callback */
+ }
+ }
+ /* check actual number of frames */
+ if (xfer->aframes > xfer->nframes) {
+ if (xfer->error == 0) {
+ panic("%s: actual number of frames, %d, is "
+ "greater than initial number of frames, %d!\n",
+ __FUNCTION__, xfer->aframes, xfer->nframes);
+ } else {
+ /* just set some valid value */
+ xfer->aframes = xfer->nframes;
+ }
+ }
+ /* compute actual length */
+ xfer->actlen = 0;
+
+ for (x = 0; x != xfer->aframes; x++) {
+ xfer->actlen += xfer->frlengths[x];
+ }
+
+ /*
+ * Frames that were not transferred get zero actual length in
+ * case the USB device driver does not check the actual number
+ * of frames transferred, "xfer->aframes":
+ */
+ for (; x < xfer->nframes; x++) {
+ xfer->frlengths[x] = 0;
+ }
+
+ /* check actual length */
+ if (xfer->actlen > xfer->sumlen) {
+ if (xfer->error == 0) {
+ panic("%s: actual length, %d, is greater than "
+ "initial length, %d!\n",
+ __FUNCTION__, xfer->actlen, xfer->sumlen);
+ } else {
+ /* just set some valid value */
+ xfer->actlen = xfer->sumlen;
+ }
+ }
+ DPRINTFN(6, "xfer=%p pipe=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n",
+ xfer, xfer->pipe, xfer->error, xfer->actlen, xfer->sumlen,
+ xfer->aframes, xfer->nframes);
+
+ if (xfer->error) {
+ /* end of control transfer, if any */
+ xfer->flags_int.control_act = 0;
+
+ /* check if we should block the execution queue */
+ if ((xfer->error != USB_ERR_CANCELLED) &&
+ (xfer->flags.pipe_bof)) {
+ DPRINTFN(2, "xfer=%p: Block On Failure "
+ "on pipe=%p\n", xfer, xfer->pipe);
+ goto done;
+ }
+ } else {
+ /* check for short transfers */
+ if (xfer->actlen < xfer->sumlen) {
+
+ /* end of control transfer, if any */
+ xfer->flags_int.control_act = 0;
+
+ if (!xfer->flags_int.short_xfer_ok) {
+ xfer->error = USB_ERR_SHORT_XFER;
+ if (xfer->flags.pipe_bof) {
+ DPRINTFN(2, "xfer=%p: Block On Failure on "
+ "Short Transfer on pipe %p.\n",
+ xfer, xfer->pipe);
+ goto done;
+ }
+ }
+ } else {
+ /*
+ * Check if we are in the middle of a
+ * control transfer:
+ */
+ if (xfer->flags_int.control_act) {
+ DPRINTFN(5, "xfer=%p: Control transfer "
+ "active on pipe=%p\n", xfer, xfer->pipe);
+ goto done;
+ }
+ }
+ }
+
+ pipe = xfer->pipe;
+
+ /*
+ * If the current USB transfer is completing we need to start the
+ * next one:
+ */
+ mtx_lock(xfer->usb2_mtx);
+ if (pipe->pipe_q.curr == xfer) {
+ usb2_command_wrapper(&pipe->pipe_q, NULL);
+
+ if (pipe->pipe_q.curr || TAILQ_FIRST(&pipe->pipe_q.head)) {
+ /* there is another USB transfer waiting */
+ } else {
+ /* this is the last USB transfer */
+ /* clear isochronous sync flag */
+ xfer->pipe->is_synced = 0;
+ }
+ }
+ mtx_unlock(xfer->usb2_mtx);
+done:
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_command_wrapper
+ *
+ * This function is used to execute commands non-recursivly on an USB
+ * transfer.
+ *------------------------------------------------------------------------*/
+void
+usb2_command_wrapper(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer)
+{
+ if (xfer) {
+ /*
+ * If the transfer is not already processing,
+ * queue it!
+ */
+ if (pq->curr != xfer) {
+ usb2_transfer_enqueue(pq, xfer);
+ if (pq->curr != NULL) {
+ /* something is already processing */
+ DPRINTFN(6, "busy %p\n", pq->curr);
+ return;
+ }
+ }
+ } else {
+ /* Get next element in queue */
+ pq->curr = NULL;
+ }
+
+ if (!pq->recurse_1) {
+
+ do {
+
+ /* set both recurse flags */
+ pq->recurse_1 = 1;
+ pq->recurse_2 = 1;
+
+ if (pq->curr == NULL) {
+ xfer = TAILQ_FIRST(&pq->head);
+ if (xfer) {
+ TAILQ_REMOVE(&pq->head, xfer,
+ wait_entry);
+ xfer->wait_queue = NULL;
+ pq->curr = xfer;
+ } else {
+ break;
+ }
+ }
+ DPRINTFN(6, "cb %p (enter)\n", pq->curr);
+ (pq->command) (pq);
+ DPRINTFN(6, "cb %p (leave)\n", pq->curr);
+
+ } while (!pq->recurse_2);
+
+ /* clear first recurse flag */
+ pq->recurse_1 = 0;
+
+ } else {
+ /* clear second recurse flag */
+ pq->recurse_2 = 0;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_default_transfer_setup
+ *
+ * This function is used to setup the default USB control endpoint
+ * transfer.
+ *------------------------------------------------------------------------*/
+void
+usb2_default_transfer_setup(struct usb2_device *udev)
+{
+ struct usb2_xfer *xfer;
+ uint8_t no_resetup;
+ uint8_t iface_index;
+
+repeat:
+
+ xfer = udev->default_xfer[0];
+ if (xfer) {
+ mtx_lock(xfer->priv_mtx);
+ no_resetup =
+ ((xfer->address == udev->address) &&
+ (udev->default_ep_desc.wMaxPacketSize[0] ==
+ udev->ddesc.bMaxPacketSize));
+ if (udev->flags.usb2_mode == USB_MODE_DEVICE) {
+ if (no_resetup) {
+ /*
+ * NOTE: checking "xfer->address" and
+ * starting the USB transfer must be
+ * atomic!
+ */
+ usb2_transfer_start(xfer);
+ }
+ }
+ mtx_unlock(xfer->priv_mtx);
+ } else {
+ no_resetup = 0;
+ }
+
+ if (no_resetup) {
+ /*
+ * All parameters are exactly the same like before.
+ * Just return.
+ */
+ return;
+ }
+ /*
+ * Update wMaxPacketSize for the default control endpoint:
+ */
+ udev->default_ep_desc.wMaxPacketSize[0] =
+ udev->ddesc.bMaxPacketSize;
+
+ /*
+ * Unsetup any existing USB transfer:
+ */
+ usb2_transfer_unsetup(udev->default_xfer, USB_DEFAULT_XFER_MAX);
+
+ /*
+ * Try to setup a new USB transfer for the
+ * default control endpoint:
+ */
+ iface_index = 0;
+ if (usb2_transfer_setup(udev, &iface_index,
+ udev->default_xfer, usb2_control_ep_cfg, USB_DEFAULT_XFER_MAX, NULL,
+ udev->default_mtx)) {
+ DPRINTFN(0, "could not setup default "
+ "USB transfer!\n");
+ } else {
+ goto repeat;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_clear_data_toggle - factored out code
+ *
+ * NOTE: the intention of this function is not to reset the hardware
+ * data toggle.
+ *------------------------------------------------------------------------*/
+void
+usb2_clear_data_toggle(struct usb2_device *udev, struct usb2_pipe *pipe)
+{
+ DPRINTFN(5, "udev=%p pipe=%p\n", udev, pipe);
+
+ mtx_lock(&udev->bus->mtx);
+ pipe->toggle_next = 0;
+ mtx_unlock(&udev->bus->mtx);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_clear_stall_callback - factored out clear stall callback
+ *
+ * Input parameters:
+ * xfer1: Clear Stall Control Transfer
+ * xfer2: Stalled USB Transfer
+ *
+ * This function is NULL safe.
+ *
+ * Return values:
+ * 0: In progress
+ * Else: Finished
+ *
+ * Clear stall config example:
+ *
+ * static const struct usb2_config my_clearstall = {
+ * .type = UE_CONTROL,
+ * .endpoint = 0,
+ * .direction = UE_DIR_ANY,
+ * .interval = 50, //50 milliseconds
+ * .bufsize = sizeof(struct usb2_device_request),
+ * .mh.timeout = 1000, //1.000 seconds
+ * .mh.flags = { },
+ * .mh.callback = &my_clear_stall_callback, // **
+ * };
+ *
+ * ** "my_clear_stall_callback" calls "usb2_clear_stall_callback"
+ * passing the correct parameters.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_clear_stall_callback(struct usb2_xfer *xfer1,
+ struct usb2_xfer *xfer2)
+{
+ struct usb2_device_request req;
+
+ if (xfer2 == NULL) {
+ /* looks like we are tearing down */
+ DPRINTF("NULL input parameter\n");
+ return (0);
+ }
+ mtx_assert(xfer1->priv_mtx, MA_OWNED);
+ mtx_assert(xfer2->priv_mtx, MA_OWNED);
+
+ switch (USB_GET_STATE(xfer1)) {
+ case USB_ST_SETUP:
+
+ /*
+ * pre-clear the data toggle to DATA0 ("umass.c" and
+ * "ata-usb.c" depends on this)
+ */
+
+ usb2_clear_data_toggle(xfer2->udev, xfer2->pipe);
+
+ /* setup a clear-stall packet */
+
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ req.wIndex[0] = xfer2->pipe->edesc->bEndpointAddress;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ /*
+ * "usb2_transfer_setup_sub()" will ensure that
+ * we have sufficient room in the buffer for
+ * the request structure!
+ */
+
+ /* copy in the transfer */
+
+ usb2_copy_in(xfer1->frbuffers, 0, &req, sizeof(req));
+
+ /* set length */
+ xfer1->frlengths[0] = sizeof(req);
+ xfer1->nframes = 1;
+
+ usb2_start_hardware(xfer1);
+ return (0);
+
+ case USB_ST_TRANSFERRED:
+ break;
+
+ default: /* Error */
+ if (xfer1->error == USB_ERR_CANCELLED) {
+ return (0);
+ }
+ break;
+ }
+ return (1); /* Clear Stall Finished */
+}
+
+#if (USB_NO_POLL == 0)
+
+/*------------------------------------------------------------------------*
+ * usb2_callout_poll
+ *------------------------------------------------------------------------*/
+static void
+usb2_callout_poll(struct usb2_xfer *xfer)
+{
+ struct usb2_callout *co;
+ void (*cb) (void *);
+ void *arg;
+ struct mtx *mtx;
+ uint32_t delta;
+
+ if (xfer == NULL) {
+ return;
+ }
+ co = &xfer->timeout_handle;
+
+#if __FreeBSD_version >= 800000
+ mtx = (void *)(co->co.c_lock);
+#else
+ mtx = co->co.c_mtx;
+#endif
+ mtx_lock(mtx);
+
+ if (usb2_callout_pending(co)) {
+ delta = ticks - co->co.c_time;
+ if (!(delta & 0x80000000)) {
+
+ cb = co->co.c_func;
+ arg = co->co.c_arg;
+
+ /* timed out */
+ usb2_callout_stop(co);
+
+ (cb) (arg);
+
+ /* the callback should drop the mutex */
+ } else {
+ mtx_unlock(mtx);
+ }
+ } else {
+ mtx_unlock(mtx);
+ }
+ return;
+}
+
+
+/*------------------------------------------------------------------------*
+ * usb2_do_poll
+ *
+ * This function is called from keyboard driver when in polling
+ * mode.
+ *------------------------------------------------------------------------*/
+void
+usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max)
+{
+ struct usb2_xfer *xfer;
+ struct usb2_xfer_root *usb2_root;
+ struct usb2_device *udev;
+ struct usb2_proc_msg *pm;
+ uint32_t to;
+ uint16_t n;
+
+ /* compute system tick delay */
+ to = ((uint32_t)(1000000)) / ((uint32_t)(hz));
+ DELAY(to);
+ atomic_add_int((volatile int *)&ticks, 1);
+
+ for (n = 0; n != max; n++) {
+ xfer = ppxfer[n];
+ if (xfer) {
+ usb2_root = xfer->usb2_root;
+ udev = xfer->udev;
+
+ /*
+ * Poll hardware - signal that we are polling by
+ * locking the private mutex:
+ */
+ mtx_lock(xfer->priv_mtx);
+ (udev->bus->methods->do_poll) (udev->bus);
+ mtx_unlock(xfer->priv_mtx);
+
+ /* poll clear stall start */
+ mtx_lock(xfer->usb2_mtx);
+ pm = &udev->cs_msg[0].hdr;
+ (pm->pm_callback) (pm);
+ mtx_unlock(xfer->usb2_mtx);
+
+ if (udev->default_xfer[1]) {
+
+ /* poll timeout */
+ usb2_callout_poll(udev->default_xfer[1]);
+
+ /* poll clear stall done thread */
+ mtx_lock(xfer->usb2_mtx);
+ pm = &udev->default_xfer[1]->
+ usb2_root->done_m[0].hdr;
+ (pm->pm_callback) (pm);
+ mtx_unlock(xfer->usb2_mtx);
+ }
+ /* poll timeout */
+ usb2_callout_poll(xfer);
+
+ /* poll done thread */
+ mtx_lock(xfer->usb2_mtx);
+ pm = &usb2_root->done_m[0].hdr;
+ (pm->pm_callback) (pm);
+ mtx_unlock(xfer->usb2_mtx);
+ }
+ }
+ return;
+}
+
+#else
+
+void
+usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max)
+{
+ /* polling not supported */
+ return;
+}
+
+#endif
diff --git a/sys/dev/usb2/core/usb2_transfer.h b/sys/dev/usb2/core/usb2_transfer.h
new file mode 100644
index 000000000000..83920e5fc444
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_transfer.h
@@ -0,0 +1,123 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_TRANSFER_H_
+#define _USB2_TRANSFER_H_
+
+/*
+ * The following structure defines the messages that is used to signal
+ * the "done_p" USB process.
+ */
+struct usb2_done_msg {
+ struct usb2_proc_msg hdr;
+ struct usb2_xfer_root *usb2_root;
+};
+
+/*
+ * The following structure is used to keep information about memory
+ * that should be automatically freed at the moment all USB transfers
+ * have been freed.
+ */
+struct usb2_xfer_root {
+ struct usb2_xfer_queue dma_q;
+ struct usb2_xfer_queue done_q;
+ struct usb2_done_msg done_m[2];
+ struct cv cv_drain;
+
+ struct usb2_dma_parent_tag dma_parent_tag;
+
+ struct usb2_process done_p;
+ void *memory_base;
+ struct mtx *priv_mtx;
+ struct mtx *usb2_mtx;
+ struct usb2_page_cache *dma_page_cache_start;
+ struct usb2_page_cache *dma_page_cache_end;
+ struct usb2_page_cache *xfer_page_cache_start;
+ struct usb2_page_cache *xfer_page_cache_end;
+ struct usb2_bus *bus;
+
+ uint32_t memory_size;
+ uint32_t setup_refcount;
+ uint32_t page_size;
+ uint32_t dma_nframes; /* number of page caches to load */
+ uint32_t dma_currframe; /* currect page cache number */
+ uint32_t dma_frlength_0; /* length of page cache zero */
+ uint8_t dma_error; /* set if virtual memory could not be
+ * loaded */
+ uint8_t done_sleep; /* set if done thread is sleeping */
+};
+
+/*
+ * The following structure is used when setting up an array of USB
+ * transfers.
+ */
+struct usb2_setup_params {
+ struct usb2_dma_tag *dma_tag_p;
+ struct usb2_page *dma_page_ptr;
+ struct usb2_page_cache *dma_page_cache_ptr; /* these will be
+ * auto-freed */
+ struct usb2_page_cache *xfer_page_cache_ptr; /* these will not be
+ * auto-freed */
+ struct usb2_device *udev;
+ struct usb2_xfer *curr_xfer;
+ const struct usb2_config *curr_setup;
+ const struct usb2_config_sub *curr_setup_sub;
+ const struct usb2_pipe_methods *methods;
+ void *buf;
+ uint32_t *xfer_length_ptr;
+
+ uint32_t size[7];
+ uint32_t bufsize;
+ uint32_t bufsize_max;
+ uint32_t hc_max_frame_size;
+
+ uint16_t hc_max_packet_size;
+ uint8_t hc_max_packet_count;
+ uint8_t speed;
+ uint8_t dma_tag_max;
+ usb2_error_t err;
+};
+
+/* function prototypes */
+
+uint8_t usb2_transfer_pending(struct usb2_xfer *xfer);
+uint8_t usb2_transfer_setup_sub_malloc(struct usb2_setup_params *parm, struct usb2_page_cache **ppc, uint32_t size, uint32_t align, uint32_t count);
+void usb2_command_wrapper(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer);
+void usb2_pipe_enter(struct usb2_xfer *xfer);
+void usb2_pipe_start(struct usb2_xfer_queue *pq);
+void usb2_transfer_dequeue(struct usb2_xfer *xfer);
+void usb2_transfer_done(struct usb2_xfer *xfer, usb2_error_t error);
+void usb2_transfer_enqueue(struct usb2_xfer_queue *pq, struct usb2_xfer *xfer);
+void usb2_transfer_setup_sub(struct usb2_setup_params *parm);
+void usb2_default_transfer_setup(struct usb2_device *udev);
+void usb2_clear_data_toggle(struct usb2_device *udev, struct usb2_pipe *pipe);
+void usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max);
+usb2_callback_t usb2_do_request_callback;
+usb2_callback_t usb2_handle_request_callback;
+usb2_callback_t usb2_do_clear_stall_callback;
+void usb2_transfer_timeout_ms(struct usb2_xfer *xfer, void (*cb) (void *arg), uint32_t ms);
+
+#endif /* _USB2_TRANSFER_H_ */
diff --git a/sys/dev/usb2/core/usb2_util.c b/sys/dev/usb2/core/usb2_util.c
new file mode 100644
index 000000000000..d944e441abb7
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_util.c
@@ -0,0 +1,354 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#include <dev/usb2/include/usb2_defs.h>
+#include <dev/usb2/include/usb2_mfunc.h>
+#include <dev/usb2/include/usb2_error.h>
+#include <dev/usb2/include/usb2_standard.h>
+
+#include <dev/usb2/core/usb2_core.h>
+#include <dev/usb2/core/usb2_util.h>
+#include <dev/usb2/core/usb2_process.h>
+#include <dev/usb2/core/usb2_device.h>
+#include <dev/usb2/core/usb2_request.h>
+#include <dev/usb2/core/usb2_busdma.h>
+
+#include <dev/usb2/controller/usb2_controller.h>
+#include <dev/usb2/controller/usb2_bus.h>
+
+/* function prototypes */
+#if (USB_USE_CONDVAR == 0)
+static int usb2_msleep(void *chan, struct mtx *mtx, int priority, const char *wmesg, int timo);
+
+#endif
+
+/*------------------------------------------------------------------------*
+ * device_delete_all_children - delete all children of a device
+ *------------------------------------------------------------------------*/
+int
+device_delete_all_children(device_t dev)
+{
+ device_t *devlist;
+ int devcount;
+ int error;
+
+ error = device_get_children(dev, &devlist, &devcount);
+ if (error == 0) {
+ while (devcount-- > 0) {
+ error = device_delete_child(dev, devlist[devcount]);
+ if (error) {
+ break;
+ }
+ }
+ free(devlist, M_TEMP);
+ }
+ return (error);
+}
+
+/*------------------------------------------------------------------------*
+ * device_set_usb2_desc
+ *
+ * This function can be called at probe or attach to set the USB
+ * device supplied textual description for the given device.
+ *------------------------------------------------------------------------*/
+void
+device_set_usb2_desc(device_t dev)
+{
+ struct usb2_attach_arg *uaa;
+ struct usb2_device *udev;
+ struct usb2_interface *iface;
+ char *temp_p;
+ usb2_error_t err;
+
+ if (dev == NULL) {
+ /* should not happen */
+ return;
+ }
+ uaa = device_get_ivars(dev);
+ if (uaa == NULL) {
+ /* can happend if called at the wrong time */
+ return;
+ }
+ udev = uaa->device;
+ iface = uaa->iface;
+
+ if ((iface == NULL) ||
+ (iface->idesc == NULL) ||
+ (iface->idesc->iInterface == 0)) {
+ err = USB_ERR_INVAL;
+ } else {
+ err = 0;
+ }
+
+ temp_p = (char *)udev->bus->scratch[0].data;
+
+ if (!err) {
+ /* try to get the interface string ! */
+ err = usb2_req_get_string_any
+ (udev, NULL, temp_p,
+ sizeof(udev->bus->scratch), iface->idesc->iInterface);
+ }
+ if (err) {
+ /* use default description */
+ usb2_devinfo(udev, temp_p,
+ sizeof(udev->bus->scratch));
+ }
+ device_set_desc_copy(dev, temp_p);
+ device_printf(dev, "<%s> on %s\n", temp_p,
+ device_get_nameunit(udev->bus->bdev));
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pause_mtx - factored out code
+ *
+ * This function will delay the code by the passed number of
+ * milliseconds. The passed mutex "mtx" will be dropped while waiting,
+ * if "mtx" is not NULL. The number of milliseconds per second is 1024
+ * for sake of optimisation.
+ *------------------------------------------------------------------------*/
+void
+usb2_pause_mtx(struct mtx *mtx, uint32_t ms)
+{
+ if (cold) {
+ ms = (ms + 1) * 1024;
+ DELAY(ms);
+
+ } else {
+
+ ms = USB_MS_TO_TICKS(ms);
+ /*
+ * Add one to the number of ticks so that we don't return
+ * too early!
+ */
+ ms++;
+
+ if (mtx != NULL)
+ mtx_unlock(mtx);
+
+ if (pause("USBWAIT", ms)) {
+ /* ignore */
+ }
+ if (mtx != NULL)
+ mtx_lock(mtx);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_printBCD
+ *
+ * This function will print the version number "bcd" to the string
+ * pointed to by "p" having a maximum length of "p_len" bytes
+ * including the terminating zero.
+ *------------------------------------------------------------------------*/
+void
+usb2_printBCD(char *p, uint16_t p_len, uint16_t bcd)
+{
+ if (snprintf(p, p_len, "%x.%02x", bcd >> 8, bcd & 0xff)) {
+ /* ignore any errors */
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_trim_spaces
+ *
+ * This function removes spaces at the beginning and the end of the string
+ * pointed to by the "p" argument.
+ *------------------------------------------------------------------------*/
+void
+usb2_trim_spaces(char *p)
+{
+ char *q;
+ char *e;
+
+ if (p == NULL)
+ return;
+ q = e = p;
+ while (*q == ' ') /* skip leading spaces */
+ q++;
+ while ((*p = *q++)) /* copy string */
+ if (*p++ != ' ') /* remember last non-space */
+ e = p;
+ *e = 0; /* kill trailing spaces */
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_get_devid
+ *
+ * This function returns the USB Vendor and Product ID like a 32-bit
+ * unsigned integer.
+ *------------------------------------------------------------------------*/
+uint32_t
+usb2_get_devid(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ return ((uaa->info.idVendor << 16) | (uaa->info.idProduct));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_make_str_desc - convert an ASCII string into a UNICODE string
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_make_str_desc(void *ptr, uint16_t max_len, const char *s)
+{
+ struct usb2_string_descriptor *p = ptr;
+ uint8_t totlen;
+ int j;
+
+ if (max_len < 2) {
+ /* invalid length */
+ return (0);
+ }
+ max_len = ((max_len / 2) - 1);
+
+ j = strlen(s);
+
+ if (j < 0) {
+ j = 0;
+ }
+ if (j > 126) {
+ j = 126;
+ }
+ if (max_len > j) {
+ max_len = j;
+ }
+ totlen = (max_len + 1) * 2;
+
+ p->bLength = totlen;
+ p->bDescriptorType = UDESC_STRING;
+
+ while (max_len--) {
+ USETW2(p->bString[max_len], 0, s[max_len]);
+ }
+ return (totlen);
+}
+
+#if (USB_USE_CONDVAR == 0)
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_init - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_init(struct cv *cv, const char *desc)
+{
+ cv_init(cv, desc);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_destroy - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_destroy(struct cv *cv)
+{
+ cv_destroy(cv);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_wait - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_wait(struct cv *cv, struct mtx *mtx)
+{
+ int err;
+
+ err = usb2_msleep(cv, mtx, 0, cv_wmesg(cv), 0);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_wait_sig - wrapper function
+ *------------------------------------------------------------------------*/
+int
+usb2_cv_wait_sig(struct cv *cv, struct mtx *mtx)
+{
+ int err;
+
+ err = usb2_msleep(cv, mtx, PCATCH, cv_wmesg(cv), 0);
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_timedwait - wrapper function
+ *------------------------------------------------------------------------*/
+int
+usb2_cv_timedwait(struct cv *cv, struct mtx *mtx, int timo)
+{
+ int err;
+
+ if (timo == 0)
+ timo = 1; /* zero means no timeout */
+ err = usb2_msleep(cv, mtx, 0, cv_wmesg(cv), timo);
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_signal - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_signal(struct cv *cv)
+{
+ wakeup_one(cv);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_broadcast - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_broadcast(struct cv *cv)
+{
+ wakeup(cv);
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_msleep - wrapper function
+ *------------------------------------------------------------------------*/
+static int
+usb2_msleep(void *chan, struct mtx *mtx, int priority, const char *wmesg,
+ int timo)
+{
+ int err;
+
+ if (mtx == &Giant) {
+ err = tsleep(chan, priority, wmesg, timo);
+ } else {
+#ifdef mtx_sleep
+ err = mtx_sleep(chan, mtx, priority, wmesg, timo);
+#else
+ err = msleep(chan, mtx, priority, wmesg, timo);
+#endif
+ }
+ return (err);
+}
+
+#endif
diff --git a/sys/dev/usb2/core/usb2_util.h b/sys/dev/usb2/core/usb2_util.h
new file mode 100644
index 000000000000..8bbae8c5d32c
--- /dev/null
+++ b/sys/dev/usb2/core/usb2_util.h
@@ -0,0 +1,57 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _USB2_UTIL_H_
+#define _USB2_UTIL_H_
+
+int device_delete_all_children(device_t dev);
+uint32_t usb2_get_devid(device_t dev);
+uint8_t usb2_make_str_desc(void *ptr, uint16_t max_len, const char *s);
+void device_set_usb2_desc(device_t dev);
+void usb2_pause_mtx(struct mtx *mtx, uint32_t ms);
+void usb2_printBCD(char *p, uint16_t p_len, uint16_t bcd);
+void usb2_trim_spaces(char *p);
+
+#if (USB_USE_CONDVAR == 0)
+void usb2_cv_init(struct cv *cv, const char *desc);
+void usb2_cv_destroy(struct cv *cv);
+void usb2_cv_wait(struct cv *cv, struct mtx *mtx);
+int usb2_cv_wait_sig(struct cv *cv, struct mtx *mtx);
+int usb2_cv_timedwait(struct cv *cv, struct mtx *mtx, int timo);
+void usb2_cv_signal(struct cv *cv);
+void usb2_cv_broadcast(struct cv *cv);
+
+#else
+#define usb2_cv_init cv_init
+#define usb2_cv_destroy cv_destroy
+#define usb2_cv_wait cv_wait
+#define usb2_cv_wait_sig cv_wait_sig
+#define usb2_cv_timedwait cv_timedwait
+#define usb2_cv_signal cv_signal
+#define usb2_cv_broadcast cv_broadcast
+#endif
+
+#endif /* _USB2_UTIL_H_ */
diff --git a/sys/dev/usb2/core/usbdevs b/sys/dev/usb2/core/usbdevs
new file mode 100644
index 000000000000..8fa1cf1f14c2
--- /dev/null
+++ b/sys/dev/usb2/core/usbdevs
@@ -0,0 +1,2482 @@
+$FreeBSD$
+/* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */
+
+/*-
+ * Copyright (c) 1998-2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * List of known USB vendors
+ *
+ * USB.org publishes a VID list of USB-IF member companies at
+ * http://www.usb.org/developers/tools
+ * Note that it does not show companies that have obtained a Vendor ID
+ * without becoming full members.
+ *
+ * Please note that these IDs do not do anything. Adding an ID here and
+ * regenerating the usbdevs.h and usbdevs_data.h only makes a symbolic name
+ * available to the source code and does not change any functionality, nor
+ * does it make your device available to a specific driver.
+ * It will however make the descriptive string available if a device does not
+ * provide the string itself.
+ *
+ * After adding a vendor ID VNDR and a product ID PRDCT you will have the
+ * following extra defines:
+ * #define USB_VENDOR_VNDR 0x????
+ * #define USB_PRODUCT_VNDR_PRDCT 0x????
+ *
+ * You may have to add these defines to the respective probe routines to
+ * make the device recognised by the appropriate device driver.
+ */
+
+vendor UNKNOWN1 0x0053 Unknown vendor
+vendor UNKNOWN2 0x0105 Unknown vendor
+vendor EGALAX2 0x0123 eGalax, Inc.
+vendor HUMAX 0x02ad HUMAX
+vendor LTS 0x0386 LTS
+vendor BWCT 0x03da Bernd Walter Computer Technology
+vendor AOX 0x03e8 AOX
+vendor THESYS 0x03e9 Thesys
+vendor DATABROADCAST 0x03ea Data Broadcasting
+vendor ATMEL 0x03eb Atmel
+vendor IWATSU 0x03ec Iwatsu America
+vendor MITSUMI 0x03ee Mitsumi
+vendor HP 0x03f0 Hewlett Packard
+vendor GENOA 0x03f1 Genoa
+vendor OAK 0x03f2 Oak
+vendor ADAPTEC 0x03f3 Adaptec
+vendor DIEBOLD 0x03f4 Diebold
+vendor SIEMENSELECTRO 0x03f5 Siemens Electromechanical
+vendor EPSONIMAGING 0x03f8 Epson Imaging
+vendor KEYTRONIC 0x03f9 KeyTronic
+vendor OPTI 0x03fb OPTi
+vendor ELITEGROUP 0x03fc Elitegroup
+vendor XILINX 0x03fd Xilinx
+vendor FARALLON 0x03fe Farallon Communications
+vendor NATIONAL 0x0400 National Semiconductor
+vendor NATIONALREG 0x0401 National Registry
+vendor ACERLABS 0x0402 Acer Labs
+vendor FTDI 0x0403 Future Technology Devices
+vendor NCR 0x0404 NCR
+vendor SYNOPSYS2 0x0405 Synopsys
+vendor FUJITSUICL 0x0406 Fujitsu-ICL
+vendor FUJITSU2 0x0407 Fujitsu Personal Systems
+vendor QUANTA 0x0408 Quanta
+vendor NEC 0x0409 NEC
+vendor KODAK 0x040a Eastman Kodak
+vendor WELTREND 0x040b Weltrend
+vendor VIA 0x040d VIA
+vendor MCCI 0x040e MCCI
+vendor MELCO 0x0411 Melco
+vendor LEADTEK 0x0413 Leadtek
+vendor WINBOND 0x0416 Winbond
+vendor PHOENIX 0x041a Phoenix
+vendor CREATIVE 0x041e Creative Labs
+vendor NOKIA 0x0421 Nokia
+vendor ADI 0x0422 ADI Systems
+vendor CATC 0x0423 Computer Access Technology
+vendor SMC2 0x0424 Standard Microsystems
+vendor MOTOROLA_HK 0x0425 Motorola HK
+vendor GRAVIS 0x0428 Advanced Gravis Computer
+vendor CIRRUSLOGIC 0x0429 Cirrus Logic
+vendor INNOVATIVE 0x042c Innovative Semiconductors
+vendor MOLEX 0x042f Molex
+vendor SUN 0x0430 Sun Microsystems
+vendor UNISYS 0x0432 Unisys
+vendor TAUGA 0x0436 Taugagreining HF
+vendor AMD 0x0438 Advanced Micro Devices
+vendor LEXMARK 0x043d Lexmark International
+vendor LG 0x043e LG Electronics
+vendor NANAO 0x0440 NANAO
+vendor GATEWAY 0x0443 Gateway 2000
+vendor NMB 0x0446 NMB
+vendor ALPS 0x044e Alps Electric
+vendor THRUST 0x044f Thrustmaster
+vendor TI 0x0451 Texas Instruments
+vendor ANALOGDEVICES 0x0456 Analog Devices
+vendor SIS 0x0457 Silicon Integrated Systems Corp.
+vendor KYE 0x0458 KYE Systems
+vendor DIAMOND2 0x045a Diamond (Supra)
+vendor RENESAS 0x045b Renesas
+vendor MICROSOFT 0x045e Microsoft
+vendor PRIMAX 0x0461 Primax Electronics
+vendor MGE 0x0463 MGE UPS Systems
+vendor AMP 0x0464 AMP
+vendor CHERRY 0x046a Cherry Mikroschalter
+vendor MEGATRENDS 0x046b American Megatrends
+vendor LOGITECH 0x046d Logitech
+vendor BTC 0x046e Behavior Tech. Computer
+vendor PHILIPS 0x0471 Philips
+vendor SUN2 0x0472 Sun Microsystems (offical)
+vendor SANYO 0x0474 Sanyo Electric
+vendor SEAGATE 0x0477 Seagate
+vendor CONNECTIX 0x0478 Connectix
+vendor SEMTECH 0x047a Semtech
+vendor KENSINGTON 0x047d Kensington
+vendor LUCENT 0x047e Lucent
+vendor PLANTRONICS 0x047f Plantronics
+vendor KYOCERA 0x0482 Kyocera Wireless Corp.
+vendor STMICRO 0x0483 STMicroelectronics
+vendor FOXCONN 0x0489 Foxconn
+vendor YAMAHA 0x0499 YAMAHA
+vendor COMPAQ 0x049f Compaq
+vendor HITACHI 0x04a4 Hitachi
+vendor ACERP 0x04a5 Acer Peripherals
+vendor DAVICOM 0x04a6 Davicom
+vendor VISIONEER 0x04a7 Visioneer
+vendor CANON 0x04a9 Canon
+vendor NIKON 0x04b0 Nikon
+vendor PAN 0x04b1 Pan International
+vendor IBM 0x04b3 IBM
+vendor CYPRESS 0x04b4 Cypress Semiconductor
+vendor ROHM 0x04b5 ROHM
+vendor COMPAL 0x04b7 Compal
+vendor EPSON 0x04b8 Seiko Epson
+vendor RAINBOW 0x04b9 Rainbow Technologies
+vendor IODATA 0x04bb I-O Data
+vendor TDK 0x04bf TDK
+vendor 3COMUSR 0x04c1 U.S. Robotics
+vendor METHODE 0x04c2 Methode Electronics Far East
+vendor MAXISWITCH 0x04c3 Maxi Switch
+vendor LOCKHEEDMER 0x04c4 Lockheed Martin Energy Research
+vendor FUJITSU 0x04c5 Fujitsu
+vendor TOSHIBAAM 0x04c6 Toshiba America
+vendor MICROMACRO 0x04c7 Micro Macro Technologies
+vendor KONICA 0x04c8 Konica
+vendor LITEON 0x04ca Lite-On Technology
+vendor FUJIPHOTO 0x04cb Fuji Photo Film
+vendor PHILIPSSEMI 0x04cc Philips Semiconductors
+vendor TATUNG 0x04cd Tatung Co. Of America
+vendor SCANLOGIC 0x04ce ScanLogic
+vendor MYSON 0x04cf Myson Technology
+vendor DIGI2 0x04d0 Digi
+vendor ITTCANON 0x04d1 ITT Canon
+vendor ALTEC 0x04d2 Altec Lansing
+vendor LSI 0x04d4 LSI
+vendor MENTORGRAPHICS 0x04d6 Mentor Graphics
+vendor ITUNERNET 0x04d8 I-Tuner Networks
+vendor HOLTEK 0x04d9 Holtek Semiconductor, Inc.
+vendor PANASONIC 0x04da Panasonic (Matsushita)
+vendor HUANHSIN 0x04dc Huan Hsin
+vendor SHARP 0x04dd Sharp
+vendor IIYAMA 0x04e1 Iiyama
+vendor SHUTTLE 0x04e6 Shuttle Technology
+vendor ELO 0x04e7 Elo TouchSystems
+vendor SAMSUNG 0x04e8 Samsung Electronics
+vendor NORTHSTAR 0x04eb Northstar
+vendor TOKYOELECTRON 0x04ec Tokyo Electron
+vendor ANNABOOKS 0x04ed Annabooks
+vendor JVC 0x04f1 JVC
+vendor CHICONY 0x04f2 Chicony Electronics
+vendor ELAN 0x04f3 Elan
+vendor NEWNEX 0x04f7 Newnex
+vendor BROTHER 0x04f9 Brother Industries
+vendor DALLAS 0x04fa Dallas Semiconductor
+vendor SUNPLUS 0x04fc Sunplus
+vendor PFU 0x04fe PFU
+vendor FUJIKURA 0x0501 Fujikura/DDK
+vendor ACER 0x0502 Acer
+vendor 3COM 0x0506 3Com
+vendor HOSIDEN 0x0507 Hosiden Corporation
+vendor AZTECH 0x0509 Aztech Systems
+vendor BELKIN 0x050d Belkin Components
+vendor KAWATSU 0x050f Kawatsu Semiconductor
+vendor FCI 0x0514 FCI
+vendor LONGWELL 0x0516 Longwell
+vendor COMPOSITE 0x0518 Composite
+vendor STAR 0x0519 Star Micronics
+vendor APC 0x051d American Power Conversion
+vendor SCIATLANTA 0x051e Scientific Atlanta
+vendor TSM 0x0520 TSM
+vendor CONNECTEK 0x0522 Advanced Connectek USA
+vendor NETCHIP 0x0525 NetChip Technology
+vendor ALTRA 0x0527 ALTRA
+vendor ATI 0x0528 ATI Technologies
+vendor AKS 0x0529 Aladdin Knowledge Systems
+vendor TEKOM 0x052b Tekom
+vendor CANONDEV 0x052c Canon
+vendor WACOMTECH 0x0531 Wacom
+vendor INVENTEC 0x0537 Inventec
+vendor SHYHSHIUN 0x0539 Shyh Shiun Terminals
+vendor PREHWERKE 0x053a Preh Werke Gmbh & Co. KG
+vendor SYNOPSYS 0x053f Synopsys
+vendor UNIACCESS 0x0540 Universal Access
+vendor VIEWSONIC 0x0543 ViewSonic
+vendor XIRLINK 0x0545 Xirlink
+vendor ANCHOR 0x0547 Anchor Chips
+vendor SONY 0x054c Sony
+vendor FUJIXEROX 0x0550 Fuji Xerox
+vendor VISION 0x0553 VLSI Vision
+vendor ASAHIKASEI 0x0556 Asahi Kasei Microsystems
+vendor ATEN 0x0557 ATEN International
+vendor SAMSUNG2 0x055d Samsung Electronics
+vendor MUSTEK 0x055f Mustek Systems
+vendor TELEX 0x0562 Telex Communications
+vendor CHINON 0x0564 Chinon
+vendor PERACOM 0x0565 Peracom Networks
+vendor ALCOR2 0x0566 Alcor Micro
+vendor XYRATEX 0x0567 Xyratex
+vendor WACOM 0x056a WACOM
+vendor ETEK 0x056c e-TEK Labs
+vendor EIZO 0x056d EIZO
+vendor ELECOM 0x056e Elecom
+vendor CONEXANT 0x0572 Conexant
+vendor HAUPPAUGE 0x0573 Hauppauge Computer Works
+vendor BAFO 0x0576 BAFO/Quality Computer Accessories
+vendor YEDATA 0x057b Y-E Data
+vendor AVM 0x057c AVM
+vendor QUICKSHOT 0x057f Quickshot
+vendor ROLAND 0x0582 Roland
+vendor ROCKFIRE 0x0583 Rockfire
+vendor RATOC 0x0584 RATOC Systems
+vendor ZYXEL 0x0586 ZyXEL Communication
+vendor INFINEON 0x058b Infineon
+vendor MICREL 0x058d Micrel
+vendor ALCOR 0x058f Alcor Micro
+vendor OMRON 0x0590 OMRON
+vendor ZORAN 0x0595 Zoran Microelectronics
+vendor NIIGATA 0x0598 Niigata
+vendor IOMEGA 0x059b Iomega
+vendor ATREND 0x059c A-Trend Technology
+vendor AID 0x059d Advanced Input Devices
+vendor LACIE 0x059f LaCie
+vendor FUJIFILM 0x05a2 Fuji Film
+vendor ARC 0x05a3 ARC
+vendor ORTEK 0x05a4 Ortek
+vendor BOSE 0x05a7 Bose
+vendor OMNIVISION 0x05a9 OmniVision
+vendor INSYSTEM 0x05ab In-System Design
+vendor APPLE 0x05ac Apple Computer
+vendor YCCABLE 0x05ad Y.C. Cable
+vendor DIGITALPERSONA 0x05ba DigitalPersona
+vendor 3G 0x05bc 3G Green Green Globe
+vendor RAFI 0x05bd RAFI
+vendor TYCO 0x05be Tyco
+vendor KAWASAKI 0x05c1 Kawasaki
+vendor DIGI 0x05c5 Digi International
+vendor QUALCOMM2 0x05c6 Qualcomm
+vendor QTRONIX 0x05c7 Qtronix
+vendor FOXLINK 0x05c8 Foxlink
+vendor RICOH 0x05ca Ricoh
+vendor ELSA 0x05cc ELSA
+vendor SCIWORX 0x05ce sci-worx
+vendor BRAINBOXES 0x05d1 Brainboxes Limited
+vendor ULTIMA 0x05d8 Ultima
+vendor AXIOHM 0x05d9 Axiohm Transaction Solutions
+vendor MICROTEK 0x05da Microtek
+vendor SUNTAC 0x05db SUN Corporation
+vendor LEXAR 0x05dc Lexar Media
+vendor ADDTRON 0x05dd Addtron
+vendor SYMBOL 0x05e0 Symbol Technologies
+vendor SYNTEK 0x05e1 Syntek
+vendor GENESYS 0x05e3 Genesys Logic
+vendor FUJI 0x05e5 Fuji Electric
+vendor KEITHLEY 0x05e6 Keithley Instruments
+vendor EIZONANAO 0x05e7 EIZO Nanao
+vendor KLSI 0x05e9 Kawasaki LSI
+vendor FFC 0x05eb FFC
+vendor ANKO 0x05ef Anko Electronic
+vendor PIENGINEERING 0x05f3 P.I. Engineering
+vendor AOC 0x05f6 AOC International
+vendor CHIC 0x05fe Chic Technology
+vendor BARCO 0x0600 Barco Display Systems
+vendor BRIDGE 0x0607 Bridge Information
+vendor SOLIDYEAR 0x060b Solid Year
+vendor BIORAD 0x0614 Bio-Rad Laboratories
+vendor MACALLY 0x0618 Macally
+vendor ACTLABS 0x061c Act Labs
+vendor ALARIS 0x0620 Alaris
+vendor APEX 0x0624 Apex
+vendor CREATIVE3 0x062a Creative Labs
+vendor VIVITAR 0x0636 Vivitar
+vendor GUNZE 0x0637 Gunze Electronics USA
+vendor AVISION 0x0638 Avision
+vendor TEAC 0x0644 TEAC
+vendor SGI 0x065e Silicon Graphics
+vendor SANWASUPPLY 0x0663 Sanwa Supply
+vendor LINKSYS 0x066b Linksys
+vendor ACERSA 0x066e Acer Semiconductor America
+vendor SIGMATEL 0x066f Sigmatel
+vendor DRAYTEK 0x0675 DrayTek
+vendor AIWA 0x0677 Aiwa
+vendor ACARD 0x0678 ACARD Technology
+vendor PROLIFIC 0x067b Prolific Technology
+vendor SIEMENS 0x067c Siemens
+vendor AVANCELOGIC 0x0680 Avance Logic
+vendor SIEMENS2 0x0681 Siemens
+vendor MINOLTA 0x0686 Minolta
+vendor CHPRODUCTS 0x068e CH Products
+vendor HAGIWARA 0x0693 Hagiwara Sys-Com
+vendor CTX 0x0698 Chuntex
+vendor ASKEY 0x069a Askey Computer
+vendor SAITEK 0x06a3 Saitek
+vendor ALCATELT 0x06b9 Alcatel Telecom
+vendor AGFA 0x06bd AGFA-Gevaert
+vendor ASIAMD 0x06be Asia Microelectronic Development
+vendor BIZLINK 0x06c4 Bizlink International
+vendor KEYSPAN 0x06cd Keyspan / InnoSys Inc.
+vendor AASHIMA 0x06d6 Aashima Technology
+vendor MULTITECH 0x06e0 MultiTech
+vendor ADS 0x06e1 ADS Technologies
+vendor ALCATELM 0x06e4 Alcatel Microelectronics
+vendor SIRIUS 0x06ea Sirius Technologies
+vendor GUILLEMOT 0x06f8 Guillemot
+vendor BOSTON 0x06fd Boston Acoustics
+vendor SMC 0x0707 Standard Microsystems
+vendor PUTERCOM 0x0708 Putercom
+vendor MCT 0x0711 MCT
+vendor IMATION 0x0718 Imation
+vendor SONYERICSSON 0x0731 Sony Ericsson
+vendor EICON 0x0734 Eicon Networks
+vendor SYNTECH 0x0745 Syntech Information
+vendor DIGITALSTREAM 0x074e Digital Stream
+vendor AUREAL 0x0755 Aureal Semiconductor
+vendor MIDIMAN 0x0763 Midiman
+vendor CYBERPOWER 0x0764 Cyber Power Systems, Inc.
+vendor SURECOM 0x0769 Surecom Technology
+vendor LINKSYS2 0x077b Linksys
+vendor GRIFFIN 0x077d Griffin Technology
+vendor SANDISK 0x0781 SanDisk
+vendor JENOPTIK 0x0784 Jenoptik
+vendor LOGITEC 0x0789 Logitec
+vendor BRIMAX 0x078e Brimax
+vendor AXIS 0x0792 Axis Communications
+vendor ABL 0x0794 ABL Electronics
+vendor SAGEM 0x079b Sagem
+vendor SUNCOMM 0x079c Sun Communications, Inc.
+vendor ALFADATA 0x079d Alfadata Computer
+vendor NATIONALTECH 0x07a2 National Technical Systems
+vendor ONNTO 0x07a3 Onnto
+vendor BE 0x07a4 Be
+vendor ADMTEK 0x07a6 ADMtek
+vendor COREGA 0x07aa Corega
+vendor FREECOM 0x07ab Freecom
+vendor MICROTECH 0x07af Microtech
+vendor GENERALINSTMNTS 0x07b2 General Instruments (Motorola)
+vendor OLYMPUS 0x07b4 Olympus
+vendor ABOCOM 0x07b8 AboCom Systems
+vendor KEISOKUGIKEN 0x07c1 Keisokugiken
+vendor ONSPEC 0x07c4 OnSpec
+vendor APG 0x07c5 APG Cash Drawer
+vendor BUG 0x07c8 B.U.G.
+vendor ALLIEDTELESYN 0x07c9 Allied Telesyn International
+vendor AVERMEDIA 0x07ca AVerMedia Technologies
+vendor SIIG 0x07cc SIIG
+vendor CASIO 0x07cf CASIO
+vendor DLINK2 0x07d1 D-Link
+vendor APTIO 0x07d2 Aptio Products
+vendor ARASAN 0x07da Arasan Chip Systems
+vendor ALLIEDCABLE 0x07e6 Allied Cable
+vendor STSN 0x07ef STSN
+vendor CENTURY 0x07f7 Century Corp
+vendor ZOOM 0x0803 Zoom Telephonics
+vendor PCS 0x0810 Personal Communication Systems
+vendor BROADLOGIC 0x0827 BroadLogic
+vendor HANDSPRING 0x082d Handspring
+vendor PALM 0x0830 Palm Computing
+vendor SOURCENEXT 0x0833 SOURCENEXT
+vendor ACTIONSTAR 0x0835 Action Star Enterprise
+vendor SAMSUNG_TECHWIN 0x0839 Samsung Techwin
+vendor ACCTON 0x083a Accton Technology
+vendor DIAMOND 0x0841 Diamond
+vendor NETGEAR 0x0846 BayNETGEAR
+vendor TOPRE 0x0853 Topre Corporation
+vendor ACTIVEWIRE 0x0854 ActiveWire
+vendor BBELECTRONICS 0x0856 B&B Electronics
+vendor PORTGEAR 0x085a PortGear
+vendor NETGEAR2 0x0864 Netgear
+vendor SYSTEMTALKS 0x086e System Talks
+vendor METRICOM 0x0870 Metricom
+vendor ADESSOKBTEK 0x087c ADESSO/Kbtek America
+vendor JATON 0x087d Jaton
+vendor APT 0x0880 APT Technologies
+vendor BOCARESEARCH 0x0885 Boca Research
+vendor ANDREA 0x08a8 Andrea Electronics
+vendor BURRBROWN 0x08bb Burr-Brown Japan
+vendor 2WIRE 0x08c8 2Wire
+vendor AIPTEK 0x08ca AIPTEK International
+vendor SMARTBRIDGES 0x08d1 SmartBridges
+vendor BILLIONTON 0x08dd Billionton Systems
+vendor EXTENDED 0x08e9 Extended Systems
+vendor MSYSTEMS 0x08ec M-Systems
+vendor AUTHENTEC 0x08ff AuthenTec
+vendor AUDIOTECHNICA 0x0909 Audio-Technica
+vendor TRUMPION 0x090a Trumpion Microelectronics
+vendor FEIYA 0x090c Feiya
+vendor ALATION 0x0910 Alation Systems
+vendor GLOBESPAN 0x0915 Globespan
+vendor CONCORDCAMERA 0x0919 Concord Camera
+vendor GARMIN 0x091e Garmin International
+vendor GOHUBS 0x0921 GoHubs
+vendor XEROX 0x0924 Xerox
+vendor BIOMETRIC 0x0929 American Biometric Company
+vendor TOSHIBA 0x0930 Toshiba
+vendor PLEXTOR 0x093b Plextor
+vendor INTREPIDCS 0x093c Intrepid
+vendor YANO 0x094f Yano
+vendor KINGSTON 0x0951 Kingston Technology
+vendor BLUEWATER 0x0956 BlueWater Systems
+vendor AGILENT 0x0957 Agilent Technologies
+vendor GUDE 0x0959 Gude ADS
+vendor PORTSMITH 0x095a Portsmith
+vendor ACERW 0x0967 Acer
+vendor ADIRONDACK 0x0976 Adirondack Wire & Cable
+vendor BECKHOFF 0x0978 Beckhoff
+vendor MINDSATWORK 0x097a Minds At Work
+vendor POINTCHIPS 0x09a6 PointChips
+vendor INTERSIL 0x09aa Intersil
+vendor ALTIUS 0x09b3 Altius Solutions
+vendor ARRIS 0x09c1 Arris Interactive
+vendor ACTIVCARD 0x09c3 ACTIVCARD
+vendor ACTISYS 0x09c4 ACTiSYS
+vendor NOVATEL2 0x09d7 Novatel Wireless
+vendor AFOURTECH 0x09da A-FOUR TECH
+vendor AIMEX 0x09dc AIMEX
+vendor ADDONICS 0x09df Addonics Technologies
+vendor AKAI 0x09e8 AKAI professional M.I.
+vendor ARESCOM 0x09f5 ARESCOM
+vendor BAY 0x09f9 Bay Associates
+vendor ALTERA 0x09fb Altera
+vendor CSR 0x0a12 Cambridge Silicon Radio
+vendor TREK 0x0a16 Trek Technology
+vendor ASAHIOPTICAL 0x0a17 Asahi Optical
+vendor BOCASYSTEMS 0x0a43 Boca Systems
+vendor SHANTOU 0x0a46 ShanTou
+vendor MEDIAGEAR 0x0a48 MediaGear
+vendor BROADCOM 0x0a5c Broadcom
+vendor GREENHOUSE 0x0a6b GREENHOUSE
+vendor GEOCAST 0x0a79 Geocast Network Systems
+vendor IDQUANTIQUE 0x0aba id Quantique
+vendor ZYDAS 0x0ace Zydas Technology Corporation
+vendor NEODIO 0x0aec Neodio
+vendor OPTION 0x0af0 Option N.V:
+vendor ASUS 0x0b05 ASUSTeK Computer
+vendor TODOS 0x0b0c Todos Data System
+vendor SIIG2 0x0b39 SIIG
+vendor TEKRAM 0x0b3b Tekram Technology
+vendor HAL 0x0b41 HAL Corporation
+vendor EMS 0x0b43 EMS Production
+vendor NEC2 0x0b62 NEC
+vendor ATI2 0x0b6f ATI
+vendor ZEEVO 0x0b7a Zeevo, Inc.
+vendor KURUSUGAWA 0x0b7e Kurusugawa Electronics, Inc.
+vendor ASIX 0x0b95 ASIX Electronics
+vendor O2MICRO 0x0b97 O2 Micro, Inc.
+vendor USR 0x0baf U.S. Robotics
+vendor AMBIT 0x0bb2 Ambit Microsystems
+vendor HTC 0x0bb4 HTC
+vendor REALTEK 0x0bda Realtek
+vendor ADDONICS2 0x0bf6 Addonics Technology
+vendor FSC 0x0bf8 Fujitsu Siemens Computers
+vendor AGATE 0x0c08 Agate Technologies
+vendor DMI 0x0c0b DMI
+vendor MICRODIA 0x0c45 Chicony
+vendor SEALEVEL 0x0c52 Sealevel System
+vendor LUWEN 0x0c76 Luwen
+vendor KYOCERA2 0x0c88 Kyocera Wireless Corp.
+vendor ZCOM 0x0cde Z-Com
+vendor ATHEROS2 0x0cf3 Atheros Communications
+vendor TANGTOP 0x0d3d Tangtop
+vendor SMC3 0x0d5c Standard Microsystems
+vendor ADDON 0x0d7d Add-on Technology
+vendor ACDC 0x0d7e American Computer & Digital Components
+vendor ABC 0x0d8c ABC
+vendor CONCEPTRONIC 0x0d8e Conceptronic
+vendor SKANHEX 0x0d96 Skanhex Technology, Inc.
+vendor MSI 0x0db0 Micro Star International
+vendor ELCON 0x0db7 ELCON Systemtechnik
+vendor NETAC 0x0dd8 Netac
+vendor SITECOMEU 0x0df6 Sitecom Europe
+vendor MOBILEACTION 0x0df7 Mobile Action
+vendor SPEEDDRAGON 0x0e55 Speed Dragon Multimedia
+vendor HAWKING 0x0e66 Hawking
+vendor FOSSIL 0x0e67 Fossil, Inc
+vendor GMATE 0x0e7e G.Mate, Inc
+vendor OTI 0x0ea0 Ours Technology
+vendor PILOTECH 0x0eaf Pilotech
+vendor NOVATECH 0x0eb0 NovaTech
+vendor ITEGNO 0x0eba iTegno
+vendor WINMAXGROUP 0x0ed1 WinMaxGroup
+vendor TOD 0x0ede TOD
+vendor EGALAX 0x0eef eGalax, Inc.
+vendor AIRPRIME 0x0f3d AirPrime, Inc.
+vendor MICROTUNE 0x0f4d Microtune
+vendor VTECH 0x0f88 VTech
+vendor FALCOM 0x0f94 Falcom Wireless Communications GmbH
+vendor RIM 0x0fca Research In Motion
+vendor DYNASTREAM 0x0fcf Dynastream Innovations
+vendor QUALCOMM 0x1004 Qualcomm
+vendor DESKNOTE 0x1019 Desknote
+vendor GIGABYTE 0x1044 GIGABYTE
+vendor WESTERN 0x1058 Western Digital
+vendor MOTOROLA 0x1063 Motorola
+vendor CCYU 0x1065 CCYU Technology
+vendor CURITEL 0x106c Curitel Communications Inc
+vendor SILABS2 0x10a6 SILABS2
+vendor USI 0x10ab USI
+vendor PLX 0x10b5 PLX
+vendor ASANTE 0x10bd Asante
+vendor SILABS 0x10c4 Silicon Labs
+vendor ANALOG 0x1110 Analog Devices
+vendor TENX 0x1130 Ten X Technology, Inc.
+vendor ISSC 0x1131 Integrated System Solution Corp.
+vendor JRC 0x1145 Japan Radio Company
+vendor SPHAIRON 0x114b Sphairon Access Systems GmbH
+vendor DELORME 0x1163 DeLorme
+vendor SERVERWORKS 0x1166 ServerWorks
+vendor ACERCM 0x1189 Acer Communications & Multimedia
+vendor SIERRA 0x1199 Sierra Wireless
+vendor TOPFIELD 0x11db Topfield Co., Ltd
+vendor SIEMENS3 0x11f5 Siemens
+vendor PROLIFIC2 0x11f6 Prolific
+vendor ALCATEL 0x11f7 Alcatel
+vendor UNKNOWN3 0x1233 Unknown vendor
+vendor TSUNAMI 0x1241 Tsunami
+vendor PHEENET 0x124a Pheenet
+vendor TARGUS 0x1267 Targus
+vendor TWINMOS 0x126f TwinMOS
+vendor TENDA 0x1286 Tenda
+vendor CREATIVE2 0x1292 Creative Labs
+vendor BELKIN2 0x1293 Belkin Components
+vendor CYBERTAN 0x129b CyberTAN Technology
+vendor HUAWEI 0x12d1 Huawei Technologies
+vendor ARANEUS 0x12d8 Araneus Information Systems
+vendor TAPWAVE 0x12ef Tapwave
+vendor AINCOMM 0x12fd Aincomm
+vendor MOBILITY 0x1342 Mobility
+vendor DICKSMITH 0x1371 Dick Smith Electronics
+vendor NETGEAR3 0x1385 Netgear
+vendor BALTECH 0x13ad Baltech
+vendor CISCOLINKSYS 0x13b1 Cisco-Linksys
+vendor SHARK 0x13d2 Shark
+vendor NOVATEL 0x1410 Novatel Wireless
+vendor MERLIN 0x1416 Merlin
+vendor WISTRONNEWEB 0x1435 Wistron NeWeb
+vendor RADIOSHACK 0x1453 Radio Shack
+vendor HUAWEI3COM 0x1472 Huawei-3Com
+vendor SILICOM 0x1485 Silicom
+vendor RALINK 0x148f Ralink Technology
+vendor IMAGINATION 0x149a Imagination Technologies
+vendor CONCEPTRONIC2 0x14b2 Conceptronic
+vendor PLANEX3 0x14ea Planex Communications
+vendor SILICONPORTALS 0x1527 Silicon Portals
+vendor UBIQUAM 0x1529 UBIQUAM Co., Ltd.
+vendor UBLOX 0x1546 U-blox
+vendor PNY 0x154b PNY
+vendor OQO 0x1557 OQO
+vendor UMEDIA 0x157e U-MEDIA Communications
+vendor FIBERLINE 0x1582 Fiberline
+vendor SPARKLAN 0x15a9 SparkLAN
+vendor SOHOWARE 0x15e8 SOHOware
+vendor UMAX 0x1606 UMAX Data Systems
+vendor INSIDEOUT 0x1608 Inside Out Networks
+vendor GOODWAY 0x1631 Good Way Technology
+vendor ENTREGA 0x1645 Entrega
+vendor ACTIONTEC 0x1668 Actiontec Electronics
+vendor ATHEROS 0x168c Atheros Communications
+vendor GIGASET 0x1690 Gigaset
+vendor GLOBALSUN 0x16ab Global Sun Technology
+vendor ANYDATA 0x16d5 AnyDATA Corporation
+vendor JABLOTRON 0x16d6 Jablotron
+vendor CMOTECH 0x16d8 CMOTECH Co., Ltd.
+vendor AXESSTEL 0x1726 Axesstel Co., Ltd.
+vendor LINKSYS4 0x1737 Linksys
+vendor SENAO 0x1740 Senao
+vendor METAGEEK 0x1781 MetaGeek
+vendor AMIT 0x18c5 AMIT
+vendor QCOM 0x18e8 Qcom
+vendor LINKSYS3 0x1915 Linksys
+vendor QUALCOMMINC 0x19d2 Qualcomm, Incorporated
+vendor DLINK 0x2001 D-Link
+vendor PLANEX2 0x2019 Planex Communications
+vendor ERICSSON 0x2282 Ericsson
+vendor MOTOROLA2 0x22b8 Motorola
+vendor TRIPPLITE 0x2478 Tripp-Lite
+vendor HIROSE 0x2631 Hirose Electric
+vendor NHJ 0x2770 NHJ
+vendor PLANEX 0x2c02 Planex Communications
+vendor VIDZMEDIA 0x3275 VidzMedia Pte Ltd
+vendor AEI 0x3334 AEI
+vendor HANK 0x3353 Hank Connection
+vendor PQI 0x3538 PQI
+vendor DAISY 0x3579 Daisy Technology
+vendor NI 0x3923 National Instruments
+vendor MICRONET 0x3980 Micronet Communications
+vendor IODATA2 0x40bb I-O Data
+vendor IRIVER 0x4102 iRiver
+vendor DELL 0x413c Dell
+vendor WCH 0x4348 QinHeng Electronics
+vendor ACEECA 0x4766 Aceeca
+vendor AVERATEC 0x50c2 Averatec
+vendor SWEEX 0x5173 Sweex
+vendor ONSPEC2 0x55aa OnSpec Electronic Inc.
+vendor ZINWELL 0x5a57 Zinwell
+vendor SITECOM 0x6189 Sitecom
+vendor ARKMICRO 0x6547 Arkmicro Technologies Inc.
+vendor 3COM2 0x6891 3Com
+vendor INTEL 0x8086 Intel
+vendor SITECOM2 0x9016 Sitecom
+vendor MOSCHIP 0x9710 MosChip Semiconductor
+vendor 3COM3 0xa727 3Com
+vendor HP2 0xf003 Hewlett Packard
+vendor USRP 0xfffe GNU Radio USRP
+
+/*
+ * List of known products. Grouped by vendor.
+ */
+
+/* 3Com products */
+product 3COM HOMECONN 0x009d HomeConnect Camera
+product 3COM 3CREB96 0x00a0 Bluetooth USB Adapter
+product 3COM 3C19250 0x03e8 3C19250 Ethernet Adapter
+product 3COM 3CRSHEW696 0x0a01 3CRSHEW696 Wireless Adapter
+product 3COM 3C460 0x11f8 HomeConnect 3C460
+product 3COM USR56K 0x3021 U.S.Robotics 56000 Voice FaxModem Pro
+product 3COM 3C460B 0x4601 HomeConnect 3C460B
+product 3COM2 3CRUSB10075 0xa727 3CRUSB10075
+product 3COM3 AR5523_1 0x6893 AR5523
+product 3COM3 AR5523_2 0x6895 AR5523
+product 3COM3 AR5523_3 0x6897 AR5523
+
+product 3COMUSR OFFICECONN 0x0082 3Com OfficeConnect Analog Modem
+product 3COMUSR USRISDN 0x008f 3Com U.S. Robotics Pro ISDN TA
+product 3COMUSR HOMECONN 0x009d 3Com HomeConnect Camera
+product 3COMUSR USR56K 0x3021 U.S. Robotics 56000 Voice FaxModem Pro
+
+/* AboCom products */
+product ABOCOM XX1 0x110c XX1
+product ABOCOM XX2 0x200c XX2
+product ABOCOM URE450 0x4000 URE450 Ethernet Adapter
+product ABOCOM UFE1000 0x4002 UFE1000 Fast Ethernet Adapter
+product ABOCOM DSB650TX_PNA 0x4003 1/10/100 Ethernet Adapter
+product ABOCOM XX4 0x4004 XX4
+product ABOCOM XX5 0x4007 XX5
+product ABOCOM XX6 0x400b XX6
+product ABOCOM XX7 0x400c XX7
+product ABOCOM RTL8151 0x401a RTL8151
+product ABOCOM XX8 0x4102 XX8
+product ABOCOM XX9 0x4104 XX9
+product ABOCOM UF200 0x420a UF200 Ethernet
+product ABOCOM WL54 0x6001 WL54
+product ABOCOM XX10 0xabc1 XX10
+product ABOCOM BWU613 0xb000 BWU613
+product ABOCOM HWU54DM 0xb21b HWU54DM
+product ABOCOM RT2573_2 0xb21c RT2573
+product ABOCOM RT2573_3 0xb21d RT2573
+product ABOCOM RT2573_4 0xb21e RT2573
+product ABOCOM WUG2700 0xb21f WUG2700
+
+/* Accton products */
+product ACCTON USB320_EC 0x1046 USB320-EC Ethernet Adapter
+product ACCTON 2664W 0x3501 2664W
+product ACCTON 111 0x3503 T-Sinus 111 Wireless Adapter
+product ACCTON SMCWUSBG 0x4505 SMCWUSB-G
+product ACCTON PRISM_GT 0x4521 PrismGT USB 2.0 WLAN
+product ACCTON SS1001 0x5046 SpeedStream Ethernet Adapter
+product ACCTON ZD1211B 0xe501 ZD1211B
+
+/* Aceeca products */
+product ACEECA MEZ1000 0x0001 MEZ1000 RDA
+
+/* Acer Communications & Multimedia (oemd by Surecom) */
+product ACERCM EP1427X2 0x0893 EP-1427X-2 Ethernet Adapter
+
+/* Acer Labs products */
+product ACERLABS M5632 0x5632 USB 2.0 Data Link
+
+/* Acer Peripherals, Inc. products */
+product ACERP ACERSCAN_C310U 0x12a6 Acerscan C310U
+product ACERP ACERSCAN_320U 0x2022 Acerscan 320U
+product ACERP ACERSCAN_640U 0x2040 Acerscan 640U
+product ACERP ACERSCAN_620U 0x2060 Acerscan 620U
+product ACERP ACERSCAN_4300U 0x20b0 Benq 3300U/4300U
+product ACERP ACERSCAN_640BT 0x20be Acerscan 640BT
+product ACERP ACERSCAN_1240U 0x20c0 Acerscan 1240U
+product ACERP ATAPI 0x6003 ATA/ATAPI Adapter
+product ACERP AWL300 0x9000 AWL300 Wireless Adapter
+product ACERP AWL400 0x9001 AWL400 Wireless Adapter
+
+/* Acer Warp products */
+product ACERW WARPLINK 0x0204 Warplink
+
+/* Actiontec, Inc. products */
+product ACTIONTEC PRISM_25 0x0408 Prism2.5 Wireless Adapter
+product ACTIONTEC PRISM_25A 0x0421 Prism2.5 Wireless Adapter A
+product ACTIONTEC FREELAN 0x6106 ROPEX FreeLan 802.11b
+product ACTIONTEC UAT1 0x7605 UAT1 Wireless Ethernet Adapter
+
+/* ACTiSYS products */
+product ACTISYS IR2000U 0x0011 ACT-IR2000U FIR
+
+/* ActiveWire, Inc. products */
+product ACTIVEWIRE IOBOARD 0x0100 I/O Board
+product ACTIVEWIRE IOBOARD_FW1 0x0101 I/O Board, rev. 1 firmware
+
+/* Adaptec products */
+product ADAPTEC AWN8020 0x0020 AWN-8020 WLAN
+
+/* Addtron products */
+product ADDTRON AWU120 0xff31 AWU-120
+
+/* ADMtek products */
+product ADMTEK PEGASUSII_4 0x07c2 AN986A Ethernet
+product ADMTEK PEGASUS 0x0986 AN986 Ethernet
+product ADMTEK PEGASUSII 0x8511 AN8511 Ethernet
+product ADMTEK PEGASUSII_2 0x8513 AN8513 Ethernet
+product ADMTEK PEGASUSII_3 0x8515 AN8515 Ethernet
+
+/* ADDON products */
+/* PNY OEMs these */
+product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive
+product ADDON ATTACHE 0x1300 USB 2.0 Flash Drive
+product ADDON A256MB 0x1400 Attache 256MB USB 2.0 Flash Drive
+product ADDON DISKPRO512 0x1420 USB 2.0 Flash Drive (DANE-ELEC zMate 512MB USB flash drive)
+
+/* Addonics products */
+product ADDONICS2 CABLE_205 0xa001 Cable 205
+
+/* ADS products */
+product ADS UBS10BT 0x0008 UBS-10BT Ethernet
+product ADS UBS10BTX 0x0009 UBS-10BT Ethernet
+
+/* AEI products */
+product AEI FASTETHERNET 0x1701 Fast Ethernet
+
+/* Agate Technologies products */
+product AGATE QDRIVE 0x0378 Q-Drive
+
+/* AGFA products */
+product AGFA SNAPSCAN1212U 0x0001 SnapScan 1212U
+product AGFA SNAPSCAN1236U 0x0002 SnapScan 1236U
+product AGFA SNAPSCANTOUCH 0x0100 SnapScan Touch
+product AGFA SNAPSCAN1212U2 0x2061 SnapScan 1212U
+product AGFA SNAPSCANE40 0x208d SnapScan e40
+product AGFA SNAPSCANE50 0x208f SnapScan e50
+product AGFA SNAPSCANE20 0x2091 SnapScan e20
+product AGFA SNAPSCANE25 0x2095 SnapScan e25
+product AGFA SNAPSCANE26 0x2097 SnapScan e26
+product AGFA SNAPSCANE52 0x20fd SnapScan e52
+
+/* Ain Communication Technology products */
+product AINCOMM AWU2000B 0x1001 AWU2000B Wireless Adapter
+
+/* AIPTEK products */
+product AIPTEK POCKETCAM3M 0x2011 PocketCAM 3Mega
+product SUNPLUS PENCAM_MEGA_1_3 0x504a PenCam Mega 1.3
+
+/* AirPrime products */
+product AIRPRIME PC5220 0x0112 CDMA Wireless PC Card
+
+/* AKS products */
+product AKS USBHASP 0x0001 USB-HASP 0.06
+
+/* Alcor Micro, Inc. products */
+product ALCOR2 KBD_HUB 0x2802 Kbd Hub
+
+product ALCOR MA_KBD_HUB 0x9213 MacAlly Kbd Hub
+product ALCOR AU9814 0x9215 AU9814 Hub
+product ALCOR UMCR_9361 0x9361 USB Multimedia Card Reader
+product ALCOR SM_KBD 0x9410 MicroConnectors/StrongMan Keyboard
+product ALCOR NEC_KBD_HUB 0x9472 NEC Kbd Hub
+
+/* Altec Lansing products */
+product ALTEC ADA70 0x0070 ADA70 Speakers
+product ALTEC ASC495 0xff05 ASC495 Speakers
+
+/* Allied Telesyn International products */
+product ALLIEDTELESYN ATUSB100 0xb100 AT-USB100
+
+/* American Power Conversion products */
+product APC UPS 0x0002 Uninterruptible Power Supply
+
+/* Ambit Microsystems products */
+product AMBIT WLAN 0x0302 WLAN
+product AMBIT NTL_250 0x6098 NTL 250 cable modem
+
+/* AMIT products */
+product AMIT CGWLUSB2GO 0x0002 CG-WLUSB2GO
+
+/* Anchor products */
+product ANCHOR EZUSB 0x2131 EZUSB
+product ANCHOR EZLINK 0x2720 EZLINK
+
+/* AnyData products */
+product ANYDATA ADU_E100X 0x6501 CDMA 2000 1xRTT/EV-DO USB Modem
+product ANYDATA ADU_500A 0x6502 CDMA 2000 EV-DO USB Modem
+
+/* AOX, Inc. products */
+product AOX USB101 0x0008 Ethernet
+
+/* American Power Conversion products */
+product APC UPS 0x0002 Uninterruptible Power Supply
+
+/* Apple Computer products */
+product APPLE EXT_KBD 0x020c Apple Extended USB Keyboard
+product APPLE OPTMOUSE 0x0302 Optical mouse
+product APPLE MIGHTYMOUSE 0x0304 Mighty Mouse
+product APPLE EXT_KBD_HUB 0x1003 Hub in Apple Extended USB Keyboard
+product APPLE SPEAKERS 0x1101 Speakers
+product APPLE IPOD 0x1201 iPod
+product APPLE IPOD2G 0x1202 iPod 2G
+product APPLE IPOD3G 0x1203 iPod 3G
+product APPLE IPOD_04 0x1204 iPod '04'
+product APPLE IPODMINI 0x1205 iPod Mini
+product APPLE IPOD_06 0x1206 iPod '06'
+product APPLE IPOD_07 0x1207 iPod '07'
+product APPLE IPOD_08 0x1208 iPod '08'
+product APPLE IPODVIDEO 0x1209 iPod Video
+product APPLE IPODNANO 0x120a iPod Nano
+product APPLE IPHONE 0x1290 iPhone
+product APPLE IPHONE_3G 0x1292 iPhone 3G
+product APPLE ETHERNET 0x1402 Ethernet A1277
+
+/* Arkmicro Technologies */
+product ARKMICRO ARK3116 0x0232 ARK3116 Serial
+
+/* Asahi Optical products */
+product ASAHIOPTICAL OPTIO230 0x0004 Digital camera
+product ASAHIOPTICAL OPTIO330 0x0006 Digital camera
+
+/* Asante products */
+product ASANTE EA 0x1427 Ethernet
+
+/* ASIX Electronics products */
+product ASIX AX88172 0x1720 10/100 Ethernet
+product ASIX AX88178 0x1780 AX88178
+product ASIX AX88772 0x7720 AX88772
+
+/* ASUS products */
+product ASUS WL167G 0x1707 WL-167g Wireless Adapter
+product ASUS WL159G 0x170c WL-159g
+product ASUS A9T_WIFI 0x171b A9T wireless
+product ASUS RT2573_1 0x1723 RT2573
+product ASUS RT2573_2 0x1724 RT2573
+product ASUS LCM 0x1726 LCM display
+product ASUS P535 0x420f ASUS P535 PDA
+
+/* ATen products */
+product ATEN UC1284 0x2001 Parallel printer
+product ATEN UC10T 0x2002 10Mbps Ethernet
+product ATEN UC110T 0x2007 UC-110T Ethernet
+product ATEN UC232A 0x2008 Serial
+product ATEN UC210T 0x2009 UC-210T Ethernet
+product ATEN DSB650C 0x4000 DSB-650C
+
+/* Atheros Communications products */
+product ATHEROS AR5523 0x0001 AR5523
+product ATHEROS AR5523_NF 0x0002 AR5523 (no firmware)
+product ATHEROS2 AR5523_1 0x0001 AR5523
+product ATHEROS2 AR5523_1_NF 0x0002 AR5523 (no firmware)
+product ATHEROS2 AR5523_2 0x0003 AR5523
+product ATHEROS2 AR5523_2_NF 0x0004 AR5523 (no firmware)
+product ATHEROS2 AR5523_3 0x0005 AR5523
+product ATHEROS2 AR5523_3_NF 0x0006 AR5523 (no firmware)
+
+/* Atmel Comp. products */
+product ATMEL UHB124 0x3301 UHB124 hub
+product ATMEL DWL120 0x7603 DWL-120 Wireless Adapter
+product ATMEL BW002 0x7605 BW002 Wireless Adapter
+product ATMEL WL1130USB 0x7613 WL-1130 USB
+product ATMEL AT76C505A 0x7614 AT76c505a Wireless Adapter
+
+/* Avision products */
+product AVISION 1200U 0x0268 1200U scanner
+
+/* Axesstel products */
+product AXESSTEL DATAMODEM 0x1000 Data Modem
+
+/* Baltech products */
+product BALTECH CARDREADER 0x9999 Card reader
+
+/* B&B Electronics products */
+product BBELECTRONICS USOTL4 0xAC01 RS-422/485
+
+/* Belkin products */
+/*product BELKIN F5U111 0x???? F5U111 Ethernet*/
+product BELKIN F5D6050 0x0050 F5D6050 802.11b Wireless Adapter
+product BELKIN FBT001V 0x0081 FBT001v2 Bluetooth
+product BELKIN FBT003V 0x0084 FBT003v2 Bluetooth
+product BELKIN F5U103 0x0103 F5U103 Serial
+product BELKIN F5U109 0x0109 F5U109 Serial
+product BELKIN USB2SCSI 0x0115 USB to SCSI
+product BELKIN USB2LAN 0x0121 USB to LAN
+product BELKIN F5U208 0x0208 F5U208 VideoBus II
+product BELKIN F5U237 0x0237 F5U237 USB 2.0 7-Port Hub
+product BELKIN F5U257 0x0257 F5U257 Serial
+product BELKIN F5U409 0x0409 F5U409 Serial
+product BELKIN F6C550AVR 0x0551 F6C550-AVR UPS
+product BELKIN F5U120 0x1203 F5U120-PC Hub
+product BELKIN ZD1211B 0x4050 ZD1211B
+product BELKIN F5D5055 0x5055 F5D5055
+product BELKIN F5D7050 0x7050 F5D7050 Wireless Adapter
+product BELKIN F5D7051 0x7051 F5D7051 54g USB Network Adapter
+product BELKIN F5D7050A 0x705a F5D7050A Wireless Adapter
+product BELKIN F5D7050_V4000 0x705c F5D7050 v4000 Wireless Adapter
+product BELKIN F5D9050V3 0x905b F5D9050 ver 3 Wireless Adapter
+product BELKIN2 F5U002 0x0002 F5U002 Parallel printer
+
+/* Billionton products */
+product BILLIONTON USB100 0x0986 USB100N 10/100 FastEthernet
+product BILLIONTON USBLP100 0x0987 USB100LP
+product BILLIONTON USBEL100 0x0988 USB100EL
+product BILLIONTON USBE100 0x8511 USBE100
+product BILLIONTON USB2AR 0x90ff USB2AR Ethernet
+
+/* Broadcom products */
+product BROADCOM BCM2033 0x2033 BCM2033 Bluetooth USB dongle
+
+/* Brother Industries products */
+product BROTHER HL1050 0x0002 HL-1050 laser printer
+
+/* Behavior Technology Computer products */
+product BTC BTC7932 0x6782 Keyboard with mouse port
+
+/* Canon, Inc. products */
+product CANON N656U 0x2206 CanoScan N656U
+product CANON N1220U 0x2207 CanoScan N1220U
+product CANON D660U 0x2208 CanoScan D660U
+product CANON N676U 0x220d CanoScan N676U
+product CANON N1240U 0x220e CanoScan N1240U
+product CANON LIDE25 0x2220 CanoScan LIDE 25
+product CANON S10 0x3041 PowerShot S10
+product CANON S100 0x3045 PowerShot S100
+product CANON S200 0x3065 PowerShot S200
+product CANON REBELXT 0x30ef Digital Rebel XT
+
+/* CATC products */
+product CATC NETMATE 0x000a Netmate Ethernet
+product CATC NETMATE2 0x000c Netmate2 Ethernet
+product CATC CHIEF 0x000d USB Chief Bus & Protocol Analyzer
+product CATC ANDROMEDA 0x1237 Andromeda hub
+
+/* CASIO products */
+product CASIO QV_DIGICAM 0x1001 QV DigiCam
+product CASIO EXS880 0x1105 Exilim EX-S880
+product CASIO BE300 0x2002 BE-300 PDA
+product CASIO NAMELAND 0x4001 CASIO Nameland EZ-USB
+
+/* CCYU products */
+product CCYU ED1064 0x2136 EasyDisk ED1064
+
+/* Century products */
+product CENTURY EX35QUAT 0x011e Century USB Disk Enclosure
+
+/* Cherry products */
+product CHERRY MY3000KBD 0x0001 My3000 keyboard
+product CHERRY MY3000HUB 0x0003 My3000 hub
+product CHERRY CYBOARD 0x0004 CyBoard Keyboard
+
+/* Chic Technology products */
+product CHIC MOUSE1 0x0001 mouse
+product CHIC CYPRESS 0x0003 Cypress USB Mouse
+
+/* Chicony products */
+product CHICONY KB8933 0x0001 KB-8933 keyboard
+product MICRODIA TWINKLECAM 0x600d TwinkleCam USB camera
+
+/* CH Products */
+product CHPRODUCTS PROTHROTTLE 0x00f1 Pro Throttle
+product CHPRODUCTS PROPEDALS 0x00f2 Pro Pedals
+product CHPRODUCTS FIGHTERSTICK 0x00f3 Fighterstick
+product CHPRODUCTS FLIGHTYOKE 0x00ff Flight Sim Yoke
+
+/* Cisco-Linksys products */
+product CISCOLINKSYS WUSB54G 0x000d WUSB54G Wireless Adapter
+product CISCOLINKSYS WUSB54GP 0x0011 WUSB54GP Wireless Adapter
+product CISCOLINKSYS USB200MV2 0x0018 USB200M v2
+product CISCOLINKSYS HU200TS 0x001a HU200TS Wireless Adapter
+product CISCOLINKSYS WUSB54GC 0x0020 WUSB54GC
+product CISCOLINKSYS WUSB54GR 0x0023 WUSB54GR
+product CISCOLINKSYS WUSBF54G 0x0024 WUSBF54G
+
+/* CMOTECH products */
+product CMOTECH CNU510 0x5141 CMOTECH CDMA Technologies USB modem
+product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem
+product CMOTECH CDMA_MODEM1 0x6280 CMOTECH CDMA Technologies USB modem
+
+/* Compaq products */
+product COMPAQ IPAQPOCKETPC 0x0003 iPAQ PocketPC
+product COMPAQ PJB100 0x504a Personal Jukebox PJB100
+product COMPAQ IPAQLINUX 0x505a iPAQ Linux
+
+/* Composite Corp products looks the same as "TANGTOP" */
+product COMPOSITE USBPS2 0x0001 USB to PS2 Adaptor
+
+/* Conceptronic products */
+product CONCEPTRONIC PRISM_GT 0x3762 PrismGT USB 2.0 WLAN
+product CONCEPTRONIC C11U 0x7100 C11U
+product CONCEPTRONIC WL210 0x7110 WL-210
+product CONCEPTRONIC AR5523_1 0x7801 AR5523
+product CONCEPTRONIC AR5523_1_NF 0x7802 AR5523 (no firmware)
+product CONCEPTRONIC AR5523_2 0x7811 AR5523
+product CONCEPTRONIC AR5523_2_NF 0x7812 AR5523 (no firmware)
+product CONCEPTRONIC2 C54RU 0x3c02 C54RU WLAN
+product CONCEPTRONIC2 C54RU2 0x3c22 C54RU
+
+/* Connectix products */
+product CONNECTIX QUICKCAM 0x0001 QuickCam
+
+/* Corega products */
+product COREGA ETHER_USB_T 0x0001 Ether USB-T
+product COREGA FETHER_USB_TX 0x0004 FEther USB-TX
+product COREGA WLAN_USB_USB_11 0x000c WirelessLAN USB-11
+product COREGA FETHER_USB_TXS 0x000d FEther USB-TXS
+product COREGA WLANUSB 0x0012 Wireless LAN Stick-11
+product COREGA FETHER_USB2_TX 0x0017 FEther USB2-TX
+product COREGA WLUSB_11_KEY 0x001a ULUSB-11 Key
+product COREGA CGWLUSB2GL 0x002d CG-WLUSB2GL
+product COREGA CGWLUSB2GPX 0x002e CG-WLUSB2GPX
+product COREGA WLUSB_11_STICK 0x7613 WLAN USB Stick 11
+product COREGA FETHER_USB_TXC 0x9601 FEther USB-TXC
+
+/* Creative products */
+product CREATIVE NOMAD_II 0x1002 Nomad II MP3 player
+product CREATIVE NOMAD_IIMG 0x4004 Nomad II MG
+product CREATIVE NOMAD 0x4106 Nomad
+product CREATIVE2 VOIP_BLASTER 0x0258 Voip Blaster
+product CREATIVE3 OPTICAL_MOUSE 0x0001 Notebook Optical Mouse
+
+/* Cambridge Silicon Radio Ltd. products */
+product CSR BT_DONGLE 0x0001 Bluetooth USB dongle
+product CSR CSRDFU 0xffff USB Bluetooth Device in DFU State
+
+/* CTX products */
+product CTX EX1300 0x9999 Ex1300 hub
+
+/* Curitel products */
+product CURITEL HX550C 0x1101 CDMA 2000 1xRTT USB modem (HX-550C)
+product CURITEL HX57XB 0x2101 CDMA 2000 1xRTT USB modem (HX-570/575B/PR-600)
+product CURITEL PC5740 0x3701 Broadband Wireless modem
+
+/* CyberPower products */
+product CYBERPOWER 1500CAVRLCD 0x0501 1500CAVRLCD
+
+/* CyberTAN Technology products */
+product CYBERTAN TG54USB 0x1666 TG54USB
+
+/* Cypress Semiconductor products */
+product CYPRESS MOUSE 0x0001 mouse
+product CYPRESS THERMO 0x0002 thermometer
+product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy
+product CYPRESS KBDHUB 0x0101 Keyboard/Hub
+product CYPRESS FMRADIO 0x1002 FM Radio
+product CYPRESS USBRS232 0x5500 USB-RS232 Interface
+product CYPRESS SLIM_HUB 0x6560 Slim Hub
+
+/* Daisy Technology products */
+product DAISY DMC 0x6901 USB MultiMedia Reader
+
+/* Dallas Semiconductor products */
+product DALLAS J6502 0x4201 J-6502 speakers
+
+/* Dell products */
+product DELL PORT 0x0058 Port Replicator
+product DELL AIO926 0x5115 Photo AIO Printer 926
+product DELL BC02 0x8000 BC02 Bluetooth USB Adapter
+product DELL PRISM_GT_1 0x8102 PrismGT USB 2.0 WLAN
+product DELL TM350 0x8103 TrueMobile 350 Bluetooth USB Adapter
+product DELL PRISM_GT_2 0x8104 PrismGT USB 2.0 WLAN
+product DELL U740 0x8135 Dell U740 CDMA
+
+/* Delorme Paublishing products */
+product DELORME EARTHMATE 0x0100 Earthmate GPS
+
+/* Desknote products */
+product DESKNOTE UCR_61S2B 0x0c55 UCR-61S2B
+
+/* Diamond products */
+product DIAMOND RIO500USB 0x0001 Rio 500 USB
+
+/* Dick Smith Electronics (really C-Net) products */
+product DICKSMITH RT2573 0x9022 RT2573
+product DICKSMITH CWD854F 0x9032 C-Net CWD-854 rev F
+
+/* Digi International products */
+product DIGI ACCELEPORT2 0x0002 AccelePort USB 2
+product DIGI ACCELEPORT4 0x0004 AccelePort USB 4
+product DIGI ACCELEPORT8 0x0008 AccelePort USB 8
+
+/* D-Link products */
+/*product DLINK DSBS25 0x0100 DSB-S25 serial*/
+product DLINK DUBE100 0x1a00 10/100 Ethernet
+product DLINK DSB650TX4 0x200c 10/100 Ethernet
+product DLINK DWL120E 0x3200 DWL-120 rev E
+product DLINK DWL122 0x3700 DWL-122
+product DLINK DWLG120 0x3701 DWL-G120
+product DLINK DWL120F 0x3702 DWL-120 rev F
+product DLINK DWLAG132 0x3a00 DWL-AG132
+product DLINK DWLAG132_NF 0x3a01 DWL-AG132 (no firmware)
+product DLINK DWLG132 0x3a02 DWL-G132
+product DLINK DWLG132_NF 0x3a03 DWL-G132 (no firmware)
+product DLINK DWLAG122 0x3a04 DWL-AG122
+product DLINK DWLAG122_NF 0x3a05 DWL-AG122 (no firmware)
+product DLINK DWLG122 0x3c00 DWL-G122 b1 Wireless Adapter
+product DLINK DUBE100B1 0x3c05 DUB-E100 rev B1
+product DLINK DSB650C 0x4000 10Mbps Ethernet
+product DLINK DSB650TX1 0x4001 10/100 Ethernet
+product DLINK DSB650TX 0x4002 10/100 Ethernet
+product DLINK DSB650TX_PNA 0x4003 1/10/100 Ethernet
+product DLINK DSB650TX3 0x400b 10/100 Ethernet
+product DLINK DSB650TX2 0x4102 10/100 Ethernet
+product DLINK DSB650 0xabc1 10/100 Ethernet
+product DLINK2 DWLG122C1 0x3c03 DWL-G122 c1
+product DLINK2 WUA1340 0x3c04 WUA-1340
+product DLINK2 DWA111 0x3c06 DWA-111
+product DLINK2 DWA110 0x3c07 DWA-110
+
+/* DMI products */
+product DMI CFSM_RW 0xa109 CF/SM Reader/Writer
+
+/* DrayTek products */
+product DRAYTEK VIGOR550 0x0550 Vigor550
+
+/* Dynastream Innovations */
+product DYNASTREAM ANTDEVBOARD 0x1003 ANT dev board
+
+/* EIZO products */
+product EIZO HUB 0x0000 hub
+product EIZO MONITOR 0x0001 monitor
+
+/* ELCON Systemtechnik products */
+product ELCON PLAN 0x0002 Goldpfeil P-LAN
+
+/* Elecom products */
+product ELECOM MOUSE29UO 0x0002 mouse 29UO
+product ELECOM LDUSBTX0 0x200c LD-USB/TX
+product ELECOM LDUSBTX1 0x4002 LD-USB/TX
+product ELECOM LDUSBLTX 0x4005 LD-USBL/TX
+product ELECOM LDUSBTX2 0x400b LD-USB/TX
+product ELECOM LDUSB20 0x4010 LD-USB20
+product ELECOM UCSGT 0x5003 UC-SGT
+product ELECOM UCSGT0 0x5004 UC-SGT
+product ELECOM LDUSBTX3 0xabc1 LD-USB/TX
+
+/* Elsa products */
+product ELSA MODEM1 0x2265 ELSA Modem Board
+product ELSA USB2ETHERNET 0x3000 Microlink USB2Ethernet
+
+/* EMS products */
+product EMS DUAL_SHOOTER 0x0003 PSX gun controller converter
+
+/* Entrega products */
+product ENTREGA 1S 0x0001 1S serial
+product ENTREGA 2S 0x0002 2S serial
+product ENTREGA 1S25 0x0003 1S25 serial
+product ENTREGA 4S 0x0004 4S serial
+product ENTREGA E45 0x0005 E45 Ethernet
+product ENTREGA CENTRONICS 0x0006 Parallel Port
+product ENTREGA XX1 0x0008 Ethernet
+product ENTREGA 1S9 0x0093 1S9 serial
+product ENTREGA EZUSB 0x8000 EZ-USB
+/*product ENTREGA SERIAL 0x8001 DB25 Serial*/
+product ENTREGA 2U4S 0x8004 2U4S serial/usb hub
+product ENTREGA XX2 0x8005 Ethernet
+/*product ENTREGA SERIAL_DB9 0x8093 DB9 Serial*/
+
+/* Epson products */
+product EPSON PRINTER1 0x0001 USB Printer
+product EPSON PRINTER2 0x0002 ISD USB Smart Cable for Mac
+product EPSON PRINTER3 0x0003 ISD USB Smart Cable
+product EPSON PRINTER5 0x0005 USB Printer
+product EPSON 636 0x0101 Perfection 636U / 636Photo scanner
+product EPSON 610 0x0103 Perfection 610 scanner
+product EPSON 1200 0x0104 Perfection 1200U / 1200Photo scanner
+product EPSON 1600 0x0107 Expression 1600 scanner
+product EPSON 1640 0x010a Perfection 1640SU scanner
+product EPSON 1240 0x010b Perfection 1240U / 1240Photo scanner
+product EPSON 640U 0x010c Perfection 640U scanner
+product EPSON 1250 0x010f Perfection 1250U / 1250Photo scanner
+product EPSON 1650 0x0110 Perfection 1650 scanner
+product EPSON GT9700F 0x0112 GT-9700F scanner
+product EPSON GT9300UF 0x011b GT-9300UF scanner
+product EPSON 3200 0x011c Perfection 3200 scanner
+product EPSON 1260 0x011d Perfection 1260 scanner
+product EPSON 1660 0x011e Perfection 1660 scanner
+product EPSON 1670 0x011f Perfection 1670 scanner
+product EPSON 1270 0x0120 Perfection 1270 scanner
+product EPSON 2480 0x0121 Perfection 2480 scanner
+product EPSON 3590 0x0122 Perfection 3590 scanner
+product EPSON 4990 0x012a Perfection 4990 Photo scanner
+product EPSON STYLUS_875DC 0x0601 Stylus Photo 875DC Card Reader
+product EPSON STYLUS_895 0x0602 Stylus Photo 895 Card Reader
+product EPSON CX5400 0x0808 CX5400 scanner
+product EPSON 3500 0x080e CX-3500/3600/3650 MFP
+product EPSON RX425 0x080f Stylus Photo RX425 scanner
+product EPSON 4800 0x0819 CX4800 MP scanner
+product EPSON 4200 0x0820 CX4200 MP scanner
+product EPSON 5000 0x082b DX-50x0 MFP scanner
+product EPSON 6000 0x082e DX-60x0 MFP scanner
+product EPSON DX7400 0x0838 DX7400/CX7300 scanner
+product EPSON DX8400 0x0839 DX8400 scanner
+
+/* e-TEK Labs products */
+product ETEK 1COM 0x8007 Serial
+
+/* Extended Systems products */
+product EXTENDED XTNDACCESS 0x0100 XTNDAccess IrDA
+
+/* FEIYA products */
+product FEIYA 5IN1 0x1132 5-in-1 Card Reader
+
+/* Fiberline */
+product FIBERLINE WL430U 0x6003 WL-430U
+
+/* Fossil, Inc products */
+product FOSSIL WRISTPDA 0x0002 Wrist PDA
+
+/* Freecom products */
+product FREECOM DVD 0xfc01 DVD drive
+
+/* Fujitsu Siemens Computers products */
+product FSC E5400 0x1009 PrismGT USB 2.0 WLAN
+
+/* Future Technology Devices products */
+product FTDI SERIAL_8U100AX 0x8372 8U100AX Serial
+product FTDI SERIAL_8U232AM 0x6001 8U232AM Serial
+product FTDI SERIAL_2232C 0x6010 FT2232C Dual port Serial
+/* Gude Analog- und Digitalsysteme products also uses FTDI's id: */
+product FTDI TACTRIX_OPENPORT_13M 0xcc48 OpenPort 1.3 Mitsubishi
+product FTDI TACTRIX_OPENPORT_13S 0xcc49 OpenPort 1.3 Subaru
+product FTDI TACTRIX_OPENPORT_13U 0xcc4a OpenPort 1.3 Universal
+product FTDI EISCOU 0xe888 Expert ISDN Control USB
+product FTDI UOPTBR 0xe889 USB-RS232 OptoBridge
+product FTDI EMCU2D 0xe88a Expert mouseCLOCK USB II
+product FTDI PCMSFU 0xe88b Precision Clock MSF USB
+product FTDI EMCU2H 0xe88c Expert mouseCLOCK USB II HBG
+product FTDI USBSERIAL 0xfa00 Matrix Orbital USB Serial
+product FTDI MX2_3 0xfa01 Matrix Orbital MX2 or MX3
+product FTDI MX4_5 0xfa02 Matrix Orbital MX4 or MX5
+product FTDI LK202 0xfa03 Matrix Orbital VK/LK202 Family
+product FTDI LK204 0xfa04 Matrix Orbital VK/LK204 Family
+product FTDI CFA_632 0xfc08 Crystalfontz CFA-632 USB LCD
+product FTDI CFA_634 0xfc09 Crystalfontz CFA-634 USB LCD
+product FTDI CFA_633 0xfc0b Crystalfontz CFA-633 USB LCD
+product FTDI CFA_631 0xfc0c Crystalfontz CFA-631 USB LCD
+product FTDI CFA_635 0xfc0d Crystalfontz CFA-635 USB LCD
+product FTDI SEMC_DSS20 0xfc82 SEMC DSS-20 SyncStation
+
+/* Fuji photo products */
+product FUJIPHOTO MASS0100 0x0100 Mass Storage
+
+/* Fujitsu protducts */
+product FUJITSU AH_F401U 0x105b AH-F401U Air H device
+
+/* Garmin products */
+product GARMIN IQUE_3600 0x0004 iQue 3600
+
+/* General Instruments (Motorola) products */
+product GENERALINSTMNTS SB5100 0x5100 SURFboard SB5100 Cable modem
+
+/* Genesys Logic products */
+product GENESYS GL620USB 0x0501 GL620USB Host-Host interface
+product GENESYS GL650 0x0604 GL650 Hub
+product GENESYS GL641USB 0x0700 GL641USB CompactFlash Card Reader
+product GENESYS GL641USB2IDE_2 0x0701 GL641USB USB-IDE Bridge No 2
+product GENESYS GL641USB2IDE 0x0702 GL641USB USB-IDE Bridge
+product GENESYS GL641USB_2 0x0760 GL641USB 6-in-1 Card Reader
+
+/* GIGABYTE products */
+product GIGABYTE GN54G 0x8001 GN-54G
+product GIGABYTE GNBR402W 0x8002 GN-BR402W
+product GIGABYTE GNWLBM101 0x8003 GN-WLBM101
+product GIGABYTE GNWBKG 0x8007 GN-WBKG
+product GIGABYTE GNWB01GS 0x8008 GN-WB01GS
+product GIGABYTE GNWI05GS 0x800a GN-WI05GS
+
+/* Gigaset products */
+product GIGASET WLAN 0x0701 WLAN
+product GIGASET SMCWUSBTG 0x0710 SMCWUSBT-G
+product GIGASET SMCWUSBTG_NF 0x0711 SMCWUSBT-G (no firmware)
+product GIGASET AR5523 0x0712 AR5523
+product GIGASET AR5523_NF 0x0713 AR5523 (no firmware)
+product GIGASET RT2573 0x0722 RT2573
+
+/* Global Sun Technology product */
+product GLOBALSUN AR5523_1 0x7801 AR5523
+product GLOBALSUN AR5523_1_NF 0x7802 AR5523 (no firmware)
+product GLOBALSUN AR5523_2 0x7811 AR5523
+product GLOBALSUN AR5523_2_NF 0x7812 AR5523 (no firmware)
+
+/* Globespan products */
+product GLOBESPAN PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN
+product GLOBESPAN PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN
+
+/* G.Mate, Inc products */
+product GMATE YP3X00 0x1001 YP3X00 PDA
+
+/* GoHubs products */
+product GOHUBS GOCOM232 0x1001 GoCOM232 Serial
+
+/* Good Way Technology products */
+product GOODWAY GWUSB2E 0x6200 GWUSB2E
+product GOODWAY RT2573 0xc019 RT2573
+
+/* Gravis products */
+product GRAVIS GAMEPADPRO 0x4001 GamePad Pro
+
+/* GREENHOUSE products */
+product GREENHOUSE KANA21 0x0001 CF-writer with MP3
+
+/* Griffin Technology */
+product GRIFFIN IMATE 0x0405 iMate, ADB Adapter
+
+/* Guillemot Corporation */
+product GUILLEMOT DALEADER 0xa300 DA Leader
+product GUILLEMOT HWGUSB254 0xe000 HWGUSB2-54 WLAN
+product GUILLEMOT HWGUSB254LB 0xe010 HWGUSB2-54-LB
+product GUILLEMOT HWGUSB254V2AP 0xe020 HWGUSB2-54V2-AP
+
+/* Hagiwara products */
+product HAGIWARA FGSM 0x0002 FlashGate SmartMedia Card Reader
+product HAGIWARA FGCF 0x0003 FlashGate CompactFlash Card Reader
+product HAGIWARA FG 0x0005 FlashGate
+
+/* HAL Corporation products */
+product HAL IMR001 0x0011 Crossam2+USB IR commander
+
+/* Handspring, Inc. */
+product HANDSPRING VISOR 0x0100 Handspring Visor
+product HANDSPRING TREO 0x0200 Handspring Treo
+product HANDSPRING TREO600 0x0300 Handspring Treo 600
+
+/* Hauppauge Computer Works */
+product HAUPPAUGE WINTV_USB_FM 0x4d12 WinTV USB FM
+
+/* Hawking Technologies products */
+product HAWKING UF100 0x400c 10/100 USB Ethernet
+
+/* Hitachi, Ltd. products */
+product HITACHI DVDCAM_DZ_MV100A 0x0004 DVD-CAM DZ-MV100A Camcorder
+product HITACHI DVDCAM_USB 0x001e DVDCAM USB HS Interface
+
+/* HP products */
+product HP 895C 0x0004 DeskJet 895C
+product HP 4100C 0x0101 Scanjet 4100C
+product HP S20 0x0102 Photosmart S20
+product HP 880C 0x0104 DeskJet 880C
+product HP 4200C 0x0105 ScanJet 4200C
+product HP CDWRITERPLUS 0x0107 CD-Writer Plus
+product HP KBDHUB 0x010c Multimedia Keyboard Hub
+product HP G55XI 0x0111 OfficeJet G55xi
+product HP HN210W 0x011c HN210W 802.11b WLAN
+product HP 49GPLUS 0x0121 49g+ graphing calculator
+product HP 6200C 0x0201 ScanJet 6200C
+product HP S20b 0x0202 PhotoSmart S20
+product HP 815C 0x0204 DeskJet 815C
+product HP 3300C 0x0205 ScanJet 3300C
+product HP CDW8200 0x0207 CD-Writer Plus 8200e
+product HP MMKEYB 0x020c Multimedia keyboard
+product HP 1220C 0x0212 DeskJet 1220C
+product HP 810C 0x0304 DeskJet 810C/812C
+product HP 4300C 0x0305 Scanjet 4300C
+product HP CDW4E 0x0307 CD-Writer+ CD-4e
+product HP G85XI 0x0311 OfficeJet G85xi
+product HP 1200 0x0317 LaserJet 1200
+product HP 5200C 0x0401 Scanjet 5200C
+product HP 830C 0x0404 DeskJet 830C
+product HP 3400CSE 0x0405 ScanJet 3400cse
+product HP 6300C 0x0601 Scanjet 6300C
+product HP 840C 0x0604 DeskJet 840c
+product HP 2200C 0x0605 ScanJet 2200C
+product HP 5300C 0x0701 Scanjet 5300C
+product HP 4400C 0x0705 Scanjet 4400C
+product HP 82x0C 0x0b01 Scanjet 82x0C
+product HP 2300D 0x0b17 Laserjet 2300d
+product HP 970CSE 0x1004 Deskjet 970Cse
+product HP 5400C 0x1005 Scanjet 5400C
+product HP 2215 0x1016 iPAQ 22xx/Jornada 548
+product HP 568J 0x1116 Jornada 568
+product HP 930C 0x1204 DeskJet 930c
+product HP P2000U 0x1801 Inkjet P-2000U
+product HP 640C 0x2004 DeskJet 640c
+product HP 4670V 0x3005 ScanJet 4670v
+product HP P1100 0x3102 Photosmart P1100
+product HP HN210E 0x811c Ethernet HN210E
+product HP2 C500 0x6002 PhotoSmart C500
+
+/* HTC products */
+product HTC WINMOBILE 0x00ce HTC USB Sync
+product HTC PPC6700MODEM 0x00cf PPC6700 Modem
+product HTC SMARTPHONE 0x0a51 SmartPhone USB Sync
+
+/* HUAWEI products */
+product HUAWEI MOBILE 0x1001 Huawei Mobile
+product HUAWEI E270 0x1003 Huawei HSPA modem
+
+/* HUAWEI 3com products */
+product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g
+
+/* IBM Corporation */
+product IBM USBCDROMDRIVE 0x4427 USB CD-ROM Drive
+
+/* Imagination Technologies products */
+product IMAGINATION DBX1 0x2107 DBX1 DSP core
+
+/* Inside Out Networks products */
+product INSIDEOUT EDGEPORT4 0x0001 EdgePort/4 serial ports
+
+/* In-System products */
+product INSYSTEM F5U002 0x0002 Parallel printer
+product INSYSTEM ATAPI 0x0031 ATAPI Adapter
+product INSYSTEM ISD110 0x0200 IDE Adapter ISD110
+product INSYSTEM ISD105 0x0202 IDE Adapter ISD105
+product INSYSTEM USBCABLE 0x081a USB cable
+product INSYSTEM STORAGE_V2 0x5701 USB Storage Adapter V2
+
+/* Intel products */
+product INTEL EASYPC_CAMERA 0x0110 Easy PC Camera
+product INTEL TESTBOARD 0x9890 82930 test board
+
+/* Intersil products */
+product INTERSIL PRISM_GT 0x1000 PrismGT USB 2.0 WLAN
+product INTERSIL PRISM_2X 0x3642 Prism2.x or Atmel WLAN
+
+/* Interpid Control Systems products */
+product INTREPIDCS VALUECAN 0x0601 ValueCAN CAN bus interface
+product INTREPIDCS NEOVI 0x0701 NeoVI Blue vehicle bus interface
+
+/* I/O DATA products */
+product IODATA IU_CD2 0x0204 DVD Multi-plus unit iU-CD2
+product IODATA DVR_UEH8 0x0206 DVD Multi-plus unit DVR-UEH8
+product IODATA USBSSMRW 0x0314 USB-SSMRW SD-card
+product IODATA USBSDRW 0x031e USB-SDRW SD-card
+product IODATA USBETT 0x0901 USB ETT
+product IODATA USBETTX 0x0904 USB ETTX
+product IODATA USBETTXS 0x0913 USB ETTX
+product IODATA USBWNB11A 0x0919 USB WN-B11
+product IODATA USBWNB11 0x0922 USB Airport WN-B11
+product IODATA ETGUS2 0x0930 ETG-US2
+product IODATA USBRSAQ 0x0a03 Serial USB-RSAQ1
+product IODATA2 USB2SC 0x0a09 USB2.0-SCSI Bridge USB2-SC
+
+/* Iomega products */
+product IOMEGA ZIP100 0x0001 Zip 100
+product IOMEGA ZIP250 0x0030 Zip 250
+
+/* Ituner networks products */
+product ITUNERNET USBLCD2X20 0x0002 USB-LCD 2x20
+
+/* Jablotron products */
+product JABLOTRON PC60B 0x0001 PC-60B
+
+/* Jaton products */
+product JATON EDA 0x5704 Ethernet
+
+/* JVC products */
+product JVC GR_DX95 0x000a GR-DX95
+product JVC MP_PRX1 0x3008 MP-PRX1 Ethernet
+
+/* JRC products */
+product JRC AH_J3001V_J3002V 0x0001 AirH PHONE AH-J3001V/J3002V
+
+/* Kawatsu products */
+product KAWATSU MH4000P 0x0003 MiniHub 4000P
+
+/* Keisokugiken Corp. products */
+product KEISOKUGIKEN USBDAQ 0x0068 HKS-0200 USBDAQ
+
+/* Kensington products */
+product KENSINGTON ORBIT 0x1003 Orbit USB/PS2 trackball
+product KENSINGTON TURBOBALL 0x1005 TurboBall
+
+/* Keyspan products */
+product KEYSPAN USA28_NF 0x0101 USA-28 serial Adapter (no firmware)
+product KEYSPAN USA28X_NF 0x0102 USA-28X serial Adapter (no firmware)
+product KEYSPAN USA19_NF 0x0103 USA-19 serial Adapter (no firmware)
+product KEYSPAN USA18_NF 0x0104 USA-18 serial Adapter (no firmware)
+product KEYSPAN USA18X_NF 0x0105 USA-18X serial Adapter (no firmware)
+product KEYSPAN USA19W_NF 0x0106 USA-19W serial Adapter (no firmware)
+product KEYSPAN USA19 0x0107 USA-19 serial Adapter
+product KEYSPAN USA19W 0x0108 USA-19W serial Adapter
+product KEYSPAN USA49W_NF 0x0109 USA-49W serial Adapter (no firmware)
+product KEYSPAN USA49W 0x010a USA-49W serial Adapter
+product KEYSPAN USA19QI_NF 0x010b USA-19QI serial Adapter (no firmware)
+product KEYSPAN USA19QI 0x010c USA-19QI serial Adapter
+product KEYSPAN USA19Q_NF 0x010d USA-19Q serial Adapter (no firmware)
+product KEYSPAN USA19Q 0x010e USA-19Q serial Adapter
+product KEYSPAN USA28 0x010f USA-28 serial Adapter
+product KEYSPAN USA28XXB 0x0110 USA-28X/XB serial Adapter
+product KEYSPAN USA18 0x0111 USA-18 serial Adapter
+product KEYSPAN USA18X 0x0112 USA-18X serial Adapter
+product KEYSPAN USA28XB_NF 0x0113 USA-28XB serial Adapter (no firmware)
+product KEYSPAN USA28XA_NF 0x0114 USA-28XB serial Adapter (no firmware)
+product KEYSPAN USA28XA 0x0115 USA-28XA serial Adapter
+product KEYSPAN USA18XA_NF 0x0116 USA-18XA serial Adapter (no firmware)
+product KEYSPAN USA18XA 0x0117 USA-18XA serial Adapter
+product KEYSPAN USA19QW_NF 0x0118 USA-19WQ serial Adapter (no firmware)
+product KEYSPAN USA19QW 0x0119 USA-19WQ serial Adapter
+product KEYSPAN USA19HA 0x0121 USA-19HS serial Adapter
+product KEYSPAN UIA10 0x0201 UIA-10 remote control
+product KEYSPAN UIA11 0x0202 UIA-11 remote control
+
+/* Kingston products */
+product KINGSTON XX1 0x0008 Ethernet
+product KINGSTON KNU101TX 0x000a KNU101TX USB Ethernet
+
+/* Kawasaki products */
+product KLSI DUH3E10BT 0x0008 USB Ethernet
+product KLSI DUH3E10BTN 0x0009 USB Ethernet
+
+/* Kodak products */
+product KODAK DC220 0x0100 Digital Science DC220
+product KODAK DC260 0x0110 Digital Science DC260
+product KODAK DC265 0x0111 Digital Science DC265
+product KODAK DC290 0x0112 Digital Science DC290
+product KODAK DC240 0x0120 Digital Science DC240
+product KODAK DC280 0x0130 Digital Science DC280
+
+/* Konica Corp. Products */
+product KONICA CAMERA 0x0720 Digital Color Camera
+
+/* KYE products */
+product KYE NICHE 0x0001 Niche mouse
+product KYE NETSCROLL 0x0003 Genius NetScroll mouse
+product KYE FLIGHT2000 0x1004 Flight 2000 joystick
+product KYE VIVIDPRO 0x2001 ColorPage Vivid-Pro scanner
+
+/* Kyocera products */
+product KYOCERA FINECAM_S3X 0x0100 Finecam S3x
+product KYOCERA FINECAM_S4 0x0101 Finecam S4
+product KYOCERA FINECAM_S5 0x0103 Finecam S5
+product KYOCERA FINECAM_L3 0x0105 Finecam L3
+product KYOCERA AHK3001V 0x0203 AH-K3001V
+product KYOCERA2 CDMA_MSM_K 0x17da Qualcomm Kyocera CDMA Technologies MSM
+
+/* LaCie products */
+product LACIE HD 0xa601 Hard Disk
+product LACIE CDRW 0xa602 CD R/W
+
+/* Lexar products */
+product LEXAR JUMPSHOT 0x0001 jumpSHOT CompactFlash Reader
+product LEXAR CF_READER 0xb002 USB CF Reader
+
+/* Lexmark products */
+product LEXMARK S2450 0x0009 Optra S 2450
+
+/* Linksys products */
+product LINKSYS MAUSB2 0x0105 Camedia MAUSB-2
+product LINKSYS USB10TX1 0x200c USB10TX
+product LINKSYS USB10T 0x2202 USB10T Ethernet
+product LINKSYS USB100TX 0x2203 USB100TX Ethernet
+product LINKSYS USB100H1 0x2204 USB100H1 Ethernet/HPNA
+product LINKSYS USB10TA 0x2206 USB10TA Ethernet
+product LINKSYS USB10TX2 0x400b USB10TX
+product LINKSYS2 WUSB11 0x2219 WUSB11 Wireless Adapter
+product LINKSYS2 USB200M 0x2226 USB 2.0 10/100 Ethernet
+product LINKSYS3 WUSB11v28 0x2233 WUSB11 v2.8 Wireless Adapter
+product LINKSYS4 USB1000 0x0039 USB1000
+
+/* Logitech products */
+product LOGITECH M2452 0x0203 M2452 keyboard
+product LOGITECH M4848 0x0301 M4848 mouse
+product LOGITECH PAGESCAN 0x040f PageScan
+product LOGITECH QUICKCAMWEB 0x0801 QuickCam Web
+product LOGITECH QUICKCAMPRO 0x0810 QuickCam Pro
+product LOGITECH QUICKCAMEXP 0x0840 QuickCam Express
+product LOGITECH QUICKCAM 0x0850 QuickCam
+product LOGITECH N43 0xc000 N43
+product LOGITECH N48 0xc001 N48 mouse
+product LOGITECH MBA47 0xc002 M-BA47 mouse
+product LOGITECH WMMOUSE 0xc004 WingMan Gaming Mouse
+product LOGITECH BD58 0xc00c BD58 mouse
+product LOGITECH UN58A 0xc030 iFeel Mouse
+product LOGITECH UN53B 0xc032 iFeel MouseMan
+product LOGITECH WMPAD 0xc208 WingMan GamePad Extreme
+product LOGITECH WMRPAD 0xc20a WingMan RumblePad
+product LOGITECH WMJOY 0xc281 WingMan Force joystick
+product LOGITECH BB13 0xc401 USB-PS/2 Trackball
+product LOGITECH RK53 0xc501 Cordless mouse
+product LOGITECH RB6 0xc503 Cordless keyboard
+product LOGITECH MX700 0xc506 Cordless optical mouse
+product LOGITECH QUICKCAMPRO2 0xd001 QuickCam Pro
+
+/* Logitec Corp. products */
+product LOGITEC LDR_H443SU2 0x0033 DVD Multi-plus unit LDR-H443SU2
+product LOGITEC LDR_H443U2 0x00b3 DVD Multi-plus unit LDR-H443U2
+
+/* Lucent products */
+product LUCENT EVALKIT 0x1001 USS-720 evaluation kit
+
+/* Luwen products */
+product LUWEN EASYDISK 0x0005 EasyDisc
+
+/* Macally products */
+product MACALLY MOUSE1 0x0101 mouse
+
+/* MCT Corp. */
+product MCT HUB0100 0x0100 Hub
+product MCT DU_H3SP_USB232 0x0200 D-Link DU-H3SP USB BAY Hub
+product MCT USB232 0x0210 USB-232 Interface
+product MCT SITECOM_USB232 0x0230 Sitecom USB-232 Products
+
+/* Melco, Inc products */
+product MELCO LUATX1 0x0001 LUA-TX Ethernet
+product MELCO LUATX5 0x0005 LUA-TX Ethernet
+product MELCO LUA2TX5 0x0009 LUA2-TX Ethernet
+product MELCO LUAKTX 0x0012 LUA-KTX Ethernet
+product MELCO DUBPXXG 0x001c USB-IDE Bridge: DUB-PxxG
+product MELCO LUAU2KTX 0x003d LUA-U2-KTX Ethernet
+product MELCO KG54YB 0x005e WLI-U2-KG54-YB WLAN
+product MELCO KG54 0x0066 WLI-U2-KG54 WLAN
+product MELCO KG54AI 0x0067 WLI-U2-KG54-AI WLAN
+product MELCO NINWIFI 0x008b Nintendo Wi-Fi
+product MELCO PCOPRS1 0x00b3 PC-OP-RS1 RemoteStation
+product MELCO SG54HP 0x00d8 WLI-U2-SG54HP
+product MELCO G54HP 0x00d9 WLI-U2-G54HP
+product MELCO KG54L 0x00da WLI-U2-KG54L
+
+/* Merlin products */
+product MERLIN V620 0x1110 Merlin V620
+
+/* MetaGeek products */
+product METAGEEK WISPY1B 0x083e MetaGeek Wi-Spy
+product METAGEEK WISPY24X 0x083f MetaGeek Wi-Spy 2.4x
+
+/* Metricom products */
+product METRICOM RICOCHET_GS 0x0001 Ricochet GS
+
+/* MGE UPS Systems */
+product MGE UPS1 0x0001 MGE UPS SYSTEMS PROTECTIONCENTER 1
+product MGE UPS2 0xffff MGE UPS SYSTEMS PROTECTIONCENTER 2
+
+/* Micro Star International products */
+product MSI BT_DONGLE 0x1967 Bluetooth USB dongle
+product MSI UB11B 0x6823 UB11B
+product MSI RT2570 0x6861 RT2570
+product MSI RT2570_2 0x6865 RT2570
+product MSI RT2570_3 0x6869 RT2570
+product MSI RT2573_1 0x6874 RT2573
+product MSI RT2573_2 0x6877 RT2573
+product MSI RT2573_3 0xa861 RT2573
+product MSI RT2573_4 0xa874 RT2573
+
+/* Microdia products */
+product MICRODIA TWINKLECAM 0x600d TwinkleCam USB camera
+
+/* Microsoft products */
+product MICROSOFT SIDEPREC 0x0008 SideWinder Precision Pro
+product MICROSOFT INTELLIMOUSE 0x0009 IntelliMouse
+product MICROSOFT NATURALKBD 0x000b Natural Keyboard Elite
+product MICROSOFT DDS80 0x0014 Digital Sound System 80
+product MICROSOFT SIDEWINDER 0x001a Sidewinder Precision Racing Wheel
+product MICROSOFT INETPRO 0x001c Internet Keyboard Pro
+product MICROSOFT TBEXPLORER 0x0024 Trackball Explorer
+product MICROSOFT INTELLIEYE 0x0025 IntelliEye mouse
+product MICROSOFT INETPRO2 0x002b Internet Keyboard Pro
+product MICROSOFT MN510 0x006e MN510 Wireless
+product MICROSOFT MN110 0x007a 10/100 USB NIC
+product MICROSOFT WLINTELLIMOUSE 0x008c Wireless Optical IntelliMouse
+product MICROSOFT WLNOTEBOOK 0x00b9 Wireless Optical Mouse (Model 1023)
+product MICROSOFT WLNOTEBOOK2 0x00e1 Wireless Optical Mouse 3000 (Model 1056)
+product MICROSOFT WLNOTEBOOK3 0x00d2 Wireless Optical Mouse 3000 (Model 1049)
+product MICROSOFT WLUSBMOUSE 0x00b9 Wireless USB Mouse
+product MICROSOFT XBOX360 0x0292 XBOX 360 WLAN
+
+/* Microtech products */
+product MICROTECH SCSIDB25 0x0004 USB-SCSI-DB25
+product MICROTECH SCSIHD50 0x0005 USB-SCSI-HD50
+product MICROTECH DPCM 0x0006 USB CameraMate
+product MICROTECH FREECOM 0xfc01 Freecom USB-IDE
+
+/* Microtek products */
+product MICROTEK 336CX 0x0094 Phantom 336CX - C3 scanner
+product MICROTEK X6U 0x0099 ScanMaker X6 - X6U
+product MICROTEK C6 0x009a Phantom C6 scanner
+product MICROTEK 336CX2 0x00a0 Phantom 336CX - C3 scanner
+product MICROTEK V6USL 0x00a3 ScanMaker V6USL
+product MICROTEK V6USL2 0x80a3 ScanMaker V6USL
+product MICROTEK V6UL 0x80ac ScanMaker V6UL
+
+/* Microtune, Inc. products */
+product MICROTUNE BT_DONGLE 0x1000 Bluetooth USB dongle
+
+/* Midiman products */
+product MIDIMAN MIDISPORT2X2 0x1001 Midisport 2x2
+
+/* MindsAtWork products */
+product MINDSATWORK WALLET 0x0001 Digital Wallet
+
+/* Minolta Co., Ltd. */
+product MINOLTA 2300 0x4001 Dimage 2300
+product MINOLTA S304 0x4007 Dimage S304
+product MINOLTA X 0x4009 Dimage X
+product MINOLTA 5400 0x400e Dimage 5400
+product MINOLTA F300 0x4011 Dimage F300
+product MINOLTA E223 0x4017 Dimage E223
+
+/* Mitsumi products */
+product MITSUMI CDRRW 0x0000 CD-R/RW Drive
+product MITSUMI BT_DONGLE 0x641f Bluetooth USB dongle
+product MITSUMI FDD 0x6901 USB FDD
+
+/* Mobility products */
+product MOBILITY EA 0x0204 Ethernet
+product MOBILITY EASIDOCK 0x0304 EasiDock Ethernet
+
+/* MosChip products */
+product MOSCHIP MCS7703 0x7703 MCS7703 Serial Port Adapter
+product MOSCHIP MCS7830 0x7830 MCS7830 Ethernet
+
+/* Motorola products */
+product MOTOROLA MC141555 0x1555 MC141555 hub controller
+product MOTOROLA SB4100 0x4100 SB4100 USB Cable Modem
+product MOTOROLA2 A41XV32X 0x2a22 A41x/V32x Mobile Phones
+product MOTOROLA2 E398 0x4810 E398 Mobile Phone
+product MOTOROLA2 USBLAN 0x600c USBLAN
+product MOTOROLA2 USBLAN2 0x6027 USBLAN
+
+/* MultiTech products */
+product MULTITECH ATLAS 0xf101 MT5634ZBA-USB modem
+
+/* Mustek products */
+product MUSTEK 1200CU 0x0001 1200 CU scanner
+product MUSTEK 600CU 0x0002 600 CU scanner
+product MUSTEK 1200USB 0x0003 1200 USB scanner
+product MUSTEK 1200UB 0x0006 1200 UB scanner
+product MUSTEK 1200USBPLUS 0x0007 1200 USB Plus scanner
+product MUSTEK 1200CUPLUS 0x0008 1200 CU Plus scanner
+product MUSTEK BEARPAW1200F 0x0010 BearPaw 1200F scanner
+product MUSTEK BEARPAW1200TA 0x021e BearPaw 1200TA scanner
+product MUSTEK 600USB 0x0873 600 USB scanner
+product MUSTEK MDC800 0xa800 MDC-800 digital camera
+
+/* M-Systems products */
+product MSYSTEMS DISKONKEY 0x0010 DiskOnKey
+product MSYSTEMS DISKONKEY2 0x0011 DiskOnKey
+
+/* Myson products */
+product MYSON HEDEN 0x8818 USB-IDE
+
+/* National Semiconductor */
+product NATIONAL BEARPAW1200 0x1000 BearPaw 1200
+product NATIONAL BEARPAW2400 0x1001 BearPaw 2400
+
+/* NEC products */
+product NEC HUB 0x55aa hub
+product NEC HUB_B 0x55ab hub
+
+/* NEODIO products */
+product NEODIO ND3260 0x3260 8-in-1 Multi-format Flash Controller
+product NEODIO ND5010 0x5010 Multi-format Flash Controller
+
+/* Netac products */
+product NETAC CF_CARD 0x1060 USB-CF-Card
+product NETAC ONLYDISK 0x0003 OnlyDisk
+
+/* NetChip Technology Products */
+product NETCHIP TURBOCONNECT 0x1080 Turbo-Connect
+product NETCHIP CLIK_40 0xa140 USB Clik! 40
+product NETCHIP ETHERNETGADGET 0xa4a2 Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+
+/* Netgear products */
+product NETGEAR EA101 0x1001 Ethernet
+product NETGEAR EA101X 0x1002 Ethernet
+product NETGEAR FA101 0x1020 Ethernet 10/100, USB1.1
+product NETGEAR FA120 0x1040 USB 2.0 Ethernet
+product NETGEAR WG111V2_2 0x4240 PrismGT USB 2.0 WLAN
+product NETGEAR WG111U 0x4300 WG111U
+product NETGEAR WG111U_NF 0x4301 WG111U (no firmware)
+product NETGEAR2 MA101 0x4100 MA101
+product NETGEAR2 MA101B 0x4102 MA101 Rev B
+product NETGEAR3 WG111T 0x4250 WG111T
+product NETGEAR3 WG111T_NF 0x4251 WG111T (no firmware)
+product NETGEAR3 WPN111 0x5f00 WPN111
+product NETGEAR3 WPN111_NF 0x5f01 WPN111 (no firmware)
+
+/* Nikon products */
+product NIKON E990 0x0102 Digital Camera E990
+product NIKON LS40 0x4000 CoolScan LS40 ED
+product NIKON D300 0x041a Digital Camera D300
+
+/* NovaTech Products */
+product NOVATECH NV902 0x9020 NovaTech NV-902W
+product NOVATECH RT2573 0x9021 RT2573
+
+/* Novatel Wireless products */
+product NOVATEL V640 0x1100 Merlin V620
+product NOVATEL CDMA_MODEM 0x1110 Novatel Wireless Merlin CDMA
+product NOVATEL V620 0x1110 Merlin V620
+product NOVATEL V740 0x1120 Merlin V740
+product NOVATEL V720 0x1130 Merlin V720
+product NOVATEL U740 0x1400 Merlin U740
+product NOVATEL U740_2 0x1410 Merlin U740
+product NOVATEL U870 0x1420 Merlin U870
+product NOVATEL XU870 0x1430 Merlin XU870
+product NOVATEL X950D 0x1450 Merlin X950D
+product NOVATEL ES620 0x2100 ES620 CDMA
+product NOVATEL U720 0x2110 Merlin U720
+product NOVATEL U727 0x4100 Merlin U727 CDMA
+product NOVATEL U950D 0x4400 Novatel MC950D HSUPA
+product NOVATEL ZEROCD 0x5010 Novatel ZeroCD
+product NOVATEL2 FLEXPACKGPS 0x0100 NovAtel FlexPack GPS receiver
+
+/* Merlin products */
+product MERLIN V620 0x1110 Merlin V620
+
+/* Olympus products */
+product OLYMPUS C1 0x0102 C-1 Digital Camera
+product OLYMPUS C700 0x0105 C-700 Ultra Zoom
+
+/* OmniVision Technologies, Inc. products */
+product OMNIVISION OV511 0x0511 OV511 Camera
+product OMNIVISION OV511PLUS 0xa511 OV511+ Camera
+
+/* OnSpec Electronic, Inc. */
+product ONSPEC MDCFE_B_CF_READER 0xa000 MDCFE-B USB CF Reader
+product ONSPEC CFMS_RW 0xa001 SIIG/Datafab Memory Stick+CF Reader/Writer
+product ONSPEC READER 0xa003 Datafab-based Reader
+product ONSPEC CFSM_READER 0xa005 PNY/Datafab CF+SM Reader
+product ONSPEC CFSM_READER2 0xa006 Simple Tech/Datafab CF+SM Reader
+product ONSPEC MDSM_B_READER 0xa103 MDSM-B reader
+product ONSPEC CFSM_COMBO 0xa109 USB to CF + SM Combo (LC1)
+product ONSPEC UCF100 0xa400 FlashLink UCF-100 CompactFlash Reader
+product ONSPEC2 IMAGEMATE_SDDR55 0xa103 ImageMate SDDR55
+
+/* Option products */
+product OPTION VODAFONEMC3G 0x5000 Vodafone Mobile Connect 3G datacard
+product OPTION GT3G 0x6000 GlobeTrotter 3G datacard
+product OPTION GT3GQUAD 0x6300 GlobeTrotter 3G QUAD datacard
+product OPTION GT3GPLUS 0x6600 GlobeTrotter 3G+ datacard
+product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem
+
+/* OQO */
+product OQO WIFI01 0x0002 model 01 WiFi interface
+product OQO BT01 0x0003 model 01 Bluetooth interface
+product OQO ETHER01PLUS 0x7720 model 01+ Ethernet
+product OQO ETHER01 0x8150 model 01 Ethernet interface
+
+/* Palm Computing, Inc. product */
+product PALM SERIAL 0x0080 USB Serial
+product PALM M500 0x0001 Palm m500
+product PALM M505 0x0002 Palm m505
+product PALM M515 0x0003 Palm m515
+product PALM I705 0x0020 Palm i705
+product PALM TUNGSTEN_Z 0x0031 Palm Tungsten Z
+product PALM M125 0x0040 Palm m125
+product PALM M130 0x0050 Palm m130
+product PALM TUNGSTEN_T 0x0060 Palm Tungsten T
+product PALM ZIRE31 0x0061 Palm Zire 31
+product PALM ZIRE 0x0070 Palm Zire
+
+/* Panasonic products */
+product PANASONIC LS120CAM 0x0901 LS-120 Camera
+product PANASONIC KXL840AN 0x0d01 CD-R Drive KXL-840AN
+product PANASONIC KXLRW32AN 0x0d09 CD-R Drive KXL-RW32AN
+product PANASONIC KXLCB20AN 0x0d0a CD-R Drive KXL-CB20AN
+product PANASONIC KXLCB35AN 0x0d0e DVD-ROM & CD-R/RW
+product PANASONIC SDCAAE 0x1b00 MultiMediaCard
+
+/* Peracom products */
+product PERACOM SERIAL1 0x0001 Serial
+product PERACOM ENET 0x0002 Ethernet
+product PERACOM ENET3 0x0003 At Home Ethernet
+product PERACOM ENET2 0x0005 Ethernet
+
+/* Philips products */
+product PHILIPS DSS350 0x0101 DSS 350 Digital Speaker System
+product PHILIPS DSS 0x0104 DSS XXX Digital Speaker System
+product PHILIPS HUB 0x0201 hub
+product PHILIPS PCA646VC 0x0303 PCA646VC PC Camera
+product PHILIPS PCVC680K 0x0308 PCVC680K Vesta Pro PC Camera
+product PHILIPS DSS150 0x0471 DSS 150 Digital Speaker System
+product PHILIPS SNU5600 0x1236 SNU5600
+product PHILIPS UM10016 0x1552 ISP 1581 Hi-Speed USB MPEG2 Encoder Reference Kit
+product PHILIPS DIVAUSB 0x1801 DIVA USB mp3 player
+
+/* Philips Semiconductor products */
+product PHILIPSSEMI HUB1122 0x1122 hub
+
+/* P.I. Engineering products */
+product PIENGINEERING PS2USB 0x020b PS2 to Mac USB Adapter
+
+/* Planex Communications products */
+product PLANEX GW_US11H 0x14ea GW-US11H WLAN
+product PLANEX2 GW_US11S 0x3220 GW-US11S WLAN
+product PLANEX2 GW_US54GXS 0x5303 GW-US54GXS WLAN
+product PLANEX2 GWUS54HP 0xab01 GW-US54HP
+product PLANEX2 GWUS54MINI2 0xab50 GW-US54Mini2
+product PLANEX2 GWUS54SG 0xc002 GW-US54SG
+product PLANEX2 GWUS54GZL 0xc007 GW-US54GZL
+product PLANEX2 GWUS54GD 0xed01 GW-US54GD
+product PLANEX2 GWUSMM 0xed02 GW-USMM
+product PLANEX3 GWUS54GZ 0xab10 GW-US54GZ
+product PLANEX3 GU1000T 0xab11 GU-1000T
+product PLANEX3 GWUS54MINI 0xab13 GW-US54Mini
+
+/* Plextor Corp. */
+product PLEXTOR 40_12_40U 0x0011 PlexWriter 40/12/40U
+
+/* PLX products */
+product PLX TESTBOARD 0x9060 test board
+
+/* PNY products */
+product PNY ATTACHE2 0x0010 USB 2.0 Flash Drive
+
+/* PortGear products */
+product PORTGEAR EA8 0x0008 Ethernet
+product PORTGEAR EA9 0x0009 Ethernet
+
+/* Portsmith products */
+product PORTSMITH EEA 0x3003 Express Ethernet
+
+/* Primax products */
+product PRIMAX G2X300 0x0300 G2-200 scanner
+product PRIMAX G2E300 0x0301 G2E-300 scanner
+product PRIMAX G2300 0x0302 G2-300 scanner
+product PRIMAX G2E3002 0x0303 G2E-300 scanner
+product PRIMAX 9600 0x0340 Colorado USB 9600 scanner
+product PRIMAX 600U 0x0341 Colorado 600u scanner
+product PRIMAX 6200 0x0345 Visioneer 6200 scanner
+product PRIMAX 19200 0x0360 Colorado USB 19200 scanner
+product PRIMAX 1200U 0x0361 Colorado 1200u scanner
+product PRIMAX G600 0x0380 G2-600 scanner
+product PRIMAX 636I 0x0381 ReadyScan 636i
+product PRIMAX G2600 0x0382 G2-600 scanner
+product PRIMAX G2E600 0x0383 G2E-600 scanner
+product PRIMAX COMFORT 0x4d01 Comfort
+product PRIMAX MOUSEINABOX 0x4d02 Mouse-in-a-Box
+product PRIMAX PCGAUMS1 0x4d04 Sony PCGA-UMS1
+
+/* Prolific products */
+product PROLIFIC PL2301 0x0000 PL2301 Host-Host interface
+product PROLIFIC PL2302 0x0001 PL2302 Host-Host interface
+product PROLIFIC RSAQ2 0x04bb PL2303 Serial (IODATA USB-RSAQ2)
+product PROLIFIC PL2303 0x2303 PL2303 Serial (ATEN/IOGEAR UC232A)
+product PROLIFIC PL2305 0x2305 Parallel printer
+product PROLIFIC ATAPI4 0x2307 ATAPI-4 Controller
+product PROLIFIC PL2501 0x2501 PL2501 Host-Host interface
+product PROLIFIC PHAROS 0xaaa0 Prolific Pharos
+product PROLIFIC RSAQ3 0xaaa2 PL2303 Serial Adapter (IODATA USB-RSAQ3)
+product PROLIFIC2 WSIM 0x2001 Willcom WSIM
+
+/* Putercom products */
+product PUTERCOM UPA100 0x047e USB-1284 BRIDGE
+
+/* Qcom products */
+product QCOM RT2573 0x6196 RT2573
+product QCOM RT2573_2 0x6229 RT2573
+
+/* Qualcomm products */
+product QUALCOMM CDMA_MSM 0x6000 CDMA Technologies MSM phone
+product QUALCOMM2 RWT_FCT 0x3100 RWT FCT-CDMA 2000 1xRTT modem
+product QUALCOMM2 CDMA_MSM 0x3196 CDMA Technologies MSM modem
+product QUALCOMMINC CDMA_MSM 0x0001 CDMA Technologies MSM modem
+
+/* Qtronix products */
+product QTRONIX 980N 0x2011 Scorpion-980N keyboard
+
+/* Quickshot products */
+product QUICKSHOT STRIKEPAD 0x6238 USB StrikePad
+
+/* Radio Shack */
+product RADIOSHACK USBCABLE 0x4026 USB to Serial Cable
+
+/* Rainbow Technologies products */
+product RAINBOW IKEY2000 0x1200 i-Key 2000
+
+/* Ralink Technology products */
+product RALINK RT2570 0x1706 RT2500USB Wireless Adapter
+product RALINK RT2570_2 0x2570 RT2500USB Wireless Adapter
+product RALINK RT2573 0x2573 RT2501USB Wireless Adapter
+product RALINK RT2671 0x2671 RT2601USB Wireless Adapter
+product RALINK RT2570_3 0x9020 RT2500USB Wireless Adapter
+product RALINK RT2573_2 0x9021 RT2501USB Wireless Adapter
+
+/* ReakTek products */
+/* Green House and CompUSA OEM this part */
+product REALTEK USBKR100 0x8150 USBKR100 USB Ethernet
+
+/* Ricoh products */
+product RICOH VGPVCC2 0x1830 VGP-VCC2 Camera
+product RICOH VGPVCC3 0x1832 VGP-VCC3 Camera
+product RICOH VGPVCC2_2 0x1833 VGP-VCC2 Camera
+product RICOH VGPVCC2_3 0x1834 VGP-VCC2 Camera
+product RICOH VGPVCC7 0x183a VGP-VCC7 Camera
+product RICOH VGPVCC8 0x183b VGP-VCC8 Camera
+
+/* Roland products */
+product ROLAND UM1 0x0009 UM-1 MIDI I/F
+product ROLAND UM880N 0x0014 EDIROL UM-880 MIDI I/F (native)
+product ROLAND UM880G 0x0015 EDIROL UM-880 MIDI I/F (generic)
+
+/* Rockfire products */
+product ROCKFIRE GAMEPAD 0x2033 gamepad 203USB
+
+/* RATOC Systems products */
+product RATOC REXUSB60 0xb000 REX-USB60
+
+/* Sagem products */
+product SAGEM USBSERIAL 0x0027 USB-Serial Controller
+product SAGEM XG760A 0x004a XG-760A
+product SAGEM XG76NA 0x0062 XG-76NA
+
+/* Samsung products */
+product SAMSUNG ML6060 0x3008 ML-6060 laser printer
+product SAMSUNG YP_U2 0x5050 YP-U2 MP3 Player
+product SAMSUNG I500 0x6601 I500 Palm USB Phone
+
+/* Samsung Techwin products */
+product SAMSUNG_TECHWIN DIGIMAX_410 0x000a Digimax 410
+
+/* SanDisk products */
+product SANDISK SDDR05A 0x0001 ImageMate SDDR-05a
+product SANDISK SDDR31 0x0002 ImageMate SDDR-31
+product SANDISK SDDR05 0x0005 ImageMate SDDR-05
+product SANDISK SDDR12 0x0100 ImageMate SDDR-12
+product SANDISK SDDR09 0x0200 ImageMate SDDR-09
+product SANDISK SDDR75 0x0810 ImageMate SDDR-75
+product SANDISK SDCZ2_256 0x7104 Cruzer Mini 256MB
+product SANDISK SDCZ4_128 0x7112 Cruzer Micro 128MB
+product SANDISK SDCZ4_256 0x7113 Cruzer Micro 256MB
+
+/* Sanyo Electric products */
+product SANYO SCP4900 0x0701 Sanyo SCP-4900 USB Phone
+
+/* ScanLogic products */
+product SCANLOGIC SL11R 0x0002 SL11R IDE Adapter
+product SCANLOGIC 336CX 0x0300 Phantom 336CX - C3 scanner
+
+/* Senao products */
+product SENAO NUB8301 0x2000 NUB-8301
+
+/* ShanTou products */
+product SHANTOU ST268 0x0268 ST268
+product SHANTOU DM9601 0x9601 DM 9601
+
+/* Shark products */
+product SHARK PA 0x0400 Pocket Adapter
+
+/* Sharp products */
+product SHARP SL5500 0x8004 Zaurus SL-5500 PDA
+product SHARP SLA300 0x8005 Zaurus SL-A300 PDA
+product SHARP SL5600 0x8006 Zaurus SL-5600 PDA
+product SHARP SLC700 0x8007 Zaurus SL-C700 PDA
+product SHARP SLC750 0x9031 Zaurus SL-C750 PDA
+product SHARP WZERO3ES 0x9123 W-ZERO3 ES Smartphone
+
+/* Shuttle Technology products */
+product SHUTTLE EUSB 0x0001 E-USB Bridge
+product SHUTTLE EUSCSI 0x0002 eUSCSI Bridge
+product SHUTTLE SDDR09 0x0003 ImageMate SDDR09
+product SHUTTLE EUSBCFSM 0x0005 eUSB SmartMedia / CompactFlash Adapter
+product SHUTTLE ZIOMMC 0x0006 eUSB MultiMediaCard Adapter
+product SHUTTLE HIFD 0x0007 Sony Hifd
+product SHUTTLE EUSBATAPI 0x0009 eUSB ATA/ATAPI Adapter
+product SHUTTLE CF 0x000a eUSB CompactFlash Adapter
+product SHUTTLE EUSCSI_B 0x000b eUSCSI Bridge
+product SHUTTLE EUSCSI_C 0x000c eUSCSI Bridge
+product SHUTTLE CDRW 0x0101 CD-RW Device
+product SHUTTLE EUSBORCA 0x0325 eUSB ORCA Quad Reader
+
+/* Siemens products */
+product SIEMENS SPEEDSTREAM 0x1001 SpeedStream
+product SIEMENS SPEEDSTREAM22 0x1022 SpeedStream 1022
+product SIEMENS2 WLL013 0x001b WLL013
+product SIEMENS2 ES75 0x0034 GSM module MC35
+product SIEMENS2 WL54G 0x3c06 54g USB Network Adapter
+product SIEMENS3 SX1 0x0001 SX1
+product SIEMENS3 X65 0x0003 X65
+product SIEMENS3 X75 0x0004 X75
+
+/* Sierra Wireless products */
+product SIERRA AIRCARD580 0x0112 Sierra Wireless AirCard 580
+product SIERRA AIRCARD595 0x0019 Sierra Wireless AirCard 595
+product SIERRA AC595U 0x0120 Sierra Wireless AirCard 595U
+product SIERRA AC597E 0x0021 Sierra Wireless AirCard 597E
+product SIERRA C597 0x0023 Sierra Wireless Compass 597
+product SIERRA AC880 0x6850 Sierra Wireless AirCard 880
+product SIERRA AC881 0x6851 Sierra Wireless AirCard 881
+product SIERRA AC880E 0x6852 Sierra Wireless AirCard 880E
+product SIERRA AC881E 0x6853 Sierra Wireless AirCard 881E
+product SIERRA AC880U 0x6855 Sierra Wireless AirCard 880U
+product SIERRA AC881U 0x6856 Sierra Wireless AirCard 881U
+product SIERRA EM5625 0x0017 EM5625
+product SIERRA MC5720 0x0218 MC5720 Wireless Modem
+product SIERRA MC5720_2 0x0018 MC5720
+product SIERRA MC5725 0x0020 MC5725
+product SIERRA MINI5725 0x0220 Sierra Wireless miniPCI 5275
+product SIERRA MC8755_2 0x6802 MC8755
+product SIERRA MC8765 0x6803 MC8765
+product SIERRA MC8755 0x6804 MC8755
+product SIERRA AC875U 0x6812 AC875U HSDPA USB Modem
+product SIERRA MC8755_3 0x6813 MC8755 HSDPA
+product SIERRA MC8775_2 0x6815 MC8775
+product SIERRA AIRCARD875 0x6820 Aircard 875 HSDPA
+product SIERRA MC8780 0x6832 MC8780
+product SIERRA MC8781 0x6833 MC8781
+product SIERRA TRUINSTALL 0x0fff Aircard Tru Installer
+
+/* Sigmatel products */
+product SIGMATEL I_BEAD100 0x8008 i-Bead 100 MP3 Player
+
+/* SIIG products */
+/* Also: Omnidirectional Control Technology products */
+product SIIG DIGIFILMREADER 0x0004 DigiFilm-Combo Reader
+product SIIG WINTERREADER 0x0330 WINTERREADER Reader
+product SIIG2 USBTOETHER 0x0109 USB TO Ethernet
+product SIIG2 US2308 0x0421 Serial
+
+/* Silicom products */
+product SILICOM U2E 0x0001 U2E
+product SILICOM GPE 0x0002 Psion Gold Port Ethernet
+
+/* SI Labs */
+product SILABS POLOLU 0x803b Pololu Serial
+product SILABS ARGUSISP 0x8066 Argussoft ISP
+product SILABS CRUMB128 0x807a Crumb128 board
+product SILABS DEGREE 0x80ca Degree Controls Inc
+product SILABS TRAQMATE 0x80ed Track Systems Traqmate
+product SILABS SUUNTO 0x80f6 Suunto Sports Instrument
+product SILABS BURNSIDE 0x813d Burnside Telecon Deskmobile
+product SILABS HELICOM 0x815e Helicomm IP-Link 1220-DVM
+product SILABS CP2102 0xea60 SILABS USB UART
+product SILABS LIPOWSKY_JTAG 0x81c8 Lipowsky Baby-JTAG
+product SILABS LIPOWSKY_LIN 0x81e2 Lipowsky Baby-LIN
+product SILABS LIPOWSKY_HARP 0x8218 Lipowsky HARP-1
+product SILABS CP2102 0xea60 SILABS USB UARTa
+product SILABS CP210X_2 0xea61 CP210x Serial
+product SILABS2 DCU11CLONE 0xaa26 DCU-11 clone
+
+/* Silicon Portals Inc. */
+product SILICONPORTALS YAPPH_NF 0x0200 YAP Phone (no firmware)
+product SILICONPORTALS YAPPHONE 0x0201 YAP Phone
+
+/* Sirius Technologies products */
+product SIRIUS ROADSTER 0x0001 NetComm Roadster II 56 USB
+
+/* Sitecom products */
+product SITECOM LN029 0x182d USB 2.0 Ethernet
+product SITECOM SERIAL 0x2068 USB to serial cable (v2)
+product SITECOM2 WL022 0x182d WL-022
+
+/* Sitecom Europe products */
+product SITECOMEU LN028 0x061c LN-028
+product SITECOMEU WL113 0x9071 WL-113
+product SITECOMEU ZD1211B 0x9075 ZD1211B
+product SITECOMEU WL172 0x90ac WL-172
+product SITECOMEU WL113R2 0x9712 WL-113 rev 2
+
+/* Skanhex Technology products */
+product SKANHEX MD_7425 0x410a MD 7425 Camera
+product SKANHEX SX_520Z 0x5200 SX 520z Camera
+
+/* SmartBridges products */
+product SMARTBRIDGES SMARTLINK 0x0001 SmartLink USB Ethernet
+product SMARTBRIDGES SMARTNIC 0x0003 smartNIC 2 PnP Ethernet
+
+/* SMC products */
+product SMC 2102USB 0x0100 10Mbps Ethernet
+product SMC 2202USB 0x0200 10/100 Ethernet
+product SMC 2206USB 0x0201 EZ Connect USB Ethernet
+product SMC 2862WG 0xee13 EZ Connect Wireless Adapter
+product SMC2 2020HUB 0x2020 USB Hub
+product SMC3 2662WUSB 0xa002 2662W-AR Wireless
+
+/* SOHOware products */
+product SOHOWARE NUB100 0x9100 10/100 USB Ethernet
+product SOHOWARE NUB110 0x9110 10/100 USB Ethernet
+
+/* SOLID YEAR products */
+product SOLIDYEAR KEYBOARD 0x2101 Solid Year USB keyboard
+
+/* SONY products */
+product SONY DSC 0x0010 DSC cameras
+product SONY MS_NW_MS7 0x0025 Memorystick NW-MS7
+product SONY PORTABLE_HDD_V2 0x002b Portable USB Harddrive V2
+product SONY MSACUS1 0x002d Memorystick MSAC-US1
+product SONY HANDYCAM 0x002e Handycam
+product SONY MSC 0x0032 MSC memory stick slot
+product SONY CLIE_35 0x0038 Sony Clie v3.5
+product SONY MS_PEG_N760C 0x0058 PEG N760c Memorystick
+product SONY CLIE_40 0x0066 Sony Clie v4.0
+product SONY MS_MSC_U03 0x0069 Memorystick MSC-U03
+product SONY CLIE_40_MS 0x006d Sony Clie v4.0 Memory Stick slot
+product SONY CLIE_S360 0x0095 Sony Clie s360
+product SONY CLIE_41_MS 0x0099 Sony Clie v4.1 Memory Stick slot
+product SONY CLIE_41 0x009a Sony Clie v4.1
+product SONY CLIE_NX60 0x00da Sony Clie nx60
+product SONY CLIE_TH55 0x0144 Sony Clie th55
+product SONY CLIE_TJ37 0x0169 Sony Clie tj37
+
+/* Sony Ericsson products */
+product SONYERICSSON DCU10 0x0528 USB Cable
+
+/* SOURCENEXT products */
+product SOURCENEXT KEIKAI8 0x039f KeikaiDenwa 8
+product SOURCENEXT KEIKAI8_CHG 0x012e KeikaiDenwa 8 with charger
+
+/* SparkLAN products */
+product SPARKLAN RT2573 0x0004 RT2573
+
+/* Sphairon Access Systems GmbH products */
+product SPHAIRON UB801R 0x0110 UB801R
+
+/* STMicroelectronics products */
+product STMICRO BIOCPU 0x2016 Biometric Coprocessor
+product STMICRO COMMUNICATOR 0x7554 USB Communicator
+
+/* STSN products */
+product STSN STSN0001 0x0001 Internet Access Device
+
+/* SUN Corporation products */
+product SUNTAC DS96L 0x0003 SUNTAC U-Cable type D2
+product SUNTAC PS64P1 0x0005 SUNTAC U-Cable type P1
+product SUNTAC VS10U 0x0009 SUNTAC Slipper U
+product SUNTAC IS96U 0x000a SUNTAC Ir-Trinity
+product SUNTAC AS64LX 0x000b SUNTAC U-Cable type A3
+product SUNTAC AS144L4 0x0011 SUNTAC U-Cable type A4
+
+/* Sun Microsystems products */
+product SUN KEYBOARD 0x0005 Type 6 USB keyboard
+/* XXX The above is a North American PC style keyboard possibly */
+product SUN MOUSE 0x0100 Type 6 USB mouse
+
+/* Supra products */
+product DIAMOND2 SUPRAEXPRESS56K 0x07da Supra Express 56K modem
+product DIAMOND2 SUPRA2890 0x0b4a SupraMax 2890 56K Modem
+product DIAMOND2 RIO600USB 0x5001 Rio 600 USB
+product DIAMOND2 RIO800USB 0x5002 Rio 800 USB
+
+/* Surecom Technology products */
+product SURECOM RT2570 0x11f3 RT2570
+product SURECOM RT2573 0x31f3 RT2573
+
+/* Sweex products */
+product SWEEX ZD1211 0x1809 ZD1211
+
+/* System TALKS, Inc. */
+product SYSTEMTALKS SGCX2UL 0x1920 SGC-X2UL
+
+/* Tapwave products */
+product TAPWAVE ZODIAC 0x0100 Zodiac
+
+/* Taugagreining products */
+product TAUGA CAMERAMATE 0x0005 CameraMate (DPCM_USB)
+
+/* TDK products */
+product TDK UPA9664 0x0115 USB-PDC Adapter UPA9664
+product TDK UCA1464 0x0116 USB-cdmaOne Adapter UCA1464
+product TDK UHA6400 0x0117 USB-PHS Adapter UHA6400
+product TDK UPA6400 0x0118 USB-PHS Adapter UPA6400
+product TDK BT_DONGLE 0x0309 Bluetooth USB dongle
+
+/* TEAC products */
+product TEAC FD05PUB 0x0000 FD-05PUB floppy
+
+/* Tekram Technology products */
+product TEKRAM QUICKWLAN 0x1630 QuickWLAN
+product TEKRAM ZD1211_1 0x5630 ZD1211
+product TEKRAM ZD1211_2 0x6630 ZD1211
+
+/* Telex Communications products */
+product TELEX MIC1 0x0001 Enhanced USB Microphone
+
+/* Ten X Technology, Inc. */
+product TENX UAUDIO0 0xf211 USB audio headset
+
+/* Texas Intel products */
+product TI UTUSB41 0x1446 UT-USB41 hub
+product TI TUSB2046 0x2046 TUSB2046 hub
+
+/* Thrustmaster products */
+product THRUST FUSION_PAD 0xa0a3 Fusion Digital Gamepad
+
+/* Topre Corporation products */
+product TOPRE HHKB 0x0100 HHKB Professional
+
+/* Toshiba Corporation products */
+product TOSHIBA POCKETPC_E740 0x0706 PocketPC e740
+
+/* Trek Technology products */
+product TREK THUMBDRIVE 0x1111 ThumbDrive
+product TREK MEMKEY 0x8888 IBM USB Memory Key
+product TREK THUMBDRIVE_8MB 0x9988 ThumbDrive_8MB
+
+/* Tripp-Lite products */
+product TRIPPLITE U209 0x2008 Serial
+
+/* Trumpion products */
+product TRUMPION T33520 0x1001 T33520 USB Flash Card Controller
+product TRUMPION C3310 0x1100 Comotron C3310 MP3 player
+product TRUMPION MP3 0x1200 MP3 player
+
+/* TwinMOS */
+product TWINMOS G240 0xa006 G240
+product TWINMOS MDIV 0x1325 Memory Disk IV
+
+/* Ubiquam products */
+product UBIQUAM UALL 0x3100 CDMA 1xRTT USB Modem (U-100/105/200/300/520)
+
+/* Ultima products */
+product ULTIMA 1200UBPLUS 0x4002 1200 UB Plus scanner
+
+/* UMAX products */
+product UMAX ASTRA1236U 0x0002 Astra 1236U Scanner
+product UMAX ASTRA1220U 0x0010 Astra 1220U Scanner
+product UMAX ASTRA2000U 0x0030 Astra 2000U Scanner
+product UMAX ASTRA2100U 0x0130 Astra 2100U Scanner
+product UMAX ASTRA2200U 0x0230 Astra 2200U Scanner
+product UMAX ASTRA3400 0x0060 Astra 3400 Scanner
+
+/* U-MEDIA Communications products */
+product UMEDIA TEW444UBEU 0x3006 TEW-444UB EU
+product UMEDIA TEW444UBEU_NF 0x3007 TEW-444UB EU (no firmware)
+product UMEDIA TEW429UB_A 0x300a TEW-429UB_A
+product UMEDIA TEW429UB 0x300b TEW-429UB
+product UMEDIA TEW429UBC1 0x300d TEW-429UB C1
+product UMEDIA ALL0298V2 0x3204 ALL0298 v2
+product UMEDIA AR5523_2 0x3205 AR5523
+product UMEDIA AR5523_2_NF 0x3206 AR5523 (no firmware)
+
+/* Universal Access products */
+product UNIACCESS PANACHE 0x0101 Panache Surf USB ISDN Adapter
+
+/* U.S. Robotics products */
+product USR USR5423 0x0121 USR5423 WLAN
+
+/* VIA Technologies products */
+product VIA USB2IDEBRIDGE 0x6204 USB 2.0 IDE Bridge
+
+/* USI products */
+product USI MC60 0x10c5 MC60 Serial
+
+/* VidzMedia products */
+product VIDZMEDIA MONSTERTV 0x4fb1 MonsterTV P2H
+
+/* Vision products */
+product VISION VC6452V002 0x0002 CPiA Camera
+
+/* Visioneer products */
+product VISIONEER 7600 0x0211 OneTouch 7600
+product VISIONEER 5300 0x0221 OneTouch 5300
+product VISIONEER 3000 0x0224 Scanport 3000
+product VISIONEER 6100 0x0231 OneTouch 6100
+product VISIONEER 6200 0x0311 OneTouch 6200
+product VISIONEER 8100 0x0321 OneTouch 8100
+product VISIONEER 8600 0x0331 OneTouch 8600
+
+/* Vivitar products */
+product VIVITAR 35XX 0x0003 Vivicam 35Xx
+
+/* VTech products */
+product VTECH RT2570 0x3012 RT2570
+product VTECH ZD1211B 0x3014 ZD1211B
+
+/* Wacom products */
+product WACOM CT0405U 0x0000 CT-0405-U Tablet
+product WACOM GRAPHIRE 0x0010 Graphire
+product WACOM GRAPHIRE3_4X5 0x0013 Graphire 3 4x5
+product WACOM INTUOSA5 0x0021 Intuos A5
+product WACOM GD0912U 0x0022 Intuos 9x12 Graphics Tablet
+/* WCH products*/
+product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge
+/* Western Digital products */
+product WESTERN COMBO 0x0200 Firewire USB Combo
+product WESTERN EXTHDD 0x0400 External HDD
+product WESTERN HUB 0x0500 USB HUB
+product WESTERN MYBOOK 0x0901 MyBook External HDD
+
+/* Windbond Electronics */
+product WINBOND UH104 0x5518 4-port USB Hub
+
+/* WinMaxGroup products */
+product WINMAXGROUP FLASH64MC 0x6660 USB Flash Disk 64M-C
+
+/* Wistron NeWeb products */
+product WISTRONNEWEB UR045G 0x0427 PrismGT USB 2.0 WLAN
+product WISTRONNEWEB UR055G 0x0711 UR055G
+product WISTRONNEWEB AR5523_1 0x0826 AR5523
+product WISTRONNEWEB AR5523_1_NF 0x0827 AR5523 (no firmware)
+product WISTRONNEWEB AR5523_2 0x082a AR5523
+product WISTRONNEWEB AR5523_2_NF 0x0829 AR5523 (no firmware)
+
+/* Xerox products */
+product XEROX WCM15 0xffef WorkCenter M15
+
+/* Xirlink products */
+product XIRLINK PCCAM 0x8080 IBM PC Camera
+
+/* Xyratex products */
+product XYRATEX PRISM_GT_1 0x2000 PrismGT USB 2.0 WLAN
+product XYRATEX PRISM_GT_2 0x2002 PrismGT USB 2.0 WLAN
+
+/* Y-E Data products */
+product YEDATA FLASHBUSTERU 0x0000 Flashbuster-U
+
+/* Yamaha products */
+product YAMAHA UX256 0x1000 UX256 MIDI I/F
+product YAMAHA UX96 0x1008 UX96 MIDI I/F
+product YAMAHA RTA54I 0x4000 NetVolante RTA54i Broadband&ISDN Router
+product YAMAHA RTA55I 0x4004 NetVolante RTA55i Broadband VoIP Router
+product YAMAHA RTW65B 0x4001 NetVolante RTW65b Broadband Wireless Router
+product YAMAHA RTW65I 0x4002 NetVolante RTW65i Broadband&ISDN Wireless Router
+
+/* Yano products */
+product YANO U640MO 0x0101 U640MO-03
+product YANO FW800HD 0x05fc METALWEAR-HDD
+
+/* Z-Com products */
+product ZCOM M4Y750 0x0001 M4Y-750
+product ZCOM XI725 0x0002 XI-725/726
+product ZCOM XI735 0x0005 XI-735
+product ZCOM XG703A 0x0008 PrismGT USB 2.0 WLAN
+product ZCOM ZD1211 0x0011 ZD1211
+product ZCOM AR5523 0x0012 AR5523
+product ZCOM AR5523_NF 0x0013 AR5523 driver (no firmware)
+product ZCOM ZD1211B 0x001a ZD1211B
+
+/* Zinwell products */
+product ZINWELL RT2570 0x0260 RT2570
+
+/* Zoom Telephonics, Inc. products */
+product ZOOM 2986L 0x9700 2986L Fax modem
+
+/* Zoran Microelectronics products */
+product ZORAN EX20DSC 0x4343 Digital Camera EX-20 DSC
+
+/* Zydas Technology Corporation products */
+product ZYDAS ZD1211 0x1211 ZD1211 WLAN abg
+product ZYDAS ZD1211B 0x1215 ZD1211B
+
+/* ZyXEL Communication Co. products */
+product ZYXEL OMNI56K 0x1500 Omni 56K Plus
+product ZYXEL 980N 0x2011 Scorpion-980N keyboard
+product ZYXEL ZYAIRG220 0x3401 ZyAIR G-220
+product ZYXEL G200V2 0x3407 G-200 v2
+product ZYXEL AG225H 0x3409 AG-225H
+product ZYXEL M202 0x340a M-202
+product ZYXEL G220V2 0x340f G-220 v2
+product ZYXEL G202 0x3410 G-202