summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb')
-rw-r--r--sys/dev/usb/README.TXT411
-rw-r--r--sys/dev/usb/bluetooth/TODO.TXT18
-rw-r--r--sys/dev/usb/bluetooth/ng_ubt.c1718
-rw-r--r--sys/dev/usb/bluetooth/ng_ubt_var.h131
-rw-r--r--sys/dev/usb/bluetooth/ubtbcmfw.c430
-rw-r--r--sys/dev/usb/controller/at91dci.c2467
-rw-r--r--sys/dev/usb/controller/at91dci.h245
-rw-r--r--sys/dev/usb/controller/at91dci_atmelarm.c347
-rw-r--r--sys/dev/usb/controller/atmegadci.c2327
-rw-r--r--sys/dev/usb/controller/atmegadci.h273
-rw-r--r--sys/dev/usb/controller/atmegadci_atmelarm.c27
-rw-r--r--sys/dev/usb/controller/ehci.c3965
-rw-r--r--sys/dev/usb/controller/ehci.h532
-rw-r--r--sys/dev/usb/controller/ehci_ixp4xx.c348
-rw-r--r--sys/dev/usb/controller/ehci_mbus.c364
-rw-r--r--sys/dev/usb/controller/ehci_pci.c486
-rw-r--r--sys/dev/usb/controller/musb_otg.c2875
-rw-r--r--sys/dev/usb/controller/musb_otg.h407
-rw-r--r--sys/dev/usb/controller/musb_otg_atmelarm.c239
-rw-r--r--sys/dev/usb/controller/ohci.c2862
-rw-r--r--sys/dev/usb/controller/ohci.h366
-rw-r--r--sys/dev/usb/controller/ohci_atmelarm.c223
-rw-r--r--sys/dev/usb/controller/ohci_pci.c387
-rw-r--r--sys/dev/usb/controller/uhci.c3381
-rw-r--r--sys/dev/usb/controller/uhci.h321
-rw-r--r--sys/dev/usb/controller/uhci_pci.c443
-rw-r--r--sys/dev/usb/controller/usb_controller.c620
-rw-r--r--sys/dev/usb/controller/uss820dci.c2489
-rw-r--r--sys/dev/usb/controller/uss820dci.h377
-rw-r--r--sys/dev/usb/controller/uss820dci_atmelarm.c238
-rw-r--r--sys/dev/usb/image/uscanner.c643
-rw-r--r--sys/dev/usb/input/uhid.c789
-rw-r--r--sys/dev/usb/input/ukbd.c1489
-rw-r--r--sys/dev/usb/input/ums.c901
-rw-r--r--sys/dev/usb/input/usb_rdesc.h276
-rw-r--r--sys/dev/usb/misc/udbp.c853
-rw-r--r--sys/dev/usb/misc/udbp.h80
-rw-r--r--sys/dev/usb/misc/ufm.c329
-rw-r--r--sys/dev/usb/net/if_aue.c1054
-rw-r--r--sys/dev/usb/net/if_auereg.h220
-rw-r--r--sys/dev/usb/net/if_axe.c1076
-rw-r--r--sys/dev/usb/net/if_axereg.h196
-rw-r--r--sys/dev/usb/net/if_cdce.c771
-rw-r--r--sys/dev/usb/net/if_cdcereg.h68
-rw-r--r--sys/dev/usb/net/if_cue.c645
-rw-r--r--sys/dev/usb/net/if_cuereg.h132
-rw-r--r--sys/dev/usb/net/if_kue.c704
-rw-r--r--sys/dev/usb/net/if_kuefw.h685
-rw-r--r--sys/dev/usb/net/if_kuereg.h141
-rw-r--r--sys/dev/usb/net/if_rue.c913
-rw-r--r--sys/dev/usb/net/if_ruereg.h183
-rw-r--r--sys/dev/usb/net/if_udav.c856
-rw-r--r--sys/dev/usb/net/if_udavreg.h166
-rw-r--r--sys/dev/usb/net/usb_ethernet.c587
-rw-r--r--sys/dev/usb/net/usb_ethernet.h122
-rw-r--r--sys/dev/usb/quirk/usb_quirk.c397
-rw-r--r--sys/dev/usb/quirk/usb_quirk.h59
-rw-r--r--sys/dev/usb/serial/u3g.c583
-rw-r--r--sys/dev/usb/serial/uark.c407
-rw-r--r--sys/dev/usb/serial/ubsa.c634
-rw-r--r--sys/dev/usb/serial/ubser.c518
-rw-r--r--sys/dev/usb/serial/uchcom.c883
-rw-r--r--sys/dev/usb/serial/ucycom.c564
-rw-r--r--sys/dev/usb/serial/ufoma.c1212
-rw-r--r--sys/dev/usb/serial/uftdi.c784
-rw-r--r--sys/dev/usb/serial/uftdi_reg.h340
-rw-r--r--sys/dev/usb/serial/ugensa.c352
-rw-r--r--sys/dev/usb/serial/uipaq.c1314
-rw-r--r--sys/dev/usb/serial/ulpt.c726
-rw-r--r--sys/dev/usb/serial/umct.c579
-rw-r--r--sys/dev/usb/serial/umodem.c788
-rw-r--r--sys/dev/usb/serial/umoscom.c672
-rw-r--r--sys/dev/usb/serial/uplcom.c831
-rw-r--r--sys/dev/usb/serial/usb_serial.c1127
-rw-r--r--sys/dev/usb/serial/usb_serial.h198
-rw-r--r--sys/dev/usb/serial/uslcom.c539
-rw-r--r--sys/dev/usb/serial/uvisor.c610
-rw-r--r--sys/dev/usb/serial/uvscom.c707
-rw-r--r--sys/dev/usb/sound/uaudio.c3750
-rw-r--r--sys/dev/usb/sound/uaudio.h63
-rw-r--r--sys/dev/usb/sound/uaudio_pcm.c234
-rw-r--r--sys/dev/usb/sound/uaudio_reg.h406
-rw-r--r--sys/dev/usb/storage/ata-usb.c1102
-rw-r--r--sys/dev/usb/storage/rio500_usb.h48
-rw-r--r--sys/dev/usb/storage/umass.c3619
-rw-r--r--sys/dev/usb/storage/urio.c479
-rw-r--r--sys/dev/usb/storage/ustorage_fs.c1897
-rw-r--r--sys/dev/usb/template/usb_template.c1312
-rw-r--r--sys/dev/usb/template/usb_template.h102
-rw-r--r--sys/dev/usb/template/usb_template_cdce.c292
-rw-r--r--sys/dev/usb/template/usb_template_msc.c199
-rw-r--r--sys/dev/usb/template/usb_template_mtp.c262
-rw-r--r--sys/dev/usb/ufm_ioctl.h39
-rw-r--r--sys/dev/usb/usb.h619
-rw-r--r--sys/dev/usb/usb_bus.h104
-rw-r--r--sys/dev/usb/usb_busdma.c1426
-rw-r--r--sys/dev/usb/usb_busdma.h183
-rw-r--r--sys/dev/usb/usb_cdc.h191
-rw-r--r--sys/dev/usb/usb_compat_linux.c1653
-rw-r--r--sys/dev/usb/usb_compat_linux.h472
-rw-r--r--sys/dev/usb/usb_controller.h199
-rw-r--r--sys/dev/usb/usb_core.c40
-rw-r--r--sys/dev/usb/usb_core.h467
-rw-r--r--sys/dev/usb/usb_debug.c152
-rw-r--r--sys/dev/usb/usb_debug.h70
-rw-r--r--sys/dev/usb/usb_defs.h77
-rw-r--r--sys/dev/usb/usb_dev.c2814
-rw-r--r--sys/dev/usb/usb_dev.h168
-rw-r--r--sys/dev/usb/usb_device.c2192
-rw-r--r--sys/dev/usb/usb_device.h187
-rw-r--r--sys/dev/usb/usb_dynamic.c155
-rw-r--r--sys/dev/usb/usb_dynamic.h70
-rw-r--r--sys/dev/usb/usb_endian.h119
-rw-r--r--sys/dev/usb/usb_error.c73
-rw-r--r--sys/dev/usb/usb_error.h63
-rw-r--r--sys/dev/usb/usb_generic.c2195
-rw-r--r--sys/dev/usb/usb_generic.h33
-rw-r--r--sys/dev/usb/usb_handle_request.c756
-rw-r--r--sys/dev/usb/usb_handle_request.h30
-rw-r--r--sys/dev/usb/usb_hid.c582
-rw-r--r--sys/dev/usb/usb_hid.h95
-rw-r--r--sys/dev/usb/usb_hub.c1842
-rw-r--r--sys/dev/usb/usb_hub.h80
-rw-r--r--sys/dev/usb/usb_if.m52
-rw-r--r--sys/dev/usb/usb_ioctl.h292
-rw-r--r--sys/dev/usb/usb_lookup.c134
-rw-r--r--sys/dev/usb/usb_lookup.h122
-rw-r--r--sys/dev/usb/usb_mbuf.c77
-rw-r--r--sys/dev/usb/usb_mbuf.h102
-rw-r--r--sys/dev/usb/usb_mfunc.h78
-rw-r--r--sys/dev/usb/usb_msctest.c578
-rw-r--r--sys/dev/usb/usb_msctest.h33
-rw-r--r--sys/dev/usb/usb_parse.c225
-rw-r--r--sys/dev/usb/usb_parse.h41
-rw-r--r--sys/dev/usb/usb_pci.h39
-rw-r--r--sys/dev/usb/usb_process.c426
-rw-r--r--sys/dev/usb/usb_process.h88
-rw-r--r--sys/dev/usb/usb_request.c1486
-rw-r--r--sys/dev/usb/usb_request.h103
-rw-r--r--sys/dev/usb/usb_revision.h65
-rw-r--r--sys/dev/usb/usb_sw_transfer.c170
-rw-r--r--sys/dev/usb/usb_sw_transfer.h62
-rw-r--r--sys/dev/usb/usb_transfer.c2826
-rw-r--r--sys/dev/usb/usb_transfer.h129
-rw-r--r--sys/dev/usb/usb_util.c346
-rw-r--r--sys/dev/usb/usb_util.h57
-rw-r--r--sys/dev/usb/usbdevs2527
-rw-r--r--sys/dev/usb/usbhid.h175
-rw-r--r--sys/dev/usb/wlan/if_rum.c2435
-rw-r--r--sys/dev/usb/wlan/if_rumfw.h213
-rw-r--r--sys/dev/usb/wlan/if_rumreg.h235
-rw-r--r--sys/dev/usb/wlan/if_rumvar.h156
-rw-r--r--sys/dev/usb/wlan/if_ural.c2364
-rw-r--r--sys/dev/usb/wlan/if_uralreg.h211
-rw-r--r--sys/dev/usb/wlan/if_uralvar.h155
-rw-r--r--sys/dev/usb/wlan/if_zyd.c3121
-rw-r--r--sys/dev/usb/wlan/if_zydfw.h1144
-rw-r--r--sys/dev/usb/wlan/if_zydreg.h1338
-rw-r--r--sys/dev/usb/wlan/usb_wlan.h57
159 files changed, 110593 insertions, 0 deletions
diff --git a/sys/dev/usb/README.TXT b/sys/dev/usb/README.TXT
new file mode 100644
index 0000000..d24770c
--- /dev/null
+++ b/sys/dev/usb/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/usb/usb_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/usb/net/if_axe.c
+/sys/dev/usb/net/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/usb/bluetooth/TODO.TXT b/sys/dev/usb/bluetooth/TODO.TXT
new file mode 100644
index 0000000..b0d6695
--- /dev/null
+++ b/sys/dev/usb/bluetooth/TODO.TXT
@@ -0,0 +1,18 @@
+$Id: TODO,v 1.1 2002/11/24 19:46:56 max Exp $
+$FreeBSD$
+
+1) SMP/Locking
+
+ The code makes use of ng_send_fn() whenever possible. Just
+ need to verify and make sure i did it right
+
+2) Firmware upgrade
+
+ According to Bluetooth spec device may present third interface
+ to perform firmware upgrade. 3Com USB Bluetooth dongle has
+ such interface. Need to implement set of Netgraph messages.
+
+3) Isochronous USB transfers (SCO data)
+
+ Tried to fix isochrounous transfers, which are still disabled
+ by default.
diff --git a/sys/dev/usb/bluetooth/ng_ubt.c b/sys/dev/usb/bluetooth/ng_ubt.c
new file mode 100644
index 0000000..07ac2194
--- /dev/null
+++ b/sys/dev/usb/bluetooth/ng_ubt.c
@@ -0,0 +1,1718 @@
+/*
+ * ng_ubt.c
+ */
+
+/*-
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * $Id: ng_ubt.c,v 1.16 2003/10/10 19:15:06 max Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * NOTE: ng_ubt2 driver has a split personality. On one side it is
+ * a USB device driver and on the other it is a Netgraph node. This
+ * driver will *NOT* create traditional /dev/ enties, only Netgraph
+ * node.
+ *
+ * NOTE ON LOCKS USED: ng_ubt2 drives uses 2 locks (mutexes)
+ *
+ * 1) sc_if_mtx - lock for device's interface #0 and #1. This lock is used
+ * by USB for any USB request going over device's interface #0 and #1,
+ * i.e. interrupt, control, bulk and isoc. transfers.
+ *
+ * 2) sc_ng_mtx - this lock is used to protect shared (between USB, Netgraph
+ * and Taskqueue) data, such as outgoing mbuf queues, task flags and hook
+ * pointer. This lock *SHOULD NOT* be grabbed for a long time. In fact,
+ * think of it as a spin lock.
+ *
+ * NOTE ON LOCKING STRATEGY: ng_ubt2 driver operates in 3 different contexts.
+ *
+ * 1) USB context. This is where all the USB related stuff happens. All
+ * callbacks run in this context. All callbacks are called (by USB) with
+ * appropriate interface lock held. It is (generally) allowed to grab
+ * any additional locks.
+ *
+ * 2) Netgraph context. This is where all the Netgraph related stuff happens.
+ * Since we mark node as WRITER, the Netgraph node will be "locked" (from
+ * Netgraph point of view). Any variable that is only modified from the
+ * Netgraph context does not require any additonal locking. It is generally
+ * *NOT* allowed to grab *ANY* additional locks. Whatever you do, *DO NOT*
+ * grab any lock in the Netgraph context that could cause de-scheduling of
+ * the Netgraph thread for significant amount of time. In fact, the only
+ * lock that is allowed in the Netgraph context is the sc_ng_mtx lock.
+ * Also make sure that any code that is called from the Netgraph context
+ * follows the rule above.
+ *
+ * 3) Taskqueue context. This is where ubt_task runs. Since we are generally
+ * NOT allowed to grab any lock that could cause de-scheduling in the
+ * Netgraph context, and, USB requires us to grab interface lock before
+ * doing things with transfers, it is safer to transition from the Netgraph
+ * context to the Taskqueue context before we can call into USB subsystem.
+ *
+ * So, to put everything together, the rules are as follows.
+ * It is OK to call from the USB context or the Taskqueue context into
+ * the Netgraph context (i.e. call NG_SEND_xxx functions). In other words
+ * it is allowed to call into the Netgraph context with locks held.
+ * Is it *NOT* OK to call from the Netgraph context into the USB context,
+ * because USB requires us to grab interface locks, and, it is safer to
+ * avoid it. So, to make things safer we set task flags to indicate which
+ * actions we want to perform and schedule ubt_task which would run in the
+ * Taskqueue context.
+ * Is is OK to call from the Taskqueue context into the USB context,
+ * and, ubt_task does just that (i.e. grabs appropriate interface locks
+ * before calling into USB).
+ * Access to the outgoing queues, task flags and hook pointer is
+ * controlled by the sc_ng_mtx lock. It is an unavoidable evil. Again,
+ * sc_ng_mtx should really be a spin lock (and it is very likely to an
+ * equivalent of spin lock due to adaptive nature of freebsd mutexes).
+ * All USB callbacks accept softc pointer as a private data. USB ensures
+ * that this pointer is valid.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+
+#include <sys/mbuf.h>
+#include <sys/taskqueue.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/bluetooth/include/ng_bluetooth.h>
+#include <netgraph/bluetooth/include/ng_hci.h>
+#include <netgraph/bluetooth/include/ng_ubt.h>
+
+#include <dev/usb/bluetooth/ng_ubt_var.h>
+
+static int ubt_modevent(module_t, int, void *);
+static device_probe_t ubt_probe;
+static device_attach_t ubt_attach;
+static device_detach_t ubt_detach;
+
+static void ubt_task_schedule(ubt_softc_p, int);
+static task_fn_t ubt_task;
+
+#define ubt_xfer_start(sc, i) usb2_transfer_start((sc)->sc_xfer[(i)])
+
+/* Netgraph methods */
+static ng_constructor_t ng_ubt_constructor;
+static ng_shutdown_t ng_ubt_shutdown;
+static ng_newhook_t ng_ubt_newhook;
+static ng_connect_t ng_ubt_connect;
+static ng_disconnect_t ng_ubt_disconnect;
+static ng_rcvmsg_t ng_ubt_rcvmsg;
+static ng_rcvdata_t ng_ubt_rcvdata;
+
+/* Queue length */
+static const struct ng_parse_struct_field ng_ubt_node_qlen_type_fields[] =
+{
+ { "queue", &ng_parse_int32_type, },
+ { "qlen", &ng_parse_int32_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_ubt_node_qlen_type =
+{
+ &ng_parse_struct_type,
+ &ng_ubt_node_qlen_type_fields
+};
+
+/* Stat info */
+static const struct ng_parse_struct_field ng_ubt_node_stat_type_fields[] =
+{
+ { "pckts_recv", &ng_parse_uint32_type, },
+ { "bytes_recv", &ng_parse_uint32_type, },
+ { "pckts_sent", &ng_parse_uint32_type, },
+ { "bytes_sent", &ng_parse_uint32_type, },
+ { "oerrors", &ng_parse_uint32_type, },
+ { "ierrors", &ng_parse_uint32_type, },
+ { NULL, }
+};
+static const struct ng_parse_type ng_ubt_node_stat_type =
+{
+ &ng_parse_struct_type,
+ &ng_ubt_node_stat_type_fields
+};
+
+/* Netgraph node command list */
+static const struct ng_cmdlist ng_ubt_cmdlist[] =
+{
+ {
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_SET_DEBUG,
+ "set_debug",
+ &ng_parse_uint16_type,
+ NULL
+ },
+ {
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_DEBUG,
+ "get_debug",
+ NULL,
+ &ng_parse_uint16_type
+ },
+ {
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_SET_QLEN,
+ "set_qlen",
+ &ng_ubt_node_qlen_type,
+ NULL
+ },
+ {
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_QLEN,
+ "get_qlen",
+ &ng_ubt_node_qlen_type,
+ &ng_ubt_node_qlen_type
+ },
+ {
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_GET_STAT,
+ "get_stat",
+ NULL,
+ &ng_ubt_node_stat_type
+ },
+ {
+ NGM_UBT_COOKIE,
+ NGM_UBT_NODE_RESET_STAT,
+ "reset_stat",
+ NULL,
+ NULL
+ },
+ { 0, }
+};
+
+/* Netgraph node type */
+static struct ng_type typestruct =
+{
+ .version = NG_ABI_VERSION,
+ .name = NG_UBT_NODE_TYPE,
+ .constructor = ng_ubt_constructor,
+ .rcvmsg = ng_ubt_rcvmsg,
+ .shutdown = ng_ubt_shutdown,
+ .newhook = ng_ubt_newhook,
+ .connect = ng_ubt_connect,
+ .rcvdata = ng_ubt_rcvdata,
+ .disconnect = ng_ubt_disconnect,
+ .cmdlist = ng_ubt_cmdlist
+};
+
+/****************************************************************************
+ ****************************************************************************
+ ** USB specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/* USB methods */
+static usb2_callback_t ubt_ctrl_write_callback;
+static usb2_callback_t ubt_intr_read_callback;
+static usb2_callback_t ubt_bulk_read_callback;
+static usb2_callback_t ubt_bulk_write_callback;
+static usb2_callback_t ubt_isoc_read_callback;
+static usb2_callback_t ubt_isoc_write_callback;
+
+static int ubt_fwd_mbuf_up(ubt_softc_p, struct mbuf **);
+static int ubt_isoc_read_one_frame(struct usb2_xfer *, int);
+
+/*
+ * USB config
+ *
+ * The following desribes usb transfers that could be submitted on USB device.
+ *
+ * Interface 0 on the USB device must present the following endpoints
+ * 1) Interrupt endpoint to receive HCI events
+ * 2) Bulk IN endpoint to receive ACL data
+ * 3) Bulk OUT endpoint to send ACL data
+ *
+ * Interface 1 on the USB device must present the following endpoints
+ * 1) Isochronous IN endpoint to receive SCO data
+ * 2) Isochronous OUT endpoint to send SCO data
+ */
+
+static const struct usb2_config ubt_config[UBT_N_TRANSFER] =
+{
+ /*
+ * Interface #0
+ */
+
+ /* Outgoing bulk transfer - ACL packets */
+ [UBT_IF_0_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 0,
+ .mh.bufsize = UBT_BULK_WRITE_BUFFER_SIZE,
+ .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1, },
+ .mh.callback = &ubt_bulk_write_callback,
+ },
+ /* Incoming bulk transfer - ACL packets */
+ [UBT_IF_0_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 0,
+ .mh.bufsize = UBT_BULK_READ_BUFFER_SIZE,
+ .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, },
+ .mh.callback = &ubt_bulk_read_callback,
+ },
+ /* Incoming interrupt transfer - HCI events */
+ [UBT_IF_0_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 0,
+ .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1, },
+ .mh.bufsize = UBT_INTR_BUFFER_SIZE,
+ .mh.callback = &ubt_intr_read_callback,
+ },
+ /* Outgoing control transfer - HCI commands */
+ [UBT_IF_0_CTRL_DT_WR] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* control pipe */
+ .direction = UE_DIR_ANY,
+ .if_index = 0,
+ .mh.bufsize = UBT_CTRL_BUFFER_SIZE,
+ .mh.callback = &ubt_ctrl_write_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ /*
+ * Interface #1
+ */
+
+ /* Incoming isochronous transfer #1 - SCO packets */
+ [UBT_IF_1_ISOC_DT_RD1] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 1,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UBT_ISOC_NFRAMES,
+ .mh.flags = { .short_xfer_ok = 1, },
+ .mh.callback = &ubt_isoc_read_callback,
+ },
+ /* Incoming isochronous transfer #2 - SCO packets */
+ [UBT_IF_1_ISOC_DT_RD2] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 1,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UBT_ISOC_NFRAMES,
+ .mh.flags = { .short_xfer_ok = 1, },
+ .mh.callback = &ubt_isoc_read_callback,
+ },
+ /* Outgoing isochronous transfer #1 - SCO packets */
+ [UBT_IF_1_ISOC_DT_WR1] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 1,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UBT_ISOC_NFRAMES,
+ .mh.flags = { .short_xfer_ok = 1, },
+ .mh.callback = &ubt_isoc_write_callback,
+ },
+ /* Outgoing isochronous transfer #2 - SCO packets */
+ [UBT_IF_1_ISOC_DT_WR2] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 1,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UBT_ISOC_NFRAMES,
+ .mh.flags = { .short_xfer_ok = 1, },
+ .mh.callback = &ubt_isoc_write_callback,
+ },
+};
+
+/*
+ * If for some reason device should not be attached then put
+ * VendorID/ProductID pair into the list below. The format is
+ * as follows:
+ *
+ * { USB_VPI(VENDOR_ID, PRODUCT_ID, 0) },
+ *
+ * where VENDOR_ID and PRODUCT_ID are hex numbers.
+ */
+
+static const struct usb2_device_id ubt_ignore_devs[] =
+{
+ /* AVM USB Bluetooth-Adapter BlueFritz! v1.0 */
+ { USB_VPI(USB_VENDOR_AVM, 0x2200, 0) },
+};
+
+/* List of supported bluetooth devices */
+static const struct usb2_device_id ubt_devs[] =
+{
+ /* Generic Bluetooth class devices */
+ { USB_IFACE_CLASS(UDCLASS_WIRELESS),
+ USB_IFACE_SUBCLASS(UDSUBCLASS_RF),
+ USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) },
+
+ /* AVM USB Bluetooth-Adapter BlueFritz! v2.0 */
+ { USB_VPI(USB_VENDOR_AVM, 0x3800, 0) },
+};
+
+/*
+ * Probe for a USB Bluetooth device.
+ * USB context.
+ */
+
+static int
+ubt_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ if (uaa->info.bIfaceIndex != 0)
+ return (ENXIO);
+
+ if (uaa->use_generic == 0)
+ return (ENXIO);
+
+ if (usb2_lookup_id_by_uaa(ubt_ignore_devs,
+ sizeof(ubt_ignore_devs), uaa) == 0)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(ubt_devs, sizeof(ubt_devs), uaa));
+} /* ubt_probe */
+
+/*
+ * Attach the device.
+ * USB context.
+ */
+
+static int
+ubt_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ubt_softc *sc = device_get_softc(dev);
+ struct usb2_endpoint_descriptor *ed;
+ uint16_t wMaxPacketSize;
+ uint8_t alt_index, i, j;
+ uint8_t iface_index[2] = { 0, 1 };
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_dev = dev;
+ sc->sc_debug = NG_UBT_WARN_LEVEL;
+
+ /*
+ * Create Netgraph node
+ */
+
+ if (ng_make_node_common(&typestruct, &sc->sc_node) != 0) {
+ UBT_ALERT(sc, "could not create Netgraph node\n");
+ return (ENXIO);
+ }
+
+ /* Name Netgraph node */
+ if (ng_name_node(sc->sc_node, device_get_nameunit(dev)) != 0) {
+ UBT_ALERT(sc, "could not name Netgraph node\n");
+ NG_NODE_UNREF(sc->sc_node);
+ return (ENXIO);
+ }
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+ NG_NODE_FORCE_WRITER(sc->sc_node);
+
+ /*
+ * Initialize device softc structure
+ */
+
+ /* initialize locks */
+ mtx_init(&sc->sc_ng_mtx, "ubt ng", NULL, MTX_DEF);
+ mtx_init(&sc->sc_if_mtx, "ubt if", NULL, MTX_DEF | MTX_RECURSE);
+
+ /* initialize packet queues */
+ NG_BT_MBUFQ_INIT(&sc->sc_cmdq, UBT_DEFAULT_QLEN);
+ NG_BT_MBUFQ_INIT(&sc->sc_aclq, UBT_DEFAULT_QLEN);
+ NG_BT_MBUFQ_INIT(&sc->sc_scoq, UBT_DEFAULT_QLEN);
+
+ /* initialize glue task */
+ TASK_INIT(&sc->sc_task, 0, ubt_task, sc);
+
+ /*
+ * Configure Bluetooth USB device. Discover all required USB
+ * interfaces and endpoints.
+ *
+ * USB device must present two interfaces:
+ * 1) Interface 0 that has 3 endpoints
+ * 1) Interrupt endpoint to receive HCI events
+ * 2) Bulk IN endpoint to receive ACL data
+ * 3) Bulk OUT endpoint to send ACL data
+ *
+ * 2) Interface 1 then has 2 endpoints
+ * 1) Isochronous IN endpoint to receive SCO data
+ * 2) Isochronous OUT endpoint to send SCO data
+ *
+ * Interface 1 (with isochronous endpoints) has several alternate
+ * configurations with different packet size.
+ */
+
+ /*
+ * For interface #1 search alternate settings, and find
+ * the descriptor with the largest wMaxPacketSize
+ */
+
+ wMaxPacketSize = 0;
+ alt_index = 0;
+ i = 0;
+ j = 0;
+
+ /* Search through all the descriptors looking for bidir mode */
+ while (1) {
+ uint16_t temp;
+
+ ed = usb2_find_edesc(usb2_get_config_descriptor(uaa->device),
+ 1, i, j);
+ if (ed == NULL) {
+ if (j != 0) {
+ /* next interface */
+ j = 0;
+ i ++;
+ continue;
+ }
+
+ break; /* end of interfaces */
+ }
+
+ temp = UGETW(ed->wMaxPacketSize);
+ if (temp > wMaxPacketSize) {
+ wMaxPacketSize = temp;
+ alt_index = i;
+ }
+
+ j ++;
+ }
+
+ /* Set alt configuration on interface #1 only if we found it */
+ if (wMaxPacketSize > 0 &&
+ usb2_set_alt_interface_index(uaa->device, 1, alt_index)) {
+ UBT_ALERT(sc, "could not set alternate setting %d " \
+ "for interface 1!\n", alt_index);
+ goto detach;
+ }
+
+ /* Setup transfers for both interfaces */
+ if (usb2_transfer_setup(uaa->device, iface_index, sc->sc_xfer,
+ ubt_config, UBT_N_TRANSFER, sc, &sc->sc_if_mtx)) {
+ UBT_ALERT(sc, "could not allocate transfers\n");
+ goto detach;
+ }
+
+ /* Claim all interfaces on the device */
+ for (i = 1; usb2_get_iface(uaa->device, i) != NULL; i ++)
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+
+ return (0); /* success */
+
+detach:
+ ubt_detach(dev);
+
+ return (ENXIO);
+} /* ubt_attach */
+
+/*
+ * Detach the device.
+ * USB context.
+ */
+
+int
+ubt_detach(device_t dev)
+{
+ struct ubt_softc *sc = device_get_softc(dev);
+ node_p node = sc->sc_node;
+
+ /* Destroy Netgraph node */
+ if (node != NULL) {
+ sc->sc_node = NULL;
+ NG_NODE_REALLY_DIE(node);
+ ng_rmnode_self(node);
+ }
+
+ /* Make sure ubt_task in gone */
+ taskqueue_drain(taskqueue_swi, &sc->sc_task);
+
+ /* Free USB transfers, if any */
+ usb2_transfer_unsetup(sc->sc_xfer, UBT_N_TRANSFER);
+
+ /* Destroy queues */
+ UBT_NG_LOCK(sc);
+ NG_BT_MBUFQ_DESTROY(&sc->sc_cmdq);
+ NG_BT_MBUFQ_DESTROY(&sc->sc_aclq);
+ NG_BT_MBUFQ_DESTROY(&sc->sc_scoq);
+ UBT_NG_UNLOCK(sc);
+
+ mtx_destroy(&sc->sc_if_mtx);
+ mtx_destroy(&sc->sc_ng_mtx);
+
+ return (0);
+} /* ubt_detach */
+
+/*
+ * Called when outgoing control request (HCI command) has completed, i.e.
+ * HCI command was sent to the device.
+ * USB context.
+ */
+
+static void
+ubt_ctrl_write_callback(struct usb2_xfer *xfer)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ struct mbuf *m;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ UBT_INFO(sc, "sent %d bytes to control pipe\n", xfer->actlen);
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+send_next:
+ /* Get next command mbuf, if any */
+ UBT_NG_LOCK(sc);
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_cmdq, m);
+ UBT_NG_UNLOCK(sc);
+
+ if (m == NULL) {
+ UBT_INFO(sc, "HCI command queue is empty\n");
+ break; /* transfer complete */
+ }
+
+ /* Initialize a USB control request and then schedule it */
+ bzero(&req, sizeof(req));
+ req.bmRequestType = UBT_HCI_REQUEST;
+ USETW(req.wLength, m->m_pkthdr.len);
+
+ UBT_INFO(sc, "Sending control request, " \
+ "bmRequestType=0x%02x, wLength=%d\n",
+ req.bmRequestType, UGETW(req.wLength));
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_m_copy_in(xfer->frbuffers + 1, 0, m, 0, m->m_pkthdr.len);
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = m->m_pkthdr.len;
+ xfer->nframes = 2;
+
+ NG_FREE_M(m);
+
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "control transfer failed: %s\n",
+ usb2_errstr(xfer->error));
+
+ UBT_STAT_OERROR(sc);
+ goto send_next;
+ }
+
+ /* transfer cancelled */
+ break;
+ }
+} /* ubt_ctrl_write_callback */
+
+/*
+ * Called when incoming interrupt transfer (HCI event) has completed, i.e.
+ * HCI event was received from the device.
+ * USB context.
+ */
+
+static void
+ubt_intr_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ ng_hci_event_pkt_t *hdr;
+
+ m = NULL;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ /* Allocate a new mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ /* Add HCI packet type */
+ *mtod(m, uint8_t *)= NG_HCI_EVENT_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+
+ if (xfer->actlen > MCLBYTES - 1)
+ xfer->actlen = MCLBYTES - 1;
+
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
+ xfer->actlen);
+ m->m_pkthdr.len += xfer->actlen;
+ m->m_len += xfer->actlen;
+
+ UBT_INFO(sc, "got %d bytes from interrupt pipe\n",
+ xfer->actlen);
+
+ /* Validate packet and send it up the stack */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ UBT_INFO(sc, "HCI event packet is too short\n");
+
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ hdr = mtod(m, ng_hci_event_pkt_t *);
+ if (hdr->length != (m->m_pkthdr.len - sizeof(*hdr))) {
+ UBT_ERR(sc, "Invalid HCI event packet size, " \
+ "length=%d, pktlen=%d\n",
+ hdr->length, m->m_pkthdr.len);
+
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ UBT_INFO(sc, "got complete HCI event frame, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len, hdr->length);
+
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
+
+ ubt_fwd_mbuf_up(sc, &m);
+ /* m == NULL at this point */
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+submit_next:
+ NG_FREE_M(m); /* checks for m != NULL */
+
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "interrupt transfer failed: %s\n",
+ usb2_errstr(xfer->error));
+
+ /* Try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto submit_next;
+ }
+ /* transfer cancelled */
+ break;
+ }
+} /* ubt_intr_read_callback */
+
+/*
+ * Called when incoming bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was received from the device.
+ * USB context.
+ */
+
+static void
+ubt_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ ng_hci_acldata_pkt_t *hdr;
+ uint16_t len;
+
+ m = NULL;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ /* Allocate new mbuf */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ /* Add HCI packet type */
+ *mtod(m, uint8_t *)= NG_HCI_ACL_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = 1;
+
+ if (xfer->actlen > MCLBYTES - 1)
+ xfer->actlen = MCLBYTES - 1;
+
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *) + 1,
+ xfer->actlen);
+ m->m_pkthdr.len += xfer->actlen;
+ m->m_len += xfer->actlen;
+
+ UBT_INFO(sc, "got %d bytes from bulk-in pipe\n",
+ xfer->actlen);
+
+ /* Validate packet and send it up the stack */
+ if (m->m_pkthdr.len < sizeof(*hdr)) {
+ UBT_INFO(sc, "HCI ACL packet is too short\n");
+
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ hdr = mtod(m, ng_hci_acldata_pkt_t *);
+ len = le16toh(hdr->length);
+ if (len != (m->m_pkthdr.len - sizeof(*hdr))) {
+ UBT_ERR(sc, "Invalid ACL packet size, length=%d, " \
+ "pktlen=%d\n", len, m->m_pkthdr.len);
+
+ UBT_STAT_IERROR(sc);
+ goto submit_next;
+ }
+
+ UBT_INFO(sc, "got complete ACL data packet, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len, len);
+
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
+
+ ubt_fwd_mbuf_up(sc, &m);
+ /* m == NULL at this point */
+ /* FALLTHOUGH */
+
+ case USB_ST_SETUP:
+submit_next:
+ NG_FREE_M(m); /* checks for m != NULL */
+
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "bulk-in transfer failed: %s\n",
+ usb2_errstr(xfer->error));
+
+ /* Try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto submit_next;
+ }
+ /* transfer cancelled */
+ break;
+ }
+} /* ubt_bulk_read_callback */
+
+/*
+ * Called when outgoing bulk transfer (ACL packet) has completed, i.e.
+ * ACL packet was sent to the device.
+ * USB context.
+ */
+
+static void
+ubt_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ UBT_INFO(sc, "sent %d bytes to bulk-out pipe\n", xfer->actlen);
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+send_next:
+ /* Get next mbuf, if any */
+ UBT_NG_LOCK(sc);
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_aclq, m);
+ UBT_NG_UNLOCK(sc);
+
+ if (m == NULL) {
+ UBT_INFO(sc, "ACL data queue is empty\n");
+ break; /* transfer completed */
+ }
+
+ /*
+ * Copy ACL data frame back to a linear USB transfer buffer
+ * and schedule transfer
+ */
+
+ usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len);
+ xfer->frlengths[0] = m->m_pkthdr.len;
+
+ UBT_INFO(sc, "bulk-out transfer has been started, len=%d\n",
+ m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_WARN(sc, "bulk-out transfer failed: %s\n",
+ usb2_errstr(xfer->error));
+
+ UBT_STAT_OERROR(sc);
+
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto send_next;
+ }
+ /* transfer cancelled */
+ break;
+ }
+} /* ubt_bulk_write_callback */
+
+/*
+ * Called when incoming isoc transfer (SCO packet) has completed, i.e.
+ * SCO packet was received from the device.
+ * USB context.
+ */
+
+static void
+ubt_isoc_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ int n;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ for (n = 0; n < xfer->nframes; n ++)
+ if (ubt_isoc_read_one_frame(xfer, n) < 0)
+ break;
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+read_next:
+ for (n = 0; n < xfer->nframes; n ++)
+ xfer->frlengths[n] = xfer->max_frame_size;
+
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_STAT_IERROR(sc);
+ goto read_next;
+ }
+
+ /* transfer cancelled */
+ break;
+ }
+} /* ubt_isoc_read_callback */
+
+/*
+ * Helper function. Called from ubt_isoc_read_callback() to read
+ * SCO data from one frame.
+ * USB context.
+ */
+
+static int
+ubt_isoc_read_one_frame(struct usb2_xfer *xfer, int frame_no)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ int len, want, got;
+
+ /* Get existing SCO reassembly buffer */
+ m = sc->sc_isoc_in_buffer;
+ sc->sc_isoc_in_buffer = NULL;
+
+ /* While we have data in the frame */
+ while ((len = xfer->frlengths[frame_no]) > 0) {
+ if (m == NULL) {
+ /* Start new reassembly buffer */
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ UBT_STAT_IERROR(sc);
+ return (-1); /* XXX out of sync! */
+ }
+
+ MCLGET(m, M_DONTWAIT);
+ if (!(m->m_flags & M_EXT)) {
+ UBT_STAT_IERROR(sc);
+ NG_FREE_M(m);
+ return (-1); /* XXX out of sync! */
+ }
+
+ /* Expect SCO header */
+ *mtod(m, uint8_t *) = NG_HCI_SCO_DATA_PKT;
+ m->m_pkthdr.len = m->m_len = got = 1;
+ want = sizeof(ng_hci_scodata_pkt_t);
+ } else {
+ /*
+ * Check if we have SCO header and if so
+ * adjust amount of data we want
+ */
+ got = m->m_pkthdr.len;
+ want = sizeof(ng_hci_scodata_pkt_t);
+
+ if (got >= want)
+ want += mtod(m, ng_hci_scodata_pkt_t *)->length;
+ }
+
+ /* Append frame data to the SCO reassembly buffer */
+ if (got + len > want)
+ len = want - got;
+
+ usb2_copy_out(xfer->frbuffers, frame_no * xfer->max_frame_size,
+ mtod(m, uint8_t *) + m->m_pkthdr.len, len);
+
+ m->m_pkthdr.len += len;
+ m->m_len += len;
+ xfer->frlengths[frame_no] -= len;
+
+ /* Check if we got everything we wanted, if not - continue */
+ if (got != want)
+ continue;
+
+ /* If we got here then we got complete SCO frame */
+ UBT_INFO(sc, "got complete SCO data frame, pktlen=%d, " \
+ "length=%d\n", m->m_pkthdr.len,
+ mtod(m, ng_hci_scodata_pkt_t *)->length);
+
+ UBT_STAT_PCKTS_RECV(sc);
+ UBT_STAT_BYTES_RECV(sc, m->m_pkthdr.len);
+
+ ubt_fwd_mbuf_up(sc, &m);
+ /* m == NULL at this point */
+ }
+
+ /* Put SCO reassembly buffer back */
+ sc->sc_isoc_in_buffer = m;
+
+ return (0);
+} /* ubt_isoc_read_one_frame */
+
+/*
+ * Called when outgoing isoc transfer (SCO packet) has completed, i.e.
+ * SCO packet was sent to the device.
+ * USB context.
+ */
+
+static void
+ubt_isoc_write_callback(struct usb2_xfer *xfer)
+{
+ struct ubt_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ int n, space, offset;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ UBT_INFO(sc, "sent %d bytes to isoc-out pipe\n", xfer->actlen);
+ UBT_STAT_BYTES_SENT(sc, xfer->actlen);
+ UBT_STAT_PCKTS_SENT(sc);
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+send_next:
+ offset = 0;
+ space = xfer->max_frame_size * xfer->nframes;
+ m = NULL;
+
+ while (space > 0) {
+ if (m == NULL) {
+ UBT_NG_LOCK(sc);
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_scoq, m);
+ UBT_NG_UNLOCK(sc);
+
+ if (m == NULL)
+ break;
+ }
+
+ n = min(space, m->m_pkthdr.len);
+ if (n > 0) {
+ usb2_m_copy_in(xfer->frbuffers, offset, m,0, n);
+ m_adj(m, n);
+
+ offset += n;
+ space -= n;
+ }
+
+ if (m->m_pkthdr.len == 0)
+ NG_FREE_M(m); /* sets m = NULL */
+ }
+
+ /* Put whatever is left from mbuf back on queue */
+ if (m != NULL) {
+ UBT_NG_LOCK(sc);
+ NG_BT_MBUFQ_PREPEND(&sc->sc_scoq, m);
+ UBT_NG_UNLOCK(sc);
+ }
+
+ /*
+ * Calculate sizes for isoc frames.
+ * Note that offset could be 0 at this point (i.e. we have
+ * nothing to send). That is fine, as we have isoc. transfers
+ * going in both directions all the time. In this case it
+ * would be just empty isoc. transfer.
+ */
+
+ for (n = 0; n < xfer->nframes; n ++) {
+ xfer->frlengths[n] = min(offset, xfer->max_frame_size);
+ offset -= xfer->frlengths[n];
+ }
+
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ UBT_STAT_OERROR(sc);
+ goto send_next;
+ }
+
+ /* transfer cancelled */
+ break;
+ }
+}
+
+/*
+ * Utility function to forward provided mbuf upstream (i.e. up the stack).
+ * Modifies value of the mbuf pointer (sets it to NULL).
+ * Save to call from any context.
+ */
+
+static int
+ubt_fwd_mbuf_up(ubt_softc_p sc, struct mbuf **m)
+{
+ hook_p hook;
+ int error;
+
+ /*
+ * Close the race with Netgraph hook newhook/disconnect methods.
+ * Save the hook pointer atomically. Two cases are possible:
+ *
+ * 1) The hook pointer is NULL. It means disconnect method got
+ * there first. In this case we are done.
+ *
+ * 2) The hook pointer is not NULL. It means that hook pointer
+ * could be either in valid or invalid (i.e. in the process
+ * of disconnect) state. In any case grab an extra reference
+ * to protect the hook pointer.
+ *
+ * It is ok to pass hook in invalid state to NG_SEND_DATA_ONLY() as
+ * it checks for it. Drop extra reference after NG_SEND_DATA_ONLY().
+ */
+
+ UBT_NG_LOCK(sc);
+ if ((hook = sc->sc_hook) != NULL)
+ NG_HOOK_REF(hook);
+ UBT_NG_UNLOCK(sc);
+
+ if (hook == NULL) {
+ NG_FREE_M(*m);
+ return (ENETDOWN);
+ }
+
+ NG_SEND_DATA_ONLY(error, hook, *m);
+ NG_HOOK_UNREF(hook);
+
+ if (error != 0)
+ UBT_STAT_IERROR(sc);
+
+ return (error);
+} /* ubt_fwd_mbuf_up */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Glue
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Schedule glue task. Should be called with sc_ng_mtx held.
+ * Netgraph context.
+ */
+
+static void
+ubt_task_schedule(ubt_softc_p sc, int action)
+{
+ mtx_assert(&sc->sc_ng_mtx, MA_OWNED);
+
+ /*
+ * Try to handle corner case when "start all" and "stop all"
+ * actions can both be set before task is executed.
+ *
+ * The rules are
+ *
+ * sc_task_flags action new sc_task_flags
+ * ------------------------------------------------------
+ * 0 start start
+ * 0 stop stop
+ * start start start
+ * start stop stop
+ * stop start stop|start
+ * stop stop stop
+ * stop|start start stop|start
+ * stop|start stop stop
+ */
+
+ if (action != 0) {
+ if ((action & UBT_FLAG_T_STOP_ALL) != 0)
+ sc->sc_task_flags &= ~UBT_FLAG_T_START_ALL;
+
+ sc->sc_task_flags |= action;
+ }
+
+ if (sc->sc_task_flags & UBT_FLAG_T_PENDING)
+ return;
+
+ if (taskqueue_enqueue(taskqueue_swi, &sc->sc_task) == 0) {
+ sc->sc_task_flags |= UBT_FLAG_T_PENDING;
+ return;
+ }
+
+ /* XXX: i think this should never happen */
+} /* ubt_task_schedule */
+
+/*
+ * Glue task. Examines sc_task_flags and does things depending on it.
+ * Taskqueue context.
+ */
+
+static void
+ubt_task(void *context, int pending)
+{
+ ubt_softc_p sc = context;
+ int task_flags, i;
+
+ UBT_NG_LOCK(sc);
+ task_flags = sc->sc_task_flags;
+ sc->sc_task_flags = 0;
+ UBT_NG_UNLOCK(sc);
+
+ /*
+ * Stop all USB transfers synchronously.
+ * Stop interface #0 and #1 transfers at the same time and in the
+ * same loop. usb2_transfer_drain() will do appropriate locking.
+ */
+
+ if (task_flags & UBT_FLAG_T_STOP_ALL)
+ for (i = 0; i < UBT_N_TRANSFER; i ++)
+ usb2_transfer_drain(sc->sc_xfer[i]);
+
+ /* Start incoming interrupt and bulk, and all isoc. USB transfers */
+ if (task_flags & UBT_FLAG_T_START_ALL) {
+ /*
+ * Interface #0
+ */
+
+ mtx_lock(&sc->sc_if_mtx);
+
+ ubt_xfer_start(sc, UBT_IF_0_INTR_DT_RD);
+ ubt_xfer_start(sc, UBT_IF_0_BULK_DT_RD);
+
+ /*
+ * Interface #1
+ * Start both read and write isoc. transfers by default.
+ * Get them going all the time even if we have nothing
+ * to send to avoid any delays.
+ */
+
+ ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD1);
+ ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_RD2);
+ ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR1);
+ ubt_xfer_start(sc, UBT_IF_1_ISOC_DT_WR2);
+
+ mtx_unlock(&sc->sc_if_mtx);
+ }
+
+ /* Start outgoing control transfer */
+ if (task_flags & UBT_FLAG_T_START_CTRL) {
+ mtx_lock(&sc->sc_if_mtx);
+ ubt_xfer_start(sc, UBT_IF_0_CTRL_DT_WR);
+ mtx_unlock(&sc->sc_if_mtx);
+ }
+
+ /* Start outgoing bulk transfer */
+ if (task_flags & UBT_FLAG_T_START_BULK) {
+ mtx_lock(&sc->sc_if_mtx);
+ ubt_xfer_start(sc, UBT_IF_0_BULK_DT_WR);
+ mtx_unlock(&sc->sc_if_mtx);
+ }
+} /* ubt_task */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Netgraph specific
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Netgraph node constructor. Do not allow to create node of this type.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_constructor(node_p node)
+{
+ return (EINVAL);
+} /* ng_ubt_constructor */
+
+/*
+ * Netgraph node destructor. Destroy node only when device has been detached.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_shutdown(node_p node)
+{
+ if (node->nd_flags & NGF_REALLY_DIE) {
+ /*
+ * We came here because the USB device is being
+ * detached, so stop being persistant.
+ */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node);
+ } else
+ NG_NODE_REVIVE(node); /* tell ng_rmnode we are persisant */
+
+ return (0);
+} /* ng_ubt_shutdown */
+
+/*
+ * Create new hook. There can only be one.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_newhook(node_p node, hook_p hook, char const *name)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
+
+ if (strcmp(name, NG_UBT_HOOK) != 0)
+ return (EINVAL);
+
+ UBT_NG_LOCK(sc);
+ if (sc->sc_hook != NULL) {
+ UBT_NG_UNLOCK(sc);
+
+ return (EISCONN);
+ }
+
+ sc->sc_hook = hook;
+ UBT_NG_UNLOCK(sc);
+
+ return (0);
+} /* ng_ubt_newhook */
+
+/*
+ * Connect hook. Start incoming USB transfers.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_connect(hook_p hook)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ UBT_NG_LOCK(sc);
+ ubt_task_schedule(sc, UBT_FLAG_T_START_ALL);
+ UBT_NG_UNLOCK(sc);
+
+ return (0);
+} /* ng_ubt_connect */
+
+/*
+ * Disconnect hook.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_disconnect(hook_p hook)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ UBT_NG_LOCK(sc);
+
+ if (hook != sc->sc_hook) {
+ UBT_NG_UNLOCK(sc);
+
+ return (EINVAL);
+ }
+
+ sc->sc_hook = NULL;
+
+ /* Kick off task to stop all USB xfers */
+ ubt_task_schedule(sc, UBT_FLAG_T_STOP_ALL);
+
+ /* Drain queues */
+ NG_BT_MBUFQ_DRAIN(&sc->sc_cmdq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_aclq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_scoq);
+
+ UBT_NG_UNLOCK(sc);
+
+ return (0);
+} /* ng_ubt_disconnect */
+
+/*
+ * Process control message.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg, *rsp = NULL;
+ struct ng_bt_mbufq *q;
+ int error = 0, queue, qlen;
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.typecookie) {
+ case NGM_GENERIC_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_TEXT_STATUS:
+ NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ snprintf(rsp->data, NG_TEXTRESPONSE,
+ "Hook: %s\n" \
+ "Task flags: %#x\n" \
+ "Debug: %d\n" \
+ "CMD queue: [have:%d,max:%d]\n" \
+ "ACL queue: [have:%d,max:%d]\n" \
+ "SCO queue: [have:%d,max:%d]",
+ (sc->sc_hook != NULL) ? NG_UBT_HOOK : "",
+ sc->sc_task_flags,
+ sc->sc_debug,
+ sc->sc_cmdq.len,
+ sc->sc_cmdq.maxlen,
+ sc->sc_aclq.len,
+ sc->sc_aclq.maxlen,
+ sc->sc_scoq.len,
+ sc->sc_scoq.maxlen);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ case NGM_UBT_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_UBT_NODE_SET_DEBUG:
+ if (msg->header.arglen != sizeof(ng_ubt_node_debug_ep)){
+ error = EMSGSIZE;
+ break;
+ }
+
+ sc->sc_debug = *((ng_ubt_node_debug_ep *) (msg->data));
+ break;
+
+ case NGM_UBT_NODE_GET_DEBUG:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_debug_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ *((ng_ubt_node_debug_ep *) (rsp->data)) = sc->sc_debug;
+ break;
+
+ case NGM_UBT_NODE_SET_QLEN:
+ if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
+ error = EMSGSIZE;
+ break;
+ }
+
+ queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
+ qlen = ((ng_ubt_node_qlen_ep *) (msg->data))->qlen;
+
+ switch (queue) {
+ case NGM_UBT_NODE_QUEUE_CMD:
+ q = &sc->sc_cmdq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_ACL:
+ q = &sc->sc_aclq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_SCO:
+ q = &sc->sc_scoq;
+ break;
+
+ default:
+ error = EINVAL;
+ goto done;
+ /* NOT REACHED */
+ }
+
+ q->maxlen = qlen;
+ break;
+
+ case NGM_UBT_NODE_GET_QLEN:
+ if (msg->header.arglen != sizeof(ng_ubt_node_qlen_ep)) {
+ error = EMSGSIZE;
+ break;
+ }
+
+ queue = ((ng_ubt_node_qlen_ep *) (msg->data))->queue;
+
+ switch (queue) {
+ case NGM_UBT_NODE_QUEUE_CMD:
+ q = &sc->sc_cmdq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_ACL:
+ q = &sc->sc_aclq;
+ break;
+
+ case NGM_UBT_NODE_QUEUE_SCO:
+ q = &sc->sc_scoq;
+ break;
+
+ default:
+ error = EINVAL;
+ goto done;
+ /* NOT REACHED */
+ }
+
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_qlen_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ ((ng_ubt_node_qlen_ep *) (rsp->data))->queue = queue;
+ ((ng_ubt_node_qlen_ep *) (rsp->data))->qlen = q->maxlen;
+ break;
+
+ case NGM_UBT_NODE_GET_STAT:
+ NG_MKRESPONSE(rsp, msg, sizeof(ng_ubt_node_stat_ep),
+ M_NOWAIT);
+ if (rsp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ bcopy(&sc->sc_stat, rsp->data,
+ sizeof(ng_ubt_node_stat_ep));
+ break;
+
+ case NGM_UBT_NODE_RESET_STAT:
+ UBT_STAT_RESET(sc);
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+done:
+ NG_RESPOND_MSG(error, node, item, rsp);
+ NG_FREE_MSG(msg);
+
+ return (error);
+} /* ng_ubt_rcvmsg */
+
+/*
+ * Process data.
+ * Netgraph context.
+ */
+
+static int
+ng_ubt_rcvdata(hook_p hook, item_p item)
+{
+ struct ubt_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct mbuf *m;
+ struct ng_bt_mbufq *q;
+ int action, error = 0;
+
+ if (hook != sc->sc_hook) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Deatch mbuf and get HCI frame type */
+ NGI_GET_M(item, m);
+
+ /*
+ * Minimal size of the HCI frame is 4 bytes: 1 byte frame type,
+ * 2 bytes connection handle and at least 1 byte of length.
+ * Panic on data frame that has size smaller than 4 bytes (it
+ * should not happen)
+ */
+
+ if (m->m_pkthdr.len < 4)
+ panic("HCI frame size is too small! pktlen=%d\n",
+ m->m_pkthdr.len);
+
+ /* Process HCI frame */
+ switch (*mtod(m, uint8_t *)) { /* XXX call m_pullup ? */
+ case NG_HCI_CMD_PKT:
+ if (m->m_pkthdr.len - 1 > UBT_CTRL_BUFFER_SIZE)
+ panic("HCI command frame size is too big! " \
+ "buffer size=%zd, packet len=%d\n",
+ UBT_CTRL_BUFFER_SIZE, m->m_pkthdr.len);
+
+ q = &sc->sc_cmdq;
+ action = UBT_FLAG_T_START_CTRL;
+ break;
+
+ case NG_HCI_ACL_DATA_PKT:
+ if (m->m_pkthdr.len - 1 > UBT_BULK_WRITE_BUFFER_SIZE)
+ panic("ACL data frame size is too big! " \
+ "buffer size=%d, packet len=%d\n",
+ UBT_BULK_WRITE_BUFFER_SIZE, m->m_pkthdr.len);
+
+ q = &sc->sc_aclq;
+ action = UBT_FLAG_T_START_BULK;
+ break;
+
+ case NG_HCI_SCO_DATA_PKT:
+ q = &sc->sc_scoq;
+ action = 0;
+ break;
+
+ default:
+ UBT_ERR(sc, "Dropping unsupported HCI frame, type=0x%02x, " \
+ "pktlen=%d\n", *mtod(m, uint8_t *), m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+ error = EINVAL;
+ goto done;
+ /* NOT REACHED */
+ }
+
+ UBT_NG_LOCK(sc);
+ if (NG_BT_MBUFQ_FULL(q)) {
+ NG_BT_MBUFQ_DROP(q);
+ UBT_NG_UNLOCK(sc);
+
+ UBT_ERR(sc, "Dropping HCI frame 0x%02x, len=%d. Queue full\n",
+ *mtod(m, uint8_t *), m->m_pkthdr.len);
+
+ NG_FREE_M(m);
+ } else {
+ /* Loose HCI packet type, enqueue mbuf and kick off task */
+ m_adj(m, sizeof(uint8_t));
+ NG_BT_MBUFQ_ENQUEUE(q, m);
+ ubt_task_schedule(sc, action);
+ UBT_NG_UNLOCK(sc);
+ }
+done:
+ NG_FREE_ITEM(item);
+
+ return (error);
+} /* ng_ubt_rcvdata */
+
+/****************************************************************************
+ ****************************************************************************
+ ** Module
+ ****************************************************************************
+ ****************************************************************************/
+
+/*
+ * Load/Unload the driver module
+ */
+
+static int
+ubt_modevent(module_t mod, int event, void *data)
+{
+ int error;
+
+ switch (event) {
+ case MOD_LOAD:
+ error = ng_newtype(&typestruct);
+ if (error != 0)
+ printf("%s: Could not register Netgraph node type, " \
+ "error=%d\n", NG_UBT_NODE_TYPE, error);
+ break;
+
+ case MOD_UNLOAD:
+ error = ng_rmtype(&typestruct);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+} /* ubt_modevent */
+
+static devclass_t ubt_devclass;
+
+static device_method_t ubt_methods[] =
+{
+ DEVMETHOD(device_probe, ubt_probe),
+ DEVMETHOD(device_attach, ubt_attach),
+ DEVMETHOD(device_detach, ubt_detach),
+ { 0, 0 }
+};
+
+static driver_t ubt_driver =
+{
+ .name = "ubt",
+ .methods = ubt_methods,
+ .size = sizeof(struct ubt_softc),
+};
+
+DRIVER_MODULE(ng_ubt, ushub, ubt_driver, ubt_devclass, ubt_modevent, 0);
+MODULE_VERSION(ng_ubt, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_ubt, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
+MODULE_DEPEND(ng_ubt, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
+MODULE_DEPEND(ng_ubt, usb, 1, 1, 1);
+
diff --git a/sys/dev/usb/bluetooth/ng_ubt_var.h b/sys/dev/usb/bluetooth/ng_ubt_var.h
new file mode 100644
index 0000000..721e2f1
--- /dev/null
+++ b/sys/dev/usb/bluetooth/ng_ubt_var.h
@@ -0,0 +1,131 @@
+/*
+ * ng_ubt_var.h
+ */
+
+/*-
+ * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * $Id: ng_ubt_var.h,v 1.2 2003/03/22 23:44:36 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _NG_UBT_VAR_H_
+#define _NG_UBT_VAR_H_ 1
+
+/* Debug printf's */
+#define UBT_DEBUG(level, sc, fmt, ...) \
+do { \
+ if ((sc)->sc_debug >= (level)) \
+ device_printf((sc)->sc_dev, "%s:%d: " fmt, \
+ __FUNCTION__, __LINE__,## __VA_ARGS__); \
+} while (0)
+
+#define UBT_ALERT(...) UBT_DEBUG(NG_UBT_ALERT_LEVEL, __VA_ARGS__)
+#define UBT_ERR(...) UBT_DEBUG(NG_UBT_ERR_LEVEL, __VA_ARGS__)
+#define UBT_WARN(...) UBT_DEBUG(NG_UBT_WARN_LEVEL, __VA_ARGS__)
+#define UBT_INFO(...) UBT_DEBUG(NG_UBT_INFO_LEVEL, __VA_ARGS__)
+
+#define UBT_NG_LOCK(sc) mtx_lock(&(sc)->sc_ng_mtx)
+#define UBT_NG_UNLOCK(sc) mtx_unlock(&(sc)->sc_ng_mtx)
+
+/* Bluetooth USB control request type */
+#define UBT_HCI_REQUEST 0x20
+#define UBT_DEFAULT_QLEN 64
+#define UBT_ISOC_NFRAMES 32 /* should be factor of 8 */
+
+/* Bluetooth USB defines */
+enum {
+ /* Interface #0 transfers */
+ UBT_IF_0_BULK_DT_WR = 0,
+ UBT_IF_0_BULK_DT_RD,
+ UBT_IF_0_INTR_DT_RD,
+ UBT_IF_0_CTRL_DT_WR,
+
+ /* Interface #1 transfers */
+ UBT_IF_1_ISOC_DT_RD1,
+ UBT_IF_1_ISOC_DT_RD2,
+ UBT_IF_1_ISOC_DT_WR1,
+ UBT_IF_1_ISOC_DT_WR2,
+
+ UBT_N_TRANSFER, /* total number of transfers */
+};
+
+/* USB device softc structure */
+struct ubt_softc {
+ device_t sc_dev; /* for debug printf */
+
+ /* State */
+ ng_ubt_node_debug_ep sc_debug; /* debug level */
+
+ ng_ubt_node_stat_ep sc_stat; /* statistic */
+#define UBT_STAT_PCKTS_SENT(sc) (sc)->sc_stat.pckts_sent ++
+#define UBT_STAT_BYTES_SENT(sc, n) (sc)->sc_stat.bytes_sent += (n)
+#define UBT_STAT_PCKTS_RECV(sc) (sc)->sc_stat.pckts_recv ++
+#define UBT_STAT_BYTES_RECV(sc, n) (sc)->sc_stat.bytes_recv += (n)
+#define UBT_STAT_OERROR(sc) (sc)->sc_stat.oerrors ++
+#define UBT_STAT_IERROR(sc) (sc)->sc_stat.ierrors ++
+#define UBT_STAT_RESET(sc) bzero(&(sc)->sc_stat, sizeof((sc)->sc_stat))
+
+ /* USB device specific */
+ struct mtx sc_if_mtx; /* interfaces lock */
+ struct usb2_xfer *sc_xfer[UBT_N_TRANSFER];
+
+ struct mtx sc_ng_mtx; /* lock for shared NG data */
+
+ /* HCI commands */
+ struct ng_bt_mbufq sc_cmdq; /* HCI command queue */
+#define UBT_CTRL_BUFFER_SIZE (sizeof(struct usb2_device_request) + \
+ sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE)
+#define UBT_INTR_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */
+
+ /* ACL data */
+ struct ng_bt_mbufq sc_aclq; /* ACL data queue */
+#define UBT_BULK_READ_BUFFER_SIZE (MCLBYTES-1) /* reserve 1 byte for ID-tag */
+#define UBT_BULK_WRITE_BUFFER_SIZE (MCLBYTES)
+
+ /* SCO data */
+ struct ng_bt_mbufq sc_scoq; /* SCO data queue */
+ struct mbuf *sc_isoc_in_buffer; /* SCO reassembly buffer */
+
+ /* Netgraph specific */
+ node_p sc_node; /* pointer back to node */
+ hook_p sc_hook; /* upstream hook */
+
+ /* Glue */
+ int sc_task_flags; /* task flags */
+#define UBT_FLAG_T_PENDING (1 << 0) /* task pending */
+#define UBT_FLAG_T_STOP_ALL (1 << 1) /* stop all xfers */
+#define UBT_FLAG_T_START_ALL (1 << 2) /* start all read and isoc
+ write xfers */
+#define UBT_FLAG_T_START_CTRL (1 << 3) /* start control xfer (write) */
+#define UBT_FLAG_T_START_BULK (1 << 4) /* start bulk xfer (write) */
+
+ struct task sc_task;
+};
+typedef struct ubt_softc ubt_softc_t;
+typedef struct ubt_softc * ubt_softc_p;
+
+#endif /* ndef _NG_UBT_VAR_H_ */
+
diff --git a/sys/dev/usb/bluetooth/ubtbcmfw.c b/sys/dev/usb/bluetooth/ubtbcmfw.c
new file mode 100644
index 0000000..3685f56
--- /dev/null
+++ b/sys/dev/usb/bluetooth/ubtbcmfw.c
@@ -0,0 +1,430 @@
+/*
+ * ubtbcmfw.c
+ */
+
+/*-
+ * Copyright (c) 2003-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * 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.
+ *
+ * $Id: ubtbcmfw.c,v 1.3 2003/10/10 19:15:08 max Exp $
+ * $FreeBSD$
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+
+/*
+ * Download firmware to BCM2033.
+ */
+
+#define UBTBCMFW_CONFIG_NO 1 /* Config number */
+#define UBTBCMFW_IFACE_IDX 0 /* Control interface */
+
+#define UBTBCMFW_BSIZE 1024
+#define UBTBCMFW_IFQ_MAXLEN 2
+
+enum {
+ UBTBCMFW_BULK_DT_WR = 0,
+ UBTBCMFW_INTR_DT_RD,
+ UBTBCMFW_N_TRANSFER,
+};
+
+struct ubtbcmfw_softc {
+ struct usb2_device *sc_udev;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[UBTBCMFW_N_TRANSFER];
+ struct usb2_fifo_sc sc_fifo;
+};
+
+/*
+ * Prototypes
+ */
+
+static device_probe_t ubtbcmfw_probe;
+static device_attach_t ubtbcmfw_attach;
+static device_detach_t ubtbcmfw_detach;
+
+static usb2_callback_t ubtbcmfw_write_callback;
+static usb2_callback_t ubtbcmfw_read_callback;
+
+static usb2_fifo_close_t ubtbcmfw_close;
+static usb2_fifo_cmd_t ubtbcmfw_start_read;
+static usb2_fifo_cmd_t ubtbcmfw_start_write;
+static usb2_fifo_cmd_t ubtbcmfw_stop_read;
+static usb2_fifo_cmd_t ubtbcmfw_stop_write;
+static usb2_fifo_ioctl_t ubtbcmfw_ioctl;
+static usb2_fifo_open_t ubtbcmfw_open;
+
+static struct usb2_fifo_methods ubtbcmfw_fifo_methods =
+{
+ .f_close = &ubtbcmfw_close,
+ .f_ioctl = &ubtbcmfw_ioctl,
+ .f_open = &ubtbcmfw_open,
+ .f_start_read = &ubtbcmfw_start_read,
+ .f_start_write = &ubtbcmfw_start_write,
+ .f_stop_read = &ubtbcmfw_stop_read,
+ .f_stop_write = &ubtbcmfw_stop_write,
+ .basename[0] = "ubtbcmfw",
+ .basename[1] = "ubtbcmfw",
+ .basename[2] = "ubtbcmfw",
+ .postfix[0] = "",
+ .postfix[1] = ".1",
+ .postfix[2] = ".2",
+};
+
+/*
+ * Device's config structure
+ */
+
+static const struct usb2_config ubtbcmfw_config[UBTBCMFW_N_TRANSFER] =
+{
+ [UBTBCMFW_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = 0x02, /* fixed */
+ .direction = UE_DIR_OUT,
+ .if_index = UBTBCMFW_IFACE_IDX,
+ .mh.bufsize = UBTBCMFW_BSIZE,
+ .mh.flags = { .pipe_bof = 1, .force_short_xfer = 1,
+ .proxy_buffer = 1, },
+ .mh.callback = &ubtbcmfw_write_callback,
+ },
+
+ [UBTBCMFW_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = 0x01, /* fixed */
+ .direction = UE_DIR_IN,
+ .if_index = UBTBCMFW_IFACE_IDX,
+ .mh.bufsize = UBTBCMFW_BSIZE,
+ .mh.flags = { .pipe_bof = 1, .short_xfer_ok = 1,
+ .proxy_buffer = 1, },
+ .mh.callback = &ubtbcmfw_read_callback,
+ },
+};
+
+/*
+ * Module
+ */
+
+static devclass_t ubtbcmfw_devclass;
+
+static device_method_t ubtbcmfw_methods[] =
+{
+ DEVMETHOD(device_probe, ubtbcmfw_probe),
+ DEVMETHOD(device_attach, ubtbcmfw_attach),
+ DEVMETHOD(device_detach, ubtbcmfw_detach),
+ {0, 0}
+};
+
+static driver_t ubtbcmfw_driver =
+{
+ .name = "ubtbcmfw",
+ .methods = ubtbcmfw_methods,
+ .size = sizeof(struct ubtbcmfw_softc),
+};
+
+DRIVER_MODULE(ubtbcmfw, ushub, ubtbcmfw_driver, ubtbcmfw_devclass, NULL, 0);
+MODULE_DEPEND(ubtbcmfw, usb, 1, 1, 1);
+
+/*
+ * Probe for a USB Bluetooth device
+ */
+
+static int
+ubtbcmfw_probe(device_t dev)
+{
+ const struct usb2_device_id devs[] = {
+ /* Broadcom BCM2033 devices only */
+ { USB_VPI(USB_VENDOR_BROADCOM, USB_PRODUCT_BROADCOM_BCM2033, 0) },
+ };
+
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ if (uaa->info.bIfaceIndex != 0)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(devs, sizeof(devs), uaa));
+} /* ubtbcmfw_probe */
+
+/*
+ * Attach the device
+ */
+
+static int
+ubtbcmfw_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ubtbcmfw_softc *sc = device_get_softc(dev);
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_udev = uaa->device;
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "ubtbcmfw lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ iface_index = UBTBCMFW_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ ubtbcmfw_config, UBTBCMFW_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error != 0) {
+ device_printf(dev, "allocating USB transfers failed. %s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+
+ /* Set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &ubtbcmfw_fifo_methods, &sc->sc_fifo,
+ device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex);
+ if (error != 0) {
+ device_printf(dev, "could not attach fifo. %s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+
+ return (0); /* success */
+
+detach:
+ ubtbcmfw_detach(dev);
+
+ return (ENXIO); /* failure */
+} /* ubtbcmfw_attach */
+
+/*
+ * Detach the device
+ */
+
+static int
+ubtbcmfw_detach(device_t dev)
+{
+ struct ubtbcmfw_softc *sc = device_get_softc(dev);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UBTBCMFW_N_TRANSFER);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+} /* ubtbcmfw_detach */
+
+/*
+ * USB write callback
+ */
+
+static void
+ubtbcmfw_write_callback(struct usb2_xfer *xfer)
+{
+ struct ubtbcmfw_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX];
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+setup_next:
+ if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
+ xfer->max_data_length, &actlen, 0)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto setup_next;
+ }
+ break;
+ }
+} /* ubtbcmfw_write_callback */
+
+/*
+ * USB read callback
+ */
+
+static void
+ubtbcmfw_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubtbcmfw_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *fifo = sc->sc_fifo.fp[USB_FIFO_RX];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_fifo_put_data(fifo, xfer->frbuffers, 0, xfer->actlen, 1);
+ /* FALLTHROUGH */
+
+ case USB_ST_SETUP:
+setup_next:
+ if (usb2_fifo_put_bytes_max(fifo) > 0) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto setup_next;
+ }
+ break;
+ }
+} /* ubtbcmfw_read_callback */
+
+/*
+ * Called when we about to start read()ing from the device
+ */
+
+static void
+ubtbcmfw_start_read(struct usb2_fifo *fifo)
+{
+ struct ubtbcmfw_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
+} /* ubtbcmfw_start_read */
+
+/*
+ * Called when we about to stop reading (i.e. closing fifo)
+ */
+
+static void
+ubtbcmfw_stop_read(struct usb2_fifo *fifo)
+{
+ struct ubtbcmfw_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_INTR_DT_RD]);
+} /* ubtbcmfw_stop_read */
+
+/*
+ * Called when we about to start write()ing to the device, poll()ing
+ * for write or flushing fifo
+ */
+
+static void
+ubtbcmfw_start_write(struct usb2_fifo *fifo)
+{
+ struct ubtbcmfw_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
+} /* ubtbcmfw_start_write */
+
+/*
+ * Called when we about to stop writing (i.e. closing fifo)
+ */
+
+static void
+ubtbcmfw_stop_write(struct usb2_fifo *fifo)
+{
+ struct ubtbcmfw_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[UBTBCMFW_BULK_DT_WR]);
+} /* ubtbcmfw_stop_write */
+
+/*
+ * Called when fifo is open
+ */
+
+static int
+ubtbcmfw_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ubtbcmfw_softc *sc = fifo->priv_sc0;
+ struct usb2_xfer *xfer;
+
+ /*
+ * f_open fifo method can only be called with either FREAD
+ * or FWRITE flag set at one time.
+ */
+
+ if (fflags & FREAD)
+ xfer = sc->sc_xfer[UBTBCMFW_INTR_DT_RD];
+ else if (fflags & FWRITE)
+ xfer = sc->sc_xfer[UBTBCMFW_BULK_DT_WR];
+ else
+ return (EINVAL); /* should not happen */
+
+ if (usb2_fifo_alloc_buffer(fifo, xfer->max_data_length,
+ UBTBCMFW_IFQ_MAXLEN) != 0)
+ return (ENOMEM);
+
+ return (0);
+} /* ubtbcmfw_open */
+
+/*
+ * Called when fifo is closed
+ */
+
+static void
+ubtbcmfw_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ if (fflags & (FREAD | FWRITE))
+ usb2_fifo_free_buffer(fifo);
+} /* ubtbcmfw_close */
+
+/*
+ * Process ioctl() on USB device
+ */
+
+static int
+ubtbcmfw_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
+ int fflags, struct thread *td)
+{
+ struct ubtbcmfw_softc *sc = fifo->priv_sc0;
+ int error = 0;
+
+ switch (cmd) {
+ case USB_GET_DEVICE_DESC:
+ memcpy(data, usb2_get_device_descriptor(sc->sc_udev),
+ sizeof(struct usb2_device_descriptor));
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+} /* ubtbcmfw_ioctl */
diff --git a/sys/dev/usb/controller/at91dci.c b/sys/dev/usb/controller/at91dci.c
new file mode 100644
index 0000000..a7283b4
--- /dev/null
+++ b/sys/dev/usb/controller/at91dci.c
@@ -0,0 +1,2467 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2007-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 the driver for the AT91 series USB Device
+ * Controller
+ */
+
+/*
+ * Thanks to "David Brownell" for helping out regarding the hardware
+ * endpoint profiles.
+ */
+
+/*
+ * NOTE: The "fifo_bank" is not reset in hardware when the endpoint is
+ * reset !
+ *
+ * NOTE: When the chip detects BUS-reset it will also reset the
+ * endpoints, Function-address and more.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR at91dcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/at91dci.h>
+
+#define AT9100_DCI_BUS2SC(bus) \
+ ((struct at91dci_softc *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((struct at91dci_softc *)0)->sc_bus))))
+
+#define AT9100_DCI_PC2SC(pc) \
+ AT9100_DCI_BUS2SC((pc)->tag_parent->info->bus)
+
+#if USB_DEBUG
+static int at91dcidebug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, at91dci, CTLFLAG_RW, 0, "USB at91dci");
+SYSCTL_INT(_hw_usb2_at91dci, OID_AUTO, debug, CTLFLAG_RW,
+ &at91dcidebug, 0, "at91dci debug level");
+#endif
+
+#define AT9100_DCI_INTR_ENDPT 1
+
+/* prototypes */
+
+struct usb2_bus_methods at91dci_bus_methods;
+struct usb2_pipe_methods at91dci_device_bulk_methods;
+struct usb2_pipe_methods at91dci_device_ctrl_methods;
+struct usb2_pipe_methods at91dci_device_intr_methods;
+struct usb2_pipe_methods at91dci_device_isoc_fs_methods;
+struct usb2_pipe_methods at91dci_root_ctrl_methods;
+struct usb2_pipe_methods at91dci_root_intr_methods;
+
+static at91dci_cmd_t at91dci_setup_rx;
+static at91dci_cmd_t at91dci_data_rx;
+static at91dci_cmd_t at91dci_data_tx;
+static at91dci_cmd_t at91dci_data_tx_sync;
+static void at91dci_device_done(struct usb2_xfer *, usb2_error_t);
+static void at91dci_do_poll(struct usb2_bus *);
+static void at91dci_root_ctrl_poll(struct at91dci_softc *);
+static void at91dci_standard_done(struct usb2_xfer *);
+
+static usb2_sw_transfer_func_t at91dci_root_intr_done;
+static usb2_sw_transfer_func_t at91dci_root_ctrl_done;
+
+/*
+ * NOTE: Some of the bits in the CSR register have inverse meaning so
+ * we need a helper macro when acknowledging events:
+ */
+#define AT91_CSR_ACK(csr, what) do { \
+ (csr) &= ~((AT91_UDP_CSR_FORCESTALL| \
+ AT91_UDP_CSR_TXPKTRDY| \
+ AT91_UDP_CSR_RXBYTECNT) ^ (what));\
+ (csr) |= ((AT91_UDP_CSR_RX_DATA_BK0| \
+ AT91_UDP_CSR_RX_DATA_BK1| \
+ AT91_UDP_CSR_TXCOMP| \
+ AT91_UDP_CSR_RXSETUP| \
+ AT91_UDP_CSR_STALLSENT) ^ (what)); \
+} while (0)
+
+/*
+ * Here is a list of what the chip supports.
+ * Probably it supports more than listed here!
+ */
+static const struct usb2_hw_ep_profile
+ at91dci_ep_profile[AT91_UDP_EP_MAX] = {
+
+ [0] = {
+ .max_in_frame_size = 8,
+ .max_out_frame_size = 8,
+ .is_simplex = 1,
+ .support_control = 1,
+ },
+ [1] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 1,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+ [2] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 1,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+ [3] = {
+ /* can also do BULK */
+ .max_in_frame_size = 8,
+ .max_out_frame_size = 8,
+ .is_simplex = 1,
+ .support_interrupt = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+ [4] = {
+ .max_in_frame_size = 256,
+ .max_out_frame_size = 256,
+ .is_simplex = 1,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+ [5] = {
+ .max_in_frame_size = 256,
+ .max_out_frame_size = 256,
+ .is_simplex = 1,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+};
+
+static void
+at91dci_get_hw_ep_profile(struct usb2_device *udev,
+ const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+ if (ep_addr < AT91_UDP_EP_MAX) {
+ *ppf = (at91dci_ep_profile + ep_addr);
+ } else {
+ *ppf = NULL;
+ }
+}
+
+static void
+at91dci_clocks_on(struct at91dci_softc *sc)
+{
+ if (sc->sc_flags.clocks_off &&
+ sc->sc_flags.port_powered) {
+
+ DPRINTFN(5, "\n");
+
+ if (sc->sc_clocks_on) {
+ (sc->sc_clocks_on) (sc->sc_clocks_arg);
+ }
+ sc->sc_flags.clocks_off = 0;
+
+ /* enable Transceiver */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, 0);
+ }
+}
+
+static void
+at91dci_clocks_off(struct at91dci_softc *sc)
+{
+ if (!sc->sc_flags.clocks_off) {
+
+ DPRINTFN(5, "\n");
+
+ /* disable Transceiver */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS);
+
+ if (sc->sc_clocks_off) {
+ (sc->sc_clocks_off) (sc->sc_clocks_arg);
+ }
+ sc->sc_flags.clocks_off = 1;
+ }
+}
+
+static void
+at91dci_pull_up(struct at91dci_softc *sc)
+{
+ /* pullup D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up &&
+ sc->sc_flags.port_powered) {
+ sc->sc_flags.d_pulled_up = 1;
+ (sc->sc_pull_up) (sc->sc_pull_arg);
+ }
+}
+
+static void
+at91dci_pull_down(struct at91dci_softc *sc)
+{
+ /* pulldown D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ sc->sc_flags.d_pulled_up = 0;
+ (sc->sc_pull_down) (sc->sc_pull_arg);
+ }
+}
+
+static void
+at91dci_wakeup_peer(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ uint8_t use_polling;
+
+ if (!(sc->sc_flags.status_suspend)) {
+ return;
+ }
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, AT91_UDP_GSTATE_ESR);
+
+ /* wait 8 milliseconds */
+ if (use_polling) {
+ /* polling */
+ DELAY(8000);
+ } else {
+ /* Wait for reset to complete. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+ }
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, 0);
+}
+
+static void
+at91dci_set_address(struct at91dci_softc *sc, uint8_t addr)
+{
+ DPRINTFN(5, "addr=%d\n", addr);
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_FADDR, addr |
+ AT91_UDP_FADDR_EN);
+}
+
+static uint8_t
+at91dci_setup_rx(struct at91dci_td *td)
+{
+ struct at91dci_softc *sc;
+ struct usb2_device_request req;
+ uint32_t csr;
+ uint32_t temp;
+ uint16_t count;
+
+ /* read out FIFO status */
+ csr = bus_space_read_4(td->io_tag, td->io_hdl,
+ td->status_reg);
+
+ DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder);
+
+ temp = csr;
+ temp &= (AT91_UDP_CSR_RX_DATA_BK0 |
+ AT91_UDP_CSR_RX_DATA_BK1 |
+ AT91_UDP_CSR_STALLSENT |
+ AT91_UDP_CSR_RXSETUP |
+ AT91_UDP_CSR_TXCOMP);
+
+ if (!(csr & AT91_UDP_CSR_RXSETUP)) {
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(5, "stalling\n");
+ temp |= AT91_UDP_CSR_FORCESTALL;
+ td->did_stall = 1;
+ }
+ goto not_complete;
+ }
+ /* get the packet byte count */
+ count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16;
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(0, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(0, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ /* receive data */
+ bus_space_read_multi_1(td->io_tag, td->io_hdl,
+ td->fifo_reg, (void *)&req, sizeof(req));
+
+ /* copy data into real buffer */
+ usb2_copy_in(td->pc, 0, &req, sizeof(req));
+
+ td->offset = sizeof(req);
+ td->remainder = 0;
+
+ /* get pointer to softc */
+ sc = AT9100_DCI_PC2SC(td->pc);
+
+ /* sneak peek the set address */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+
+ /* sneak peek the endpoint direction */
+ if (req.bmRequestType & UE_DIR_IN) {
+ csr |= AT91_UDP_CSR_DIR;
+ } else {
+ csr &= ~AT91_UDP_CSR_DIR;
+ }
+
+ /* write the direction of the control transfer */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+ return (0); /* complete */
+
+not_complete:
+ /* clear interrupts, if any */
+ if (temp) {
+ DPRINTFN(5, "clearing 0x%08x\n", temp);
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+ }
+ return (1); /* not complete */
+
+}
+
+static uint8_t
+at91dci_data_rx(struct at91dci_td *td)
+{
+ struct usb2_page_search buf_res;
+ uint32_t csr;
+ uint32_t temp;
+ uint16_t count;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 2; /* don't loop forever! */
+ got_short = 0;
+
+ /* check if any of the FIFO banks have data */
+repeat:
+ /* read out FIFO status */
+ csr = bus_space_read_4(td->io_tag, td->io_hdl,
+ td->status_reg);
+
+ DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder);
+
+ if (csr & AT91_UDP_CSR_RXSETUP) {
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP
+ */
+ DPRINTFN(5, "faking complete\n");
+ return (0); /* complete */
+ }
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ /* Make sure that "STALLSENT" gets cleared */
+ temp = csr;
+ temp &= AT91_UDP_CSR_STALLSENT;
+
+ /* check status */
+ if (!(csr & (AT91_UDP_CSR_RX_DATA_BK0 |
+ AT91_UDP_CSR_RX_DATA_BK1))) {
+ if (temp) {
+ /* write command */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+ }
+ return (1); /* not complete */
+ }
+ /* get the packet byte count */
+ count = (csr & AT91_UDP_CSR_RXBYTECNT) >> 16;
+
+ /* verify the packet byte count */
+ if (count != td->max_packet_size) {
+ if (count < td->max_packet_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* receive data */
+ bus_space_read_multi_1(td->io_tag, td->io_hdl,
+ td->fifo_reg, buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* clear status bits */
+ if (td->support_multi_buffer) {
+ if (td->fifo_bank) {
+ td->fifo_bank = 0;
+ temp |= AT91_UDP_CSR_RX_DATA_BK1;
+ } else {
+ td->fifo_bank = 1;
+ temp |= AT91_UDP_CSR_RX_DATA_BK0;
+ }
+ } else {
+ temp |= (AT91_UDP_CSR_RX_DATA_BK0 |
+ AT91_UDP_CSR_RX_DATA_BK1);
+ }
+
+ /* write command */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+
+ /*
+ * NOTE: We may have to delay a little bit before
+ * proceeding after clearing the DATA_BK bits.
+ */
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+at91dci_data_tx(struct at91dci_td *td)
+{
+ struct usb2_page_search buf_res;
+ uint32_t csr;
+ uint32_t temp;
+ uint16_t count;
+ uint8_t to;
+
+ to = 2; /* don't loop forever! */
+
+repeat:
+
+ /* read out FIFO status */
+ csr = bus_space_read_4(td->io_tag, td->io_hdl,
+ td->status_reg);
+
+ DPRINTFN(5, "csr=0x%08x rem=%u\n", csr, td->remainder);
+
+ if (csr & AT91_UDP_CSR_RXSETUP) {
+ /*
+ * The current transfer was aborted
+ * by the USB Host
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ /* Make sure that "STALLSENT" gets cleared */
+ temp = csr;
+ temp &= AT91_UDP_CSR_STALLSENT;
+
+ if (csr & AT91_UDP_CSR_TXPKTRDY) {
+ if (temp) {
+ /* write command */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+ }
+ return (1); /* not complete */
+ } else {
+ /* clear TXCOMP and set TXPKTRDY */
+ temp |= (AT91_UDP_CSR_TXCOMP |
+ AT91_UDP_CSR_TXPKTRDY);
+ }
+
+ count = td->max_packet_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ while (count > 0) {
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* transmit data */
+ bus_space_write_multi_1(td->io_tag, td->io_hdl,
+ td->fifo_reg, buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* write command */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+at91dci_data_tx_sync(struct at91dci_td *td)
+{
+ struct at91dci_softc *sc;
+ uint32_t csr;
+ uint32_t temp;
+
+#if 0
+repeat:
+#endif
+
+ /* read out FIFO status */
+ csr = bus_space_read_4(td->io_tag, td->io_hdl,
+ td->status_reg);
+
+ DPRINTFN(5, "csr=0x%08x\n", csr);
+
+ if (csr & AT91_UDP_CSR_RXSETUP) {
+ DPRINTFN(5, "faking complete\n");
+ /* Race condition */
+ return (0); /* complete */
+ }
+ temp = csr;
+ temp &= (AT91_UDP_CSR_STALLSENT |
+ AT91_UDP_CSR_TXCOMP);
+
+ /* check status */
+ if (csr & AT91_UDP_CSR_TXPKTRDY) {
+ goto not_complete;
+ }
+ if (!(csr & AT91_UDP_CSR_TXCOMP)) {
+ goto not_complete;
+ }
+ sc = AT9100_DCI_PC2SC(td->pc);
+ if (sc->sc_dv_addr != 0xFF) {
+ /*
+ * The AT91 has a special requirement with regard to
+ * setting the address and that is to write the new
+ * address before clearing TXCOMP:
+ */
+ at91dci_set_address(sc, sc->sc_dv_addr);
+ }
+ /* write command */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+
+ return (0); /* complete */
+
+not_complete:
+ if (temp) {
+ /* write command */
+ AT91_CSR_ACK(csr, temp);
+ bus_space_write_4(td->io_tag, td->io_hdl,
+ td->status_reg, csr);
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+at91dci_xfer_do_fifo(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc;
+ struct at91dci_td *td;
+ uint8_t temp;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+ while (1) {
+ if ((td->func) (td)) {
+ /* operation in progress */
+ break;
+ }
+ if (((void *)td) == xfer->td_transfer_last) {
+ goto done;
+ }
+ if (td->error) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ goto done;
+ }
+ }
+ /*
+ * Fetch the next transfer descriptor and transfer
+ * some flags to the next transfer descriptor
+ */
+ temp = 0;
+ if (td->fifo_bank)
+ temp |= 1;
+ td = td->obj_next;
+ xfer->td_transfer_cache = td;
+ if (temp & 1)
+ td->fifo_bank = 1;
+ }
+ return (1); /* not complete */
+
+done:
+ sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ temp = (xfer->endpoint & UE_ADDR);
+
+ /* update FIFO bank flag and multi buffer */
+ if (td->fifo_bank) {
+ sc->sc_ep_flags[temp].fifo_bank = 1;
+ } else {
+ sc->sc_ep_flags[temp].fifo_bank = 0;
+ }
+
+ /* compute all actual lengths */
+
+ at91dci_standard_done(xfer);
+
+ return (0); /* complete */
+}
+
+static void
+at91dci_interrupt_poll(struct at91dci_softc *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!at91dci_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+void
+at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on)
+{
+ DPRINTFN(5, "vbus = %u\n", is_on);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ if (is_on) {
+ if (!sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &at91dci_root_intr_done);
+ }
+ } else {
+ if (sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &at91dci_root_intr_done);
+ }
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+at91dci_interrupt(struct at91dci_softc *sc)
+{
+ uint32_t status;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ status = AT91_UDP_READ_4(sc, AT91_UDP_ISR);
+ status &= AT91_UDP_INT_DEFAULT;
+
+ if (!status) {
+ USB_BUS_UNLOCK(&sc->sc_bus);
+ return;
+ }
+ /* acknowledge interrupts */
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, status);
+
+ /* check for any bus state change interrupts */
+
+ if (status & AT91_UDP_INT_BUS) {
+
+ DPRINTFN(5, "real bus interrupt 0x%08x\n", status);
+
+ if (status & AT91_UDP_INT_END_BR) {
+
+ /* set correct state */
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* disable resume interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR,
+ AT91_UDP_INT_RXRSM);
+ /* enable suspend interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IER,
+ AT91_UDP_INT_RXSUSP);
+ }
+ /*
+ * If RXRSM and RXSUSP is set at the same time we interpret
+ * that like RESUME. Resume is set when there is at least 3
+ * milliseconds of inactivity on the USB BUS.
+ */
+ if (status & AT91_UDP_INT_RXRSM) {
+ if (sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+
+ /* disable resume interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR,
+ AT91_UDP_INT_RXRSM);
+ /* enable suspend interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IER,
+ AT91_UDP_INT_RXSUSP);
+ }
+ } else if (status & AT91_UDP_INT_RXSUSP) {
+ if (!sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+
+ /* disable suspend interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR,
+ AT91_UDP_INT_RXSUSP);
+
+ /* enable resume interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IER,
+ AT91_UDP_INT_RXRSM);
+ }
+ }
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &at91dci_root_intr_done);
+ }
+ /* check for any endpoint interrupts */
+
+ if (status & AT91_UDP_INT_EPS) {
+
+ DPRINTFN(5, "real endpoint interrupt 0x%08x\n", status);
+
+ at91dci_interrupt_poll(sc);
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+at91dci_setup_standard_chain_sub(struct at91dci_std_temp *temp)
+{
+ struct at91dci_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ td->fifo_bank = 0;
+ td->error = 0;
+ td->did_stall = 0;
+ td->short_pkt = temp->short_pkt;
+ td->alt_next = temp->setup_alt_next;
+}
+
+static void
+at91dci_setup_standard_chain(struct usb2_xfer *xfer)
+{
+ struct at91dci_std_temp temp;
+ struct at91dci_softc *sc;
+ struct at91dci_td *td;
+ uint32_t x;
+ uint8_t ep_no;
+ uint8_t need_sync;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.max_frame_size = xfer->max_frame_size;
+
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ /* setup temp */
+
+ temp.td = NULL;
+ temp.td_next = xfer->td_start[0];
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.offset = 0;
+
+ sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.func = &at91dci_setup_rx;
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.short_pkt = temp.len ? 1 : 0;
+
+ at91dci_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &at91dci_data_tx;
+ need_sync = 1;
+ } else {
+ temp.func = &at91dci_data_rx;
+ need_sync = 0;
+ }
+
+ /* setup "pc" pointer */
+ temp.pc = xfer->frbuffers + x;
+ } else {
+ need_sync = 0;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ at91dci_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+
+ /* check if we need to sync */
+ if (need_sync && xfer->flags_int.control_xfr) {
+
+ /* we need a SYNC point after TX */
+ temp.func = &at91dci_data_tx_sync;
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ at91dci_setup_standard_chain_sub(&temp);
+ }
+ /* check if we should append a status stage */
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &at91dci_data_rx;
+ need_sync = 0;
+ } else {
+ temp.func = &at91dci_data_tx;
+ need_sync = 1;
+ }
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ at91dci_setup_standard_chain_sub(&temp);
+ if (need_sync) {
+ /* we need a SYNC point after TX */
+ temp.func = &at91dci_data_tx_sync;
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ at91dci_setup_standard_chain_sub(&temp);
+ }
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+
+ /* setup the correct fifo bank */
+ if (sc->sc_ep_flags[ep_no].fifo_bank) {
+ td = xfer->td_transfer_first;
+ td->fifo_bank = 1;
+ }
+}
+
+static void
+at91dci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ at91dci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+at91dci_start_standard_chain(struct usb2_xfer *xfer)
+{
+ DPRINTFN(9, "\n");
+
+ /* poll one time */
+ if (at91dci_xfer_do_fifo(xfer)) {
+
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ uint8_t ep_no = xfer->endpoint & UE_ADDR;
+
+ /*
+ * Only enable the endpoint interrupt when we are actually
+ * waiting for data, hence we are dealing with level
+ * triggered interrupts !
+ */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_EP(ep_no));
+
+ DPRINTFN(15, "enable interrupts on endpoint %d\n", ep_no);
+
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer,
+ &at91dci_timeout, xfer->timeout);
+ }
+ }
+}
+
+static void
+at91dci_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTFN(9, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ at91dci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+
+ /* set port bit */
+ sc->sc_hub_idata[0] = 0x02; /* we only have one port */
+
+done:
+ return;
+}
+
+static usb2_error_t
+at91dci_standard_done_sub(struct usb2_xfer *xfer)
+{
+ struct at91dci_td *td;
+ uint32_t len;
+ uint8_t error;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ do {
+ len = td->remainder;
+
+ if (xfer->aframes != xfer->nframes) {
+ /*
+ * Verify the length and subtract
+ * the remainder from "frlengths[]":
+ */
+ if (len > xfer->frlengths[xfer->aframes]) {
+ td->error = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error) {
+ /* the transfer is finished */
+ error = 1;
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+at91dci_standard_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = at91dci_standard_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = at91dci_standard_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = at91dci_standard_done_sub(xfer);
+ }
+done:
+ at91dci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * at91dci_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+at91dci_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ uint8_t ep_no;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* disable endpoint interrupt */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, AT91_UDP_INT_EP(ep_no));
+
+ DPRINTFN(15, "disable interrupts on endpoint %d\n", ep_no);
+ }
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+static void
+at91dci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer,
+ struct usb2_pipe *pipe)
+{
+ struct at91dci_softc *sc;
+ uint32_t csr_val;
+ uint8_t csr_reg;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(5, "pipe=%p\n", pipe);
+
+ if (xfer) {
+ /* cancel any ongoing transfers */
+ at91dci_device_done(xfer, USB_ERR_STALLED);
+ }
+ /* set FORCESTALL */
+ sc = AT9100_DCI_BUS2SC(udev->bus);
+ csr_reg = (pipe->edesc->bEndpointAddress & UE_ADDR);
+ csr_reg = AT91_UDP_CSR(csr_reg);
+ csr_val = AT91_UDP_READ_4(sc, csr_reg);
+ AT91_CSR_ACK(csr_val, AT91_UDP_CSR_FORCESTALL);
+ AT91_UDP_WRITE_4(sc, csr_reg, csr_val);
+}
+
+static void
+at91dci_clear_stall_sub(struct at91dci_softc *sc, uint8_t ep_no,
+ uint8_t ep_type, uint8_t ep_dir)
+{
+ const struct usb2_hw_ep_profile *pf;
+ uint32_t csr_val;
+ uint32_t temp;
+ uint8_t csr_reg;
+ uint8_t to;
+
+ if (ep_type == UE_CONTROL) {
+ /* clearing stall is not needed */
+ return;
+ }
+ /* compute CSR register offset */
+ csr_reg = AT91_UDP_CSR(ep_no);
+
+ /* compute default CSR value */
+ csr_val = 0;
+ AT91_CSR_ACK(csr_val, 0);
+
+ /* disable endpoint */
+ AT91_UDP_WRITE_4(sc, csr_reg, csr_val);
+
+ /* get endpoint profile */
+ at91dci_get_hw_ep_profile(NULL, &pf, ep_no);
+
+ /* reset FIFO */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_RST, AT91_UDP_RST_EP(ep_no));
+ AT91_UDP_WRITE_4(sc, AT91_UDP_RST, 0);
+
+ /*
+ * NOTE: One would assume that a FIFO reset would release the
+ * FIFO banks aswell, but it doesn't! We have to do this
+ * manually!
+ */
+
+ /* release FIFO banks, if any */
+ for (to = 0; to != 2; to++) {
+
+ /* get csr value */
+ csr_val = AT91_UDP_READ_4(sc, csr_reg);
+
+ if (csr_val & (AT91_UDP_CSR_RX_DATA_BK0 |
+ AT91_UDP_CSR_RX_DATA_BK1)) {
+ /* clear status bits */
+ if (pf->support_multi_buffer) {
+ if (sc->sc_ep_flags[ep_no].fifo_bank) {
+ sc->sc_ep_flags[ep_no].fifo_bank = 0;
+ temp = AT91_UDP_CSR_RX_DATA_BK1;
+ } else {
+ sc->sc_ep_flags[ep_no].fifo_bank = 1;
+ temp = AT91_UDP_CSR_RX_DATA_BK0;
+ }
+ } else {
+ temp = (AT91_UDP_CSR_RX_DATA_BK0 |
+ AT91_UDP_CSR_RX_DATA_BK1);
+ }
+ } else {
+ temp = 0;
+ }
+
+ /* clear FORCESTALL */
+ temp |= AT91_UDP_CSR_STALLSENT;
+
+ AT91_CSR_ACK(csr_val, temp);
+ AT91_UDP_WRITE_4(sc, csr_reg, csr_val);
+ }
+
+ /* compute default CSR value */
+ csr_val = 0;
+ AT91_CSR_ACK(csr_val, 0);
+
+ /* enable endpoint */
+ csr_val &= ~AT91_UDP_CSR_ET_MASK;
+ csr_val |= AT91_UDP_CSR_EPEDS;
+
+ if (ep_type == UE_CONTROL) {
+ csr_val |= AT91_UDP_CSR_ET_CTRL;
+ } else {
+ if (ep_type == UE_BULK) {
+ csr_val |= AT91_UDP_CSR_ET_BULK;
+ } else if (ep_type == UE_INTERRUPT) {
+ csr_val |= AT91_UDP_CSR_ET_INT;
+ } else {
+ csr_val |= AT91_UDP_CSR_ET_ISO;
+ }
+ if (ep_dir & UE_DIR_IN) {
+ csr_val |= AT91_UDP_CSR_ET_DIR_IN;
+ }
+ }
+
+ /* enable endpoint */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(ep_no), csr_val);
+}
+
+static void
+at91dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe)
+{
+ struct at91dci_softc *sc;
+ struct usb2_endpoint_descriptor *ed;
+
+ DPRINTFN(5, "pipe=%p\n", pipe);
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ /* check mode */
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ /* get softc */
+ sc = AT9100_DCI_BUS2SC(udev->bus);
+
+ /* get endpoint descriptor */
+ ed = pipe->edesc;
+
+ /* reset endpoint */
+ at91dci_clear_stall_sub(sc,
+ (ed->bEndpointAddress & UE_ADDR),
+ (ed->bmAttributes & UE_XFERTYPE),
+ (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+}
+
+usb2_error_t
+at91dci_init(struct at91dci_softc *sc)
+{
+ uint32_t csr_val;
+ uint8_t n;
+
+ DPRINTF("start\n");
+
+ /* set up the bus structure */
+ sc->sc_bus.usbrev = USB_REV_1_1;
+ sc->sc_bus.methods = &at91dci_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* turn on clocks */
+
+ if (sc->sc_clocks_on) {
+ (sc->sc_clocks_on) (sc->sc_clocks_arg);
+ }
+ /* wait a little for things to stabilise */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+
+ /* disable and clear all interrupts */
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF);
+ AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF);
+
+ /* compute default CSR value */
+
+ csr_val = 0;
+ AT91_CSR_ACK(csr_val, 0);
+
+ /* disable all endpoints */
+
+ for (n = 0; n != AT91_UDP_EP_MAX; n++) {
+
+ /* disable endpoint */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(n), csr_val);
+ }
+
+ /* enable the control endpoint */
+
+ AT91_CSR_ACK(csr_val, AT91_UDP_CSR_ET_CTRL |
+ AT91_UDP_CSR_EPEDS);
+
+ /* write to FIFO control register */
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_CSR(0), csr_val);
+
+ /* enable the interrupts we want */
+
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IER, AT91_UDP_INT_BUS);
+
+ /* turn off clocks */
+
+ at91dci_clocks_off(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+
+ at91dci_do_poll(&sc->sc_bus);
+
+ return (0); /* success */
+}
+
+void
+at91dci_uninit(struct at91dci_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* disable and clear all interrupts */
+ AT91_UDP_WRITE_4(sc, AT91_UDP_IDR, 0xFFFFFFFF);
+ AT91_UDP_WRITE_4(sc, AT91_UDP_ICR, 0xFFFFFFFF);
+
+ sc->sc_flags.port_powered = 0;
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ at91dci_pull_down(sc);
+ at91dci_clocks_off(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+at91dci_suspend(struct at91dci_softc *sc)
+{
+ return;
+}
+
+void
+at91dci_resume(struct at91dci_softc *sc)
+{
+ return;
+}
+
+static void
+at91dci_do_poll(struct usb2_bus *bus)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ at91dci_interrupt_poll(sc);
+ at91dci_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * at91dci bulk support
+ *------------------------------------------------------------------------*/
+static void
+at91dci_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_bulk_close(struct usb2_xfer *xfer)
+{
+ at91dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+at91dci_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_bulk_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ at91dci_setup_standard_chain(xfer);
+ at91dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods at91dci_device_bulk_methods =
+{
+ .open = at91dci_device_bulk_open,
+ .close = at91dci_device_bulk_close,
+ .enter = at91dci_device_bulk_enter,
+ .start = at91dci_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci control support
+ *------------------------------------------------------------------------*/
+static void
+at91dci_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ at91dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+at91dci_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ at91dci_setup_standard_chain(xfer);
+ at91dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods at91dci_device_ctrl_methods =
+{
+ .open = at91dci_device_ctrl_open,
+ .close = at91dci_device_ctrl_close,
+ .enter = at91dci_device_ctrl_enter,
+ .start = at91dci_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci interrupt support
+ *------------------------------------------------------------------------*/
+static void
+at91dci_device_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_intr_close(struct usb2_xfer *xfer)
+{
+ at91dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+at91dci_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_intr_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ at91dci_setup_standard_chain(xfer);
+ at91dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods at91dci_device_intr_methods =
+{
+ .open = at91dci_device_intr_open,
+ .close = at91dci_device_intr_close,
+ .enter = at91dci_device_intr_enter,
+ .start = at91dci_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci full speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+at91dci_device_isoc_fs_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_device_isoc_fs_close(struct usb2_xfer *xfer)
+{
+ at91dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+at91dci_device_isoc_fs_enter(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ uint32_t temp;
+ uint32_t nframes;
+
+ DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ nframes = AT91_UDP_READ_4(sc, AT91_UDP_FRM);
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ temp = (nframes - xfer->pipe->isoc_next) & AT91_UDP_FRM_MASK;
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (temp < xfer->nframes)) {
+ /*
+ * If there is data underflow or the pipe queue is
+ * empty we schedule the transfer a few frames ahead
+ * of the current frame position. Else two isochronous
+ * transfers might overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) & AT91_UDP_FRM_MASK;
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->pipe->isoc_next - nframes) & AT91_UDP_FRM_MASK;
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+ xfer->nframes;
+
+ /* compute frame number for next insertion */
+ xfer->pipe->isoc_next += xfer->nframes;
+
+ /* setup TDs */
+ at91dci_setup_standard_chain(xfer);
+}
+
+static void
+at91dci_device_isoc_fs_start(struct usb2_xfer *xfer)
+{
+ /* start TD chain */
+ at91dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods at91dci_device_isoc_fs_methods =
+{
+ .open = at91dci_device_isoc_fs_open,
+ .close = at91dci_device_isoc_fs_close,
+ .enter = at91dci_device_isoc_fs_enter,
+ .start = at91dci_device_isoc_fs_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware HUB by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+
+static void
+at91dci_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ at91dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/*
+ * USB descriptors for the virtual Root HUB:
+ */
+
+static const struct usb2_device_descriptor at91dci_devd = {
+ .bLength = sizeof(struct usb2_device_descriptor),
+ .bDescriptorType = UDESC_DEVICE,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_HSHUBSTT,
+ .bMaxPacketSize = 64,
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb2_device_qualifier at91dci_odevd = {
+ .bLength = sizeof(struct usb2_device_qualifier),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+};
+
+static const struct at91dci_config_desc at91dci_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(at91dci_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_HSHUBSTT,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = (UE_DIR_IN | AT9100_DCI_INTR_ENDPT),
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 255,
+ },
+};
+
+static const struct usb2_hub_descriptor_min at91dci_hubd = {
+ .bDescLength = sizeof(at91dci_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = 1,
+ .wHubCharacteristics[0] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF,
+ .wHubCharacteristics[1] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 8,
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0}, /* port is removable */
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_VENDOR \
+ 'A', 0, 'T', 0, 'M', 0, 'E', 0, 'L', 0
+
+#define STRING_PRODUCT \
+ 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \
+ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \
+ 'U', 0, 'B', 0,
+
+USB_MAKE_STRING_DESC(STRING_LANG, at91dci_langtab);
+USB_MAKE_STRING_DESC(STRING_VENDOR, at91dci_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, at91dci_product);
+
+static void
+at91dci_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+at91dci_root_ctrl_task(struct usb2_bus *bus)
+{
+ at91dci_root_ctrl_poll(AT9100_DCI_BUS2SC(bus));
+}
+
+static void
+at91dci_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+ uint16_t value;
+ uint16_t index;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ at91dci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0);
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ /* demultiplex the control request */
+
+ switch (std->req.bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (std->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 (std->req.bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_DESCRIPTOR:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SYNCH_FRAME:
+ goto tr_valid; /* nop */
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_SET_INTERFACE:
+ goto tr_handle_set_interface;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_GET_INTERFACE:
+ goto tr_handle_get_interface;
+ case UR_GET_STATUS:
+ goto tr_handle_get_iface_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_INTERFACE:
+ case UT_WRITE_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_READ_CLASS_INTERFACE:
+ case UT_READ_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_WRITE_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_valid;
+ case UR_SET_DESCRIPTOR:
+ case UR_SET_FEATURE:
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_handle_clear_port_feature;
+ case UR_SET_FEATURE:
+ goto tr_handle_set_port_feature;
+ case UR_CLEAR_TT_BUFFER:
+ case UR_RESET_TT:
+ case UR_STOP_TT:
+ goto tr_valid;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_GET_TT_STATE:
+ goto tr_handle_get_tt_state;
+ case UR_GET_STATUS:
+ goto tr_handle_get_port_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ case UR_GET_STATUS:
+ goto tr_handle_get_class_status;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(at91dci_devd);
+ std->ptr = USB_ADD_BYTES(&at91dci_devd, 0);
+ goto tr_valid;
+ case UDESC_CONFIG:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(at91dci_confd);
+ std->ptr = USB_ADD_BYTES(&at91dci_confd, 0);
+ goto tr_valid;
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ std->len = sizeof(at91dci_langtab);
+ std->ptr = USB_ADD_BYTES(&at91dci_langtab, 0);
+ goto tr_valid;
+
+ case 1: /* Vendor */
+ std->len = sizeof(at91dci_vendor);
+ std->ptr = USB_ADD_BYTES(&at91dci_vendor, 0);
+ goto tr_valid;
+
+ case 2: /* Product */
+ std->len = sizeof(at91dci_product);
+ std->ptr = USB_ADD_BYTES(&at91dci_product, 0);
+ goto tr_valid;
+ default:
+ break;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_config:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+ goto tr_valid;
+
+tr_handle_get_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (value & 0xFF00) {
+ goto tr_stalled;
+ }
+ sc->sc_rt_addr = value;
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (value >= 2) {
+ goto tr_stalled;
+ }
+ sc->sc_conf = value;
+ goto tr_valid;
+
+tr_handle_get_interface:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = 0;
+ goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, 0);
+ goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+ goto tr_valid;
+
+tr_handle_clear_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
+
+ switch (value) {
+ case UHF_PORT_SUSPEND:
+ at91dci_wakeup_peer(xfer);
+ break;
+
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 0;
+ break;
+
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 0;
+ at91dci_pull_down(sc);
+ at91dci_clocks_off(sc);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_flags.change_connect = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_flags.change_suspend = 0;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_set_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(9, "UR_SET_PORT_FEATURE\n");
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 1;
+ break;
+ case UHF_PORT_SUSPEND:
+ case UHF_PORT_RESET:
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 1;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_get_port_status:
+
+ DPRINTFN(9, "UR_GET_PORT_STATUS\n");
+
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ if (sc->sc_flags.status_vbus) {
+ at91dci_clocks_on(sc);
+ at91dci_pull_up(sc);
+ } else {
+ at91dci_pull_down(sc);
+ at91dci_clocks_off(sc);
+ }
+
+ /* Select FULL-speed and Device Side Mode */
+
+ value = UPS_PORT_MODE_DEVICE;
+
+ if (sc->sc_flags.port_powered) {
+ value |= UPS_PORT_POWER;
+ }
+ if (sc->sc_flags.port_enabled) {
+ value |= UPS_PORT_ENABLED;
+ }
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ value |= UPS_CURRENT_CONNECT_STATUS;
+ }
+ if (sc->sc_flags.status_suspend) {
+ value |= UPS_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+ value = 0;
+
+ if (sc->sc_flags.change_connect) {
+ value |= UPS_C_CONNECT_STATUS;
+
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ /* reset endpoint flags */
+ bzero(sc->sc_ep_flags, sizeof(sc->sc_ep_flags));
+ }
+ }
+ if (sc->sc_flags.change_suspend) {
+ value |= UPS_C_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortChange, value);
+ std->len = sizeof(sc->sc_hub_temp.ps);
+ goto tr_valid;
+
+tr_handle_get_class_descriptor:
+ if (value & 0xFF) {
+ goto tr_stalled;
+ }
+ std->ptr = USB_ADD_BYTES(&at91dci_hubd, 0);
+ std->len = sizeof(at91dci_hubd);
+ goto tr_valid;
+
+tr_stalled:
+ std->err = USB_ERR_STALLED;
+tr_valid:
+done:
+ return;
+}
+
+static void
+at91dci_root_ctrl_poll(struct at91dci_softc *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &at91dci_root_ctrl_done);
+}
+
+struct usb2_pipe_methods at91dci_root_ctrl_methods =
+{
+ .open = at91dci_root_ctrl_open,
+ .close = at91dci_root_ctrl_close,
+ .enter = at91dci_root_ctrl_enter,
+ .start = at91dci_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+at91dci_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_root_intr_close(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ at91dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+at91dci_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_root_intr_start(struct usb2_xfer *xfer)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+}
+
+struct usb2_pipe_methods at91dci_root_intr_methods =
+{
+ .open = at91dci_root_intr_open,
+ .close = at91dci_root_intr_close,
+ .enter = at91dci_root_intr_enter,
+ .start = at91dci_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+at91dci_xfer_setup(struct usb2_setup_params *parm)
+{
+ const struct usb2_hw_ep_profile *pf;
+ struct at91dci_softc *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t n;
+ uint8_t ep_no;
+
+ sc = AT9100_DCI_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+ parm->hc_max_packet_size = 0x500;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x500;
+
+ usb2_transfer_setup_sub(parm);
+
+ /*
+ * compute maximum number of TDs
+ */
+ if (parm->methods == &at91dci_device_ctrl_methods) {
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */
+ + 1 /* SYNC 2 */ ;
+
+ } else if (parm->methods == &at91dci_device_bulk_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &at91dci_device_intr_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &at91dci_device_isoc_fs_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else {
+
+ ntd = 0;
+ }
+
+ /*
+ * check if "usb2_transfer_setup_sub" set an error
+ */
+ if (parm->err) {
+ return;
+ }
+ /*
+ * allocate transfer descriptors
+ */
+ last_obj = NULL;
+
+ /*
+ * get profile stuff
+ */
+ if (ntd) {
+
+ ep_no = xfer->endpoint & UE_ADDR;
+ at91dci_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+ if (pf == NULL) {
+ /* should not happen */
+ parm->err = USB_ERR_INVAL;
+ return;
+ }
+ } else {
+ ep_no = 0;
+ pf = NULL;
+ }
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ for (n = 0; n != ntd; n++) {
+
+ struct at91dci_td *td;
+
+ if (parm->buf) {
+
+ td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ /* init TD */
+ td->io_tag = sc->sc_io_tag;
+ td->io_hdl = sc->sc_io_hdl;
+ td->max_packet_size = xfer->max_packet_size;
+ td->status_reg = AT91_UDP_CSR(ep_no);
+ td->fifo_reg = AT91_UDP_FDR(ep_no);
+ if (pf->support_multi_buffer) {
+ td->support_multi_buffer = 1;
+ }
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ parm->size[0] += sizeof(*td);
+ }
+
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+at91dci_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+at91dci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ struct at91dci_softc *sc = AT9100_DCI_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_rt_addr);
+
+ if (udev->device_index == sc->sc_rt_addr) {
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &at91dci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | AT9100_DCI_INTR_ENDPT:
+ pipe->methods = &at91dci_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ if (udev->speed != USB_SPEED_FULL) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &at91dci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &at91dci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ pipe->methods = &at91dci_device_isoc_fs_methods;
+ break;
+ case UE_BULK:
+ pipe->methods = &at91dci_device_bulk_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+struct usb2_bus_methods at91dci_bus_methods =
+{
+ .pipe_init = &at91dci_pipe_init,
+ .xfer_setup = &at91dci_xfer_setup,
+ .xfer_unsetup = &at91dci_xfer_unsetup,
+ .do_poll = &at91dci_do_poll,
+ .get_hw_ep_profile = &at91dci_get_hw_ep_profile,
+ .set_stall = &at91dci_set_stall,
+ .clear_stall = &at91dci_clear_stall,
+ .roothub_exec = &at91dci_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/at91dci.h b/sys/dev/usb/controller/at91dci.h
new file mode 100644
index 0000000..d386307
--- /dev/null
+++ b/sys/dev/usb/controller/at91dci.h
@@ -0,0 +1,245 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2006 ATMEL
+ * Copyright (c) 2007 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * USB Device Port (UDP) register definition, based on
+ * "AT91RM9200.h" provided by ATMEL.
+ */
+
+#ifndef _AT9100_DCI_H_
+#define _AT9100_DCI_H_
+
+#define AT91_MAX_DEVICES (USB_MIN_DEVICES + 1)
+
+#define AT91_UDP_FRM 0x00 /* Frame number register */
+#define AT91_UDP_FRM_MASK (0x7FF << 0) /* Frame Number as Defined in
+ * the Packet Field Formats */
+#define AT91_UDP_FRM_ERR (0x1 << 16) /* Frame Error */
+#define AT91_UDP_FRM_OK (0x1 << 17) /* Frame OK */
+
+#define AT91_UDP_GSTATE 0x04 /* Global state register */
+#define AT91_UDP_GSTATE_ADDR (0x1 << 0) /* Addressed state */
+#define AT91_UDP_GSTATE_CONFG (0x1 << 1) /* Configured */
+#define AT91_UDP_GSTATE_ESR (0x1 << 2) /* Enable Send Resume */
+#define AT91_UDP_GSTATE_RSM (0x1 << 3) /* A Resume Has Been Sent to
+ * the Host */
+#define AT91_UDP_GSTATE_RMW (0x1 << 4) /* Remote Wake Up Enable */
+
+#define AT91_UDP_FADDR 0x08 /* Function Address Register */
+#define AT91_UDP_FADDR_MASK (0x7F << 0)/* Function Address Mask */
+#define AT91_UDP_FADDR_EN (0x1 << 8)/* Function Enable */
+
+#define AT91_UDP_RES0 0x0C /* Reserved 0 */
+
+#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */
+#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */
+#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */
+#define AT91_UDP_ISR 0x1C /* Interrupt Status Register */
+#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */
+#define AT91_UDP_INT_EP(n) (0x1 <<(n))/* Endpoint "n" Interrupt */
+#define AT91_UDP_INT_RXSUSP (0x1 << 8)/* USB Suspend Interrupt */
+#define AT91_UDP_INT_RXRSM (0x1 << 9)/* USB Resume Interrupt */
+#define AT91_UDP_INT_EXTRSM (0x1 << 10)/* USB External Resume Interrupt */
+#define AT91_UDP_INT_SOFINT (0x1 << 11)/* USB Start Of frame Interrupt */
+#define AT91_UDP_INT_END_BR (0x1 << 12)/* USB End Of Bus Reset Interrupt */
+#define AT91_UDP_INT_WAKEUP (0x1 << 13)/* USB Resume Interrupt */
+
+#define AT91_UDP_INT_BUS \
+ (AT91_UDP_INT_RXSUSP|AT91_UDP_INT_RXRSM| \
+ AT91_UDP_INT_END_BR)
+
+#define AT91_UDP_INT_EPS \
+ (AT91_UDP_INT_EP(0)|AT91_UDP_INT_EP(1)| \
+ AT91_UDP_INT_EP(2)|AT91_UDP_INT_EP(3)| \
+ AT91_UDP_INT_EP(4)|AT91_UDP_INT_EP(5))
+
+#define AT91_UDP_INT_DEFAULT \
+ (AT91_UDP_INT_EPS|AT91_UDP_INT_BUS)
+
+#define AT91_UDP_RES1 0x24 /* Reserved 1 */
+#define AT91_UDP_RST 0x28 /* Reset Endpoint Register */
+#define AT91_UDP_RST_EP(n) (0x1 << (n))/* Reset Endpoint "n" */
+
+#define AT91_UDP_RES2 0x2C /* Reserved 2 */
+
+#define AT91_UDP_CSR(n) (0x30 + (4*(n)))/* Endpoint Control and Status
+ * Register */
+#define AT91_UDP_CSR_TXCOMP (0x1 << 0) /* Generates an IN packet with data
+ * previously written in the DPR */
+#define AT91_UDP_CSR_RX_DATA_BK0 (0x1 << 1) /* Receive Data Bank 0 */
+#define AT91_UDP_CSR_RXSETUP (0x1 << 2) /* Sends STALL to the Host
+ * (Control endpoints) */
+#define AT91_UDP_CSR_ISOERROR (0x1 << 3) /* Isochronous error
+ * (Isochronous endpoints) */
+#define AT91_UDP_CSR_STALLSENT (0x1 << 3) /* Stall sent (Control, bulk,
+ * interrupt endpoints) */
+#define AT91_UDP_CSR_TXPKTRDY (0x1 << 4) /* Transmit Packet Ready */
+#define AT91_UDP_CSR_FORCESTALL (0x1 << 5) /* Force Stall (used by
+ * Control, Bulk and
+ * Isochronous endpoints). */
+#define AT91_UDP_CSR_RX_DATA_BK1 (0x1 << 6) /* Receive Data Bank 1 (only
+ * used by endpoints with
+ * ping-pong attributes). */
+#define AT91_UDP_CSR_DIR (0x1 << 7) /* Transfer Direction */
+#define AT91_UDP_CSR_ET_MASK (0x7 << 8) /* Endpoint transfer type mask */
+#define AT91_UDP_CSR_ET_CTRL (0x0 << 8) /* Control IN+OUT */
+#define AT91_UDP_CSR_ET_ISO (0x1 << 8) /* Isochronous */
+#define AT91_UDP_CSR_ET_BULK (0x2 << 8) /* Bulk */
+#define AT91_UDP_CSR_ET_INT (0x3 << 8) /* Interrupt */
+#define AT91_UDP_CSR_ET_DIR_OUT (0x0 << 8) /* OUT tokens */
+#define AT91_UDP_CSR_ET_DIR_IN (0x4 << 8) /* IN tokens */
+#define AT91_UDP_CSR_DTGLE (0x1 << 11) /* Data Toggle */
+#define AT91_UDP_CSR_EPEDS (0x1 << 15) /* Endpoint Enable Disable */
+#define AT91_UDP_CSR_RXBYTECNT (0x7FF << 16) /* Number Of Bytes Available
+ * in the FIFO */
+
+#define AT91_UDP_FDR(n) (0x50 + (4*(n)))/* Endpoint FIFO Data Register */
+#define AT91_UDP_RES3 0x70 /* Reserved 3 */
+#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */
+#define AT91_UDP_TXVC_DIS (0x1 << 8)
+
+#define AT91_UDP_EP_MAX 6 /* maximum number of endpoints
+ * supported */
+
+#define AT91_UDP_READ_4(sc, reg) \
+ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
+
+#define AT91_UDP_WRITE_4(sc, reg, data) \
+ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+struct at91dci_td;
+
+typedef uint8_t (at91dci_cmd_t)(struct at91dci_td *td);
+
+struct at91dci_td {
+ bus_space_tag_t io_tag;
+ bus_space_handle_t io_hdl;
+ struct at91dci_td *obj_next;
+ at91dci_cmd_t *func;
+ struct usb2_page_cache *pc;
+ uint32_t offset;
+ uint32_t remainder;
+ uint16_t max_packet_size;
+ uint8_t status_reg;
+ uint8_t fifo_reg;
+ uint8_t fifo_bank:1;
+ uint8_t error:1;
+ uint8_t alt_next:1;
+ uint8_t short_pkt:1;
+ uint8_t support_multi_buffer:1;
+ uint8_t did_stall:1;
+};
+
+struct at91dci_std_temp {
+ at91dci_cmd_t *func;
+ struct usb2_page_cache *pc;
+ struct at91dci_td *td;
+ struct at91dci_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t max_frame_size;
+ uint8_t short_pkt;
+ /*
+ * short_pkt = 0: transfer should be short terminated
+ * short_pkt = 1: transfer should not be short terminated
+ */
+ uint8_t setup_alt_next;
+};
+
+struct at91dci_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union at91dci_hub_temp {
+ uWord wValue;
+ struct usb2_port_status ps;
+};
+
+struct at91dci_ep_flags {
+ uint8_t fifo_bank:1; /* hardware specific */
+};
+
+struct at91dci_flags {
+ uint8_t change_connect:1;
+ uint8_t change_suspend:1;
+ uint8_t status_suspend:1; /* set if suspended */
+ uint8_t status_vbus:1; /* set if present */
+ uint8_t status_bus_reset:1; /* set if reset complete */
+ uint8_t remote_wakeup:1;
+ uint8_t self_powered:1;
+ uint8_t clocks_off:1;
+ uint8_t port_powered:1;
+ uint8_t port_enabled:1;
+ uint8_t d_pulled_up:1;
+};
+
+struct at91dci_softc {
+ struct usb2_bus sc_bus;
+ union at91dci_hub_temp sc_hub_temp;
+ LIST_HEAD(, usb2_xfer) sc_interrupt_list_head;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+
+ struct usb2_device *sc_devices[AT91_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ void (*sc_clocks_on) (void *arg);
+ void (*sc_clocks_off) (void *arg);
+ void *sc_clocks_arg;
+
+ void (*sc_pull_up) (void *arg);
+ void (*sc_pull_down) (void *arg);
+ void *sc_pull_arg;
+
+ uint8_t sc_rt_addr; /* root HUB address */
+ uint8_t sc_dv_addr; /* device address */
+ uint8_t sc_conf; /* root HUB config */
+
+ uint8_t sc_hub_idata[1];
+
+ struct at91dci_flags sc_flags;
+ struct at91dci_ep_flags sc_ep_flags[AT91_UDP_EP_MAX];
+};
+
+/* prototypes */
+
+usb2_error_t at91dci_init(struct at91dci_softc *sc);
+void at91dci_uninit(struct at91dci_softc *sc);
+void at91dci_suspend(struct at91dci_softc *sc);
+void at91dci_resume(struct at91dci_softc *sc);
+void at91dci_interrupt(struct at91dci_softc *sc);
+void at91dci_vbus_interrupt(struct at91dci_softc *sc, uint8_t is_on);
+
+#endif /* _AT9100_DCI_H_ */
diff --git a/sys/dev/usb/controller/at91dci_atmelarm.c b/sys/dev/usb/controller/at91dci_atmelarm.c
new file mode 100644
index 0000000..71d2937
--- /dev/null
+++ b/sys/dev/usb/controller/at91dci_atmelarm.c
@@ -0,0 +1,347 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2007-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/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/at91dci.h>
+
+#include <sys/rman.h>
+
+#include <arm/at91/at91_pmcvar.h>
+#include <arm/at91/at91rm92reg.h>
+#include <arm/at91/at91_pio_rm9200.h>
+#include <arm/at91/at91_piovar.h>
+
+#define MEM_RID 0
+
+/* Pin Definitions - do they belong here or somewhere else ? */
+
+#define VBUS_MASK AT91C_PIO_PB24
+#define VBUS_BASE AT91RM92_PIOB_BASE
+
+#define PULLUP_MASK AT91C_PIO_PB22
+#define PULLUP_BASE AT91RM92_PIOB_BASE
+
+static device_probe_t at91_udp_probe;
+static device_attach_t at91_udp_attach;
+static device_detach_t at91_udp_detach;
+static device_shutdown_t at91_udp_shutdown;
+
+struct at91_udp_softc {
+ struct at91dci_softc sc_dci; /* must be first */
+ struct at91_pmc_clock *sc_iclk;
+ struct at91_pmc_clock *sc_fclk;
+ struct resource *sc_vbus_irq_res;
+ void *sc_vbus_intr_hdl;
+};
+
+static void
+at91_vbus_poll(struct at91_udp_softc *sc)
+{
+ uint32_t temp;
+ uint8_t vbus_val;
+
+ /* XXX temporary clear interrupts here */
+
+ temp = at91_pio_gpio_clear_interrupt(VBUS_BASE);
+
+ /* just forward it */
+
+ vbus_val = at91_pio_gpio_get(VBUS_BASE, VBUS_MASK);
+ at91dci_vbus_interrupt(&sc->sc_dci, vbus_val);
+}
+
+static void
+at91_udp_clocks_on(void *arg)
+{
+ struct at91_udp_softc *sc = arg;
+
+ at91_pmc_clock_enable(sc->sc_iclk);
+ at91_pmc_clock_enable(sc->sc_fclk);
+}
+
+static void
+at91_udp_clocks_off(void *arg)
+{
+ struct at91_udp_softc *sc = arg;
+
+ at91_pmc_clock_disable(sc->sc_fclk);
+ at91_pmc_clock_disable(sc->sc_iclk);
+}
+
+static void
+at91_udp_pull_up(void *arg)
+{
+ at91_pio_gpio_set(PULLUP_BASE, PULLUP_MASK);
+}
+
+static void
+at91_udp_pull_down(void *arg)
+{
+ at91_pio_gpio_clear(PULLUP_BASE, PULLUP_MASK);
+}
+
+static int
+at91_udp_probe(device_t dev)
+{
+ device_set_desc(dev, "AT91 integrated AT91_UDP controller");
+ return (0);
+}
+
+static int
+at91_udp_attach(device_t dev)
+{
+ struct at91_udp_softc *sc = device_get_softc(dev);
+ int err;
+ int rid;
+
+ /* setup AT9100 USB device controller interface softc */
+
+ sc->sc_dci.sc_clocks_on = &at91_udp_clocks_on;
+ sc->sc_dci.sc_clocks_off = &at91_udp_clocks_off;
+ sc->sc_dci.sc_clocks_arg = sc;
+ sc->sc_dci.sc_pull_up = &at91_udp_pull_up;
+ sc->sc_dci.sc_pull_down = &at91_udp_pull_down;
+ sc->sc_dci.sc_pull_arg = sc;
+
+ /* initialise some bus fields */
+ sc->sc_dci.sc_bus.parent = dev;
+ sc->sc_dci.sc_bus.devices = sc->sc_dci.sc_devices;
+ sc->sc_dci.sc_bus.devices_max = AT91_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_dci.sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ return (ENOMEM);
+ }
+ /*
+ * configure VBUS input pin, enable deglitch and enable
+ * interrupt :
+ */
+ at91_pio_use_gpio(VBUS_BASE, VBUS_MASK);
+ at91_pio_gpio_input(VBUS_BASE, VBUS_MASK);
+ at91_pio_gpio_set_deglitch(VBUS_BASE, VBUS_MASK, 1);
+ at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 1);
+
+ /*
+ * configure PULLUP output pin :
+ */
+ at91_pio_use_gpio(PULLUP_BASE, PULLUP_MASK);
+ at91_pio_gpio_output(PULLUP_BASE, PULLUP_MASK, 0);
+
+ at91_udp_pull_down(sc);
+
+ /* wait 10ms for pulldown to stabilise */
+ usb2_pause_mtx(NULL, hz / 100);
+
+ sc->sc_iclk = at91_pmc_clock_ref("udc_clk");
+ sc->sc_fclk = at91_pmc_clock_ref("udpck");
+
+ rid = MEM_RID;
+ sc->sc_dci.sc_io_res =
+ bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+
+ if (!(sc->sc_dci.sc_io_res)) {
+ err = ENOMEM;
+ goto error;
+ }
+ sc->sc_dci.sc_io_tag = rman_get_bustag(sc->sc_dci.sc_io_res);
+ sc->sc_dci.sc_io_hdl = rman_get_bushandle(sc->sc_dci.sc_io_res);
+ sc->sc_dci.sc_io_size = rman_get_size(sc->sc_dci.sc_io_res);
+
+ rid = 0;
+ sc->sc_dci.sc_irq_res =
+ bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (!(sc->sc_dci.sc_irq_res)) {
+ goto error;
+ }
+ rid = 1;
+ sc->sc_vbus_irq_res =
+ bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (!(sc->sc_vbus_irq_res)) {
+ goto error;
+ }
+ sc->sc_dci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_dci.sc_bus.bdev)) {
+ goto error;
+ }
+ device_set_ivars(sc->sc_dci.sc_bus.bdev, &sc->sc_dci.sc_bus);
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
+#else
+ err = bus_setup_intr(dev, sc->sc_dci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)at91dci_interrupt, sc, &sc->sc_dci.sc_intr_hdl);
+#endif
+ if (err) {
+ sc->sc_dci.sc_intr_hdl = NULL;
+ goto error;
+ }
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(dev, sc->sc_vbus_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)at91_vbus_poll, sc, &sc->sc_vbus_intr_hdl);
+#else
+ err = bus_setup_intr(dev, sc->sc_vbus_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)at91_vbus_poll, sc, &sc->sc_vbus_intr_hdl);
+#endif
+ if (err) {
+ sc->sc_vbus_intr_hdl = NULL;
+ goto error;
+ }
+ err = at91dci_init(&sc->sc_dci);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_dci.sc_bus.bdev);
+ }
+ if (err) {
+ goto error;
+ } else {
+ /* poll VBUS one time */
+ at91_vbus_poll(sc);
+ }
+ return (0);
+
+error:
+ at91_udp_detach(dev);
+ return (ENXIO);
+}
+
+static int
+at91_udp_detach(device_t dev)
+{
+ struct at91_udp_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_dci.sc_bus.bdev) {
+ bdev = sc->sc_dci.sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(dev);
+
+ /* disable Transceiver */
+ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_TXVC, AT91_UDP_TXVC_DIS);
+
+ /* disable and clear all interrupts */
+ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_IDR, 0xFFFFFFFF);
+ AT91_UDP_WRITE_4(&sc->sc_dci, AT91_UDP_ICR, 0xFFFFFFFF);
+
+ /* disable VBUS interrupt */
+ at91_pio_gpio_set_interrupt(VBUS_BASE, VBUS_MASK, 0);
+
+ if (sc->sc_vbus_irq_res && sc->sc_vbus_intr_hdl) {
+ err = bus_teardown_intr(dev, sc->sc_vbus_irq_res,
+ sc->sc_vbus_intr_hdl);
+ sc->sc_vbus_intr_hdl = NULL;
+ }
+ if (sc->sc_vbus_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 1,
+ sc->sc_vbus_irq_res);
+ sc->sc_vbus_irq_res = NULL;
+ }
+ if (sc->sc_dci.sc_irq_res && sc->sc_dci.sc_intr_hdl) {
+ /*
+ * only call at91_udp_uninit() after at91_udp_init()
+ */
+ at91dci_uninit(&sc->sc_dci);
+
+ err = bus_teardown_intr(dev, sc->sc_dci.sc_irq_res,
+ sc->sc_dci.sc_intr_hdl);
+ sc->sc_dci.sc_intr_hdl = NULL;
+ }
+ if (sc->sc_dci.sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->sc_dci.sc_irq_res);
+ sc->sc_dci.sc_irq_res = NULL;
+ }
+ if (sc->sc_dci.sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID,
+ sc->sc_dci.sc_io_res);
+ sc->sc_dci.sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_dci.sc_bus, NULL);
+
+ /* disable clocks */
+ at91_pmc_clock_disable(sc->sc_iclk);
+ at91_pmc_clock_disable(sc->sc_fclk);
+ at91_pmc_clock_deref(sc->sc_fclk);
+ at91_pmc_clock_deref(sc->sc_iclk);
+
+ return (0);
+}
+
+static int
+at91_udp_shutdown(device_t dev)
+{
+ struct at91_udp_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_shutdown(dev);
+ if (err)
+ return (err);
+
+ at91dci_uninit(&sc->sc_dci);
+
+ return (0);
+}
+
+static device_method_t at91_udp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, at91_udp_probe),
+ DEVMETHOD(device_attach, at91_udp_attach),
+ DEVMETHOD(device_detach, at91_udp_detach),
+ DEVMETHOD(device_shutdown, at91_udp_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t at91_udp_driver = {
+ "at91_udp",
+ at91_udp_methods,
+ sizeof(struct at91_udp_softc),
+};
+
+static devclass_t at91_udp_devclass;
+
+DRIVER_MODULE(at91_udp, atmelarm, at91_udp_driver, at91_udp_devclass, 0, 0);
diff --git a/sys/dev/usb/controller/atmegadci.c b/sys/dev/usb/controller/atmegadci.c
new file mode 100644
index 0000000..8a68822
--- /dev/null
+++ b/sys/dev/usb/controller/atmegadci.c
@@ -0,0 +1,2327 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2009 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the driver for the ATMEGA series USB Device
+ * Controller
+ */
+
+/*
+ * NOTE: When the chip detects BUS-reset it will also reset the
+ * endpoints, Function-address and more.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR atmegadci_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/atmegadci.h>
+
+#define ATMEGA_BUS2SC(bus) \
+ ((struct atmegadci_softc *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((struct atmegadci_softc *)0)->sc_bus))))
+
+#define ATMEGA_PC2SC(pc) \
+ ATMEGA_BUS2SC((pc)->tag_parent->info->bus)
+
+#if USB_DEBUG
+static int atmegadci_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, atmegadci, CTLFLAG_RW, 0, "USB ATMEGA DCI");
+SYSCTL_INT(_hw_usb2_atmegadci, OID_AUTO, debug, CTLFLAG_RW,
+ &atmegadci_debug, 0, "ATMEGA DCI debug level");
+#endif
+
+#define ATMEGA_INTR_ENDPT 1
+
+/* prototypes */
+
+struct usb2_bus_methods atmegadci_bus_methods;
+struct usb2_pipe_methods atmegadci_device_bulk_methods;
+struct usb2_pipe_methods atmegadci_device_ctrl_methods;
+struct usb2_pipe_methods atmegadci_device_intr_methods;
+struct usb2_pipe_methods atmegadci_device_isoc_fs_methods;
+struct usb2_pipe_methods atmegadci_root_ctrl_methods;
+struct usb2_pipe_methods atmegadci_root_intr_methods;
+
+static atmegadci_cmd_t atmegadci_setup_rx;
+static atmegadci_cmd_t atmegadci_data_rx;
+static atmegadci_cmd_t atmegadci_data_tx;
+static atmegadci_cmd_t atmegadci_data_tx_sync;
+static void atmegadci_device_done(struct usb2_xfer *, usb2_error_t);
+static void atmegadci_do_poll(struct usb2_bus *);
+static void atmegadci_root_ctrl_poll(struct atmegadci_softc *);
+static void atmegadci_standard_done(struct usb2_xfer *);
+
+static usb2_sw_transfer_func_t atmegadci_root_intr_done;
+static usb2_sw_transfer_func_t atmegadci_root_ctrl_done;
+
+/*
+ * Here is a list of what the chip supports:
+ */
+static const struct usb2_hw_ep_profile
+ atmegadci_ep_profile[2] = {
+
+ [0] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 1,
+ .support_control = 1,
+ },
+ [1] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 1,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+};
+
+static void
+atmegadci_get_hw_ep_profile(struct usb2_device *udev,
+ const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+ if (ep_addr == 0)
+ *ppf = atmegadci_ep_profile;
+ else if (ep_addr < ATMEGA_EP_MAX)
+ *ppf = atmegadci_ep_profile + 1;
+ else
+ *ppf = NULL;
+}
+
+static void
+atmegadci_clocks_on(struct atmegadci_softc *sc)
+{
+ if (sc->sc_flags.clocks_off &&
+ sc->sc_flags.port_powered) {
+
+ DPRINTFN(5, "\n");
+
+ /* turn on clocks */
+ (sc->sc_clocks_on) (&sc->sc_bus);
+
+ ATMEGA_WRITE_1(sc, ATMEGA_USBCON,
+ ATMEGA_USBCON_USBE |
+ ATMEGA_USBCON_OTGPADE |
+ ATMEGA_USBCON_VBUSTE);
+
+ sc->sc_flags.clocks_off = 0;
+
+ /* enable transceiver ? */
+ }
+}
+
+static void
+atmegadci_clocks_off(struct atmegadci_softc *sc)
+{
+ if (!sc->sc_flags.clocks_off) {
+
+ DPRINTFN(5, "\n");
+
+ /* disable Transceiver ? */
+
+ ATMEGA_WRITE_1(sc, ATMEGA_USBCON,
+ ATMEGA_USBCON_USBE |
+ ATMEGA_USBCON_OTGPADE |
+ ATMEGA_USBCON_FRZCLK |
+ ATMEGA_USBCON_VBUSTE);
+
+ /* turn clocks off */
+ (sc->sc_clocks_off) (&sc->sc_bus);
+
+ sc->sc_flags.clocks_off = 1;
+ }
+}
+
+static void
+atmegadci_pull_up(struct atmegadci_softc *sc)
+{
+ /* pullup D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up &&
+ sc->sc_flags.port_powered) {
+ sc->sc_flags.d_pulled_up = 1;
+ ATMEGA_WRITE_1(sc, ATMEGA_UDCON, 0);
+ }
+}
+
+static void
+atmegadci_pull_down(struct atmegadci_softc *sc)
+{
+ /* pulldown D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ sc->sc_flags.d_pulled_up = 0;
+ ATMEGA_WRITE_1(sc, ATMEGA_UDCON, ATMEGA_UDCON_DETACH);
+ }
+}
+
+static void
+atmegadci_wakeup_peer(struct usb2_xfer *xfer)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+ uint8_t use_polling;
+ uint8_t temp;
+
+ if (!sc->sc_flags.status_suspend) {
+ return;
+ }
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ temp = ATMEGA_READ_1(sc, ATMEGA_UDCON);
+ ATMEGA_WRITE_1(sc, ATMEGA_UDCON, temp | ATMEGA_UDCON_RMWKUP);
+
+ /* wait 8 milliseconds */
+ if (use_polling) {
+ /* polling */
+ DELAY(8000);
+ } else {
+ /* Wait for reset to complete. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+ }
+
+ /* hardware should have cleared RMWKUP bit */
+}
+
+static void
+atmegadci_set_address(struct atmegadci_softc *sc, uint8_t addr)
+{
+ DPRINTFN(5, "addr=%d\n", addr);
+
+ ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr);
+
+ addr |= ATMEGA_UDADDR_ADDEN;
+
+ ATMEGA_WRITE_1(sc, ATMEGA_UDADDR, addr);
+}
+
+static uint8_t
+atmegadci_setup_rx(struct atmegadci_td *td)
+{
+ struct atmegadci_softc *sc;
+ struct usb2_device_request req;
+ uint16_t count;
+ uint8_t temp;
+
+ /* get pointer to softc */
+ sc = ATMEGA_PC2SC(td->pc);
+
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+ /* check endpoint status */
+ temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+ DPRINTFN(5, "UEINTX=0x%02x\n", temp);
+
+ if (!(temp & ATMEGA_UEINTX_RXSTPI)) {
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(5, "stalling\n");
+ ATMEGA_WRITE_1(sc, ATMEGA_UECONX,
+ ATMEGA_UECONX_EPEN |
+ ATMEGA_UECONX_STALLRQ);
+ td->did_stall = 1;
+ }
+ goto not_complete;
+ }
+ /* get the packet byte count */
+ count =
+ (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) |
+ (ATMEGA_READ_1(sc, ATMEGA_UEBCLX));
+
+ /* mask away undefined bits */
+ count &= 0x7FF;
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(0, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(0, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ /* receive data */
+ ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX,
+ (void *)&req, sizeof(req));
+
+ /* copy data into real buffer */
+ usb2_copy_in(td->pc, 0, &req, sizeof(req));
+
+ td->offset = sizeof(req);
+ td->remainder = 0;
+
+ /* sneak peek the set address */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+
+ /* clear SETUP packet interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ~ATMEGA_UEINTX_RXSTPI);
+ return (0); /* complete */
+
+not_complete:
+ /* we only want to know if there is a SETUP packet */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, ATMEGA_UEIENX_RXSTPE);
+ return (1); /* not complete */
+}
+
+static uint8_t
+atmegadci_data_rx(struct atmegadci_td *td)
+{
+ struct atmegadci_softc *sc;
+ struct usb2_page_search buf_res;
+ uint16_t count;
+ uint8_t temp;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 3; /* don't loop forever! */
+ got_short = 0;
+
+ /* get pointer to softc */
+ sc = ATMEGA_PC2SC(td->pc);
+
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+repeat:
+ /* check if any of the FIFO banks have data */
+ /* check endpoint status */
+ temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+ DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder);
+
+ if (temp & ATMEGA_UEINTX_RXSTPI) {
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP
+ */
+ DPRINTFN(5, "faking complete\n");
+ return (0); /* complete */
+ }
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ /* check status */
+ if (!(temp & (ATMEGA_UEINTX_FIFOCON |
+ ATMEGA_UEINTX_RXOUTI))) {
+ /* no data */
+ goto not_complete;
+ }
+ /* get the packet byte count */
+ count =
+ (ATMEGA_READ_1(sc, ATMEGA_UEBCHX) << 8) |
+ (ATMEGA_READ_1(sc, ATMEGA_UEBCLX));
+
+ /* mask away undefined bits */
+ count &= 0x7FF;
+
+ /* verify the packet byte count */
+ if (count != td->max_packet_size) {
+ if (count < td->max_packet_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* receive data */
+ ATMEGA_READ_MULTI_1(sc, ATMEGA_UEDATX,
+ buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* clear OUT packet interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_RXOUTI ^ 0xFF);
+
+ /* release FIFO bank */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, ATMEGA_UEINTX_FIFOCON ^ 0xFF);
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+not_complete:
+ /* we only want to know if there is a SETUP packet or OUT packet */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEIENX,
+ ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_RXOUTE);
+ return (1); /* not complete */
+}
+
+static uint8_t
+atmegadci_data_tx(struct atmegadci_td *td)
+{
+ struct atmegadci_softc *sc;
+ struct usb2_page_search buf_res;
+ uint16_t count;
+ uint8_t to;
+ uint8_t temp;
+
+ to = 3; /* don't loop forever! */
+
+ /* get pointer to softc */
+ sc = ATMEGA_PC2SC(td->pc);
+
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+repeat:
+
+ /* check endpoint status */
+ temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+ DPRINTFN(5, "temp=0x%02x rem=%u\n", temp, td->remainder);
+
+ if (temp & ATMEGA_UEINTX_RXSTPI) {
+ /*
+ * The current transfer was aborted
+ * by the USB Host
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (!(temp & (ATMEGA_UEINTX_FIFOCON |
+ ATMEGA_UEINTX_TXINI))) {
+ /* cannot write any data */
+ goto not_complete;
+ }
+ count = td->max_packet_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ while (count > 0) {
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* transmit data */
+ ATMEGA_WRITE_MULTI_1(sc, ATMEGA_UEDATX,
+ buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* clear IN packet interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_TXINI);
+
+ /* allocate FIFO bank */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEINTX, 0xFF ^ ATMEGA_UEINTX_FIFOCON);
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+not_complete:
+ /* we only want to know if there is a SETUP packet or free IN packet */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEIENX,
+ ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE);
+ return (1); /* not complete */
+}
+
+static uint8_t
+atmegadci_data_tx_sync(struct atmegadci_td *td)
+{
+ struct atmegadci_softc *sc;
+ uint8_t temp;
+
+ /* get pointer to softc */
+ sc = ATMEGA_PC2SC(td->pc);
+
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, td->ep_no);
+
+ /* check endpoint status */
+ temp = ATMEGA_READ_1(sc, ATMEGA_UEINTX);
+
+ DPRINTFN(5, "temp=0x%02x\n", temp);
+
+ if (temp & ATMEGA_UEINTX_RXSTPI) {
+ DPRINTFN(5, "faking complete\n");
+ /* Race condition */
+ return (0); /* complete */
+ }
+ /*
+ * The control endpoint has only got one bank, so if that bank
+ * is free the packet has been transferred!
+ */
+ if (!(temp & (ATMEGA_UEINTX_FIFOCON |
+ ATMEGA_UEINTX_TXINI))) {
+ /* cannot write any data */
+ goto not_complete;
+ }
+ if (sc->sc_dv_addr != 0xFF) {
+ /* set new address */
+ atmegadci_set_address(sc, sc->sc_dv_addr);
+ }
+ return (0); /* complete */
+
+not_complete:
+ /* we only want to know if there is a SETUP packet or free IN packet */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEIENX,
+ ATMEGA_UEIENX_RXSTPE | ATMEGA_UEIENX_TXINE);
+ return (1); /* not complete */
+}
+
+static uint8_t
+atmegadci_xfer_do_fifo(struct usb2_xfer *xfer)
+{
+ struct atmegadci_td *td;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+ while (1) {
+ if ((td->func) (td)) {
+ /* operation in progress */
+ break;
+ }
+ if (((void *)td) == xfer->td_transfer_last) {
+ goto done;
+ }
+ if (td->error) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ goto done;
+ }
+ }
+ /*
+ * Fetch the next transfer descriptor and transfer
+ * some flags to the next transfer descriptor
+ */
+ td = td->obj_next;
+ xfer->td_transfer_cache = td;
+ }
+ return (1); /* not complete */
+
+done:
+ /* compute all actual lengths */
+
+ atmegadci_standard_done(xfer);
+ return (0); /* complete */
+}
+
+static void
+atmegadci_interrupt_poll(struct atmegadci_softc *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!atmegadci_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+static void
+atmegadci_vbus_interrupt(struct atmegadci_softc *sc, uint8_t is_on)
+{
+ DPRINTFN(5, "vbus = %u\n", is_on);
+
+ if (is_on) {
+ if (!sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &atmegadci_root_intr_done);
+ }
+ } else {
+ if (sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &atmegadci_root_intr_done);
+ }
+ }
+}
+
+void
+atmegadci_interrupt(struct atmegadci_softc *sc)
+{
+ uint8_t status;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* read interrupt status */
+ status = ATMEGA_READ_1(sc, ATMEGA_UDINT);
+
+ /* clear all set interrupts */
+ ATMEGA_WRITE_1(sc, ATMEGA_UDINT, ~status);
+
+ /* check for any bus state change interrupts */
+ if (status & ATMEGA_UDINT_EORSTI) {
+
+ DPRINTFN(5, "end of reset\n");
+
+ /* set correct state */
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* disable resume interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+ ATMEGA_UDINT_SUSPE |
+ ATMEGA_UDINT_EORSTE);
+
+ /* complete root HUB interrupt endpoint */
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &atmegadci_root_intr_done);
+ }
+ /*
+ * If resume and suspend is set at the same time we interpret
+ * that like RESUME. Resume is set when there is at least 3
+ * milliseconds of inactivity on the USB BUS.
+ */
+ if (status & ATMEGA_UDINT_EORSMI) {
+
+ DPRINTFN(5, "resume interrupt\n");
+
+ if (sc->sc_flags.status_suspend) {
+ /* update status bits */
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+
+ /* disable resume interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+ ATMEGA_UDINT_SUSPE |
+ ATMEGA_UDINT_EORSTE);
+
+ /* complete root HUB interrupt endpoint */
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &atmegadci_root_intr_done);
+ }
+ } else if (status & ATMEGA_UDINT_SUSPI) {
+
+ DPRINTFN(5, "suspend interrupt\n");
+
+ if (!sc->sc_flags.status_suspend) {
+ /* update status bits */
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+
+ /* disable suspend interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+ ATMEGA_UDINT_EORSMI |
+ ATMEGA_UDINT_EORSTE);
+
+ /* complete root HUB interrupt endpoint */
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &atmegadci_root_intr_done);
+ }
+ }
+ /* check VBUS */
+ status = ATMEGA_READ_1(sc, ATMEGA_USBINT);
+
+ /* clear all set interrupts */
+ ATMEGA_WRITE_1(sc, ATMEGA_USBINT, ~status);
+
+ if (status & ATMEGA_USBINT_VBUSTI) {
+ uint8_t temp;
+
+ temp = ATMEGA_READ_1(sc, ATMEGA_USBSTA);
+ atmegadci_vbus_interrupt(sc, temp & ATMEGA_USBSTA_VBUS);
+ }
+ /* check for any endpoint interrupts */
+ status = ATMEGA_READ_1(sc, ATMEGA_UEINT);
+
+ /* clear all set interrupts */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEINT, ~status);
+
+ if (status) {
+
+ DPRINTFN(5, "real endpoint interrupt 0x%02x\n", status);
+
+ atmegadci_interrupt_poll(sc);
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+atmegadci_setup_standard_chain_sub(struct atmegadci_std_temp *temp)
+{
+ struct atmegadci_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ td->error = 0;
+ td->did_stall = 0;
+ td->short_pkt = temp->short_pkt;
+ td->alt_next = temp->setup_alt_next;
+}
+
+static void
+atmegadci_setup_standard_chain(struct usb2_xfer *xfer)
+{
+ struct atmegadci_std_temp temp;
+ struct atmegadci_softc *sc;
+ struct atmegadci_td *td;
+ uint32_t x;
+ uint8_t ep_no;
+ uint8_t need_sync;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.max_frame_size = xfer->max_frame_size;
+
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ /* setup temp */
+
+ temp.td = NULL;
+ temp.td_next = xfer->td_start[0];
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.offset = 0;
+
+ sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.func = &atmegadci_setup_rx;
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.short_pkt = temp.len ? 1 : 0;
+
+ atmegadci_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &atmegadci_data_tx;
+ need_sync = 1;
+ } else {
+ temp.func = &atmegadci_data_rx;
+ need_sync = 0;
+ }
+
+ /* setup "pc" pointer */
+ temp.pc = xfer->frbuffers + x;
+ } else {
+ need_sync = 0;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ atmegadci_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+
+ /* check if we need to sync */
+ if (need_sync && xfer->flags_int.control_xfr) {
+
+ /* we need a SYNC point after TX */
+ temp.func = &atmegadci_data_tx_sync;
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ atmegadci_setup_standard_chain_sub(&temp);
+ }
+ /* check if we should append a status stage */
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &atmegadci_data_rx;
+ need_sync = 0;
+ } else {
+ temp.func = &atmegadci_data_tx;
+ need_sync = 1;
+ }
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ atmegadci_setup_standard_chain_sub(&temp);
+ if (need_sync) {
+ /* we need a SYNC point after TX */
+ temp.func = &atmegadci_data_tx_sync;
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ atmegadci_setup_standard_chain_sub(&temp);
+ }
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+}
+
+static void
+atmegadci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ atmegadci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+atmegadci_start_standard_chain(struct usb2_xfer *xfer)
+{
+ DPRINTFN(9, "\n");
+
+ /* poll one time - will turn on interrupts */
+ if (atmegadci_xfer_do_fifo(xfer)) {
+
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer,
+ &atmegadci_timeout, xfer->timeout);
+ }
+ }
+}
+
+static void
+atmegadci_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+
+ DPRINTFN(9, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ atmegadci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+
+ /* set port bit */
+ sc->sc_hub_idata[0] = 0x02; /* we only have one port */
+
+done:
+ return;
+}
+
+static usb2_error_t
+atmegadci_standard_done_sub(struct usb2_xfer *xfer)
+{
+ struct atmegadci_td *td;
+ uint32_t len;
+ uint8_t error;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ do {
+ len = td->remainder;
+
+ if (xfer->aframes != xfer->nframes) {
+ /*
+ * Verify the length and subtract
+ * the remainder from "frlengths[]":
+ */
+ if (len > xfer->frlengths[xfer->aframes]) {
+ td->error = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error) {
+ /* the transfer is finished */
+ error = 1;
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+atmegadci_standard_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = atmegadci_standard_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = atmegadci_standard_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = atmegadci_standard_done_sub(xfer);
+ }
+done:
+ atmegadci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * atmegadci_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+atmegadci_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+ uint8_t ep_no;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no);
+
+ /* disable endpoint interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, 0);
+
+ DPRINTFN(15, "disabled interrupts!\n");
+ }
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+static void
+atmegadci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer,
+ struct usb2_pipe *pipe)
+{
+ struct atmegadci_softc *sc;
+ uint8_t ep_no;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(5, "pipe=%p\n", pipe);
+
+ if (xfer) {
+ /* cancel any ongoing transfers */
+ atmegadci_device_done(xfer, USB_ERR_STALLED);
+ }
+ sc = ATMEGA_BUS2SC(udev->bus);
+ /* get endpoint number */
+ ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR);
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no);
+ /* set stall */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECONX,
+ ATMEGA_UECONX_EPEN |
+ ATMEGA_UECONX_STALLRQ);
+}
+
+static void
+atmegadci_clear_stall_sub(struct atmegadci_softc *sc, uint8_t ep_no,
+ uint8_t ep_type, uint8_t ep_dir)
+{
+ uint8_t temp;
+
+ if (ep_type == UE_CONTROL) {
+ /* clearing stall is not needed */
+ return;
+ }
+ /* select endpoint number */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, ep_no);
+
+ /* set endpoint reset */
+ ATMEGA_WRITE_1(sc, ATMEGA_UERST, ATMEGA_UERST_MASK(ep_no));
+
+ /* clear endpoint reset */
+ ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0);
+
+ /* set stall */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECONX,
+ ATMEGA_UECONX_EPEN |
+ ATMEGA_UECONX_STALLRQ);
+
+ /* reset data toggle */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECONX,
+ ATMEGA_UECONX_EPEN |
+ ATMEGA_UECONX_RSTDT);
+
+ /* clear stall */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECONX,
+ ATMEGA_UECONX_EPEN |
+ ATMEGA_UECONX_STALLRQC);
+
+ if (ep_type == UE_CONTROL) {
+ /* one bank, 64-bytes wMaxPacket */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECFG0X,
+ ATMEGA_UECFG0X_EPTYPE0);
+ ATMEGA_WRITE_1(sc, ATMEGA_UECFG1X,
+ ATMEGA_UECFG1X_ALLOC |
+ ATMEGA_UECFG1X_EPBK0 |
+ ATMEGA_UECFG1X_EPSIZE(7));
+ } else {
+ temp = 0;
+ if (ep_type == UE_BULK) {
+ temp |= ATMEGA_UECFG0X_EPTYPE2;
+ } else if (ep_type == UE_INTERRUPT) {
+ temp |= ATMEGA_UECFG0X_EPTYPE3;
+ } else {
+ temp |= ATMEGA_UECFG0X_EPTYPE1;
+ }
+ if (ep_dir & UE_DIR_IN) {
+ temp |= ATMEGA_UECFG0X_EPDIR;
+ }
+ /* two banks, 64-bytes wMaxPacket */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECFG0X, temp);
+ ATMEGA_WRITE_1(sc, ATMEGA_UECFG1X,
+ ATMEGA_UECFG1X_ALLOC |
+ ATMEGA_UECFG1X_EPBK1 |
+ ATMEGA_UECFG1X_EPSIZE(7));
+
+ temp = ATMEGA_READ_1(sc, ATMEGA_UESTA0X);
+ if (!(temp & ATMEGA_UESTA0X_CFGOK)) {
+ DPRINTFN(0, "Chip rejected configuration\n");
+ }
+ }
+}
+
+static void
+atmegadci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe)
+{
+ struct atmegadci_softc *sc;
+ struct usb2_endpoint_descriptor *ed;
+
+ DPRINTFN(5, "pipe=%p\n", pipe);
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ /* check mode */
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ /* get softc */
+ sc = ATMEGA_BUS2SC(udev->bus);
+
+ /* get endpoint descriptor */
+ ed = pipe->edesc;
+
+ /* reset endpoint */
+ atmegadci_clear_stall_sub(sc,
+ (ed->bEndpointAddress & UE_ADDR),
+ (ed->bmAttributes & UE_XFERTYPE),
+ (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+}
+
+usb2_error_t
+atmegadci_init(struct atmegadci_softc *sc)
+{
+ uint8_t n;
+
+ DPRINTF("start\n");
+
+ /* set up the bus structure */
+ sc->sc_bus.usbrev = USB_REV_1_1;
+ sc->sc_bus.methods = &atmegadci_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* enable USB PAD regulator */
+ ATMEGA_WRITE_1(sc, ATMEGA_UHWCON,
+ ATMEGA_UHWCON_UVREGE);
+
+ /* turn on clocks */
+ (sc->sc_clocks_on) (&sc->sc_bus);
+
+ /* wait a little for things to stabilise */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+
+ /* enable interrupts */
+ ATMEGA_WRITE_1(sc, ATMEGA_UDIEN,
+ ATMEGA_UDINT_SUSPE |
+ ATMEGA_UDINT_EORSTE);
+
+ /* reset all endpoints */
+ ATMEGA_WRITE_1(sc, ATMEGA_UERST,
+ (1 << ATMEGA_EP_MAX) - 1);
+
+ /* disable reset */
+ ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0);
+
+ /* disable all endpoints */
+ for (n = 1; n != ATMEGA_EP_MAX; n++) {
+
+ /* select endpoint */
+ ATMEGA_WRITE_1(sc, ATMEGA_UENUM, n);
+
+ /* disable endpoint interrupt */
+ ATMEGA_WRITE_1(sc, ATMEGA_UEIENX, 0);
+
+ /* disable endpoint */
+ ATMEGA_WRITE_1(sc, ATMEGA_UECONX, 0);
+ }
+
+ /* turn off clocks */
+
+ atmegadci_clocks_off(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+
+ atmegadci_do_poll(&sc->sc_bus);
+
+ return (0); /* success */
+}
+
+void
+atmegadci_uninit(struct atmegadci_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* turn on clocks */
+ (sc->sc_clocks_on) (&sc->sc_bus);
+
+ /* disable interrupts */
+ ATMEGA_WRITE_1(sc, ATMEGA_UDIEN, 0);
+
+ /* reset all endpoints */
+ ATMEGA_WRITE_1(sc, ATMEGA_UERST,
+ (1 << ATMEGA_EP_MAX) - 1);
+
+ /* disable reset */
+ ATMEGA_WRITE_1(sc, ATMEGA_UERST, 0);
+
+ sc->sc_flags.port_powered = 0;
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ atmegadci_pull_down(sc);
+ atmegadci_clocks_off(sc);
+
+ /* disable USB PAD regulator */
+ ATMEGA_WRITE_1(sc, ATMEGA_UHWCON, 0);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+atmegadci_suspend(struct atmegadci_softc *sc)
+{
+ return;
+}
+
+void
+atmegadci_resume(struct atmegadci_softc *sc)
+{
+ return;
+}
+
+static void
+atmegadci_do_poll(struct usb2_bus *bus)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ atmegadci_interrupt_poll(sc);
+ atmegadci_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * at91dci bulk support
+ *------------------------------------------------------------------------*/
+static void
+atmegadci_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_bulk_close(struct usb2_xfer *xfer)
+{
+ atmegadci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+atmegadci_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_bulk_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ atmegadci_setup_standard_chain(xfer);
+ atmegadci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods atmegadci_device_bulk_methods =
+{
+ .open = atmegadci_device_bulk_open,
+ .close = atmegadci_device_bulk_close,
+ .enter = atmegadci_device_bulk_enter,
+ .start = atmegadci_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci control support
+ *------------------------------------------------------------------------*/
+static void
+atmegadci_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ atmegadci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+atmegadci_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ atmegadci_setup_standard_chain(xfer);
+ atmegadci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods atmegadci_device_ctrl_methods =
+{
+ .open = atmegadci_device_ctrl_open,
+ .close = atmegadci_device_ctrl_close,
+ .enter = atmegadci_device_ctrl_enter,
+ .start = atmegadci_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci interrupt support
+ *------------------------------------------------------------------------*/
+static void
+atmegadci_device_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_intr_close(struct usb2_xfer *xfer)
+{
+ atmegadci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+atmegadci_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_intr_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ atmegadci_setup_standard_chain(xfer);
+ atmegadci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods atmegadci_device_intr_methods =
+{
+ .open = atmegadci_device_intr_open,
+ .close = atmegadci_device_intr_close,
+ .enter = atmegadci_device_intr_enter,
+ .start = atmegadci_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci full speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+atmegadci_device_isoc_fs_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_device_isoc_fs_close(struct usb2_xfer *xfer)
+{
+ atmegadci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+atmegadci_device_isoc_fs_enter(struct usb2_xfer *xfer)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+ uint32_t temp;
+ uint32_t nframes;
+
+ DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ nframes =
+ (ATMEGA_READ_1(sc, ATMEGA_UDFNUMH) << 8) |
+ (ATMEGA_READ_1(sc, ATMEGA_UDFNUML));
+
+ nframes &= ATMEGA_FRAME_MASK;
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ temp = (nframes - xfer->pipe->isoc_next) & ATMEGA_FRAME_MASK;
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (temp < xfer->nframes)) {
+ /*
+ * If there is data underflow or the pipe queue is
+ * empty we schedule the transfer a few frames ahead
+ * of the current frame position. Else two isochronous
+ * transfers might overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) & ATMEGA_FRAME_MASK;
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->pipe->isoc_next - nframes) & ATMEGA_FRAME_MASK;
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+ xfer->nframes;
+
+ /* compute frame number for next insertion */
+ xfer->pipe->isoc_next += xfer->nframes;
+
+ /* setup TDs */
+ atmegadci_setup_standard_chain(xfer);
+}
+
+static void
+atmegadci_device_isoc_fs_start(struct usb2_xfer *xfer)
+{
+ /* start TD chain */
+ atmegadci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods atmegadci_device_isoc_fs_methods =
+{
+ .open = atmegadci_device_isoc_fs_open,
+ .close = atmegadci_device_isoc_fs_close,
+ .enter = atmegadci_device_isoc_fs_enter,
+ .start = atmegadci_device_isoc_fs_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware HUB by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+
+static void
+atmegadci_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ atmegadci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/*
+ * USB descriptors for the virtual Root HUB:
+ */
+
+static const struct usb2_device_descriptor atmegadci_devd = {
+ .bLength = sizeof(struct usb2_device_descriptor),
+ .bDescriptorType = UDESC_DEVICE,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_HSHUBSTT,
+ .bMaxPacketSize = 64,
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb2_device_qualifier atmegadci_odevd = {
+ .bLength = sizeof(struct usb2_device_qualifier),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+};
+
+static const struct atmegadci_config_desc atmegadci_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(atmegadci_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_HSHUBSTT,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = (UE_DIR_IN | ATMEGA_INTR_ENDPT),
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 255,
+ },
+};
+
+static const struct usb2_hub_descriptor_min atmegadci_hubd = {
+ .bDescLength = sizeof(atmegadci_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = 1,
+ .wHubCharacteristics[0] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF,
+ .wHubCharacteristics[1] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 8,
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0}, /* port is removable */
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_VENDOR \
+ 'A', 0, 'T', 0, 'M', 0, 'E', 0, 'G', 0, 'A', 0
+
+#define STRING_PRODUCT \
+ 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \
+ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \
+ 'U', 0, 'B', 0,
+
+USB_MAKE_STRING_DESC(STRING_LANG, atmegadci_langtab);
+USB_MAKE_STRING_DESC(STRING_VENDOR, atmegadci_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, atmegadci_product);
+
+static void
+atmegadci_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+atmegadci_root_ctrl_task(struct usb2_bus *bus)
+{
+ atmegadci_root_ctrl_poll(ATMEGA_BUS2SC(bus));
+}
+
+static void
+atmegadci_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+ uint16_t value;
+ uint16_t index;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ atmegadci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0);
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ /* demultiplex the control request */
+
+ switch (std->req.bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (std->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 (std->req.bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_DESCRIPTOR:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SYNCH_FRAME:
+ goto tr_valid; /* nop */
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_SET_INTERFACE:
+ goto tr_handle_set_interface;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_GET_INTERFACE:
+ goto tr_handle_get_interface;
+ case UR_GET_STATUS:
+ goto tr_handle_get_iface_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_INTERFACE:
+ case UT_WRITE_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_READ_CLASS_INTERFACE:
+ case UT_READ_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_WRITE_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_valid;
+ case UR_SET_DESCRIPTOR:
+ case UR_SET_FEATURE:
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_handle_clear_port_feature;
+ case UR_SET_FEATURE:
+ goto tr_handle_set_port_feature;
+ case UR_CLEAR_TT_BUFFER:
+ case UR_RESET_TT:
+ case UR_STOP_TT:
+ goto tr_valid;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_GET_TT_STATE:
+ goto tr_handle_get_tt_state;
+ case UR_GET_STATUS:
+ goto tr_handle_get_port_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ case UR_GET_STATUS:
+ goto tr_handle_get_class_status;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(atmegadci_devd);
+ std->ptr = USB_ADD_BYTES(&atmegadci_devd, 0);
+ goto tr_valid;
+ case UDESC_CONFIG:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(atmegadci_confd);
+ std->ptr = USB_ADD_BYTES(&atmegadci_confd, 0);
+ goto tr_valid;
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ std->len = sizeof(atmegadci_langtab);
+ std->ptr = USB_ADD_BYTES(&atmegadci_langtab, 0);
+ goto tr_valid;
+
+ case 1: /* Vendor */
+ std->len = sizeof(atmegadci_vendor);
+ std->ptr = USB_ADD_BYTES(&atmegadci_vendor, 0);
+ goto tr_valid;
+
+ case 2: /* Product */
+ std->len = sizeof(atmegadci_product);
+ std->ptr = USB_ADD_BYTES(&atmegadci_product, 0);
+ goto tr_valid;
+ default:
+ break;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_config:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+ goto tr_valid;
+
+tr_handle_get_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (value & 0xFF00) {
+ goto tr_stalled;
+ }
+ sc->sc_rt_addr = value;
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (value >= 2) {
+ goto tr_stalled;
+ }
+ sc->sc_conf = value;
+ goto tr_valid;
+
+tr_handle_get_interface:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = 0;
+ goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, 0);
+ goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+ goto tr_valid;
+
+tr_handle_clear_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
+
+ switch (value) {
+ case UHF_PORT_SUSPEND:
+ atmegadci_wakeup_peer(xfer);
+ break;
+
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 0;
+ break;
+
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 0;
+ atmegadci_pull_down(sc);
+ atmegadci_clocks_off(sc);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_flags.change_connect = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_flags.change_suspend = 0;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_set_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(9, "UR_SET_PORT_FEATURE\n");
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 1;
+ break;
+ case UHF_PORT_SUSPEND:
+ case UHF_PORT_RESET:
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 1;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_get_port_status:
+
+ DPRINTFN(9, "UR_GET_PORT_STATUS\n");
+
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ if (sc->sc_flags.status_vbus) {
+ atmegadci_clocks_on(sc);
+ atmegadci_pull_up(sc);
+ } else {
+ atmegadci_pull_down(sc);
+ atmegadci_clocks_off(sc);
+ }
+
+ /* Select FULL-speed and Device Side Mode */
+
+ value = UPS_PORT_MODE_DEVICE;
+
+ if (sc->sc_flags.port_powered) {
+ value |= UPS_PORT_POWER;
+ }
+ if (sc->sc_flags.port_enabled) {
+ value |= UPS_PORT_ENABLED;
+ }
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ value |= UPS_CURRENT_CONNECT_STATUS;
+ }
+ if (sc->sc_flags.status_suspend) {
+ value |= UPS_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+ value = 0;
+
+ if (sc->sc_flags.change_connect) {
+ value |= UPS_C_CONNECT_STATUS;
+ }
+ if (sc->sc_flags.change_suspend) {
+ value |= UPS_C_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortChange, value);
+ std->len = sizeof(sc->sc_hub_temp.ps);
+ goto tr_valid;
+
+tr_handle_get_class_descriptor:
+ if (value & 0xFF) {
+ goto tr_stalled;
+ }
+ std->ptr = USB_ADD_BYTES(&atmegadci_hubd, 0);
+ std->len = sizeof(atmegadci_hubd);
+ goto tr_valid;
+
+tr_stalled:
+ std->err = USB_ERR_STALLED;
+tr_valid:
+done:
+ return;
+}
+
+static void
+atmegadci_root_ctrl_poll(struct atmegadci_softc *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &atmegadci_root_ctrl_done);
+}
+
+struct usb2_pipe_methods atmegadci_root_ctrl_methods =
+{
+ .open = atmegadci_root_ctrl_open,
+ .close = atmegadci_root_ctrl_close,
+ .enter = atmegadci_root_ctrl_enter,
+ .start = atmegadci_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+atmegadci_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_root_intr_close(struct usb2_xfer *xfer)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ atmegadci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+atmegadci_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_root_intr_start(struct usb2_xfer *xfer)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+}
+
+struct usb2_pipe_methods atmegadci_root_intr_methods =
+{
+ .open = atmegadci_root_intr_open,
+ .close = atmegadci_root_intr_close,
+ .enter = atmegadci_root_intr_enter,
+ .start = atmegadci_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+atmegadci_xfer_setup(struct usb2_setup_params *parm)
+{
+ const struct usb2_hw_ep_profile *pf;
+ struct atmegadci_softc *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t n;
+ uint8_t ep_no;
+
+ sc = ATMEGA_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+ parm->hc_max_packet_size = 0x500;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x500;
+
+ usb2_transfer_setup_sub(parm);
+
+ /*
+ * compute maximum number of TDs
+ */
+ if (parm->methods == &atmegadci_device_ctrl_methods) {
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC 1 */
+ + 1 /* SYNC 2 */ ;
+
+ } else if (parm->methods == &atmegadci_device_bulk_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &atmegadci_device_intr_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &atmegadci_device_isoc_fs_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else {
+
+ ntd = 0;
+ }
+
+ /*
+ * check if "usb2_transfer_setup_sub" set an error
+ */
+ if (parm->err) {
+ return;
+ }
+ /*
+ * allocate transfer descriptors
+ */
+ last_obj = NULL;
+
+ /*
+ * get profile stuff
+ */
+ if (ntd) {
+
+ ep_no = xfer->endpoint & UE_ADDR;
+ atmegadci_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+ if (pf == NULL) {
+ /* should not happen */
+ parm->err = USB_ERR_INVAL;
+ return;
+ }
+ } else {
+ ep_no = 0;
+ pf = NULL;
+ }
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ for (n = 0; n != ntd; n++) {
+
+ struct atmegadci_td *td;
+
+ if (parm->buf) {
+
+ td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ /* init TD */
+ td->max_packet_size = xfer->max_packet_size;
+ td->ep_no = ep_no;
+ if (pf->support_multi_buffer) {
+ td->support_multi_buffer = 1;
+ }
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ parm->size[0] += sizeof(*td);
+ }
+
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+atmegadci_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+atmegadci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ struct atmegadci_softc *sc = ATMEGA_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_rt_addr);
+
+ if (udev->device_index == sc->sc_rt_addr) {
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &atmegadci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | ATMEGA_INTR_ENDPT:
+ pipe->methods = &atmegadci_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ if (udev->speed != USB_SPEED_FULL) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &atmegadci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &atmegadci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ pipe->methods = &atmegadci_device_isoc_fs_methods;
+ break;
+ case UE_BULK:
+ pipe->methods = &atmegadci_device_bulk_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+struct usb2_bus_methods atmegadci_bus_methods =
+{
+ .pipe_init = &atmegadci_pipe_init,
+ .xfer_setup = &atmegadci_xfer_setup,
+ .xfer_unsetup = &atmegadci_xfer_unsetup,
+ .do_poll = &atmegadci_do_poll,
+ .get_hw_ep_profile = &atmegadci_get_hw_ep_profile,
+ .set_stall = &atmegadci_set_stall,
+ .clear_stall = &atmegadci_clear_stall,
+ .roothub_exec = &atmegadci_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/atmegadci.h b/sys/dev/usb/controller/atmegadci.h
new file mode 100644
index 0000000..90b3334
--- /dev/null
+++ b/sys/dev/usb/controller/atmegadci.h
@@ -0,0 +1,273 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2009 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 Device Port register definitions, copied from ATMEGA
+ * documentation provided by ATMEL.
+ */
+
+#ifndef _ATMEGADCI_H_
+#define _ATMEGADCI_H_
+
+#define ATMEGA_MAX_DEVICES (USB_MIN_DEVICES + 1)
+
+#ifndef ATMEGA_HAVE_BUS_SPACE
+#define ATMEGA_HAVE_BUS_SPACE 1
+#endif
+
+#define ATMEGA_UEINT 0xF4
+#define ATMEGA_UEINT_MASK(n) (1 << (n)) /* endpoint interrupt mask */
+
+#define ATMEGA_UEBCHX 0xF3 /* FIFO byte count high */
+#define ATMEGA_UEBCLX 0xF2 /* FIFO byte count low */
+#define ATMEGA_UEDATX 0xF1 /* FIFO data */
+
+#define ATMEGA_UEIENX 0xF0 /* interrupt enable register */
+#define ATMEGA_UEIENX_TXINE (1 << 0)
+#define ATMEGA_UEIENX_STALLEDE (1 << 1)
+#define ATMEGA_UEIENX_RXOUTE (1 << 2)
+#define ATMEGA_UEIENX_RXSTPE (1 << 3) /* received SETUP packet */
+#define ATMEGA_UEIENX_NAKOUTE (1 << 4)
+#define ATMEGA_UEIENX_NAKINE (1 << 6)
+#define ATMEGA_UEIENX_FLERRE (1 << 7)
+
+#define ATMEGA_UESTA1X 0xEF
+#define ATMEGA_UESTA1X_CURRBK (3 << 0) /* current bank */
+#define ATMEGA_UESTA1X_CTRLDIR (1 << 2) /* control endpoint direction */
+
+#define ATMEGA_UESTA0X 0xEE
+#define ATMEGA_UESTA0X_NBUSYBK (3 << 0)
+#define ATMEGA_UESTA0X_DTSEQ (3 << 2)
+#define ATMEGA_UESTA0X_UNDERFI (1 << 5) /* underflow */
+#define ATMEGA_UESTA0X_OVERFI (1 << 6) /* overflow */
+#define ATMEGA_UESTA0X_CFGOK (1 << 7)
+
+#define ATMEGA_UECFG1X 0xED /* endpoint config register */
+#define ATMEGA_UECFG1X_ALLOC (1 << 1)
+#define ATMEGA_UECFG1X_EPBK0 (0 << 2)
+#define ATMEGA_UECFG1X_EPBK1 (1 << 2)
+#define ATMEGA_UECFG1X_EPBK2 (2 << 2)
+#define ATMEGA_UECFG1X_EPBK3 (3 << 2)
+#define ATMEGA_UECFG1X_EPSIZE(n) ((n) << 4)
+
+#define ATMEGA_UECFG0X 0xEC
+#define ATMEGA_UECFG0X_EPDIR (1 << 0) /* endpoint direction */
+#define ATMEGA_UECFG0X_EPTYPE0 (0 << 6)
+#define ATMEGA_UECFG0X_EPTYPE1 (1 << 6)
+#define ATMEGA_UECFG0X_EPTYPE2 (2 << 6)
+#define ATMEGA_UECFG0X_EPTYPE3 (3 << 6)
+
+#define ATMEGA_UECONX 0xEB
+#define ATMEGA_UECONX_EPEN (1 << 0)
+#define ATMEGA_UECONX_RSTDT (1 << 3)
+#define ATMEGA_UECONX_STALLRQC (1 << 4) /* stall request clear */
+#define ATMEGA_UECONX_STALLRQ (1 << 5) /* stall request set */
+
+#define ATMEGA_UERST 0xEA /* endpoint reset register */
+#define ATMEGA_UERST_MASK(n) (1 << (n))
+
+#define ATMEGA_UENUM 0xE9 /* endpoint number */
+
+#define ATMEGA_UEINTX 0xE8 /* interrupt register */
+#define ATMEGA_UEINTX_TXINI (1 << 0)
+#define ATMEGA_UEINTX_STALLEDI (1 << 1)
+#define ATMEGA_UEINTX_RXOUTI (1 << 2)
+#define ATMEGA_UEINTX_RXSTPI (1 << 3) /* received setup packet */
+#define ATMEGA_UEINTX_NAKOUTI (1 << 4)
+#define ATMEGA_UEINTX_RWAL (1 << 5)
+#define ATMEGA_UEINTX_NAKINI (1 << 6)
+#define ATMEGA_UEINTX_FIFOCON (1 << 7)
+
+#define ATMEGA_UDMFN 0xE6
+#define ATMEGA_UDMFN_FNCERR (1 << 4)
+
+#define ATMEGA_UDFNUMH 0xE5 /* frame number high */
+#define ATMEGA_UDFNUMH_MASK 7
+
+#define ATMEGA_UDFNUML 0xE4 /* frame number low */
+#define ATMEGA_UDFNUML_MASK 0xFF
+
+#define ATMEGA_FRAME_MASK 0x7FF
+
+#define ATMEGA_UDADDR 0xE3 /* USB address */
+#define ATMEGA_UDADDR_MASK 0x7F
+#define ATMEGA_UDADDR_ADDEN (1 << 7)
+
+#define ATMEGA_UDIEN 0xE2 /* USB device interrupt enable */
+#define ATMEGA_UDINT_SUSPE (1 << 0)
+#define ATMEGA_UDINT_MSOFE (1 << 1)
+#define ATMEGA_UDINT_SOFE (1 << 2)
+#define ATMEGA_UDINT_EORSTE (1 << 3)
+#define ATMEGA_UDINT_WAKEUPE (1 << 4)
+#define ATMEGA_UDINT_EORSME (1 << 5)
+#define ATMEGA_UDINT_UPRSME (1 << 6)
+
+#define ATMEGA_UDINT 0xE1 /* USB device interrupt status */
+#define ATMEGA_UDINT_SUSPI (1 << 0)
+#define ATMEGA_UDINT_MSOFI (1 << 1)
+#define ATMEGA_UDINT_SOFI (1 << 2)
+#define ATMEGA_UDINT_EORSTI (1 << 3)
+#define ATMEGA_UDINT_WAKEUPI (1 << 4)
+#define ATMEGA_UDINT_EORSMI (1 << 5)
+#define ATMEGA_UDINT_UPRSMI (1 << 6)
+
+#define ATMEGA_UDCON 0xE0 /* USB device connection register */
+#define ATMEGA_UDCON_DETACH (1 << 0)
+#define ATMEGA_UDCON_RMWKUP (1 << 1)
+#define ATMEGA_UDCON_LSM (1 << 2)
+#define ATMEGA_UDCON_RSTCPU (1 << 3)
+
+#define ATMEGA_USBINT 0xDA
+#define ATMEGA_USBINT_VBUSTI (1 << 0) /* USB VBUS interrupt */
+
+#define ATMEGA_USBSTA 0xD9
+#define ATMEGA_USBSTA_VBUS (1 << 0)
+#define ATMEGA_USBSTA_ID (1 << 1)
+
+#define ATMEGA_USBCON 0xD8
+#define ATMEGA_USBCON_VBUSTE (1 << 0)
+#define ATMEGA_USBCON_OTGPADE (1 << 4)
+#define ATMEGA_USBCON_FRZCLK (1 << 5)
+#define ATMEGA_USBCON_USBE (1 << 7)
+
+#define ATMEGA_UHWCON 0xD7
+#define ATMEGA_UHWCON_UVREGE (1 << 0)
+
+#define ATMEGA_READ_1(sc, reg) \
+ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
+
+#define ATMEGA_WRITE_1(sc, reg, data) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+#define ATMEGA_WRITE_MULTI_1(sc, reg, ptr, len) \
+ bus_space_write_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, ptr, len)
+
+#define ATMEGA_READ_MULTI_1(sc, reg, ptr, len) \
+ bus_space_read_multi_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, ptr, len)
+
+/*
+ * Maximum number of endpoints supported:
+ */
+#define ATMEGA_EP_MAX 7
+
+struct atmegadci_td;
+
+typedef uint8_t (atmegadci_cmd_t)(struct atmegadci_td *td);
+typedef void (atmegadci_clocks_t)(struct usb2_bus *);
+
+struct atmegadci_td {
+ struct atmegadci_td *obj_next;
+ atmegadci_cmd_t *func;
+ struct usb2_page_cache *pc;
+ uint32_t offset;
+ uint32_t remainder;
+ uint16_t max_packet_size;
+ uint8_t error:1;
+ uint8_t alt_next:1;
+ uint8_t short_pkt:1;
+ uint8_t support_multi_buffer:1;
+ uint8_t did_stall:1;
+ uint8_t ep_no:3;
+};
+
+struct atmegadci_std_temp {
+ atmegadci_cmd_t *func;
+ struct usb2_page_cache *pc;
+ struct atmegadci_td *td;
+ struct atmegadci_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t max_frame_size;
+ uint8_t short_pkt;
+ /*
+ * short_pkt = 0: transfer should be short terminated
+ * short_pkt = 1: transfer should not be short terminated
+ */
+ uint8_t setup_alt_next;
+};
+
+struct atmegadci_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union atmegadci_hub_temp {
+ uWord wValue;
+ struct usb2_port_status ps;
+};
+
+struct atmegadci_flags {
+ uint8_t change_connect:1;
+ uint8_t change_suspend:1;
+ uint8_t status_suspend:1; /* set if suspended */
+ uint8_t status_vbus:1; /* set if present */
+ uint8_t status_bus_reset:1; /* set if reset complete */
+ uint8_t remote_wakeup:1;
+ uint8_t self_powered:1;
+ uint8_t clocks_off:1;
+ uint8_t port_powered:1;
+ uint8_t port_enabled:1;
+ uint8_t d_pulled_up:1;
+};
+
+struct atmegadci_softc {
+ struct usb2_bus sc_bus;
+ union atmegadci_hub_temp sc_hub_temp;
+ LIST_HEAD(, usb2_xfer) sc_interrupt_list_head;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+
+ /* must be set by by the bus interface layer */
+ atmegadci_clocks_t *sc_clocks_on;
+ atmegadci_clocks_t *sc_clocks_off;
+
+ struct usb2_device *sc_devices[ATMEGA_MAX_DEVICES];
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+#if (ATMEGA_HAVE_BUS_SPACE != 0)
+ struct resource *sc_io_res;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+#endif
+ uint8_t sc_rt_addr; /* root hub address */
+ uint8_t sc_dv_addr; /* device address */
+ uint8_t sc_conf; /* root hub config */
+
+ uint8_t sc_hub_idata[1];
+
+ struct atmegadci_flags sc_flags;
+};
+
+/* prototypes */
+
+usb2_error_t atmegadci_init(struct atmegadci_softc *sc);
+void atmegadci_uninit(struct atmegadci_softc *sc);
+void atmegadci_suspend(struct atmegadci_softc *sc);
+void atmegadci_resume(struct atmegadci_softc *sc);
+void atmegadci_interrupt(struct atmegadci_softc *sc);
+
+#endif /* _ATMEGADCI_H_ */
diff --git a/sys/dev/usb/controller/atmegadci_atmelarm.c b/sys/dev/usb/controller/atmegadci_atmelarm.c
new file mode 100644
index 0000000..e63f5cc
--- /dev/null
+++ b/sys/dev/usb/controller/atmegadci_atmelarm.c
@@ -0,0 +1,27 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2009 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.
+ */
diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c
new file mode 100644
index 0000000..5802268
--- /dev/null
+++ b/sys/dev/usb/controller/ehci.c
@@ -0,0 +1,3965 @@
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2004 The NetBSD Foundation, Inc. All rights reserved.
+ * Copyright (c) 2004 Lennart Augustsson. All rights reserved.
+ * Copyright (c) 2004 Charles M. Hannum. 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 Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 0.96 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r096.pdf
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ *
+ */
+
+/*
+ * TODO:
+ * 1) command failures are not recovered correctly
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR ehcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+
+#define EHCI_BUS2SC(bus) ((ehci_softc_t *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((ehci_softc_t *)0)->sc_bus))))
+
+#if USB_DEBUG
+static int ehcidebug = 0;
+static int ehcinohighspeed = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci");
+SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, debug, CTLFLAG_RW,
+ &ehcidebug, 0, "Debug level");
+SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, no_hs, CTLFLAG_RW,
+ &ehcinohighspeed, 0, "Disable High Speed USB");
+
+static void ehci_dump_regs(ehci_softc_t *sc);
+static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh);
+
+#endif
+
+#define EHCI_INTR_ENDPT 1
+
+extern struct usb2_bus_methods ehci_bus_methods;
+extern struct usb2_pipe_methods ehci_device_bulk_methods;
+extern struct usb2_pipe_methods ehci_device_ctrl_methods;
+extern struct usb2_pipe_methods ehci_device_intr_methods;
+extern struct usb2_pipe_methods ehci_device_isoc_fs_methods;
+extern struct usb2_pipe_methods ehci_device_isoc_hs_methods;
+extern struct usb2_pipe_methods ehci_root_ctrl_methods;
+extern struct usb2_pipe_methods ehci_root_intr_methods;
+
+static void ehci_do_poll(struct usb2_bus *bus);
+static void ehci_root_ctrl_poll(ehci_softc_t *sc);
+static void ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error);
+static uint8_t ehci_check_transfer(struct usb2_xfer *xfer);
+static void ehci_timeout(void *arg);
+
+static usb2_sw_transfer_func_t ehci_root_intr_done;
+static usb2_sw_transfer_func_t ehci_root_ctrl_done;
+
+struct ehci_std_temp {
+ ehci_softc_t *sc;
+ struct usb2_page_cache *pc;
+ ehci_qtd_t *td;
+ ehci_qtd_t *td_next;
+ uint32_t average;
+ uint32_t qtd_status;
+ uint32_t len;
+ uint16_t max_frame_size;
+ uint8_t shortpkt;
+ uint8_t auto_data_toggle;
+ uint8_t setup_alt_next;
+ uint8_t short_frames_ok;
+};
+
+/*
+ * Byte-order conversion functions.
+ */
+static uint32_t
+htoehci32(ehci_softc_t *sc, const uint32_t v)
+{
+ return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ?
+ htobe32(v) : htole32(v));
+}
+
+static uint32_t
+ehci32toh(ehci_softc_t *sc, const uint32_t v)
+{
+ return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ?
+ be32toh(v) : le32toh(v));
+}
+
+void
+ehci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(bus);
+ uint32_t i;
+
+ cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg,
+ sizeof(uint32_t) * EHCI_FRAMELIST_COUNT, EHCI_FRAMELIST_ALIGN);
+
+ cb(bus, &sc->sc_hw.async_start_pc, &sc->sc_hw.async_start_pg,
+ sizeof(ehci_qh_t), EHCI_QH_ALIGN);
+
+ for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
+ cb(bus, sc->sc_hw.intr_start_pc + i,
+ sc->sc_hw.intr_start_pg + i,
+ sizeof(ehci_qh_t), EHCI_QH_ALIGN);
+ }
+
+ for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
+ cb(bus, sc->sc_hw.isoc_hs_start_pc + i,
+ sc->sc_hw.isoc_hs_start_pg + i,
+ sizeof(ehci_itd_t), EHCI_ITD_ALIGN);
+ }
+
+ for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
+ cb(bus, sc->sc_hw.isoc_fs_start_pc + i,
+ sc->sc_hw.isoc_fs_start_pg + i,
+ sizeof(ehci_sitd_t), EHCI_SITD_ALIGN);
+ }
+}
+
+static usb2_error_t
+ehci_hc_reset(ehci_softc_t *sc)
+{
+ uint32_t hcr;
+ uint32_t n;
+
+ EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */
+
+ for (n = 0; n != 100; n++) {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+ hcr = EOREAD4(sc, EHCI_USBSTS);
+ if (hcr & EHCI_STS_HCH) {
+ hcr = 0;
+ break;
+ }
+ }
+
+ /*
+ * Fall through and try reset anyway even though
+ * Table 2-9 in the EHCI spec says this will result
+ * in undefined behavior.
+ */
+
+ EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
+ for (n = 0; n != 100; n++) {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+ hcr = EOREAD4(sc, EHCI_USBCMD);
+ if (!(hcr & EHCI_CMD_HCRESET)) {
+ if (sc->sc_flags & EHCI_SCFLG_SETMODE)
+ EOWRITE4(sc, 0x68, 0x3);
+ hcr = 0;
+ break;
+ }
+ }
+
+ if (hcr) {
+ return (USB_ERR_IOERROR);
+ }
+ return (0);
+}
+
+usb2_error_t
+ehci_init(ehci_softc_t *sc)
+{
+ struct usb2_page_search buf_res;
+ uint32_t version;
+ uint32_t sparams;
+ uint32_t cparams;
+ uint32_t hcr;
+ uint16_t i;
+ uint16_t x;
+ uint16_t y;
+ uint16_t bit;
+ usb2_error_t err = 0;
+
+ DPRINTF("start\n");
+
+ usb2_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0);
+
+#if USB_DEBUG
+ if (ehcidebug > 2) {
+ ehci_dump_regs(sc);
+ }
+#endif
+
+ sc->sc_offs = EREAD1(sc, EHCI_CAPLENGTH);
+
+ version = EREAD2(sc, EHCI_HCIVERSION);
+ device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n",
+ version >> 8, version & 0xff);
+
+ sparams = EREAD4(sc, EHCI_HCSPARAMS);
+ DPRINTF("sparams=0x%x\n", sparams);
+
+ sc->sc_noport = EHCI_HCS_N_PORTS(sparams);
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+ DPRINTF("cparams=0x%x\n", cparams);
+
+ if (EHCI_HCC_64BIT(cparams)) {
+ DPRINTF("HCC uses 64-bit structures\n");
+
+ /* MUST clear segment register if 64 bit capable */
+ EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+ }
+ sc->sc_bus.usbrev = USB_REV_2_0;
+
+ /* Reset the controller */
+ DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev));
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ err = ehci_hc_reset(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+ if (err) {
+ device_printf(sc->sc_bus.bdev, "reset timeout\n");
+ return (err);
+ }
+ /*
+ * use current frame-list-size selection 0: 1024*4 bytes 1: 512*4
+ * bytes 2: 256*4 bytes 3: unknown
+ */
+ if (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD)) == 3) {
+ device_printf(sc->sc_bus.bdev, "invalid frame-list-size\n");
+ return (USB_ERR_IOERROR);
+ }
+ /* set up the bus struct */
+ sc->sc_bus.methods = &ehci_bus_methods;
+
+ sc->sc_eintrs = EHCI_NORMAL_INTRS;
+
+ for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
+ ehci_qh_t *qh;
+
+ usb2_get_page(sc->sc_hw.intr_start_pc + i, 0, &buf_res);
+
+ qh = buf_res.buffer;
+
+ /* initialize page cache pointer */
+
+ qh->page_cache = sc->sc_hw.intr_start_pc + i;
+
+ /* store a pointer to queue head */
+
+ sc->sc_intr_p_last[i] = qh;
+
+ qh->qh_self =
+ htoehci32(sc, buf_res.physaddr) |
+ htoehci32(sc, EHCI_LINK_QH);
+
+ qh->qh_endp =
+ htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH));
+ qh->qh_endphub =
+ htoehci32(sc, EHCI_QH_SET_MULT(1));
+ qh->qh_curqtd = 0;
+
+ qh->qh_qtd.qtd_next =
+ htoehci32(sc, EHCI_LINK_TERMINATE);
+ qh->qh_qtd.qtd_altnext =
+ htoehci32(sc, EHCI_LINK_TERMINATE);
+ qh->qh_qtd.qtd_status =
+ htoehci32(sc, EHCI_QTD_HALTED);
+ }
+
+ /*
+ * the QHs are arranged to give poll intervals that are
+ * powers of 2 times 1ms
+ */
+ bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2;
+ while (bit) {
+ x = bit;
+ while (x & bit) {
+ ehci_qh_t *qh_x;
+ ehci_qh_t *qh_y;
+
+ y = (x ^ bit) | (bit / 2);
+
+ qh_x = sc->sc_intr_p_last[x];
+ qh_y = sc->sc_intr_p_last[y];
+
+ /*
+ * the next QH has half the poll interval
+ */
+ qh_x->qh_link = qh_y->qh_self;
+
+ x++;
+ }
+ bit >>= 1;
+ }
+
+ if (1) {
+ ehci_qh_t *qh;
+
+ qh = sc->sc_intr_p_last[0];
+
+ /* the last (1ms) QH terminates */
+ qh->qh_link = htoehci32(sc, EHCI_LINK_TERMINATE);
+ }
+ for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
+ ehci_sitd_t *sitd;
+ ehci_itd_t *itd;
+
+ usb2_get_page(sc->sc_hw.isoc_fs_start_pc + i, 0, &buf_res);
+
+ sitd = buf_res.buffer;
+
+ /* initialize page cache pointer */
+
+ sitd->page_cache = sc->sc_hw.isoc_fs_start_pc + i;
+
+ /* store a pointer to the transfer descriptor */
+
+ sc->sc_isoc_fs_p_last[i] = sitd;
+
+ /* initialize full speed isochronous */
+
+ sitd->sitd_self =
+ htoehci32(sc, buf_res.physaddr) |
+ htoehci32(sc, EHCI_LINK_SITD);
+
+ sitd->sitd_back =
+ htoehci32(sc, EHCI_LINK_TERMINATE);
+
+ sitd->sitd_next =
+ sc->sc_intr_p_last[i | (EHCI_VIRTUAL_FRAMELIST_COUNT / 2)]->qh_self;
+
+
+ usb2_get_page(sc->sc_hw.isoc_hs_start_pc + i, 0, &buf_res);
+
+ itd = buf_res.buffer;
+
+ /* initialize page cache pointer */
+
+ itd->page_cache = sc->sc_hw.isoc_hs_start_pc + i;
+
+ /* store a pointer to the transfer descriptor */
+
+ sc->sc_isoc_hs_p_last[i] = itd;
+
+ /* initialize high speed isochronous */
+
+ itd->itd_self =
+ htoehci32(sc, buf_res.physaddr) |
+ htoehci32(sc, EHCI_LINK_ITD);
+
+ itd->itd_next =
+ sitd->sitd_self;
+ }
+
+ usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
+
+ if (1) {
+ uint32_t *pframes;
+
+ pframes = buf_res.buffer;
+
+ /*
+ * execution order:
+ * pframes -> high speed isochronous ->
+ * full speed isochronous -> interrupt QH's
+ */
+ for (i = 0; i < EHCI_FRAMELIST_COUNT; i++) {
+ pframes[i] = sc->sc_isoc_hs_p_last
+ [i & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1)]->itd_self;
+ }
+ }
+ /* setup sync list pointer */
+ EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr);
+
+ usb2_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res);
+
+ if (1) {
+
+ ehci_qh_t *qh;
+
+ qh = buf_res.buffer;
+
+ /* initialize page cache pointer */
+
+ qh->page_cache = &sc->sc_hw.async_start_pc;
+
+ /* store a pointer to the queue head */
+
+ sc->sc_async_p_last = qh;
+
+ /* init dummy QH that starts the async list */
+
+ qh->qh_self =
+ htoehci32(sc, buf_res.physaddr) |
+ htoehci32(sc, EHCI_LINK_QH);
+
+ /* fill the QH */
+ qh->qh_endp =
+ htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL);
+ qh->qh_endphub = htoehci32(sc, EHCI_QH_SET_MULT(1));
+ qh->qh_link = qh->qh_self;
+ qh->qh_curqtd = 0;
+
+ /* fill the overlay qTD */
+ qh->qh_qtd.qtd_next = htoehci32(sc, EHCI_LINK_TERMINATE);
+ qh->qh_qtd.qtd_altnext = htoehci32(sc, EHCI_LINK_TERMINATE);
+ qh->qh_qtd.qtd_status = htoehci32(sc, EHCI_QTD_HALTED);
+ }
+ /* flush all cache into memory */
+
+ usb2_bus_mem_flush_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+#if USB_DEBUG
+ if (ehcidebug) {
+ ehci_dump_sqh(sc, sc->sc_async_p_last);
+ }
+#endif
+
+ /* setup async list pointer */
+ EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH);
+
+
+ /* enable interrupts */
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ /* turn on controller */
+ EOWRITE4(sc, EHCI_USBCMD,
+ EHCI_CMD_ITC_1 | /* 1 microframes interrupt delay */
+ (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
+ EHCI_CMD_ASE |
+ EHCI_CMD_PSE |
+ EHCI_CMD_RS);
+
+ /* Take over port ownership */
+ EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF);
+
+ for (i = 0; i < 100; i++) {
+ usb2_pause_mtx(NULL, hz / 1000);
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (!hcr) {
+ break;
+ }
+ }
+ if (hcr) {
+ device_printf(sc->sc_bus.bdev, "run timeout\n");
+ return (USB_ERR_IOERROR);
+ }
+
+ if (!err) {
+ /* catch any lost interrupts */
+ ehci_do_poll(&sc->sc_bus);
+ }
+ return (err);
+}
+
+/*
+ * shut down the controller when the system is going down
+ */
+void
+ehci_detach(ehci_softc_t *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ usb2_callout_stop(&sc->sc_tmo_pcd);
+
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ if (ehci_hc_reset(sc)) {
+ DPRINTF("reset failed!\n");
+ }
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* XXX let stray task complete */
+ usb2_pause_mtx(NULL, hz / 20);
+
+ usb2_callout_drain(&sc->sc_tmo_pcd);
+}
+
+void
+ehci_suspend(ehci_softc_t *sc)
+{
+ uint32_t cmd;
+ uint32_t hcr;
+ uint8_t i;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ for (i = 1; i <= sc->sc_noport; i++) {
+ cmd = EOREAD4(sc, EHCI_PORTSC(i));
+ if (((cmd & EHCI_PS_PO) == 0) &&
+ ((cmd & EHCI_PS_PE) == EHCI_PS_PE)) {
+ EOWRITE4(sc, EHCI_PORTSC(i),
+ cmd | EHCI_PS_SUSP);
+ }
+ }
+
+ sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD);
+
+ cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
+ EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+ for (i = 0; i < 100; i++) {
+ hcr = EOREAD4(sc, EHCI_USBSTS) &
+ (EHCI_STS_ASS | EHCI_STS_PSS);
+
+ if (hcr == 0) {
+ break;
+ }
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+ }
+
+ if (hcr != 0) {
+ device_printf(sc->sc_bus.bdev, "reset timeout\n");
+ }
+ cmd &= ~EHCI_CMD_RS;
+ EOWRITE4(sc, EHCI_USBCMD, cmd);
+
+ for (i = 0; i < 100; i++) {
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (hcr == EHCI_STS_HCH) {
+ break;
+ }
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+ }
+
+ if (hcr != EHCI_STS_HCH) {
+ device_printf(sc->sc_bus.bdev,
+ "config timeout\n");
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+ehci_resume(ehci_softc_t *sc)
+{
+ struct usb2_page_search buf_res;
+ uint32_t cmd;
+ uint32_t hcr;
+ uint8_t i;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* restore things in case the bios doesn't */
+ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+
+ usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
+ EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr);
+
+ usb2_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res);
+ EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH);
+
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ hcr = 0;
+ for (i = 1; i <= sc->sc_noport; i++) {
+ cmd = EOREAD4(sc, EHCI_PORTSC(i));
+ if (((cmd & EHCI_PS_PO) == 0) &&
+ ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) {
+ EOWRITE4(sc, EHCI_PORTSC(i),
+ cmd | EHCI_PS_FPR);
+ hcr = 1;
+ }
+ }
+
+ if (hcr) {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_RESUME_WAIT));
+
+ for (i = 1; i <= sc->sc_noport; i++) {
+ cmd = EOREAD4(sc, EHCI_PORTSC(i));
+ if (((cmd & EHCI_PS_PO) == 0) &&
+ ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) {
+ EOWRITE4(sc, EHCI_PORTSC(i),
+ cmd & ~EHCI_PS_FPR);
+ }
+ }
+ }
+ EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd);
+
+ for (i = 0; i < 100; i++) {
+ hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
+ if (hcr != EHCI_STS_HCH) {
+ break;
+ }
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+ }
+ if (hcr == EHCI_STS_HCH) {
+ device_printf(sc->sc_bus.bdev, "config timeout\n");
+ }
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ usb2_pause_mtx(NULL,
+ USB_MS_TO_TICKS(USB_RESUME_WAIT));
+
+ /* catch any lost interrupts */
+ ehci_do_poll(&sc->sc_bus);
+}
+
+void
+ehci_shutdown(ehci_softc_t *sc)
+{
+ DPRINTF("stopping the HC\n");
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ if (ehci_hc_reset(sc)) {
+ DPRINTF("reset failed!\n");
+ }
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+#if USB_DEBUG
+static void
+ehci_dump_regs(ehci_softc_t *sc)
+{
+ uint32_t i;
+
+ i = EOREAD4(sc, EHCI_USBCMD);
+ printf("cmd=0x%08x\n", i);
+
+ if (i & EHCI_CMD_ITC_1)
+ printf(" EHCI_CMD_ITC_1\n");
+ if (i & EHCI_CMD_ITC_2)
+ printf(" EHCI_CMD_ITC_2\n");
+ if (i & EHCI_CMD_ITC_4)
+ printf(" EHCI_CMD_ITC_4\n");
+ if (i & EHCI_CMD_ITC_8)
+ printf(" EHCI_CMD_ITC_8\n");
+ if (i & EHCI_CMD_ITC_16)
+ printf(" EHCI_CMD_ITC_16\n");
+ if (i & EHCI_CMD_ITC_32)
+ printf(" EHCI_CMD_ITC_32\n");
+ if (i & EHCI_CMD_ITC_64)
+ printf(" EHCI_CMD_ITC_64\n");
+ if (i & EHCI_CMD_ASPME)
+ printf(" EHCI_CMD_ASPME\n");
+ if (i & EHCI_CMD_ASPMC)
+ printf(" EHCI_CMD_ASPMC\n");
+ if (i & EHCI_CMD_LHCR)
+ printf(" EHCI_CMD_LHCR\n");
+ if (i & EHCI_CMD_IAAD)
+ printf(" EHCI_CMD_IAAD\n");
+ if (i & EHCI_CMD_ASE)
+ printf(" EHCI_CMD_ASE\n");
+ if (i & EHCI_CMD_PSE)
+ printf(" EHCI_CMD_PSE\n");
+ if (i & EHCI_CMD_FLS_M)
+ printf(" EHCI_CMD_FLS_M\n");
+ if (i & EHCI_CMD_HCRESET)
+ printf(" EHCI_CMD_HCRESET\n");
+ if (i & EHCI_CMD_RS)
+ printf(" EHCI_CMD_RS\n");
+
+ i = EOREAD4(sc, EHCI_USBSTS);
+
+ printf("sts=0x%08x\n", i);
+
+ if (i & EHCI_STS_ASS)
+ printf(" EHCI_STS_ASS\n");
+ if (i & EHCI_STS_PSS)
+ printf(" EHCI_STS_PSS\n");
+ if (i & EHCI_STS_REC)
+ printf(" EHCI_STS_REC\n");
+ if (i & EHCI_STS_HCH)
+ printf(" EHCI_STS_HCH\n");
+ if (i & EHCI_STS_IAA)
+ printf(" EHCI_STS_IAA\n");
+ if (i & EHCI_STS_HSE)
+ printf(" EHCI_STS_HSE\n");
+ if (i & EHCI_STS_FLR)
+ printf(" EHCI_STS_FLR\n");
+ if (i & EHCI_STS_PCD)
+ printf(" EHCI_STS_PCD\n");
+ if (i & EHCI_STS_ERRINT)
+ printf(" EHCI_STS_ERRINT\n");
+ if (i & EHCI_STS_INT)
+ printf(" EHCI_STS_INT\n");
+
+ printf("ien=0x%08x\n",
+ EOREAD4(sc, EHCI_USBINTR));
+ printf("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n",
+ EOREAD4(sc, EHCI_FRINDEX),
+ EOREAD4(sc, EHCI_CTRLDSSEGMENT),
+ EOREAD4(sc, EHCI_PERIODICLISTBASE),
+ EOREAD4(sc, EHCI_ASYNCLISTADDR));
+ for (i = 1; i <= sc->sc_noport; i++) {
+ printf("port %d status=0x%08x\n", i,
+ EOREAD4(sc, EHCI_PORTSC(i)));
+ }
+}
+
+static void
+ehci_dump_link(ehci_softc_t *sc, uint32_t link, int type)
+{
+ link = ehci32toh(sc, link);
+ printf("0x%08x", link);
+ if (link & EHCI_LINK_TERMINATE)
+ printf("<T>");
+ else {
+ printf("<");
+ if (type) {
+ switch (EHCI_LINK_TYPE(link)) {
+ case EHCI_LINK_ITD:
+ printf("ITD");
+ break;
+ case EHCI_LINK_QH:
+ printf("QH");
+ break;
+ case EHCI_LINK_SITD:
+ printf("SITD");
+ break;
+ case EHCI_LINK_FSTN:
+ printf("FSTN");
+ break;
+ }
+ }
+ printf(">");
+ }
+}
+
+static void
+ehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd)
+{
+ uint32_t s;
+
+ printf(" next=");
+ ehci_dump_link(sc, qtd->qtd_next, 0);
+ printf(" altnext=");
+ ehci_dump_link(sc, qtd->qtd_altnext, 0);
+ printf("\n");
+ s = ehci32toh(sc, qtd->qtd_status);
+ printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n",
+ s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s),
+ EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s));
+ printf(" cerr=%d pid=%d stat=%s%s%s%s%s%s%s%s\n",
+ EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s),
+ (s & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE",
+ (s & EHCI_QTD_HALTED) ? "-HALTED" : "",
+ (s & EHCI_QTD_BUFERR) ? "-BUFERR" : "",
+ (s & EHCI_QTD_BABBLE) ? "-BABBLE" : "",
+ (s & EHCI_QTD_XACTERR) ? "-XACTERR" : "",
+ (s & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "",
+ (s & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "",
+ (s & EHCI_QTD_PINGSTATE) ? "-PING" : "");
+
+ for (s = 0; s < 5; s++) {
+ printf(" buffer[%d]=0x%08x\n", s,
+ ehci32toh(sc, qtd->qtd_buffer[s]));
+ }
+ for (s = 0; s < 5; s++) {
+ printf(" buffer_hi[%d]=0x%08x\n", s,
+ ehci32toh(sc, qtd->qtd_buffer_hi[s]));
+ }
+}
+
+static uint8_t
+ehci_dump_sqtd(ehci_softc_t *sc, ehci_qtd_t *sqtd)
+{
+ uint8_t temp;
+
+ usb2_pc_cpu_invalidate(sqtd->page_cache);
+ printf("QTD(%p) at 0x%08x:\n", sqtd, ehci32toh(sc, sqtd->qtd_self));
+ ehci_dump_qtd(sc, sqtd);
+ temp = (sqtd->qtd_next & htoehci32(sc, EHCI_LINK_TERMINATE)) ? 1 : 0;
+ return (temp);
+}
+
+static void
+ehci_dump_sqtds(ehci_softc_t *sc, ehci_qtd_t *sqtd)
+{
+ uint16_t i;
+ uint8_t stop;
+
+ stop = 0;
+ for (i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) {
+ stop = ehci_dump_sqtd(sc, sqtd);
+ }
+ if (sqtd) {
+ printf("dump aborted, too many TDs\n");
+ }
+}
+
+static void
+ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *qh)
+{
+ uint32_t endp;
+ uint32_t endphub;
+
+ usb2_pc_cpu_invalidate(qh->page_cache);
+ printf("QH(%p) at 0x%08x:\n", qh, ehci32toh(sc, qh->qh_self) & ~0x1F);
+ printf(" link=");
+ ehci_dump_link(sc, qh->qh_link, 1);
+ printf("\n");
+ endp = ehci32toh(sc, qh->qh_endp);
+ printf(" endp=0x%08x\n", endp);
+ printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n",
+ EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp),
+ EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp),
+ EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp));
+ printf(" mpl=0x%x ctl=%d nrl=%d\n",
+ EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp),
+ EHCI_QH_GET_NRL(endp));
+ endphub = ehci32toh(sc, qh->qh_endphub);
+ printf(" endphub=0x%08x\n", endphub);
+ printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n",
+ EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub),
+ EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub),
+ EHCI_QH_GET_MULT(endphub));
+ printf(" curqtd=");
+ ehci_dump_link(sc, qh->qh_curqtd, 0);
+ printf("\n");
+ printf("Overlay qTD:\n");
+ ehci_dump_qtd(sc, (void *)&qh->qh_qtd);
+}
+
+static void
+ehci_dump_sitd(ehci_softc_t *sc, ehci_sitd_t *sitd)
+{
+ usb2_pc_cpu_invalidate(sitd->page_cache);
+ printf("SITD(%p) at 0x%08x\n", sitd, ehci32toh(sc, sitd->sitd_self) & ~0x1F);
+ printf(" next=0x%08x\n", ehci32toh(sc, sitd->sitd_next));
+ printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n",
+ ehci32toh(sc, sitd->sitd_portaddr),
+ (sitd->sitd_portaddr & htoehci32(sc, EHCI_SITD_SET_DIR_IN))
+ ? "in" : "out",
+ EHCI_SITD_GET_ADDR(ehci32toh(sc, sitd->sitd_portaddr)),
+ EHCI_SITD_GET_ENDPT(ehci32toh(sc, sitd->sitd_portaddr)),
+ EHCI_SITD_GET_PORT(ehci32toh(sc, sitd->sitd_portaddr)),
+ EHCI_SITD_GET_HUBA(ehci32toh(sc, sitd->sitd_portaddr)));
+ printf(" mask=0x%08x\n", ehci32toh(sc, sitd->sitd_mask));
+ printf(" status=0x%08x <%s> len=0x%x\n", ehci32toh(sc, sitd->sitd_status),
+ (sitd->sitd_status & htoehci32(sc, EHCI_SITD_ACTIVE)) ? "ACTIVE" : "",
+ EHCI_SITD_GET_LEN(ehci32toh(sc, sitd->sitd_status)));
+ printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n",
+ ehci32toh(sc, sitd->sitd_back),
+ ehci32toh(sc, sitd->sitd_bp[0]),
+ ehci32toh(sc, sitd->sitd_bp[1]),
+ ehci32toh(sc, sitd->sitd_bp_hi[0]),
+ ehci32toh(sc, sitd->sitd_bp_hi[1]));
+}
+
+static void
+ehci_dump_itd(ehci_softc_t *sc, ehci_itd_t *itd)
+{
+ usb2_pc_cpu_invalidate(itd->page_cache);
+ printf("ITD(%p) at 0x%08x\n", itd, ehci32toh(sc, itd->itd_self) & ~0x1F);
+ printf(" next=0x%08x\n", ehci32toh(sc, itd->itd_next));
+ printf(" status[0]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[0]),
+ (itd->itd_status[0] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[1]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[1]),
+ (itd->itd_status[1] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[2]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[2]),
+ (itd->itd_status[2] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[3]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[3]),
+ (itd->itd_status[3] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[4]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[4]),
+ (itd->itd_status[4] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[5]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[5]),
+ (itd->itd_status[5] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[6]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[6]),
+ (itd->itd_status[6] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" status[7]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[7]),
+ (itd->itd_status[7] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
+ printf(" bp[0]=0x%08x\n", ehci32toh(sc, itd->itd_bp[0]));
+ printf(" addr=0x%02x; endpt=0x%01x\n",
+ EHCI_ITD_GET_ADDR(ehci32toh(sc, itd->itd_bp[0])),
+ EHCI_ITD_GET_ENDPT(ehci32toh(sc, itd->itd_bp[0])));
+ printf(" bp[1]=0x%08x\n", ehci32toh(sc, itd->itd_bp[1]));
+ printf(" dir=%s; mpl=0x%02x\n",
+ (ehci32toh(sc, itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out",
+ EHCI_ITD_GET_MPL(ehci32toh(sc, itd->itd_bp[1])));
+ printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n",
+ ehci32toh(sc, itd->itd_bp[2]),
+ ehci32toh(sc, itd->itd_bp[3]),
+ ehci32toh(sc, itd->itd_bp[4]),
+ ehci32toh(sc, itd->itd_bp[5]),
+ ehci32toh(sc, itd->itd_bp[6]));
+ printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n"
+ " 0x%08x,0x%08x,0x%08x\n",
+ ehci32toh(sc, itd->itd_bp_hi[0]),
+ ehci32toh(sc, itd->itd_bp_hi[1]),
+ ehci32toh(sc, itd->itd_bp_hi[2]),
+ ehci32toh(sc, itd->itd_bp_hi[3]),
+ ehci32toh(sc, itd->itd_bp_hi[4]),
+ ehci32toh(sc, itd->itd_bp_hi[5]),
+ ehci32toh(sc, itd->itd_bp_hi[6]));
+}
+
+static void
+ehci_dump_isoc(ehci_softc_t *sc)
+{
+ ehci_itd_t *itd;
+ ehci_sitd_t *sitd;
+ uint16_t max = 1000;
+ uint16_t pos;
+
+ pos = (EOREAD4(sc, EHCI_FRINDEX) / 8) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+
+ printf("%s: isochronous dump from frame 0x%03x:\n",
+ __FUNCTION__, pos);
+
+ itd = sc->sc_isoc_hs_p_last[pos];
+ sitd = sc->sc_isoc_fs_p_last[pos];
+
+ while (itd && max && max--) {
+ ehci_dump_itd(sc, itd);
+ itd = itd->prev;
+ }
+
+ while (sitd && max && max--) {
+ ehci_dump_sitd(sc, sitd);
+ sitd = sitd->prev;
+ }
+}
+
+#endif
+
+static void
+ehci_transfer_intr_enqueue(struct usb2_xfer *xfer)
+{
+ /* check for early completion */
+ if (ehci_check_transfer(xfer)) {
+ return;
+ }
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout);
+ }
+}
+
+#define EHCI_APPEND_FS_TD(std,last) (last) = _ehci_append_fs_td(std,last)
+static ehci_sitd_t *
+_ehci_append_fs_td(ehci_sitd_t *std, ehci_sitd_t *last)
+{
+ DPRINTFN(11, "%p to %p\n", std, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ std->next = last->next;
+ std->sitd_next = last->sitd_next;
+
+ std->prev = last;
+
+ usb2_pc_cpu_flush(std->page_cache);
+
+ /*
+ * the last->next->prev is never followed: std->next->prev = std;
+ */
+ last->next = std;
+ last->sitd_next = std->sitd_self;
+
+ usb2_pc_cpu_flush(last->page_cache);
+
+ return (std);
+}
+
+#define EHCI_APPEND_HS_TD(std,last) (last) = _ehci_append_hs_td(std,last)
+static ehci_itd_t *
+_ehci_append_hs_td(ehci_itd_t *std, ehci_itd_t *last)
+{
+ DPRINTFN(11, "%p to %p\n", std, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ std->next = last->next;
+ std->itd_next = last->itd_next;
+
+ std->prev = last;
+
+ usb2_pc_cpu_flush(std->page_cache);
+
+ /*
+ * the last->next->prev is never followed: std->next->prev = std;
+ */
+ last->next = std;
+ last->itd_next = std->itd_self;
+
+ usb2_pc_cpu_flush(last->page_cache);
+
+ return (std);
+}
+
+#define EHCI_APPEND_QH(sqh,last) (last) = _ehci_append_qh(sqh,last)
+static ehci_qh_t *
+_ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last)
+{
+ DPRINTFN(11, "%p to %p\n", sqh, last);
+
+ if (sqh->prev != NULL) {
+ /* should not happen */
+ DPRINTFN(0, "QH already linked!\n");
+ return (last);
+ }
+ /* (sc->sc_bus.mtx) must be locked */
+
+ sqh->next = last->next;
+ sqh->qh_link = last->qh_link;
+
+ sqh->prev = last;
+
+ usb2_pc_cpu_flush(sqh->page_cache);
+
+ /*
+ * the last->next->prev is never followed: sqh->next->prev = sqh;
+ */
+
+ last->next = sqh;
+ last->qh_link = sqh->qh_self;
+
+ usb2_pc_cpu_flush(last->page_cache);
+
+ return (sqh);
+}
+
+#define EHCI_REMOVE_FS_TD(std,last) (last) = _ehci_remove_fs_td(std,last)
+static ehci_sitd_t *
+_ehci_remove_fs_td(ehci_sitd_t *std, ehci_sitd_t *last)
+{
+ DPRINTFN(11, "%p from %p\n", std, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ std->prev->next = std->next;
+ std->prev->sitd_next = std->sitd_next;
+
+ usb2_pc_cpu_flush(std->prev->page_cache);
+
+ if (std->next) {
+ std->next->prev = std->prev;
+ usb2_pc_cpu_flush(std->next->page_cache);
+ }
+ return ((last == std) ? std->prev : last);
+}
+
+#define EHCI_REMOVE_HS_TD(std,last) (last) = _ehci_remove_hs_td(std,last)
+static ehci_itd_t *
+_ehci_remove_hs_td(ehci_itd_t *std, ehci_itd_t *last)
+{
+ DPRINTFN(11, "%p from %p\n", std, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ std->prev->next = std->next;
+ std->prev->itd_next = std->itd_next;
+
+ usb2_pc_cpu_flush(std->prev->page_cache);
+
+ if (std->next) {
+ std->next->prev = std->prev;
+ usb2_pc_cpu_flush(std->next->page_cache);
+ }
+ return ((last == std) ? std->prev : last);
+}
+
+#define EHCI_REMOVE_QH(sqh,last) (last) = _ehci_remove_qh(sqh,last)
+static ehci_qh_t *
+_ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last)
+{
+ DPRINTFN(11, "%p from %p\n", sqh, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ /* only remove if not removed from a queue */
+ if (sqh->prev) {
+
+ sqh->prev->next = sqh->next;
+ sqh->prev->qh_link = sqh->qh_link;
+
+ usb2_pc_cpu_flush(sqh->prev->page_cache);
+
+ if (sqh->next) {
+ sqh->next->prev = sqh->prev;
+ usb2_pc_cpu_flush(sqh->next->page_cache);
+ }
+ last = ((last == sqh) ? sqh->prev : last);
+
+ sqh->prev = 0;
+
+ usb2_pc_cpu_flush(sqh->page_cache);
+ }
+ return (last);
+}
+
+static usb2_error_t
+ehci_non_isoc_done_sub(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ ehci_qtd_t *td;
+ ehci_qtd_t *td_alt_next;
+ uint32_t status;
+ uint16_t len;
+
+ td = xfer->td_transfer_cache;
+ td_alt_next = td->alt_next;
+
+ if (xfer->aframes != xfer->nframes) {
+ xfer->frlengths[xfer->aframes] = 0;
+ }
+ while (1) {
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = ehci32toh(sc, td->qtd_status);
+
+ len = EHCI_QTD_GET_BYTES(status);
+
+ /*
+ * Verify the status length and
+ * add the length to "frlengths[]":
+ */
+ if (len > td->len) {
+ /* should not happen */
+ DPRINTF("Invalid status length, "
+ "0x%04x/0x%04x bytes\n", len, td->len);
+ status |= EHCI_QTD_HALTED;
+ } else if (xfer->aframes != xfer->nframes) {
+ xfer->frlengths[xfer->aframes] += td->len - len;
+ }
+ /* Check for last transfer */
+ if (((void *)td) == xfer->td_transfer_last) {
+ if (len == 0) {
+ /*
+ * Halt is ok if descriptor is last,
+ * and complete:
+ */
+ status &= ~EHCI_QTD_HALTED;
+ }
+ td = NULL;
+ break;
+ }
+ /* Check for transfer error */
+ if (status & EHCI_QTD_HALTED) {
+ /* the transfer is finished */
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ td = td->alt_next;
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ break;
+ }
+ td = td->obj_next;
+
+ if (td->alt_next != td_alt_next) {
+ /* this USB frame is complete */
+ break;
+ }
+ }
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ /* update data toggle */
+
+ xfer->pipe->toggle_next =
+ (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0;
+
+#if USB_DEBUG
+ if (status & EHCI_QTD_STATERRS) {
+ DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x"
+ "status=%s%s%s%s%s%s%s%s\n",
+ xfer->address, xfer->endpoint, xfer->aframes,
+ (status & EHCI_QTD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]",
+ (status & EHCI_QTD_HALTED) ? "[HALTED]" : "",
+ (status & EHCI_QTD_BUFERR) ? "[BUFERR]" : "",
+ (status & EHCI_QTD_BABBLE) ? "[BABBLE]" : "",
+ (status & EHCI_QTD_XACTERR) ? "[XACTERR]" : "",
+ (status & EHCI_QTD_MISSEDMICRO) ? "[MISSED]" : "",
+ (status & EHCI_QTD_SPLITXSTATE) ? "[SPLIT]" : "",
+ (status & EHCI_QTD_PINGSTATE) ? "[PING]" : "");
+ }
+#endif
+
+ return ((status & EHCI_QTD_HALTED) ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+ehci_non_isoc_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+#if USB_DEBUG
+ if (ehcidebug > 10) {
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ ehci_dump_sqtds(sc, xfer->td_transfer_first);
+ }
+#endif
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = ehci_non_isoc_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = ehci_non_isoc_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = ehci_non_isoc_done_sub(xfer);
+ }
+done:
+ ehci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * ehci_check_transfer
+ *
+ * Return values:
+ * 0: USB transfer is not finished
+ * Else: USB transfer is finished
+ *------------------------------------------------------------------------*/
+static uint8_t
+ehci_check_transfer(struct usb2_xfer *xfer)
+{
+ struct usb2_pipe_methods *methods = xfer->pipe->methods;
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ uint32_t status;
+
+ DPRINTFN(13, "xfer=%p checking transfer\n", xfer);
+
+ if (methods == &ehci_device_isoc_fs_methods) {
+ ehci_sitd_t *td;
+
+ /* isochronous full speed transfer */
+
+ td = xfer->td_transfer_last;
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = ehci32toh(sc, td->sitd_status);
+
+ /* also check if first is complete */
+
+ td = xfer->td_transfer_first;
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status |= ehci32toh(sc, td->sitd_status);
+
+ if (!(status & EHCI_SITD_ACTIVE)) {
+ ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION);
+ goto transferred;
+ }
+ } else if (methods == &ehci_device_isoc_hs_methods) {
+ ehci_itd_t *td;
+
+ /* isochronous high speed transfer */
+
+ td = xfer->td_transfer_last;
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status =
+ td->itd_status[0] | td->itd_status[1] |
+ td->itd_status[2] | td->itd_status[3] |
+ td->itd_status[4] | td->itd_status[5] |
+ td->itd_status[6] | td->itd_status[7];
+
+ /* also check first transfer */
+ td = xfer->td_transfer_first;
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status |=
+ td->itd_status[0] | td->itd_status[1] |
+ td->itd_status[2] | td->itd_status[3] |
+ td->itd_status[4] | td->itd_status[5] |
+ td->itd_status[6] | td->itd_status[7];
+
+ /* if no transactions are active we continue */
+ if (!(status & htoehci32(sc, EHCI_ITD_ACTIVE))) {
+ ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION);
+ goto transferred;
+ }
+ } else {
+ ehci_qtd_t *td;
+
+ /* non-isochronous transfer */
+
+ /*
+ * check whether there is an error somewhere in the middle,
+ * or whether there was a short packet (SPD and not ACTIVE)
+ */
+ td = xfer->td_transfer_cache;
+
+ while (1) {
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = ehci32toh(sc, td->qtd_status);
+
+ /*
+ * if there is an active TD the transfer isn't done
+ */
+ if (status & EHCI_QTD_ACTIVE) {
+ /* update cache */
+ xfer->td_transfer_cache = td;
+ goto done;
+ }
+ /*
+ * last transfer descriptor makes the transfer done
+ */
+ if (((void *)td) == xfer->td_transfer_last) {
+ break;
+ }
+ /*
+ * any kind of error makes the transfer done
+ */
+ if (status & EHCI_QTD_HALTED) {
+ break;
+ }
+ /*
+ * if there is no alternate next transfer, a short
+ * packet also makes the transfer done
+ */
+ if (EHCI_QTD_GET_BYTES(status)) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->alt_next;
+ continue;
+ }
+ }
+ /* transfer is done */
+ break;
+ }
+ td = td->obj_next;
+ }
+ ehci_non_isoc_done(xfer);
+ goto transferred;
+ }
+
+done:
+ DPRINTFN(13, "xfer=%p is still active\n", xfer);
+ return (0);
+
+transferred:
+ return (1);
+}
+
+static void
+ehci_pcd_enable(ehci_softc_t *sc)
+{
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ sc->sc_eintrs |= EHCI_STS_PCD;
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ /* acknowledge any PCD interrupt */
+ EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD);
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &ehci_root_intr_done);
+}
+
+static void
+ehci_interrupt_poll(ehci_softc_t *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ /*
+ * check if transfer is transferred
+ */
+ if (ehci_check_transfer(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * ehci_interrupt - EHCI interrupt handler
+ *
+ * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler,
+ * hence the interrupt handler will be setup before "sc->sc_bus.bdev"
+ * is present !
+ *------------------------------------------------------------------------*/
+void
+ehci_interrupt(ehci_softc_t *sc)
+{
+ uint32_t status;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ DPRINTFN(16, "real interrupt\n");
+
+#if USB_DEBUG
+ if (ehcidebug > 15) {
+ ehci_dump_regs(sc);
+ }
+#endif
+
+ status = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
+ if (status == 0) {
+ /* the interrupt was not for us */
+ goto done;
+ }
+ if (!(status & sc->sc_eintrs)) {
+ goto done;
+ }
+ EOWRITE4(sc, EHCI_USBSTS, status); /* acknowledge */
+
+ status &= sc->sc_eintrs;
+
+ if (status & EHCI_STS_HSE) {
+ printf("%s: unrecoverable error, "
+ "controller halted\n", __FUNCTION__);
+#if USB_DEBUG
+ ehci_dump_regs(sc);
+ ehci_dump_isoc(sc);
+#endif
+ }
+ if (status & EHCI_STS_PCD) {
+ /*
+ * Disable PCD interrupt for now, because it will be
+ * on until the port has been reset.
+ */
+ sc->sc_eintrs &= ~EHCI_STS_PCD;
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &ehci_root_intr_done);
+
+ /* do not allow RHSC interrupts > 1 per second */
+ usb2_callout_reset(&sc->sc_tmo_pcd, hz,
+ (void *)&ehci_pcd_enable, sc);
+ }
+ status &= ~(EHCI_STS_INT | EHCI_STS_ERRINT | EHCI_STS_PCD | EHCI_STS_IAA);
+
+ if (status != 0) {
+ /* block unprocessed interrupts */
+ sc->sc_eintrs &= ~status;
+ EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
+ printf("%s: blocking interrupts 0x%x\n", __FUNCTION__, status);
+ }
+ /* poll all the USB transfers */
+ ehci_interrupt_poll(sc);
+
+done:
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*
+ * called when a request does not complete
+ */
+static void
+ehci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ ehci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+ehci_do_poll(struct usb2_bus *bus)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ ehci_interrupt_poll(sc);
+ ehci_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+ehci_setup_standard_chain_sub(struct ehci_std_temp *temp)
+{
+ struct usb2_page_search buf_res;
+ ehci_qtd_t *td;
+ ehci_qtd_t *td_next;
+ ehci_qtd_t *td_alt_next;
+ uint32_t qtd_altnext;
+ uint32_t buf_offset;
+ uint32_t average;
+ uint32_t len_old;
+ uint8_t shortpkt_old;
+ uint8_t precompute;
+
+ qtd_altnext = htoehci32(temp->sc, EHCI_LINK_TERMINATE);
+ td_alt_next = NULL;
+ buf_offset = 0;
+ shortpkt_old = temp->shortpkt;
+ len_old = temp->len;
+ precompute = 1;
+
+restart:
+
+ td = temp->td;
+ td_next = temp->td_next;
+
+ while (1) {
+
+ if (temp->len == 0) {
+
+ if (temp->shortpkt) {
+ break;
+ }
+ /* send a Zero Length Packet, ZLP, last */
+
+ temp->shortpkt = 1;
+ average = 0;
+
+ } else {
+
+ average = temp->average;
+
+ if (temp->len < average) {
+ if (temp->len % temp->max_frame_size) {
+ temp->shortpkt = 1;
+ }
+ average = temp->len;
+ }
+ }
+
+ if (td_next == NULL) {
+ panic("%s: out of EHCI transfer descriptors!", __FUNCTION__);
+ }
+ /* get next TD */
+
+ td = td_next;
+ td_next = td->obj_next;
+
+ /* check if we are pre-computing */
+
+ if (precompute) {
+
+ /* update remaining length */
+
+ temp->len -= average;
+
+ continue;
+ }
+ /* fill out current TD */
+
+ td->qtd_status =
+ temp->qtd_status |
+ htoehci32(temp->sc, EHCI_QTD_SET_BYTES(average));
+
+ if (average == 0) {
+
+ if (temp->auto_data_toggle == 0) {
+
+ /* update data toggle, ZLP case */
+
+ temp->qtd_status ^=
+ htoehci32(temp->sc, EHCI_QTD_TOGGLE_MASK);
+ }
+ td->len = 0;
+
+ td->qtd_buffer[0] = 0;
+ td->qtd_buffer_hi[0] = 0;
+
+ td->qtd_buffer[1] = 0;
+ td->qtd_buffer_hi[1] = 0;
+
+ } else {
+
+ uint8_t x;
+
+ if (temp->auto_data_toggle == 0) {
+
+ /* update data toggle */
+
+ if (((average + temp->max_frame_size - 1) /
+ temp->max_frame_size) & 1) {
+ temp->qtd_status ^=
+ htoehci32(temp->sc, EHCI_QTD_TOGGLE_MASK);
+ }
+ }
+ td->len = average;
+
+ /* update remaining length */
+
+ temp->len -= average;
+
+ /* fill out buffer pointers */
+
+ usb2_get_page(temp->pc, buf_offset, &buf_res);
+ td->qtd_buffer[0] =
+ htoehci32(temp->sc, buf_res.physaddr);
+ td->qtd_buffer_hi[0] = 0;
+
+ x = 1;
+
+ while (average > EHCI_PAGE_SIZE) {
+ average -= EHCI_PAGE_SIZE;
+ buf_offset += EHCI_PAGE_SIZE;
+ usb2_get_page(temp->pc, buf_offset, &buf_res);
+ td->qtd_buffer[x] =
+ htoehci32(temp->sc,
+ buf_res.physaddr & (~0xFFF));
+ td->qtd_buffer_hi[x] = 0;
+ x++;
+ }
+
+ /*
+ * NOTE: The "average" variable is never zero after
+ * exiting the loop above !
+ *
+ * NOTE: We have to subtract one from the offset to
+ * ensure that we are computing the physical address
+ * of a valid page !
+ */
+ buf_offset += average;
+ usb2_get_page(temp->pc, buf_offset - 1, &buf_res);
+ td->qtd_buffer[x] =
+ htoehci32(temp->sc,
+ buf_res.physaddr & (~0xFFF));
+ td->qtd_buffer_hi[x] = 0;
+ }
+
+ if (td_next) {
+ /* link the current TD with the next one */
+ td->qtd_next = td_next->qtd_self;
+ }
+ td->qtd_altnext = qtd_altnext;
+ td->alt_next = td_alt_next;
+
+ usb2_pc_cpu_flush(td->page_cache);
+ }
+
+ if (precompute) {
+ precompute = 0;
+
+ /* setup alt next pointer, if any */
+ if (temp->short_frames_ok) {
+ if (temp->setup_alt_next) {
+ td_alt_next = td_next;
+ qtd_altnext = td_next->qtd_self;
+ }
+ } else {
+ /* we use this field internally */
+ td_alt_next = td_next;
+ }
+
+ /* restore */
+ temp->shortpkt = shortpkt_old;
+ temp->len = len_old;
+ goto restart;
+ }
+ temp->td = td;
+ temp->td_next = td_next;
+}
+
+static void
+ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last)
+{
+ struct ehci_std_temp temp;
+ struct usb2_pipe_methods *methods;
+ ehci_qh_t *qh;
+ ehci_qtd_t *td;
+ uint32_t qh_endp;
+ uint32_t qh_endphub;
+ uint32_t x;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.average = xfer->max_usb2_frame_size;
+ temp.max_frame_size = xfer->max_frame_size;
+ temp.sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ temp.td = NULL;
+ temp.td_next = td;
+ temp.qtd_status = 0;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.short_frames_ok = xfer->flags_int.short_frames_ok;
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->pipe->toggle_next) {
+ /* DATA1 is next */
+ temp.qtd_status |=
+ htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1));
+ }
+ temp.auto_data_toggle = 0;
+ } else {
+ temp.auto_data_toggle = 1;
+ }
+
+ if (usb2_get_speed(xfer->xroot->udev) != USB_SPEED_HIGH) {
+ /* max 3 retries */
+ temp.qtd_status |=
+ htoehci32(temp.sc, EHCI_QTD_SET_CERR(3));
+ }
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.qtd_status &=
+ htoehci32(temp.sc, EHCI_QTD_SET_CERR(3));
+ temp.qtd_status |= htole32
+ (EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
+ EHCI_QTD_SET_TOGGLE(0));
+
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.shortpkt = temp.len ? 1 : 0;
+
+ ehci_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+ temp.pc = xfer->frbuffers + x;
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ /* keep previous data toggle and error count */
+
+ temp.qtd_status &=
+ htoehci32(temp.sc, EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(1));
+
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.shortpkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ /* set endpoint direction */
+
+ temp.qtd_status |=
+ (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ?
+ htoehci32(temp.sc, EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) :
+ htoehci32(temp.sc, EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT));
+
+ ehci_setup_standard_chain_sub(&temp);
+ }
+
+ /* check if we should append a status stage */
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current endpoint
+ * direction.
+ */
+
+ temp.qtd_status &= htoehci32(temp.sc, EHCI_QTD_SET_CERR(3) |
+ EHCI_QTD_SET_TOGGLE(1));
+ temp.qtd_status |=
+ (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ?
+ htoehci32(temp.sc, EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) |
+ EHCI_QTD_SET_TOGGLE(1)) :
+ htoehci32(temp.sc, EHCI_QTD_ACTIVE |
+ EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) |
+ EHCI_QTD_SET_TOGGLE(1));
+
+ temp.len = 0;
+ temp.pc = NULL;
+ temp.shortpkt = 0;
+
+ ehci_setup_standard_chain_sub(&temp);
+ }
+ td = temp.td;
+
+ /* the last TD terminates the transfer: */
+ td->qtd_next = htoehci32(temp.sc, EHCI_LINK_TERMINATE);
+ td->qtd_altnext = htoehci32(temp.sc, EHCI_LINK_TERMINATE);
+ td->qtd_status |= htoehci32(temp.sc, EHCI_QTD_IOC);
+
+ usb2_pc_cpu_flush(td->page_cache);
+
+ /* must have at least one frame! */
+
+ xfer->td_transfer_last = td;
+
+#if USB_DEBUG
+ if (ehcidebug > 8) {
+ DPRINTF("nexttog=%d; data before transfer:\n",
+ xfer->pipe->toggle_next);
+ ehci_dump_sqtds(temp.sc,
+ xfer->td_transfer_first);
+ }
+#endif
+
+ methods = xfer->pipe->methods;
+
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ /* the "qh_link" field is filled when the QH is added */
+
+ qh_endp =
+ (EHCI_QH_SET_ADDR(xfer->address) |
+ EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) |
+ EHCI_QH_SET_MPL(xfer->max_packet_size));
+
+ if (usb2_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
+ qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) |
+ EHCI_QH_DTC);
+ if (methods != &ehci_device_intr_methods)
+ qh_endp |= EHCI_QH_SET_NRL(8);
+ } else {
+
+ if (usb2_get_speed(xfer->xroot->udev) == USB_SPEED_FULL) {
+ qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL) |
+ EHCI_QH_DTC);
+ } else {
+ qh_endp |= (EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW) |
+ EHCI_QH_DTC);
+ }
+
+ if (methods == &ehci_device_ctrl_methods) {
+ qh_endp |= EHCI_QH_CTL;
+ }
+ if (methods != &ehci_device_intr_methods) {
+ /* Only try one time per microframe! */
+ qh_endp |= EHCI_QH_SET_NRL(1);
+ }
+ }
+
+ qh->qh_endp = htoehci32(temp.sc, qh_endp);
+
+ qh_endphub =
+ (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) |
+ EHCI_QH_SET_CMASK(xfer->usb2_cmask) |
+ EHCI_QH_SET_SMASK(xfer->usb2_smask) |
+ EHCI_QH_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
+ EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no));
+
+ qh->qh_endphub = htoehci32(temp.sc, qh_endphub);
+ qh->qh_curqtd = htoehci32(temp.sc, 0);
+
+ /* fill the overlay qTD */
+ qh->qh_qtd.qtd_status = htoehci32(temp.sc, 0);
+
+ if (temp.auto_data_toggle) {
+
+ /* let the hardware compute the data toggle */
+
+ qh->qh_endp &= htoehci32(temp.sc, ~EHCI_QH_DTC);
+
+ if (xfer->pipe->toggle_next) {
+ /* DATA1 is next */
+ qh->qh_qtd.qtd_status |=
+ htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1));
+ }
+ }
+ td = xfer->td_transfer_first;
+
+ qh->qh_qtd.qtd_next = td->qtd_self;
+ qh->qh_qtd.qtd_altnext =
+ htoehci32(temp.sc, EHCI_LINK_TERMINATE);
+
+ usb2_pc_cpu_flush(qh->page_cache);
+
+ if (xfer->xroot->udev->pwr_save.suspended == 0) {
+ EHCI_APPEND_QH(qh, *qh_last);
+ }
+}
+
+static void
+ehci_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ uint16_t i;
+ uint16_t m;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ ehci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+
+ /* clear any old interrupt data */
+ bzero(sc->sc_hub_idata, sizeof(sc->sc_hub_idata));
+
+ /* set bits */
+ m = (sc->sc_noport + 1);
+ if (m > (8 * sizeof(sc->sc_hub_idata))) {
+ m = (8 * sizeof(sc->sc_hub_idata));
+ }
+ for (i = 1; i < m; i++) {
+ /* pick out CHANGE bits from the status register */
+ if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) {
+ sc->sc_hub_idata[i / 8] |= 1 << (i % 8);
+ DPRINTF("port %d changed\n", i);
+ }
+ }
+done:
+ return;
+}
+
+static void
+ehci_isoc_fs_done(ehci_softc_t *sc, struct usb2_xfer *xfer)
+{
+ uint32_t nframes = xfer->nframes;
+ uint32_t status;
+ uint32_t *plen = xfer->frlengths;
+ uint16_t len = 0;
+ ehci_sitd_t *td = xfer->td_transfer_first;
+ ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos];
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
+ pp_last = &sc->sc_isoc_fs_p_last[0];
+ }
+#if USB_DEBUG
+ if (ehcidebug > 15) {
+ DPRINTF("isoc FS-TD\n");
+ ehci_dump_sitd(sc, td);
+ }
+#endif
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = ehci32toh(sc, td->sitd_status);
+
+ len = EHCI_SITD_GET_LEN(status);
+
+ if (*plen >= len) {
+ len = *plen - len;
+ } else {
+ len = 0;
+ }
+
+ *plen = len;
+
+ /* remove FS-TD from schedule */
+ EHCI_REMOVE_FS_TD(td, *pp_last);
+
+ pp_last++;
+ plen++;
+ td = td->obj_next;
+ }
+
+ xfer->aframes = xfer->nframes;
+}
+
+static void
+ehci_isoc_hs_done(ehci_softc_t *sc, struct usb2_xfer *xfer)
+{
+ uint32_t nframes = xfer->nframes;
+ uint32_t status;
+ uint32_t *plen = xfer->frlengths;
+ uint16_t len = 0;
+ uint8_t td_no = 0;
+ ehci_itd_t *td = xfer->td_transfer_first;
+ ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos];
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
+ pp_last = &sc->sc_isoc_hs_p_last[0];
+ }
+#if USB_DEBUG
+ if (ehcidebug > 15) {
+ DPRINTF("isoc HS-TD\n");
+ ehci_dump_itd(sc, td);
+ }
+#endif
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = ehci32toh(sc, td->itd_status[td_no]);
+
+ len = EHCI_ITD_GET_LEN(status);
+
+ if (*plen >= len) {
+ /*
+ * The length is valid. NOTE: The complete
+ * length is written back into the status
+ * field, and not the remainder like with
+ * other transfer descriptor types.
+ */
+ } else {
+ /* Invalid length - truncate */
+ len = 0;
+ }
+
+ *plen = len;
+
+ plen++;
+ td_no++;
+
+ if ((td_no == 8) || (nframes == 0)) {
+ /* remove HS-TD from schedule */
+ EHCI_REMOVE_HS_TD(td, *pp_last);
+ pp_last++;
+
+ td_no = 0;
+ td = td->obj_next;
+ }
+ }
+ xfer->aframes = xfer->nframes;
+}
+
+/* NOTE: "done" can be run two times in a row,
+ * from close and from interrupt
+ */
+static void
+ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ struct usb2_pipe_methods *methods = xfer->pipe->methods;
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ if ((methods == &ehci_device_bulk_methods) ||
+ (methods == &ehci_device_ctrl_methods)) {
+#if USB_DEBUG
+ if (ehcidebug > 8) {
+ DPRINTF("nexttog=%d; data after transfer:\n",
+ xfer->pipe->toggle_next);
+ ehci_dump_sqtds(sc,
+ xfer->td_transfer_first);
+ }
+#endif
+
+ EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_async_p_last);
+ }
+ if (methods == &ehci_device_intr_methods) {
+ EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ /*
+ * Only finish isochronous transfers once which will update
+ * "xfer->frlengths".
+ */
+ if (xfer->td_transfer_first &&
+ xfer->td_transfer_last) {
+ if (methods == &ehci_device_isoc_fs_methods) {
+ ehci_isoc_fs_done(sc, xfer);
+ }
+ if (methods == &ehci_device_isoc_hs_methods) {
+ ehci_isoc_hs_done(sc, xfer);
+ }
+ xfer->td_transfer_first = NULL;
+ xfer->td_transfer_last = NULL;
+ }
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+/*------------------------------------------------------------------------*
+ * ehci bulk support
+ *------------------------------------------------------------------------*/
+static void
+ehci_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_device_bulk_close(struct usb2_xfer *xfer)
+{
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ehci_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_device_bulk_start(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ /* setup TD's and QH */
+ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last);
+
+ /* put transfer on interrupt queue */
+ ehci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ehci_device_bulk_methods =
+{
+ .open = ehci_device_bulk_open,
+ .close = ehci_device_bulk_close,
+ .enter = ehci_device_bulk_enter,
+ .start = ehci_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ehci control support
+ *------------------------------------------------------------------------*/
+static void
+ehci_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ehci_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ /* setup TD's and QH */
+ ehci_setup_standard_chain(xfer, &sc->sc_async_p_last);
+
+ /* put transfer on interrupt queue */
+ ehci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ehci_device_ctrl_methods =
+{
+ .open = ehci_device_ctrl_open,
+ .close = ehci_device_ctrl_close,
+ .enter = ehci_device_ctrl_enter,
+ .start = ehci_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ehci interrupt support
+ *------------------------------------------------------------------------*/
+static void
+ehci_device_intr_open(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ uint16_t best;
+ uint16_t bit;
+ uint16_t x;
+ uint8_t slot;
+
+ /* Allocate a microframe slot first: */
+
+ slot = usb2_intr_schedule_adjust
+ (xfer->xroot->udev, xfer->max_frame_size, USB_HS_MICRO_FRAMES_MAX);
+
+ if (usb2_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
+ xfer->usb2_uframe = slot;
+ xfer->usb2_smask = (1 << slot) & 0xFF;
+ xfer->usb2_cmask = 0;
+ } else {
+ xfer->usb2_uframe = slot;
+ xfer->usb2_smask = (1 << slot) & 0x3F;
+ xfer->usb2_cmask = (-(4 << slot)) & 0xFE;
+ }
+
+ /*
+ * Find the best QH position corresponding to the given interval:
+ */
+
+ best = 0;
+ bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2;
+ while (bit) {
+ if (xfer->interval >= bit) {
+ x = bit;
+ best = bit;
+ while (x & bit) {
+ if (sc->sc_intr_stat[x] <
+ sc->sc_intr_stat[best]) {
+ best = x;
+ }
+ x++;
+ }
+ break;
+ }
+ bit >>= 1;
+ }
+
+ sc->sc_intr_stat[best]++;
+ xfer->qh_pos = best;
+
+ DPRINTFN(3, "best=%d interval=%d\n",
+ best, xfer->interval);
+}
+
+static void
+ehci_device_intr_close(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ uint8_t slot;
+
+ slot = usb2_intr_schedule_adjust
+ (xfer->xroot->udev, -(xfer->max_frame_size), xfer->usb2_uframe);
+
+ sc->sc_intr_stat[xfer->qh_pos]--;
+
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ehci_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_device_intr_start(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ /* setup TD's and QH */
+ ehci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]);
+
+ /* put transfer on interrupt queue */
+ ehci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ehci_device_intr_methods =
+{
+ .open = ehci_device_intr_open,
+ .close = ehci_device_intr_close,
+ .enter = ehci_device_intr_enter,
+ .start = ehci_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ehci full speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+ehci_device_isoc_fs_open(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ ehci_sitd_t *td;
+ uint32_t sitd_portaddr;
+ uint8_t ds;
+
+ sitd_portaddr =
+ EHCI_SITD_SET_ADDR(xfer->address) |
+ EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)) |
+ EHCI_SITD_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
+ EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no);
+
+ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+ sitd_portaddr |= EHCI_SITD_SET_DIR_IN;
+ }
+ sitd_portaddr = htoehci32(sc, sitd_portaddr);
+
+ /* initialize all TD's */
+
+ for (ds = 0; ds != 2; ds++) {
+
+ for (td = xfer->td_start[ds]; td; td = td->obj_next) {
+
+ td->sitd_portaddr = sitd_portaddr;
+
+ /*
+ * TODO: make some kind of automatic
+ * SMASK/CMASK selection based on micro-frame
+ * usage
+ *
+ * micro-frame usage (8 microframes per 1ms)
+ */
+ td->sitd_back = htoehci32(sc, EHCI_LINK_TERMINATE);
+
+ usb2_pc_cpu_flush(td->page_cache);
+ }
+ }
+}
+
+static void
+ehci_device_isoc_fs_close(struct usb2_xfer *xfer)
+{
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ehci_device_isoc_fs_enter(struct usb2_xfer *xfer)
+{
+ struct usb2_page_search buf_res;
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ struct usb2_fs_isoc_schedule *fss_start;
+ struct usb2_fs_isoc_schedule *fss_end;
+ struct usb2_fs_isoc_schedule *fss;
+ ehci_sitd_t *td;
+ ehci_sitd_t *td_last = NULL;
+ ehci_sitd_t **pp_last;
+ uint32_t *plen;
+ uint32_t buf_offset;
+ uint32_t nframes;
+ uint32_t temp;
+ uint32_t sitd_mask;
+ uint16_t tlen;
+ uint8_t sa;
+ uint8_t sb;
+ uint8_t error;
+
+#if USB_DEBUG
+ uint8_t once = 1;
+
+#endif
+
+ DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ nframes = EOREAD4(sc, EHCI_FRINDEX) / 8;
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ buf_offset = (nframes - xfer->pipe->isoc_next) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (buf_offset < xfer->nframes)) {
+ /*
+ * If there is data underflow or the pipe queue is empty we
+ * schedule the transfer a few frames ahead of the current
+ * frame position. Else two isochronous transfers might
+ * overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ buf_offset = (xfer->pipe->isoc_next - nframes) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_fs_isoc_schedule_isoc_time_expand
+ (xfer->xroot->udev, &fss_start, &fss_end, nframes) + buf_offset +
+ xfer->nframes;
+
+ /* get the real number of frames */
+
+ nframes = xfer->nframes;
+
+ buf_offset = 0;
+
+ plen = xfer->frlengths;
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+ xfer->td_transfer_first = td;
+
+ pp_last = &sc->sc_isoc_fs_p_last[xfer->pipe->isoc_next];
+
+ /* store starting position */
+
+ xfer->qh_pos = xfer->pipe->isoc_next;
+
+ fss = fss_start + (xfer->qh_pos % USB_ISOC_TIME_MAX);
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
+ pp_last = &sc->sc_isoc_fs_p_last[0];
+ }
+ if (fss >= fss_end) {
+ fss = fss_start;
+ }
+ /* reuse sitd_portaddr and sitd_back from last transfer */
+
+ if (*plen > xfer->max_frame_size) {
+#if USB_DEBUG
+ if (once) {
+ once = 0;
+ printf("%s: frame length(%d) exceeds %d "
+ "bytes (frame truncated)\n",
+ __FUNCTION__, *plen,
+ xfer->max_frame_size);
+ }
+#endif
+ *plen = xfer->max_frame_size;
+ }
+ /*
+ * We currently don't care if the ISOCHRONOUS schedule is
+ * full!
+ */
+ error = usb2_fs_isoc_schedule_alloc(fss, &sa, *plen);
+ if (error) {
+ /*
+ * The FULL speed schedule is FULL! Set length
+ * to zero.
+ */
+ *plen = 0;
+ }
+ if (*plen) {
+ /*
+ * only call "usb2_get_page()" when we have a
+ * non-zero length
+ */
+ usb2_get_page(xfer->frbuffers, buf_offset, &buf_res);
+ td->sitd_bp[0] = htoehci32(sc, buf_res.physaddr);
+ buf_offset += *plen;
+ /*
+ * NOTE: We need to subtract one from the offset so
+ * that we are on a valid page!
+ */
+ usb2_get_page(xfer->frbuffers, buf_offset - 1,
+ &buf_res);
+ temp = buf_res.physaddr & ~0xFFF;
+ } else {
+ td->sitd_bp[0] = 0;
+ temp = 0;
+ }
+
+ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) {
+ tlen = *plen;
+ if (tlen <= 188) {
+ temp |= 1; /* T-count = 1, TP = ALL */
+ tlen = 1;
+ } else {
+ tlen += 187;
+ tlen /= 188;
+ temp |= tlen; /* T-count = [1..6] */
+ temp |= 8; /* TP = Begin */
+ }
+
+ tlen += sa;
+
+ if (tlen >= 8) {
+ sb = 0;
+ } else {
+ sb = (1 << tlen);
+ }
+
+ sa = (1 << sa);
+ sa = (sb - sa) & 0x3F;
+ sb = 0;
+ } else {
+ sb = (-(4 << sa)) & 0xFE;
+ sa = (1 << sa) & 0x3F;
+ }
+
+ sitd_mask = (EHCI_SITD_SET_SMASK(sa) |
+ EHCI_SITD_SET_CMASK(sb));
+
+ td->sitd_bp[1] = htoehci32(sc, temp);
+
+ td->sitd_mask = htoehci32(sc, sitd_mask);
+
+ if (nframes == 0) {
+ td->sitd_status = htole32
+ (EHCI_SITD_IOC |
+ EHCI_SITD_ACTIVE |
+ EHCI_SITD_SET_LEN(*plen));
+ } else {
+ td->sitd_status = htole32
+ (EHCI_SITD_ACTIVE |
+ EHCI_SITD_SET_LEN(*plen));
+ }
+ usb2_pc_cpu_flush(td->page_cache);
+
+#if USB_DEBUG
+ if (ehcidebug > 15) {
+ DPRINTF("FS-TD %d\n", nframes);
+ ehci_dump_sitd(sc, td);
+ }
+#endif
+ /* insert TD into schedule */
+ EHCI_APPEND_FS_TD(td, *pp_last);
+ pp_last++;
+
+ plen++;
+ fss++;
+ td_last = td;
+ td = td->obj_next;
+ }
+
+ xfer->td_transfer_last = td_last;
+
+ /* update isoc_next */
+ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+}
+
+static void
+ehci_device_isoc_fs_start(struct usb2_xfer *xfer)
+{
+ /* put transfer on interrupt queue */
+ ehci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ehci_device_isoc_fs_methods =
+{
+ .open = ehci_device_isoc_fs_open,
+ .close = ehci_device_isoc_fs_close,
+ .enter = ehci_device_isoc_fs_enter,
+ .start = ehci_device_isoc_fs_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ehci high speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+ehci_device_isoc_hs_open(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ ehci_itd_t *td;
+ uint32_t temp;
+ uint8_t ds;
+
+ /* initialize all TD's */
+
+ for (ds = 0; ds != 2; ds++) {
+
+ for (td = xfer->td_start[ds]; td; td = td->obj_next) {
+
+ /* set TD inactive */
+ td->itd_status[0] = 0;
+ td->itd_status[1] = 0;
+ td->itd_status[2] = 0;
+ td->itd_status[3] = 0;
+ td->itd_status[4] = 0;
+ td->itd_status[5] = 0;
+ td->itd_status[6] = 0;
+ td->itd_status[7] = 0;
+
+ /* set endpoint and address */
+ td->itd_bp[0] = htole32
+ (EHCI_ITD_SET_ADDR(xfer->address) |
+ EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpoint)));
+
+ temp =
+ EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF);
+
+ /* set direction */
+ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+ temp |= EHCI_ITD_SET_DIR_IN;
+ }
+ /* set maximum packet size */
+ td->itd_bp[1] = htoehci32(sc, temp);
+
+ /* set transfer multiplier */
+ td->itd_bp[2] = htoehci32(sc, xfer->max_packet_count & 3);
+
+ usb2_pc_cpu_flush(td->page_cache);
+ }
+ }
+}
+
+static void
+ehci_device_isoc_hs_close(struct usb2_xfer *xfer)
+{
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ehci_device_isoc_hs_enter(struct usb2_xfer *xfer)
+{
+ struct usb2_page_search buf_res;
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ ehci_itd_t *td;
+ ehci_itd_t *td_last = NULL;
+ ehci_itd_t **pp_last;
+ bus_size_t page_addr;
+ uint32_t *plen;
+ uint32_t status;
+ uint32_t buf_offset;
+ uint32_t nframes;
+ uint32_t itd_offset[8 + 1];
+ uint8_t x;
+ uint8_t td_no;
+ uint8_t page_no;
+
+#if USB_DEBUG
+ uint8_t once = 1;
+
+#endif
+
+ DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ nframes = EOREAD4(sc, EHCI_FRINDEX) / 8;
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ buf_offset = (nframes - xfer->pipe->isoc_next) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (buf_offset < ((xfer->nframes + 7) / 8))) {
+ /*
+ * If there is data underflow or the pipe queue is empty we
+ * schedule the transfer a few frames ahead of the current
+ * frame position. Else two isochronous transfers might
+ * overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ buf_offset = (xfer->pipe->isoc_next - nframes) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset +
+ ((xfer->nframes + 7) / 8);
+
+ /* get the real number of frames */
+
+ nframes = xfer->nframes;
+
+ buf_offset = 0;
+ td_no = 0;
+
+ plen = xfer->frlengths;
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+ xfer->td_transfer_first = td;
+
+ pp_last = &sc->sc_isoc_hs_p_last[xfer->pipe->isoc_next];
+
+ /* store starting position */
+
+ xfer->qh_pos = xfer->pipe->isoc_next;
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
+ pp_last = &sc->sc_isoc_hs_p_last[0];
+ }
+ /* range check */
+ if (*plen > xfer->max_frame_size) {
+#if USB_DEBUG
+ if (once) {
+ once = 0;
+ printf("%s: frame length(%d) exceeds %d bytes "
+ "(frame truncated)\n",
+ __FUNCTION__, *plen, xfer->max_frame_size);
+ }
+#endif
+ *plen = xfer->max_frame_size;
+ }
+ status = (EHCI_ITD_SET_LEN(*plen) |
+ EHCI_ITD_ACTIVE |
+ EHCI_ITD_SET_PG(0));
+ td->itd_status[td_no] = htoehci32(sc, status);
+ itd_offset[td_no] = buf_offset;
+ buf_offset += *plen;
+ plen++;
+ td_no++;
+
+ if ((td_no == 8) || (nframes == 0)) {
+
+ /* the rest of the transfers are not active, if any */
+ for (x = td_no; x != 8; x++) {
+ td->itd_status[x] = 0; /* not active */
+ }
+
+ /* check if there is any data to be transferred */
+ if (itd_offset[0] != buf_offset) {
+ page_no = 0;
+ itd_offset[td_no] = buf_offset;
+
+ /* get first page offset */
+ usb2_get_page(xfer->frbuffers, itd_offset[0], &buf_res);
+ /* get page address */
+ page_addr = buf_res.physaddr & ~0xFFF;
+ /* update page address */
+ td->itd_bp[0] &= htoehci32(sc, 0xFFF);
+ td->itd_bp[0] |= htoehci32(sc, page_addr);
+
+ for (x = 0; x != td_no; x++) {
+ /* set page number and page offset */
+ status = (EHCI_ITD_SET_PG(page_no) |
+ (buf_res.physaddr & 0xFFF));
+ td->itd_status[x] |= htoehci32(sc, status);
+
+ /* get next page offset */
+ if (itd_offset[x + 1] == buf_offset) {
+ /*
+ * We subtract one so that
+ * we don't go off the last
+ * page!
+ */
+ usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res);
+ } else {
+ usb2_get_page(xfer->frbuffers, itd_offset[x + 1], &buf_res);
+ }
+
+ /* check if we need a new page */
+ if ((buf_res.physaddr ^ page_addr) & ~0xFFF) {
+ /* new page needed */
+ page_addr = buf_res.physaddr & ~0xFFF;
+ if (page_no == 6) {
+ panic("%s: too many pages\n", __FUNCTION__);
+ }
+ page_no++;
+ /* update page address */
+ td->itd_bp[page_no] &= htoehci32(sc, 0xFFF);
+ td->itd_bp[page_no] |= htoehci32(sc, page_addr);
+ }
+ }
+ }
+ /* set IOC bit if we are complete */
+ if (nframes == 0) {
+ td->itd_status[7] |= htoehci32(sc, EHCI_ITD_IOC);
+ }
+ usb2_pc_cpu_flush(td->page_cache);
+#if USB_DEBUG
+ if (ehcidebug > 15) {
+ DPRINTF("HS-TD %d\n", nframes);
+ ehci_dump_itd(sc, td);
+ }
+#endif
+ /* insert TD into schedule */
+ EHCI_APPEND_HS_TD(td, *pp_last);
+ pp_last++;
+
+ td_no = 0;
+ td_last = td;
+ td = td->obj_next;
+ }
+ }
+
+ xfer->td_transfer_last = td_last;
+
+ /* update isoc_next */
+ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) &
+ (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
+}
+
+static void
+ehci_device_isoc_hs_start(struct usb2_xfer *xfer)
+{
+ /* put transfer on interrupt queue */
+ ehci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ehci_device_isoc_hs_methods =
+{
+ .open = ehci_device_isoc_hs_open,
+ .close = ehci_device_isoc_hs_close,
+ .enter = ehci_device_isoc_hs_enter,
+ .start = ehci_device_isoc_hs_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ehci root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware hub by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+
+static void
+ehci_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/* data structures and routines
+ * to emulate the root hub:
+ */
+
+static const
+struct usb2_device_descriptor ehci_devd =
+{
+ sizeof(struct usb2_device_descriptor),
+ UDESC_DEVICE, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_HSHUBSTT, /* protocol */
+ 64, /* max packet */
+ {0}, {0}, {0x00, 0x01}, /* device id */
+ 1, 2, 0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+static const
+struct usb2_device_qualifier ehci_odevd =
+{
+ sizeof(struct usb2_device_qualifier),
+ UDESC_DEVICE_QUALIFIER, /* type */
+ {0x00, 0x02}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 0, /* max packet */
+ 0, /* # of configurations */
+ 0
+};
+
+static const struct ehci_config_desc ehci_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(ehci_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0 /* max power */
+ },
+
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_HSHUBSTT,
+ 0
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = UE_DIR_IN | EHCI_INTR_ENDPT,
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8, /* max packet (63 ports) */
+ .bInterval = 255,
+ },
+};
+
+static const
+struct usb2_hub_descriptor ehci_hubd =
+{
+ 0, /* dynamic length */
+ UDESC_HUB,
+ 0,
+ {0, 0},
+ 0,
+ 0,
+ {0},
+};
+
+static void
+ehci_disown(ehci_softc_t *sc, uint16_t index, uint8_t lowspeed)
+{
+ uint32_t port;
+ uint32_t v;
+
+ DPRINTF("index=%d lowspeed=%d\n", index, lowspeed);
+
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR;
+ EOWRITE4(sc, port, v | EHCI_PS_PO);
+}
+
+static void
+ehci_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTF("\n");
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+ehci_root_ctrl_task(struct usb2_bus *bus)
+{
+ ehci_root_ctrl_poll(EHCI_BUS2SC(bus));
+}
+
+static void
+ehci_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+ char *ptr;
+ uint32_t port;
+ uint32_t v;
+ uint16_t i;
+ uint16_t value;
+ uint16_t index;
+ uint8_t l;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ ehci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = sc->sc_hub_desc.temp;
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x "
+ "wValue=0x%04x wIndex=0x%04x\n",
+ std->req.bmRequestType, std->req.bRequest,
+ UGETW(std->req.wLength), value, index);
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(std->req.bRequest, std->req.bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] = sc->sc_conf;
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(ehci_devd);
+ sc->sc_hub_desc.devd = ehci_devd;
+ break;
+ /*
+ * We can't really operate at another speed,
+ * but the specification says we need this
+ * descriptor:
+ */
+ case UDESC_DEVICE_QUALIFIER:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(ehci_odevd);
+ sc->sc_hub_desc.odevd = ehci_odevd;
+ break;
+
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(ehci_confd);
+ std->ptr = USB_ADD_BYTES(&ehci_confd, 0);
+ break;
+
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ ptr = "\001";
+ break;
+
+ case 1: /* Vendor */
+ ptr = sc->sc_vendor;
+ break;
+
+ case 2: /* Product */
+ ptr = "EHCI root HUB";
+ break;
+
+ default:
+ ptr = "";
+ break;
+ }
+
+ std->len = usb2_make_str_desc
+ (sc->sc_hub_desc.temp,
+ sizeof(sc->sc_hub_desc.temp),
+ ptr);
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] = 0;
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ std->len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED);
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ std->len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, 0);
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if ((value != 0) && (value != 1)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n");
+
+ if ((index < 1) ||
+ (index > sc->sc_noport)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR;
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ EOWRITE4(sc, port, v & ~EHCI_PS_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) {
+
+ /*
+ * waking up a High Speed device is rather
+ * complicated if
+ */
+ EOWRITE4(sc, port, v | EHCI_PS_FPR);
+ }
+ /* wait 20ms for resume sequence to complete */
+ if (use_polling) {
+ /* polling */
+ DELAY(20000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
+ }
+
+ EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP |
+ EHCI_PS_FPR | (3 << 10) /* High Speed */ ));
+
+ /* settle time */
+ if (use_polling) {
+ /* polling */
+ DELAY(4000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250);
+ }
+ break;
+ case UHF_PORT_POWER:
+ EOWRITE4(sc, port, v & ~EHCI_PS_PP);
+ break;
+ case UHF_PORT_TEST:
+ DPRINTFN(3, "clear port test "
+ "%d\n", index);
+ break;
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(3, "clear port ind "
+ "%d\n", index);
+ EOWRITE4(sc, port, v & ~EHCI_PS_PIC);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ EOWRITE4(sc, port, v | EHCI_PS_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ EOWRITE4(sc, port, v | EHCI_PS_PEC);
+ break;
+ case UHF_C_PORT_SUSPEND:
+ EOWRITE4(sc, port, v | EHCI_PS_SUSP);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ EOWRITE4(sc, port, v | EHCI_PS_OCC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ v = EOREAD4(sc, EHCI_HCSPARAMS);
+
+ sc->sc_hub_desc.hubd = ehci_hubd;
+ sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport;
+ USETW(sc->sc_hub_desc.hubd.wHubCharacteristics,
+ (EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH) |
+ (EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ?
+ UHD_PORT_IND : 0));
+ /* XXX can't find out? */
+ sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200;
+ for (l = 0; l < sc->sc_noport; l++) {
+ /* XXX can't find out? */
+ sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] &= ~(1 << (l % 8));
+ }
+ sc->sc_hub_desc.hubd.bDescLength =
+ 8 + ((sc->sc_noport + 7) / 8);
+ std->len = sc->sc_hub_desc.hubd.bDescLength;
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ std->len = 16;
+ bzero(sc->sc_hub_desc.temp, 16);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(9, "get port status i=%d\n",
+ index);
+ if ((index < 1) ||
+ (index > sc->sc_noport)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ v = EOREAD4(sc, EHCI_PORTSC(index));
+ DPRINTFN(9, "port status=0x%04x\n", v);
+ if (sc->sc_flags & EHCI_SCFLG_FORCESPEED) {
+ if ((v & 0xc000000) == 0x8000000)
+ i = UPS_HIGH_SPEED;
+ else if ((v & 0xc000000) == 0x4000000)
+ i = UPS_LOW_SPEED;
+ else
+ i = 0;
+ } else {
+ i = UPS_HIGH_SPEED;
+ }
+ if (v & EHCI_PS_CS)
+ i |= UPS_CURRENT_CONNECT_STATUS;
+ if (v & EHCI_PS_PE)
+ i |= UPS_PORT_ENABLED;
+ if ((v & EHCI_PS_SUSP) && !(v & EHCI_PS_FPR))
+ i |= UPS_SUSPEND;
+ if (v & EHCI_PS_OCA)
+ i |= UPS_OVERCURRENT_INDICATOR;
+ if (v & EHCI_PS_PR)
+ i |= UPS_RESET;
+ if (v & EHCI_PS_PP)
+ i |= UPS_PORT_POWER;
+ USETW(sc->sc_hub_desc.ps.wPortStatus, i);
+ i = 0;
+ if (v & EHCI_PS_CSC)
+ i |= UPS_C_CONNECT_STATUS;
+ if (v & EHCI_PS_PEC)
+ i |= UPS_C_PORT_ENABLED;
+ if (v & EHCI_PS_OCC)
+ i |= UPS_C_OVERCURRENT_INDICATOR;
+ if (v & EHCI_PS_FPR)
+ i |= UPS_C_SUSPEND;
+ if (sc->sc_isreset)
+ i |= UPS_C_PORT_RESET;
+ USETW(sc->sc_hub_desc.ps.wPortChange, i);
+ std->len = sizeof(sc->sc_hub_desc.ps);
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if ((index < 1) ||
+ (index > sc->sc_noport)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ port = EHCI_PORTSC(index);
+ v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR;
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ EOWRITE4(sc, port, v | EHCI_PS_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ EOWRITE4(sc, port, v | EHCI_PS_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(6, "reset port %d\n", index);
+#if USB_DEBUG
+ if (ehcinohighspeed) {
+ /*
+ * Connect USB device to companion
+ * controller.
+ */
+ ehci_disown(sc, index, 1);
+ break;
+ }
+#endif
+ if (EHCI_PS_IS_LOWSPEED(v)) {
+ /* Low speed device, give up ownership. */
+ ehci_disown(sc, index, 1);
+ break;
+ }
+ /* Start reset sequence. */
+ v &= ~(EHCI_PS_PE | EHCI_PS_PR);
+ EOWRITE4(sc, port, v | EHCI_PS_PR);
+
+ if (use_polling) {
+ /* polling */
+ DELAY(USB_PORT_ROOT_RESET_DELAY * 1000);
+ } else {
+ /* Wait for reset to complete. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY));
+ }
+
+ /* Terminate reset sequence. */
+ if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM))
+ EOWRITE4(sc, port, v);
+
+ if (use_polling) {
+ /* polling */
+ DELAY(EHCI_PORT_RESET_COMPLETE * 1000);
+ } else {
+ /* Wait for HC to complete reset. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(EHCI_PORT_RESET_COMPLETE));
+ }
+
+ v = EOREAD4(sc, port);
+ DPRINTF("ehci after reset, status=0x%08x\n", v);
+ if (v & EHCI_PS_PR) {
+ device_printf(sc->sc_bus.bdev,
+ "port reset timeout\n");
+ std->err = USB_ERR_TIMEOUT;
+ goto done;
+ }
+ if (!(v & EHCI_PS_PE)) {
+ /*
+ * Not a high speed device, give up
+ * ownership.
+ */
+ ehci_disown(sc, index, 0);
+ break;
+ }
+ sc->sc_isreset = 1;
+ DPRINTF("ehci port %d reset, status = 0x%08x\n",
+ index, v);
+ break;
+
+ case UHF_PORT_POWER:
+ DPRINTFN(3, "set port power %d\n", index);
+ EOWRITE4(sc, port, v | EHCI_PS_PP);
+ break;
+
+ case UHF_PORT_TEST:
+ DPRINTFN(3, "set port test %d\n", index);
+ break;
+
+ case UHF_PORT_INDICATOR:
+ DPRINTFN(3, "set port ind %d\n", index);
+ EOWRITE4(sc, port, v | EHCI_PS_PIC);
+ break;
+
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
+ case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
+ case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
+ case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+done:
+ return;
+}
+
+static void
+ehci_root_ctrl_poll(ehci_softc_t *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &ehci_root_ctrl_done);
+}
+
+struct usb2_pipe_methods ehci_root_ctrl_methods =
+{
+ .open = ehci_root_ctrl_open,
+ .close = ehci_root_ctrl_close,
+ .enter = ehci_root_ctrl_enter,
+ .start = ehci_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * ehci root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+ehci_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_root_intr_close(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ ehci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ehci_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_root_intr_start(struct usb2_xfer *xfer)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+}
+
+struct usb2_pipe_methods ehci_root_intr_methods =
+{
+ .open = ehci_root_intr_open,
+ .close = ehci_root_intr_close,
+ .enter = ehci_root_intr_enter,
+ .start = ehci_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+ehci_xfer_setup(struct usb2_setup_params *parm)
+{
+ struct usb2_page_search page_info;
+ struct usb2_page_cache *pc;
+ ehci_softc_t *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t nqtd;
+ uint32_t nqh;
+ uint32_t nsitd;
+ uint32_t nitd;
+ uint32_t n;
+
+ sc = EHCI_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ nqtd = 0;
+ nqh = 0;
+ nsitd = 0;
+ nitd = 0;
+
+ /*
+ * compute maximum number of some structures
+ */
+ if (parm->methods == &ehci_device_ctrl_methods) {
+
+ /*
+ * The proof for the "nqtd" formula is illustrated like
+ * this:
+ *
+ * +------------------------------------+
+ * | |
+ * | |remainder -> |
+ * | +-----+---+ |
+ * | | xxx | x | frm 0 |
+ * | +-----+---++ |
+ * | | xxx | xx | frm 1 |
+ * | +-----+----+ |
+ * | ... |
+ * +------------------------------------+
+ *
+ * "xxx" means a completely full USB transfer descriptor
+ *
+ * "x" and "xx" means a short USB packet
+ *
+ * For the remainder of an USB transfer modulo
+ * "max_data_length" we need two USB transfer descriptors.
+ * One to transfer the remaining data and one to finalise
+ * with a zero length packet in case the "force_short_xfer"
+ * flag is set. We only need two USB transfer descriptors in
+ * the case where the transfer length of the first one is a
+ * factor of "max_frame_size". The rest of the needed USB
+ * transfer descriptors is given by the buffer size divided
+ * by the maximum data payload.
+ */
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX;
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 1;
+ nqtd = ((2 * xfer->nframes) + 1 /* STATUS */
+ + (xfer->max_data_length / xfer->max_usb2_frame_size));
+
+ } else if (parm->methods == &ehci_device_bulk_methods) {
+
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX;
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 1;
+ nqtd = ((2 * xfer->nframes)
+ + (xfer->max_data_length / xfer->max_usb2_frame_size));
+
+ } else if (parm->methods == &ehci_device_intr_methods) {
+
+ if (parm->speed == USB_SPEED_HIGH) {
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_packet_count = 3;
+ } else if (parm->speed == USB_SPEED_FULL) {
+ parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME;
+ parm->hc_max_packet_count = 1;
+ } else {
+ parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME / 8;
+ parm->hc_max_packet_count = 1;
+ }
+
+ parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX;
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 1;
+ nqtd = ((2 * xfer->nframes)
+ + (xfer->max_data_length / xfer->max_usb2_frame_size));
+
+ } else if (parm->methods == &ehci_device_isoc_fs_methods) {
+
+ parm->hc_max_packet_size = 0x3FF;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x3FF;
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nsitd = xfer->nframes;
+
+ } else if (parm->methods == &ehci_device_isoc_hs_methods) {
+
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_packet_count = 3;
+ parm->hc_max_frame_size = 0xC00;
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nitd = (xfer->nframes + 7) / 8;
+
+ } else {
+
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x400;
+
+ usb2_transfer_setup_sub(parm);
+ }
+
+alloc_dma_set:
+
+ if (parm->err) {
+ return;
+ }
+ /*
+ * Allocate queue heads and transfer descriptors
+ */
+ last_obj = NULL;
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ehci_itd_t),
+ EHCI_ITD_ALIGN, nitd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nitd; n++) {
+ ehci_itd_t *td;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ td = page_info.buffer;
+
+ /* init TD */
+ td->itd_self = htoehci32(sc, page_info.physaddr | EHCI_LINK_ITD);
+ td->obj_next = last_obj;
+ td->page_cache = pc + n;
+
+ last_obj = td;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ehci_sitd_t),
+ EHCI_SITD_ALIGN, nsitd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nsitd; n++) {
+ ehci_sitd_t *td;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ td = page_info.buffer;
+
+ /* init TD */
+ td->sitd_self = htoehci32(sc, page_info.physaddr | EHCI_LINK_SITD);
+ td->obj_next = last_obj;
+ td->page_cache = pc + n;
+
+ last_obj = td;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ehci_qtd_t),
+ EHCI_QTD_ALIGN, nqtd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nqtd; n++) {
+ ehci_qtd_t *qtd;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ qtd = page_info.buffer;
+
+ /* init TD */
+ qtd->qtd_self = htoehci32(sc, page_info.physaddr);
+ qtd->obj_next = last_obj;
+ qtd->page_cache = pc + n;
+
+ last_obj = qtd;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
+
+ last_obj = NULL;
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ehci_qh_t),
+ EHCI_QH_ALIGN, nqh)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nqh; n++) {
+ ehci_qh_t *qh;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ qh = page_info.buffer;
+
+ /* init QH */
+ qh->qh_self = htoehci32(sc, page_info.physaddr | EHCI_LINK_QH);
+ qh->obj_next = last_obj;
+ qh->page_cache = pc + n;
+
+ last_obj = qh;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj;
+
+ if (!xfer->flags_int.curr_dma_set) {
+ xfer->flags_int.curr_dma_set = 1;
+ goto alloc_dma_set;
+ }
+}
+
+static void
+ehci_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ehci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_addr);
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ if (udev->device_index == sc->sc_addr) {
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &ehci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | EHCI_INTR_ENDPT:
+ pipe->methods = &ehci_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+ if ((udev->speed != USB_SPEED_HIGH) &&
+ ((udev->hs_hub_addr == 0) ||
+ (udev->hs_port_no == 0) ||
+ (udev->bus->devices[udev->hs_hub_addr] == NULL) ||
+ (udev->bus->devices[udev->hs_hub_addr]->hub == NULL))) {
+ /* We need a transaction translator */
+ goto done;
+ }
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &ehci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &ehci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ if (udev->speed == USB_SPEED_HIGH) {
+ pipe->methods = &ehci_device_isoc_hs_methods;
+ } else if (udev->speed == USB_SPEED_FULL) {
+ pipe->methods = &ehci_device_isoc_fs_methods;
+ }
+ break;
+ case UE_BULK:
+ if (udev->speed != USB_SPEED_LOW) {
+ pipe->methods = &ehci_device_bulk_methods;
+ }
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+done:
+ return;
+}
+
+static void
+ehci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus)
+{
+ /*
+ * Wait until the hardware has finished any possible use of
+ * the transfer descriptor(s) and QH
+ */
+ *pus = (188); /* microseconds */
+}
+
+static void
+ehci_device_resume(struct usb2_device *udev)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev == udev) {
+
+ methods = xfer->pipe->methods;
+
+ if ((methods == &ehci_device_bulk_methods) ||
+ (methods == &ehci_device_ctrl_methods)) {
+ EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_async_p_last);
+ }
+ if (methods == &ehci_device_intr_methods) {
+ EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ehci_device_suspend(struct usb2_device *udev)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev == udev) {
+
+ methods = xfer->pipe->methods;
+
+ if ((methods == &ehci_device_bulk_methods) ||
+ (methods == &ehci_device_ctrl_methods)) {
+ EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_async_p_last);
+ }
+ if (methods == &ehci_device_intr_methods) {
+ EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ehci_set_hw_power(struct usb2_bus *bus)
+{
+ ehci_softc_t *sc = EHCI_BUS2SC(bus);
+ uint32_t temp;
+ uint32_t flags;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(bus);
+
+ flags = bus->hw_power_state;
+
+ temp = EOREAD4(sc, EHCI_USBCMD);
+
+ temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
+
+ if (flags & (USB_HW_POWER_CONTROL |
+ USB_HW_POWER_BULK)) {
+ DPRINTF("Async is active\n");
+ temp |= EHCI_CMD_ASE;
+ }
+ if (flags & (USB_HW_POWER_INTERRUPT |
+ USB_HW_POWER_ISOC)) {
+ DPRINTF("Periodic is active\n");
+ temp |= EHCI_CMD_PSE;
+ }
+ EOWRITE4(sc, EHCI_USBCMD, temp);
+
+ USB_BUS_UNLOCK(bus);
+
+ return;
+}
+
+struct usb2_bus_methods ehci_bus_methods =
+{
+ .pipe_init = ehci_pipe_init,
+ .xfer_setup = ehci_xfer_setup,
+ .xfer_unsetup = ehci_xfer_unsetup,
+ .do_poll = ehci_do_poll,
+ .get_dma_delay = ehci_get_dma_delay,
+ .device_resume = ehci_device_resume,
+ .device_suspend = ehci_device_suspend,
+ .set_hw_power = ehci_set_hw_power,
+ .roothub_exec = ehci_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/ehci.h b/sys/dev/usb/controller/ehci.h
new file mode 100644
index 0000000..9d7baa1
--- /dev/null
+++ b/sys/dev/usb/controller/ehci.h
@@ -0,0 +1,532 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _EHCI_H_
+#define _EHCI_H_
+
+#define EHCI_MAX_DEVICES USB_MAX_DEVICES
+
+/* PCI config registers */
+#define PCI_CBMEM 0x10 /* configuration base MEM */
+#define PCI_INTERFACE_EHCI 0x20
+#define PCI_USBREV 0x60 /* RO USB protocol revision */
+#define PCI_USB_REV_MASK 0xff
+#define PCI_USB_REV_PRE_1_0 0x00
+#define PCI_USB_REV_1_0 0x10
+#define PCI_USB_REV_1_1 0x11
+#define PCI_USB_REV_2_0 0x20
+#define PCI_EHCI_FLADJ 0x61 /* RW Frame len adj, SOF=59488+6*fladj */
+#define PCI_EHCI_PORTWAKECAP 0x62 /* RW Port wake caps (opt) */
+
+/* EHCI Extended Capabilities */
+#define EHCI_EC_LEGSUP 0x01
+#define EHCI_EECP_NEXT(x) (((x) >> 8) & 0xff)
+#define EHCI_EECP_ID(x) ((x) & 0xff)
+
+/* Legacy support extended capability */
+#define EHCI_LEGSUP_BIOS_SEM 0x02
+#define EHCI_LEGSUP_OS_SEM 0x03
+#define EHCI_LEGSUP_USBLEGCTLSTS 0x04
+
+/* EHCI capability registers */
+#define EHCI_CAPLENGTH 0x00 /* RO Capability register length field */
+/* reserved 0x01 */
+#define EHCI_HCIVERSION 0x02 /* RO Interface version number */
+#define EHCI_HCSPARAMS 0x04 /* RO Structural parameters */
+#define EHCI_HCS_DEBUGPORT(x) (((x) >> 20) & 0xf)
+#define EHCI_HCS_P_INDICATOR(x) ((x) & 0x10000)
+#define EHCI_HCS_N_CC(x) (((x) >> 12) & 0xf) /* # of companion ctlrs */
+#define EHCI_HCS_N_PCC(x) (((x) >> 8) & 0xf) /* # of ports per comp. */
+#define EHCI_HCS_PPC(x) ((x) & 0x10) /* port power control */
+#define EHCI_HCS_N_PORTS(x) ((x) & 0xf) /* # of ports */
+#define EHCI_HCCPARAMS 0x08 /* RO Capability parameters */
+#define EHCI_HCC_EECP(x) (((x) >> 8) & 0xff) /* extended ports caps */
+#define EHCI_HCC_IST(x) (((x) >> 4) & 0xf) /* isoc sched threshold */
+#define EHCI_HCC_ASPC(x) ((x) & 0x4) /* async sched park cap */
+#define EHCI_HCC_PFLF(x) ((x) & 0x2) /* prog frame list flag */
+#define EHCI_HCC_64BIT(x) ((x) & 0x1) /* 64 bit address cap */
+#define EHCI_HCSP_PORTROUTE 0x0c /* RO Companion port route description */
+
+/* EHCI operational registers. Offset given by EHCI_CAPLENGTH register */
+#define EHCI_USBCMD 0x00 /* RO, RW, WO Command register */
+#define EHCI_CMD_ITC_M 0x00ff0000 /* RW interrupt threshold ctrl */
+#define EHCI_CMD_ITC_1 0x00010000
+#define EHCI_CMD_ITC_2 0x00020000
+#define EHCI_CMD_ITC_4 0x00040000
+#define EHCI_CMD_ITC_8 0x00080000
+#define EHCI_CMD_ITC_16 0x00100000
+#define EHCI_CMD_ITC_32 0x00200000
+#define EHCI_CMD_ITC_64 0x00400000
+#define EHCI_CMD_ASPME 0x00000800 /* RW/RO async park enable */
+#define EHCI_CMD_ASPMC 0x00000300 /* RW/RO async park count */
+#define EHCI_CMD_LHCR 0x00000080 /* RW light host ctrl reset */
+#define EHCI_CMD_IAAD 0x00000040 /* RW intr on async adv door
+ * bell */
+#define EHCI_CMD_ASE 0x00000020 /* RW async sched enable */
+#define EHCI_CMD_PSE 0x00000010 /* RW periodic sched enable */
+#define EHCI_CMD_FLS_M 0x0000000c /* RW/RO frame list size */
+#define EHCI_CMD_FLS(x) (((x) >> 2) & 3) /* RW/RO frame list size */
+#define EHCI_CMD_HCRESET 0x00000002 /* RW reset */
+#define EHCI_CMD_RS 0x00000001 /* RW run/stop */
+#define EHCI_USBSTS 0x04 /* RO, RW, RWC Status register */
+#define EHCI_STS_ASS 0x00008000 /* RO async sched status */
+#define EHCI_STS_PSS 0x00004000 /* RO periodic sched status */
+#define EHCI_STS_REC 0x00002000 /* RO reclamation */
+#define EHCI_STS_HCH 0x00001000 /* RO host controller halted */
+#define EHCI_STS_IAA 0x00000020 /* RWC interrupt on async adv */
+#define EHCI_STS_HSE 0x00000010 /* RWC host system error */
+#define EHCI_STS_FLR 0x00000008 /* RWC frame list rollover */
+#define EHCI_STS_PCD 0x00000004 /* RWC port change detect */
+#define EHCI_STS_ERRINT 0x00000002 /* RWC error interrupt */
+#define EHCI_STS_INT 0x00000001 /* RWC interrupt */
+#define EHCI_STS_INTRS(x) ((x) & 0x3f)
+
+/*
+ * NOTE: the doorbell interrupt is enabled, but the doorbell is never
+ * used! SiS chipsets require this.
+ */
+#define EHCI_NORMAL_INTRS (EHCI_STS_IAA | EHCI_STS_HSE | \
+ EHCI_STS_PCD | EHCI_STS_ERRINT | EHCI_STS_INT)
+
+#define EHCI_USBINTR 0x08 /* RW Interrupt register */
+#define EHCI_INTR_IAAE 0x00000020 /* interrupt on async advance
+ * ena */
+#define EHCI_INTR_HSEE 0x00000010 /* host system error ena */
+#define EHCI_INTR_FLRE 0x00000008 /* frame list rollover ena */
+#define EHCI_INTR_PCIE 0x00000004 /* port change ena */
+#define EHCI_INTR_UEIE 0x00000002 /* USB error intr ena */
+#define EHCI_INTR_UIE 0x00000001 /* USB intr ena */
+
+#define EHCI_FRINDEX 0x0c /* RW Frame Index register */
+
+#define EHCI_CTRLDSSEGMENT 0x10 /* RW Control Data Structure Segment */
+
+#define EHCI_PERIODICLISTBASE 0x14 /* RW Periodic List Base */
+#define EHCI_ASYNCLISTADDR 0x18 /* RW Async List Base */
+
+#define EHCI_CONFIGFLAG 0x40 /* RW Configure Flag register */
+#define EHCI_CONF_CF 0x00000001 /* RW configure flag */
+
+#define EHCI_PORTSC(n) (0x40+(4*(n))) /* RO, RW, RWC Port Status reg */
+#define EHCI_PS_WKOC_E 0x00400000 /* RW wake on over current ena */
+#define EHCI_PS_WKDSCNNT_E 0x00200000 /* RW wake on disconnect ena */
+#define EHCI_PS_WKCNNT_E 0x00100000 /* RW wake on connect ena */
+#define EHCI_PS_PTC 0x000f0000 /* RW port test control */
+#define EHCI_PS_PIC 0x0000c000 /* RW port indicator control */
+#define EHCI_PS_PO 0x00002000 /* RW port owner */
+#define EHCI_PS_PP 0x00001000 /* RW,RO port power */
+#define EHCI_PS_LS 0x00000c00 /* RO line status */
+#define EHCI_PS_IS_LOWSPEED(x) (((x) & EHCI_PS_LS) == 0x00000400)
+#define EHCI_PS_PR 0x00000100 /* RW port reset */
+#define EHCI_PS_SUSP 0x00000080 /* RW suspend */
+#define EHCI_PS_FPR 0x00000040 /* RW force port resume */
+#define EHCI_PS_OCC 0x00000020 /* RWC over current change */
+#define EHCI_PS_OCA 0x00000010 /* RO over current active */
+#define EHCI_PS_PEC 0x00000008 /* RWC port enable change */
+#define EHCI_PS_PE 0x00000004 /* RW port enable */
+#define EHCI_PS_CSC 0x00000002 /* RWC connect status change */
+#define EHCI_PS_CS 0x00000001 /* RO connect status */
+#define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC)
+
+#define EHCI_USBMODE 0x68 /* RW USB Device mode register */
+#define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */
+#define EHCI_UM_CM_IDLE 0x0 /* Idle */
+#define EHCI_UM_CM_HOST 0x3 /* Host Controller */
+#define EHCI_UM_ES 0x00000004 /* R/WO Endian Select */
+#define EHCI_UM_ES_LE 0x0 /* Little-endian byte alignment */
+#define EHCI_UM_ES_BE 0x4 /* Big-endian byte alignment */
+#define EHCI_UM_SDIS 0x00000010 /* R/WO Stream Disable Mode */
+
+#define EHCI_PORT_RESET_COMPLETE 2 /* ms */
+
+/*
+ * Alignment NOTE: structures must be aligned so that the hardware can index
+ * without performing addition.
+ */
+#define EHCI_FRAMELIST_ALIGN 0x1000 /* bytes */
+#define EHCI_FRAMELIST_COUNT 1024 /* units */
+#define EHCI_VIRTUAL_FRAMELIST_COUNT 128 /* units */
+
+#if ((8*EHCI_VIRTUAL_FRAMELIST_COUNT) < USB_MAX_HS_ISOC_FRAMES_PER_XFER)
+#error "maximum number of high-speed isochronous frames is higher than supported!"
+#endif
+
+#if (EHCI_VIRTUAL_FRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER)
+#error "maximum number of full-speed isochronous frames is higher than supported!"
+#endif
+
+/* Link types */
+#define EHCI_LINK_TERMINATE 0x00000001
+#define EHCI_LINK_TYPE(x) ((x) & 0x00000006)
+#define EHCI_LINK_ITD 0x0
+#define EHCI_LINK_QH 0x2
+#define EHCI_LINK_SITD 0x4
+#define EHCI_LINK_FSTN 0x6
+#define EHCI_LINK_ADDR(x) ((x) &~ 0x1f)
+
+/* Structures alignment (bytes) */
+#define EHCI_ITD_ALIGN 128
+#define EHCI_SITD_ALIGN 64
+#define EHCI_QTD_ALIGN 64
+#define EHCI_QH_ALIGN 128
+#define EHCI_FSTN_ALIGN 32
+/* Data buffers are divided into one or more pages */
+#define EHCI_PAGE_SIZE 0x1000
+#if ((USB_PAGE_SIZE < EHCI_PAGE_SIZE) || (EHCI_PAGE_SIZE == 0) || \
+ (USB_PAGE_SIZE < EHCI_ITD_ALIGN) || (EHCI_ITD_ALIGN == 0) || \
+ (USB_PAGE_SIZE < EHCI_SITD_ALIGN) || (EHCI_SITD_ALIGN == 0) || \
+ (USB_PAGE_SIZE < EHCI_QTD_ALIGN) || (EHCI_QTD_ALIGN == 0) || \
+ (USB_PAGE_SIZE < EHCI_QH_ALIGN) || (EHCI_QH_ALIGN == 0) || \
+ (USB_PAGE_SIZE < EHCI_FSTN_ALIGN) || (EHCI_FSTN_ALIGN == 0))
+#error "Invalid USB page size!"
+#endif
+
+
+/*
+ * Isochronous Transfer Descriptor. This descriptor is used for high speed
+ * transfers only.
+ */
+struct ehci_itd {
+ volatile uint32_t itd_next;
+ volatile uint32_t itd_status[8];
+#define EHCI_ITD_SET_LEN(x) ((x) << 16)
+#define EHCI_ITD_GET_LEN(x) (((x) >> 16) & 0xFFF)
+#define EHCI_ITD_IOC (1 << 15)
+#define EHCI_ITD_SET_PG(x) ((x) << 12)
+#define EHCI_ITD_GET_PG(x) (((x) >> 12) & 0x7)
+#define EHCI_ITD_SET_OFFS(x) (x)
+#define EHCI_ITD_GET_OFFS(x) (((x) >> 0) & 0xFFF)
+#define EHCI_ITD_ACTIVE (1 << 31)
+#define EHCI_ITD_DATABUFERR (1 << 30)
+#define EHCI_ITD_BABBLE (1 << 29)
+#define EHCI_ITD_XACTERR (1 << 28)
+ volatile uint32_t itd_bp[7];
+ /* itd_bp[0] */
+#define EHCI_ITD_SET_ADDR(x) (x)
+#define EHCI_ITD_GET_ADDR(x) (((x) >> 0) & 0x7F)
+#define EHCI_ITD_SET_ENDPT(x) ((x) << 8)
+#define EHCI_ITD_GET_ENDPT(x) (((x) >> 8) & 0xF)
+ /* itd_bp[1] */
+#define EHCI_ITD_SET_DIR_IN (1 << 11)
+#define EHCI_ITD_SET_DIR_OUT (0 << 11)
+#define EHCI_ITD_SET_MPL(x) (x)
+#define EHCI_ITD_GET_MPL(x) (((x) >> 0) & 0x7FF)
+ volatile uint32_t itd_bp_hi[7];
+/*
+ * Extra information needed:
+ */
+ uint32_t itd_self;
+ struct ehci_itd *next;
+ struct ehci_itd *prev;
+ struct ehci_itd *obj_next;
+ struct usb2_page_cache *page_cache;
+} __aligned(EHCI_ITD_ALIGN);
+
+typedef struct ehci_itd ehci_itd_t;
+
+/*
+ * Split Transaction Isochronous Transfer Descriptor. This descriptor is used
+ * for full speed transfers only.
+ */
+struct ehci_sitd {
+ volatile uint32_t sitd_next;
+ volatile uint32_t sitd_portaddr;
+#define EHCI_SITD_SET_DIR_OUT (0 << 31)
+#define EHCI_SITD_SET_DIR_IN (1 << 31)
+#define EHCI_SITD_SET_ADDR(x) (x)
+#define EHCI_SITD_GET_ADDR(x) ((x) & 0x7F)
+#define EHCI_SITD_SET_ENDPT(x) ((x) << 8)
+#define EHCI_SITD_GET_ENDPT(x) (((x) >> 8) & 0xF)
+#define EHCI_SITD_GET_DIR(x) ((x) >> 31)
+#define EHCI_SITD_SET_PORT(x) ((x) << 24)
+#define EHCI_SITD_GET_PORT(x) (((x) >> 24) & 0x7F)
+#define EHCI_SITD_SET_HUBA(x) ((x) << 16)
+#define EHCI_SITD_GET_HUBA(x) (((x) >> 16) & 0x7F)
+ volatile uint32_t sitd_mask;
+#define EHCI_SITD_SET_SMASK(x) (x)
+#define EHCI_SITD_SET_CMASK(x) ((x) << 8)
+ volatile uint32_t sitd_status;
+#define EHCI_SITD_COMPLETE_SPLIT (1<<1)
+#define EHCI_SITD_START_SPLIT (0<<1)
+#define EHCI_SITD_MISSED_MICRO_FRAME (1<<2)
+#define EHCI_SITD_XACTERR (1<<3)
+#define EHCI_SITD_BABBLE (1<<4)
+#define EHCI_SITD_DATABUFERR (1<<5)
+#define EHCI_SITD_ERROR (1<<6)
+#define EHCI_SITD_ACTIVE (1<<7)
+#define EHCI_SITD_IOC (1<<31)
+#define EHCI_SITD_SET_LEN(len) ((len)<<16)
+#define EHCI_SITD_GET_LEN(x) (((x)>>16) & 0x3FF)
+ volatile uint32_t sitd_bp[2];
+ volatile uint32_t sitd_back;
+ volatile uint32_t sitd_bp_hi[2];
+/*
+ * Extra information needed:
+ */
+ uint32_t sitd_self;
+ struct ehci_sitd *next;
+ struct ehci_sitd *prev;
+ struct ehci_sitd *obj_next;
+ struct usb2_page_cache *page_cache;
+} __aligned(EHCI_SITD_ALIGN);
+
+typedef struct ehci_sitd ehci_sitd_t;
+
+/* Queue Element Transfer Descriptor */
+struct ehci_qtd {
+ volatile uint32_t qtd_next;
+ volatile uint32_t qtd_altnext;
+ volatile uint32_t qtd_status;
+#define EHCI_QTD_GET_STATUS(x) (((x) >> 0) & 0xff)
+#define EHCI_QTD_SET_STATUS(x) ((x) << 0)
+#define EHCI_QTD_ACTIVE 0x80
+#define EHCI_QTD_HALTED 0x40
+#define EHCI_QTD_BUFERR 0x20
+#define EHCI_QTD_BABBLE 0x10
+#define EHCI_QTD_XACTERR 0x08
+#define EHCI_QTD_MISSEDMICRO 0x04
+#define EHCI_QTD_SPLITXSTATE 0x02
+#define EHCI_QTD_PINGSTATE 0x01
+#define EHCI_QTD_STATERRS 0x74
+#define EHCI_QTD_GET_PID(x) (((x) >> 8) & 0x3)
+#define EHCI_QTD_SET_PID(x) ((x) << 8)
+#define EHCI_QTD_PID_OUT 0x0
+#define EHCI_QTD_PID_IN 0x1
+#define EHCI_QTD_PID_SETUP 0x2
+#define EHCI_QTD_GET_CERR(x) (((x) >> 10) & 0x3)
+#define EHCI_QTD_SET_CERR(x) ((x) << 10)
+#define EHCI_QTD_GET_C_PAGE(x) (((x) >> 12) & 0x7)
+#define EHCI_QTD_SET_C_PAGE(x) ((x) << 12)
+#define EHCI_QTD_GET_IOC(x) (((x) >> 15) & 0x1)
+#define EHCI_QTD_IOC 0x00008000
+#define EHCI_QTD_GET_BYTES(x) (((x) >> 16) & 0x7fff)
+#define EHCI_QTD_SET_BYTES(x) ((x) << 16)
+#define EHCI_QTD_GET_TOGGLE(x) (((x) >> 31) & 0x1)
+#define EHCI_QTD_SET_TOGGLE(x) ((x) << 31)
+#define EHCI_QTD_TOGGLE_MASK 0x80000000
+#define EHCI_QTD_NBUFFERS 5
+#define EHCI_QTD_PAYLOAD_MAX ((EHCI_QTD_NBUFFERS-1)*EHCI_PAGE_SIZE)
+ volatile uint32_t qtd_buffer[EHCI_QTD_NBUFFERS];
+ volatile uint32_t qtd_buffer_hi[EHCI_QTD_NBUFFERS];
+/*
+ * Extra information needed:
+ */
+ struct ehci_qtd *alt_next;
+ struct ehci_qtd *obj_next;
+ struct usb2_page_cache *page_cache;
+ uint32_t qtd_self;
+ uint16_t len;
+} __aligned(EHCI_QTD_ALIGN);
+
+typedef struct ehci_qtd ehci_qtd_t;
+
+/* Queue Head Sub Structure */
+struct ehci_qh_sub {
+ volatile uint32_t qtd_next;
+ volatile uint32_t qtd_altnext;
+ volatile uint32_t qtd_status;
+ volatile uint32_t qtd_buffer[EHCI_QTD_NBUFFERS];
+ volatile uint32_t qtd_buffer_hi[EHCI_QTD_NBUFFERS];
+} __aligned(4);
+
+/* Queue Head */
+struct ehci_qh {
+ volatile uint32_t qh_link;
+ volatile uint32_t qh_endp;
+#define EHCI_QH_GET_ADDR(x) (((x) >> 0) & 0x7f) /* endpoint addr */
+#define EHCI_QH_SET_ADDR(x) (x)
+#define EHCI_QH_ADDRMASK 0x0000007f
+#define EHCI_QH_GET_INACT(x) (((x) >> 7) & 0x01) /* inactivate on next */
+#define EHCI_QH_INACT 0x00000080
+#define EHCI_QH_GET_ENDPT(x) (((x) >> 8) & 0x0f) /* endpoint no */
+#define EHCI_QH_SET_ENDPT(x) ((x) << 8)
+#define EHCI_QH_GET_EPS(x) (((x) >> 12) & 0x03) /* endpoint speed */
+#define EHCI_QH_SET_EPS(x) ((x) << 12)
+#define EHCI_QH_SPEED_FULL 0x0
+#define EHCI_QH_SPEED_LOW 0x1
+#define EHCI_QH_SPEED_HIGH 0x2
+#define EHCI_QH_GET_DTC(x) (((x) >> 14) & 0x01) /* data toggle control */
+#define EHCI_QH_DTC 0x00004000
+#define EHCI_QH_GET_HRECL(x) (((x) >> 15) & 0x01) /* head of reclamation */
+#define EHCI_QH_HRECL 0x00008000
+#define EHCI_QH_GET_MPL(x) (((x) >> 16) & 0x7ff) /* max packet len */
+#define EHCI_QH_SET_MPL(x) ((x) << 16)
+#define EHCI_QH_MPLMASK 0x07ff0000
+#define EHCI_QH_GET_CTL(x) (((x) >> 27) & 0x01) /* control endpoint */
+#define EHCI_QH_CTL 0x08000000
+#define EHCI_QH_GET_NRL(x) (((x) >> 28) & 0x0f) /* NAK reload */
+#define EHCI_QH_SET_NRL(x) ((x) << 28)
+ volatile uint32_t qh_endphub;
+#define EHCI_QH_GET_SMASK(x) (((x) >> 0) & 0xff) /* intr sched mask */
+#define EHCI_QH_SET_SMASK(x) ((x) << 0)
+#define EHCI_QH_GET_CMASK(x) (((x) >> 8) & 0xff) /* split completion mask */
+#define EHCI_QH_SET_CMASK(x) ((x) << 8)
+#define EHCI_QH_GET_HUBA(x) (((x) >> 16) & 0x7f) /* hub address */
+#define EHCI_QH_SET_HUBA(x) ((x) << 16)
+#define EHCI_QH_GET_PORT(x) (((x) >> 23) & 0x7f) /* hub port */
+#define EHCI_QH_SET_PORT(x) ((x) << 23)
+#define EHCI_QH_GET_MULT(x) (((x) >> 30) & 0x03) /* pipe multiplier */
+#define EHCI_QH_SET_MULT(x) ((x) << 30)
+ volatile uint32_t qh_curqtd;
+ struct ehci_qh_sub qh_qtd;
+/*
+ * Extra information needed:
+ */
+ struct ehci_qh *next;
+ struct ehci_qh *prev;
+ struct ehci_qh *obj_next;
+ struct usb2_page_cache *page_cache;
+ uint32_t qh_self;
+} __aligned(EHCI_QH_ALIGN);
+
+typedef struct ehci_qh ehci_qh_t;
+
+/* Periodic Frame Span Traversal Node */
+struct ehci_fstn {
+ volatile uint32_t fstn_link;
+ volatile uint32_t fstn_back;
+} __aligned(EHCI_FSTN_ALIGN);
+
+typedef struct ehci_fstn ehci_fstn_t;
+
+struct ehci_hw_softc {
+ struct usb2_page_cache pframes_pc;
+ struct usb2_page_cache async_start_pc;
+ struct usb2_page_cache intr_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ struct usb2_page_cache isoc_hs_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ struct usb2_page_cache isoc_fs_start_pc[EHCI_VIRTUAL_FRAMELIST_COUNT];
+
+ struct usb2_page pframes_pg;
+ struct usb2_page async_start_pg;
+ struct usb2_page intr_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ struct usb2_page isoc_hs_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ struct usb2_page isoc_fs_start_pg[EHCI_VIRTUAL_FRAMELIST_COUNT];
+};
+
+struct ehci_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union ehci_hub_desc {
+ struct usb2_status stat;
+ struct usb2_port_status ps;
+ struct usb2_device_descriptor devd;
+ struct usb2_device_qualifier odevd;
+ struct usb2_hub_descriptor hubd;
+ uint8_t temp[128];
+};
+
+typedef struct ehci_softc {
+ struct ehci_hw_softc sc_hw;
+ struct usb2_bus sc_bus; /* base device */
+ struct usb2_callout sc_tmo_pcd;
+ union ehci_hub_desc sc_hub_desc;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+
+ struct usb2_device *sc_devices[EHCI_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ struct ehci_qh *sc_async_p_last;
+ struct ehci_qh *sc_intr_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ struct ehci_sitd *sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ struct ehci_itd *sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ void *sc_intr_hdl;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ uint32_t sc_eintrs;
+ uint32_t sc_cmd; /* shadow of cmd register during
+ * suspend */
+
+ uint16_t sc_intr_stat[EHCI_VIRTUAL_FRAMELIST_COUNT];
+ uint16_t sc_id_vendor; /* vendor ID for root hub */
+ uint16_t sc_flags; /* chip specific flags */
+#define EHCI_SCFLG_SETMODE 0x0001 /* set bridge mode again after init */
+#define EHCI_SCFLG_FORCESPEED 0x0002 /* force speed */
+#define EHCI_SCFLG_NORESTERM 0x0004 /* don't terminate reset sequence */
+#define EHCI_SCFLG_BIGEDESC 0x0008 /* big-endian byte order descriptors */
+#define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */
+#define EHCI_SCFLG_TT 0x0020 /* transaction translator present */
+
+ uint8_t sc_offs; /* offset to operational registers */
+ uint8_t sc_doorbell_disable; /* set on doorbell failure */
+ uint8_t sc_noport;
+ uint8_t sc_addr; /* device address */
+ uint8_t sc_conf; /* device configuration */
+ uint8_t sc_isreset;
+ uint8_t sc_hub_idata[8];
+
+ char sc_vendor[16]; /* vendor string for root hub */
+
+} ehci_softc_t;
+
+#define EREAD1(sc, a) bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (a))
+#define EREAD2(sc, a) bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (a))
+#define EREAD4(sc, a) bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (a))
+#define EWRITE1(sc, a, x) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (a), (x))
+#define EWRITE2(sc, a, x) \
+ bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (a), (x))
+#define EWRITE4(sc, a, x) \
+ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (a), (x))
+#define EOREAD1(sc, a) \
+ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a))
+#define EOREAD2(sc, a) \
+ bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a))
+#define EOREAD4(sc, a) \
+ bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a))
+#define EOWRITE1(sc, a, x) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a), (x))
+#define EOWRITE2(sc, a, x) \
+ bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a), (x))
+#define EOWRITE4(sc, a, x) \
+ bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (sc)->sc_offs+(a), (x))
+
+usb2_bus_mem_cb_t ehci_iterate_hw_softc;
+
+usb2_error_t ehci_init(ehci_softc_t *sc);
+void ehci_detach(struct ehci_softc *sc);
+void ehci_suspend(struct ehci_softc *sc);
+void ehci_resume(struct ehci_softc *sc);
+void ehci_shutdown(ehci_softc_t *sc);
+void ehci_interrupt(ehci_softc_t *sc);
+
+#endif /* _EHCI_H_ */
diff --git a/sys/dev/usb/controller/ehci_ixp4xx.c b/sys/dev/usb/controller/ehci_ixp4xx.c
new file mode 100644
index 0000000..b369d47
--- /dev/null
+++ b/sys/dev/usb/controller/ehci_ixp4xx.c
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 2008 Sam Leffler. 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 ``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.
+ */
+
+/*
+ * IXP435 attachment driver for the USB Enhanced Host Controller.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+
+#include <arm/xscale/ixp425/ixp425reg.h>
+#include <arm/xscale/ixp425/ixp425var.h>
+
+#define EHCI_VENDORID_IXP4XX 0x42fa05
+#define EHCI_HC_DEVSTR "IXP4XX Integrated USB 2.0 controller"
+
+struct ixp_ehci_softc {
+ ehci_softc_t base; /* storage for EHCI code */
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ struct bus_space tag; /* tag for private bus space ops */
+};
+
+static device_attach_t ehci_ixp_attach;
+static device_detach_t ehci_ixp_detach;
+static device_shutdown_t ehci_ixp_shutdown;
+static device_suspend_t ehci_ixp_suspend;
+static device_resume_t ehci_ixp_resume;
+
+static uint8_t ehci_bs_r_1(void *, bus_space_handle_t, bus_size_t);
+static void ehci_bs_w_1(void *, bus_space_handle_t, bus_size_t, u_int8_t);
+static uint16_t ehci_bs_r_2(void *, bus_space_handle_t, bus_size_t);
+static void ehci_bs_w_2(void *, bus_space_handle_t, bus_size_t, uint16_t);
+static uint32_t ehci_bs_r_4(void *, bus_space_handle_t, bus_size_t);
+static void ehci_bs_w_4(void *, bus_space_handle_t, bus_size_t, uint32_t);
+
+static int
+ehci_ixp_suspend(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return (err);
+ ehci_suspend(sc);
+ return (0);
+}
+
+static int
+ehci_ixp_resume(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ ehci_resume(sc);
+
+ bus_generic_resume(self);
+
+ return (0);
+}
+
+static int
+ehci_ixp_shutdown(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_shutdown(self);
+ if (err)
+ return (err);
+ ehci_shutdown(sc);
+
+ return (0);
+}
+
+static int
+ehci_ixp_probe(device_t self)
+{
+
+ device_set_desc(self, EHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ehci_ixp_attach(device_t self)
+{
+ struct ixp_ehci_softc *isc = device_get_softc(self);
+ ehci_softc_t *sc = &isc->base;
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ sc->sc_bus.usbrev = USB_REV_2_0;
+
+ /* NB: hints fix the memory location and irq */
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+
+ /*
+ * Craft special resource for bus space ops that handle
+ * byte-alignment of non-word addresses. Also, since
+ * we're already intercepting bus space ops we handle
+ * the register window offset that could otherwise be
+ * done with bus_space_subregion.
+ */
+ isc->iot = rman_get_bustag(sc->sc_io_res);
+ isc->tag.bs_cookie = isc->iot;
+ /* read single */
+ isc->tag.bs_r_1 = ehci_bs_r_1,
+ isc->tag.bs_r_2 = ehci_bs_r_2,
+ isc->tag.bs_r_4 = ehci_bs_r_4,
+ /* write (single) */
+ isc->tag.bs_w_1 = ehci_bs_w_1,
+ isc->tag.bs_w_2 = ehci_bs_w_2,
+ isc->tag.bs_w_4 = ehci_bs_w_4,
+
+ sc->sc_io_tag = &isc->tag;
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = IXP435_USB1_SIZE - 0x100;
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
+
+ sprintf(sc->sc_vendor, "Intel");
+
+
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ /*
+ * Arrange to force Host mode, select big-endian byte alignment,
+ * and arrange to not terminate reset operations (the adapter
+ * will ignore it if we do but might as well save a reg write).
+ * Also, the controller has an embedded Transaction Translator
+ * which means port speed must be read from the Port Status
+ * register following a port enable.
+ */
+ sc->sc_flags |= EHCI_SCFLG_TT
+ | EHCI_SCFLG_SETMODE
+ | EHCI_SCFLG_BIGEDESC
+ | EHCI_SCFLG_BIGEMMIO
+ | EHCI_SCFLG_NORESTERM
+ ;
+
+ err = ehci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ ehci_ixp_detach(self);
+ return (ENXIO);
+}
+
+static int
+ehci_ixp_detach(device_t self)
+{
+ struct ixp_ehci_softc *isc = device_get_softc(self);
+ ehci_softc_t *sc = &isc->base;
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(self);
+
+ /*
+ * disable interrupts that might have been switched on in ehci_init
+ */
+ if (sc->sc_io_res) {
+ EWRITE4(sc, EHCI_USBINTR, 0);
+ }
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ return (0);
+}
+
+/*
+ * Bus space accessors for PIO operations.
+ */
+
+static uint8_t
+ehci_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o)
+{
+ return bus_space_read_1((bus_space_tag_t) t, h,
+ 0x100 + (o &~ 3) + (3 - (o & 3)));
+}
+
+static void
+ehci_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
+{
+ panic("%s", __func__);
+}
+
+static uint16_t
+ehci_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o)
+{
+ return bus_space_read_2((bus_space_tag_t) t, h,
+ 0x100 + (o &~ 3) + (2 - (o & 3)));
+}
+
+static void
+ehci_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v)
+{
+ panic("%s", __func__);
+}
+
+static uint32_t
+ehci_bs_r_4(void *t, bus_space_handle_t h, bus_size_t o)
+{
+ return bus_space_read_4((bus_space_tag_t) t, h, 0x100 + o);
+}
+
+static void
+ehci_bs_w_4(void *t, bus_space_handle_t h, bus_size_t o, uint32_t v)
+{
+ bus_space_write_4((bus_space_tag_t) t, h, 0x100 + o, v);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_ixp_probe),
+ DEVMETHOD(device_attach, ehci_ixp_attach),
+ DEVMETHOD(device_detach, ehci_ixp_detach),
+ DEVMETHOD(device_suspend, ehci_ixp_suspend),
+ DEVMETHOD(device_resume, ehci_ixp_resume),
+ DEVMETHOD(device_shutdown, ehci_ixp_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(struct ixp_ehci_softc),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, ixp, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/ehci_mbus.c b/sys/dev/usb/controller/ehci_mbus.c
new file mode 100644
index 0000000..66cf8cc
--- /dev/null
+++ b/sys/dev/usb/controller/ehci_mbus.c
@@ -0,0 +1,364 @@
+/*-
+ * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * All rights reserved.
+ *
+ * Developed by Semihalf.
+ *
+ * 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. Neither the name of MARVELL nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY 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 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.
+ */
+
+/*
+ * MBus attachment driver for the USB Enhanced Host Controller.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_bus.h"
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ehci.h>
+
+#include <arm/mv/mvreg.h>
+#include <arm/mv/mvvar.h>
+
+#define EHCI_VENDORID_MRVL 0x1286
+#define EHCI_HC_DEVSTR "Marvell Integrated USB 2.0 controller"
+
+static device_attach_t ehci_mbus_attach;
+static device_detach_t ehci_mbus_detach;
+static device_shutdown_t ehci_mbus_shutdown;
+static device_suspend_t ehci_mbus_suspend;
+static device_resume_t ehci_mbus_resume;
+
+static int err_intr(void *arg);
+
+static struct resource *irq_err;
+static void *ih_err;
+
+#define USB_BRIDGE_INTR_CAUSE 0x210
+#define USB_BRIDGE_INTR_MASK 0x214
+
+#define MV_USB_ADDR_DECODE_ERR (1 << 0)
+#define MV_USB_HOST_UNDERFLOW (1 << 1)
+#define MV_USB_HOST_OVERFLOW (1 << 2)
+#define MV_USB_DEVICE_UNDERFLOW (1 << 3)
+
+static int
+ehci_mbus_suspend(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return (err);
+ ehci_suspend(sc);
+ return (0);
+}
+
+static int
+ehci_mbus_resume(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ ehci_resume(sc);
+
+ bus_generic_resume(self);
+
+ return (0);
+}
+
+static int
+ehci_mbus_shutdown(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_shutdown(self);
+ if (err)
+ return (err);
+ ehci_shutdown(sc);
+
+ return (0);
+}
+
+static int
+ehci_mbus_probe(device_t self)
+{
+
+ device_set_desc(self, EHCI_HC_DEVSTR);
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ehci_mbus_attach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ bus_space_handle_t bsh;
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ sc->sc_bus.usbrev = USB_REV_2_0;
+
+ rid = 0;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ bsh = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = MV_USB_SIZE - MV_USB_HOST_OFST;
+
+ /*
+ * Marvell EHCI host controller registers start at certain offset within
+ * the whole USB registers range, so create a subregion for the host
+ * mode configuration purposes.
+ */
+ if (bus_space_subregion(sc->sc_io_tag, bsh, MV_USB_HOST_OFST,
+ sc->sc_io_size, &sc->sc_io_hdl) != 0)
+ panic("%s: unable to subregion USB host registers",
+ device_get_name(self));
+
+ rid = 0;
+ irq_err = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (irq_err == NULL) {
+ device_printf(self, "Could not allocate error irq\n");
+ ehci_mbus_detach(self);
+ return (ENXIO);
+ }
+
+ /*
+ * Notice: Marvell EHCI controller has TWO interrupt lines, so make sure to
+ * use the correct rid for the main one (controller interrupt) --
+ * refer to obio_devices[] for the right resource number to use here.
+ */
+ rid = 1;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+ device_set_desc(sc->sc_bus.bdev, EHCI_HC_DEVSTR);
+
+ sprintf(sc->sc_vendor, "Marvell");
+
+ err = bus_setup_intr(self, irq_err, INTR_FAST | INTR_TYPE_BIO,
+ err_intr, NULL, sc, &ih_err);
+ if (err) {
+ device_printf(self, "Could not setup error irq, %d\n", err);
+ ih_err = NULL;
+ goto error;
+ }
+
+ EWRITE4(sc, USB_BRIDGE_INTR_MASK, MV_USB_ADDR_DECODE_ERR |
+ MV_USB_HOST_UNDERFLOW | MV_USB_HOST_OVERFLOW |
+ MV_USB_DEVICE_UNDERFLOW);
+
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+
+ /*
+ * Workaround for Marvell integrated EHCI controller: reset of
+ * the EHCI core clears the USBMODE register, which sets the core in
+ * an undefined state (neither host nor agent), so it needs to be set
+ * again for proper operation.
+ *
+ * Refer to errata document MV-S500832-00D.pdf (p. 5.24 GL USB-2) for
+ * details.
+ */
+ sc->sc_flags |= EHCI_SCFLG_SETMODE;
+ if (bootverbose)
+ device_printf(self, "5.24 GL USB-2 workaround enabled\n");
+
+ /* XXX all MV chips need it? */
+ sc->sc_flags |= EHCI_SCFLG_FORCESPEED | EHCI_SCFLG_NORESTERM;
+
+ err = ehci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ ehci_mbus_detach(self);
+ return (ENXIO);
+}
+
+static int
+ehci_mbus_detach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(self);
+
+ /*
+ * disable interrupts that might have been switched on in ehci_init
+ */
+ if (sc->sc_io_res) {
+ EWRITE4(sc, EHCI_USBINTR, 0);
+ EWRITE4(sc, USB_BRIDGE_INTR_MASK, 0);
+ }
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (irq_err && ih_err) {
+ err = bus_teardown_intr(self, irq_err, ih_err);
+
+ if (err)
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ ih_err = NULL;
+ }
+ if (irq_err) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, irq_err);
+ irq_err = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 1, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ return (0);
+}
+
+static int
+err_intr(void *arg)
+{
+ ehci_softc_t *sc = arg;
+ unsigned int cause;
+
+ cause = EREAD4(sc, USB_BRIDGE_INTR_CAUSE);
+ if (cause) {
+ printf("IRQ ERR: cause: 0x%08x\n", cause);
+ if (cause & MV_USB_ADDR_DECODE_ERR)
+ printf("IRQ ERR: Address decoding error\n");
+ if (cause & MV_USB_HOST_UNDERFLOW)
+ printf("IRQ ERR: USB Host Underflow\n");
+ if (cause & MV_USB_HOST_OVERFLOW)
+ printf("IRQ ERR: USB Host Overflow\n");
+ if (cause & MV_USB_DEVICE_UNDERFLOW)
+ printf("IRQ ERR: USB Device Underflow\n");
+ if (cause & ~(MV_USB_ADDR_DECODE_ERR | MV_USB_HOST_UNDERFLOW |
+ MV_USB_HOST_OVERFLOW | MV_USB_DEVICE_UNDERFLOW))
+ printf("IRQ ERR: Unknown error\n");
+
+ EWRITE4(sc, USB_BRIDGE_INTR_CAUSE, 0);
+ }
+ return (FILTER_HANDLED);
+}
+
+static device_method_t ehci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ehci_mbus_probe),
+ DEVMETHOD(device_attach, ehci_mbus_attach),
+ DEVMETHOD(device_detach, ehci_mbus_detach),
+ DEVMETHOD(device_suspend, ehci_mbus_suspend),
+ DEVMETHOD(device_resume, ehci_mbus_resume),
+ DEVMETHOD(device_shutdown, ehci_mbus_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ehci_driver = {
+ "ehci",
+ ehci_methods,
+ sizeof(ehci_softc_t),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, mbus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/ehci_pci.c b/sys/dev/usb/controller/ehci_pci.c
new file mode 100644
index 0000000..856ea68
--- /dev/null
+++ b/sys/dev/usb/controller/ehci_pci.c
@@ -0,0 +1,486 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
+ *
+ * The EHCI 1.0 spec can be found at
+ * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
+ * and the USB 2.0 spec at
+ * http://www.usb.org/developers/docs/usb_20.zip
+ */
+
+/* The low level controller code for EHCI has been split into
+ * PCI probes and EHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/usb_pci.h>
+#include <dev/usb/controller/ehci.h>
+
+#define PCI_EHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_EHCI_VENDORID_AMD 0x1022
+#define PCI_EHCI_VENDORID_APPLE 0x106b
+#define PCI_EHCI_VENDORID_ATI 0x1002
+#define PCI_EHCI_VENDORID_CMDTECH 0x1095
+#define PCI_EHCI_VENDORID_INTEL 0x8086
+#define PCI_EHCI_VENDORID_NEC 0x1033
+#define PCI_EHCI_VENDORID_OPTI 0x1045
+#define PCI_EHCI_VENDORID_PHILIPS 0x1131
+#define PCI_EHCI_VENDORID_SIS 0x1039
+#define PCI_EHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_EHCI_VENDORID_NVIDIA2 0x10DE
+#define PCI_EHCI_VENDORID_VIA 0x1106
+
+#define PCI_EHCI_BASE_REG 0x10
+
+static void ehci_pci_takecontroller(device_t self);
+
+static device_probe_t ehci_pci_probe;
+static device_attach_t ehci_pci_attach;
+static device_detach_t ehci_pci_detach;
+static device_suspend_t ehci_pci_suspend;
+static device_resume_t ehci_pci_resume;
+static device_shutdown_t ehci_pci_shutdown;
+
+static int
+ehci_pci_suspend(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err)
+ return (err);
+ ehci_suspend(sc);
+ return (0);
+}
+
+static int
+ehci_pci_resume(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+
+ ehci_pci_takecontroller(self);
+ ehci_resume(sc);
+
+ bus_generic_resume(self);
+
+ return (0);
+}
+
+static int
+ehci_pci_shutdown(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_shutdown(self);
+ if (err)
+ return (err);
+ ehci_shutdown(sc);
+
+ return (0);
+}
+
+static const char *
+ehci_pci_match(device_t self)
+{
+ uint32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case 0x268c8086:
+ return ("Intel 63XXESB USB 2.0 controller");
+
+ case 0x523910b9:
+ return "ALi M5239 USB 2.0 controller";
+
+ case 0x10227463:
+ return "AMD 8111 USB 2.0 controller";
+
+ case 0x20951022:
+ return ("AMD CS5536 (Geode) USB 2.0 controller");
+
+ case 0x43451002:
+ return "ATI SB200 USB 2.0 controller";
+ case 0x43731002:
+ return "ATI SB400 USB 2.0 controller";
+
+ case 0x25ad8086:
+ return "Intel 6300ESB USB 2.0 controller";
+ case 0x24cd8086:
+ return "Intel 82801DB/L/M (ICH4) USB 2.0 controller";
+ case 0x24dd8086:
+ return "Intel 82801EB/R (ICH5) USB 2.0 controller";
+ case 0x265c8086:
+ return "Intel 82801FB (ICH6) USB 2.0 controller";
+ case 0x27cc8086:
+ return "Intel 82801GB/R (ICH7) USB 2.0 controller";
+
+ case 0x28368086:
+ return "Intel 82801H (ICH8) USB 2.0 controller USB2-A";
+ case 0x283a8086:
+ return "Intel 82801H (ICH8) USB 2.0 controller USB2-B";
+ case 0x293a8086:
+ return "Intel 82801I (ICH9) USB 2.0 controller";
+ case 0x293c8086:
+ return "Intel 82801I (ICH9) USB 2.0 controller";
+
+ case 0x00e01033:
+ return ("NEC uPD 720100 USB 2.0 controller");
+
+ case 0x006810de:
+ return "NVIDIA nForce2 USB 2.0 controller";
+ case 0x008810de:
+ return "NVIDIA nForce2 Ultra 400 USB 2.0 controller";
+ case 0x00d810de:
+ return "NVIDIA nForce3 USB 2.0 controller";
+ case 0x00e810de:
+ return "NVIDIA nForce3 250 USB 2.0 controller";
+ case 0x005b10de:
+ return "NVIDIA nForce4 USB 2.0 controller";
+
+ case 0x15621131:
+ return "Philips ISP156x USB 2.0 controller";
+
+ case 0x31041106:
+ return ("VIA VT6202 USB 2.0 controller");
+
+ default:
+ break;
+ }
+
+ if ((pci_get_class(self) == PCIC_SERIALBUS)
+ && (pci_get_subclass(self) == PCIS_SERIALBUS_USB)
+ && (pci_get_progif(self) == PCI_INTERFACE_EHCI)) {
+ return ("EHCI (generic) USB 2.0 controller");
+ }
+ return (NULL); /* dunno */
+}
+
+static int
+ehci_pci_probe(device_t self)
+{
+ const char *desc = ehci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return (0);
+ } else {
+ return (ENXIO);
+ }
+}
+
+static int
+ehci_pci_attach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(self), &ehci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+
+ pci_enable_busmaster(self);
+
+ switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) {
+ case PCI_USB_REV_PRE_1_0:
+ case PCI_USB_REV_1_0:
+ case PCI_USB_REV_1_1:
+ /*
+ * NOTE: some EHCI USB controllers have the wrong USB
+ * revision number. It appears those controllers are
+ * fully compliant so we just ignore this value in
+ * some common cases.
+ */
+ device_printf(self, "pre-2.0 USB revision (ignored)\n");
+ /* fallthrough */
+ case PCI_USB_REV_2_0:
+ sc->sc_bus.usbrev = USB_REV_2_0;
+ break;
+ default:
+ /* Quirk for Parallels Desktop 4.0 */
+ device_printf(self, "USB revision is unknown. Assuming v2.0.\n");
+ sc->sc_bus.usbrev = USB_REV_2_0;
+ break;
+ }
+
+ rid = PCI_CBMEM;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /*
+ * ehci_pci_match will never return NULL if ehci_pci_probe
+ * succeeded
+ */
+ device_set_desc(sc->sc_bus.bdev, ehci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_EHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_EHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_EHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_EHCI_VENDORID_ATI:
+ sprintf(sc->sc_vendor, "ATI");
+ break;
+ case PCI_EHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_EHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_EHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_EHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_EHCI_VENDORID_PHILIPS:
+ sprintf(sc->sc_vendor, "Philips");
+ break;
+ case PCI_EHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ case PCI_EHCI_VENDORID_NVIDIA:
+ case PCI_EHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ case PCI_EHCI_VENDORID_VIA:
+ sprintf(sc->sc_vendor, "VIA");
+ break;
+ default:
+ if (bootverbose)
+ device_printf(self, "(New EHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+#else
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)(void *)ehci_interrupt, sc, &sc->sc_intr_hdl);
+#endif
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ ehci_pci_takecontroller(self);
+ err = ehci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed err=%d\n", err);
+ goto error;
+ }
+ return (0);
+
+error:
+ ehci_pci_detach(self);
+ return (ENXIO);
+}
+
+static int
+ehci_pci_detach(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ device_t bdev;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(self);
+
+ pci_disable_busmaster(self);
+
+ /*
+ * disable interrupts that might have been switched on in ehci_init
+ */
+ if (sc->sc_io_res) {
+ EWRITE4(sc, EHCI_USBINTR, 0);
+ }
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ehci_detach() after ehci_init()
+ */
+ ehci_detach(sc);
+
+ int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err)
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc);
+
+ return (0);
+}
+
+static void
+ehci_pci_takecontroller(device_t self)
+{
+ ehci_softc_t *sc = device_get_softc(self);
+ uint32_t cparams;
+ uint32_t eec;
+ uint16_t to;
+ uint8_t eecp;
+ uint8_t bios_sem;
+
+ cparams = EREAD4(sc, EHCI_HCCPARAMS);
+
+ /* Synchronise with the BIOS if it owns the controller. */
+ for (eecp = EHCI_HCC_EECP(cparams); eecp != 0;
+ eecp = EHCI_EECP_NEXT(eec)) {
+ eec = pci_read_config(self, eecp, 4);
+ if (EHCI_EECP_ID(eec) != EHCI_EC_LEGSUP) {
+ continue;
+ }
+ bios_sem = pci_read_config(self, eecp +
+ EHCI_LEGSUP_BIOS_SEM, 1);
+ if (bios_sem == 0) {
+ continue;
+ }
+ device_printf(sc->sc_bus.bdev, "waiting for BIOS "
+ "to give up control\n");
+ pci_write_config(self, eecp +
+ EHCI_LEGSUP_OS_SEM, 1, 1);
+ to = 500;
+ while (1) {
+ bios_sem = pci_read_config(self, eecp +
+ EHCI_LEGSUP_BIOS_SEM, 1);
+ if (bios_sem == 0)
+ break;
+
+ if (--to == 0) {
+ device_printf(sc->sc_bus.bdev,
+ "timed out waiting for BIOS\n");
+ break;
+ }
+ usb2_pause_mtx(NULL, hz / 100); /* wait 10ms */
+ }
+ }
+}
+
+static driver_t ehci_driver =
+{
+ .name = "ehci",
+ .methods = (device_method_t[]){
+ /* device interface */
+ DEVMETHOD(device_probe, ehci_pci_probe),
+ DEVMETHOD(device_attach, ehci_pci_attach),
+ DEVMETHOD(device_detach, ehci_pci_detach),
+ DEVMETHOD(device_suspend, ehci_pci_suspend),
+ DEVMETHOD(device_resume, ehci_pci_resume),
+ DEVMETHOD(device_shutdown, ehci_pci_shutdown),
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+ },
+ .size = sizeof(struct ehci_softc),
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, pci, ehci_driver, ehci_devclass, 0, 0);
+DRIVER_MODULE(ehci, cardbus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c
new file mode 100644
index 0000000..2194756
--- /dev/null
+++ b/sys/dev/usb/controller/musb_otg.c
@@ -0,0 +1,2875 @@
+/* $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.
+ */
+
+/*
+ * Thanks to Mentor Graphics for providing a reference driver for this
+ * USB chip at their homepage.
+ */
+
+/*
+ * This file contains the driver for the Mentor Graphics Inventra USB
+ * 2.0 High Speed Dual-Role controller.
+ *
+ * NOTE: The current implementation only supports Device Side Mode!
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR musbotgdebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/musb_otg.h>
+
+#define MUSBOTG_INTR_ENDPT 1
+
+#define MUSBOTG_BUS2SC(bus) \
+ ((struct musbotg_softc *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((struct musbotg_softc *)0)->sc_bus))))
+
+#define MUSBOTG_PC2SC(pc) \
+ MUSBOTG_BUS2SC((pc)->tag_parent->info->bus)
+
+#if USB_DEBUG
+static int musbotgdebug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, musbotg, CTLFLAG_RW, 0, "USB musbotg");
+SYSCTL_INT(_hw_usb2_musbotg, OID_AUTO, debug, CTLFLAG_RW,
+ &musbotgdebug, 0, "Debug level");
+#endif
+
+/* prototypes */
+
+struct usb2_bus_methods musbotg_bus_methods;
+struct usb2_pipe_methods musbotg_device_bulk_methods;
+struct usb2_pipe_methods musbotg_device_ctrl_methods;
+struct usb2_pipe_methods musbotg_device_intr_methods;
+struct usb2_pipe_methods musbotg_device_isoc_methods;
+struct usb2_pipe_methods musbotg_root_ctrl_methods;
+struct usb2_pipe_methods musbotg_root_intr_methods;
+
+static musbotg_cmd_t musbotg_setup_rx;
+static musbotg_cmd_t musbotg_setup_data_rx;
+static musbotg_cmd_t musbotg_setup_data_tx;
+static musbotg_cmd_t musbotg_setup_status;
+static musbotg_cmd_t musbotg_data_rx;
+static musbotg_cmd_t musbotg_data_tx;
+static void musbotg_device_done(struct usb2_xfer *, usb2_error_t);
+static void musbotg_do_poll(struct usb2_bus *);
+static void musbotg_root_ctrl_poll(struct musbotg_softc *);
+static void musbotg_standard_done(struct usb2_xfer *);
+static void musbotg_interrupt_poll(struct musbotg_softc *);
+
+static usb2_sw_transfer_func_t musbotg_root_intr_done;
+static usb2_sw_transfer_func_t musbotg_root_ctrl_done;
+
+/*
+ * Here is a configuration that the chip supports.
+ */
+static const struct usb2_hw_ep_profile musbotg_ep_profile[1] = {
+
+ [0] = {
+ .max_in_frame_size = 64,/* fixed */
+ .max_out_frame_size = 64, /* fixed */
+ .is_simplex = 1,
+ .support_control = 1,
+ }
+};
+
+static void
+musbotg_get_hw_ep_profile(struct usb2_device *udev,
+ const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+ struct musbotg_softc *sc;
+
+ sc = MUSBOTG_BUS2SC(udev->bus);
+
+ if (ep_addr == 0) {
+ /* control endpoint */
+ *ppf = musbotg_ep_profile;
+ } else if (ep_addr <= sc->sc_ep_max) {
+ /* other endpoints */
+ *ppf = sc->sc_hw_ep_profile + ep_addr;
+ } else {
+ *ppf = NULL;
+ }
+}
+
+static void
+musbotg_clocks_on(struct musbotg_softc *sc)
+{
+ if (sc->sc_flags.clocks_off &&
+ sc->sc_flags.port_powered) {
+
+ DPRINTFN(4, "\n");
+
+ if (sc->sc_clocks_on) {
+ (sc->sc_clocks_on) (sc->sc_clocks_arg);
+ }
+ sc->sc_flags.clocks_off = 0;
+
+ /* XXX enable Transceiver */
+ }
+}
+
+static void
+musbotg_clocks_off(struct musbotg_softc *sc)
+{
+ if (!sc->sc_flags.clocks_off) {
+
+ DPRINTFN(4, "\n");
+
+ /* XXX disable Transceiver */
+
+ if (sc->sc_clocks_off) {
+ (sc->sc_clocks_off) (sc->sc_clocks_arg);
+ }
+ sc->sc_flags.clocks_off = 1;
+ }
+}
+
+static void
+musbotg_pull_common(struct musbotg_softc *sc, uint8_t on)
+{
+ uint8_t temp;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ if (on)
+ temp |= MUSB2_MASK_SOFTC;
+ else
+ temp &= ~MUSB2_MASK_SOFTC;
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+}
+
+static void
+musbotg_pull_up(struct musbotg_softc *sc)
+{
+ /* pullup D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up &&
+ sc->sc_flags.port_powered) {
+ sc->sc_flags.d_pulled_up = 1;
+ musbotg_pull_common(sc, 1);
+ }
+}
+
+static void
+musbotg_pull_down(struct musbotg_softc *sc)
+{
+ /* pulldown D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ sc->sc_flags.d_pulled_up = 0;
+ musbotg_pull_common(sc, 0);
+ }
+}
+
+static void
+musbotg_wakeup_peer(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ uint8_t temp;
+ uint8_t use_polling;
+
+ if (!(sc->sc_flags.status_suspend)) {
+ return;
+ }
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp |= MUSB2_MASK_RESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+
+ /* wait 8 milliseconds */
+ if (use_polling) {
+ /* polling */
+ DELAY(8000);
+ } else {
+ /* Wait for reset to complete. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 125);
+ }
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ temp &= ~MUSB2_MASK_RESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER, temp);
+}
+
+static void
+musbotg_set_address(struct musbotg_softc *sc, uint8_t addr)
+{
+ DPRINTFN(4, "addr=%d\n", addr);
+ addr &= 0x7F;
+ MUSB2_WRITE_1(sc, MUSB2_REG_FADDR, addr);
+}
+
+static uint8_t
+musbotg_setup_rx(struct musbotg_td *td)
+{
+ struct musbotg_softc *sc;
+ struct usb2_device_request req;
+ uint16_t count;
+ uint8_t csr;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ /*
+ * NOTE: If DATAEND is set we should not call the
+ * callback, hence the status stage is not complete.
+ */
+ if (csr & MUSB2_MASK_CSR0L_DATAEND) {
+ /* wait for interrupt */
+ goto not_complete;
+ }
+ if (csr & MUSB2_MASK_CSR0L_SENTSTALL) {
+ /* clear SENTSTALL */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ /* get latest status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ /* update EP0 state */
+ sc->sc_ep0_busy = 0;
+ }
+ if (csr & MUSB2_MASK_CSR0L_SETUPEND) {
+ /* clear SETUPEND */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_SETUPEND_CLR);
+ /* get latest status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ /* update EP0 state */
+ sc->sc_ep0_busy = 0;
+ }
+ if (sc->sc_ep0_busy) {
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(4, "stalling\n");
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_SENDSTALL);
+ td->did_stall = 1;
+ }
+ goto not_complete;
+ }
+ if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) {
+ goto not_complete;
+ }
+ /* get the packet byte count */
+ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(0, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(0, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ /* receive data */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), (void *)&req, sizeof(req));
+
+ /* copy data into real buffer */
+ usb2_copy_in(td->pc, 0, &req, sizeof(req));
+
+ td->offset = sizeof(req);
+ td->remainder = 0;
+
+ /* set pending command */
+ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR;
+
+ /* we need set stall or dataend after this */
+ sc->sc_ep0_busy = 1;
+
+ /* sneak peek the set address */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+ return (0); /* complete */
+
+not_complete:
+ return (1); /* not complete */
+}
+
+/* Control endpoint only data handling functions (RX/TX/SYNC) */
+
+static uint8_t
+musbotg_setup_data_rx(struct musbotg_td *td)
+{
+ struct usb2_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+ uint8_t got_short;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* check if a command is pending */
+ if (sc->sc_ep0_cmd) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 0;
+ }
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ got_short = 0;
+
+ if (csr & (MUSB2_MASK_CSR0L_SETUPEND |
+ MUSB2_MASK_CSR0L_SENTSTALL)) {
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP
+ */
+ DPRINTFN(4, "faking complete\n");
+ return (0); /* complete */
+ }
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (!(csr & MUSB2_MASK_CSR0L_RXPKTRDY)) {
+ return (1); /* not complete */
+ }
+ /* get the packet byte count */
+ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+ /* verify the packet byte count */
+ if (count != td->max_frame_size) {
+ if (count < td->max_frame_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ uint32_t temp;
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf,
+ temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0),
+ (void *)(&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ usb2_copy_in(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* receive data */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_RXPKTRDY_CLR;
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+ /* write command - need more data */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_RXPKTRDY_CLR);
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_setup_data_tx(struct musbotg_td *td)
+{
+ struct usb2_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* check if a command is pending */
+ if (sc->sc_ep0_cmd) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 0;
+ }
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (csr & (MUSB2_MASK_CSR0L_SETUPEND |
+ MUSB2_MASK_CSR0L_SENTSTALL)) {
+ /*
+ * The current transfer was aborted
+ * by the USB Host
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (csr & MUSB2_MASK_CSR0L_TXPKTRDY) {
+ return (1); /* not complete */
+ }
+ count = td->max_frame_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ while (count > 0) {
+ uint32_t temp;
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ usb2_copy_out(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), sc->sc_bounce_buf,
+ temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0),
+ ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* transmit data */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(0), buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ sc->sc_ep0_cmd = MUSB2_MASK_CSR0L_TXPKTRDY;
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSR0L_TXPKTRDY);
+
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_setup_status(struct musbotg_td *td)
+{
+ struct musbotg_softc *sc;
+ uint8_t csr;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint 0 */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ if (sc->sc_ep0_busy) {
+ sc->sc_ep0_busy = 0;
+ sc->sc_ep0_cmd |= MUSB2_MASK_CSR0L_DATAEND;
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, sc->sc_ep0_cmd);
+ sc->sc_ep0_cmd = 0;
+ }
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (csr & MUSB2_MASK_CSR0L_DATAEND) {
+ /* wait for interrupt */
+ return (1); /* not complete */
+ }
+ if (sc->sc_dv_addr != 0xFF) {
+ /* write function address */
+ musbotg_set_address(sc, sc->sc_dv_addr);
+ }
+ return (0); /* complete */
+}
+
+static uint8_t
+musbotg_data_rx(struct musbotg_td *td)
+{
+ struct usb2_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 8; /* don't loop forever! */
+ got_short = 0;
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no);
+
+repeat:
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ /* clear overrun */
+ if (csr & MUSB2_MASK_CSRL_RXOVERRUN) {
+ /* make sure we don't clear "RXPKTRDY" */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXPKTRDY);
+ }
+ /* check status */
+ if (!(csr & MUSB2_MASK_CSRL_RXPKTRDY)) {
+ return (1); /* not complete */
+ }
+ /* get the packet byte count */
+ count = MUSB2_READ_2(sc, MUSB2_REG_RXCOUNT);
+
+ DPRINTFN(4, "count=0x%04x\n", count);
+
+ /*
+ * Check for short or invalid packet:
+ */
+ if (count != td->max_frame_size) {
+ if (count < td->max_frame_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ uint32_t temp;
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->ep_no), sc->sc_bounce_buf,
+ temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_read_multi_1(sc->sc_io_tag,
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no),
+ ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ usb2_copy_in(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* receive data 4 bytes at a time */
+ bus_space_read_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* receive data */
+ bus_space_read_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* clear status bits */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_data_tx(struct musbotg_td *td)
+{
+ struct usb2_page_search buf_res;
+ struct musbotg_softc *sc;
+ uint16_t count;
+ uint8_t csr;
+ uint8_t to;
+
+ to = 8; /* don't loop forever! */
+
+ /* get pointer to softc */
+ sc = MUSBOTG_PC2SC(td->pc);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, td->ep_no);
+
+repeat:
+
+ /* read out FIFO status */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ DPRINTFN(4, "csr=0x%02x\n", csr);
+
+ if (csr & (MUSB2_MASK_CSRL_TXINCOMP |
+ MUSB2_MASK_CSRL_TXUNDERRUN)) {
+ /* clear status bits */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ }
+ if (csr & MUSB2_MASK_CSRL_TXPKTRDY) {
+ return (1); /* not complete */
+ }
+ /* check for short packet */
+ count = td->max_frame_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ while (count > 0) {
+ uint32_t temp;
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* check for unaligned memory address */
+ if (USB_P2U(buf_res.buffer) & 3) {
+
+ usb2_copy_out(td->pc, td->offset,
+ sc->sc_bounce_buf, count);
+
+ temp = count & ~3;
+
+ if (temp) {
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag,
+ sc->sc_io_hdl, MUSB2_REG_EPFIFO(td->ep_no),
+ sc->sc_bounce_buf, temp / 4);
+ }
+ temp = count & 3;
+ if (temp) {
+ /* receive data 1 byte at a time */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->ep_no),
+ ((void *)&sc->sc_bounce_buf[count / 4]), temp);
+ }
+ /* update offset and remainder */
+ td->offset += count;
+ td->remainder -= count;
+ break;
+ }
+ /* check if we can optimise */
+ if (buf_res.length >= 4) {
+
+ /* transmit data 4 bytes at a time */
+ bus_space_write_multi_4(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ buf_res.length / 4);
+
+ temp = buf_res.length & ~3;
+
+ /* update counters */
+ count -= temp;
+ td->offset += temp;
+ td->remainder -= temp;
+ continue;
+ }
+ /* transmit data */
+ bus_space_write_multi_1(sc->sc_io_tag, sc->sc_io_hdl,
+ MUSB2_REG_EPFIFO(td->ep_no), buf_res.buffer,
+ buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* write command */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXPKTRDY);
+
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+musbotg_xfer_do_fifo(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc;
+ struct musbotg_td *td;
+
+ DPRINTFN(8, "\n");
+
+ td = xfer->td_transfer_cache;
+ while (1) {
+ if ((td->func) (td)) {
+ /* operation in progress */
+ break;
+ }
+ if (((void *)td) == xfer->td_transfer_last) {
+ goto done;
+ }
+ if (td->error) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ goto done;
+ }
+ }
+ /*
+ * Fetch the next transfer descriptor and transfer
+ * some flags to the next transfer descriptor
+ */
+ td = td->obj_next;
+ xfer->td_transfer_cache = td;
+ }
+ return (1); /* not complete */
+
+done:
+ sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+
+ /* compute all actual lengths */
+
+ musbotg_standard_done(xfer);
+
+ return (0); /* complete */
+}
+
+static void
+musbotg_interrupt_poll(struct musbotg_softc *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!musbotg_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+void
+musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on)
+{
+ DPRINTFN(4, "vbus = %u\n", is_on);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ if (is_on) {
+ if (!sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &musbotg_root_intr_done);
+ }
+ } else {
+ if (sc->sc_flags.status_vbus) {
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &musbotg_root_intr_done);
+ }
+ }
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+musbotg_interrupt(struct musbotg_softc *sc)
+{
+ uint16_t rx_status;
+ uint16_t tx_status;
+ uint8_t usb_status;
+ uint8_t temp;
+ uint8_t to = 2;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+repeat:
+
+ /* read all interrupt registers */
+ usb_status = MUSB2_READ_1(sc, MUSB2_REG_INTUSB);
+
+ /* read all FIFO interrupts */
+ rx_status = MUSB2_READ_2(sc, MUSB2_REG_INTRX);
+ tx_status = MUSB2_READ_2(sc, MUSB2_REG_INTTX);
+
+ /* check for any bus state change interrupts */
+
+ if (usb_status & (MUSB2_MASK_IRESET |
+ MUSB2_MASK_IRESUME | MUSB2_MASK_ISUSP)) {
+
+ DPRINTFN(4, "real bus interrupt 0x%08x\n", usb_status);
+
+ if (usb_status & MUSB2_MASK_IRESET) {
+
+ /* set correct state */
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* determine line speed */
+ temp = MUSB2_READ_1(sc, MUSB2_REG_POWER);
+ if (temp & MUSB2_MASK_HSMODE)
+ sc->sc_flags.status_high_speed = 1;
+ else
+ sc->sc_flags.status_high_speed = 0;
+
+ /*
+ * After reset all interrupts are on and we need to
+ * turn them off!
+ */
+ temp = MUSB2_MASK_IRESET;
+ /* disable resume interrupt */
+ temp &= ~MUSB2_MASK_IRESUME;
+ /* enable suspend interrupt */
+ temp |= MUSB2_MASK_ISUSP;
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp);
+ /* disable TX and RX interrupts */
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
+ }
+ /*
+ * If RXRSM and RXSUSP is set at the same time we interpret
+ * that like RESUME. Resume is set when there is at least 3
+ * milliseconds of inactivity on the USB BUS.
+ */
+ if (usb_status & MUSB2_MASK_IRESUME) {
+ if (sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE);
+ /* disable resume interrupt */
+ temp &= ~MUSB2_MASK_IRESUME;
+ /* enable suspend interrupt */
+ temp |= MUSB2_MASK_ISUSP;
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp);
+ }
+ } else if (usb_status & MUSB2_MASK_ISUSP) {
+ if (!sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_INTUSBE);
+ /* disable suspend interrupt */
+ temp &= ~MUSB2_MASK_ISUSP;
+ /* enable resume interrupt */
+ temp |= MUSB2_MASK_IRESUME;
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, temp);
+ }
+ }
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &musbotg_root_intr_done);
+ }
+ /* check for any endpoint interrupts */
+
+ if (rx_status || tx_status) {
+ DPRINTFN(4, "real endpoint interrupt "
+ "rx=0x%04x, tx=0x%04x\n", rx_status, tx_status);
+ }
+ /* poll one time regardless of FIFO status */
+
+ musbotg_interrupt_poll(sc);
+
+ if (--to)
+ goto repeat;
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+musbotg_setup_standard_chain_sub(struct musbotg_std_temp *temp)
+{
+ struct musbotg_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ td->error = 0;
+ td->did_stall = 0;
+ td->short_pkt = temp->short_pkt;
+ td->alt_next = temp->setup_alt_next;
+}
+
+static void
+musbotg_setup_standard_chain(struct usb2_xfer *xfer)
+{
+ struct musbotg_std_temp temp;
+ struct musbotg_softc *sc;
+ struct musbotg_td *td;
+ uint32_t x;
+ uint8_t ep_no;
+
+ DPRINTFN(8, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.max_frame_size = xfer->max_frame_size;
+
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ /* setup temp */
+
+ temp.td = NULL;
+ temp.td_next = xfer->td_start[0];
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.offset = 0;
+
+ sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.func = &musbotg_setup_rx;
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.short_pkt = temp.len ? 1 : 0;
+
+ musbotg_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpoint & UE_DIR_IN) {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &musbotg_setup_data_tx;
+ else
+ temp.func = &musbotg_data_tx;
+ } else {
+ if (xfer->flags_int.control_xfr)
+ temp.func = &musbotg_setup_data_rx;
+ else
+ temp.func = &musbotg_data_rx;
+ }
+
+ /* setup "pc" pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ musbotg_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+
+ /* check if we should append a status stage */
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ temp.func = &musbotg_setup_status;
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ musbotg_setup_standard_chain_sub(&temp);
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+}
+
+static void
+musbotg_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTFN(1, "xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ musbotg_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+musbotg_ep_int_set(struct usb2_xfer *xfer, uint8_t on)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ uint16_t temp;
+ uint8_t ep_no = xfer->endpoint & UE_ADDR;
+
+ /*
+ * Only enable the endpoint interrupt when we are
+ * actually waiting for data, hence we are dealing
+ * with level triggered interrupts !
+ */
+ if (ep_no == 0) {
+ temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE);
+ if (on)
+ temp |= MUSB2_MASK_EPINT(0);
+ else
+ temp &= ~MUSB2_MASK_EPINT(0);
+
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp);
+ } else {
+ if (USB_GET_DATA_ISREAD(xfer)) {
+ temp = MUSB2_READ_2(sc, MUSB2_REG_INTRXE);
+ if (on)
+ temp |= MUSB2_MASK_EPINT(ep_no);
+ else
+ temp &= ~MUSB2_MASK_EPINT(ep_no);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, temp);
+
+ } else {
+ temp = MUSB2_READ_2(sc, MUSB2_REG_INTTXE);
+ if (on)
+ temp |= MUSB2_MASK_EPINT(ep_no);
+ else
+ temp &= ~MUSB2_MASK_EPINT(ep_no);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, temp);
+ }
+ }
+}
+
+static void
+musbotg_start_standard_chain(struct usb2_xfer *xfer)
+{
+ DPRINTFN(8, "\n");
+
+ /* poll one time */
+ if (musbotg_xfer_do_fifo(xfer)) {
+
+ musbotg_ep_int_set(xfer, 1);
+
+ DPRINTFN(14, "enabled interrupts on endpoint\n");
+
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer,
+ &musbotg_timeout, xfer->timeout);
+ }
+ }
+}
+
+static void
+musbotg_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+
+ DPRINTFN(8, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ musbotg_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+
+ /* set port bit */
+ sc->sc_hub_idata[0] = 0x02; /* we only have one port */
+
+done:
+ return;
+}
+
+static usb2_error_t
+musbotg_standard_done_sub(struct usb2_xfer *xfer)
+{
+ struct musbotg_td *td;
+ uint32_t len;
+ uint8_t error;
+
+ DPRINTFN(8, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ do {
+ len = td->remainder;
+
+ if (xfer->aframes != xfer->nframes) {
+ /*
+ * Verify the length and subtract
+ * the remainder from "frlengths[]":
+ */
+ if (len > xfer->frlengths[xfer->aframes]) {
+ td->error = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error) {
+ /* the transfer is finished */
+ error = 1;
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+musbotg_standard_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(12, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = musbotg_standard_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = musbotg_standard_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = musbotg_standard_done_sub(xfer);
+ }
+done:
+ musbotg_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * musbotg_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+
+ musbotg_ep_int_set(xfer, 0);
+
+ DPRINTFN(14, "disabled interrupts on endpoint\n");
+ }
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+static void
+musbotg_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer,
+ struct usb2_pipe *pipe)
+{
+ struct musbotg_softc *sc;
+ uint8_t ep_no;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(4, "pipe=%p\n", pipe);
+
+ if (xfer) {
+ /* cancel any ongoing transfers */
+ musbotg_device_done(xfer, USB_ERR_STALLED);
+ }
+ /* set FORCESTALL */
+ sc = MUSBOTG_BUS2SC(udev->bus);
+
+ ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR);
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no);
+
+ if (pipe->edesc->bEndpointAddress & UE_DIR_IN) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXSENDSTALL);
+ } else {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXSENDSTALL);
+ }
+}
+
+static void
+musbotg_clear_stall_sub(struct musbotg_softc *sc, uint16_t wMaxPacket,
+ uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
+{
+ uint16_t mps;
+ uint16_t temp;
+ uint8_t csr;
+
+ if (ep_type == UE_CONTROL) {
+ /* clearing stall is not needed */
+ return;
+ }
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, ep_no);
+
+ /* compute max frame size */
+ mps = wMaxPacket & 0x7FF;
+ switch ((wMaxPacket >> 11) & 3) {
+ case 1:
+ mps *= 2;
+ break;
+ case 2:
+ mps *= 3;
+ break;
+ default:
+ break;
+ }
+
+ if (ep_dir == UE_DIR_IN) {
+
+ temp = 0;
+
+ /* Configure endpoint */
+ switch (ep_type) {
+ case UE_INTERRUPT:
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH,
+ MUSB2_MASK_CSRH_TXMODE | temp);
+ break;
+ case UE_ISOCHRONOUS:
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH,
+ MUSB2_MASK_CSRH_TXMODE |
+ MUSB2_MASK_CSRH_TXISO | temp);
+ break;
+ case UE_BULK:
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRH,
+ MUSB2_MASK_CSRH_TXMODE | temp);
+ break;
+ default:
+ break;
+ }
+
+ /* Need to flush twice in case of double bufring */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ if (csr & MUSB2_MASK_CSRL_TXFIFONEMPTY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ }
+ /* reset data toggle */
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL,
+ MUSB2_MASK_CSRL_TXDT_CLR);
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+
+ /* set double/single buffering */
+ temp = MUSB2_READ_2(sc, MUSB2_REG_TXDBDIS);
+ if (mps <= (sc->sc_hw_ep_profile[ep_no].
+ max_in_frame_size / 2)) {
+ /* double buffer */
+ temp &= ~(1 << ep_no);
+ } else {
+ /* single buffer */
+ temp |= (1 << ep_no);
+ }
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, temp);
+
+ /* clear sent stall */
+ if (csr & MUSB2_MASK_CSRL_TXSENTSTALL) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_TXCSRL, 0);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_TXCSRL);
+ }
+ } else {
+
+ temp = 0;
+
+ /* Configure endpoint */
+ switch (ep_type) {
+ case UE_INTERRUPT:
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH,
+ MUSB2_MASK_CSRH_RXNYET | temp);
+ break;
+ case UE_ISOCHRONOUS:
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH,
+ MUSB2_MASK_CSRH_RXNYET |
+ MUSB2_MASK_CSRH_RXISO | temp);
+ break;
+ case UE_BULK:
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXMAXP, wMaxPacket);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRH, temp);
+ break;
+ default:
+ break;
+ }
+
+ /* Need to flush twice in case of double bufring */
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ if (csr & MUSB2_MASK_CSRL_RXPKTRDY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ if (csr & MUSB2_MASK_CSRL_RXPKTRDY) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXFFLUSH);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+ }
+ }
+ /* reset data toggle */
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL,
+ MUSB2_MASK_CSRL_RXDT_CLR);
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
+ csr = MUSB2_READ_1(sc, MUSB2_REG_RXCSRL);
+
+ /* set double/single buffering */
+ temp = MUSB2_READ_2(sc, MUSB2_REG_RXDBDIS);
+ if (mps <= (sc->sc_hw_ep_profile[ep_no].
+ max_out_frame_size / 2)) {
+ /* double buffer */
+ temp &= ~(1 << ep_no);
+ } else {
+ /* single buffer */
+ temp |= (1 << ep_no);
+ }
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, temp);
+
+ /* clear sent stall */
+ if (csr & MUSB2_MASK_CSRL_RXSENTSTALL) {
+ MUSB2_WRITE_1(sc, MUSB2_REG_RXCSRL, 0);
+ }
+ }
+}
+
+static void
+musbotg_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe)
+{
+ struct musbotg_softc *sc;
+ struct usb2_endpoint_descriptor *ed;
+
+ DPRINTFN(4, "pipe=%p\n", pipe);
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ /* check mode */
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ /* get softc */
+ sc = MUSBOTG_BUS2SC(udev->bus);
+
+ /* get endpoint descriptor */
+ ed = pipe->edesc;
+
+ /* reset endpoint */
+ musbotg_clear_stall_sub(sc,
+ UGETW(ed->wMaxPacketSize),
+ (ed->bEndpointAddress & UE_ADDR),
+ (ed->bmAttributes & UE_XFERTYPE),
+ (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+}
+
+usb2_error_t
+musbotg_init(struct musbotg_softc *sc)
+{
+ struct usb2_hw_ep_profile *pf;
+ uint8_t nrx;
+ uint8_t ntx;
+ uint8_t temp;
+ uint8_t fsize;
+ uint8_t frx;
+ uint8_t ftx;
+
+ DPRINTFN(1, "start\n");
+
+ /* set up the bus structure */
+ sc->sc_bus.usbrev = USB_REV_2_0;
+ sc->sc_bus.methods = &musbotg_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* turn on clocks */
+
+ if (sc->sc_clocks_on) {
+ (sc->sc_clocks_on) (sc->sc_clocks_arg);
+ }
+ /* wait a little for things to stabilise */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+
+ /* disable all interrupts */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
+
+ /* disable pullup */
+
+ musbotg_pull_common(sc, 0);
+
+ /* wait a little bit (10ms) */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+ /* disable double packet buffering */
+ MUSB2_WRITE_2(sc, MUSB2_REG_RXDBDIS, 0xFFFF);
+ MUSB2_WRITE_2(sc, MUSB2_REG_TXDBDIS, 0xFFFF);
+
+ /* enable HighSpeed and ISO Update flags */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_POWER,
+ MUSB2_MASK_HSENAB | MUSB2_MASK_ISOUPD);
+
+ /* clear Session bit, if set */
+
+ temp = MUSB2_READ_1(sc, MUSB2_REG_DEVCTL);
+ temp &= ~MUSB2_MASK_SESS;
+ MUSB2_WRITE_1(sc, MUSB2_REG_DEVCTL, temp);
+
+ DPRINTF("DEVCTL=0x%02x\n", temp);
+
+ /* disable testmode */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_TESTMODE, 0);
+
+ /* set default value */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_MISC, 0);
+
+ /* select endpoint index 0 */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, 0);
+
+ /* read out number of endpoints */
+
+ nrx =
+ (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) / 16);
+
+ ntx =
+ (MUSB2_READ_1(sc, MUSB2_REG_EPINFO) % 16);
+
+ /* these numbers exclude the control endpoint */
+
+ DPRINTFN(2, "RX/TX endpoints: %u/%u\n", nrx, ntx);
+
+ sc->sc_ep_max = (nrx > ntx) ? nrx : ntx;
+ if (sc->sc_ep_max == 0) {
+ DPRINTFN(2, "ERROR: Looks like the clocks are off!\n");
+ }
+ /* read out configuration data */
+
+ sc->sc_conf_data = MUSB2_READ_1(sc, MUSB2_REG_CONFDATA);
+
+ DPRINTFN(2, "Config Data: 0x%02x\n",
+ sc->sc_conf_data);
+
+ DPRINTFN(2, "HW version: 0x%04x\n",
+ MUSB2_READ_1(sc, MUSB2_REG_HWVERS));
+
+ /* initialise endpoint profiles */
+
+ for (temp = 1; temp <= sc->sc_ep_max; temp++) {
+ pf = sc->sc_hw_ep_profile + temp;
+
+ /* select endpoint */
+ MUSB2_WRITE_1(sc, MUSB2_REG_EPINDEX, temp);
+
+ fsize = MUSB2_READ_1(sc, MUSB2_REG_FSIZE);
+ frx = (fsize & MUSB2_MASK_RX_FSIZE) / 16;;
+ ftx = (fsize & MUSB2_MASK_TX_FSIZE);
+
+ DPRINTF("Endpoint %u FIFO size: IN=%u, OUT=%u\n",
+ temp, pf->max_in_frame_size,
+ pf->max_out_frame_size);
+
+ if (frx && ftx && (temp <= nrx) && (temp <= ntx)) {
+ pf->max_in_frame_size = 1 << ftx;
+ pf->max_out_frame_size = 1 << frx;
+ pf->is_simplex = 0; /* duplex */
+ pf->support_multi_buffer = 1;
+ pf->support_bulk = 1;
+ pf->support_interrupt = 1;
+ pf->support_isochronous = 1;
+ pf->support_in = 1;
+ pf->support_out = 1;
+ } else if (frx && (temp <= nrx)) {
+ pf->max_out_frame_size = 1 << frx;
+ pf->is_simplex = 1; /* simplex */
+ pf->support_multi_buffer = 1;
+ pf->support_bulk = 1;
+ pf->support_interrupt = 1;
+ pf->support_isochronous = 1;
+ pf->support_out = 1;
+ } else if (ftx && (temp <= ntx)) {
+ pf->max_in_frame_size = 1 << ftx;
+ pf->is_simplex = 1; /* simplex */
+ pf->support_multi_buffer = 1;
+ pf->support_bulk = 1;
+ pf->support_interrupt = 1;
+ pf->support_isochronous = 1;
+ pf->support_in = 1;
+ }
+ }
+
+ /* turn on default interrupts */
+
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE,
+ MUSB2_MASK_IRESET);
+
+ musbotg_clocks_off(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+
+ musbotg_do_poll(&sc->sc_bus);
+
+ return (0); /* success */
+}
+
+void
+musbotg_uninit(struct musbotg_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* disable all interrupts */
+ MUSB2_WRITE_1(sc, MUSB2_REG_INTUSBE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTTXE, 0);
+ MUSB2_WRITE_2(sc, MUSB2_REG_INTRXE, 0);
+
+ sc->sc_flags.port_powered = 0;
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ musbotg_pull_down(sc);
+ musbotg_clocks_off(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+musbotg_suspend(struct musbotg_softc *sc)
+{
+ return;
+}
+
+void
+musbotg_resume(struct musbotg_softc *sc)
+{
+ return;
+}
+
+static void
+musbotg_do_poll(struct usb2_bus *bus)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ musbotg_interrupt_poll(sc);
+ musbotg_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * musbotg bulk support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_bulk_close(struct usb2_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_bulk_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+ musbotg_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods musbotg_device_bulk_methods =
+{
+ .open = musbotg_device_bulk_open,
+ .close = musbotg_device_bulk_close,
+ .enter = musbotg_device_bulk_enter,
+ .start = musbotg_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg control support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+ musbotg_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods musbotg_device_ctrl_methods =
+{
+ .open = musbotg_device_ctrl_open,
+ .close = musbotg_device_ctrl_close,
+ .enter = musbotg_device_ctrl_enter,
+ .start = musbotg_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg interrupt support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_intr_close(struct usb2_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_intr_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+ musbotg_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods musbotg_device_intr_methods =
+{
+ .open = musbotg_device_intr_open,
+ .close = musbotg_device_intr_close,
+ .enter = musbotg_device_intr_enter,
+ .start = musbotg_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg full speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_device_isoc_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_device_isoc_close(struct usb2_xfer *xfer)
+{
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_device_isoc_enter(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ uint32_t temp;
+ uint32_t nframes;
+ uint32_t fs_frames;
+
+ DPRINTFN(5, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ /* get the current frame index */
+
+ nframes = MUSB2_READ_2(sc, MUSB2_REG_FRAME);
+
+ /*
+ * check if the frame index is within the window where the frames
+ * will be inserted
+ */
+ temp = (nframes - xfer->pipe->isoc_next) & MUSB2_MASK_FRAME;
+
+ if (usb2_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
+ fs_frames = (xfer->nframes + 7) / 8;
+ } else {
+ fs_frames = xfer->nframes;
+ }
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (temp < fs_frames)) {
+ /*
+ * If there is data underflow or the pipe queue is
+ * empty we schedule the transfer a few frames ahead
+ * of the current frame position. Else two isochronous
+ * transfers might overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) & MUSB2_MASK_FRAME;
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(2, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->pipe->isoc_next - nframes) & MUSB2_MASK_FRAME;
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+ fs_frames;
+
+ /* compute frame number for next insertion */
+ xfer->pipe->isoc_next += fs_frames;
+
+ /* setup TDs */
+ musbotg_setup_standard_chain(xfer);
+}
+
+static void
+musbotg_device_isoc_start(struct usb2_xfer *xfer)
+{
+ /* start TD chain */
+ musbotg_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods musbotg_device_isoc_methods =
+{
+ .open = musbotg_device_isoc_open,
+ .close = musbotg_device_isoc_close,
+ .enter = musbotg_device_isoc_enter,
+ .start = musbotg_device_isoc_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware HUB by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+static void
+musbotg_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/*
+ * USB descriptors for the virtual Root HUB:
+ */
+
+static const struct usb2_device_descriptor musbotg_devd = {
+ .bLength = sizeof(struct usb2_device_descriptor),
+ .bDescriptorType = UDESC_DEVICE,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_HSHUBSTT,
+ .bMaxPacketSize = 64,
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb2_device_qualifier musbotg_odevd = {
+ .bLength = sizeof(struct usb2_device_qualifier),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+};
+
+static const struct musbotg_config_desc musbotg_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(musbotg_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_HSHUBSTT,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = (UE_DIR_IN | MUSBOTG_INTR_ENDPT),
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 255,
+ },
+};
+
+static const struct usb2_hub_descriptor_min musbotg_hubd = {
+ .bDescLength = sizeof(musbotg_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = 1,
+ .wHubCharacteristics[0] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF,
+ .wHubCharacteristics[1] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 16,
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0}, /* port is removable */
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_VENDOR \
+ 'M', 0, 'e', 0, 'n', 0, 't', 0, 'o', 0, 'r', 0, ' ', 0, \
+ 'G', 0, 'r', 0, 'a', 0, 'p', 0, 'h', 0, 'i', 0, 'c', 0, 's', 0
+
+#define STRING_PRODUCT \
+ 'O', 0, 'T', 0, 'G', 0, ' ', 0, 'R', 0, \
+ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \
+ 'U', 0, 'B', 0,
+
+USB_MAKE_STRING_DESC(STRING_LANG, musbotg_langtab);
+USB_MAKE_STRING_DESC(STRING_VENDOR, musbotg_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, musbotg_product);
+
+static void
+musbotg_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+musbotg_root_ctrl_task(struct usb2_bus *bus)
+{
+ musbotg_root_ctrl_poll(MUSBOTG_BUS2SC(bus));
+}
+
+static void
+musbotg_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+ uint16_t value;
+ uint16_t index;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ musbotg_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0);
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ /* demultiplex the control request */
+
+ switch (std->req.bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (std->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 (std->req.bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_DESCRIPTOR:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SYNCH_FRAME:
+ goto tr_valid; /* nop */
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_SET_INTERFACE:
+ goto tr_handle_set_interface;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_GET_INTERFACE:
+ goto tr_handle_get_interface;
+ case UR_GET_STATUS:
+ goto tr_handle_get_iface_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_INTERFACE:
+ case UT_WRITE_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_READ_CLASS_INTERFACE:
+ case UT_READ_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_WRITE_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_valid;
+ case UR_SET_DESCRIPTOR:
+ case UR_SET_FEATURE:
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_handle_clear_port_feature;
+ case UR_SET_FEATURE:
+ goto tr_handle_set_port_feature;
+ case UR_CLEAR_TT_BUFFER:
+ case UR_RESET_TT:
+ case UR_STOP_TT:
+ goto tr_valid;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_GET_TT_STATE:
+ goto tr_handle_get_tt_state;
+ case UR_GET_STATUS:
+ goto tr_handle_get_port_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ case UR_GET_STATUS:
+ goto tr_handle_get_class_status;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(musbotg_devd);
+ std->ptr = USB_ADD_BYTES(&musbotg_devd, 0);
+ goto tr_valid;
+ case UDESC_CONFIG:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(musbotg_confd);
+ std->ptr = USB_ADD_BYTES(&musbotg_confd, 0);
+ goto tr_valid;
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ std->len = sizeof(musbotg_langtab);
+ std->ptr = USB_ADD_BYTES(&musbotg_langtab, 0);
+ goto tr_valid;
+
+ case 1: /* Vendor */
+ std->len = sizeof(musbotg_vendor);
+ std->ptr = USB_ADD_BYTES(&musbotg_vendor, 0);
+ goto tr_valid;
+
+ case 2: /* Product */
+ std->len = sizeof(musbotg_product);
+ std->ptr = USB_ADD_BYTES(&musbotg_product, 0);
+ goto tr_valid;
+ default:
+ break;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_config:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+ goto tr_valid;
+
+tr_handle_get_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (value & 0xFF00) {
+ goto tr_stalled;
+ }
+ sc->sc_rt_addr = value;
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (value >= 2) {
+ goto tr_stalled;
+ }
+ sc->sc_conf = value;
+ goto tr_valid;
+
+tr_handle_get_interface:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = 0;
+ goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, 0);
+ goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+ goto tr_valid;
+
+tr_handle_clear_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(8, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
+
+ switch (value) {
+ case UHF_PORT_SUSPEND:
+ musbotg_wakeup_peer(xfer);
+ break;
+
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 0;
+ break;
+
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 0;
+ musbotg_pull_down(sc);
+ musbotg_clocks_off(sc);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_flags.change_connect = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_flags.change_suspend = 0;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_set_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(8, "UR_SET_PORT_FEATURE\n");
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 1;
+ break;
+ case UHF_PORT_SUSPEND:
+ case UHF_PORT_RESET:
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 1;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_get_port_status:
+
+ DPRINTFN(8, "UR_GET_PORT_STATUS\n");
+
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ if (sc->sc_flags.status_vbus) {
+ musbotg_clocks_on(sc);
+ musbotg_pull_up(sc);
+ } else {
+ musbotg_pull_down(sc);
+ musbotg_clocks_off(sc);
+ }
+
+ /* Select Device Side Mode */
+ value = UPS_PORT_MODE_DEVICE;
+
+ if (sc->sc_flags.status_high_speed) {
+ value |= UPS_HIGH_SPEED;
+ }
+ if (sc->sc_flags.port_powered) {
+ value |= UPS_PORT_POWER;
+ }
+ if (sc->sc_flags.port_enabled) {
+ value |= UPS_PORT_ENABLED;
+ }
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ value |= UPS_CURRENT_CONNECT_STATUS;
+ }
+ if (sc->sc_flags.status_suspend) {
+ value |= UPS_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+ value = 0;
+
+ if (sc->sc_flags.change_connect) {
+ value |= UPS_C_CONNECT_STATUS;
+
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ /* reset EP0 state */
+ sc->sc_ep0_busy = 0;
+ sc->sc_ep0_cmd = 0;
+ }
+ }
+ if (sc->sc_flags.change_suspend) {
+ value |= UPS_C_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortChange, value);
+ std->len = sizeof(sc->sc_hub_temp.ps);
+ goto tr_valid;
+
+tr_handle_get_class_descriptor:
+ if (value & 0xFF) {
+ goto tr_stalled;
+ }
+ std->ptr = USB_ADD_BYTES(&musbotg_hubd, 0);
+ std->len = sizeof(musbotg_hubd);
+ goto tr_valid;
+
+tr_stalled:
+ std->err = USB_ERR_STALLED;
+tr_valid:
+done:
+ return;
+}
+
+static void
+musbotg_root_ctrl_poll(struct musbotg_softc *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &musbotg_root_ctrl_done);
+}
+
+struct usb2_pipe_methods musbotg_root_ctrl_methods =
+{
+ .open = musbotg_root_ctrl_open,
+ .close = musbotg_root_ctrl_close,
+ .enter = musbotg_root_ctrl_enter,
+ .start = musbotg_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * musbotg root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+musbotg_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_root_intr_close(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ musbotg_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+musbotg_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_root_intr_start(struct usb2_xfer *xfer)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+}
+
+struct usb2_pipe_methods musbotg_root_intr_methods =
+{
+ .open = musbotg_root_intr_open,
+ .close = musbotg_root_intr_close,
+ .enter = musbotg_root_intr_enter,
+ .start = musbotg_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+musbotg_xfer_setup(struct usb2_setup_params *parm)
+{
+ const struct usb2_hw_ep_profile *pf;
+ struct musbotg_softc *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t n;
+ uint8_t ep_no;
+
+ sc = MUSBOTG_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+ parm->hc_max_packet_size = 0x400;
+ parm->hc_max_frame_size = 0x400;
+
+ if ((parm->methods == &musbotg_device_isoc_methods) ||
+ (parm->methods == &musbotg_device_intr_methods))
+ parm->hc_max_packet_count = 3;
+ else
+ parm->hc_max_packet_count = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ /*
+ * compute maximum number of TDs
+ */
+ if (parm->methods == &musbotg_device_ctrl_methods) {
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &musbotg_device_bulk_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &musbotg_device_intr_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &musbotg_device_isoc_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else {
+
+ ntd = 0;
+ }
+
+ /*
+ * check if "usb2_transfer_setup_sub" set an error
+ */
+ if (parm->err) {
+ return;
+ }
+ /*
+ * allocate transfer descriptors
+ */
+ last_obj = NULL;
+
+ /*
+ * get profile stuff
+ */
+ if (ntd) {
+
+ ep_no = xfer->endpoint & UE_ADDR;
+ musbotg_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+ if (pf == NULL) {
+ /* should not happen */
+ parm->err = USB_ERR_INVAL;
+ return;
+ }
+ } else {
+ ep_no = 0;
+ pf = NULL;
+ }
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ for (n = 0; n != ntd; n++) {
+
+ struct musbotg_td *td;
+
+ if (parm->buf) {
+
+ td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ /* init TD */
+ td->max_frame_size = xfer->max_frame_size;
+ td->ep_no = ep_no;
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ parm->size[0] += sizeof(*td);
+ }
+
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+musbotg_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+musbotg_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ struct musbotg_softc *sc = MUSBOTG_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_rt_addr);
+
+ if (udev->device_index == sc->sc_rt_addr) {
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &musbotg_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | MUSBOTG_INTR_ENDPT:
+ pipe->methods = &musbotg_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ if ((udev->speed != USB_SPEED_FULL) &&
+ (udev->speed != USB_SPEED_HIGH)) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &musbotg_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &musbotg_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ pipe->methods = &musbotg_device_isoc_methods;
+ break;
+ case UE_BULK:
+ pipe->methods = &musbotg_device_bulk_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+struct usb2_bus_methods musbotg_bus_methods =
+{
+ .pipe_init = &musbotg_pipe_init,
+ .xfer_setup = &musbotg_xfer_setup,
+ .xfer_unsetup = &musbotg_xfer_unsetup,
+ .do_poll = &musbotg_do_poll,
+ .get_hw_ep_profile = &musbotg_get_hw_ep_profile,
+ .set_stall = &musbotg_set_stall,
+ .clear_stall = &musbotg_clear_stall,
+ .roothub_exec = &musbotg_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/musb_otg.h b/sys/dev/usb/controller/musb_otg.h
new file mode 100644
index 0000000..0d880e1
--- /dev/null
+++ b/sys/dev/usb/controller/musb_otg.h
@@ -0,0 +1,407 @@
+/* $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 header file defines the registers of the Mentor Graphics
+ * USB OnTheGo Inventra chip.
+ */
+
+#ifndef _MUSB2_OTG_H_
+#define _MUSB2_OTG_H_
+
+#define MUSB2_MAX_DEVICES (USB_MIN_DEVICES + 1)
+
+/* Common registers */
+
+#define MUSB2_REG_FADDR 0x0000 /* function address register */
+#define MUSB2_MASK_FADDR 0x7F
+
+#define MUSB2_REG_POWER 0x0001 /* power register */
+#define MUSB2_MASK_SUSPM_ENA 0x01
+#define MUSB2_MASK_SUSPMODE 0x02
+#define MUSB2_MASK_RESUME 0x04
+#define MUSB2_MASK_RESET 0x08
+#define MUSB2_MASK_HSMODE 0x10
+#define MUSB2_MASK_HSENAB 0x20
+#define MUSB2_MASK_SOFTC 0x40
+#define MUSB2_MASK_ISOUPD 0x80
+
+/* Endpoint interrupt handling */
+
+#define MUSB2_REG_INTTX 0x0002 /* transmit interrupt register */
+#define MUSB2_REG_INTRX 0x0004 /* receive interrupt register */
+#define MUSB2_REG_INTTXE 0x0006 /* transmit interrupt enable register */
+#define MUSB2_REG_INTRXE 0x0008 /* receive interrupt enable register */
+#define MUSB2_MASK_EPINT(epn) (1 << (epn)) /* epn = [0..15] */
+
+/* Common interrupt handling */
+
+#define MUSB2_REG_INTUSB 0x000A /* USB interrupt register */
+#define MUSB2_MASK_ISUSP 0x01
+#define MUSB2_MASK_IRESUME 0x02
+#define MUSB2_MASK_IRESET 0x04
+#define MUSB2_MASK_IBABBLE 0x04
+#define MUSB2_MASK_ISOF 0x08
+#define MUSB2_MASK_ICONN 0x10
+#define MUSB2_MASK_IDISC 0x20
+#define MUSB2_MASK_ISESSRQ 0x40
+#define MUSB2_MASK_IVBUSERR 0x80
+
+#define MUSB2_REG_INTUSBE 0x000B /* USB interrupt enable register */
+#define MUSB2_REG_FRAME 0x000C /* USB frame register */
+#define MUSB2_MASK_FRAME 0x3FF /* 0..1023 */
+
+#define MUSB2_REG_EPINDEX 0x000E /* endpoint index register */
+#define MUSB2_MASK_EPINDEX 0x0F
+
+#define MUSB2_REG_TESTMODE 0x000F /* test mode register */
+#define MUSB2_MASK_TSE0_NAK 0x01
+#define MUSB2_MASK_TJ 0x02
+#define MUSB2_MASK_TK 0x04
+#define MUSB2_MASK_TPACKET 0x08
+#define MUSB2_MASK_TFORCE_HS 0x10
+#define MUSB2_MASK_TFORCE_LS 0x20
+#define MUSB2_MASK_TFIFO_ACC 0x40
+#define MUSB2_MASK_TFORCE_HC 0x80
+
+#define MUSB2_REG_INDEXED_CSR 0x0010 /* EP control status register offset */
+
+#define MUSB2_REG_TXMAXP (0x0000 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_REG_RXMAXP (0x0004 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_PKTSIZE 0x03FF /* in bytes, should be even */
+#define MUSB2_MASK_PKTMULT 0xFC00 /* HS packet multiplier: 0..2 */
+
+#define MUSB2_REG_TXCSRL (0x0002 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_CSRL_TXPKTRDY 0x01
+#define MUSB2_MASK_CSRL_TXFIFONEMPTY 0x02
+#define MUSB2_MASK_CSRL_TXUNDERRUN 0x04 /* Device Mode */
+#define MUSB2_MASK_CSRL_TXERROR 0x04 /* Host Mode */
+#define MUSB2_MASK_CSRL_TXFFLUSH 0x08
+#define MUSB2_MASK_CSRL_TXSENDSTALL 0x10/* Device Mode */
+#define MUSB2_MASK_CSRL_TXSETUPPKT 0x10 /* Host Mode */
+#define MUSB2_MASK_CSRL_TXSENTSTALL 0x20/* Device Mode */
+#define MUSB2_MASK_CSRL_TXSTALLED 0x20 /* Host Mode */
+#define MUSB2_MASK_CSRL_TXDT_CLR 0x40
+#define MUSB2_MASK_CSRL_TXINCOMP 0x80
+
+/* Device Side Mode */
+#define MUSB2_MASK_CSR0L_RXPKTRDY 0x01
+#define MUSB2_MASK_CSR0L_TXPKTRDY 0x02
+#define MUSB2_MASK_CSR0L_SENTSTALL 0x04
+#define MUSB2_MASK_CSR0L_DATAEND 0x08
+#define MUSB2_MASK_CSR0L_SETUPEND 0x10
+#define MUSB2_MASK_CSR0L_SENDSTALL 0x20
+#define MUSB2_MASK_CSR0L_RXPKTRDY_CLR 0x40
+#define MUSB2_MASK_CSR0L_SETUPEND_CLR 0x80
+
+/* Host Side Mode */
+#define MUSB2_MASK_CSR0L_RXSTALL 0x04
+#define MUSB2_MASK_CSR0L_SETUPPKT 0x08
+#define MUSB2_MASK_CSR0L_ERROR 0x10
+#define MUSB2_MASK_CSR0L_REQPKT 0x20
+#define MUSB2_MASK_CSR0L_STATUSPKT 0x40
+#define MUSB2_MASK_CSR0L_NAKTIMO 0x80
+
+#define MUSB2_REG_TXCSRH (0x0003 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_CSRH_TXDT_VAL 0x01 /* Host Mode */
+#define MUSB2_MASK_CSRH_TXDT_WR 0x02 /* Host Mode */
+#define MUSB2_MASK_CSRH_TXDMAREQMODE 0x04
+#define MUSB2_MASK_CSRH_TXDT_SWITCH 0x08
+#define MUSB2_MASK_CSRH_TXDMAREQENA 0x10
+#define MUSB2_MASK_CSRH_RXMODE 0x00
+#define MUSB2_MASK_CSRH_TXMODE 0x20
+#define MUSB2_MASK_CSRH_TXISO 0x40 /* Device Mode */
+#define MUSB2_MASK_CSRH_TXAUTOSET 0x80
+
+#define MUSB2_MASK_CSR0H_FFLUSH 0x01 /* Device Side flush FIFO */
+#define MUSB2_MASK_CSR0H_DT 0x02 /* Host Side data toggle */
+#define MUSB2_MASK_CSR0H_DT_SET 0x04 /* Host Side */
+#define MUSB2_MASK_CSR0H_PING_DIS 0x08 /* Host Side */
+
+#define MUSB2_REG_RXCSRL (0x0006 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_CSRL_RXPKTRDY 0x01
+#define MUSB2_MASK_CSRL_RXFIFOFULL 0x02
+#define MUSB2_MASK_CSRL_RXOVERRUN 0x04
+#define MUSB2_MASK_CSRL_RXDATAERR 0x08
+#define MUSB2_MASK_CSRL_RXFFLUSH 0x10
+#define MUSB2_MASK_CSRL_RXSENDSTALL 0x20/* Device Mode */
+#define MUSB2_MASK_CSRL_RXREQPKT 0x20 /* Host Mode */
+#define MUSB2_MASK_CSRL_RXSENTSTALL 0x40/* Device Mode */
+#define MUSB2_MASK_CSRL_RXSTALL 0x40 /* Host Mode */
+#define MUSB2_MASK_CSRL_RXDT_CLR 0x80
+
+#define MUSB2_REG_RXCSRH (0x0007 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_CSRH_RXINCOMP 0x01
+#define MUSB2_MASK_CSRH_RXDT_VAL 0x02 /* Host Mode */
+#define MUSB2_MASK_CSRH_RXDT_SET 0x04 /* Host Mode */
+#define MUSB2_MASK_CSRH_RXDMAREQMODE 0x08
+#define MUSB2_MASK_CSRH_RXNYET 0x10
+#define MUSB2_MASK_CSRH_RXDMAREQENA 0x20
+#define MUSB2_MASK_CSRH_RXISO 0x40 /* Device Mode */
+#define MUSB2_MASK_CSRH_RXAUTOREQ 0x40 /* Host Mode */
+#define MUSB2_MASK_CSRH_RXAUTOCLEAR 0x80
+
+#define MUSB2_REG_RXCOUNT (0x0008 + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_RXCOUNT 0xFFFF
+
+#define MUSB2_REG_TXTI (0x000A + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_REG_RXTI (0x000C + MUSB2_REG_INDEXED_CSR)
+
+/* Host Mode */
+#define MUSB2_MASK_TI_SPEED 0xC0
+#define MUSB2_MASK_TI_SPEED_LO 0xC0
+#define MUSB2_MASK_TI_SPEED_FS 0x80
+#define MUSB2_MASK_TI_SPEED_HS 0x40
+#define MUSB2_MASK_TI_PROTO_CTRL 0x00
+#define MUSB2_MASK_TI_PROTO_ISOC 0x10
+#define MUSB2_MASK_TI_PROTO_BULK 0x20
+#define MUSB2_MASK_TI_PROTO_INTR 0x30
+#define MUSB2_MASK_TI_EP_NUM 0x0F
+
+#define MUSB2_REG_TXNAKLIMIT (0x000B /* EPN=0 */ + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_REG_RXNAKLIMIT (0x000D /* EPN=0 */ + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_NAKLIMIT 0xFF
+
+#define MUSB2_REG_FSIZE (0x000F + MUSB2_REG_INDEXED_CSR)
+#define MUSB2_MASK_RX_FSIZE 0xF0 /* 3..13, 2**n bytes */
+#define MUSB2_MASK_TX_FSIZE 0x0F /* 3..13, 2**n bytes */
+
+#define MUSB2_REG_EPFIFO(n) (0x0020 + (4*(n)))
+
+#define MUSB2_REG_CONFDATA 0x000F /* EPN=0 */
+#define MUSB2_MASK_CD_UTMI_DW 0x01
+#define MUSB2_MASK_CD_SOFTCONE 0x02
+#define MUSB2_MASK_CD_DYNFIFOSZ 0x04
+#define MUSB2_MASK_CD_HBTXE 0x08
+#define MUSB2_MASK_CD_HBRXE 0x10
+#define MUSB2_MASK_CD_BIGEND 0x20
+#define MUSB2_MASK_CD_MPTXE 0x40
+#define MUSB2_MASK_CD_MPRXE 0x80
+
+/* Various registers */
+
+#define MUSB2_REG_DEVCTL 0x0060
+#define MUSB2_MASK_SESS 0x01
+#define MUSB2_MASK_HOSTREQ 0x02
+#define MUSB2_MASK_HOSTMD 0x04
+#define MUSB2_MASK_VBUS0 0x08
+#define MUSB2_MASK_VBUS1 0x10
+#define MUSB2_MASK_LSDEV 0x20
+#define MUSB2_MASK_FSDEV 0x40
+#define MUSB2_MASK_BDEV 0x80
+
+#define MUSB2_REG_MISC 0x0061
+#define MUSB2_MASK_RXEDMA 0x01
+#define MUSB2_MASK_TXEDMA 0x02
+
+#define MUSB2_REG_TXFIFOSZ 0x0062
+#define MUSB2_REG_RXFIFOSZ 0x0063
+#define MUSB2_MASK_FIFODB 0x10 /* set if double buffering, r/w */
+#define MUSB2_MASK_FIFOSZ 0x0F
+#define MUSB2_VAL_FIFOSZ_8 0
+#define MUSB2_VAL_FIFOSZ_16 1
+#define MUSB2_VAL_FIFOSZ_32 2
+#define MUSB2_VAL_FIFOSZ_64 3
+#define MUSB2_VAL_FIFOSZ_128 4
+#define MUSB2_VAL_FIFOSZ_256 5
+#define MUSB2_VAL_FIFOSZ_512 6
+#define MUSB2_VAL_FIFOSZ_1024 7
+#define MUSB2_VAL_FIFOSZ_2048 8
+#define MUSB2_VAL_FIFOSZ_4096 9
+
+#define MUSB2_REG_TXFIFOADD 0x0064
+#define MUSB2_REG_RXFIFOADD 0x0066
+#define MUSB2_MASK_FIFOADD 0xFFF /* unit is 8-bytes */
+
+#define MUSB2_REG_VSTATUS 0x0068
+#define MUSB2_REG_VCONTROL 0x0068
+#define MUSB2_REG_HWVERS 0x006C
+#define MUSB2_REG_ULPI_BASE 0x0070
+
+#define MUSB2_REG_EPINFO 0x0078
+#define MUSB2_MASK_NRXEP 0xF0
+#define MUSB2_MASK_NTXEP 0x0F
+
+#define MUSB2_REG_RAMINFO 0x0079
+#define MUSB2_REG_LINKINFO 0x007A
+
+#define MUSB2_REG_VPLEN 0x007B
+#define MUSB2_MASK_VPLEN 0xFF
+
+#define MUSB2_REG_HS_EOF1 0x007C
+#define MUSB2_REG_FS_EOF1 0x007D
+#define MUSB2_REG_LS_EOF1 0x007E
+#define MUSB2_REG_SOFT_RST 0x007F
+#define MUSB2_MASK_SRST 0x01
+#define MUSB2_MASK_SRSTX 0x02
+
+#define MUSB2_REG_RQPKTCOUNT(n) (0x0300 + (4*(n))
+#define MUSB2_REG_RXDBDIS 0x0340
+#define MUSB2_REG_TXDBDIS 0x0342
+#define MUSB2_MASK_DB(n) (1 << (n)) /* disable double buffer, n = [0..15] */
+
+#define MUSB2_REG_CHIRPTO 0x0344
+#define MUSB2_REG_HSRESUM 0x0346
+
+/* Host Mode only registers */
+
+#define MUSB2_REG_TXFADDR(n) (0x0080 + (8*(n)))
+#define MUSB2_REG_TXHADDR(n) (0x0082 + (8*(n)))
+#define MUSB2_REG_TXHUBPORT(n) (0x0083 + (8*(n)))
+#define MUSB2_REG_RXFADDR(n) (0x0084 + (8*(n)))
+#define MUSB2_REG_RXHADDR(n) (0x0086 + (8*(n)))
+#define MUSB2_REG_RXHPORT(n) (0x0087 + (8*(n)))
+
+#define MUSB2_EP_MAX 16 /* maximum number of endpoints */
+
+#define MUSB2_READ_2(sc, reg) \
+ bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
+
+#define MUSB2_WRITE_2(sc, reg, data) \
+ bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+#define MUSB2_READ_1(sc, reg) \
+ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg)
+
+#define MUSB2_WRITE_1(sc, reg, data) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, reg, data)
+
+struct musbotg_td;
+struct musbotg_softc;
+
+typedef uint8_t (musbotg_cmd_t)(struct musbotg_td *td);
+
+struct musbotg_dma {
+ struct musbotg_softc *sc;
+ uint32_t dma_chan;
+ uint8_t busy:1;
+ uint8_t complete:1;
+ uint8_t error:1;
+};
+
+struct musbotg_td {
+ struct musbotg_td *obj_next;
+ musbotg_cmd_t *func;
+ struct usb2_page_cache *pc;
+ uint32_t offset;
+ uint32_t remainder;
+ uint16_t max_frame_size; /* packet_size * mult */
+ uint8_t ep_no;
+ uint8_t error:1;
+ uint8_t alt_next:1;
+ uint8_t short_pkt:1;
+ uint8_t support_multi_buffer:1;
+ uint8_t did_stall:1;
+ uint8_t dma_enabled:1;
+};
+
+struct musbotg_std_temp {
+ musbotg_cmd_t *func;
+ struct usb2_page_cache *pc;
+ struct musbotg_td *td;
+ struct musbotg_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t max_frame_size;
+ uint8_t short_pkt;
+ /*
+ * short_pkt = 0: transfer should be short terminated
+ * short_pkt = 1: transfer should not be short terminated
+ */
+ uint8_t setup_alt_next;
+};
+
+struct musbotg_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union musbotg_hub_temp {
+ uWord wValue;
+ struct usb2_port_status ps;
+};
+
+struct musbotg_flags {
+ uint8_t change_connect:1;
+ uint8_t change_suspend:1;
+ uint8_t status_suspend:1; /* set if suspended */
+ uint8_t status_vbus:1; /* set if present */
+ uint8_t status_bus_reset:1; /* set if reset complete */
+ uint8_t status_high_speed:1; /* set if High Speed is selected */
+ uint8_t remote_wakeup:1;
+ uint8_t self_powered:1;
+ uint8_t clocks_off:1;
+ uint8_t port_powered:1;
+ uint8_t port_enabled:1;
+ uint8_t d_pulled_up:1;
+};
+
+struct musbotg_softc {
+ struct usb2_bus sc_bus;
+ union musbotg_hub_temp sc_hub_temp;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+ struct usb2_hw_ep_profile sc_hw_ep_profile[16];
+
+ struct usb2_device *sc_devices[MUSB2_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ void (*sc_clocks_on) (void *arg);
+ void (*sc_clocks_off) (void *arg);
+ void *sc_clocks_arg;
+
+ uint32_t sc_bounce_buf[(1024 * 3) / 4]; /* bounce buffer */
+
+ uint8_t sc_ep_max; /* maximum number of RX and TX
+ * endpoints supported */
+ uint8_t sc_rt_addr; /* root HUB address */
+ uint8_t sc_dv_addr; /* device address */
+ uint8_t sc_conf; /* root HUB config */
+ uint8_t sc_ep0_busy; /* set if ep0 is busy */
+ uint8_t sc_ep0_cmd; /* pending commands */
+ uint8_t sc_conf_data; /* copy of hardware register */
+
+ uint8_t sc_hub_idata[1];
+
+ struct musbotg_flags sc_flags;
+};
+
+/* prototypes */
+
+usb2_error_t musbotg_init(struct musbotg_softc *sc);
+void musbotg_uninit(struct musbotg_softc *sc);
+void musbotg_suspend(struct musbotg_softc *sc);
+void musbotg_resume(struct musbotg_softc *sc);
+void musbotg_interrupt(struct musbotg_softc *sc);
+void musbotg_vbus_interrupt(struct musbotg_softc *sc, uint8_t is_on);
+
+#endif /* _MUSB2_OTG_H_ */
diff --git a/sys/dev/usb/controller/musb_otg_atmelarm.c b/sys/dev/usb/controller/musb_otg_atmelarm.c
new file mode 100644
index 0000000..7652424
--- /dev/null
+++ b/sys/dev/usb/controller/musb_otg_atmelarm.c
@@ -0,0 +1,239 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/mus2_otg.h>
+
+#include <sys/rman.h>
+
+static device_probe_t musbotg_probe;
+static device_attach_t musbotg_attach;
+static device_detach_t musbotg_detach;
+static device_shutdown_t musbotg_shutdown;
+
+struct musbotg_super_softc {
+ struct musbotg_softc sc_otg; /* must be first */
+};
+
+static void
+musbotg_vbus_poll(struct musbotg_super_softc *sc)
+{
+ uint8_t vbus_val = 1; /* fake VBUS on - TODO */
+
+ /* just forward it */
+ musbotg_vbus_interrupt(&sc->sc_otg, vbus_val);
+}
+
+static void
+musbotg_clocks_on(void *arg)
+{
+#if 0
+ struct musbotg_super_softc *sc = arg;
+
+#endif
+}
+
+static void
+musbotg_clocks_off(void *arg)
+{
+#if 0
+ struct musbotg_super_softc *sc = arg;
+
+#endif
+}
+
+static int
+musbotg_probe(device_t dev)
+{
+ device_set_desc(dev, "MUSB OTG integrated USB controller");
+ return (0);
+}
+
+static int
+musbotg_attach(device_t dev)
+{
+ struct musbotg_super_softc *sc = device_get_softc(dev);
+ int err;
+ int rid;
+
+ /* setup MUSB OTG USB controller interface softc */
+ sc->sc_otg.sc_clocks_on = &musbotg_clocks_on;
+ sc->sc_otg.sc_clocks_off = &musbotg_clocks_off;
+ sc->sc_otg.sc_clocks_arg = sc;
+
+ /* initialise some bus fields */
+ sc->sc_otg.sc_bus.parent = dev;
+ sc->sc_otg.sc_bus.devices = sc->sc_otg.sc_devices;
+ sc->sc_otg.sc_bus.devices_max = MUSB2_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_otg.sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ return (ENOMEM);
+ }
+ rid = 0;
+ sc->sc_otg.sc_io_res =
+ bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+
+ if (!(sc->sc_otg.sc_io_res)) {
+ err = ENOMEM;
+ goto error;
+ }
+ sc->sc_otg.sc_io_tag = rman_get_bustag(sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_hdl = rman_get_bushandle(sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_size = rman_get_size(sc->sc_otg.sc_io_res);
+
+ rid = 0;
+ sc->sc_otg.sc_irq_res =
+ bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+ if (!(sc->sc_otg.sc_irq_res)) {
+ goto error;
+ }
+ sc->sc_otg.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_otg.sc_bus.bdev)) {
+ goto error;
+ }
+ device_set_ivars(sc->sc_otg.sc_bus.bdev, &sc->sc_otg.sc_bus);
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
+#else
+ err = bus_setup_intr(dev, sc->sc_otg.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)musbotg_interrupt, sc, &sc->sc_otg.sc_intr_hdl);
+#endif
+ if (err) {
+ sc->sc_otg.sc_intr_hdl = NULL;
+ goto error;
+ }
+ err = musbotg_init(&sc->sc_otg);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_otg.sc_bus.bdev);
+ }
+ if (err) {
+ goto error;
+ } else {
+ /* poll VBUS one time */
+ musbotg_vbus_poll(sc);
+ }
+ return (0);
+
+error:
+ musbotg_detach(dev);
+ return (ENXIO);
+}
+
+static int
+musbotg_detach(device_t dev)
+{
+ struct musbotg_super_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_otg.sc_bus.bdev) {
+ bdev = sc->sc_otg.sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(dev);
+
+ if (sc->sc_otg.sc_irq_res && sc->sc_otg.sc_intr_hdl) {
+ /*
+ * only call musbotg_uninit() after musbotg_init()
+ */
+ musbotg_uninit(&sc->sc_otg);
+
+ err = bus_teardown_intr(dev, sc->sc_otg.sc_irq_res,
+ sc->sc_otg.sc_intr_hdl);
+ sc->sc_otg.sc_intr_hdl = NULL;
+ }
+ /* free IRQ channel, if any */
+ if (sc->sc_otg.sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->sc_otg.sc_irq_res);
+ sc->sc_otg.sc_irq_res = NULL;
+ }
+ /* free memory resource, if any */
+ if (sc->sc_otg.sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, 0,
+ sc->sc_otg.sc_io_res);
+ sc->sc_otg.sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_otg.sc_bus, NULL);
+
+ return (0);
+}
+
+static int
+musbotg_shutdown(device_t dev)
+{
+ struct musbotg_super_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_shutdown(dev);
+ if (err)
+ return (err);
+
+ musbotg_uninit(&sc->sc_otg);
+
+ return (0);
+}
+
+static device_method_t musbotg_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, musbotg_probe),
+ DEVMETHOD(device_attach, musbotg_attach),
+ DEVMETHOD(device_detach, musbotg_detach),
+ DEVMETHOD(device_shutdown, musbotg_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t musbotg_driver = {
+ "musbotg",
+ musbotg_methods,
+ sizeof(struct musbotg_super_softc),
+};
+
+static devclass_t musbotg_devclass;
+
+DRIVER_MODULE(musbotg, atmelarm, musbotg_driver, musbotg_devclass, 0, 0);
+MODULE_DEPEND(musbotg, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/ohci.c b/sys/dev/usb/controller/ohci.c
new file mode 100644
index 0000000..7751bd5
--- /dev/null
+++ b/sys/dev/usb/controller/ohci.c
@@ -0,0 +1,2862 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Open Host Controller driver.
+ *
+ * OHCI spec: http://www.compaq.com/productinfo/development/openhci.html
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR ohcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ohci.h>
+
+#define OHCI_BUS2SC(bus) ((ohci_softc_t *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((ohci_softc_t *)0)->sc_bus))))
+
+#if USB_DEBUG
+static int ohcidebug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci");
+SYSCTL_INT(_hw_usb2_ohci, OID_AUTO, debug, CTLFLAG_RW,
+ &ohcidebug, 0, "ohci debug level");
+static void ohci_dumpregs(ohci_softc_t *);
+static void ohci_dump_tds(ohci_td_t *);
+static uint8_t ohci_dump_td(ohci_td_t *);
+static void ohci_dump_ed(ohci_ed_t *);
+static uint8_t ohci_dump_itd(ohci_itd_t *);
+static void ohci_dump_itds(ohci_itd_t *);
+
+#endif
+
+#define OBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define OWRITE1(sc, r, x) \
+ do { OBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0)
+#define OWRITE2(sc, r, x) \
+ do { OBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0)
+#define OWRITE4(sc, r, x) \
+ do { OBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0)
+#define OREAD1(sc, r) (OBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+#define OREAD2(sc, r) (OBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+#define OREAD4(sc, r) (OBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+
+#define OHCI_INTR_ENDPT 1
+
+extern struct usb2_bus_methods ohci_bus_methods;
+extern struct usb2_pipe_methods ohci_device_bulk_methods;
+extern struct usb2_pipe_methods ohci_device_ctrl_methods;
+extern struct usb2_pipe_methods ohci_device_intr_methods;
+extern struct usb2_pipe_methods ohci_device_isoc_methods;
+extern struct usb2_pipe_methods ohci_root_ctrl_methods;
+extern struct usb2_pipe_methods ohci_root_intr_methods;
+
+static void ohci_root_ctrl_poll(struct ohci_softc *sc);
+static void ohci_do_poll(struct usb2_bus *bus);
+static void ohci_device_done(struct usb2_xfer *xfer, usb2_error_t error);
+
+static usb2_sw_transfer_func_t ohci_root_intr_done;
+static usb2_sw_transfer_func_t ohci_root_ctrl_done;
+static void ohci_timeout(void *arg);
+static uint8_t ohci_check_transfer(struct usb2_xfer *xfer);
+
+struct ohci_std_temp {
+ struct usb2_page_cache *pc;
+ ohci_td_t *td;
+ ohci_td_t *td_next;
+ uint32_t average;
+ uint32_t td_flags;
+ uint32_t len;
+ uint16_t max_frame_size;
+ uint8_t shortpkt;
+ uint8_t setup_alt_next;
+ uint8_t short_frames_ok;
+};
+
+static struct ohci_hcca *
+ohci_get_hcca(ohci_softc_t *sc)
+{
+ usb2_pc_cpu_invalidate(&sc->sc_hw.hcca_pc);
+ return (sc->sc_hcca_p);
+}
+
+void
+ohci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(bus);
+ uint32_t i;
+
+ cb(bus, &sc->sc_hw.hcca_pc, &sc->sc_hw.hcca_pg,
+ sizeof(ohci_hcca_t), OHCI_HCCA_ALIGN);
+
+ cb(bus, &sc->sc_hw.ctrl_start_pc, &sc->sc_hw.ctrl_start_pg,
+ sizeof(ohci_ed_t), OHCI_ED_ALIGN);
+
+ cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg,
+ sizeof(ohci_ed_t), OHCI_ED_ALIGN);
+
+ cb(bus, &sc->sc_hw.isoc_start_pc, &sc->sc_hw.isoc_start_pg,
+ sizeof(ohci_ed_t), OHCI_ED_ALIGN);
+
+ for (i = 0; i != OHCI_NO_EDS; i++) {
+ cb(bus, sc->sc_hw.intr_start_pc + i, sc->sc_hw.intr_start_pg + i,
+ sizeof(ohci_ed_t), OHCI_ED_ALIGN);
+ }
+}
+
+static usb2_error_t
+ohci_controller_init(ohci_softc_t *sc)
+{
+ struct usb2_page_search buf_res;
+ uint32_t i;
+ uint32_t ctl;
+ uint32_t ival;
+ uint32_t hcr;
+ uint32_t fm;
+ uint32_t per;
+ uint32_t desca;
+
+ /* Determine in what context we are running. */
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ if (ctl & OHCI_IR) {
+ /* SMM active, request change */
+ DPRINTF("SMM active, request owner change\n");
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_OCR);
+ for (i = 0; (i < 100) && (ctl & OHCI_IR); i++) {
+ usb2_pause_mtx(NULL, hz / 1000);
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ }
+ if (ctl & OHCI_IR) {
+ device_printf(sc->sc_bus.bdev,
+ "SMM does not respond, resetting\n");
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ goto reset;
+ }
+ } else {
+ DPRINTF("cold started\n");
+reset:
+ /* controller was cold started */
+ usb2_pause_mtx(NULL,
+ USB_MS_TO_TICKS(USB_BUS_RESET_DELAY));
+ }
+
+ /*
+ * This reset should not be necessary according to the OHCI spec, but
+ * without it some controllers do not start.
+ */
+ DPRINTF("%s: resetting\n", device_get_nameunit(sc->sc_bus.bdev));
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+
+ usb2_pause_mtx(NULL,
+ USB_MS_TO_TICKS(USB_BUS_RESET_DELAY));
+
+ /* we now own the host controller and the bus has been reset */
+ ival = OHCI_GET_IVAL(OREAD4(sc, OHCI_FM_INTERVAL));
+
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_HCR); /* Reset HC */
+ /* nominal time for a reset is 10 us */
+ for (i = 0; i < 10; i++) {
+ DELAY(10);
+ hcr = OREAD4(sc, OHCI_COMMAND_STATUS) & OHCI_HCR;
+ if (!hcr) {
+ break;
+ }
+ }
+ if (hcr) {
+ device_printf(sc->sc_bus.bdev, "reset timeout\n");
+ return (USB_ERR_IOERROR);
+ }
+#if USB_DEBUG
+ if (ohcidebug > 15) {
+ ohci_dumpregs(sc);
+ }
+#endif
+
+ /* The controller is now in SUSPEND state, we have 2ms to finish. */
+
+ /* set up HC registers */
+ usb2_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res);
+ OWRITE4(sc, OHCI_HCCA, buf_res.physaddr);
+
+ usb2_get_page(&sc->sc_hw.ctrl_start_pc, 0, &buf_res);
+ OWRITE4(sc, OHCI_CONTROL_HEAD_ED, buf_res.physaddr);
+
+ usb2_get_page(&sc->sc_hw.bulk_start_pc, 0, &buf_res);
+ OWRITE4(sc, OHCI_BULK_HEAD_ED, buf_res.physaddr);
+
+ /* disable all interrupts and then switch on all desired interrupts */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE, sc->sc_eintrs | OHCI_MIE);
+ /* switch on desired functional features */
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ ctl &= ~(OHCI_CBSR_MASK | OHCI_LES | OHCI_HCFS_MASK | OHCI_IR);
+ ctl |= OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE |
+ OHCI_RATIO_1_4 | OHCI_HCFS_OPERATIONAL;
+ /* And finally start it! */
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+
+ /*
+ * The controller is now OPERATIONAL. Set a some final
+ * registers that should be set earlier, but that the
+ * controller ignores when in the SUSPEND state.
+ */
+ fm = (OREAD4(sc, OHCI_FM_INTERVAL) & OHCI_FIT) ^ OHCI_FIT;
+ fm |= OHCI_FSMPS(ival) | ival;
+ OWRITE4(sc, OHCI_FM_INTERVAL, fm);
+ per = OHCI_PERIODIC(ival); /* 90% periodic */
+ OWRITE4(sc, OHCI_PERIODIC_START, per);
+
+ /* Fiddle the No OverCurrent Protection bit to avoid chip bug. */
+ desca = OREAD4(sc, OHCI_RH_DESCRIPTOR_A);
+ OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca | OHCI_NOCP);
+ OWRITE4(sc, OHCI_RH_STATUS, OHCI_LPSC); /* Enable port power */
+ usb2_pause_mtx(NULL,
+ USB_MS_TO_TICKS(OHCI_ENABLE_POWER_DELAY));
+ OWRITE4(sc, OHCI_RH_DESCRIPTOR_A, desca);
+
+ /*
+ * The AMD756 requires a delay before re-reading the register,
+ * otherwise it will occasionally report 0 ports.
+ */
+ sc->sc_noport = 0;
+ for (i = 0; (i < 10) && (sc->sc_noport == 0); i++) {
+ usb2_pause_mtx(NULL,
+ USB_MS_TO_TICKS(OHCI_READ_DESC_DELAY));
+ sc->sc_noport = OHCI_GET_NDP(OREAD4(sc, OHCI_RH_DESCRIPTOR_A));
+ }
+
+#if USB_DEBUG
+ if (ohcidebug > 5) {
+ ohci_dumpregs(sc);
+ }
+#endif
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static struct ohci_ed *
+ohci_init_ed(struct usb2_page_cache *pc)
+{
+ struct usb2_page_search buf_res;
+ struct ohci_ed *ed;
+
+ usb2_get_page(pc, 0, &buf_res);
+
+ ed = buf_res.buffer;
+
+ ed->ed_self = htole32(buf_res.physaddr);
+ ed->ed_flags = htole32(OHCI_ED_SKIP);
+ ed->page_cache = pc;
+
+ return (ed);
+}
+
+usb2_error_t
+ohci_init(ohci_softc_t *sc)
+{
+ struct usb2_page_search buf_res;
+ uint16_t i;
+ uint16_t bit;
+ uint16_t x;
+ uint16_t y;
+
+ DPRINTF("start\n");
+
+ sc->sc_eintrs = OHCI_NORMAL_INTRS;
+
+ /*
+ * Setup all ED's
+ */
+
+ sc->sc_ctrl_p_last =
+ ohci_init_ed(&sc->sc_hw.ctrl_start_pc);
+
+ sc->sc_bulk_p_last =
+ ohci_init_ed(&sc->sc_hw.bulk_start_pc);
+
+ sc->sc_isoc_p_last =
+ ohci_init_ed(&sc->sc_hw.isoc_start_pc);
+
+ for (i = 0; i != OHCI_NO_EDS; i++) {
+ sc->sc_intr_p_last[i] =
+ ohci_init_ed(sc->sc_hw.intr_start_pc + i);
+ }
+
+ /*
+ * the QHs are arranged to give poll intervals that are
+ * powers of 2 times 1ms
+ */
+ bit = OHCI_NO_EDS / 2;
+ while (bit) {
+ x = bit;
+ while (x & bit) {
+ ohci_ed_t *ed_x;
+ ohci_ed_t *ed_y;
+
+ y = (x ^ bit) | (bit / 2);
+
+ /*
+ * the next QH has half the poll interval
+ */
+ ed_x = sc->sc_intr_p_last[x];
+ ed_y = sc->sc_intr_p_last[y];
+
+ ed_x->next = NULL;
+ ed_x->ed_next = ed_y->ed_self;
+
+ x++;
+ }
+ bit >>= 1;
+ }
+
+ if (1) {
+
+ ohci_ed_t *ed_int;
+ ohci_ed_t *ed_isc;
+
+ ed_int = sc->sc_intr_p_last[0];
+ ed_isc = sc->sc_isoc_p_last;
+
+ /* the last (1ms) QH */
+ ed_int->next = ed_isc;
+ ed_int->ed_next = ed_isc->ed_self;
+ }
+ usb2_get_page(&sc->sc_hw.hcca_pc, 0, &buf_res);
+
+ sc->sc_hcca_p = buf_res.buffer;
+
+ /*
+ * Fill HCCA interrupt table. The bit reversal is to get
+ * the tree set up properly to spread the interrupts.
+ */
+ for (i = 0; i != OHCI_NO_INTRS; i++) {
+ sc->sc_hcca_p->hcca_interrupt_table[i] =
+ sc->sc_intr_p_last[i | (OHCI_NO_EDS / 2)]->ed_self;
+ }
+ /* flush all cache into memory */
+
+ usb2_bus_mem_flush_all(&sc->sc_bus, &ohci_iterate_hw_softc);
+
+ /* set up the bus struct */
+ sc->sc_bus.methods = &ohci_bus_methods;
+
+ usb2_callout_init_mtx(&sc->sc_tmo_rhsc, &sc->sc_bus.bus_mtx, 0);
+
+#if USB_DEBUG
+ if (ohcidebug > 15) {
+ for (i = 0; i != OHCI_NO_EDS; i++) {
+ printf("ed#%d ", i);
+ ohci_dump_ed(sc->sc_intr_p_last[i]);
+ }
+ printf("iso ");
+ ohci_dump_ed(sc->sc_isoc_p_last);
+ }
+#endif
+
+ sc->sc_bus.usbrev = USB_REV_1_0;
+
+ if (ohci_controller_init(sc)) {
+ return (USB_ERR_INVAL);
+ } else {
+ /* catch any lost interrupts */
+ ohci_do_poll(&sc->sc_bus);
+ return (USB_ERR_NORMAL_COMPLETION);
+ }
+}
+
+/*
+ * shut down the controller when the system is going down
+ */
+void
+ohci_detach(struct ohci_softc *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ usb2_callout_stop(&sc->sc_tmo_rhsc);
+
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_ALL_INTRS);
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* XXX let stray task complete */
+ usb2_pause_mtx(NULL, hz / 20);
+
+ usb2_callout_drain(&sc->sc_tmo_rhsc);
+}
+
+/* NOTE: suspend/resume is called from
+ * interrupt context and cannot sleep!
+ */
+void
+ohci_suspend(ohci_softc_t *sc)
+{
+ uint32_t ctl;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+#if USB_DEBUG
+ DPRINTF("\n");
+ if (ohcidebug > 2) {
+ ohci_dumpregs(sc);
+ }
+#endif
+
+ ctl = OREAD4(sc, OHCI_CONTROL) & ~OHCI_HCFS_MASK;
+ if (sc->sc_control == 0) {
+ /*
+ * Preserve register values, in case that APM BIOS
+ * does not recover them.
+ */
+ sc->sc_control = ctl;
+ sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE);
+ }
+ ctl |= OHCI_HCFS_SUSPEND;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_RESUME_WAIT));
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+ohci_resume(ohci_softc_t *sc)
+{
+ uint32_t ctl;
+
+#if USB_DEBUG
+ DPRINTF("\n");
+ if (ohcidebug > 2) {
+ ohci_dumpregs(sc);
+ }
+#endif
+ /* some broken BIOSes never initialize the Controller chip */
+ ohci_controller_init(sc);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ if (sc->sc_intre) {
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE,
+ sc->sc_intre & (OHCI_ALL_INTRS | OHCI_MIE));
+ }
+ if (sc->sc_control)
+ ctl = sc->sc_control;
+ else
+ ctl = OREAD4(sc, OHCI_CONTROL);
+ ctl |= OHCI_HCFS_RESUME;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_RESUME_DELAY));
+ ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL;
+ OWRITE4(sc, OHCI_CONTROL, ctl);
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_RESUME_RECOVERY));
+ sc->sc_control = sc->sc_intre = 0;
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+ ohci_do_poll(&sc->sc_bus);
+}
+
+#if USB_DEBUG
+static void
+ohci_dumpregs(ohci_softc_t *sc)
+{
+ struct ohci_hcca *hcca;
+
+ DPRINTF("ohci_dumpregs: rev=0x%08x control=0x%08x command=0x%08x\n",
+ OREAD4(sc, OHCI_REVISION),
+ OREAD4(sc, OHCI_CONTROL),
+ OREAD4(sc, OHCI_COMMAND_STATUS));
+ DPRINTF(" intrstat=0x%08x intre=0x%08x intrd=0x%08x\n",
+ OREAD4(sc, OHCI_INTERRUPT_STATUS),
+ OREAD4(sc, OHCI_INTERRUPT_ENABLE),
+ OREAD4(sc, OHCI_INTERRUPT_DISABLE));
+ DPRINTF(" hcca=0x%08x percur=0x%08x ctrlhd=0x%08x\n",
+ OREAD4(sc, OHCI_HCCA),
+ OREAD4(sc, OHCI_PERIOD_CURRENT_ED),
+ OREAD4(sc, OHCI_CONTROL_HEAD_ED));
+ DPRINTF(" ctrlcur=0x%08x bulkhd=0x%08x bulkcur=0x%08x\n",
+ OREAD4(sc, OHCI_CONTROL_CURRENT_ED),
+ OREAD4(sc, OHCI_BULK_HEAD_ED),
+ OREAD4(sc, OHCI_BULK_CURRENT_ED));
+ DPRINTF(" done=0x%08x fmival=0x%08x fmrem=0x%08x\n",
+ OREAD4(sc, OHCI_DONE_HEAD),
+ OREAD4(sc, OHCI_FM_INTERVAL),
+ OREAD4(sc, OHCI_FM_REMAINING));
+ DPRINTF(" fmnum=0x%08x perst=0x%08x lsthrs=0x%08x\n",
+ OREAD4(sc, OHCI_FM_NUMBER),
+ OREAD4(sc, OHCI_PERIODIC_START),
+ OREAD4(sc, OHCI_LS_THRESHOLD));
+ DPRINTF(" desca=0x%08x descb=0x%08x stat=0x%08x\n",
+ OREAD4(sc, OHCI_RH_DESCRIPTOR_A),
+ OREAD4(sc, OHCI_RH_DESCRIPTOR_B),
+ OREAD4(sc, OHCI_RH_STATUS));
+ DPRINTF(" port1=0x%08x port2=0x%08x\n",
+ OREAD4(sc, OHCI_RH_PORT_STATUS(1)),
+ OREAD4(sc, OHCI_RH_PORT_STATUS(2)));
+
+ hcca = ohci_get_hcca(sc);
+
+ DPRINTF(" HCCA: frame_number=0x%04x done_head=0x%08x\n",
+ le32toh(hcca->hcca_frame_number),
+ le32toh(hcca->hcca_done_head));
+}
+static void
+ohci_dump_tds(ohci_td_t *std)
+{
+ for (; std; std = std->obj_next) {
+ if (ohci_dump_td(std)) {
+ break;
+ }
+ }
+}
+
+static uint8_t
+ohci_dump_td(ohci_td_t *std)
+{
+ uint32_t td_flags;
+ uint8_t temp;
+
+ usb2_pc_cpu_invalidate(std->page_cache);
+
+ td_flags = le32toh(std->td_flags);
+ temp = (std->td_next == 0);
+
+ printf("TD(%p) at 0x%08x: %s%s%s%s%s delay=%d ec=%d "
+ "cc=%d\ncbp=0x%08x next=0x%08x be=0x%08x\n",
+ std, le32toh(std->td_self),
+ (td_flags & OHCI_TD_R) ? "-R" : "",
+ (td_flags & OHCI_TD_OUT) ? "-OUT" : "",
+ (td_flags & OHCI_TD_IN) ? "-IN" : "",
+ ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_1) ? "-TOG1" : "",
+ ((td_flags & OHCI_TD_TOGGLE_MASK) == OHCI_TD_TOGGLE_0) ? "-TOG0" : "",
+ OHCI_TD_GET_DI(td_flags),
+ OHCI_TD_GET_EC(td_flags),
+ OHCI_TD_GET_CC(td_flags),
+ le32toh(std->td_cbp),
+ le32toh(std->td_next),
+ le32toh(std->td_be));
+
+ return (temp);
+}
+
+static uint8_t
+ohci_dump_itd(ohci_itd_t *sitd)
+{
+ uint32_t itd_flags;
+ uint16_t i;
+ uint8_t temp;
+
+ usb2_pc_cpu_invalidate(sitd->page_cache);
+
+ itd_flags = le32toh(sitd->itd_flags);
+ temp = (sitd->itd_next == 0);
+
+ printf("ITD(%p) at 0x%08x: sf=%d di=%d fc=%d cc=%d\n"
+ "bp0=0x%08x next=0x%08x be=0x%08x\n",
+ sitd, le32toh(sitd->itd_self),
+ OHCI_ITD_GET_SF(itd_flags),
+ OHCI_ITD_GET_DI(itd_flags),
+ OHCI_ITD_GET_FC(itd_flags),
+ OHCI_ITD_GET_CC(itd_flags),
+ le32toh(sitd->itd_bp0),
+ le32toh(sitd->itd_next),
+ le32toh(sitd->itd_be));
+ for (i = 0; i < OHCI_ITD_NOFFSET; i++) {
+ printf("offs[%d]=0x%04x ", i,
+ (uint32_t)le16toh(sitd->itd_offset[i]));
+ }
+ printf("\n");
+
+ return (temp);
+}
+
+static void
+ohci_dump_itds(ohci_itd_t *sitd)
+{
+ for (; sitd; sitd = sitd->obj_next) {
+ if (ohci_dump_itd(sitd)) {
+ break;
+ }
+ }
+}
+
+static void
+ohci_dump_ed(ohci_ed_t *sed)
+{
+ uint32_t ed_flags;
+ uint32_t ed_headp;
+
+ usb2_pc_cpu_invalidate(sed->page_cache);
+
+ ed_flags = le32toh(sed->ed_flags);
+ ed_headp = le32toh(sed->ed_headp);
+
+ printf("ED(%p) at 0x%08x: addr=%d endpt=%d maxp=%d flags=%s%s%s%s%s\n"
+ "tailp=0x%08x headflags=%s%s headp=0x%08x nexted=0x%08x\n",
+ sed, le32toh(sed->ed_self),
+ OHCI_ED_GET_FA(ed_flags),
+ OHCI_ED_GET_EN(ed_flags),
+ OHCI_ED_GET_MAXP(ed_flags),
+ (ed_flags & OHCI_ED_DIR_OUT) ? "-OUT" : "",
+ (ed_flags & OHCI_ED_DIR_IN) ? "-IN" : "",
+ (ed_flags & OHCI_ED_SPEED) ? "-LOWSPEED" : "",
+ (ed_flags & OHCI_ED_SKIP) ? "-SKIP" : "",
+ (ed_flags & OHCI_ED_FORMAT_ISO) ? "-ISO" : "",
+ le32toh(sed->ed_tailp),
+ (ed_headp & OHCI_HALTED) ? "-HALTED" : "",
+ (ed_headp & OHCI_TOGGLECARRY) ? "-CARRY" : "",
+ le32toh(sed->ed_headp),
+ le32toh(sed->ed_next));
+}
+
+#endif
+
+static void
+ohci_transfer_intr_enqueue(struct usb2_xfer *xfer)
+{
+ /* check for early completion */
+ if (ohci_check_transfer(xfer)) {
+ return;
+ }
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer, &ohci_timeout, xfer->timeout);
+ }
+}
+
+#define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last)
+static ohci_ed_t *
+_ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last)
+{
+ DPRINTFN(11, "%p to %p\n", sed, last);
+
+ if (sed->prev != NULL) {
+ /* should not happen */
+ DPRINTFN(0, "ED already linked!\n");
+ return (last);
+ }
+ /* (sc->sc_bus.bus_mtx) must be locked */
+
+ sed->next = last->next;
+ sed->ed_next = last->ed_next;
+ sed->ed_tailp = 0;
+
+ sed->prev = last;
+
+ usb2_pc_cpu_flush(sed->page_cache);
+
+ /*
+ * the last->next->prev is never followed: sed->next->prev = sed;
+ */
+
+ last->next = sed;
+ last->ed_next = sed->ed_self;
+
+ usb2_pc_cpu_flush(last->page_cache);
+
+ return (sed);
+}
+
+#define OHCI_REMOVE_QH(sed,last) (last) = _ohci_remove_qh(sed,last)
+static ohci_ed_t *
+_ohci_remove_qh(ohci_ed_t *sed, ohci_ed_t *last)
+{
+ DPRINTFN(11, "%p from %p\n", sed, last);
+
+ /* (sc->sc_bus.bus_mtx) must be locked */
+
+ /* only remove if not removed from a queue */
+ if (sed->prev) {
+
+ sed->prev->next = sed->next;
+ sed->prev->ed_next = sed->ed_next;
+
+ usb2_pc_cpu_flush(sed->prev->page_cache);
+
+ if (sed->next) {
+ sed->next->prev = sed->prev;
+ usb2_pc_cpu_flush(sed->next->page_cache);
+ }
+ last = ((last == sed) ? sed->prev : last);
+
+ sed->prev = 0;
+
+ usb2_pc_cpu_flush(sed->page_cache);
+ }
+ return (last);
+}
+
+static void
+ohci_isoc_done(struct usb2_xfer *xfer)
+{
+ uint8_t nframes;
+ uint32_t *plen = xfer->frlengths;
+ volatile uint16_t *olen;
+ uint16_t len = 0;
+ ohci_itd_t *td = xfer->td_transfer_first;
+
+ while (1) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+#if USB_DEBUG
+ if (ohcidebug > 5) {
+ DPRINTF("isoc TD\n");
+ ohci_dump_itd(td);
+ }
+#endif
+ usb2_pc_cpu_invalidate(td->page_cache);
+
+ nframes = td->frames;
+ olen = &td->itd_offset[0];
+
+ if (nframes > 8) {
+ nframes = 8;
+ }
+ while (nframes--) {
+ len = le16toh(*olen);
+
+ if ((len >> 12) == OHCI_CC_NOT_ACCESSED) {
+ len = 0;
+ } else {
+ len &= ((1 << 12) - 1);
+ }
+
+ if (len > *plen) {
+ len = 0;/* invalid length */
+ }
+ *plen = len;
+ plen++;
+ olen++;
+ }
+
+ if (((void *)td) == xfer->td_transfer_last) {
+ break;
+ }
+ td = td->obj_next;
+ }
+
+ xfer->aframes = xfer->nframes;
+ ohci_device_done(xfer, USB_ERR_NORMAL_COMPLETION);
+}
+
+#if USB_DEBUG
+static const char *const
+ ohci_cc_strs[] =
+{
+ "NO_ERROR",
+ "CRC",
+ "BIT_STUFFING",
+ "DATA_TOGGLE_MISMATCH",
+
+ "STALL",
+ "DEVICE_NOT_RESPONDING",
+ "PID_CHECK_FAILURE",
+ "UNEXPECTED_PID",
+
+ "DATA_OVERRUN",
+ "DATA_UNDERRUN",
+ "BUFFER_OVERRUN",
+ "BUFFER_UNDERRUN",
+
+ "reserved",
+ "reserved",
+ "NOT_ACCESSED",
+ "NOT_ACCESSED"
+};
+
+#endif
+
+static usb2_error_t
+ohci_non_isoc_done_sub(struct usb2_xfer *xfer)
+{
+ ohci_td_t *td;
+ ohci_td_t *td_alt_next;
+ uint32_t temp;
+ uint32_t phy_start;
+ uint32_t phy_end;
+ uint32_t td_flags;
+ uint16_t cc;
+
+ td = xfer->td_transfer_cache;
+ td_alt_next = td->alt_next;
+ td_flags = 0;
+
+ if (xfer->aframes != xfer->nframes) {
+ xfer->frlengths[xfer->aframes] = 0;
+ }
+ while (1) {
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ phy_start = le32toh(td->td_cbp);
+ td_flags = le32toh(td->td_flags);
+ cc = OHCI_TD_GET_CC(td_flags);
+
+ if (phy_start) {
+ /*
+ * short transfer - compute the number of remaining
+ * bytes in the hardware buffer:
+ */
+ phy_end = le32toh(td->td_be);
+ temp = (OHCI_PAGE(phy_start ^ phy_end) ?
+ (OHCI_PAGE_SIZE + 1) : 0x0001);
+ temp += OHCI_PAGE_OFFSET(phy_end);
+ temp -= OHCI_PAGE_OFFSET(phy_start);
+
+ if (temp > td->len) {
+ /* guard against corruption */
+ cc = OHCI_CC_STALL;
+ } else if (xfer->aframes != xfer->nframes) {
+ /*
+ * Sum up total transfer length
+ * in "frlengths[]":
+ */
+ xfer->frlengths[xfer->aframes] += td->len - temp;
+ }
+ } else {
+ if (xfer->aframes != xfer->nframes) {
+ /* transfer was complete */
+ xfer->frlengths[xfer->aframes] += td->len;
+ }
+ }
+ /* Check for last transfer */
+ if (((void *)td) == xfer->td_transfer_last) {
+ td = NULL;
+ break;
+ }
+ /* Check transfer status */
+ if (cc) {
+ /* the transfer is finished */
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (phy_start) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ td = td->alt_next;
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ break;
+ }
+ td = td->obj_next;
+
+ if (td->alt_next != td_alt_next) {
+ /* this USB frame is complete */
+ break;
+ }
+ }
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ DPRINTFN(16, "error cc=%d (%s)\n",
+ cc, ohci_cc_strs[cc]);
+
+ return ((cc == 0) ? USB_ERR_NORMAL_COMPLETION :
+ (cc == OHCI_CC_STALL) ? USB_ERR_STALLED : USB_ERR_IOERROR);
+}
+
+static void
+ohci_non_isoc_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+#if USB_DEBUG
+ if (ohcidebug > 10) {
+ ohci_dump_tds(xfer->td_transfer_first);
+ }
+#endif
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = ohci_non_isoc_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = ohci_non_isoc_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = ohci_non_isoc_done_sub(xfer);
+ }
+done:
+ ohci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * ohci_check_transfer_sub
+ *------------------------------------------------------------------------*/
+static void
+ohci_check_transfer_sub(struct usb2_xfer *xfer)
+{
+ ohci_td_t *td;
+ ohci_ed_t *ed;
+ uint32_t phy_start;
+ uint32_t td_flags;
+ uint32_t td_next;
+ uint16_t cc;
+
+ td = xfer->td_transfer_cache;
+
+ while (1) {
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ phy_start = le32toh(td->td_cbp);
+ td_flags = le32toh(td->td_flags);
+ td_next = le32toh(td->td_next);
+
+ /* Check for last transfer */
+ if (((void *)td) == xfer->td_transfer_last) {
+ /* the transfer is finished */
+ td = NULL;
+ break;
+ }
+ /* Check transfer status */
+ cc = OHCI_TD_GET_CC(td_flags);
+ if (cc) {
+ /* the transfer is finished */
+ td = NULL;
+ break;
+ }
+ /*
+ * Check if we reached the last packet
+ * or if there is a short packet:
+ */
+
+ if (((td_next & (~0xF)) == OHCI_TD_NEXT_END) || phy_start) {
+ /* follow alt next */
+ td = td->alt_next;
+ break;
+ }
+ td = td->obj_next;
+ }
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ if (td) {
+
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ ed->ed_headp = td->td_self;
+ usb2_pc_cpu_flush(ed->page_cache);
+
+ DPRINTFN(13, "xfer=%p following alt next\n", xfer);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * ohci_check_transfer
+ *
+ * Return values:
+ * 0: USB transfer is not finished
+ * Else: USB transfer is finished
+ *------------------------------------------------------------------------*/
+static uint8_t
+ohci_check_transfer(struct usb2_xfer *xfer)
+{
+ ohci_ed_t *ed;
+ uint32_t ed_flags;
+ uint32_t ed_headp;
+ uint32_t ed_tailp;
+
+ DPRINTFN(13, "xfer=%p checking transfer\n", xfer);
+
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ usb2_pc_cpu_invalidate(ed->page_cache);
+ ed_flags = le32toh(ed->ed_flags);
+ ed_headp = le32toh(ed->ed_headp);
+ ed_tailp = le32toh(ed->ed_tailp);
+
+ if ((ed_flags & OHCI_ED_SKIP) ||
+ (ed_headp & OHCI_HALTED) ||
+ (((ed_headp ^ ed_tailp) & (~0xF)) == 0)) {
+ if (xfer->pipe->methods == &ohci_device_isoc_methods) {
+ /* isochronous transfer */
+ ohci_isoc_done(xfer);
+ } else {
+ if (xfer->flags_int.short_frames_ok) {
+ ohci_check_transfer_sub(xfer);
+ if (xfer->td_transfer_cache) {
+ /* not finished yet */
+ return (0);
+ }
+ }
+ /* store data-toggle */
+ if (ed_headp & OHCI_TOGGLECARRY) {
+ xfer->pipe->toggle_next = 1;
+ } else {
+ xfer->pipe->toggle_next = 0;
+ }
+
+ /* non-isochronous transfer */
+ ohci_non_isoc_done(xfer);
+ }
+ return (1);
+ }
+ DPRINTFN(13, "xfer=%p is still active\n", xfer);
+ return (0);
+}
+
+static void
+ohci_rhsc_enable(ohci_softc_t *sc)
+{
+ DPRINTFN(5, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ sc->sc_eintrs |= OHCI_RHSC;
+ OWRITE4(sc, OHCI_INTERRUPT_ENABLE, OHCI_RHSC);
+
+ /* acknowledge any RHSC interrupt */
+ OWRITE4(sc, OHCI_INTERRUPT_STATUS, OHCI_RHSC);
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &ohci_root_intr_done);
+}
+
+static void
+ohci_interrupt_poll(ohci_softc_t *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ /*
+ * check if transfer is transferred
+ */
+ if (ohci_check_transfer(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * ohci_interrupt - OHCI interrupt handler
+ *
+ * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler,
+ * hence the interrupt handler will be setup before "sc->sc_bus.bdev"
+ * is present !
+ *------------------------------------------------------------------------*/
+void
+ohci_interrupt(ohci_softc_t *sc)
+{
+ struct ohci_hcca *hcca;
+ uint32_t status;
+ uint32_t done;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ hcca = ohci_get_hcca(sc);
+
+ DPRINTFN(16, "real interrupt\n");
+
+#if USB_DEBUG
+ if (ohcidebug > 15) {
+ ohci_dumpregs(sc);
+ }
+#endif
+
+ done = le32toh(hcca->hcca_done_head);
+
+ /*
+ * The LSb of done is used to inform the HC Driver that an interrupt
+ * condition exists for both the Done list and for another event
+ * recorded in HcInterruptStatus. On an interrupt from the HC, the
+ * HC Driver checks the HccaDoneHead Value. If this value is 0, then
+ * the interrupt was caused by other than the HccaDoneHead update
+ * and the HcInterruptStatus register needs to be accessed to
+ * determine that exact interrupt cause. If HccaDoneHead is nonzero,
+ * then a Done list update interrupt is indicated and if the LSb of
+ * done is nonzero, then an additional interrupt event is indicated
+ * and HcInterruptStatus should be checked to determine its cause.
+ */
+ if (done != 0) {
+ status = 0;
+
+ if (done & ~OHCI_DONE_INTRS) {
+ status |= OHCI_WDH;
+ }
+ if (done & OHCI_DONE_INTRS) {
+ status |= OREAD4(sc, OHCI_INTERRUPT_STATUS);
+ }
+ hcca->hcca_done_head = 0;
+
+ usb2_pc_cpu_flush(&sc->sc_hw.hcca_pc);
+ } else {
+ status = OREAD4(sc, OHCI_INTERRUPT_STATUS) & ~OHCI_WDH;
+ }
+
+ status &= ~OHCI_MIE;
+ if (status == 0) {
+ /*
+ * nothing to be done (PCI shared
+ * interrupt)
+ */
+ goto done;
+ }
+ OWRITE4(sc, OHCI_INTERRUPT_STATUS, status); /* Acknowledge */
+
+ status &= sc->sc_eintrs;
+ if (status == 0) {
+ goto done;
+ }
+ if (status & (OHCI_SO | OHCI_RD | OHCI_UE | OHCI_RHSC)) {
+#if 0
+ if (status & OHCI_SO) {
+ /* XXX do what */
+ }
+#endif
+ if (status & OHCI_RD) {
+ printf("%s: resume detect\n", __FUNCTION__);
+ /* XXX process resume detect */
+ }
+ if (status & OHCI_UE) {
+ printf("%s: unrecoverable error, "
+ "controller halted\n", __FUNCTION__);
+ OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_RESET);
+ /* XXX what else */
+ }
+ if (status & OHCI_RHSC) {
+ /*
+ * Disable RHSC interrupt for now, because it will be
+ * on until the port has been reset.
+ */
+ sc->sc_eintrs &= ~OHCI_RHSC;
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, OHCI_RHSC);
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &ohci_root_intr_done);
+
+ /* do not allow RHSC interrupts > 1 per second */
+ usb2_callout_reset(&sc->sc_tmo_rhsc, hz,
+ (void *)&ohci_rhsc_enable, sc);
+ }
+ }
+ status &= ~(OHCI_RHSC | OHCI_WDH | OHCI_SO);
+ if (status != 0) {
+ /* Block unprocessed interrupts. XXX */
+ OWRITE4(sc, OHCI_INTERRUPT_DISABLE, status);
+ sc->sc_eintrs &= ~status;
+ printf("%s: blocking intrs 0x%x\n",
+ __FUNCTION__, status);
+ }
+ /* poll all the USB transfers */
+ ohci_interrupt_poll(sc);
+
+done:
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*
+ * called when a request does not complete
+ */
+static void
+ohci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ ohci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+ohci_do_poll(struct usb2_bus *bus)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ ohci_interrupt_poll(sc);
+ ohci_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+ohci_setup_standard_chain_sub(struct ohci_std_temp *temp)
+{
+ struct usb2_page_search buf_res;
+ ohci_td_t *td;
+ ohci_td_t *td_next;
+ ohci_td_t *td_alt_next;
+ uint32_t buf_offset;
+ uint32_t average;
+ uint32_t len_old;
+ uint8_t shortpkt_old;
+ uint8_t precompute;
+
+ td_alt_next = NULL;
+ buf_offset = 0;
+ shortpkt_old = temp->shortpkt;
+ len_old = temp->len;
+ precompute = 1;
+
+ /* software is used to detect short incoming transfers */
+
+ if ((temp->td_flags & htole32(OHCI_TD_DP_MASK)) == htole32(OHCI_TD_IN)) {
+ temp->td_flags |= htole32(OHCI_TD_R);
+ } else {
+ temp->td_flags &= ~htole32(OHCI_TD_R);
+ }
+
+restart:
+
+ td = temp->td;
+ td_next = temp->td_next;
+
+ while (1) {
+
+ if (temp->len == 0) {
+
+ if (temp->shortpkt) {
+ break;
+ }
+ /* send a Zero Length Packet, ZLP, last */
+
+ temp->shortpkt = 1;
+ average = 0;
+
+ } else {
+
+ average = temp->average;
+
+ if (temp->len < average) {
+ if (temp->len % temp->max_frame_size) {
+ temp->shortpkt = 1;
+ }
+ average = temp->len;
+ }
+ }
+
+ if (td_next == NULL) {
+ panic("%s: out of OHCI transfer descriptors!", __FUNCTION__);
+ }
+ /* get next TD */
+
+ td = td_next;
+ td_next = td->obj_next;
+
+ /* check if we are pre-computing */
+
+ if (precompute) {
+
+ /* update remaining length */
+
+ temp->len -= average;
+
+ continue;
+ }
+ /* fill out current TD */
+ td->td_flags = temp->td_flags;
+
+ /* the next TD uses TOGGLE_CARRY */
+ temp->td_flags &= ~htole32(OHCI_TD_TOGGLE_MASK);
+
+ if (average == 0) {
+
+ td->td_cbp = 0;
+ td->td_be = ~0;
+ td->len = 0;
+
+ } else {
+
+ usb2_get_page(temp->pc, buf_offset, &buf_res);
+ td->td_cbp = htole32(buf_res.physaddr);
+ buf_offset += (average - 1);
+
+ usb2_get_page(temp->pc, buf_offset, &buf_res);
+ td->td_be = htole32(buf_res.physaddr);
+ buf_offset++;
+
+ td->len = average;
+
+ /* update remaining length */
+
+ temp->len -= average;
+ }
+
+ if ((td_next == td_alt_next) && temp->setup_alt_next) {
+ /* we need to receive these frames one by one ! */
+ td->td_flags &= htole32(~OHCI_TD_INTR_MASK);
+ td->td_flags |= htole32(OHCI_TD_SET_DI(1));
+ td->td_next = htole32(OHCI_TD_NEXT_END);
+ } else {
+ if (td_next) {
+ /* link the current TD with the next one */
+ td->td_next = td_next->td_self;
+ }
+ }
+
+ td->alt_next = td_alt_next;
+
+ usb2_pc_cpu_flush(td->page_cache);
+ }
+
+ if (precompute) {
+ precompute = 0;
+
+ /* setup alt next pointer, if any */
+ if (temp->short_frames_ok) {
+ if (temp->setup_alt_next) {
+ td_alt_next = td_next;
+ }
+ } else {
+ /* we use this field internally */
+ td_alt_next = td_next;
+ }
+
+ /* restore */
+ temp->shortpkt = shortpkt_old;
+ temp->len = len_old;
+ goto restart;
+ }
+ temp->td = td;
+ temp->td_next = td_next;
+}
+
+static void
+ohci_setup_standard_chain(struct usb2_xfer *xfer, ohci_ed_t **ed_last)
+{
+ struct ohci_std_temp temp;
+ struct usb2_pipe_methods *methods;
+ ohci_ed_t *ed;
+ ohci_td_t *td;
+ uint32_t ed_flags;
+ uint32_t x;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.average = xfer->max_usb2_frame_size;
+ temp.max_frame_size = xfer->max_frame_size;
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ temp.td = NULL;
+ temp.td_next = td;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.short_frames_ok = xfer->flags_int.short_frames_ok;
+
+ methods = xfer->pipe->methods;
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.td_flags = htole32(OHCI_TD_SETUP | OHCI_TD_NOCC |
+ OHCI_TD_TOGGLE_0 | OHCI_TD_NOINTR);
+
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.shortpkt = temp.len ? 1 : 0;
+
+ ohci_setup_standard_chain_sub(&temp);
+
+ /*
+ * XXX assume that the setup message is
+ * contained within one USB packet:
+ */
+ xfer->pipe->toggle_next = 1;
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+ temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR);
+
+ /* set data toggle */
+
+ if (xfer->pipe->toggle_next) {
+ temp.td_flags |= htole32(OHCI_TD_TOGGLE_1);
+ } else {
+ temp.td_flags |= htole32(OHCI_TD_TOGGLE_0);
+ }
+
+ /* set endpoint direction */
+
+ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+ temp.td_flags |= htole32(OHCI_TD_IN);
+ } else {
+ temp.td_flags |= htole32(OHCI_TD_OUT);
+ }
+
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+ temp.pc = xfer->frbuffers + x;
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.shortpkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ ohci_setup_standard_chain_sub(&temp);
+ }
+
+ /* check if we should append a status stage */
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ /*
+ * Send a DATA1 message and invert the current endpoint
+ * direction.
+ */
+
+ /* set endpoint direction and data toggle */
+
+ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) {
+ temp.td_flags = htole32(OHCI_TD_OUT |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1));
+ } else {
+ temp.td_flags = htole32(OHCI_TD_IN |
+ OHCI_TD_NOCC | OHCI_TD_TOGGLE_1 | OHCI_TD_SET_DI(1));
+ }
+
+ temp.len = 0;
+ temp.pc = NULL;
+ temp.shortpkt = 0;
+
+ ohci_setup_standard_chain_sub(&temp);
+ }
+ td = temp.td;
+
+ td->td_next = htole32(OHCI_TD_NEXT_END);
+ td->td_flags &= ~htole32(OHCI_TD_INTR_MASK);
+ td->td_flags |= htole32(OHCI_TD_SET_DI(1));
+
+ usb2_pc_cpu_flush(td->page_cache);
+
+ /* must have at least one frame! */
+
+ xfer->td_transfer_last = td;
+
+#if USB_DEBUG
+ if (ohcidebug > 8) {
+ DPRINTF("nexttog=%d; data before transfer:\n",
+ xfer->pipe->toggle_next);
+ ohci_dump_tds(xfer->td_transfer_first);
+ }
+#endif
+
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ ed_flags = (OHCI_ED_SET_FA(xfer->address) |
+ OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) |
+ OHCI_ED_SET_MAXP(xfer->max_frame_size));
+
+ ed_flags |= (OHCI_ED_FORMAT_GEN | OHCI_ED_DIR_TD);
+
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ ed_flags |= OHCI_ED_SPEED;
+ }
+ ed->ed_flags = htole32(ed_flags);
+
+ td = xfer->td_transfer_first;
+
+ ed->ed_headp = td->td_self;
+
+ if (xfer->xroot->udev->pwr_save.suspended == 0) {
+ /* the append function will flush the endpoint descriptor */
+ OHCI_APPEND_QH(ed, *ed_last);
+
+ if (methods == &ohci_device_bulk_methods) {
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ }
+ } else {
+ usb2_pc_cpu_flush(ed->page_cache);
+ }
+}
+
+static void
+ohci_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+ uint32_t hstatus;
+ uint16_t i;
+ uint16_t m;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ ohci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+
+ /* clear any old interrupt data */
+ bzero(sc->sc_hub_idata, sizeof(sc->sc_hub_idata));
+
+ hstatus = OREAD4(sc, OHCI_RH_STATUS);
+ DPRINTF("sc=%p xfer=%p hstatus=0x%08x\n",
+ sc, xfer, hstatus);
+
+ /* set bits */
+ m = (sc->sc_noport + 1);
+ if (m > (8 * sizeof(sc->sc_hub_idata))) {
+ m = (8 * sizeof(sc->sc_hub_idata));
+ }
+ for (i = 1; i < m; i++) {
+ /* pick out CHANGE bits from the status register */
+ if (OREAD4(sc, OHCI_RH_PORT_STATUS(i)) >> 16) {
+ sc->sc_hub_idata[i / 8] |= 1 << (i % 8);
+ DPRINTF("port %d changed\n", i);
+ }
+ }
+done:
+ return;
+}
+
+/* NOTE: "done" can be run two times in a row,
+ * from close and from interrupt
+ */
+static void
+ohci_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ struct usb2_pipe_methods *methods = xfer->pipe->methods;
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+ ohci_ed_t *ed;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+ if (ed) {
+ usb2_pc_cpu_invalidate(ed->page_cache);
+ }
+ if (methods == &ohci_device_bulk_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last);
+ }
+ if (methods == &ohci_device_intr_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ if (methods == &ohci_device_isoc_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_isoc_p_last);
+ }
+ xfer->td_transfer_first = NULL;
+ xfer->td_transfer_last = NULL;
+
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+/*------------------------------------------------------------------------*
+ * ohci bulk support
+ *------------------------------------------------------------------------*/
+static void
+ohci_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_device_bulk_close(struct usb2_xfer *xfer)
+{
+ ohci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ohci_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_device_bulk_start(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ /* setup TD's and QH */
+ ohci_setup_standard_chain(xfer, &sc->sc_bulk_p_last);
+
+ /* put transfer on interrupt queue */
+ ohci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ohci_device_bulk_methods =
+{
+ .open = ohci_device_bulk_open,
+ .close = ohci_device_bulk_close,
+ .enter = ohci_device_bulk_enter,
+ .start = ohci_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ohci control support
+ *------------------------------------------------------------------------*/
+static void
+ohci_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ ohci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ohci_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ /* setup TD's and QH */
+ ohci_setup_standard_chain(xfer, &sc->sc_ctrl_p_last);
+
+ /* put transfer on interrupt queue */
+ ohci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ohci_device_ctrl_methods =
+{
+ .open = ohci_device_ctrl_open,
+ .close = ohci_device_ctrl_close,
+ .enter = ohci_device_ctrl_enter,
+ .start = ohci_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ohci interrupt support
+ *------------------------------------------------------------------------*/
+static void
+ohci_device_intr_open(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+ uint16_t best;
+ uint16_t bit;
+ uint16_t x;
+
+ best = 0;
+ bit = OHCI_NO_EDS / 2;
+ while (bit) {
+ if (xfer->interval >= bit) {
+ x = bit;
+ best = bit;
+ while (x & bit) {
+ if (sc->sc_intr_stat[x] <
+ sc->sc_intr_stat[best]) {
+ best = x;
+ }
+ x++;
+ }
+ break;
+ }
+ bit >>= 1;
+ }
+
+ sc->sc_intr_stat[best]++;
+ xfer->qh_pos = best;
+
+ DPRINTFN(3, "best=%d interval=%d\n",
+ best, xfer->interval);
+}
+
+static void
+ohci_device_intr_close(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_intr_stat[xfer->qh_pos]--;
+
+ ohci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ohci_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_device_intr_start(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ /* setup TD's and QH */
+ ohci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]);
+
+ /* put transfer on interrupt queue */
+ ohci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ohci_device_intr_methods =
+{
+ .open = ohci_device_intr_open,
+ .close = ohci_device_intr_close,
+ .enter = ohci_device_intr_enter,
+ .start = ohci_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ohci isochronous support
+ *------------------------------------------------------------------------*/
+static void
+ohci_device_isoc_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_device_isoc_close(struct usb2_xfer *xfer)
+{
+ /**/
+ ohci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ohci_device_isoc_enter(struct usb2_xfer *xfer)
+{
+ struct usb2_page_search buf_res;
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+ struct ohci_hcca *hcca;
+ uint32_t buf_offset;
+ uint32_t nframes;
+ uint32_t ed_flags;
+ uint32_t *plen;
+ uint16_t itd_offset[OHCI_ITD_NOFFSET];
+ uint16_t length;
+ uint8_t ncur;
+ ohci_itd_t *td;
+ ohci_itd_t *td_last = NULL;
+ ohci_ed_t *ed;
+
+ hcca = ohci_get_hcca(sc);
+
+ nframes = le32toh(hcca->hcca_frame_number);
+
+ DPRINTFN(6, "xfer=%p isoc_next=%u nframes=%u hcca_fn=%u\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes, nframes);
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (((nframes - xfer->pipe->isoc_next) & 0xFFFF) < xfer->nframes) ||
+ (((xfer->pipe->isoc_next - nframes) & 0xFFFF) >= 128)) {
+ /*
+ * If there is data underflow or the pipe queue is empty we
+ * schedule the transfer a few frames ahead of the current
+ * frame position. Else two isochronous transfers might
+ * overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) & 0xFFFF;
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ buf_offset = ((xfer->pipe->isoc_next - nframes) & 0xFFFF);
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ (usb2_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset +
+ xfer->nframes);
+
+ /* get the real number of frames */
+
+ nframes = xfer->nframes;
+
+ buf_offset = 0;
+
+ plen = xfer->frlengths;
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+
+ xfer->td_transfer_first = td;
+
+ ncur = 0;
+ length = 0;
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ itd_offset[ncur] = length;
+ buf_offset += *plen;
+ length += *plen;
+ plen++;
+ ncur++;
+
+ if ( /* check if the ITD is full */
+ (ncur == OHCI_ITD_NOFFSET) ||
+ /* check if we have put more than 4K into the ITD */
+ (length & 0xF000) ||
+ /* check if it is the last frame */
+ (nframes == 0)) {
+
+ /* fill current ITD */
+ td->itd_flags = htole32(
+ OHCI_ITD_NOCC |
+ OHCI_ITD_SET_SF(xfer->pipe->isoc_next) |
+ OHCI_ITD_NOINTR |
+ OHCI_ITD_SET_FC(ncur));
+
+ td->frames = ncur;
+ xfer->pipe->isoc_next += ncur;
+
+ if (length == 0) {
+ /* all zero */
+ td->itd_bp0 = 0;
+ td->itd_be = ~0;
+
+ while (ncur--) {
+ td->itd_offset[ncur] =
+ htole16(OHCI_ITD_MK_OFFS(0));
+ }
+ } else {
+ usb2_get_page(xfer->frbuffers, buf_offset - length, &buf_res);
+ length = OHCI_PAGE_MASK(buf_res.physaddr);
+ buf_res.physaddr =
+ OHCI_PAGE(buf_res.physaddr);
+ td->itd_bp0 = htole32(buf_res.physaddr);
+ usb2_get_page(xfer->frbuffers, buf_offset - 1, &buf_res);
+ td->itd_be = htole32(buf_res.physaddr);
+
+ while (ncur--) {
+ itd_offset[ncur] += length;
+ itd_offset[ncur] =
+ OHCI_ITD_MK_OFFS(itd_offset[ncur]);
+ td->itd_offset[ncur] =
+ htole16(itd_offset[ncur]);
+ }
+ }
+ ncur = 0;
+ length = 0;
+ td_last = td;
+ td = td->obj_next;
+
+ if (td) {
+ /* link the last TD with the next one */
+ td_last->itd_next = td->itd_self;
+ }
+ usb2_pc_cpu_flush(td_last->page_cache);
+ }
+ }
+
+ /* update the last TD */
+ td_last->itd_flags &= ~htole32(OHCI_ITD_NOINTR);
+ td_last->itd_flags |= htole32(OHCI_ITD_SET_DI(0));
+ td_last->itd_next = 0;
+
+ usb2_pc_cpu_flush(td_last->page_cache);
+
+ xfer->td_transfer_last = td_last;
+
+#if USB_DEBUG
+ if (ohcidebug > 8) {
+ DPRINTF("data before transfer:\n");
+ ohci_dump_itds(xfer->td_transfer_first);
+ }
+#endif
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN)
+ ed_flags = (OHCI_ED_DIR_IN | OHCI_ED_FORMAT_ISO);
+ else
+ ed_flags = (OHCI_ED_DIR_OUT | OHCI_ED_FORMAT_ISO);
+
+ ed_flags |= (OHCI_ED_SET_FA(xfer->address) |
+ OHCI_ED_SET_EN(UE_GET_ADDR(xfer->endpoint)) |
+ OHCI_ED_SET_MAXP(xfer->max_frame_size));
+
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ ed_flags |= OHCI_ED_SPEED;
+ }
+ ed->ed_flags = htole32(ed_flags);
+
+ td = xfer->td_transfer_first;
+
+ ed->ed_headp = td->itd_self;
+
+ /* isochronous transfers are not affected by suspend / resume */
+ /* the append function will flush the endpoint descriptor */
+
+ OHCI_APPEND_QH(ed, sc->sc_isoc_p_last);
+}
+
+static void
+ohci_device_isoc_start(struct usb2_xfer *xfer)
+{
+ /* put transfer on interrupt queue */
+ ohci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods ohci_device_isoc_methods =
+{
+ .open = ohci_device_isoc_open,
+ .close = ohci_device_isoc_close,
+ .enter = ohci_device_isoc_enter,
+ .start = ohci_device_isoc_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * ohci root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware hub by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+
+static void
+ohci_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ ohci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/* data structures and routines
+ * to emulate the root hub:
+ */
+static const
+struct usb2_device_descriptor ohci_devd =
+{
+ sizeof(struct usb2_device_descriptor),
+ UDESC_DEVICE, /* type */
+ {0x00, 0x01}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ {0}, {0}, {0x00, 0x01}, /* device id */
+ 1, 2, 0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+static const
+struct ohci_config_desc ohci_confd =
+{
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(ohci_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0, /* max power */
+ },
+
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_FSHUB,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = UE_DIR_IN | OHCI_INTR_ENDPT,
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 32,/* max packet (255 ports) */
+ .bInterval = 255,
+ },
+};
+
+static const
+struct usb2_hub_descriptor ohci_hubd =
+{
+ 0, /* dynamic length */
+ UDESC_HUB,
+ 0,
+ {0, 0},
+ 0,
+ 0,
+ {0},
+};
+
+static void
+ohci_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+ohci_root_ctrl_task(struct usb2_bus *bus)
+{
+ ohci_root_ctrl_poll(OHCI_BUS2SC(bus));
+}
+
+static void
+ohci_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+ char *ptr;
+ uint32_t port;
+ uint32_t v;
+ uint16_t value;
+ uint16_t index;
+ uint8_t l;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ ohci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = sc->sc_hub_desc.temp;
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x "
+ "wValue=0x%04x wIndex=0x%04x\n",
+ std->req.bmRequestType, std->req.bRequest,
+ UGETW(std->req.wLength), value, index);
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(std->req.bRequest, std->req.bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] = sc->sc_conf;
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(ohci_devd);
+ sc->sc_hub_desc.devd = ohci_devd;
+ break;
+
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(ohci_confd);
+ std->ptr = USB_ADD_BYTES(&ohci_confd, 0);
+ break;
+
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ ptr = "\001";
+ break;
+
+ case 1: /* Vendor */
+ ptr = sc->sc_vendor;
+ break;
+
+ case 2: /* Product */
+ ptr = "OHCI root HUB";
+ break;
+
+ default:
+ ptr = "";
+ break;
+ }
+
+ std->len = usb2_make_str_desc
+ (sc->sc_hub_desc.temp,
+ sizeof(sc->sc_hub_desc.temp),
+ ptr);
+ break;
+
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] = 0;
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ std->len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED);
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ std->len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, 0);
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if ((value != 0) && (value != 1)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(9, "UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value);
+ if ((index < 1) ||
+ (index > sc->sc_noport)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ port = OHCI_RH_PORT_STATUS(index);
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_CURRENT_CONNECT_STATUS);
+ break;
+ case UHF_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_OVERCURRENT_INDICATOR);
+ break;
+ case UHF_PORT_POWER:
+ /* Yes, writing to the LOW_SPEED bit clears power. */
+ OWRITE4(sc, port, UPS_LOW_SPEED);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ OWRITE4(sc, port, UPS_C_CONNECT_STATUS << 16);
+ break;
+ case UHF_C_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_C_PORT_ENABLED << 16);
+ break;
+ case UHF_C_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_C_SUSPEND << 16);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ OWRITE4(sc, port, UPS_C_OVERCURRENT_INDICATOR << 16);
+ break;
+ case UHF_C_PORT_RESET:
+ OWRITE4(sc, port, UPS_C_PORT_RESET << 16);
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ switch (value) {
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* enable RHSC interrupt if condition is cleared. */
+ if ((OREAD4(sc, port) >> 16) == 0)
+ ohci_rhsc_enable(sc);
+ break;
+ default:
+ break;
+ }
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ v = OREAD4(sc, OHCI_RH_DESCRIPTOR_A);
+
+ sc->sc_hub_desc.hubd = ohci_hubd;
+ sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport;
+ USETW(sc->sc_hub_desc.hubd.wHubCharacteristics,
+ (v & OHCI_NPS ? UHD_PWR_NO_SWITCH :
+ v & OHCI_PSM ? UHD_PWR_GANGED : UHD_PWR_INDIVIDUAL)
+ /* XXX overcurrent */
+ );
+ sc->sc_hub_desc.hubd.bPwrOn2PwrGood = OHCI_GET_POTPGT(v);
+ v = OREAD4(sc, OHCI_RH_DESCRIPTOR_B);
+
+ for (l = 0; l < sc->sc_noport; l++) {
+ if (v & 1) {
+ sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] |= (1 << (l % 8));
+ }
+ v >>= 1;
+ }
+ sc->sc_hub_desc.hubd.bDescLength =
+ 8 + ((sc->sc_noport + 7) / 8);
+ std->len = sc->sc_hub_desc.hubd.bDescLength;
+ break;
+
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ std->len = 16;
+ bzero(sc->sc_hub_desc.temp, 16);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ DPRINTFN(9, "get port status i=%d\n",
+ index);
+ if ((index < 1) ||
+ (index > sc->sc_noport)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ v = OREAD4(sc, OHCI_RH_PORT_STATUS(index));
+ DPRINTFN(9, "port status=0x%04x\n", v);
+ USETW(sc->sc_hub_desc.ps.wPortStatus, v);
+ USETW(sc->sc_hub_desc.ps.wPortChange, v >> 16);
+ std->len = sizeof(sc->sc_hub_desc.ps);
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if ((index < 1) ||
+ (index > sc->sc_noport)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ port = OHCI_RH_PORT_STATUS(index);
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ OWRITE4(sc, port, UPS_PORT_ENABLED);
+ break;
+ case UHF_PORT_SUSPEND:
+ OWRITE4(sc, port, UPS_SUSPEND);
+ break;
+ case UHF_PORT_RESET:
+ DPRINTFN(6, "reset port %d\n", index);
+ OWRITE4(sc, port, UPS_RESET);
+ for (v = 0;; v++) {
+ if (v < 12) {
+ if (use_polling) {
+ /* polling */
+ DELAY(USB_PORT_ROOT_RESET_DELAY * 1000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY));
+ }
+
+ if ((OREAD4(sc, port) & UPS_RESET) == 0) {
+ break;
+ }
+ } else {
+ std->err = USB_ERR_TIMEOUT;
+ goto done;
+ }
+ }
+ DPRINTFN(9, "ohci port %d reset, status = 0x%04x\n",
+ index, OREAD4(sc, port));
+ break;
+ case UHF_PORT_POWER:
+ DPRINTFN(3, "set port power %d\n", index);
+ OWRITE4(sc, port, UPS_PORT_POWER);
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+done:
+ return;
+}
+
+static void
+ohci_root_ctrl_poll(struct ohci_softc *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &ohci_root_ctrl_done);
+}
+
+struct usb2_pipe_methods ohci_root_ctrl_methods =
+{
+ .open = ohci_root_ctrl_open,
+ .close = ohci_root_ctrl_close,
+ .enter = ohci_root_ctrl_enter,
+ .start = ohci_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * ohci root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+ohci_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_root_intr_close(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ ohci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+ohci_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_root_intr_start(struct usb2_xfer *xfer)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+}
+
+struct usb2_pipe_methods ohci_root_intr_methods =
+{
+ .open = ohci_root_intr_open,
+ .close = ohci_root_intr_close,
+ .enter = ohci_root_intr_enter,
+ .start = ohci_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+ohci_xfer_setup(struct usb2_setup_params *parm)
+{
+ struct usb2_page_search page_info;
+ struct usb2_page_cache *pc;
+ ohci_softc_t *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t nitd;
+ uint32_t nqh;
+ uint32_t n;
+
+ sc = OHCI_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ parm->hc_max_packet_size = 0x500;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = OHCI_PAGE_SIZE;
+
+ /*
+ * calculate ntd and nqh
+ */
+ if (parm->methods == &ohci_device_ctrl_methods) {
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nitd = 0;
+ ntd = ((2 * xfer->nframes) + 1 /* STATUS */
+ + (xfer->max_data_length / xfer->max_usb2_frame_size));
+ nqh = 1;
+
+ } else if (parm->methods == &ohci_device_bulk_methods) {
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nitd = 0;
+ ntd = ((2 * xfer->nframes)
+ + (xfer->max_data_length / xfer->max_usb2_frame_size));
+ nqh = 1;
+
+ } else if (parm->methods == &ohci_device_intr_methods) {
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nitd = 0;
+ ntd = ((2 * xfer->nframes)
+ + (xfer->max_data_length / xfer->max_usb2_frame_size));
+ nqh = 1;
+
+ } else if (parm->methods == &ohci_device_isoc_methods) {
+ xfer->flags_int.bdma_enable = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nitd = ((xfer->max_data_length / OHCI_PAGE_SIZE) +
+ ((xfer->nframes + OHCI_ITD_NOFFSET - 1) / OHCI_ITD_NOFFSET) +
+ 1 /* EXTRA */ );
+ ntd = 0;
+ nqh = 1;
+
+ } else {
+
+ usb2_transfer_setup_sub(parm);
+
+ nitd = 0;
+ ntd = 0;
+ nqh = 0;
+ }
+
+alloc_dma_set:
+
+ if (parm->err) {
+ return;
+ }
+ last_obj = NULL;
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ohci_td_t),
+ OHCI_TD_ALIGN, ntd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != ntd; n++) {
+ ohci_td_t *td;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ td = page_info.buffer;
+
+ /* init TD */
+ td->td_self = htole32(page_info.physaddr);
+ td->obj_next = last_obj;
+ td->page_cache = pc + n;
+
+ last_obj = td;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ohci_itd_t),
+ OHCI_ITD_ALIGN, nitd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nitd; n++) {
+ ohci_itd_t *itd;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ itd = page_info.buffer;
+
+ /* init TD */
+ itd->itd_self = htole32(page_info.physaddr);
+ itd->obj_next = last_obj;
+ itd->page_cache = pc + n;
+
+ last_obj = itd;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
+
+ last_obj = NULL;
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(ohci_ed_t),
+ OHCI_ED_ALIGN, nqh)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nqh; n++) {
+ ohci_ed_t *ed;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ ed = page_info.buffer;
+
+ /* init QH */
+ ed->ed_self = htole32(page_info.physaddr);
+ ed->obj_next = last_obj;
+ ed->page_cache = pc + n;
+
+ last_obj = ed;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj;
+
+ if (!xfer->flags_int.curr_dma_set) {
+ xfer->flags_int.curr_dma_set = 1;
+ goto alloc_dma_set;
+ }
+}
+
+static void
+ohci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ ohci_softc_t *sc = OHCI_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_addr);
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ if (udev->device_index == sc->sc_addr) {
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &ohci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | OHCI_INTR_ENDPT:
+ pipe->methods = &ohci_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &ohci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &ohci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ if (udev->speed == USB_SPEED_FULL) {
+ pipe->methods = &ohci_device_isoc_methods;
+ }
+ break;
+ case UE_BULK:
+ if (udev->speed != USB_SPEED_LOW) {
+ pipe->methods = &ohci_device_bulk_methods;
+ }
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+static void
+ohci_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+ohci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus)
+{
+ /*
+ * Wait until hardware has finished any possible use of the
+ * transfer descriptor(s) and QH
+ */
+ *pus = (1125); /* microseconds */
+}
+
+static void
+ohci_device_resume(struct usb2_device *udev)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ ohci_ed_t *ed;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (methods == &ohci_device_bulk_methods) {
+ OHCI_APPEND_QH(ed, sc->sc_bulk_p_last);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ OHCI_APPEND_QH(ed, sc->sc_ctrl_p_last);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ }
+ if (methods == &ohci_device_intr_methods) {
+ OHCI_APPEND_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ohci_device_suspend(struct usb2_device *udev)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ ohci_ed_t *ed;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (methods == &ohci_device_bulk_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last);
+ }
+ if (methods == &ohci_device_intr_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ohci_set_hw_power(struct usb2_bus *bus)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(bus);
+ uint32_t temp;
+ uint32_t flags;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(bus);
+
+ flags = bus->hw_power_state;
+
+ temp = OREAD4(sc, OHCI_CONTROL);
+ temp &= ~(OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE);
+
+ if (flags & USB_HW_POWER_CONTROL)
+ temp |= OHCI_CLE;
+
+ if (flags & USB_HW_POWER_BULK)
+ temp |= OHCI_BLE;
+
+ if (flags & USB_HW_POWER_INTERRUPT)
+ temp |= OHCI_PLE;
+
+ if (flags & USB_HW_POWER_ISOC)
+ temp |= OHCI_IE | OHCI_PLE;
+
+ OWRITE4(sc, OHCI_CONTROL, temp);
+
+ USB_BUS_UNLOCK(bus);
+
+ return;
+}
+
+struct usb2_bus_methods ohci_bus_methods =
+{
+ .pipe_init = ohci_pipe_init,
+ .xfer_setup = ohci_xfer_setup,
+ .xfer_unsetup = ohci_xfer_unsetup,
+ .do_poll = ohci_do_poll,
+ .get_dma_delay = ohci_get_dma_delay,
+ .device_resume = ohci_device_resume,
+ .device_suspend = ohci_device_suspend,
+ .set_hw_power = ohci_set_hw_power,
+ .roothub_exec = ohci_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/ohci.h b/sys/dev/usb/controller/ohci.h
new file mode 100644
index 0000000..84a6afd
--- /dev/null
+++ b/sys/dev/usb/controller/ohci.h
@@ -0,0 +1,366 @@
+/* $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.
+ */
+
+#ifndef _OHCI_H_
+#define _OHCI_H_
+
+#define OHCI_MAX_DEVICES USB_MAX_DEVICES
+
+/* PCI config registers */
+#define PCI_CBMEM 0x10 /* configuration base memory */
+#define PCI_INTERFACE_OHCI 0x10
+
+/* OHCI registers */
+#define OHCI_REVISION 0x00 /* OHCI revision */
+#define OHCI_REV_LO(rev) ((rev) & 0xf)
+#define OHCI_REV_HI(rev) (((rev)>>4) & 0xf)
+#define OHCI_REV_LEGACY(rev) ((rev) & 0x100)
+#define OHCI_CONTROL 0x04
+#define OHCI_CBSR_MASK 0x00000003 /* Control/Bulk Service Ratio */
+#define OHCI_RATIO_1_1 0x00000000
+#define OHCI_RATIO_1_2 0x00000001
+#define OHCI_RATIO_1_3 0x00000002
+#define OHCI_RATIO_1_4 0x00000003
+#define OHCI_PLE 0x00000004 /* Periodic List Enable */
+#define OHCI_IE 0x00000008 /* Isochronous Enable */
+#define OHCI_CLE 0x00000010 /* Control List Enable */
+#define OHCI_BLE 0x00000020 /* Bulk List Enable */
+#define OHCI_HCFS_MASK 0x000000c0 /* HostControllerFunctionalStat
+ * e */
+#define OHCI_HCFS_RESET 0x00000000
+#define OHCI_HCFS_RESUME 0x00000040
+#define OHCI_HCFS_OPERATIONAL 0x00000080
+#define OHCI_HCFS_SUSPEND 0x000000c0
+#define OHCI_IR 0x00000100 /* Interrupt Routing */
+#define OHCI_RWC 0x00000200 /* Remote Wakeup Connected */
+#define OHCI_RWE 0x00000400 /* Remote Wakeup Enabled */
+#define OHCI_COMMAND_STATUS 0x08
+#define OHCI_HCR 0x00000001 /* Host Controller Reset */
+#define OHCI_CLF 0x00000002 /* Control List Filled */
+#define OHCI_BLF 0x00000004 /* Bulk List Filled */
+#define OHCI_OCR 0x00000008 /* Ownership Change Request */
+#define OHCI_SOC_MASK 0x00030000 /* Scheduling Overrun Count */
+#define OHCI_INTERRUPT_STATUS 0x0c
+#define OHCI_SO 0x00000001 /* Scheduling Overrun */
+#define OHCI_WDH 0x00000002 /* Writeback Done Head */
+#define OHCI_SF 0x00000004 /* Start of Frame */
+#define OHCI_RD 0x00000008 /* Resume Detected */
+#define OHCI_UE 0x00000010 /* Unrecoverable Error */
+#define OHCI_FNO 0x00000020 /* Frame Number Overflow */
+#define OHCI_RHSC 0x00000040 /* Root Hub Status Change */
+#define OHCI_OC 0x40000000 /* Ownership Change */
+#define OHCI_MIE 0x80000000 /* Master Interrupt Enable */
+#define OHCI_INTERRUPT_ENABLE 0x10
+#define OHCI_INTERRUPT_DISABLE 0x14
+#define OHCI_HCCA 0x18
+#define OHCI_PERIOD_CURRENT_ED 0x1c
+#define OHCI_CONTROL_HEAD_ED 0x20
+#define OHCI_CONTROL_CURRENT_ED 0x24
+#define OHCI_BULK_HEAD_ED 0x28
+#define OHCI_BULK_CURRENT_ED 0x2c
+#define OHCI_DONE_HEAD 0x30
+#define OHCI_FM_INTERVAL 0x34
+#define OHCI_GET_IVAL(s) ((s) & 0x3fff)
+#define OHCI_GET_FSMPS(s) (((s) >> 16) & 0x7fff)
+#define OHCI_FIT 0x80000000
+#define OHCI_FM_REMAINING 0x38
+#define OHCI_FM_NUMBER 0x3c
+#define OHCI_PERIODIC_START 0x40
+#define OHCI_LS_THRESHOLD 0x44
+#define OHCI_RH_DESCRIPTOR_A 0x48
+#define OHCI_GET_NDP(s) ((s) & 0xff)
+#define OHCI_PSM 0x0100 /* Power Switching Mode */
+#define OHCI_NPS 0x0200 /* No Power Switching */
+#define OHCI_DT 0x0400 /* Device Type */
+#define OHCI_OCPM 0x0800 /* Overcurrent Protection Mode */
+#define OHCI_NOCP 0x1000 /* No Overcurrent Protection */
+#define OHCI_GET_POTPGT(s) ((s) >> 24)
+#define OHCI_RH_DESCRIPTOR_B 0x4c
+#define OHCI_RH_STATUS 0x50
+#define OHCI_LPS 0x00000001 /* Local Power Status */
+#define OHCI_OCI 0x00000002 /* OverCurrent Indicator */
+#define OHCI_DRWE 0x00008000 /* Device Remote Wakeup Enable */
+#define OHCI_LPSC 0x00010000 /* Local Power Status Change */
+#define OHCI_CCIC 0x00020000 /* OverCurrent Indicator
+ * Change */
+#define OHCI_CRWE 0x80000000 /* Clear Remote Wakeup Enable */
+#define OHCI_RH_PORT_STATUS(n) (0x50 + ((n)*4)) /* 1 based indexing */
+
+#define OHCI_LES (OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE)
+#define OHCI_ALL_INTRS (OHCI_SO | OHCI_WDH | OHCI_SF | \
+ OHCI_RD | OHCI_UE | OHCI_FNO | \
+ OHCI_RHSC | OHCI_OC)
+#define OHCI_NORMAL_INTRS (OHCI_WDH | OHCI_RD | OHCI_UE | OHCI_RHSC)
+
+#define OHCI_FSMPS(i) (((i-210)*6/7) << 16)
+#define OHCI_PERIODIC(i) ((i)*9/10)
+
+#define OHCI_NO_INTRS 32
+#define OHCI_HCCA_SIZE 256
+
+/* Structures alignment (bytes) */
+#define OHCI_HCCA_ALIGN 256
+#define OHCI_ED_ALIGN 16
+#define OHCI_TD_ALIGN 16
+#define OHCI_ITD_ALIGN 32
+
+#define OHCI_PAGE_SIZE 0x1000
+#define OHCI_PAGE(x) ((x) &~ 0xfff)
+#define OHCI_PAGE_OFFSET(x) ((x) & 0xfff)
+#define OHCI_PAGE_MASK(x) ((x) & 0xfff)
+
+#if ((USB_PAGE_SIZE < OHCI_ED_ALIGN) || (OHCI_ED_ALIGN == 0) || \
+ (USB_PAGE_SIZE < OHCI_TD_ALIGN) || (OHCI_TD_ALIGN == 0) || \
+ (USB_PAGE_SIZE < OHCI_ITD_ALIGN) || (OHCI_ITD_ALIGN == 0) || \
+ (USB_PAGE_SIZE < OHCI_PAGE_SIZE) || (OHCI_PAGE_SIZE == 0))
+#error "Invalid USB page size!"
+#endif
+
+#define OHCI_VIRTUAL_FRAMELIST_COUNT 128/* dummy */
+
+#if (OHCI_VIRTUAL_FRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER)
+#error "maximum number of full-speed isochronous frames is higher than supported!"
+#endif
+
+struct ohci_hcca {
+ volatile uint32_t hcca_interrupt_table[OHCI_NO_INTRS];
+ volatile uint32_t hcca_frame_number;
+ volatile uint32_t hcca_done_head;
+#define OHCI_DONE_INTRS 1
+} __aligned(OHCI_HCCA_ALIGN);
+
+typedef struct ohci_hcca ohci_hcca_t;
+
+struct ohci_ed {
+ volatile uint32_t ed_flags;
+#define OHCI_ED_GET_FA(s) ((s) & 0x7f)
+#define OHCI_ED_ADDRMASK 0x0000007f
+#define OHCI_ED_SET_FA(s) (s)
+#define OHCI_ED_GET_EN(s) (((s) >> 7) & 0xf)
+#define OHCI_ED_SET_EN(s) ((s) << 7)
+#define OHCI_ED_DIR_MASK 0x00001800
+#define OHCI_ED_DIR_TD 0x00000000
+#define OHCI_ED_DIR_OUT 0x00000800
+#define OHCI_ED_DIR_IN 0x00001000
+#define OHCI_ED_SPEED 0x00002000
+#define OHCI_ED_SKIP 0x00004000
+#define OHCI_ED_FORMAT_GEN 0x00000000
+#define OHCI_ED_FORMAT_ISO 0x00008000
+#define OHCI_ED_GET_MAXP(s) (((s) >> 16) & 0x07ff)
+#define OHCI_ED_SET_MAXP(s) ((s) << 16)
+#define OHCI_ED_MAXPMASK (0x7ff << 16)
+ volatile uint32_t ed_tailp;
+ volatile uint32_t ed_headp;
+#define OHCI_HALTED 0x00000001
+#define OHCI_TOGGLECARRY 0x00000002
+#define OHCI_HEADMASK 0xfffffffc
+ volatile uint32_t ed_next;
+/*
+ * Extra information needed:
+ */
+ struct ohci_ed *next;
+ struct ohci_ed *prev;
+ struct ohci_ed *obj_next;
+ struct usb2_page_cache *page_cache;
+ uint32_t ed_self;
+} __aligned(OHCI_ED_ALIGN);
+
+typedef struct ohci_ed ohci_ed_t;
+
+struct ohci_td {
+ volatile uint32_t td_flags;
+#define OHCI_TD_R 0x00040000 /* Buffer Rounding */
+#define OHCI_TD_DP_MASK 0x00180000 /* Direction / PID */
+#define OHCI_TD_SETUP 0x00000000
+#define OHCI_TD_OUT 0x00080000
+#define OHCI_TD_IN 0x00100000
+#define OHCI_TD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
+#define OHCI_TD_SET_DI(x) ((x) << 21)
+#define OHCI_TD_NOINTR 0x00e00000
+#define OHCI_TD_INTR_MASK 0x00e00000
+#define OHCI_TD_TOGGLE_CARRY 0x00000000
+#define OHCI_TD_TOGGLE_0 0x02000000
+#define OHCI_TD_TOGGLE_1 0x03000000
+#define OHCI_TD_TOGGLE_MASK 0x03000000
+#define OHCI_TD_GET_EC(x) (((x) >> 26) & 3) /* Error Count */
+#define OHCI_TD_GET_CC(x) ((x) >> 28) /* Condition Code */
+#define OHCI_TD_SET_CC(x) ((x) << 28)
+#define OHCI_TD_NOCC 0xf0000000
+ volatile uint32_t td_cbp; /* Current Buffer Pointer */
+ volatile uint32_t td_next; /* Next TD */
+#define OHCI_TD_NEXT_END 0
+ volatile uint32_t td_be; /* Buffer End */
+/*
+ * Extra information needed:
+ */
+ struct ohci_td *obj_next;
+ struct ohci_td *alt_next;
+ struct usb2_page_cache *page_cache;
+ uint32_t td_self;
+ uint16_t len;
+} __aligned(OHCI_TD_ALIGN);
+
+typedef struct ohci_td ohci_td_t;
+
+struct ohci_itd {
+ volatile uint32_t itd_flags;
+#define OHCI_ITD_GET_SF(x) ((x) & 0x0000ffff)
+#define OHCI_ITD_SET_SF(x) ((x) & 0xffff)
+#define OHCI_ITD_GET_DI(x) (((x) >> 21) & 7) /* Delay Interrupt */
+#define OHCI_ITD_SET_DI(x) ((x) << 21)
+#define OHCI_ITD_NOINTR 0x00e00000
+#define OHCI_ITD_GET_FC(x) ((((x) >> 24) & 7)+1) /* Frame Count */
+#define OHCI_ITD_SET_FC(x) (((x)-1) << 24)
+#define OHCI_ITD_GET_CC(x) ((x) >> 28) /* Condition Code */
+#define OHCI_ITD_NOCC 0xf0000000
+#define OHCI_ITD_NOFFSET 8
+ volatile uint32_t itd_bp0; /* Buffer Page 0 */
+ volatile uint32_t itd_next; /* Next ITD */
+ volatile uint32_t itd_be; /* Buffer End */
+ volatile uint16_t itd_offset[OHCI_ITD_NOFFSET]; /* Buffer offsets and
+ * Status */
+#define OHCI_ITD_PAGE_SELECT 0x00001000
+#define OHCI_ITD_MK_OFFS(len) (0xe000 | ((len) & 0x1fff))
+#define OHCI_ITD_PSW_LENGTH(x) ((x) & 0xfff) /* Transfer length */
+#define OHCI_ITD_PSW_GET_CC(x) ((x) >> 12) /* Condition Code */
+/*
+ * Extra information needed:
+ */
+ struct ohci_itd *obj_next;
+ struct usb2_page_cache *page_cache;
+ uint32_t itd_self;
+ uint8_t frames;
+} __aligned(OHCI_ITD_ALIGN);
+
+typedef struct ohci_itd ohci_itd_t;
+
+#define OHCI_CC_NO_ERROR 0
+#define OHCI_CC_CRC 1
+#define OHCI_CC_BIT_STUFFING 2
+#define OHCI_CC_DATA_TOGGLE_MISMATCH 3
+#define OHCI_CC_STALL 4
+#define OHCI_CC_DEVICE_NOT_RESPONDING 5
+#define OHCI_CC_PID_CHECK_FAILURE 6
+#define OHCI_CC_UNEXPECTED_PID 7
+#define OHCI_CC_DATA_OVERRUN 8
+#define OHCI_CC_DATA_UNDERRUN 9
+#define OHCI_CC_BUFFER_OVERRUN 12
+#define OHCI_CC_BUFFER_UNDERRUN 13
+#define OHCI_CC_NOT_ACCESSED 15
+
+/* Some delay needed when changing certain registers. */
+#define OHCI_ENABLE_POWER_DELAY 5
+#define OHCI_READ_DESC_DELAY 5
+
+#define OHCI_NO_EDS (2*OHCI_NO_INTRS)
+
+struct ohci_hw_softc {
+ struct usb2_page_cache hcca_pc;
+ struct usb2_page_cache ctrl_start_pc;
+ struct usb2_page_cache bulk_start_pc;
+ struct usb2_page_cache isoc_start_pc;
+ struct usb2_page_cache intr_start_pc[OHCI_NO_EDS];
+
+ struct usb2_page hcca_pg;
+ struct usb2_page ctrl_start_pg;
+ struct usb2_page bulk_start_pg;
+ struct usb2_page isoc_start_pg;
+ struct usb2_page intr_start_pg[OHCI_NO_EDS];
+};
+
+struct ohci_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union ohci_hub_desc {
+ struct usb2_status stat;
+ struct usb2_port_status ps;
+ struct usb2_device_descriptor devd;
+ struct usb2_hub_descriptor hubd;
+ uint8_t temp[128];
+};
+
+typedef struct ohci_softc {
+ struct ohci_hw_softc sc_hw;
+ struct usb2_bus sc_bus; /* base device */
+ struct usb2_callout sc_tmo_rhsc;
+ union ohci_hub_desc sc_hub_desc;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+
+ struct usb2_device *sc_devices[OHCI_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ struct ohci_hcca *sc_hcca_p;
+ struct ohci_ed *sc_ctrl_p_last;
+ struct ohci_ed *sc_bulk_p_last;
+ struct ohci_ed *sc_isoc_p_last;
+ struct ohci_ed *sc_intr_p_last[OHCI_NO_EDS];
+ void *sc_intr_hdl;
+ device_t sc_dev;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ uint32_t sc_eintrs; /* enabled interrupts */
+ uint32_t sc_control; /* Preserved during suspend/standby */
+ uint32_t sc_intre;
+
+ uint16_t sc_intr_stat[OHCI_NO_EDS];
+ uint16_t sc_id_vendor;
+
+ uint8_t sc_noport;
+ uint8_t sc_addr; /* device address */
+ uint8_t sc_conf; /* device configuration */
+ uint8_t sc_hub_idata[32];
+
+ char sc_vendor[16];
+
+} ohci_softc_t;
+
+usb2_bus_mem_cb_t ohci_iterate_hw_softc;
+
+usb2_error_t ohci_init(ohci_softc_t *sc);
+void ohci_detach(struct ohci_softc *sc);
+void ohci_suspend(ohci_softc_t *sc);
+void ohci_resume(ohci_softc_t *sc);
+void ohci_interrupt(ohci_softc_t *sc);
+
+#endif /* _OHCI_H_ */
diff --git a/sys/dev/usb/controller/ohci_atmelarm.c b/sys/dev/usb/controller/ohci_atmelarm.c
new file mode 100644
index 0000000..562cf3d
--- /dev/null
+++ b/sys/dev/usb/controller/ohci_atmelarm.c
@@ -0,0 +1,223 @@
+/*-
+ * Copyright (c) 2006 M. Warner Losh. 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 ``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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/ohci.h>
+
+#include <sys/rman.h>
+
+#include <arm/at91/at91_pmcvar.h>
+
+#define MEM_RID 0
+
+static device_probe_t ohci_atmelarm_probe;
+static device_attach_t ohci_atmelarm_attach;
+static device_detach_t ohci_atmelarm_detach;
+
+struct at91_ohci_softc {
+ struct ohci_softc sc_ohci; /* must be first */
+ struct at91_pmc_clock *iclk;
+ struct at91_pmc_clock *fclk;
+};
+
+static int
+ohci_atmelarm_probe(device_t dev)
+{
+ device_set_desc(dev, "AT91 integrated OHCI controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ohci_atmelarm_attach(device_t dev)
+{
+ struct at91_ohci_softc *sc = device_get_softc(dev);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_ohci.sc_bus.parent = dev;
+ sc->sc_ohci.sc_bus.devices = sc->sc_ohci.sc_devices;
+ sc->sc_ohci.sc_bus.devices_max = OHCI_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_ohci.sc_bus,
+ USB_GET_DMA_TAG(dev), &ohci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+ sc->iclk = at91_pmc_clock_ref("ohci_clk");
+ sc->fclk = at91_pmc_clock_ref("uhpck");
+
+ sc->sc_ohci.sc_dev = dev;
+
+ rid = MEM_RID;
+ sc->sc_ohci.sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &rid, RF_ACTIVE);
+
+ if (!(sc->sc_ohci.sc_io_res)) {
+ err = ENOMEM;
+ goto error;
+ }
+ sc->sc_ohci.sc_io_tag = rman_get_bustag(sc->sc_ohci.sc_io_res);
+ sc->sc_ohci.sc_io_hdl = rman_get_bushandle(sc->sc_ohci.sc_io_res);
+ sc->sc_ohci.sc_io_size = rman_get_size(sc->sc_ohci.sc_io_res);
+
+ rid = 0;
+ sc->sc_ohci.sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE);
+ if (!(sc->sc_ohci.sc_irq_res)) {
+ goto error;
+ }
+ sc->sc_ohci.sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_ohci.sc_bus.bdev)) {
+ goto error;
+ }
+ device_set_ivars(sc->sc_ohci.sc_bus.bdev, &sc->sc_ohci.sc_bus);
+
+ strlcpy(sc->sc_ohci.sc_vendor, "Atmel", sizeof(sc->sc_ohci.sc_vendor));
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl);
+#else
+ err = bus_setup_intr(dev, sc->sc_ohci.sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)ohci_interrupt, sc, &sc->sc_ohci.sc_intr_hdl);
+#endif
+ if (err) {
+ sc->sc_ohci.sc_intr_hdl = NULL;
+ goto error;
+ }
+ /*
+ * turn on the clocks from the AT91's point of view. Keep the unit in reset.
+ */
+ at91_pmc_clock_enable(sc->iclk);
+ at91_pmc_clock_enable(sc->fclk);
+ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl,
+ OHCI_CONTROL, 0);
+
+ err = ohci_init(&sc->sc_ohci);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_ohci.sc_bus.bdev);
+ }
+ if (err) {
+ goto error;
+ }
+ return (0);
+
+error:
+ ohci_atmelarm_detach(dev);
+ return (ENXIO);
+}
+
+static int
+ohci_atmelarm_detach(device_t dev)
+{
+ struct at91_ohci_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_ohci.sc_bus.bdev) {
+ bdev = sc->sc_ohci.sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(dev);
+
+ /*
+ * Put the controller into reset, then disable clocks and do
+ * the MI tear down. We have to disable the clocks/hardware
+ * after we do the rest of the teardown. We also disable the
+ * clocks in the opposite order we acquire them, but that
+ * doesn't seem to be absolutely necessary. We free up the
+ * clocks after we disable them, so the system could, in
+ * theory, reuse them.
+ */
+ bus_space_write_4(sc->sc_ohci.sc_io_tag, sc->sc_ohci.sc_io_hdl,
+ OHCI_CONTROL, 0);
+
+ at91_pmc_clock_disable(sc->fclk);
+ at91_pmc_clock_disable(sc->iclk);
+ at91_pmc_clock_deref(sc->fclk);
+ at91_pmc_clock_deref(sc->iclk);
+
+ if (sc->sc_ohci.sc_irq_res && sc->sc_ohci.sc_intr_hdl) {
+ /*
+ * only call ohci_detach() after ohci_init()
+ */
+ ohci_detach(&sc->sc_ohci);
+
+ err = bus_teardown_intr(dev, sc->sc_ohci.sc_irq_res, sc->sc_ohci.sc_intr_hdl);
+ sc->sc_ohci.sc_intr_hdl = NULL;
+ }
+ if (sc->sc_ohci.sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_ohci.sc_irq_res);
+ sc->sc_ohci.sc_irq_res = NULL;
+ }
+ if (sc->sc_ohci.sc_io_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, MEM_RID,
+ sc->sc_ohci.sc_io_res);
+ sc->sc_ohci.sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_ohci.sc_bus, &ohci_iterate_hw_softc);
+
+ return (0);
+}
+
+static device_method_t ohci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ohci_atmelarm_probe),
+ DEVMETHOD(device_attach, ohci_atmelarm_attach),
+ DEVMETHOD(device_detach, ohci_atmelarm_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t ohci_driver = {
+ "ohci",
+ ohci_methods,
+ sizeof(struct at91_ohci_softc),
+};
+
+static devclass_t ohci_devclass;
+
+DRIVER_MODULE(ohci, atmelarm, ohci_driver, ohci_devclass, 0, 0);
+MODULE_DEPEND(ohci, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/ohci_pci.c b/sys/dev/usb/controller/ohci_pci.c
new file mode 100644
index 0000000..49591af
--- /dev/null
+++ b/sys/dev/usb/controller/ohci_pci.c
@@ -0,0 +1,387 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Open Host Controller driver.
+ *
+ * OHCI spec: http://www.intel.com/design/usb/ohci11d.pdf
+ */
+
+/* The low level controller code for OHCI has been split into
+ * PCI probes and OHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/usb_pci.h>
+#include <dev/usb/controller/ohci.h>
+
+#define PCI_OHCI_VENDORID_ACERLABS 0x10b9
+#define PCI_OHCI_VENDORID_AMD 0x1022
+#define PCI_OHCI_VENDORID_APPLE 0x106b
+#define PCI_OHCI_VENDORID_ATI 0x1002
+#define PCI_OHCI_VENDORID_CMDTECH 0x1095
+#define PCI_OHCI_VENDORID_NEC 0x1033
+#define PCI_OHCI_VENDORID_NVIDIA 0x12D2
+#define PCI_OHCI_VENDORID_NVIDIA2 0x10DE
+#define PCI_OHCI_VENDORID_OPTI 0x1045
+#define PCI_OHCI_VENDORID_SIS 0x1039
+#define PCI_OHCI_VENDORID_SUN 0x108e
+
+#define PCI_OHCI_BASE_REG 0x10
+
+static device_probe_t ohci_pci_probe;
+static device_attach_t ohci_pci_attach;
+static device_detach_t ohci_pci_detach;
+static device_suspend_t ohci_pci_suspend;
+static device_resume_t ohci_pci_resume;
+
+static int
+ohci_pci_suspend(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err) {
+ return (err);
+ }
+ ohci_suspend(sc);
+ return (0);
+}
+
+static int
+ohci_pci_resume(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ uint32_t reg, int_line;
+
+ if (pci_get_powerstate(self) != PCI_POWERSTATE_D0) {
+ device_printf(self, "chip is in D%d mode "
+ "-- setting to D0\n", pci_get_powerstate(self));
+ reg = pci_read_config(self, PCI_CBMEM, 4);
+ int_line = pci_read_config(self, PCIR_INTLINE, 4);
+ pci_set_powerstate(self, PCI_POWERSTATE_D0);
+ pci_write_config(self, PCI_CBMEM, reg, 4);
+ pci_write_config(self, PCIR_INTLINE, int_line, 4);
+ }
+ ohci_resume(sc);
+
+ bus_generic_resume(self);
+ return (0);
+}
+
+static const char *
+ohci_pci_match(device_t self)
+{
+ uint32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case 0x523710b9:
+ return ("AcerLabs M5237 (Aladdin-V) USB controller");
+
+ case 0x740c1022:
+ return ("AMD-756 USB Controller");
+
+ case 0x74141022:
+ return ("AMD-766 USB Controller");
+
+ case 0x43741002:
+ return "ATI SB400 USB Controller";
+ case 0x43751002:
+ return "ATI SB400 USB Controller";
+
+ case 0x06701095:
+ return ("CMD Tech 670 (USB0670) USB controller");
+
+ case 0x06731095:
+ return ("CMD Tech 673 (USB0673) USB controller");
+
+ case 0xc8611045:
+ return ("OPTi 82C861 (FireLink) USB controller");
+
+ case 0x00351033:
+ return ("NEC uPD 9210 USB controller");
+
+ case 0x00d710de:
+ return ("nVidia nForce3 USB Controller");
+
+ case 0x70011039:
+ return ("SiS 5571 USB controller");
+
+ case 0x1103108e:
+ return "Sun PCIO-2 USB controller";
+
+ case 0x0019106b:
+ return ("Apple KeyLargo USB controller");
+
+ default:
+ break;
+ }
+ if ((pci_get_class(self) == PCIC_SERIALBUS) &&
+ (pci_get_subclass(self) == PCIS_SERIALBUS_USB) &&
+ (pci_get_progif(self) == PCI_INTERFACE_OHCI)) {
+ return ("OHCI (generic) USB controller");
+ }
+ return (NULL);
+}
+
+static int
+ohci_pci_probe(device_t self)
+{
+ const char *desc = ohci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return (0);
+ } else {
+ return (ENXIO);
+ }
+}
+
+static int
+ohci_pci_attach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ int rid;
+ int err;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = OHCI_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self),
+ &ohci_iterate_hw_softc)) {
+ return (ENOMEM);
+ }
+ sc->sc_dev = self;
+
+ pci_enable_busmaster(self);
+
+ /*
+ * Some Sun PCIO-2 USB controllers have their intpin register
+ * bogusly set to 0, although it should be 4. Correct that.
+ */
+ if (pci_get_devid(self) == 0x1103108e && pci_get_intpin(self) == 0)
+ pci_set_intpin(self, 4);
+
+ rid = PCI_CBMEM;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map memory\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /*
+ * ohci_pci_match will never return NULL if ohci_pci_probe
+ * succeeded
+ */
+ device_set_desc(sc->sc_bus.bdev, ohci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_OHCI_VENDORID_ACERLABS:
+ sprintf(sc->sc_vendor, "AcerLabs");
+ break;
+ case PCI_OHCI_VENDORID_AMD:
+ sprintf(sc->sc_vendor, "AMD");
+ break;
+ case PCI_OHCI_VENDORID_APPLE:
+ sprintf(sc->sc_vendor, "Apple");
+ break;
+ case PCI_OHCI_VENDORID_ATI:
+ sprintf(sc->sc_vendor, "ATI");
+ break;
+ case PCI_OHCI_VENDORID_CMDTECH:
+ sprintf(sc->sc_vendor, "CMDTECH");
+ break;
+ case PCI_OHCI_VENDORID_NEC:
+ sprintf(sc->sc_vendor, "NEC");
+ break;
+ case PCI_OHCI_VENDORID_NVIDIA:
+ case PCI_OHCI_VENDORID_NVIDIA2:
+ sprintf(sc->sc_vendor, "nVidia");
+ break;
+ case PCI_OHCI_VENDORID_OPTI:
+ sprintf(sc->sc_vendor, "OPTi");
+ break;
+ case PCI_OHCI_VENDORID_SIS:
+ sprintf(sc->sc_vendor, "SiS");
+ break;
+ case PCI_OHCI_VENDORID_SUN:
+ sprintf(sc->sc_vendor, "SUN");
+ break;
+ default:
+ if (bootverbose) {
+ device_printf(self, "(New OHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ }
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ /* sc->sc_bus.usbrev; set by ohci_init() */
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)(void *)ohci_interrupt, sc, &sc->sc_intr_hdl);
+#else
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)(void *)ohci_interrupt, sc, &sc->sc_intr_hdl);
+#endif
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ err = ohci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed\n");
+ goto error;
+ }
+ return (0);
+
+error:
+ ohci_pci_detach(self);
+ return (ENXIO);
+}
+
+static int
+ohci_pci_detach(device_t self)
+{
+ ohci_softc_t *sc = device_get_softc(self);
+ device_t bdev;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(self);
+
+ pci_disable_busmaster(self);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call ohci_detach() after ohci_init()
+ */
+ ohci_detach(sc);
+
+ int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err) {
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ }
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_MEMORY, PCI_CBMEM,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_bus, &ohci_iterate_hw_softc);
+
+ return (0);
+}
+
+static driver_t ohci_driver =
+{
+ .name = "ohci",
+ .methods = (device_method_t[]){
+ /* device interface */
+ DEVMETHOD(device_probe, ohci_pci_probe),
+ DEVMETHOD(device_attach, ohci_pci_attach),
+ DEVMETHOD(device_detach, ohci_pci_detach),
+ DEVMETHOD(device_suspend, ohci_pci_suspend),
+ DEVMETHOD(device_resume, ohci_pci_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+ },
+ .size = sizeof(struct ohci_softc),
+};
+
+static devclass_t ohci_devclass;
+
+DRIVER_MODULE(ohci, pci, ohci_driver, ohci_devclass, 0, 0);
+DRIVER_MODULE(ohci, cardbus, ohci_driver, ohci_devclass, 0, 0);
+MODULE_DEPEND(ohci, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c
new file mode 100644
index 0000000..40ae82a
--- /dev/null
+++ b/sys/dev/usb/controller/uhci.c
@@ -0,0 +1,3381 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * USB Universal Host Controller driver.
+ * Handles e.g. PIIX3 and PIIX4.
+ *
+ * UHCI spec: http://developer.intel.com/design/USB/UHCI11D.htm
+ * USB spec: http://www.usb.org/developers/docs/usbspec.zip
+ * PIIXn spec: ftp://download.intel.com/design/intarch/datashts/29055002.pdf
+ * ftp://download.intel.com/design/intarch/datashts/29056201.pdf
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR uhcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/uhci.h>
+
+#define alt_next next
+#define UHCI_BUS2SC(bus) ((uhci_softc_t *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((uhci_softc_t *)0)->sc_bus))))
+
+#if USB_DEBUG
+static int uhcidebug = 0;
+static int uhcinoloop = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uhci, CTLFLAG_RW, 0, "USB uhci");
+SYSCTL_INT(_hw_usb2_uhci, OID_AUTO, debug, CTLFLAG_RW,
+ &uhcidebug, 0, "uhci debug level");
+SYSCTL_INT(_hw_usb2_uhci, OID_AUTO, loop, CTLFLAG_RW,
+ &uhcinoloop, 0, "uhci noloop");
+static void uhci_dumpregs(uhci_softc_t *sc);
+static void uhci_dump_tds(uhci_td_t *td);
+
+#endif
+
+#define UBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \
+ BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define UWRITE1(sc, r, x) \
+ do { UBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UWRITE2(sc, r, x) \
+ do { UBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UWRITE4(sc, r, x) \
+ do { UBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); \
+ } while (/*CONSTCOND*/0)
+#define UREAD1(sc, r) (UBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+#define UREAD2(sc, r) (UBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+#define UREAD4(sc, r) (UBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+
+#define UHCICMD(sc, cmd) UWRITE2(sc, UHCI_CMD, cmd)
+#define UHCISTS(sc) UREAD2(sc, UHCI_STS)
+
+#define UHCI_RESET_TIMEOUT 100 /* ms, reset timeout */
+
+#define UHCI_INTR_ENDPT 1
+
+struct uhci_mem_layout {
+
+ struct usb2_page_search buf_res;
+ struct usb2_page_search fix_res;
+
+ struct usb2_page_cache *buf_pc;
+ struct usb2_page_cache *fix_pc;
+
+ uint32_t buf_offset;
+
+ uint16_t max_frame_size;
+};
+
+struct uhci_std_temp {
+
+ struct uhci_mem_layout ml;
+ uhci_td_t *td;
+ uhci_td_t *td_next;
+ uint32_t average;
+ uint32_t td_status;
+ uint32_t td_token;
+ uint32_t len;
+ uint16_t max_frame_size;
+ uint8_t shortpkt;
+ uint8_t setup_alt_next;
+ uint8_t short_frames_ok;
+};
+
+extern struct usb2_bus_methods uhci_bus_methods;
+extern struct usb2_pipe_methods uhci_device_bulk_methods;
+extern struct usb2_pipe_methods uhci_device_ctrl_methods;
+extern struct usb2_pipe_methods uhci_device_intr_methods;
+extern struct usb2_pipe_methods uhci_device_isoc_methods;
+extern struct usb2_pipe_methods uhci_root_ctrl_methods;
+extern struct usb2_pipe_methods uhci_root_intr_methods;
+
+static void uhci_root_ctrl_poll(struct uhci_softc *);
+static void uhci_do_poll(struct usb2_bus *);
+static void uhci_device_done(struct usb2_xfer *, usb2_error_t);
+static void uhci_transfer_intr_enqueue(struct usb2_xfer *);
+static void uhci_root_intr_check(void *);
+static void uhci_timeout(void *);
+static uint8_t uhci_check_transfer(struct usb2_xfer *);
+
+void
+uhci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(bus);
+ uint32_t i;
+
+ cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg,
+ sizeof(uint32_t) * UHCI_FRAMELIST_COUNT, UHCI_FRAMELIST_ALIGN);
+
+ cb(bus, &sc->sc_hw.ls_ctl_start_pc, &sc->sc_hw.ls_ctl_start_pg,
+ sizeof(uhci_qh_t), UHCI_QH_ALIGN);
+
+ cb(bus, &sc->sc_hw.fs_ctl_start_pc, &sc->sc_hw.fs_ctl_start_pg,
+ sizeof(uhci_qh_t), UHCI_QH_ALIGN);
+
+ cb(bus, &sc->sc_hw.bulk_start_pc, &sc->sc_hw.bulk_start_pg,
+ sizeof(uhci_qh_t), UHCI_QH_ALIGN);
+
+ cb(bus, &sc->sc_hw.last_qh_pc, &sc->sc_hw.last_qh_pg,
+ sizeof(uhci_qh_t), UHCI_QH_ALIGN);
+
+ cb(bus, &sc->sc_hw.last_td_pc, &sc->sc_hw.last_td_pg,
+ sizeof(uhci_td_t), UHCI_TD_ALIGN);
+
+ for (i = 0; i != UHCI_VFRAMELIST_COUNT; i++) {
+ cb(bus, sc->sc_hw.isoc_start_pc + i,
+ sc->sc_hw.isoc_start_pg + i,
+ sizeof(uhci_td_t), UHCI_TD_ALIGN);
+ }
+
+ for (i = 0; i != UHCI_IFRAMELIST_COUNT; i++) {
+ cb(bus, sc->sc_hw.intr_start_pc + i,
+ sc->sc_hw.intr_start_pg + i,
+ sizeof(uhci_qh_t), UHCI_QH_ALIGN);
+ }
+}
+
+static void
+uhci_mem_layout_init(struct uhci_mem_layout *ml, struct usb2_xfer *xfer)
+{
+ ml->buf_pc = xfer->frbuffers + 0;
+ ml->fix_pc = xfer->buf_fixup;
+
+ ml->buf_offset = 0;
+
+ ml->max_frame_size = xfer->max_frame_size;
+}
+
+static void
+uhci_mem_layout_fixup(struct uhci_mem_layout *ml, struct uhci_td *td)
+{
+ usb2_get_page(ml->buf_pc, ml->buf_offset, &ml->buf_res);
+
+ if (ml->buf_res.length < td->len) {
+
+ /* need to do a fixup */
+
+ usb2_get_page(ml->fix_pc, 0, &ml->fix_res);
+
+ td->td_buffer = htole32(ml->fix_res.physaddr);
+
+ /*
+ * The UHCI driver cannot handle
+ * page crossings, so a fixup is
+ * needed:
+ *
+ * +----+----+ - - -
+ * | YYY|Y |
+ * +----+----+ - - -
+ * \ \
+ * \ \
+ * +----+
+ * |YYYY| (fixup)
+ * +----+
+ */
+
+ if ((td->td_token & htole32(UHCI_TD_PID)) ==
+ htole32(UHCI_TD_PID_IN)) {
+ td->fix_pc = ml->fix_pc;
+ usb2_pc_cpu_invalidate(ml->fix_pc);
+
+ } else {
+ td->fix_pc = NULL;
+
+ /* copy data to fixup location */
+
+ usb2_copy_out(ml->buf_pc, ml->buf_offset,
+ ml->fix_res.buffer, td->len);
+
+ usb2_pc_cpu_flush(ml->fix_pc);
+ }
+
+ /* prepare next fixup */
+
+ ml->fix_pc++;
+
+ } else {
+
+ td->td_buffer = htole32(ml->buf_res.physaddr);
+ td->fix_pc = NULL;
+ }
+
+ /* prepare next data location */
+
+ ml->buf_offset += td->len;
+}
+
+void
+uhci_reset(uhci_softc_t *sc)
+{
+ struct usb2_page_search buf_res;
+ uint16_t n;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTF("resetting the HC\n");
+
+ /* disable interrupts */
+
+ UWRITE2(sc, UHCI_INTR, 0);
+
+ /* global reset */
+
+ UHCICMD(sc, UHCI_CMD_GRESET);
+
+ /* wait */
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_BUS_RESET_DELAY));
+
+ /* terminate all transfers */
+
+ UHCICMD(sc, UHCI_CMD_HCRESET);
+
+ /* the reset bit goes low when the controller is done */
+
+ n = UHCI_RESET_TIMEOUT;
+ while (n--) {
+ /* wait one millisecond */
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+
+ if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_HCRESET)) {
+ goto done_1;
+ }
+ }
+
+ device_printf(sc->sc_bus.bdev,
+ "controller did not reset\n");
+
+done_1:
+
+ n = 10;
+ while (n--) {
+ /* wait one millisecond */
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+
+ /* check if HC is stopped */
+ if (UREAD2(sc, UHCI_STS) & UHCI_STS_HCH) {
+ goto done_2;
+ }
+ }
+
+ device_printf(sc->sc_bus.bdev,
+ "controller did not stop\n");
+
+done_2:
+
+ /* reload the configuration */
+ usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
+ UWRITE4(sc, UHCI_FLBASEADDR, buf_res.physaddr);
+ UWRITE2(sc, UHCI_FRNUM, sc->sc_saved_frnum);
+ UWRITE1(sc, UHCI_SOF, sc->sc_saved_sof);
+}
+
+static void
+uhci_start(uhci_softc_t *sc)
+{
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTFN(2, "enabling\n");
+
+ /* enable interrupts */
+
+ UWRITE2(sc, UHCI_INTR,
+ (UHCI_INTR_TOCRCIE |
+ UHCI_INTR_RIE |
+ UHCI_INTR_IOCE |
+ UHCI_INTR_SPIE));
+
+ /*
+ * assume 64 byte packets at frame end and start HC controller
+ */
+
+ UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS));
+
+ uint8_t n = 10;
+
+ while (n--) {
+ /* wait one millisecond */
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000);
+
+ /* check that controller has started */
+
+ if (!(UREAD2(sc, UHCI_STS) & UHCI_STS_HCH)) {
+ goto done;
+ }
+ }
+
+ device_printf(sc->sc_bus.bdev,
+ "cannot start HC controller\n");
+
+done:
+ return;
+}
+
+static struct uhci_qh *
+uhci_init_qh(struct usb2_page_cache *pc)
+{
+ struct usb2_page_search buf_res;
+ struct uhci_qh *qh;
+
+ usb2_get_page(pc, 0, &buf_res);
+
+ qh = buf_res.buffer;
+
+ qh->qh_self =
+ htole32(buf_res.physaddr) |
+ htole32(UHCI_PTR_QH);
+
+ qh->page_cache = pc;
+
+ return (qh);
+}
+
+static struct uhci_td *
+uhci_init_td(struct usb2_page_cache *pc)
+{
+ struct usb2_page_search buf_res;
+ struct uhci_td *td;
+
+ usb2_get_page(pc, 0, &buf_res);
+
+ td = buf_res.buffer;
+
+ td->td_self =
+ htole32(buf_res.physaddr) |
+ htole32(UHCI_PTR_TD);
+
+ td->page_cache = pc;
+
+ return (td);
+}
+
+usb2_error_t
+uhci_init(uhci_softc_t *sc)
+{
+ uint16_t bit;
+ uint16_t x;
+ uint16_t y;
+
+ DPRINTF("start\n");
+
+#if USB_DEBUG
+ if (uhcidebug > 2) {
+ uhci_dumpregs(sc);
+ }
+#endif
+
+ sc->sc_saved_sof = 0x40; /* default value */
+ sc->sc_saved_frnum = 0; /* default frame number */
+
+ /*
+ * Setup QH's
+ */
+ sc->sc_ls_ctl_p_last =
+ uhci_init_qh(&sc->sc_hw.ls_ctl_start_pc);
+
+ sc->sc_fs_ctl_p_last =
+ uhci_init_qh(&sc->sc_hw.fs_ctl_start_pc);
+
+ sc->sc_bulk_p_last =
+ uhci_init_qh(&sc->sc_hw.bulk_start_pc);
+#if 0
+ sc->sc_reclaim_qh_p =
+ sc->sc_fs_ctl_p_last;
+#else
+ /* setup reclaim looping point */
+ sc->sc_reclaim_qh_p =
+ sc->sc_bulk_p_last;
+#endif
+
+ sc->sc_last_qh_p =
+ uhci_init_qh(&sc->sc_hw.last_qh_pc);
+
+ sc->sc_last_td_p =
+ uhci_init_td(&sc->sc_hw.last_td_pc);
+
+ for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) {
+ sc->sc_isoc_p_last[x] =
+ uhci_init_td(sc->sc_hw.isoc_start_pc + x);
+ }
+
+ for (x = 0; x != UHCI_IFRAMELIST_COUNT; x++) {
+ sc->sc_intr_p_last[x] =
+ uhci_init_qh(sc->sc_hw.intr_start_pc + x);
+ }
+
+ /*
+ * the QHs are arranged to give poll intervals that are
+ * powers of 2 times 1ms
+ */
+ bit = UHCI_IFRAMELIST_COUNT / 2;
+ while (bit) {
+ x = bit;
+ while (x & bit) {
+ uhci_qh_t *qh_x;
+ uhci_qh_t *qh_y;
+
+ y = (x ^ bit) | (bit / 2);
+
+ /*
+ * the next QH has half the poll interval
+ */
+ qh_x = sc->sc_intr_p_last[x];
+ qh_y = sc->sc_intr_p_last[y];
+
+ qh_x->h_next = NULL;
+ qh_x->qh_h_next = qh_y->qh_self;
+ qh_x->e_next = NULL;
+ qh_x->qh_e_next = htole32(UHCI_PTR_T);
+ x++;
+ }
+ bit >>= 1;
+ }
+
+ if (1) {
+ uhci_qh_t *qh_ls;
+ uhci_qh_t *qh_intr;
+
+ qh_ls = sc->sc_ls_ctl_p_last;
+ qh_intr = sc->sc_intr_p_last[0];
+
+ /* start QH for interrupt traffic */
+ qh_intr->h_next = qh_ls;
+ qh_intr->qh_h_next = qh_ls->qh_self;
+ qh_intr->e_next = 0;
+ qh_intr->qh_e_next = htole32(UHCI_PTR_T);
+ }
+ for (x = 0; x != UHCI_VFRAMELIST_COUNT; x++) {
+
+ uhci_td_t *td_x;
+ uhci_qh_t *qh_intr;
+
+ td_x = sc->sc_isoc_p_last[x];
+ qh_intr = sc->sc_intr_p_last[x | (UHCI_IFRAMELIST_COUNT / 2)];
+
+ /* start TD for isochronous traffic */
+ td_x->next = NULL;
+ td_x->td_next = qh_intr->qh_self;
+ td_x->td_status = htole32(UHCI_TD_IOS);
+ td_x->td_token = htole32(0);
+ td_x->td_buffer = htole32(0);
+ }
+
+ if (1) {
+ uhci_qh_t *qh_ls;
+ uhci_qh_t *qh_fs;
+
+ qh_ls = sc->sc_ls_ctl_p_last;
+ qh_fs = sc->sc_fs_ctl_p_last;
+
+ /* start QH where low speed control traffic will be queued */
+ qh_ls->h_next = qh_fs;
+ qh_ls->qh_h_next = qh_fs->qh_self;
+ qh_ls->e_next = 0;
+ qh_ls->qh_e_next = htole32(UHCI_PTR_T);
+ }
+ if (1) {
+ uhci_qh_t *qh_ctl;
+ uhci_qh_t *qh_blk;
+ uhci_qh_t *qh_lst;
+ uhci_td_t *td_lst;
+
+ qh_ctl = sc->sc_fs_ctl_p_last;
+ qh_blk = sc->sc_bulk_p_last;
+
+ /* start QH where full speed control traffic will be queued */
+ qh_ctl->h_next = qh_blk;
+ qh_ctl->qh_h_next = qh_blk->qh_self;
+ qh_ctl->e_next = 0;
+ qh_ctl->qh_e_next = htole32(UHCI_PTR_T);
+
+ qh_lst = sc->sc_last_qh_p;
+
+ /* start QH where bulk traffic will be queued */
+ qh_blk->h_next = qh_lst;
+ qh_blk->qh_h_next = qh_lst->qh_self;
+ qh_blk->e_next = 0;
+ qh_blk->qh_e_next = htole32(UHCI_PTR_T);
+
+ td_lst = sc->sc_last_td_p;
+
+ /* end QH which is used for looping the QHs */
+ qh_lst->h_next = 0;
+ qh_lst->qh_h_next = htole32(UHCI_PTR_T); /* end of QH chain */
+ qh_lst->e_next = td_lst;
+ qh_lst->qh_e_next = td_lst->td_self;
+
+ /*
+ * end TD which hangs from the last QH, to avoid a bug in the PIIX
+ * that makes it run berserk otherwise
+ */
+ td_lst->next = 0;
+ td_lst->td_next = htole32(UHCI_PTR_T);
+ td_lst->td_status = htole32(0); /* inactive */
+ td_lst->td_token = htole32(0);
+ td_lst->td_buffer = htole32(0);
+ }
+ if (1) {
+ struct usb2_page_search buf_res;
+ uint32_t *pframes;
+
+ usb2_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
+
+ pframes = buf_res.buffer;
+
+
+ /*
+ * Setup UHCI framelist
+ *
+ * Execution order:
+ *
+ * pframes -> full speed isochronous -> interrupt QH's -> low
+ * speed control -> full speed control -> bulk transfers
+ *
+ */
+
+ for (x = 0; x != UHCI_FRAMELIST_COUNT; x++) {
+ pframes[x] =
+ sc->sc_isoc_p_last[x % UHCI_VFRAMELIST_COUNT]->td_self;
+ }
+ }
+ /* flush all cache into memory */
+
+ usb2_bus_mem_flush_all(&sc->sc_bus, &uhci_iterate_hw_softc);
+
+ /* set up the bus struct */
+ sc->sc_bus.methods = &uhci_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ /* reset the controller */
+ uhci_reset(sc);
+
+ /* start the controller */
+ uhci_start(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch lost interrupts */
+ uhci_do_poll(&sc->sc_bus);
+
+ return (0);
+}
+
+/* NOTE: suspend/resume is called from
+ * interrupt context and cannot sleep!
+ */
+
+void
+uhci_suspend(uhci_softc_t *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+#if USB_DEBUG
+ if (uhcidebug > 2) {
+ uhci_dumpregs(sc);
+ }
+#endif
+ /* save some state if BIOS doesn't */
+
+ sc->sc_saved_frnum = UREAD2(sc, UHCI_FRNUM);
+ sc->sc_saved_sof = UREAD1(sc, UHCI_SOF);
+
+ /* stop the controller */
+
+ uhci_reset(sc);
+
+ /* enter global suspend */
+
+ UHCICMD(sc, UHCI_CMD_EGSM);
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_RESUME_WAIT));
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+uhci_resume(uhci_softc_t *sc)
+{
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* reset the controller */
+
+ uhci_reset(sc);
+
+ /* force global resume */
+
+ UHCICMD(sc, UHCI_CMD_FGR);
+
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_RESUME_DELAY));
+
+ /* and start traffic again */
+
+ uhci_start(sc);
+
+#if USB_DEBUG
+ if (uhcidebug > 2) {
+ uhci_dumpregs(sc);
+ }
+#endif
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch lost interrupts */
+ uhci_do_poll(&sc->sc_bus);
+}
+
+#if USB_DEBUG
+static void
+uhci_dumpregs(uhci_softc_t *sc)
+{
+ DPRINTFN(0, "%s regs: cmd=%04x, sts=%04x, intr=%04x, frnum=%04x, "
+ "flbase=%08x, sof=%04x, portsc1=%04x, portsc2=%04x\n",
+ device_get_nameunit(sc->sc_bus.bdev),
+ UREAD2(sc, UHCI_CMD),
+ UREAD2(sc, UHCI_STS),
+ UREAD2(sc, UHCI_INTR),
+ UREAD2(sc, UHCI_FRNUM),
+ UREAD4(sc, UHCI_FLBASEADDR),
+ UREAD1(sc, UHCI_SOF),
+ UREAD2(sc, UHCI_PORTSC1),
+ UREAD2(sc, UHCI_PORTSC2));
+}
+
+static uint8_t
+uhci_dump_td(uhci_td_t *p)
+{
+ uint32_t td_next;
+ uint32_t td_status;
+ uint32_t td_token;
+ uint8_t temp;
+
+ usb2_pc_cpu_invalidate(p->page_cache);
+
+ td_next = le32toh(p->td_next);
+ td_status = le32toh(p->td_status);
+ td_token = le32toh(p->td_token);
+
+ /*
+ * Check whether the link pointer in this TD marks the link pointer
+ * as end of queue:
+ */
+ temp = ((td_next & UHCI_PTR_T) || (td_next == 0));
+
+ printf("TD(%p) at 0x%08x = link=0x%08x status=0x%08x "
+ "token=0x%08x buffer=0x%08x\n",
+ p,
+ le32toh(p->td_self),
+ td_next,
+ td_status,
+ td_token,
+ le32toh(p->td_buffer));
+
+ printf("TD(%p) td_next=%s%s%s td_status=%s%s%s%s%s%s%s%s%s%s%s, errcnt=%d, actlen=%d pid=%02x,"
+ "addr=%d,endpt=%d,D=%d,maxlen=%d\n",
+ p,
+ (td_next & 1) ? "-T" : "",
+ (td_next & 2) ? "-Q" : "",
+ (td_next & 4) ? "-VF" : "",
+ (td_status & UHCI_TD_BITSTUFF) ? "-BITSTUFF" : "",
+ (td_status & UHCI_TD_CRCTO) ? "-CRCTO" : "",
+ (td_status & UHCI_TD_NAK) ? "-NAK" : "",
+ (td_status & UHCI_TD_BABBLE) ? "-BABBLE" : "",
+ (td_status & UHCI_TD_DBUFFER) ? "-DBUFFER" : "",
+ (td_status & UHCI_TD_STALLED) ? "-STALLED" : "",
+ (td_status & UHCI_TD_ACTIVE) ? "-ACTIVE" : "",
+ (td_status & UHCI_TD_IOC) ? "-IOC" : "",
+ (td_status & UHCI_TD_IOS) ? "-IOS" : "",
+ (td_status & UHCI_TD_LS) ? "-LS" : "",
+ (td_status & UHCI_TD_SPD) ? "-SPD" : "",
+ UHCI_TD_GET_ERRCNT(td_status),
+ UHCI_TD_GET_ACTLEN(td_status),
+ UHCI_TD_GET_PID(td_token),
+ UHCI_TD_GET_DEVADDR(td_token),
+ UHCI_TD_GET_ENDPT(td_token),
+ UHCI_TD_GET_DT(td_token),
+ UHCI_TD_GET_MAXLEN(td_token));
+
+ return (temp);
+}
+
+static uint8_t
+uhci_dump_qh(uhci_qh_t *sqh)
+{
+ uint8_t temp;
+ uint32_t qh_h_next;
+ uint32_t qh_e_next;
+
+ usb2_pc_cpu_invalidate(sqh->page_cache);
+
+ qh_h_next = le32toh(sqh->qh_h_next);
+ qh_e_next = le32toh(sqh->qh_e_next);
+
+ DPRINTFN(0, "QH(%p) at 0x%08x: h_next=0x%08x e_next=0x%08x\n", sqh,
+ le32toh(sqh->qh_self), qh_h_next, qh_e_next);
+
+ temp = ((((sqh->h_next != NULL) && !(qh_h_next & UHCI_PTR_T)) ? 1 : 0) |
+ (((sqh->e_next != NULL) && !(qh_e_next & UHCI_PTR_T)) ? 2 : 0));
+
+ return (temp);
+}
+
+static void
+uhci_dump_all(uhci_softc_t *sc)
+{
+ uhci_dumpregs(sc);
+ uhci_dump_qh(sc->sc_ls_ctl_p_last);
+ uhci_dump_qh(sc->sc_fs_ctl_p_last);
+ uhci_dump_qh(sc->sc_bulk_p_last);
+ uhci_dump_qh(sc->sc_last_qh_p);
+}
+
+static void
+uhci_dump_qhs(uhci_qh_t *sqh)
+{
+ uint8_t temp;
+
+ temp = uhci_dump_qh(sqh);
+
+ /*
+ * uhci_dump_qhs displays all the QHs and TDs from the given QH
+ * onwards Traverses sideways first, then down.
+ *
+ * QH1 QH2 No QH TD2.1 TD2.2 TD1.1 etc.
+ *
+ * TD2.x being the TDs queued at QH2 and QH1 being referenced from QH1.
+ */
+
+ if (temp & 1)
+ uhci_dump_qhs(sqh->h_next);
+ else
+ DPRINTF("No QH\n");
+
+ if (temp & 2)
+ uhci_dump_tds(sqh->e_next);
+ else
+ DPRINTF("No TD\n");
+}
+
+static void
+uhci_dump_tds(uhci_td_t *td)
+{
+ for (;
+ td != NULL;
+ td = td->obj_next) {
+ if (uhci_dump_td(td)) {
+ break;
+ }
+ }
+}
+
+#endif
+
+/*
+ * Let the last QH loop back to the full speed control transfer QH.
+ * This is what intel calls "bandwidth reclamation" and improves
+ * USB performance a lot for some devices.
+ * If we are already looping, just count it.
+ */
+static void
+uhci_add_loop(uhci_softc_t *sc)
+{
+ struct uhci_qh *qh_lst;
+ struct uhci_qh *qh_rec;
+
+#if USB_DEBUG
+ if (uhcinoloop) {
+ return;
+ }
+#endif
+ if (++(sc->sc_loops) == 1) {
+ DPRINTFN(6, "add\n");
+
+ qh_lst = sc->sc_last_qh_p;
+ qh_rec = sc->sc_reclaim_qh_p;
+
+ /* NOTE: we don't loop back the soft pointer */
+
+ qh_lst->qh_h_next = qh_rec->qh_self;
+ usb2_pc_cpu_flush(qh_lst->page_cache);
+ }
+}
+
+static void
+uhci_rem_loop(uhci_softc_t *sc)
+{
+ struct uhci_qh *qh_lst;
+
+#if USB_DEBUG
+ if (uhcinoloop) {
+ return;
+ }
+#endif
+ if (--(sc->sc_loops) == 0) {
+ DPRINTFN(6, "remove\n");
+
+ qh_lst = sc->sc_last_qh_p;
+ qh_lst->qh_h_next = htole32(UHCI_PTR_T);
+ usb2_pc_cpu_flush(qh_lst->page_cache);
+ }
+}
+
+static void
+uhci_transfer_intr_enqueue(struct usb2_xfer *xfer)
+{
+ /* check for early completion */
+ if (uhci_check_transfer(xfer)) {
+ return;
+ }
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer, &uhci_timeout, xfer->timeout);
+ }
+}
+
+#define UHCI_APPEND_TD(std,last) (last) = _uhci_append_td(std,last)
+static uhci_td_t *
+_uhci_append_td(uhci_td_t *std, uhci_td_t *last)
+{
+ DPRINTFN(11, "%p to %p\n", std, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ std->next = last->next;
+ std->td_next = last->td_next;
+
+ std->prev = last;
+
+ usb2_pc_cpu_flush(std->page_cache);
+
+ /*
+ * the last->next->prev is never followed: std->next->prev = std;
+ */
+ last->next = std;
+ last->td_next = std->td_self;
+
+ usb2_pc_cpu_flush(last->page_cache);
+
+ return (std);
+}
+
+#define UHCI_APPEND_QH(sqh,last) (last) = _uhci_append_qh(sqh,last)
+static uhci_qh_t *
+_uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last)
+{
+ DPRINTFN(11, "%p to %p\n", sqh, last);
+
+ if (sqh->h_prev != NULL) {
+ /* should not happen */
+ DPRINTFN(0, "QH already linked!\n");
+ return (last);
+ }
+ /* (sc->sc_bus.mtx) must be locked */
+
+ sqh->h_next = last->h_next;
+ sqh->qh_h_next = last->qh_h_next;
+
+ sqh->h_prev = last;
+
+ usb2_pc_cpu_flush(sqh->page_cache);
+
+ /*
+ * The "last->h_next->h_prev" is never followed:
+ *
+ * "sqh->h_next->h_prev" = sqh;
+ */
+
+ last->h_next = sqh;
+ last->qh_h_next = sqh->qh_self;
+
+ usb2_pc_cpu_flush(last->page_cache);
+
+ return (sqh);
+}
+
+/**/
+
+#define UHCI_REMOVE_TD(std,last) (last) = _uhci_remove_td(std,last)
+static uhci_td_t *
+_uhci_remove_td(uhci_td_t *std, uhci_td_t *last)
+{
+ DPRINTFN(11, "%p from %p\n", std, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ std->prev->next = std->next;
+ std->prev->td_next = std->td_next;
+
+ usb2_pc_cpu_flush(std->prev->page_cache);
+
+ if (std->next) {
+ std->next->prev = std->prev;
+ usb2_pc_cpu_flush(std->next->page_cache);
+ }
+ return ((last == std) ? std->prev : last);
+}
+
+#define UHCI_REMOVE_QH(sqh,last) (last) = _uhci_remove_qh(sqh,last)
+static uhci_qh_t *
+_uhci_remove_qh(uhci_qh_t *sqh, uhci_qh_t *last)
+{
+ DPRINTFN(11, "%p from %p\n", sqh, last);
+
+ /* (sc->sc_bus.mtx) must be locked */
+
+ /* only remove if not removed from a queue */
+ if (sqh->h_prev) {
+
+ sqh->h_prev->h_next = sqh->h_next;
+ sqh->h_prev->qh_h_next = sqh->qh_h_next;
+
+ usb2_pc_cpu_flush(sqh->h_prev->page_cache);
+
+ if (sqh->h_next) {
+ sqh->h_next->h_prev = sqh->h_prev;
+ usb2_pc_cpu_flush(sqh->h_next->page_cache);
+ }
+ last = ((last == sqh) ? sqh->h_prev : last);
+
+ sqh->h_prev = 0;
+
+ usb2_pc_cpu_flush(sqh->page_cache);
+ }
+ return (last);
+}
+
+static void
+uhci_isoc_done(uhci_softc_t *sc, struct usb2_xfer *xfer)
+{
+ struct usb2_page_search res;
+ uint32_t nframes = xfer->nframes;
+ uint32_t status;
+ uint32_t offset = 0;
+ uint32_t *plen = xfer->frlengths;
+ uint16_t len = 0;
+ uhci_td_t *td = xfer->td_transfer_first;
+ uhci_td_t **pp_last = &sc->sc_isoc_p_last[xfer->qh_pos];
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ /* sync any DMA memory before doing fixups */
+
+ usb2_bdma_post_sync(xfer);
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) {
+ pp_last = &sc->sc_isoc_p_last[0];
+ }
+#if USB_DEBUG
+ if (uhcidebug > 5) {
+ DPRINTF("isoc TD\n");
+ uhci_dump_td(td);
+ }
+#endif
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = le32toh(td->td_status);
+
+ len = UHCI_TD_GET_ACTLEN(status);
+
+ if (len > *plen) {
+ len = *plen;
+ }
+ if (td->fix_pc) {
+
+ usb2_get_page(td->fix_pc, 0, &res);
+
+ /* copy data from fixup location to real location */
+
+ usb2_pc_cpu_invalidate(td->fix_pc);
+
+ usb2_copy_in(xfer->frbuffers, offset,
+ res.buffer, len);
+ }
+ offset += *plen;
+
+ *plen = len;
+
+ /* remove TD from schedule */
+ UHCI_REMOVE_TD(td, *pp_last);
+
+ pp_last++;
+ plen++;
+ td = td->obj_next;
+ }
+
+ xfer->aframes = xfer->nframes;
+}
+
+static usb2_error_t
+uhci_non_isoc_done_sub(struct usb2_xfer *xfer)
+{
+ struct usb2_page_search res;
+ uhci_td_t *td;
+ uhci_td_t *td_alt_next;
+ uint32_t status;
+ uint32_t token;
+ uint16_t len;
+
+ td = xfer->td_transfer_cache;
+ td_alt_next = td->alt_next;
+
+ if (xfer->aframes != xfer->nframes) {
+ xfer->frlengths[xfer->aframes] = 0;
+ }
+ while (1) {
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = le32toh(td->td_status);
+ token = le32toh(td->td_token);
+
+ /*
+ * Verify the status and add
+ * up the actual length:
+ */
+
+ len = UHCI_TD_GET_ACTLEN(status);
+ if (len > td->len) {
+ /* should not happen */
+ DPRINTF("Invalid status length, "
+ "0x%04x/0x%04x bytes\n", len, td->len);
+ status |= UHCI_TD_STALLED;
+
+ } else if ((xfer->aframes != xfer->nframes) && (len > 0)) {
+
+ if (td->fix_pc) {
+
+ usb2_get_page(td->fix_pc, 0, &res);
+
+ /*
+ * copy data from fixup location to real
+ * location
+ */
+
+ usb2_pc_cpu_invalidate(td->fix_pc);
+
+ usb2_copy_in(xfer->frbuffers + xfer->aframes,
+ xfer->frlengths[xfer->aframes], res.buffer, len);
+ }
+ /* update actual length */
+
+ xfer->frlengths[xfer->aframes] += len;
+ }
+ /* Check for last transfer */
+ if (((void *)td) == xfer->td_transfer_last) {
+ td = NULL;
+ break;
+ }
+ if (status & UHCI_TD_STALLED) {
+ /* the transfer is finished */
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len != td->len) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ td = td->alt_next;
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ break;
+ }
+ td = td->obj_next;
+
+ if (td->alt_next != td_alt_next) {
+ /* this USB frame is complete */
+ break;
+ }
+ }
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ /* update data toggle */
+
+ xfer->pipe->toggle_next = (token & UHCI_TD_SET_DT(1)) ? 0 : 1;
+
+#if USB_DEBUG
+ if (status & UHCI_TD_ERROR) {
+ DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x "
+ "status=%s%s%s%s%s%s%s%s%s%s%s\n",
+ xfer->address, xfer->endpoint, xfer->aframes,
+ (status & UHCI_TD_BITSTUFF) ? "[BITSTUFF]" : "",
+ (status & UHCI_TD_CRCTO) ? "[CRCTO]" : "",
+ (status & UHCI_TD_NAK) ? "[NAK]" : "",
+ (status & UHCI_TD_BABBLE) ? "[BABBLE]" : "",
+ (status & UHCI_TD_DBUFFER) ? "[DBUFFER]" : "",
+ (status & UHCI_TD_STALLED) ? "[STALLED]" : "",
+ (status & UHCI_TD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]",
+ (status & UHCI_TD_IOC) ? "[IOC]" : "",
+ (status & UHCI_TD_IOS) ? "[IOS]" : "",
+ (status & UHCI_TD_LS) ? "[LS]" : "",
+ (status & UHCI_TD_SPD) ? "[SPD]" : "");
+ }
+#endif
+ return (status & UHCI_TD_STALLED) ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION;
+}
+
+static void
+uhci_non_isoc_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+#if USB_DEBUG
+ if (uhcidebug > 10) {
+ uhci_dump_tds(xfer->td_transfer_first);
+ }
+#endif
+
+ /* sync any DMA memory before doing fixups */
+
+ usb2_bdma_post_sync(xfer);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ err = uhci_non_isoc_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = uhci_non_isoc_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = uhci_non_isoc_done_sub(xfer);
+ }
+done:
+ uhci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * uhci_check_transfer_sub
+ *
+ * The main purpose of this function is to update the data-toggle
+ * in case it is wrong.
+ *------------------------------------------------------------------------*/
+static void
+uhci_check_transfer_sub(struct usb2_xfer *xfer)
+{
+ uhci_qh_t *qh;
+ uhci_td_t *td;
+ uhci_td_t *td_alt_next;
+
+ uint32_t td_token;
+ uint32_t td_self;
+
+ td = xfer->td_transfer_cache;
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ td_token = td->obj_next->td_token;
+ td = td->alt_next;
+ xfer->td_transfer_cache = td;
+ td_self = td->td_self;
+ td_alt_next = td->alt_next;
+
+ if ((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1))) {
+
+ /*
+ * The data toggle is wrong and
+ * we need to switch it !
+ */
+
+ while (1) {
+
+ td->td_token ^= htole32(UHCI_TD_SET_DT(1));
+ usb2_pc_cpu_flush(td->page_cache);
+
+ if (td == xfer->td_transfer_last) {
+ /* last transfer */
+ break;
+ }
+ td = td->obj_next;
+
+ if (td->alt_next != td_alt_next) {
+ /* next frame */
+ break;
+ }
+ }
+ }
+ /* update the QH */
+ qh->qh_e_next = td_self;
+ usb2_pc_cpu_flush(qh->page_cache);
+
+ DPRINTFN(13, "xfer=%p following alt next\n", xfer);
+}
+
+/*------------------------------------------------------------------------*
+ * uhci_check_transfer
+ *
+ * Return values:
+ * 0: USB transfer is not finished
+ * Else: USB transfer is finished
+ *------------------------------------------------------------------------*/
+static uint8_t
+uhci_check_transfer(struct usb2_xfer *xfer)
+{
+ uint32_t status;
+ uint32_t token;
+ uhci_td_t *td;
+
+ DPRINTFN(16, "xfer=%p checking transfer\n", xfer);
+
+ if (xfer->pipe->methods == &uhci_device_isoc_methods) {
+ /* isochronous transfer */
+
+ td = xfer->td_transfer_last;
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = le32toh(td->td_status);
+
+ /* check also if the first is complete */
+
+ td = xfer->td_transfer_first;
+
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status |= le32toh(td->td_status);
+
+ if (!(status & UHCI_TD_ACTIVE)) {
+ uhci_device_done(xfer, USB_ERR_NORMAL_COMPLETION);
+ goto transferred;
+ }
+ } else {
+ /* non-isochronous transfer */
+
+ /*
+ * check whether there is an error somewhere
+ * in the middle, or whether there was a short
+ * packet (SPD and not ACTIVE)
+ */
+ td = xfer->td_transfer_cache;
+
+ while (1) {
+ usb2_pc_cpu_invalidate(td->page_cache);
+ status = le32toh(td->td_status);
+ token = le32toh(td->td_token);
+
+ /*
+ * if there is an active TD the transfer isn't done
+ */
+ if (status & UHCI_TD_ACTIVE) {
+ /* update cache */
+ xfer->td_transfer_cache = td;
+ goto done;
+ }
+ /*
+ * last transfer descriptor makes the transfer done
+ */
+ if (((void *)td) == xfer->td_transfer_last) {
+ break;
+ }
+ /*
+ * any kind of error makes the transfer done
+ */
+ if (status & UHCI_TD_STALLED) {
+ break;
+ }
+ /*
+ * check if we reached the last packet
+ * or if there is a short packet:
+ */
+ if ((td->td_next == htole32(UHCI_PTR_T)) ||
+ (UHCI_TD_GET_ACTLEN(status) < td->len)) {
+
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ /* update cache */
+ xfer->td_transfer_cache = td;
+ uhci_check_transfer_sub(xfer);
+ goto done;
+ }
+ }
+ /* transfer is done */
+ break;
+ }
+ td = td->obj_next;
+ }
+ uhci_non_isoc_done(xfer);
+ goto transferred;
+ }
+
+done:
+ DPRINTFN(13, "xfer=%p is still active\n", xfer);
+ return (0);
+
+transferred:
+ return (1);
+}
+
+static void
+uhci_interrupt_poll(uhci_softc_t *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ /*
+ * check if transfer is transferred
+ */
+ if (uhci_check_transfer(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * uhci_interrupt - UHCI interrupt handler
+ *
+ * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler,
+ * hence the interrupt handler will be setup before "sc->sc_bus.bdev"
+ * is present !
+ *------------------------------------------------------------------------*/
+void
+uhci_interrupt(uhci_softc_t *sc)
+{
+ uint32_t status;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ DPRINTFN(16, "real interrupt\n");
+
+#if USB_DEBUG
+ if (uhcidebug > 15) {
+ uhci_dumpregs(sc);
+ }
+#endif
+ status = UREAD2(sc, UHCI_STS) & UHCI_STS_ALLINTRS;
+ if (status == 0) {
+ /* the interrupt was not for us */
+ goto done;
+ }
+ if (status & (UHCI_STS_RD | UHCI_STS_HSE |
+ UHCI_STS_HCPE | UHCI_STS_HCH)) {
+
+ if (status & UHCI_STS_RD) {
+#if USB_DEBUG
+ printf("%s: resume detect\n",
+ __FUNCTION__);
+#endif
+ }
+ if (status & UHCI_STS_HSE) {
+ printf("%s: host system error\n",
+ __FUNCTION__);
+ }
+ if (status & UHCI_STS_HCPE) {
+ printf("%s: host controller process error\n",
+ __FUNCTION__);
+ }
+ if (status & UHCI_STS_HCH) {
+ /* no acknowledge needed */
+ DPRINTF("%s: host controller halted\n",
+ __FUNCTION__);
+#if USB_DEBUG
+ if (uhcidebug > 0) {
+ uhci_dump_all(sc);
+ }
+#endif
+ }
+ }
+ /* get acknowledge bits */
+ status &= (UHCI_STS_USBINT |
+ UHCI_STS_USBEI |
+ UHCI_STS_RD |
+ UHCI_STS_HSE |
+ UHCI_STS_HCPE);
+
+ if (status == 0) {
+ /* nothing to acknowledge */
+ goto done;
+ }
+ /* acknowledge interrupts */
+ UWRITE2(sc, UHCI_STS, status);
+
+ /* poll all the USB transfers */
+ uhci_interrupt_poll(sc);
+
+done:
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*
+ * called when a request does not complete
+ */
+static void
+uhci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ uhci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+uhci_do_poll(struct usb2_bus *bus)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ uhci_interrupt_poll(sc);
+ uhci_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+uhci_setup_standard_chain_sub(struct uhci_std_temp *temp)
+{
+ uhci_td_t *td;
+ uhci_td_t *td_next;
+ uhci_td_t *td_alt_next;
+ uint32_t average;
+ uint32_t len_old;
+ uint8_t shortpkt_old;
+ uint8_t precompute;
+
+ td_alt_next = NULL;
+ shortpkt_old = temp->shortpkt;
+ len_old = temp->len;
+ precompute = 1;
+
+ /* software is used to detect short incoming transfers */
+
+ if ((temp->td_token & htole32(UHCI_TD_PID)) == htole32(UHCI_TD_PID_IN)) {
+ temp->td_status |= htole32(UHCI_TD_SPD);
+ } else {
+ temp->td_status &= ~htole32(UHCI_TD_SPD);
+ }
+
+ temp->ml.buf_offset = 0;
+
+restart:
+
+ temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0));
+ temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->average));
+
+ td = temp->td;
+ td_next = temp->td_next;
+
+ while (1) {
+
+ if (temp->len == 0) {
+
+ if (temp->shortpkt) {
+ break;
+ }
+ /* send a Zero Length Packet, ZLP, last */
+
+ temp->shortpkt = 1;
+ temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(0));
+ average = 0;
+
+ } else {
+
+ average = temp->average;
+
+ if (temp->len < average) {
+ temp->shortpkt = 1;
+ temp->td_token &= ~htole32(UHCI_TD_SET_MAXLEN(0));
+ temp->td_token |= htole32(UHCI_TD_SET_MAXLEN(temp->len));
+ average = temp->len;
+ }
+ }
+
+ if (td_next == NULL) {
+ panic("%s: out of UHCI transfer descriptors!", __FUNCTION__);
+ }
+ /* get next TD */
+
+ td = td_next;
+ td_next = td->obj_next;
+
+ /* check if we are pre-computing */
+
+ if (precompute) {
+
+ /* update remaining length */
+
+ temp->len -= average;
+
+ continue;
+ }
+ /* fill out current TD */
+
+ td->td_status = temp->td_status;
+ td->td_token = temp->td_token;
+
+ /* update data toggle */
+
+ temp->td_token ^= htole32(UHCI_TD_SET_DT(1));
+
+ if (average == 0) {
+
+ td->len = 0;
+ td->td_buffer = 0;
+ td->fix_pc = NULL;
+
+ } else {
+
+ /* update remaining length */
+
+ temp->len -= average;
+
+ td->len = average;
+
+ /* fill out buffer pointer and do fixup, if any */
+
+ uhci_mem_layout_fixup(&temp->ml, td);
+ }
+
+ td->alt_next = td_alt_next;
+
+ if ((td_next == td_alt_next) && temp->setup_alt_next) {
+ /* we need to receive these frames one by one ! */
+ td->td_status |= htole32(UHCI_TD_IOC);
+ td->td_next = htole32(UHCI_PTR_T);
+ } else {
+ if (td_next) {
+ /* link the current TD with the next one */
+ td->td_next = td_next->td_self;
+ }
+ }
+
+ usb2_pc_cpu_flush(td->page_cache);
+ }
+
+ if (precompute) {
+ precompute = 0;
+
+ /* setup alt next pointer, if any */
+ if (temp->short_frames_ok) {
+ if (temp->setup_alt_next) {
+ td_alt_next = td_next;
+ }
+ } else {
+ /* we use this field internally */
+ td_alt_next = td_next;
+ }
+
+ /* restore */
+ temp->shortpkt = shortpkt_old;
+ temp->len = len_old;
+ goto restart;
+ }
+ temp->td = td;
+ temp->td_next = td_next;
+}
+
+static uhci_td_t *
+uhci_setup_standard_chain(struct usb2_xfer *xfer)
+{
+ struct uhci_std_temp temp;
+ uhci_td_t *td;
+ uint32_t x;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.average = xfer->max_frame_size;
+ temp.max_frame_size = xfer->max_frame_size;
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ temp.td = NULL;
+ temp.td_next = td;
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.short_frames_ok = xfer->flags_int.short_frames_ok;
+
+ uhci_mem_layout_init(&temp.ml, xfer);
+
+ temp.td_status =
+ htole32(UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) |
+ UHCI_TD_ACTIVE));
+
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ temp.td_status |= htole32(UHCI_TD_LS);
+ }
+ temp.td_token =
+ htole32(UHCI_TD_SET_ENDPT(xfer->endpoint) |
+ UHCI_TD_SET_DEVADDR(xfer->address));
+
+ if (xfer->pipe->toggle_next) {
+ /* DATA1 is next */
+ temp.td_token |= htole32(UHCI_TD_SET_DT(1));
+ }
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) |
+ UHCI_TD_SET_ENDPT(0xF));
+ temp.td_token |= htole32(UHCI_TD_PID_SETUP |
+ UHCI_TD_SET_DT(0));
+
+ temp.len = xfer->frlengths[0];
+ temp.ml.buf_pc = xfer->frbuffers + 0;
+ temp.shortpkt = temp.len ? 1 : 0;
+
+ uhci_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+ temp.ml.buf_pc = xfer->frbuffers + x;
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ /*
+ * Keep previous data toggle,
+ * device address and endpoint number:
+ */
+
+ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) |
+ UHCI_TD_SET_ENDPT(0xF) |
+ UHCI_TD_SET_DT(1));
+
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.shortpkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ /* set endpoint direction */
+
+ temp.td_token |=
+ (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ?
+ htole32(UHCI_TD_PID_IN) :
+ htole32(UHCI_TD_PID_OUT);
+
+ uhci_setup_standard_chain_sub(&temp);
+ }
+
+ /* check if we should append a status stage */
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ /*
+ * send a DATA1 message and reverse the current endpoint
+ * direction
+ */
+
+ temp.td_token &= htole32(UHCI_TD_SET_DEVADDR(0x7F) |
+ UHCI_TD_SET_ENDPT(0xF) |
+ UHCI_TD_SET_DT(1));
+ temp.td_token |=
+ (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ?
+ htole32(UHCI_TD_PID_IN | UHCI_TD_SET_DT(1)) :
+ htole32(UHCI_TD_PID_OUT | UHCI_TD_SET_DT(1));
+
+ temp.len = 0;
+ temp.ml.buf_pc = NULL;
+ temp.shortpkt = 0;
+
+ uhci_setup_standard_chain_sub(&temp);
+ }
+ td = temp.td;
+
+ td->td_next = htole32(UHCI_PTR_T);
+
+ /* set interrupt bit */
+
+ td->td_status |= htole32(UHCI_TD_IOC);
+
+ usb2_pc_cpu_flush(td->page_cache);
+
+ /* must have at least one frame! */
+
+ xfer->td_transfer_last = td;
+
+#if USB_DEBUG
+ if (uhcidebug > 8) {
+ DPRINTF("nexttog=%d; data before transfer:\n",
+ xfer->pipe->toggle_next);
+ uhci_dump_tds(xfer->td_transfer_first);
+ }
+#endif
+ return (xfer->td_transfer_first);
+}
+
+/* NOTE: "done" can be run two times in a row,
+ * from close and from interrupt
+ */
+
+static void
+uhci_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ struct usb2_pipe_methods *methods = xfer->pipe->methods;
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ uhci_qh_t *qh;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+ if (qh) {
+ usb2_pc_cpu_invalidate(qh->page_cache);
+ }
+ if (xfer->flags_int.bandwidth_reclaimed) {
+ xfer->flags_int.bandwidth_reclaimed = 0;
+ uhci_rem_loop(sc);
+ }
+ if (methods == &uhci_device_bulk_methods) {
+ UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last);
+ }
+ if (methods == &uhci_device_ctrl_methods) {
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last);
+ }
+ }
+ if (methods == &uhci_device_intr_methods) {
+ UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ /*
+ * Only finish isochronous transfers once
+ * which will update "xfer->frlengths".
+ */
+ if (xfer->td_transfer_first &&
+ xfer->td_transfer_last) {
+ if (methods == &uhci_device_isoc_methods) {
+ uhci_isoc_done(sc, xfer);
+ }
+ xfer->td_transfer_first = NULL;
+ xfer->td_transfer_last = NULL;
+ }
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+/*------------------------------------------------------------------------*
+ * uhci bulk support
+ *------------------------------------------------------------------------*/
+static void
+uhci_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_device_bulk_close(struct usb2_xfer *xfer)
+{
+ uhci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uhci_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_device_bulk_start(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ uhci_td_t *td;
+ uhci_qh_t *qh;
+
+ /* setup TD's */
+ td = uhci_setup_standard_chain(xfer);
+
+ /* setup QH */
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ qh->e_next = td;
+ qh->qh_e_next = td->td_self;
+
+ if (xfer->xroot->udev->pwr_save.suspended == 0) {
+ UHCI_APPEND_QH(qh, sc->sc_bulk_p_last);
+ uhci_add_loop(sc);
+ xfer->flags_int.bandwidth_reclaimed = 1;
+ } else {
+ usb2_pc_cpu_flush(qh->page_cache);
+ }
+
+ /* put transfer on interrupt queue */
+ uhci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods uhci_device_bulk_methods =
+{
+ .open = uhci_device_bulk_open,
+ .close = uhci_device_bulk_close,
+ .enter = uhci_device_bulk_enter,
+ .start = uhci_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * uhci control support
+ *------------------------------------------------------------------------*/
+static void
+uhci_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ uhci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uhci_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ uhci_qh_t *qh;
+ uhci_td_t *td;
+
+ /* setup TD's */
+ td = uhci_setup_standard_chain(xfer);
+
+ /* setup QH */
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ qh->e_next = td;
+ qh->qh_e_next = td->td_self;
+
+ /*
+ * NOTE: some devices choke on bandwidth- reclamation for control
+ * transfers
+ */
+ if (xfer->xroot->udev->pwr_save.suspended == 0) {
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last);
+ }
+ } else {
+ usb2_pc_cpu_flush(qh->page_cache);
+ }
+ /* put transfer on interrupt queue */
+ uhci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods uhci_device_ctrl_methods =
+{
+ .open = uhci_device_ctrl_open,
+ .close = uhci_device_ctrl_close,
+ .enter = uhci_device_ctrl_enter,
+ .start = uhci_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * uhci interrupt support
+ *------------------------------------------------------------------------*/
+static void
+uhci_device_intr_open(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ uint16_t best;
+ uint16_t bit;
+ uint16_t x;
+
+ best = 0;
+ bit = UHCI_IFRAMELIST_COUNT / 2;
+ while (bit) {
+ if (xfer->interval >= bit) {
+ x = bit;
+ best = bit;
+ while (x & bit) {
+ if (sc->sc_intr_stat[x] <
+ sc->sc_intr_stat[best]) {
+ best = x;
+ }
+ x++;
+ }
+ break;
+ }
+ bit >>= 1;
+ }
+
+ sc->sc_intr_stat[best]++;
+ xfer->qh_pos = best;
+
+ DPRINTFN(3, "best=%d interval=%d\n",
+ best, xfer->interval);
+}
+
+static void
+uhci_device_intr_close(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_intr_stat[xfer->qh_pos]--;
+
+ uhci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uhci_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_device_intr_start(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ uhci_qh_t *qh;
+ uhci_td_t *td;
+
+ /* setup TD's */
+ td = uhci_setup_standard_chain(xfer);
+
+ /* setup QH */
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ qh->e_next = td;
+ qh->qh_e_next = td->td_self;
+
+ if (xfer->xroot->udev->pwr_save.suspended == 0) {
+
+ /* enter QHs into the controller data structures */
+ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+
+ } else {
+ usb2_pc_cpu_flush(qh->page_cache);
+ }
+
+ /* put transfer on interrupt queue */
+ uhci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods uhci_device_intr_methods =
+{
+ .open = uhci_device_intr_open,
+ .close = uhci_device_intr_close,
+ .enter = uhci_device_intr_enter,
+ .start = uhci_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * uhci isochronous support
+ *------------------------------------------------------------------------*/
+static void
+uhci_device_isoc_open(struct usb2_xfer *xfer)
+{
+ uhci_td_t *td;
+ uint32_t td_token;
+ uint8_t ds;
+
+ td_token =
+ (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ?
+ UHCI_TD_IN(0, xfer->endpoint, xfer->address, 0) :
+ UHCI_TD_OUT(0, xfer->endpoint, xfer->address, 0);
+
+ td_token = htole32(td_token);
+
+ /* initialize all TD's */
+
+ for (ds = 0; ds != 2; ds++) {
+
+ for (td = xfer->td_start[ds]; td; td = td->obj_next) {
+
+ /* mark TD as inactive */
+ td->td_status = htole32(UHCI_TD_IOS);
+ td->td_token = td_token;
+
+ usb2_pc_cpu_flush(td->page_cache);
+ }
+ }
+}
+
+static void
+uhci_device_isoc_close(struct usb2_xfer *xfer)
+{
+ uhci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uhci_device_isoc_enter(struct usb2_xfer *xfer)
+{
+ struct uhci_mem_layout ml;
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ uint32_t nframes;
+ uint32_t temp;
+ uint32_t *plen;
+
+#if USB_DEBUG
+ uint8_t once = 1;
+
+#endif
+ uhci_td_t *td;
+ uhci_td_t *td_last = NULL;
+ uhci_td_t **pp_last;
+
+ DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ nframes = UREAD2(sc, UHCI_FRNUM);
+
+ temp = (nframes - xfer->pipe->isoc_next) &
+ (UHCI_VFRAMELIST_COUNT - 1);
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (temp < xfer->nframes)) {
+ /*
+ * If there is data underflow or the pipe queue is empty we
+ * schedule the transfer a few frames ahead of the current
+ * frame position. Else two isochronous transfers might
+ * overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) & (UHCI_VFRAMELIST_COUNT - 1);
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->pipe->isoc_next - nframes) &
+ (UHCI_VFRAMELIST_COUNT - 1);
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+ xfer->nframes;
+
+ /* get the real number of frames */
+
+ nframes = xfer->nframes;
+
+ uhci_mem_layout_init(&ml, xfer);
+
+ plen = xfer->frlengths;
+
+ /* toggle the DMA set we are using */
+ xfer->flags_int.curr_dma_set ^= 1;
+
+ /* get next DMA set */
+ td = xfer->td_start[xfer->flags_int.curr_dma_set];
+ xfer->td_transfer_first = td;
+
+ pp_last = &sc->sc_isoc_p_last[xfer->pipe->isoc_next];
+
+ /* store starting position */
+
+ xfer->qh_pos = xfer->pipe->isoc_next;
+
+ while (nframes--) {
+ if (td == NULL) {
+ panic("%s:%d: out of TD's\n",
+ __FUNCTION__, __LINE__);
+ }
+ if (pp_last >= &sc->sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]) {
+ pp_last = &sc->sc_isoc_p_last[0];
+ }
+ if (*plen > xfer->max_frame_size) {
+#if USB_DEBUG
+ if (once) {
+ once = 0;
+ printf("%s: frame length(%d) exceeds %d "
+ "bytes (frame truncated)\n",
+ __FUNCTION__, *plen,
+ xfer->max_frame_size);
+ }
+#endif
+ *plen = xfer->max_frame_size;
+ }
+ /* reuse td_token from last transfer */
+
+ td->td_token &= htole32(~UHCI_TD_MAXLEN_MASK);
+ td->td_token |= htole32(UHCI_TD_SET_MAXLEN(*plen));
+
+ td->len = *plen;
+
+ if (td->len == 0) {
+ /*
+ * Do not call "uhci_mem_layout_fixup()" when the
+ * length is zero!
+ */
+ td->td_buffer = 0;
+ td->fix_pc = NULL;
+
+ } else {
+
+ /* fill out buffer pointer and do fixup, if any */
+
+ uhci_mem_layout_fixup(&ml, td);
+
+ }
+
+ /* update status */
+ if (nframes == 0) {
+ td->td_status = htole32
+ (UHCI_TD_ZERO_ACTLEN
+ (UHCI_TD_SET_ERRCNT(0) |
+ UHCI_TD_ACTIVE |
+ UHCI_TD_IOS |
+ UHCI_TD_IOC));
+ } else {
+ td->td_status = htole32
+ (UHCI_TD_ZERO_ACTLEN
+ (UHCI_TD_SET_ERRCNT(0) |
+ UHCI_TD_ACTIVE |
+ UHCI_TD_IOS));
+ }
+
+ usb2_pc_cpu_flush(td->page_cache);
+
+#if USB_DEBUG
+ if (uhcidebug > 5) {
+ DPRINTF("TD %d\n", nframes);
+ uhci_dump_td(td);
+ }
+#endif
+ /* insert TD into schedule */
+ UHCI_APPEND_TD(td, *pp_last);
+ pp_last++;
+
+ plen++;
+ td_last = td;
+ td = td->obj_next;
+ }
+
+ xfer->td_transfer_last = td_last;
+
+ /* update isoc_next */
+ xfer->pipe->isoc_next = (pp_last - &sc->sc_isoc_p_last[0]) &
+ (UHCI_VFRAMELIST_COUNT - 1);
+}
+
+static void
+uhci_device_isoc_start(struct usb2_xfer *xfer)
+{
+ /* put transfer on interrupt queue */
+ uhci_transfer_intr_enqueue(xfer);
+}
+
+struct usb2_pipe_methods uhci_device_isoc_methods =
+{
+ .open = uhci_device_isoc_open,
+ .close = uhci_device_isoc_close,
+ .enter = uhci_device_isoc_enter,
+ .start = uhci_device_isoc_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * uhci root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware hub by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+
+static void
+uhci_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ uhci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/* data structures and routines
+ * to emulate the root hub:
+ */
+
+static const
+struct usb2_device_descriptor uhci_devd =
+{
+ sizeof(struct usb2_device_descriptor),
+ UDESC_DEVICE, /* type */
+ {0x00, 0x01}, /* USB version */
+ UDCLASS_HUB, /* class */
+ UDSUBCLASS_HUB, /* subclass */
+ UDPROTO_FSHUB, /* protocol */
+ 64, /* max packet */
+ {0}, {0}, {0x00, 0x01}, /* device id */
+ 1, 2, 0, /* string indicies */
+ 1 /* # of configurations */
+};
+
+static const struct uhci_config_desc uhci_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(uhci_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0 /* max power */
+ },
+
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_FSHUB,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = UE_DIR_IN | UHCI_INTR_ENDPT,
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8, /* max packet (63 ports) */
+ .bInterval = 255,
+ },
+};
+
+static const
+struct usb2_hub_descriptor_min uhci_hubd_piix =
+{
+ sizeof(uhci_hubd_piix),
+ UDESC_HUB,
+ 2,
+ {UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL, 0},
+ 50, /* power on to power good */
+ 0,
+ {0x00}, /* both ports are removable */
+};
+
+/*
+ * The USB hub protocol requires that SET_FEATURE(PORT_RESET) also
+ * enables the port, and also states that SET_FEATURE(PORT_ENABLE)
+ * should not be used by the USB subsystem. As we cannot issue a
+ * SET_FEATURE(PORT_ENABLE) externally, we must ensure that the port
+ * will be enabled as part of the reset.
+ *
+ * On the VT83C572, the port cannot be successfully enabled until the
+ * outstanding "port enable change" and "connection status change"
+ * events have been reset.
+ */
+static usb2_error_t
+uhci_portreset(uhci_softc_t *sc, uint16_t index, uint8_t use_polling)
+{
+ uint16_t port;
+ uint16_t x;
+ uint8_t lim;
+
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else
+ return (USB_ERR_IOERROR);
+
+ /*
+ * Before we do anything, turn on SOF messages on the USB
+ * BUS. Some USB devices do not cope without them!
+ */
+ if (!(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) {
+
+ DPRINTF("Activating SOFs!\n");
+
+ UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS));
+
+ /* wait a little bit */
+ if (use_polling) {
+ DELAY(10000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+ }
+ }
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PR);
+
+ if (use_polling) {
+ /* polling */
+ DELAY(USB_PORT_ROOT_RESET_DELAY * 1000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY));
+ }
+
+ DPRINTFN(4, "uhci port %d reset, status0 = 0x%04x\n",
+ index, UREAD2(sc, port));
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+
+
+ mtx_unlock(&sc->sc_bus.bus_mtx);
+
+ /*
+ * This delay needs to be exactly 100us, else some USB devices
+ * fail to attach!
+ */
+ DELAY(100);
+
+ mtx_lock(&sc->sc_bus.bus_mtx);
+
+ DPRINTFN(4, "uhci port %d reset, status1 = 0x%04x\n",
+ index, UREAD2(sc, port));
+
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+
+ for (lim = 0; lim < 12; lim++) {
+
+ if (use_polling) {
+ /* polling */
+ DELAY(USB_PORT_RESET_DELAY * 1000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx,
+ USB_MS_TO_TICKS(USB_PORT_RESET_DELAY));
+ }
+
+ x = UREAD2(sc, port);
+
+ DPRINTFN(4, "uhci port %d iteration %u, status = 0x%04x\n",
+ index, lim, x);
+
+ if (!(x & UHCI_PORTSC_CCS)) {
+ /*
+ * No device is connected (or was disconnected
+ * during reset). Consider the port reset.
+ * The delay must be long enough to ensure on
+ * the initial iteration that the device
+ * connection will have been registered. 50ms
+ * appears to be sufficient, but 20ms is not.
+ */
+ DPRINTFN(4, "uhci port %d loop %u, device detached\n",
+ index, lim);
+ goto done;
+ }
+ if (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)) {
+ /*
+ * Port enabled changed and/or connection
+ * status changed were set. Reset either or
+ * both raised flags (by writing a 1 to that
+ * bit), and wait again for state to settle.
+ */
+ UWRITE2(sc, port, URWMASK(x) |
+ (x & (UHCI_PORTSC_POEDC | UHCI_PORTSC_CSC)));
+ continue;
+ }
+ if (x & UHCI_PORTSC_PE) {
+ /* port is enabled */
+ goto done;
+ }
+ UWRITE2(sc, port, URWMASK(x) | UHCI_PORTSC_PE);
+ }
+
+ DPRINTFN(2, "uhci port %d reset timed out\n", index);
+ return (USB_ERR_TIMEOUT);
+
+done:
+ DPRINTFN(4, "uhci port %d reset, status2 = 0x%04x\n",
+ index, UREAD2(sc, port));
+
+ sc->sc_isreset = 1;
+ return (USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+uhci_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTF("\n");
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+uhci_root_ctrl_task(struct usb2_bus *bus)
+{
+ uhci_root_ctrl_poll(UHCI_BUS2SC(bus));
+}
+
+static void
+uhci_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+ char *ptr;
+ uint16_t x;
+ uint16_t port;
+ uint16_t value;
+ uint16_t index;
+ uint16_t status;
+ uint16_t change;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ uhci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = sc->sc_hub_desc.temp;
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x "
+ "wValue=0x%04x wIndex=0x%04x\n",
+ std->req.bmRequestType, std->req.bRequest,
+ UGETW(std->req.wLength), value, index);
+
+#define C(x,y) ((x) | ((y) << 8))
+ switch (C(std->req.bRequest, std->req.bmRequestType)) {
+ case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
+ /*
+ * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
+ * for the integrated root hub.
+ */
+ break;
+ case C(UR_GET_CONFIG, UT_READ_DEVICE):
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] = sc->sc_conf;
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(uhci_devd);
+ sc->sc_hub_desc.devd = uhci_devd;
+ break;
+
+ case UDESC_CONFIG:
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(uhci_confd);
+ std->ptr = USB_ADD_BYTES(&uhci_confd, 0);
+ break;
+
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ ptr = "\001";
+ break;
+
+ case 1: /* Vendor */
+ ptr = sc->sc_vendor;
+ break;
+
+ case 2: /* Product */
+ ptr = "UHCI root HUB";
+ break;
+
+ default:
+ ptr = "";
+ break;
+ }
+
+ std->len = usb2_make_str_desc
+ (sc->sc_hub_desc.temp,
+ sizeof(sc->sc_hub_desc.temp),
+ ptr);
+ break;
+
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] = 0;
+ break;
+ case C(UR_GET_STATUS, UT_READ_DEVICE):
+ std->len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED);
+ break;
+ case C(UR_GET_STATUS, UT_READ_INTERFACE):
+ case C(UR_GET_STATUS, UT_READ_ENDPOINT):
+ std->len = 2;
+ USETW(sc->sc_hub_desc.stat.wStatus, 0);
+ break;
+ case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
+ if (value >= USB_MAX_DEVICES) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_addr = value;
+ break;
+ case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
+ if ((value != 0) && (value != 1)) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ sc->sc_conf = value;
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
+ case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
+ case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
+ break;
+ case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
+ break;
+ /* Hub requests */
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
+ DPRINTFN(4, "UR_CLEAR_PORT_FEATURE "
+ "port=%d feature=%d\n",
+ index, value);
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~(UHCI_PORTSC_SUSP));
+ break;
+ case UHF_PORT_RESET:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x & ~UHCI_PORTSC_PR);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_CSC);
+ break;
+ case UHF_C_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_POEDC);
+ break;
+ case UHF_C_PORT_OVER_CURRENT:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_OCIC);
+ break;
+ case UHF_C_PORT_RESET:
+ sc->sc_isreset = 0;
+ std->err = USB_ERR_NORMAL_COMPLETION;
+ goto done;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_isresumed &= ~(1 << index);
+ break;
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_POWER:
+ case UHF_PORT_LOW_SPEED:
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ case C(UR_GET_BUS_STATE, UT_READ_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = 1;
+ sc->sc_hub_desc.temp[0] =
+ ((UREAD2(sc, port) & UHCI_PORTSC_LS) >>
+ UHCI_PORTSC_LS_SHIFT);
+ break;
+ case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
+ if ((value & 0xff) != 0) {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ std->len = sizeof(uhci_hubd_piix);
+ std->ptr = USB_ADD_BYTES(&uhci_hubd_piix, 0);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
+ std->len = 16;
+ bzero(sc->sc_hub_desc.temp, 16);
+ break;
+ case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ x = UREAD2(sc, port);
+ status = change = 0;
+ if (x & UHCI_PORTSC_CCS)
+ status |= UPS_CURRENT_CONNECT_STATUS;
+ if (x & UHCI_PORTSC_CSC)
+ change |= UPS_C_CONNECT_STATUS;
+ if (x & UHCI_PORTSC_PE)
+ status |= UPS_PORT_ENABLED;
+ if (x & UHCI_PORTSC_POEDC)
+ change |= UPS_C_PORT_ENABLED;
+ if (x & UHCI_PORTSC_OCI)
+ status |= UPS_OVERCURRENT_INDICATOR;
+ if (x & UHCI_PORTSC_OCIC)
+ change |= UPS_C_OVERCURRENT_INDICATOR;
+ if (x & UHCI_PORTSC_LSDA)
+ status |= UPS_LOW_SPEED;
+ if ((x & UHCI_PORTSC_PE) && (x & UHCI_PORTSC_RD)) {
+ /* need to do a write back */
+ UWRITE2(sc, port, URWMASK(x));
+
+ /* wait 20ms for resume sequence to complete */
+ if (use_polling) {
+ /* polling */
+ DELAY(20000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
+ }
+
+ /* clear suspend and resume detect */
+ UWRITE2(sc, port, URWMASK(x) & ~(UHCI_PORTSC_RD |
+ UHCI_PORTSC_SUSP));
+
+ /* wait a little bit */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 500);
+
+ sc->sc_isresumed |= (1 << index);
+
+ } else if (x & UHCI_PORTSC_SUSP) {
+ status |= UPS_SUSPEND;
+ }
+ status |= UPS_PORT_POWER;
+ if (sc->sc_isresumed & (1 << index))
+ change |= UPS_C_SUSPEND;
+ if (sc->sc_isreset)
+ change |= UPS_C_PORT_RESET;
+ USETW(sc->sc_hub_desc.ps.wPortStatus, status);
+ USETW(sc->sc_hub_desc.ps.wPortChange, change);
+ std->len = sizeof(sc->sc_hub_desc.ps);
+ break;
+ case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
+ break;
+ case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
+ if (index == 1)
+ port = UHCI_PORTSC1;
+ else if (index == 2)
+ port = UHCI_PORTSC2;
+ else {
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_PE);
+ break;
+ case UHF_PORT_SUSPEND:
+ x = URWMASK(UREAD2(sc, port));
+ UWRITE2(sc, port, x | UHCI_PORTSC_SUSP);
+ break;
+ case UHF_PORT_RESET:
+ std->err = uhci_portreset(sc, index, use_polling);
+ goto done;
+ case UHF_PORT_POWER:
+ /* pretend we turned on power */
+ std->err = USB_ERR_NORMAL_COMPLETION;
+ goto done;
+ case UHF_C_PORT_CONNECTION:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_PORT_CONNECTION:
+ case UHF_PORT_OVER_CURRENT:
+ case UHF_PORT_LOW_SPEED:
+ case UHF_C_PORT_SUSPEND:
+ case UHF_C_PORT_RESET:
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+done:
+ return;
+}
+
+static void
+uhci_root_ctrl_poll(struct uhci_softc *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &uhci_root_ctrl_done);
+}
+
+struct usb2_pipe_methods uhci_root_ctrl_methods =
+{
+ .open = uhci_root_ctrl_open,
+ .close = uhci_root_ctrl_close,
+ .enter = uhci_root_ctrl_enter,
+ .start = uhci_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * uhci root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+uhci_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_root_intr_close(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ uhci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uhci_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_root_intr_start(struct usb2_xfer *xfer)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+
+ usb2_transfer_timeout_ms(xfer,
+ &uhci_root_intr_check, xfer->interval);
+}
+
+static void
+uhci_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer is transferred */
+ uhci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+done:
+ return;
+}
+
+/*
+ * this routine is executed periodically and simulates interrupts
+ * from the root controller interrupt pipe for port status change
+ */
+static void
+uhci_root_intr_check(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+ uhci_softc_t *sc = UHCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTFN(21, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ sc->sc_hub_idata[0] = 0;
+
+ if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC |
+ UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) {
+ sc->sc_hub_idata[0] |= 1 << 1;
+ }
+ if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC |
+ UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) {
+ sc->sc_hub_idata[0] |= 1 << 2;
+ }
+ if (sc->sc_hub_idata[0] == 0) {
+ /*
+ * no change or controller not running, try again in a while
+ */
+ uhci_root_intr_start(xfer);
+ } else {
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &uhci_root_intr_done);
+ }
+}
+
+struct usb2_pipe_methods uhci_root_intr_methods =
+{
+ .open = uhci_root_intr_open,
+ .close = uhci_root_intr_close,
+ .enter = uhci_root_intr_enter,
+ .start = uhci_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+uhci_xfer_setup(struct usb2_setup_params *parm)
+{
+ struct usb2_page_search page_info;
+ struct usb2_page_cache *pc;
+ uhci_softc_t *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t nqh;
+ uint32_t nfixup;
+ uint32_t n;
+ uint16_t align;
+
+ sc = UHCI_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ parm->hc_max_packet_size = 0x500;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x500;
+
+ /*
+ * compute ntd and nqh
+ */
+ if (parm->methods == &uhci_device_ctrl_methods) {
+ xfer->flags_int.bdma_enable = 1;
+ xfer->flags_int.bdma_no_post_sync = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ /* see EHCI HC driver for proof of "ntd" formula */
+
+ nqh = 1;
+ ntd = ((2 * xfer->nframes) + 1 /* STATUS */
+ + (xfer->max_data_length / xfer->max_frame_size));
+
+ } else if (parm->methods == &uhci_device_bulk_methods) {
+ xfer->flags_int.bdma_enable = 1;
+ xfer->flags_int.bdma_no_post_sync = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 1;
+ ntd = ((2 * xfer->nframes)
+ + (xfer->max_data_length / xfer->max_frame_size));
+
+ } else if (parm->methods == &uhci_device_intr_methods) {
+ xfer->flags_int.bdma_enable = 1;
+ xfer->flags_int.bdma_no_post_sync = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 1;
+ ntd = ((2 * xfer->nframes)
+ + (xfer->max_data_length / xfer->max_frame_size));
+
+ } else if (parm->methods == &uhci_device_isoc_methods) {
+ xfer->flags_int.bdma_enable = 1;
+ xfer->flags_int.bdma_no_post_sync = 1;
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 0;
+ ntd = xfer->nframes;
+
+ } else {
+
+ usb2_transfer_setup_sub(parm);
+
+ nqh = 0;
+ ntd = 0;
+ }
+
+ if (parm->err) {
+ return;
+ }
+ /*
+ * NOTE: the UHCI controller requires that
+ * every packet must be contiguous on
+ * the same USB memory page !
+ */
+ nfixup = (parm->bufsize / USB_PAGE_SIZE) + 1;
+
+ /*
+ * Compute a suitable power of two alignment
+ * for our "max_frame_size" fixup buffer(s):
+ */
+ align = xfer->max_frame_size;
+ n = 0;
+ while (align) {
+ align >>= 1;
+ n++;
+ }
+
+ /* check for power of two */
+ if (!(xfer->max_frame_size &
+ (xfer->max_frame_size - 1))) {
+ n--;
+ }
+ /*
+ * We don't allow alignments of
+ * less than 8 bytes:
+ *
+ * NOTE: Allocating using an aligment
+ * of 1 byte has special meaning!
+ */
+ if (n < 3) {
+ n = 3;
+ }
+ align = (1 << n);
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, xfer->max_frame_size,
+ align, nfixup)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ xfer->buf_fixup = pc;
+
+alloc_dma_set:
+
+ if (parm->err) {
+ return;
+ }
+ last_obj = NULL;
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(uhci_td_t),
+ UHCI_TD_ALIGN, ntd)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != ntd; n++) {
+ uhci_td_t *td;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ td = page_info.buffer;
+
+ /* init TD */
+ if ((parm->methods == &uhci_device_bulk_methods) ||
+ (parm->methods == &uhci_device_ctrl_methods) ||
+ (parm->methods == &uhci_device_intr_methods)) {
+ /* set depth first bit */
+ td->td_self = htole32(page_info.physaddr |
+ UHCI_PTR_TD | UHCI_PTR_VF);
+ } else {
+ td->td_self = htole32(page_info.physaddr |
+ UHCI_PTR_TD);
+ }
+
+ td->obj_next = last_obj;
+ td->page_cache = pc + n;
+
+ last_obj = td;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
+
+ last_obj = NULL;
+
+ if (usb2_transfer_setup_sub_malloc(
+ parm, &pc, sizeof(uhci_qh_t),
+ UHCI_QH_ALIGN, nqh)) {
+ parm->err = USB_ERR_NOMEM;
+ return;
+ }
+ if (parm->buf) {
+ for (n = 0; n != nqh; n++) {
+ uhci_qh_t *qh;
+
+ usb2_get_page(pc + n, 0, &page_info);
+
+ qh = page_info.buffer;
+
+ /* init QH */
+ qh->qh_self = htole32(page_info.physaddr | UHCI_PTR_QH);
+ qh->obj_next = last_obj;
+ qh->page_cache = pc + n;
+
+ last_obj = qh;
+
+ usb2_pc_cpu_flush(pc + n);
+ }
+ }
+ xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj;
+
+ if (!xfer->flags_int.curr_dma_set) {
+ xfer->flags_int.curr_dma_set = 1;
+ goto alloc_dma_set;
+ }
+}
+
+static void
+uhci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ uhci_softc_t *sc = UHCI_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_addr);
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ if (udev->device_index == sc->sc_addr) {
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &uhci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | UHCI_INTR_ENDPT:
+ pipe->methods = &uhci_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &uhci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &uhci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ if (udev->speed == USB_SPEED_FULL) {
+ pipe->methods = &uhci_device_isoc_methods;
+ }
+ break;
+ case UE_BULK:
+ if (udev->speed != USB_SPEED_LOW) {
+ pipe->methods = &uhci_device_bulk_methods;
+ }
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+static void
+uhci_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uhci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus)
+{
+ /*
+ * Wait until hardware has finished any possible use of the
+ * transfer descriptor(s) and QH
+ */
+ *pus = (1125); /* microseconds */
+}
+
+static void
+uhci_device_resume(struct usb2_device *udev)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ uhci_qh_t *qh;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (methods == &uhci_device_bulk_methods) {
+ UHCI_APPEND_QH(qh, sc->sc_bulk_p_last);
+ uhci_add_loop(sc);
+ xfer->flags_int.bandwidth_reclaimed = 1;
+ }
+ if (methods == &uhci_device_ctrl_methods) {
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last);
+ }
+ }
+ if (methods == &uhci_device_intr_methods) {
+ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+uhci_device_suspend(struct usb2_device *udev)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ uhci_qh_t *qh;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->xroot->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (xfer->flags_int.bandwidth_reclaimed) {
+ xfer->flags_int.bandwidth_reclaimed = 0;
+ uhci_rem_loop(sc);
+ }
+ if (methods == &uhci_device_bulk_methods) {
+ UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last);
+ }
+ if (methods == &uhci_device_ctrl_methods) {
+ if (xfer->xroot->udev->speed == USB_SPEED_LOW) {
+ UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last);
+ }
+ }
+ if (methods == &uhci_device_intr_methods) {
+ UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+uhci_set_hw_power(struct usb2_bus *bus)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(bus);
+ uint32_t flags;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(bus);
+
+ flags = bus->hw_power_state;
+
+ /*
+ * WARNING: Some FULL speed USB devices require periodic SOF
+ * messages! If any USB devices are connected through the
+ * UHCI, power save will be disabled!
+ */
+ if (flags & (USB_HW_POWER_CONTROL |
+ USB_HW_POWER_NON_ROOT_HUB |
+ USB_HW_POWER_BULK |
+ USB_HW_POWER_INTERRUPT |
+ USB_HW_POWER_ISOC)) {
+ DPRINTF("Some USB transfer is "
+ "active on %u.\n",
+ device_get_unit(sc->sc_bus.bdev));
+ UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS));
+ } else {
+ DPRINTF("Power save on %u.\n",
+ device_get_unit(sc->sc_bus.bdev));
+ UHCICMD(sc, UHCI_CMD_MAXP);
+ }
+
+ USB_BUS_UNLOCK(bus);
+
+ return;
+}
+
+
+struct usb2_bus_methods uhci_bus_methods =
+{
+ .pipe_init = uhci_pipe_init,
+ .xfer_setup = uhci_xfer_setup,
+ .xfer_unsetup = uhci_xfer_unsetup,
+ .do_poll = uhci_do_poll,
+ .get_dma_delay = uhci_get_dma_delay,
+ .device_resume = uhci_device_resume,
+ .device_suspend = uhci_device_suspend,
+ .set_hw_power = uhci_set_hw_power,
+ .roothub_exec = uhci_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/uhci.h b/sys/dev/usb/controller/uhci.h
new file mode 100644
index 0000000..9365a4c
--- /dev/null
+++ b/sys/dev/usb/controller/uhci.h
@@ -0,0 +1,321 @@
+/* $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.
+ */
+
+#ifndef _UHCI_H_
+#define _UHCI_H_
+
+#define UHCI_MAX_DEVICES USB_MAX_DEVICES
+
+/* PCI config registers */
+#define PCI_USBREV 0x60 /* USB protocol revision */
+#define PCI_USB_REV_MASK 0xff
+#define PCI_USB_REV_PRE_1_0 0x00
+#define PCI_USB_REV_1_0 0x10
+#define PCI_USB_REV_1_1 0x11
+#define PCI_LEGSUP 0xc0 /* Legacy Support register */
+#define PCI_LEGSUP_USBPIRQDEN 0x2000 /* USB PIRQ D Enable */
+#define PCI_CBIO 0x20 /* configuration base IO */
+#define PCI_INTERFACE_UHCI 0x00
+
+/* UHCI registers */
+#define UHCI_CMD 0x00
+#define UHCI_CMD_RS 0x0001
+#define UHCI_CMD_HCRESET 0x0002
+#define UHCI_CMD_GRESET 0x0004
+#define UHCI_CMD_EGSM 0x0008
+#define UHCI_CMD_FGR 0x0010
+#define UHCI_CMD_SWDBG 0x0020
+#define UHCI_CMD_CF 0x0040
+#define UHCI_CMD_MAXP 0x0080
+#define UHCI_STS 0x02
+#define UHCI_STS_USBINT 0x0001
+#define UHCI_STS_USBEI 0x0002
+#define UHCI_STS_RD 0x0004
+#define UHCI_STS_HSE 0x0008
+#define UHCI_STS_HCPE 0x0010
+#define UHCI_STS_HCH 0x0020
+#define UHCI_STS_ALLINTRS 0x003f
+#define UHCI_INTR 0x04
+#define UHCI_INTR_TOCRCIE 0x0001
+#define UHCI_INTR_RIE 0x0002
+#define UHCI_INTR_IOCE 0x0004
+#define UHCI_INTR_SPIE 0x0008
+#define UHCI_FRNUM 0x06
+#define UHCI_FRNUM_MASK 0x03ff
+#define UHCI_FLBASEADDR 0x08
+#define UHCI_SOF 0x0c
+#define UHCI_SOF_MASK 0x7f
+#define UHCI_PORTSC1 0x010
+#define UHCI_PORTSC2 0x012
+#define UHCI_PORTSC_CCS 0x0001
+#define UHCI_PORTSC_CSC 0x0002
+#define UHCI_PORTSC_PE 0x0004
+#define UHCI_PORTSC_POEDC 0x0008
+#define UHCI_PORTSC_LS 0x0030
+#define UHCI_PORTSC_LS_SHIFT 4
+#define UHCI_PORTSC_RD 0x0040
+#define UHCI_PORTSC_LSDA 0x0100
+#define UHCI_PORTSC_PR 0x0200
+#define UHCI_PORTSC_OCI 0x0400
+#define UHCI_PORTSC_OCIC 0x0800
+#define UHCI_PORTSC_SUSP 0x1000
+
+#define URWMASK(x) ((x) & (UHCI_PORTSC_SUSP | \
+ UHCI_PORTSC_PR | UHCI_PORTSC_RD | \
+ UHCI_PORTSC_PE))
+
+#define UHCI_FRAMELIST_COUNT 1024 /* units */
+#define UHCI_FRAMELIST_ALIGN 4096 /* bytes */
+
+/* Structures alignment (bytes) */
+#define UHCI_TD_ALIGN 16
+#define UHCI_QH_ALIGN 16
+
+#if ((USB_PAGE_SIZE < UHCI_TD_ALIGN) || (UHCI_TD_ALIGN == 0) || \
+ (USB_PAGE_SIZE < UHCI_QH_ALIGN) || (UHCI_QH_ALIGN == 0))
+#error "Invalid USB page size!"
+#endif
+
+typedef uint32_t uhci_physaddr_t;
+
+#define UHCI_PTR_T 0x00000001
+#define UHCI_PTR_TD 0x00000000
+#define UHCI_PTR_QH 0x00000002
+#define UHCI_PTR_VF 0x00000004
+
+#define UHCI_QH_REMOVE_DELAY 5 /* us - QH remove delay */
+
+/*
+ * The Queue Heads (QH) and Transfer Descriptors (TD) are accessed by
+ * both the CPU and the USB-controller which run concurrently. Great
+ * care must be taken. When the data-structures are linked into the
+ * USB controller's frame list, the USB-controller "owns" the
+ * td_status and qh_elink fields, which will not be written by the
+ * CPU.
+ *
+ */
+
+struct uhci_td {
+/*
+ * Data used by the UHCI controller.
+ * volatile is used in order to mantain struct members ordering.
+ */
+ volatile uint32_t td_next;
+ volatile uint32_t td_status;
+#define UHCI_TD_GET_ACTLEN(s) (((s) + 1) & 0x3ff)
+#define UHCI_TD_ZERO_ACTLEN(t) ((t) | 0x3ff)
+#define UHCI_TD_BITSTUFF 0x00020000
+#define UHCI_TD_CRCTO 0x00040000
+#define UHCI_TD_NAK 0x00080000
+#define UHCI_TD_BABBLE 0x00100000
+#define UHCI_TD_DBUFFER 0x00200000
+#define UHCI_TD_STALLED 0x00400000
+#define UHCI_TD_ACTIVE 0x00800000
+#define UHCI_TD_IOC 0x01000000
+#define UHCI_TD_IOS 0x02000000
+#define UHCI_TD_LS 0x04000000
+#define UHCI_TD_GET_ERRCNT(s) (((s) >> 27) & 3)
+#define UHCI_TD_SET_ERRCNT(n) ((n) << 27)
+#define UHCI_TD_SPD 0x20000000
+ volatile uint32_t td_token;
+#define UHCI_TD_PID 0x000000ff
+#define UHCI_TD_PID_IN 0x00000069
+#define UHCI_TD_PID_OUT 0x000000e1
+#define UHCI_TD_PID_SETUP 0x0000002d
+#define UHCI_TD_GET_PID(s) ((s) & 0xff)
+#define UHCI_TD_SET_DEVADDR(a) ((a) << 8)
+#define UHCI_TD_GET_DEVADDR(s) (((s) >> 8) & 0x7f)
+#define UHCI_TD_SET_ENDPT(e) (((e) & 0xf) << 15)
+#define UHCI_TD_GET_ENDPT(s) (((s) >> 15) & 0xf)
+#define UHCI_TD_SET_DT(t) ((t) << 19)
+#define UHCI_TD_GET_DT(s) (((s) >> 19) & 1)
+#define UHCI_TD_SET_MAXLEN(l) (((l)-1) << 21)
+#define UHCI_TD_GET_MAXLEN(s) ((((s) >> 21) + 1) & 0x7ff)
+#define UHCI_TD_MAXLEN_MASK 0xffe00000
+ volatile uint32_t td_buffer;
+/*
+ * Extra information needed:
+ */
+ struct uhci_td *next;
+ struct uhci_td *prev;
+ struct uhci_td *obj_next;
+ struct usb2_page_cache *page_cache;
+ struct usb2_page_cache *fix_pc;
+ uint32_t td_self;
+ uint16_t len;
+} __aligned(UHCI_TD_ALIGN);
+
+typedef struct uhci_td uhci_td_t;
+
+#define UHCI_TD_ERROR (UHCI_TD_BITSTUFF | UHCI_TD_CRCTO | \
+ UHCI_TD_BABBLE | UHCI_TD_DBUFFER | UHCI_TD_STALLED)
+
+#define UHCI_TD_SETUP(len, endp, dev) (UHCI_TD_SET_MAXLEN(len) | \
+ UHCI_TD_SET_ENDPT(endp) | \
+ UHCI_TD_SET_DEVADDR(dev) | \
+ UHCI_TD_PID_SETUP)
+
+#define UHCI_TD_OUT(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | \
+ UHCI_TD_SET_ENDPT(endp) | \
+ UHCI_TD_SET_DEVADDR(dev) | \
+ UHCI_TD_PID_OUT | UHCI_TD_SET_DT(dt))
+
+#define UHCI_TD_IN(len, endp, dev, dt) (UHCI_TD_SET_MAXLEN(len) | \
+ UHCI_TD_SET_ENDPT(endp) | \
+ UHCI_TD_SET_DEVADDR(dev) | \
+ UHCI_TD_PID_IN | UHCI_TD_SET_DT(dt))
+
+struct uhci_qh {
+/*
+ * Data used by the UHCI controller.
+ */
+ volatile uint32_t qh_h_next;
+ volatile uint32_t qh_e_next;
+/*
+ * Extra information needed:
+ */
+ struct uhci_qh *h_next;
+ struct uhci_qh *h_prev;
+ struct uhci_qh *obj_next;
+ struct uhci_td *e_next;
+ struct usb2_page_cache *page_cache;
+ uint32_t qh_self;
+ uint16_t intr_pos;
+} __aligned(UHCI_QH_ALIGN);
+
+typedef struct uhci_qh uhci_qh_t;
+
+/* Maximum number of isochronous TD's and QH's interrupt */
+#define UHCI_VFRAMELIST_COUNT 128
+#define UHCI_IFRAMELIST_COUNT (2 * UHCI_VFRAMELIST_COUNT)
+
+#if (((UHCI_VFRAMELIST_COUNT & (UHCI_VFRAMELIST_COUNT-1)) != 0) || \
+ (UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT))
+#error "UHCI_VFRAMELIST_COUNT is not power of two"
+#error "or UHCI_VFRAMELIST_COUNT > UHCI_FRAMELIST_COUNT"
+#endif
+
+#if (UHCI_VFRAMELIST_COUNT < USB_MAX_FS_ISOC_FRAMES_PER_XFER)
+#error "maximum number of full-speed isochronous frames is higher than supported!"
+#endif
+
+struct uhci_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union uhci_hub_desc {
+ struct usb2_status stat;
+ struct usb2_port_status ps;
+ struct usb2_device_descriptor devd;
+ uint8_t temp[128];
+};
+
+struct uhci_hw_softc {
+ struct usb2_page_cache pframes_pc;
+ struct usb2_page_cache isoc_start_pc[UHCI_VFRAMELIST_COUNT];
+ struct usb2_page_cache intr_start_pc[UHCI_IFRAMELIST_COUNT];
+ struct usb2_page_cache ls_ctl_start_pc;
+ struct usb2_page_cache fs_ctl_start_pc;
+ struct usb2_page_cache bulk_start_pc;
+ struct usb2_page_cache last_qh_pc;
+ struct usb2_page_cache last_td_pc;
+
+ struct usb2_page pframes_pg;
+ struct usb2_page isoc_start_pg[UHCI_VFRAMELIST_COUNT];
+ struct usb2_page intr_start_pg[UHCI_IFRAMELIST_COUNT];
+ struct usb2_page ls_ctl_start_pg;
+ struct usb2_page fs_ctl_start_pg;
+ struct usb2_page bulk_start_pg;
+ struct usb2_page last_qh_pg;
+ struct usb2_page last_td_pg;
+};
+
+typedef struct uhci_softc {
+ struct uhci_hw_softc sc_hw;
+ struct usb2_bus sc_bus; /* base device */
+ union uhci_hub_desc sc_hub_desc;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+
+ struct usb2_device *sc_devices[UHCI_MAX_DEVICES];
+ struct uhci_td *sc_isoc_p_last[UHCI_VFRAMELIST_COUNT]; /* pointer to last TD
+ * for isochronous */
+ struct uhci_qh *sc_intr_p_last[UHCI_IFRAMELIST_COUNT]; /* pointer to last QH
+ * for interrupt */
+ struct uhci_qh *sc_ls_ctl_p_last; /* pointer to last QH for low
+ * speed control */
+ struct uhci_qh *sc_fs_ctl_p_last; /* pointer to last QH for full
+ * speed control */
+ struct uhci_qh *sc_bulk_p_last; /* pointer to last QH for bulk */
+ struct uhci_qh *sc_reclaim_qh_p;
+ struct uhci_qh *sc_last_qh_p;
+ struct uhci_td *sc_last_td_p;
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+ device_t sc_dev;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ uint32_t sc_loops; /* number of QHs that wants looping */
+
+ uint16_t sc_intr_stat[UHCI_IFRAMELIST_COUNT];
+ uint16_t sc_saved_frnum;
+
+ uint8_t sc_addr; /* device address */
+ uint8_t sc_conf; /* device configuration */
+ uint8_t sc_isreset; /* bits set if a root hub is reset */
+ uint8_t sc_isresumed; /* bits set if a port was resumed */
+ uint8_t sc_saved_sof;
+ uint8_t sc_hub_idata[1];
+
+ char sc_vendor[16]; /* vendor string for root hub */
+} uhci_softc_t;
+
+usb2_bus_mem_cb_t uhci_iterate_hw_softc;
+
+usb2_error_t uhci_init(uhci_softc_t *sc);
+void uhci_suspend(uhci_softc_t *sc);
+void uhci_resume(uhci_softc_t *sc);
+void uhci_reset(uhci_softc_t *sc);
+void uhci_interrupt(uhci_softc_t *sc);
+
+#endif /* _UHCI_H_ */
diff --git a/sys/dev/usb/controller/uhci_pci.c b/sys/dev/usb/controller/uhci_pci.c
new file mode 100644
index 0000000..f7f6f9c
--- /dev/null
+++ b/sys/dev/usb/controller/uhci_pci.c
@@ -0,0 +1,443 @@
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@carlstedt.se) 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Universal Host Controller Interface
+ *
+ * UHCI spec: http://www.intel.com/
+ */
+
+/* The low level controller code for UHCI has been split into
+ * PCI probes and UHCI specific code. This was done to facilitate the
+ * sharing of code between *BSD's
+ */
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/usb_pci.h>
+#include <dev/usb/controller/uhci.h>
+
+#define PCI_UHCI_VENDORID_INTEL 0x8086
+#define PCI_UHCI_VENDORID_VIA 0x1106
+
+/* PIIX4E has no separate stepping */
+
+#define PCI_UHCI_BASE_REG 0x20
+
+static device_probe_t uhci_pci_probe;
+static device_attach_t uhci_pci_attach;
+static device_detach_t uhci_pci_detach;
+static device_suspend_t uhci_pci_suspend;
+static device_resume_t uhci_pci_resume;
+
+static int
+uhci_pci_suspend(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ int err;
+
+ err = bus_generic_suspend(self);
+ if (err) {
+ return (err);
+ }
+ uhci_suspend(sc);
+ return (0);
+}
+
+static int
+uhci_pci_resume(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+
+ pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
+ uhci_resume(sc);
+
+ bus_generic_resume(self);
+ return (0);
+}
+
+static const char *
+uhci_pci_match(device_t self)
+{
+ uint32_t device_id = pci_get_devid(self);
+
+ switch (device_id) {
+ case 0x26888086:
+ return ("Intel 631XESB/632XESB/3100 USB controller USB-1");
+
+ case 0x26898086:
+ return ("Intel 631XESB/632XESB/3100 USB controller USB-2");
+
+ case 0x268a8086:
+ return ("Intel 631XESB/632XESB/3100 USB controller USB-3");
+
+ case 0x268b8086:
+ return ("Intel 631XESB/632XESB/3100 USB controller USB-4");
+
+ case 0x70208086:
+ return ("Intel 82371SB (PIIX3) USB controller");
+
+ case 0x71128086:
+ return ("Intel 82371AB/EB (PIIX4) USB controller");
+
+ case 0x24128086:
+ return ("Intel 82801AA (ICH) USB controller");
+
+ case 0x24228086:
+ return ("Intel 82801AB (ICH0) USB controller");
+
+ case 0x24428086:
+ return ("Intel 82801BA/BAM (ICH2) USB controller USB-A");
+
+ case 0x24448086:
+ return ("Intel 82801BA/BAM (ICH2) USB controller USB-B");
+
+ case 0x24828086:
+ return ("Intel 82801CA/CAM (ICH3) USB controller USB-A");
+
+ case 0x24848086:
+ return ("Intel 82801CA/CAM (ICH3) USB controller USB-B");
+
+ case 0x24878086:
+ return ("Intel 82801CA/CAM (ICH3) USB controller USB-C");
+
+ case 0x24c28086:
+ return ("Intel 82801DB (ICH4) USB controller USB-A");
+
+ case 0x24c48086:
+ return ("Intel 82801DB (ICH4) USB controller USB-B");
+
+ case 0x24c78086:
+ return ("Intel 82801DB (ICH4) USB controller USB-C");
+
+ case 0x24d28086:
+ return ("Intel 82801EB (ICH5) USB controller USB-A");
+
+ case 0x24d48086:
+ return ("Intel 82801EB (ICH5) USB controller USB-B");
+
+ case 0x24d78086:
+ return ("Intel 82801EB (ICH5) USB controller USB-C");
+
+ case 0x24de8086:
+ return ("Intel 82801EB (ICH5) USB controller USB-D");
+
+ case 0x26588086:
+ return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-A");
+
+ case 0x26598086:
+ return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-B");
+
+ case 0x265a8086:
+ return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-C");
+
+ case 0x265b8086:
+ return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D");
+
+ case 0x28308086:
+ return ("Intel 82801H (ICH8) USB controller USB-A");
+ case 0x28318086:
+ return ("Intel 82801H (ICH8) USB controller USB-B");
+ case 0x28328086:
+ return ("Intel 82801H (ICH8) USB controller USB-C");
+ case 0x28348086:
+ return ("Intel 82801H (ICH8) USB controller USB-D");
+ case 0x28358086:
+ return ("Intel 82801H (ICH8) USB controller USB-E");
+ case 0x29348086:
+ return ("Intel 82801I (ICH9) USB controller");
+ case 0x29358086:
+ return ("Intel 82801I (ICH9) USB controller");
+ case 0x29368086:
+ return ("Intel 82801I (ICH9) USB controller");
+ case 0x29378086:
+ return ("Intel 82801I (ICH9) USB controller");
+ case 0x29388086:
+ return ("Intel 82801I (ICH9) USB controller");
+ case 0x29398086:
+ return ("Intel 82801I (ICH9) USB controller");
+
+ case 0x719a8086:
+ return ("Intel 82443MX USB controller");
+
+ case 0x76028086:
+ return ("Intel 82372FB/82468GX USB controller");
+
+ case 0x30381106:
+ return ("VIA 83C572 USB controller");
+
+ default:
+ break;
+ }
+
+ if ((pci_get_class(self) == PCIC_SERIALBUS) &&
+ (pci_get_subclass(self) == PCIS_SERIALBUS_USB) &&
+ (pci_get_progif(self) == PCI_INTERFACE_UHCI)) {
+ return ("UHCI (generic) USB controller");
+ }
+ return (NULL);
+}
+
+static int
+uhci_pci_probe(device_t self)
+{
+ const char *desc = uhci_pci_match(self);
+
+ if (desc) {
+ device_set_desc(self, desc);
+ return (0);
+ } else {
+ return (ENXIO);
+ }
+}
+
+static int
+uhci_pci_attach(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ int rid;
+ int err;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = self;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = UHCI_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_bus, USB_GET_DMA_TAG(self),
+ &uhci_iterate_hw_softc)) {
+ return ENOMEM;
+ }
+ sc->sc_dev = self;
+
+ pci_enable_busmaster(self);
+
+ rid = PCI_UHCI_BASE_REG;
+ sc->sc_io_res = bus_alloc_resource_any(self, SYS_RES_IOPORT, &rid,
+ RF_ACTIVE);
+ if (!sc->sc_io_res) {
+ device_printf(self, "Could not map ports\n");
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ /* disable interrupts */
+ bus_space_write_2(sc->sc_io_tag, sc->sc_io_hdl, UHCI_INTR, 0);
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(self, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(self, "Could not allocate irq\n");
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(self, "usbus", -1);
+ if (!sc->sc_bus.bdev) {
+ device_printf(self, "Could not add USB device\n");
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+ /*
+ * uhci_pci_match must never return NULL if uhci_pci_probe
+ * succeeded
+ */
+ device_set_desc(sc->sc_bus.bdev, uhci_pci_match(self));
+ switch (pci_get_vendor(self)) {
+ case PCI_UHCI_VENDORID_INTEL:
+ sprintf(sc->sc_vendor, "Intel");
+ break;
+ case PCI_UHCI_VENDORID_VIA:
+ sprintf(sc->sc_vendor, "VIA");
+ break;
+ default:
+ if (bootverbose) {
+ device_printf(self, "(New UHCI DeviceId=0x%08x)\n",
+ pci_get_devid(self));
+ }
+ sprintf(sc->sc_vendor, "(0x%04x)", pci_get_vendor(self));
+ }
+
+ switch (pci_read_config(self, PCI_USBREV, 1) & PCI_USB_REV_MASK) {
+ case PCI_USB_REV_PRE_1_0:
+ sc->sc_bus.usbrev = USB_REV_PRE_1_0;
+ break;
+ case PCI_USB_REV_1_0:
+ sc->sc_bus.usbrev = USB_REV_1_0;
+ break;
+ default:
+ /* Quirk for Parallels Desktop 4.0 */
+ device_printf(self, "USB revision is unknown. Assuming v1.1.\n");
+ sc->sc_bus.usbrev = USB_REV_1_1;
+ break;
+ }
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)(void *)uhci_interrupt, sc, &sc->sc_intr_hdl);
+#else
+ err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)(void *)uhci_interrupt, sc, &sc->sc_intr_hdl);
+#endif
+
+ if (err) {
+ device_printf(self, "Could not setup irq, %d\n", err);
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ /*
+ * Set the PIRQD enable bit and switch off all the others. We don't
+ * want legacy support to interfere with us XXX Does this also mean
+ * that the BIOS won't touch the keyboard anymore if it is connected
+ * to the ports of the root hub?
+ */
+#if USB_DEBUG
+ if (pci_read_config(self, PCI_LEGSUP, 2) != PCI_LEGSUP_USBPIRQDEN) {
+ device_printf(self, "LegSup = 0x%04x\n",
+ pci_read_config(self, PCI_LEGSUP, 2));
+ }
+#endif
+ pci_write_config(self, PCI_LEGSUP, PCI_LEGSUP_USBPIRQDEN, 2);
+
+ err = uhci_init(sc);
+ if (!err) {
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ }
+ if (err) {
+ device_printf(self, "USB init failed\n");
+ goto error;
+ }
+ return (0);
+
+error:
+ uhci_pci_detach(self);
+ return (ENXIO);
+}
+
+int
+uhci_pci_detach(device_t self)
+{
+ uhci_softc_t *sc = device_get_softc(self);
+ device_t bdev;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(self, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(self);
+
+ /*
+ * disable interrupts that might have been switched on in
+ * uhci_init.
+ */
+ if (sc->sc_io_res) {
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* stop the controller */
+ uhci_reset(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+ }
+ pci_disable_busmaster(self);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ int err = bus_teardown_intr(self, sc->sc_irq_res, sc->sc_intr_hdl);
+
+ if (err) {
+ /* XXX or should we panic? */
+ device_printf(self, "Could not tear down irq, %d\n",
+ err);
+ }
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(self, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(self, SYS_RES_IOPORT, PCI_UHCI_BASE_REG,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_bus, &uhci_iterate_hw_softc);
+
+ return (0);
+}
+
+static driver_t uhci_driver =
+{
+ .name = "uhci",
+ .methods = (device_method_t[]){
+ /* device interface */
+ DEVMETHOD(device_probe, uhci_pci_probe),
+ DEVMETHOD(device_attach, uhci_pci_attach),
+ DEVMETHOD(device_detach, uhci_pci_detach),
+
+ DEVMETHOD(device_suspend, uhci_pci_suspend),
+ DEVMETHOD(device_resume, uhci_pci_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ {0, 0}
+ },
+ .size = sizeof(struct uhci_softc),
+};
+
+static devclass_t uhci_devclass;
+
+DRIVER_MODULE(uhci, pci, uhci_driver, uhci_devclass, 0, 0);
+DRIVER_MODULE(uhci, cardbus, uhci_driver, uhci_devclass, 0, 0);
+MODULE_DEPEND(uhci, usb, 1, 1, 1);
diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c
new file mode 100644
index 0000000..b91be6c
--- /dev/null
+++ b/sys/dev/usb/controller/usb_controller.c
@@ -0,0 +1,620 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+
+#define USB_DEBUG_VAR usb2_ctrl_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+/* function prototypes */
+
+static device_probe_t usb2_probe;
+static device_attach_t usb2_attach;
+static device_detach_t usb2_detach;
+
+static void usb2_attach_sub(device_t, struct usb2_bus *);
+static void usb2_post_init(void *);
+static void usb2_bus_mem_flush_all_cb(struct usb2_bus *,
+ struct usb2_page_cache *, struct usb2_page *, uint32_t,
+ uint32_t);
+static void usb2_bus_mem_alloc_all_cb(struct usb2_bus *,
+ struct usb2_page_cache *, struct usb2_page *, uint32_t,
+ uint32_t);
+static void usb2_bus_mem_free_all_cb(struct usb2_bus *,
+ struct usb2_page_cache *, struct usb2_page *, uint32_t,
+ uint32_t);
+static void usb2_bus_roothub(struct usb2_proc_msg *pm);
+
+/* static variables */
+
+#if USB_DEBUG
+static int usb2_ctrl_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ctrl, CTLFLAG_RW, 0, "USB controller");
+SYSCTL_INT(_hw_usb2_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb2_ctrl_debug, 0,
+ "Debug level");
+#endif
+
+static uint8_t usb2_post_init_called = 0;
+
+static devclass_t usb2_devclass;
+
+static device_method_t usb2_methods[] = {
+ DEVMETHOD(device_probe, usb2_probe),
+ DEVMETHOD(device_attach, usb2_attach),
+ DEVMETHOD(device_detach, usb2_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ {0, 0}
+};
+
+static driver_t usb2_driver = {
+ .name = "usbus",
+ .methods = usb2_methods,
+ .size = 0,
+};
+
+DRIVER_MODULE(usbus, ohci, usb2_driver, usb2_devclass, 0, 0);
+DRIVER_MODULE(usbus, uhci, usb2_driver, usb2_devclass, 0, 0);
+DRIVER_MODULE(usbus, ehci, usb2_driver, usb2_devclass, 0, 0);
+DRIVER_MODULE(usbus, at91_udp, usb2_driver, usb2_devclass, 0, 0);
+DRIVER_MODULE(usbus, uss820, usb2_driver, usb2_devclass, 0, 0);
+
+/*------------------------------------------------------------------------*
+ * usb2_probe
+ *
+ * This function is called from "{ehci,ohci,uhci}_pci_attach()".
+ *------------------------------------------------------------------------*/
+static int
+usb2_probe(device_t dev)
+{
+ DPRINTF("\n");
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_attach
+ *------------------------------------------------------------------------*/
+static int
+usb2_attach(device_t dev)
+{
+ struct usb2_bus *bus = device_get_ivars(dev);
+
+ DPRINTF("\n");
+
+ if (bus == NULL) {
+ DPRINTFN(0, "USB device has no ivars\n");
+ return (ENXIO);
+ }
+
+ /* delay vfs_mountroot until the bus is explored */
+ bus->bus_roothold = root_mount_hold(device_get_nameunit(dev));
+
+ if (usb2_post_init_called) {
+ mtx_lock(&Giant);
+ usb2_attach_sub(dev, bus);
+ mtx_unlock(&Giant);
+ usb2_needs_explore(bus, 1);
+ }
+ return (0); /* return success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_detach
+ *------------------------------------------------------------------------*/
+static int
+usb2_detach(device_t dev)
+{
+ struct usb2_bus *bus = device_get_softc(dev);
+
+ DPRINTF("\n");
+
+ if (bus == NULL) {
+ /* was never setup properly */
+ return (0);
+ }
+ /* Stop power watchdog */
+ usb2_callout_drain(&bus->power_wdog);
+
+ /* Let the USB explore process detach all devices. */
+ if (bus->bus_roothold != NULL) {
+ root_mount_rel(bus->bus_roothold);
+ bus->bus_roothold = NULL;
+ }
+
+ USB_BUS_LOCK(bus);
+ if (usb2_proc_msignal(&bus->explore_proc,
+ &bus->detach_msg[0], &bus->detach_msg[1])) {
+ /* ignore */
+ }
+ /* Wait for detach to complete */
+
+ usb2_proc_mwait(&bus->explore_proc,
+ &bus->detach_msg[0], &bus->detach_msg[1]);
+
+ USB_BUS_UNLOCK(bus);
+
+ /* Get rid of USB callback processes */
+
+ usb2_proc_free(&bus->giant_callback_proc);
+ usb2_proc_free(&bus->non_giant_callback_proc);
+
+ /* Get rid of USB roothub process */
+
+ usb2_proc_free(&bus->roothub_proc);
+
+ /* Get rid of USB explore process */
+
+ usb2_proc_free(&bus->explore_proc);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_explore
+ *
+ * This function is used to explore the device tree from the root.
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_explore(struct usb2_proc_msg *pm)
+{
+ struct usb2_bus *bus;
+ struct usb2_device *udev;
+
+ bus = ((struct usb2_bus_msg *)pm)->bus;
+ udev = bus->devices[USB_ROOT_HUB_ADDR];
+
+ if (udev && udev->hub) {
+
+ if (bus->do_probe) {
+ bus->do_probe = 0;
+ bus->driver_added_refcount++;
+ }
+ if (bus->driver_added_refcount == 0) {
+ /* avoid zero, hence that is memory default */
+ bus->driver_added_refcount = 1;
+ }
+ USB_BUS_UNLOCK(bus);
+
+ mtx_lock(&Giant);
+
+ /*
+ * First update the USB power state!
+ */
+ usb2_bus_powerd(bus);
+
+ /*
+ * Explore the Root USB HUB. This call can sleep,
+ * exiting Giant, which is actually Giant.
+ */
+ (udev->hub->explore) (udev);
+
+ mtx_unlock(&Giant);
+
+ USB_BUS_LOCK(bus);
+ }
+ if (bus->bus_roothold != NULL) {
+ root_mount_rel(bus->bus_roothold);
+ bus->bus_roothold = NULL;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_detach
+ *
+ * This function is used to detach the device tree from the root.
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_detach(struct usb2_proc_msg *pm)
+{
+ struct usb2_bus *bus;
+ struct usb2_device *udev;
+ device_t dev;
+
+ bus = ((struct usb2_bus_msg *)pm)->bus;
+ udev = bus->devices[USB_ROOT_HUB_ADDR];
+ dev = bus->bdev;
+ /* clear the softc */
+ device_set_softc(dev, NULL);
+ USB_BUS_UNLOCK(bus);
+
+ mtx_lock(&Giant);
+
+ /* detach children first */
+ bus_generic_detach(dev);
+
+ /*
+ * Free USB Root device, but not any sub-devices, hence they
+ * are freed by the caller of this function:
+ */
+ usb2_detach_device(udev, USB_IFACE_INDEX_ANY, 0);
+ usb2_free_device(udev);
+
+ mtx_unlock(&Giant);
+ USB_BUS_LOCK(bus);
+ /* clear bdev variable last */
+ bus->bdev = NULL;
+}
+
+static void
+usb2_power_wdog(void *arg)
+{
+ struct usb2_bus *bus = arg;
+
+ USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
+
+ usb2_callout_reset(&bus->power_wdog,
+ 4 * hz, usb2_power_wdog, arg);
+
+ USB_BUS_UNLOCK(bus);
+
+ usb2_bus_power_update(bus);
+
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_attach
+ *
+ * This function attaches USB in context of the explore thread.
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_attach(struct usb2_proc_msg *pm)
+{
+ struct usb2_bus *bus;
+ struct usb2_device *child;
+ device_t dev;
+ usb2_error_t err;
+ uint8_t speed;
+
+ bus = ((struct usb2_bus_msg *)pm)->bus;
+ dev = bus->bdev;
+
+ DPRINTF("\n");
+
+ switch (bus->usbrev) {
+ case USB_REV_1_0:
+ speed = USB_SPEED_FULL;
+ device_printf(bus->bdev, "12Mbps Full Speed USB v1.0\n");
+ break;
+
+ case USB_REV_1_1:
+ speed = USB_SPEED_FULL;
+ device_printf(bus->bdev, "12Mbps Full Speed USB v1.1\n");
+ break;
+
+ case USB_REV_2_0:
+ speed = USB_SPEED_HIGH;
+ device_printf(bus->bdev, "480Mbps High Speed USB v2.0\n");
+ break;
+
+ case USB_REV_2_5:
+ speed = USB_SPEED_VARIABLE;
+ device_printf(bus->bdev, "480Mbps Wireless USB v2.5\n");
+ break;
+
+ default:
+ device_printf(bus->bdev, "Unsupported USB revision!\n");
+ return;
+ }
+
+ USB_BUS_UNLOCK(bus);
+ mtx_lock(&Giant); /* XXX not required by USB */
+
+ /* Allocate the Root USB device */
+
+ child = usb2_alloc_device(bus->bdev, bus, NULL, 0, 0, 1,
+ speed, USB_MODE_HOST);
+ if (child) {
+ err = usb2_probe_and_attach(child,
+ USB_IFACE_INDEX_ANY);
+ if (!err) {
+ if (!bus->devices[USB_ROOT_HUB_ADDR]->hub) {
+ err = USB_ERR_NO_ROOT_HUB;
+ }
+ }
+ } else {
+ err = USB_ERR_NOMEM;
+ }
+
+ mtx_unlock(&Giant);
+ USB_BUS_LOCK(bus);
+
+ if (err) {
+ device_printf(bus->bdev, "Root HUB problem, error=%s\n",
+ usb2_errstr(err));
+ }
+
+ /* set softc - we are ready */
+ device_set_softc(dev, bus);
+
+ /* start watchdog - this function will unlock the BUS lock ! */
+ usb2_power_wdog(bus);
+
+ /* need to return locked */
+ USB_BUS_LOCK(bus);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_attach_sub
+ *
+ * This function creates a thread which runs the USB attach code. It
+ * is factored out, hence it can be called at two different places in
+ * time. During bootup this function is called from
+ * "usb2_post_init". During hot-plug it is called directly from the
+ * "usb2_attach()" method.
+ *------------------------------------------------------------------------*/
+static void
+usb2_attach_sub(device_t dev, struct usb2_bus *bus)
+{
+ const char *pname = device_get_nameunit(dev);
+
+ /* Initialise USB process messages */
+ bus->explore_msg[0].hdr.pm_callback = &usb2_bus_explore;
+ bus->explore_msg[0].bus = bus;
+ bus->explore_msg[1].hdr.pm_callback = &usb2_bus_explore;
+ bus->explore_msg[1].bus = bus;
+
+ bus->detach_msg[0].hdr.pm_callback = &usb2_bus_detach;
+ bus->detach_msg[0].bus = bus;
+ bus->detach_msg[1].hdr.pm_callback = &usb2_bus_detach;
+ bus->detach_msg[1].bus = bus;
+
+ bus->attach_msg[0].hdr.pm_callback = &usb2_bus_attach;
+ bus->attach_msg[0].bus = bus;
+ bus->attach_msg[1].hdr.pm_callback = &usb2_bus_attach;
+ bus->attach_msg[1].bus = bus;
+
+ bus->roothub_msg[0].hdr.pm_callback = &usb2_bus_roothub;
+ bus->roothub_msg[0].bus = bus;
+ bus->roothub_msg[1].hdr.pm_callback = &usb2_bus_roothub;
+ bus->roothub_msg[1].bus = bus;
+
+ /* Create USB explore, roothub and callback processes */
+
+ if (usb2_proc_create(&bus->giant_callback_proc,
+ &bus->bus_mtx, pname, USB_PRI_MED)) {
+ printf("WARNING: Creation of USB Giant "
+ "callback process failed.\n");
+ } else if (usb2_proc_create(&bus->non_giant_callback_proc,
+ &bus->bus_mtx, pname, USB_PRI_HIGH)) {
+ printf("WARNING: Creation of USB non-Giant "
+ "callback process failed.\n");
+ } else if (usb2_proc_create(&bus->roothub_proc,
+ &bus->bus_mtx, pname, USB_PRI_HIGH)) {
+ printf("WARNING: Creation of USB roothub "
+ "process failed.\n");
+ } else if (usb2_proc_create(&bus->explore_proc,
+ &bus->bus_mtx, pname, USB_PRI_MED)) {
+ printf("WARNING: Creation of USB explore "
+ "process failed.\n");
+ } else {
+ /* Get final attach going */
+ USB_BUS_LOCK(bus);
+ if (usb2_proc_msignal(&bus->explore_proc,
+ &bus->attach_msg[0], &bus->attach_msg[1])) {
+ /* ignore */
+ }
+ USB_BUS_UNLOCK(bus);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_post_init
+ *
+ * This function is called to attach all USB busses that were found
+ * during bootup.
+ *------------------------------------------------------------------------*/
+static void
+usb2_post_init(void *arg)
+{
+ struct usb2_bus *bus;
+ devclass_t dc;
+ device_t dev;
+ int max;
+ int n;
+
+ mtx_lock(&Giant);
+
+ usb2_devclass_ptr = devclass_find("usbus");
+
+ dc = usb2_devclass_ptr;
+ if (dc) {
+ max = devclass_get_maxunit(dc) + 1;
+ for (n = 0; n != max; n++) {
+ dev = devclass_get_device(dc, n);
+ if (dev && device_is_attached(dev)) {
+ bus = device_get_ivars(dev);
+ if (bus) {
+ mtx_lock(&Giant);
+ usb2_attach_sub(dev, bus);
+ mtx_unlock(&Giant);
+ }
+ }
+ }
+ } else {
+ DPRINTFN(0, "no devclass\n");
+ }
+ usb2_post_init_called = 1;
+
+ /* explore all USB busses in parallell */
+
+ usb2_needs_explore_all();
+
+ mtx_unlock(&Giant);
+}
+
+SYSINIT(usb2_post_init, SI_SUB_KICK_SCHEDULER, SI_ORDER_ANY, usb2_post_init, NULL);
+SYSUNINIT(usb2_bus_unload, SI_SUB_KLD, SI_ORDER_ANY, usb2_bus_unload, NULL);
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_mem_flush_all_cb
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_mem_flush_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc,
+ struct usb2_page *pg, uint32_t size, uint32_t align)
+{
+ usb2_pc_cpu_flush(pc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_mem_flush_all - factored out code
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_mem_flush_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb)
+{
+ if (cb) {
+ cb(bus, &usb2_bus_mem_flush_all_cb);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_mem_alloc_all_cb
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_mem_alloc_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc,
+ struct usb2_page *pg, uint32_t size, uint32_t align)
+{
+ /* need to initialize the page cache */
+ pc->tag_parent = bus->dma_parent_tag;
+
+ if (usb2_pc_alloc_mem(pc, pg, size, align)) {
+ bus->alloc_failed = 1;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_mem_alloc_all - factored out code
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat,
+ usb2_bus_mem_cb_t *cb)
+{
+ bus->alloc_failed = 0;
+
+ mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent),
+ NULL, MTX_DEF | MTX_RECURSE);
+
+ usb2_callout_init_mtx(&bus->power_wdog,
+ &bus->bus_mtx, CALLOUT_RETURNUNLOCKED);
+
+ TAILQ_INIT(&bus->intr_q.head);
+
+ usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags,
+ dmat, &bus->bus_mtx, NULL, NULL, 32, USB_BUS_DMA_TAG_MAX);
+
+ if ((bus->devices_max > USB_MAX_DEVICES) ||
+ (bus->devices_max < USB_MIN_DEVICES) ||
+ (bus->devices == NULL)) {
+ DPRINTFN(0, "Devices field has not been "
+ "initialised properly!\n");
+ bus->alloc_failed = 1; /* failure */
+ }
+ if (cb) {
+ cb(bus, &usb2_bus_mem_alloc_all_cb);
+ }
+ if (bus->alloc_failed) {
+ usb2_bus_mem_free_all(bus, cb);
+ }
+ return (bus->alloc_failed);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_mem_free_all_cb
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_mem_free_all_cb(struct usb2_bus *bus, struct usb2_page_cache *pc,
+ struct usb2_page *pg, uint32_t size, uint32_t align)
+{
+ usb2_pc_free_mem(pc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_mem_free_all - factored out code
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_mem_free_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb)
+{
+ if (cb) {
+ cb(bus, &usb2_bus_mem_free_all_cb);
+ }
+ usb2_dma_tag_unsetup(bus->dma_parent_tag);
+
+ mtx_destroy(&bus->bus_mtx);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_roothub
+ *
+ * This function is used to execute roothub control requests on the
+ * roothub and is called from the roothub process.
+ *------------------------------------------------------------------------*/
+static void
+usb2_bus_roothub(struct usb2_proc_msg *pm)
+{
+ struct usb2_bus *bus;
+
+ bus = ((struct usb2_bus_msg *)pm)->bus;
+
+ USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
+
+ (bus->methods->roothub_exec) (bus);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_roothub_exec
+ *
+ * This function is used to schedule the "roothub_done" bus callback
+ * method. The bus lock must be locked when calling this function.
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_roothub_exec(struct usb2_bus *bus)
+{
+ USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
+
+ if (usb2_proc_msignal(&bus->roothub_proc,
+ &bus->roothub_msg[0], &bus->roothub_msg[1])) {
+ /* ignore */
+ }
+}
diff --git a/sys/dev/usb/controller/uss820dci.c b/sys/dev/usb/controller/uss820dci.c
new file mode 100644
index 0000000..2adc4e3
--- /dev/null
+++ b/sys/dev/usb/controller/uss820dci.c
@@ -0,0 +1,2489 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the driver for the USS820 series USB Device
+ * Controller
+ *
+ * NOTE: The datasheet does not document everything!
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_revision.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR uss820dcidebug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/uss820dci.h>
+
+#define USS820_DCI_BUS2SC(bus) \
+ ((struct uss820dci_softc *)(((uint8_t *)(bus)) - \
+ USB_P2U(&(((struct uss820dci_softc *)0)->sc_bus))))
+
+#define USS820_DCI_PC2SC(pc) \
+ USS820_DCI_BUS2SC((pc)->tag_parent->info->bus)
+
+#if USB_DEBUG
+static int uss820dcidebug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uss820dci, CTLFLAG_RW, 0, "USB uss820dci");
+SYSCTL_INT(_hw_usb2_uss820dci, OID_AUTO, debug, CTLFLAG_RW,
+ &uss820dcidebug, 0, "uss820dci debug level");
+#endif
+
+#define USS820_DCI_INTR_ENDPT 1
+
+/* prototypes */
+
+struct usb2_bus_methods uss820dci_bus_methods;
+struct usb2_pipe_methods uss820dci_device_bulk_methods;
+struct usb2_pipe_methods uss820dci_device_ctrl_methods;
+struct usb2_pipe_methods uss820dci_device_intr_methods;
+struct usb2_pipe_methods uss820dci_device_isoc_fs_methods;
+struct usb2_pipe_methods uss820dci_root_ctrl_methods;
+struct usb2_pipe_methods uss820dci_root_intr_methods;
+
+static uss820dci_cmd_t uss820dci_setup_rx;
+static uss820dci_cmd_t uss820dci_data_rx;
+static uss820dci_cmd_t uss820dci_data_tx;
+static uss820dci_cmd_t uss820dci_data_tx_sync;
+static void uss820dci_device_done(struct usb2_xfer *, usb2_error_t);
+static void uss820dci_do_poll(struct usb2_bus *);
+static void uss820dci_root_ctrl_poll(struct uss820dci_softc *);
+static void uss820dci_standard_done(struct usb2_xfer *);
+static void uss820dci_intr_set(struct usb2_xfer *, uint8_t);
+static void uss820dci_update_shared_1(struct uss820dci_softc *, uint8_t,
+ uint8_t, uint8_t);
+
+static usb2_sw_transfer_func_t uss820dci_root_intr_done;
+static usb2_sw_transfer_func_t uss820dci_root_ctrl_done;
+
+/*
+ * Here is a list of what the USS820D chip can support. The main
+ * limitation is that the sum of the buffer sizes must be less than
+ * 1120 bytes.
+ */
+static const struct usb2_hw_ep_profile
+ uss820dci_ep_profile[] = {
+
+ [0] = {
+ .max_in_frame_size = 32,
+ .max_out_frame_size = 32,
+ .is_simplex = 0,
+ .support_control = 1,
+ },
+ [1] = {
+ .max_in_frame_size = 64,
+ .max_out_frame_size = 64,
+ .is_simplex = 0,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+ [2] = {
+ .max_in_frame_size = 8,
+ .max_out_frame_size = 8,
+ .is_simplex = 0,
+ .support_multi_buffer = 1,
+ .support_bulk = 1,
+ .support_interrupt = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+ [3] = {
+ .max_in_frame_size = 256,
+ .max_out_frame_size = 256,
+ .is_simplex = 0,
+ .support_multi_buffer = 1,
+ .support_isochronous = 1,
+ .support_in = 1,
+ .support_out = 1,
+ },
+};
+
+static void
+uss820dci_update_shared_1(struct uss820dci_softc *sc, uint8_t reg,
+ uint8_t keep_mask, uint8_t set_mask)
+{
+ uint8_t temp;
+
+ USS820_WRITE_1(sc, USS820_PEND, 1);
+ temp = USS820_READ_1(sc, reg);
+ temp &= (keep_mask);
+ temp |= (set_mask);
+ USS820_WRITE_1(sc, reg, temp);
+ USS820_WRITE_1(sc, USS820_PEND, 0);
+}
+
+static void
+uss820dci_get_hw_ep_profile(struct usb2_device *udev,
+ const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr)
+{
+ if (ep_addr == 0) {
+ *ppf = uss820dci_ep_profile + 0;
+ } else if (ep_addr < 5) {
+ *ppf = uss820dci_ep_profile + 1;
+ } else if (ep_addr < 7) {
+ *ppf = uss820dci_ep_profile + 2;
+ } else if (ep_addr == 7) {
+ *ppf = uss820dci_ep_profile + 3;
+ } else {
+ *ppf = NULL;
+ }
+}
+
+static void
+uss820dci_pull_up(struct uss820dci_softc *sc)
+{
+ uint8_t temp;
+
+ /* pullup D+, if possible */
+
+ if (!sc->sc_flags.d_pulled_up &&
+ sc->sc_flags.port_powered) {
+ sc->sc_flags.d_pulled_up = 1;
+
+ DPRINTF("\n");
+
+ temp = USS820_READ_1(sc, USS820_MCSR);
+ temp |= USS820_MCSR_DPEN;
+ USS820_WRITE_1(sc, USS820_MCSR, temp);
+ }
+}
+
+static void
+uss820dci_pull_down(struct uss820dci_softc *sc)
+{
+ uint8_t temp;
+
+ /* pulldown D+, if possible */
+
+ if (sc->sc_flags.d_pulled_up) {
+ sc->sc_flags.d_pulled_up = 0;
+
+ DPRINTF("\n");
+
+ temp = USS820_READ_1(sc, USS820_MCSR);
+ temp &= ~USS820_MCSR_DPEN;
+ USS820_WRITE_1(sc, USS820_MCSR, temp);
+ }
+}
+
+static void
+uss820dci_wakeup_peer(struct uss820dci_softc *sc)
+{
+ if (!(sc->sc_flags.status_suspend)) {
+ return;
+ }
+ DPRINTFN(0, "not supported\n");
+}
+
+static void
+uss820dci_set_address(struct uss820dci_softc *sc, uint8_t addr)
+{
+ DPRINTFN(5, "addr=%d\n", addr);
+
+ USS820_WRITE_1(sc, USS820_FADDR, addr);
+}
+
+static uint8_t
+uss820dci_setup_rx(struct uss820dci_td *td)
+{
+ struct uss820dci_softc *sc;
+ struct usb2_device_request req;
+ uint16_t count;
+ uint8_t rx_stat;
+ uint8_t temp;
+
+ /* select the correct endpoint */
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->ep_reg, td->ep_index);
+
+ /* read out FIFO status */
+ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_stat_reg);
+
+ /* get pointer to softc */
+ sc = USS820_DCI_PC2SC(td->pc);
+
+ DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
+
+ if (!(rx_stat & USS820_RXSTAT_RXSETUP)) {
+ /* abort any ongoing transfer */
+ if (!td->did_stall) {
+ DPRINTFN(5, "stalling\n");
+
+ /* set stall */
+
+ uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF,
+ (USS820_EPCON_TXSTL | USS820_EPCON_RXSTL));
+
+ td->did_stall = 1;
+ }
+ goto not_complete;
+ }
+ /* clear stall and all I/O */
+ uss820dci_update_shared_1(sc, USS820_EPCON,
+ 0xFF ^ (USS820_EPCON_TXSTL |
+ USS820_EPCON_RXSTL |
+ USS820_EPCON_RXIE |
+ USS820_EPCON_TXOE), 0);
+
+ /* clear end overwrite flag */
+ uss820dci_update_shared_1(sc, USS820_RXSTAT,
+ 0xFF ^ USS820_RXSTAT_EDOVW, 0);
+
+ /* get the packet byte count */
+ count = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_count_low_reg);
+ count |= (bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_count_high_reg) << 8);
+ count &= 0x3FF;
+
+ /* verify data length */
+ if (count != td->remainder) {
+ DPRINTFN(0, "Invalid SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ if (count != sizeof(req)) {
+ DPRINTFN(0, "Unsupported SETUP packet "
+ "length, %d bytes\n", count);
+ goto not_complete;
+ }
+ /* receive data */
+ bus_space_read_multi_1(td->io_tag, td->io_hdl,
+ td->rx_fifo_reg, (void *)&req, sizeof(req));
+
+ /* read out FIFO status */
+ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_stat_reg);
+
+ if (rx_stat & (USS820_RXSTAT_EDOVW |
+ USS820_RXSTAT_STOVW)) {
+ DPRINTF("new SETUP packet received\n");
+ return (1); /* not complete */
+ }
+ /* clear receive setup bit */
+ uss820dci_update_shared_1(sc, USS820_RXSTAT,
+ 0xFF ^ (USS820_RXSTAT_RXSETUP |
+ USS820_RXSTAT_EDOVW |
+ USS820_RXSTAT_STOVW), 0);
+
+ /* set RXFFRC bit */
+ temp = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_cntl_reg);
+ temp |= USS820_RXCON_RXFFRC;
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->rx_cntl_reg, temp);
+
+ /* copy data into real buffer */
+ usb2_copy_in(td->pc, 0, &req, sizeof(req));
+
+ td->offset = sizeof(req);
+ td->remainder = 0;
+
+ /* sneak peek the set address */
+ if ((req.bmRequestType == UT_WRITE_DEVICE) &&
+ (req.bRequest == UR_SET_ADDRESS)) {
+ sc->sc_dv_addr = req.wValue[0] & 0x7F;
+ } else {
+ sc->sc_dv_addr = 0xFF;
+ }
+ return (0); /* complete */
+
+not_complete:
+ /* clear end overwrite flag, if any */
+ if (rx_stat & USS820_RXSTAT_RXSETUP) {
+ uss820dci_update_shared_1(sc, USS820_RXSTAT,
+ 0xFF ^ (USS820_RXSTAT_EDOVW |
+ USS820_RXSTAT_STOVW |
+ USS820_RXSTAT_RXSETUP), 0);
+ }
+ return (1); /* not complete */
+
+}
+
+static uint8_t
+uss820dci_data_rx(struct uss820dci_td *td)
+{
+ struct usb2_page_search buf_res;
+ uint16_t count;
+ uint8_t rx_flag;
+ uint8_t rx_stat;
+ uint8_t rx_cntl;
+ uint8_t to;
+ uint8_t got_short;
+
+ to = 2; /* don't loop forever! */
+ got_short = 0;
+
+ /* select the correct endpoint */
+ bus_space_write_1(td->io_tag, td->io_hdl, td->ep_reg, td->ep_index);
+
+ /* check if any of the FIFO banks have data */
+repeat:
+ /* read out FIFO flag */
+ rx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_flag_reg);
+ /* read out FIFO status */
+ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_stat_reg);
+
+ DPRINTFN(5, "rx_stat=0x%02x rx_flag=0x%02x rem=%u\n",
+ rx_stat, rx_flag, td->remainder);
+
+ if (rx_stat & (USS820_RXSTAT_RXSETUP |
+ USS820_RXSTAT_RXSOVW |
+ USS820_RXSTAT_EDOVW)) {
+ if (td->remainder == 0) {
+ /*
+ * We are actually complete and have
+ * received the next SETUP
+ */
+ DPRINTFN(5, "faking complete\n");
+ return (0); /* complete */
+ }
+ /*
+ * USB Host Aborted the transfer.
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ /* check for errors */
+ if (rx_flag & (USS820_RXFLG_RXOVF |
+ USS820_RXFLG_RXURF)) {
+ DPRINTFN(5, "overflow or underflow\n");
+ /* should not happen */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ /* check status */
+ if (!(rx_flag & (USS820_RXFLG_RXFIF0 |
+ USS820_RXFLG_RXFIF1))) {
+
+ /* read out EPCON register */
+ /* enable RX input */
+ if (!td->did_stall) {
+ uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc),
+ USS820_EPCON, 0xFF, USS820_EPCON_RXIE);
+ td->did_stall = 1;
+ }
+ return (1); /* not complete */
+ }
+ /* get the packet byte count */
+ count = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_count_low_reg);
+
+ count |= (bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_count_high_reg) << 8);
+ count &= 0x3FF;
+
+ DPRINTFN(5, "count=0x%04x\n", count);
+
+ /* verify the packet byte count */
+ if (count != td->max_packet_size) {
+ if (count < td->max_packet_size) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ got_short = 1;
+ } else {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ }
+ /* verify the packet byte count */
+ if (count > td->remainder) {
+ /* invalid USB packet */
+ td->error = 1;
+ return (0); /* we are complete */
+ }
+ while (count > 0) {
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* receive data */
+ bus_space_read_multi_1(td->io_tag, td->io_hdl,
+ td->rx_fifo_reg, buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* set RXFFRC bit */
+ rx_cntl = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_cntl_reg);
+ rx_cntl |= USS820_RXCON_RXFFRC;
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->rx_cntl_reg, rx_cntl);
+
+ /* check if we are complete */
+ if ((td->remainder == 0) || got_short) {
+ if (td->short_pkt) {
+ /* we are complete */
+ return (0);
+ }
+ /* else need to receive a zero length packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+uss820dci_data_tx(struct uss820dci_td *td)
+{
+ struct usb2_page_search buf_res;
+ uint16_t count;
+ uint16_t count_copy;
+ uint8_t rx_stat;
+ uint8_t tx_flag;
+ uint8_t to;
+
+ /* select the correct endpoint */
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->ep_reg, td->ep_index);
+
+ to = 2; /* don't loop forever! */
+
+repeat:
+ /* read out TX FIFO flags */
+ tx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->tx_flag_reg);
+
+ /* read out RX FIFO status last */
+ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_stat_reg);
+
+ DPRINTFN(5, "rx_stat=0x%02x tx_flag=0x%02x rem=%u\n",
+ rx_stat, tx_flag, td->remainder);
+
+ if (rx_stat & (USS820_RXSTAT_RXSETUP |
+ USS820_RXSTAT_RXSOVW |
+ USS820_RXSTAT_EDOVW)) {
+ /*
+ * The current transfer was aborted
+ * by the USB Host
+ */
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (tx_flag & (USS820_TXFLG_TXOVF |
+ USS820_TXFLG_TXURF)) {
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (tx_flag & USS820_TXFLG_TXFIF0) {
+ if (tx_flag & USS820_TXFLG_TXFIF1) {
+ return (1); /* not complete */
+ }
+ }
+ if ((!td->support_multi_buffer) &&
+ (tx_flag & (USS820_TXFLG_TXFIF0 |
+ USS820_TXFLG_TXFIF1))) {
+ return (1); /* not complete */
+ }
+ count = td->max_packet_size;
+ if (td->remainder < count) {
+ /* we have a short packet */
+ td->short_pkt = 1;
+ count = td->remainder;
+ }
+ count_copy = count;
+ while (count > 0) {
+
+ usb2_get_page(td->pc, td->offset, &buf_res);
+
+ /* get correct length */
+ if (buf_res.length > count) {
+ buf_res.length = count;
+ }
+ /* transmit data */
+ bus_space_write_multi_1(td->io_tag, td->io_hdl,
+ td->tx_fifo_reg, buf_res.buffer, buf_res.length);
+
+ /* update counters */
+ count -= buf_res.length;
+ td->offset += buf_res.length;
+ td->remainder -= buf_res.length;
+ }
+
+ /* post-write high packet byte count first */
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->tx_count_high_reg, count_copy >> 8);
+
+ /* post-write low packet byte count last */
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->tx_count_low_reg, count_copy);
+
+ /*
+ * Enable TX output, which must happen after that we have written
+ * data into the FIFO. This is undocumented.
+ */
+ if (!td->did_stall) {
+ uss820dci_update_shared_1(USS820_DCI_PC2SC(td->pc),
+ USS820_EPCON, 0xFF, USS820_EPCON_TXOE);
+ td->did_stall = 1;
+ }
+ /* check remainder */
+ if (td->remainder == 0) {
+ if (td->short_pkt) {
+ return (0); /* complete */
+ }
+ /* else we need to transmit a short packet */
+ }
+ if (--to) {
+ goto repeat;
+ }
+ return (1); /* not complete */
+}
+
+static uint8_t
+uss820dci_data_tx_sync(struct uss820dci_td *td)
+{
+ struct uss820dci_softc *sc;
+ uint8_t rx_stat;
+ uint8_t tx_flag;
+
+ /* select the correct endpoint */
+ bus_space_write_1(td->io_tag, td->io_hdl,
+ td->ep_reg, td->ep_index);
+
+ /* read out TX FIFO flag */
+ tx_flag = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->tx_flag_reg);
+
+ /* read out RX FIFO status last */
+ rx_stat = bus_space_read_1(td->io_tag, td->io_hdl,
+ td->rx_stat_reg);
+
+ DPRINTFN(5, "rx_stat=0x%02x rem=%u\n", rx_stat, td->remainder);
+
+ if (rx_stat & (USS820_RXSTAT_RXSETUP |
+ USS820_RXSTAT_RXSOVW |
+ USS820_RXSTAT_EDOVW)) {
+ DPRINTFN(5, "faking complete\n");
+ /* Race condition */
+ return (0); /* complete */
+ }
+ DPRINTFN(5, "tx_flag=0x%02x rem=%u\n",
+ tx_flag, td->remainder);
+
+ if (tx_flag & (USS820_TXFLG_TXOVF |
+ USS820_TXFLG_TXURF)) {
+ td->error = 1;
+ return (0); /* complete */
+ }
+ if (tx_flag & (USS820_TXFLG_TXFIF0 |
+ USS820_TXFLG_TXFIF1)) {
+ return (1); /* not complete */
+ }
+ sc = USS820_DCI_PC2SC(td->pc);
+ if (sc->sc_dv_addr != 0xFF) {
+ /* write function address */
+ uss820dci_set_address(sc, sc->sc_dv_addr);
+ }
+ return (0); /* complete */
+}
+
+static uint8_t
+uss820dci_xfer_do_fifo(struct usb2_xfer *xfer)
+{
+ struct uss820dci_td *td;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+ while (1) {
+ if ((td->func) (td)) {
+ /* operation in progress */
+ break;
+ }
+ if (((void *)td) == xfer->td_transfer_last) {
+ goto done;
+ }
+ if (td->error) {
+ goto done;
+ } else if (td->remainder > 0) {
+ /*
+ * We had a short transfer. If there is no alternate
+ * next, stop processing !
+ */
+ if (!td->alt_next) {
+ goto done;
+ }
+ }
+ /*
+ * Fetch the next transfer descriptor.
+ */
+ td = td->obj_next;
+ xfer->td_transfer_cache = td;
+ }
+ return (1); /* not complete */
+
+done:
+ /* compute all actual lengths */
+
+ uss820dci_standard_done(xfer);
+
+ return (0); /* complete */
+}
+
+static void
+uss820dci_interrupt_poll(struct uss820dci_softc *sc)
+{
+ struct usb2_xfer *xfer;
+
+repeat:
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+ if (!uss820dci_xfer_do_fifo(xfer)) {
+ /* queue has been modified */
+ goto repeat;
+ }
+ }
+}
+
+static void
+uss820dci_wait_suspend(struct uss820dci_softc *sc, uint8_t on)
+{
+ uint8_t scr;
+ uint8_t scratch;
+
+ scr = USS820_READ_1(sc, USS820_SCR);
+ scratch = USS820_READ_1(sc, USS820_SCRATCH);
+
+ if (on) {
+ scr |= USS820_SCR_IE_SUSP;
+ scratch &= ~USS820_SCRATCH_IE_RESUME;
+ } else {
+ scr &= ~USS820_SCR_IE_SUSP;
+ scratch |= USS820_SCRATCH_IE_RESUME;
+ }
+
+ USS820_WRITE_1(sc, USS820_SCR, scr);
+ USS820_WRITE_1(sc, USS820_SCRATCH, scratch);
+}
+
+void
+uss820dci_interrupt(struct uss820dci_softc *sc)
+{
+ uint8_t ssr;
+ uint8_t event;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ ssr = USS820_READ_1(sc, USS820_SSR);
+
+ ssr &= (USS820_SSR_SUSPEND |
+ USS820_SSR_RESUME |
+ USS820_SSR_RESET);
+
+ /* acknowledge all interrupts */
+
+ uss820dci_update_shared_1(sc, USS820_SSR, 0, 0);
+
+ /* check for any bus state change interrupts */
+
+ if (ssr) {
+
+ event = 0;
+
+ if (ssr & USS820_SSR_RESET) {
+ sc->sc_flags.status_bus_reset = 1;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ /* disable resume interrupt */
+ uss820dci_wait_suspend(sc, 1);
+
+ event = 1;
+ }
+ /*
+ * If "RESUME" and "SUSPEND" is set at the same time
+ * we interpret that like "RESUME". Resume is set when
+ * there is at least 3 milliseconds of inactivity on
+ * the USB BUS.
+ */
+ if (ssr & USS820_SSR_RESUME) {
+ if (sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 1;
+ /* disable resume interrupt */
+ uss820dci_wait_suspend(sc, 1);
+ event = 1;
+ }
+ } else if (ssr & USS820_SSR_SUSPEND) {
+ if (!sc->sc_flags.status_suspend) {
+ sc->sc_flags.status_suspend = 1;
+ sc->sc_flags.change_suspend = 1;
+ /* enable resume interrupt */
+ uss820dci_wait_suspend(sc, 0);
+ event = 1;
+ }
+ }
+ if (event) {
+
+ DPRINTF("real bus interrupt 0x%02x\n", ssr);
+
+ /* complete root HUB interrupt endpoint */
+
+ usb2_sw_transfer(&sc->sc_root_intr,
+ &uss820dci_root_intr_done);
+ }
+ }
+ /* acknowledge all SBI interrupts */
+ uss820dci_update_shared_1(sc, USS820_SBI, 0, 0);
+
+ /* acknowledge all SBI1 interrupts */
+ uss820dci_update_shared_1(sc, USS820_SBI1, 0, 0);
+
+ /* poll all active transfers */
+ uss820dci_interrupt_poll(sc);
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+static void
+uss820dci_setup_standard_chain_sub(struct uss820_std_temp *temp)
+{
+ struct uss820dci_td *td;
+
+ /* get current Transfer Descriptor */
+ td = temp->td_next;
+ temp->td = td;
+
+ /* prepare for next TD */
+ temp->td_next = td->obj_next;
+
+ /* fill out the Transfer Descriptor */
+ td->func = temp->func;
+ td->pc = temp->pc;
+ td->offset = temp->offset;
+ td->remainder = temp->len;
+ td->error = 0;
+ td->did_stall = 0;
+ td->short_pkt = temp->short_pkt;
+ td->alt_next = temp->setup_alt_next;
+}
+
+static void
+uss820dci_setup_standard_chain(struct usb2_xfer *xfer)
+{
+ struct uss820_std_temp temp;
+ struct uss820dci_softc *sc;
+ struct uss820dci_td *td;
+ uint32_t x;
+ uint8_t ep_no;
+
+ DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
+ xfer->address, UE_GET_ADDR(xfer->endpoint),
+ xfer->sumlen, usb2_get_speed(xfer->xroot->udev));
+
+ temp.max_frame_size = xfer->max_frame_size;
+
+ td = xfer->td_start[0];
+ xfer->td_transfer_first = td;
+ xfer->td_transfer_cache = td;
+
+ /* setup temp */
+
+ temp.td = NULL;
+ temp.td_next = xfer->td_start[0];
+ temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+ temp.offset = 0;
+
+ sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+ ep_no = (xfer->endpoint & UE_ADDR);
+
+ /* check if we should prepend a setup message */
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+
+ temp.func = &uss820dci_setup_rx;
+ temp.len = xfer->frlengths[0];
+ temp.pc = xfer->frbuffers + 0;
+ temp.short_pkt = temp.len ? 1 : 0;
+
+ uss820dci_setup_standard_chain_sub(&temp);
+ }
+ x = 1;
+ } else {
+ x = 0;
+ }
+
+ if (x != xfer->nframes) {
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &uss820dci_data_tx;
+ } else {
+ temp.func = &uss820dci_data_rx;
+ }
+
+ /* setup "pc" pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ while (x != xfer->nframes) {
+
+ /* DATA0 / DATA1 message */
+
+ temp.len = xfer->frlengths[x];
+
+ x++;
+
+ if (x == xfer->nframes) {
+ temp.setup_alt_next = 0;
+ }
+ if (temp.len == 0) {
+
+ /* make sure that we send an USB packet */
+
+ temp.short_pkt = 0;
+
+ } else {
+
+ /* regular data transfer */
+
+ temp.short_pkt = (xfer->flags.force_short_xfer) ? 0 : 1;
+ }
+
+ uss820dci_setup_standard_chain_sub(&temp);
+
+ if (xfer->flags_int.isochronous_xfr) {
+ temp.offset += temp.len;
+ } else {
+ /* get next Page Cache pointer */
+ temp.pc = xfer->frbuffers + x;
+ }
+ }
+
+ /* always setup a valid "pc" pointer for status and sync */
+ temp.pc = xfer->frbuffers + 0;
+
+ /* check if we should append a status stage */
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+ uint8_t need_sync;
+
+ /*
+ * Send a DATA1 message and invert the current
+ * endpoint direction.
+ */
+ if (xfer->endpoint & UE_DIR_IN) {
+ temp.func = &uss820dci_data_rx;
+ need_sync = 0;
+ } else {
+ temp.func = &uss820dci_data_tx;
+ need_sync = 1;
+ }
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ uss820dci_setup_standard_chain_sub(&temp);
+ if (need_sync) {
+ /* we need a SYNC point after TX */
+ temp.func = &uss820dci_data_tx_sync;
+ temp.len = 0;
+ temp.short_pkt = 0;
+
+ uss820dci_setup_standard_chain_sub(&temp);
+ }
+ }
+ /* must have at least one frame! */
+ td = temp.td;
+ xfer->td_transfer_last = td;
+}
+
+static void
+uss820dci_timeout(void *arg)
+{
+ struct usb2_xfer *xfer = arg;
+
+ DPRINTF("xfer=%p\n", xfer);
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* transfer is transferred */
+ uss820dci_device_done(xfer, USB_ERR_TIMEOUT);
+}
+
+static void
+uss820dci_intr_set(struct usb2_xfer *xfer, uint8_t set)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+ uint8_t ep_no = (xfer->endpoint & UE_ADDR);
+ uint8_t ep_reg;
+ uint8_t temp;
+
+ DPRINTFN(15, "endpoint 0x%02x\n", xfer->endpoint);
+
+ if (ep_no > 3) {
+ ep_reg = USS820_SBIE1;
+ } else {
+ ep_reg = USS820_SBIE;
+ }
+
+ ep_no &= 3;
+ ep_no = 1 << (2 * ep_no);
+
+ if (xfer->flags_int.control_xfr) {
+ if (xfer->flags_int.control_hdr) {
+ ep_no <<= 1; /* RX interrupt only */
+ } else {
+ ep_no |= (ep_no << 1); /* RX and TX interrupt */
+ }
+ } else {
+ if (!(xfer->endpoint & UE_DIR_IN)) {
+ ep_no <<= 1;
+ }
+ }
+ temp = USS820_READ_1(sc, ep_reg);
+ if (set) {
+ temp |= ep_no;
+ } else {
+ temp &= ~ep_no;
+ }
+ USS820_WRITE_1(sc, ep_reg, temp);
+}
+
+static void
+uss820dci_start_standard_chain(struct usb2_xfer *xfer)
+{
+ DPRINTFN(9, "\n");
+
+ /* poll one time */
+ if (uss820dci_xfer_do_fifo(xfer)) {
+
+ /*
+ * Only enable the endpoint interrupt when we are
+ * actually waiting for data, hence we are dealing
+ * with level triggered interrupts !
+ */
+ uss820dci_intr_set(xfer, 1);
+
+ /* put transfer on interrupt queue */
+ usb2_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
+
+ /* start timeout, if any */
+ if (xfer->timeout != 0) {
+ usb2_transfer_timeout_ms(xfer,
+ &uss820dci_timeout, xfer->timeout);
+ }
+ }
+}
+
+static void
+uss820dci_root_intr_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTFN(9, "\n");
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_PRE_DATA) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ uss820dci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* setup buffer */
+ std->ptr = sc->sc_hub_idata;
+ std->len = sizeof(sc->sc_hub_idata);
+
+ /* set port bit */
+ sc->sc_hub_idata[0] = 0x02; /* we only have one port */
+
+done:
+ return;
+}
+
+static usb2_error_t
+uss820dci_standard_done_sub(struct usb2_xfer *xfer)
+{
+ struct uss820dci_td *td;
+ uint32_t len;
+ uint8_t error;
+
+ DPRINTFN(9, "\n");
+
+ td = xfer->td_transfer_cache;
+
+ do {
+ len = td->remainder;
+
+ if (xfer->aframes != xfer->nframes) {
+ /*
+ * Verify the length and subtract
+ * the remainder from "frlengths[]":
+ */
+ if (len > xfer->frlengths[xfer->aframes]) {
+ td->error = 1;
+ } else {
+ xfer->frlengths[xfer->aframes] -= len;
+ }
+ }
+ /* Check for transfer error */
+ if (td->error) {
+ /* the transfer is finished */
+ error = 1;
+ td = NULL;
+ break;
+ }
+ /* Check for short transfer */
+ if (len > 0) {
+ if (xfer->flags_int.short_frames_ok) {
+ /* follow alt next */
+ if (td->alt_next) {
+ td = td->obj_next;
+ } else {
+ td = NULL;
+ }
+ } else {
+ /* the transfer is finished */
+ td = NULL;
+ }
+ error = 0;
+ break;
+ }
+ td = td->obj_next;
+
+ /* this USB frame is complete */
+ error = 0;
+ break;
+
+ } while (0);
+
+ /* update transfer cache */
+
+ xfer->td_transfer_cache = td;
+
+ return (error ?
+ USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+}
+
+static void
+uss820dci_standard_done(struct usb2_xfer *xfer)
+{
+ usb2_error_t err = 0;
+
+ DPRINTFN(13, "xfer=%p pipe=%p transfer done\n",
+ xfer, xfer->pipe);
+
+ /* reset scanner */
+
+ xfer->td_transfer_cache = xfer->td_transfer_first;
+
+ if (xfer->flags_int.control_xfr) {
+
+ if (xfer->flags_int.control_hdr) {
+
+ err = uss820dci_standard_done_sub(xfer);
+ }
+ xfer->aframes = 1;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+ while (xfer->aframes != xfer->nframes) {
+
+ err = uss820dci_standard_done_sub(xfer);
+ xfer->aframes++;
+
+ if (xfer->td_transfer_cache == NULL) {
+ goto done;
+ }
+ }
+
+ if (xfer->flags_int.control_xfr &&
+ !xfer->flags_int.control_act) {
+
+ err = uss820dci_standard_done_sub(xfer);
+ }
+done:
+ uss820dci_device_done(xfer, err);
+}
+
+/*------------------------------------------------------------------------*
+ * uss820dci_device_done
+ *
+ * NOTE: this function can be called more than one time on the
+ * same USB transfer!
+ *------------------------------------------------------------------------*/
+static void
+uss820dci_device_done(struct usb2_xfer *xfer, usb2_error_t error)
+{
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ DPRINTFN(2, "xfer=%p, pipe=%p, error=%d\n",
+ xfer, xfer->pipe, error);
+
+ if (xfer->flags_int.usb2_mode == USB_MODE_DEVICE) {
+ uss820dci_intr_set(xfer, 0);
+ }
+ /* dequeue transfer and start next transfer */
+ usb2_transfer_done(xfer, error);
+}
+
+static void
+uss820dci_set_stall(struct usb2_device *udev, struct usb2_xfer *xfer,
+ struct usb2_pipe *pipe)
+{
+ struct uss820dci_softc *sc;
+ uint8_t ep_no;
+ uint8_t ep_type;
+ uint8_t ep_dir;
+ uint8_t temp;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(5, "pipe=%p\n", pipe);
+
+ if (xfer) {
+ /* cancel any ongoing transfers */
+ uss820dci_device_done(xfer, USB_ERR_STALLED);
+ }
+ /* set FORCESTALL */
+ sc = USS820_DCI_BUS2SC(udev->bus);
+ ep_no = (pipe->edesc->bEndpointAddress & UE_ADDR);
+ ep_dir = (pipe->edesc->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT));
+ ep_type = (pipe->edesc->bmAttributes & UE_XFERTYPE);
+
+ if (ep_type == UE_CONTROL) {
+ /* should not happen */
+ return;
+ }
+ USS820_WRITE_1(sc, USS820_EPINDEX, ep_no);
+
+ if (ep_dir == UE_DIR_IN) {
+ temp = USS820_EPCON_TXSTL;
+ } else {
+ temp = USS820_EPCON_RXSTL;
+ }
+ uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp);
+}
+
+static void
+uss820dci_clear_stall_sub(struct uss820dci_softc *sc,
+ uint8_t ep_no, uint8_t ep_type, uint8_t ep_dir)
+{
+ uint8_t temp;
+
+ if (ep_type == UE_CONTROL) {
+ /* clearing stall is not needed */
+ return;
+ }
+ /* select endpoint index */
+ USS820_WRITE_1(sc, USS820_EPINDEX, ep_no);
+
+ /* clear stall and disable I/O transfers */
+ if (ep_dir == UE_DIR_IN) {
+ temp = 0xFF ^ (USS820_EPCON_TXOE |
+ USS820_EPCON_TXSTL);
+ } else {
+ temp = 0xFF ^ (USS820_EPCON_RXIE |
+ USS820_EPCON_RXSTL);
+ }
+ uss820dci_update_shared_1(sc, USS820_EPCON, temp, 0);
+
+ if (ep_dir == UE_DIR_IN) {
+ /* reset data toggle */
+ USS820_WRITE_1(sc, USS820_TXSTAT,
+ USS820_TXSTAT_TXSOVW);
+
+ /* reset FIFO */
+ temp = USS820_READ_1(sc, USS820_TXCON);
+ temp |= USS820_TXCON_TXCLR;
+ USS820_WRITE_1(sc, USS820_TXCON, temp);
+ temp &= ~USS820_TXCON_TXCLR;
+ USS820_WRITE_1(sc, USS820_TXCON, temp);
+ } else {
+
+ /* reset data toggle */
+ uss820dci_update_shared_1(sc, USS820_RXSTAT,
+ 0, USS820_RXSTAT_RXSOVW);
+
+ /* reset FIFO */
+ temp = USS820_READ_1(sc, USS820_RXCON);
+ temp |= USS820_RXCON_RXCLR;
+ temp &= ~USS820_RXCON_RXFFRC;
+ USS820_WRITE_1(sc, USS820_RXCON, temp);
+ temp &= ~USS820_RXCON_RXCLR;
+ USS820_WRITE_1(sc, USS820_RXCON, temp);
+ }
+}
+
+static void
+uss820dci_clear_stall(struct usb2_device *udev, struct usb2_pipe *pipe)
+{
+ struct uss820dci_softc *sc;
+ struct usb2_endpoint_descriptor *ed;
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ DPRINTFN(5, "pipe=%p\n", pipe);
+
+ /* check mode */
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ /* get softc */
+ sc = USS820_DCI_BUS2SC(udev->bus);
+
+ /* get endpoint descriptor */
+ ed = pipe->edesc;
+
+ /* reset endpoint */
+ uss820dci_clear_stall_sub(sc,
+ (ed->bEndpointAddress & UE_ADDR),
+ (ed->bmAttributes & UE_XFERTYPE),
+ (ed->bEndpointAddress & (UE_DIR_IN | UE_DIR_OUT)));
+}
+
+usb2_error_t
+uss820dci_init(struct uss820dci_softc *sc)
+{
+ const struct usb2_hw_ep_profile *pf;
+ uint8_t n;
+ uint8_t temp;
+
+ DPRINTF("start\n");
+
+ /* set up the bus structure */
+ sc->sc_bus.usbrev = USB_REV_1_1;
+ sc->sc_bus.methods = &uss820dci_bus_methods;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* we always have VBUS */
+ sc->sc_flags.status_vbus = 1;
+
+ /* reset the chip */
+ USS820_WRITE_1(sc, USS820_SCR, USS820_SCR_SRESET);
+ DELAY(100);
+ USS820_WRITE_1(sc, USS820_SCR, 0);
+
+ /* wait for reset to complete */
+ for (n = 0;; n++) {
+
+ temp = USS820_READ_1(sc, USS820_MCSR);
+
+ if (temp & USS820_MCSR_INIT) {
+ break;
+ }
+ if (n == 100) {
+ USB_BUS_UNLOCK(&sc->sc_bus);
+ return (USB_ERR_INVAL);
+ }
+ /* wait a little for things to stabilise */
+ DELAY(100);
+ }
+
+ /* do a pulldown */
+ uss820dci_pull_down(sc);
+
+ /* wait 10ms for pulldown to stabilise */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, hz / 100);
+
+ /* check hardware revision */
+ temp = USS820_READ_1(sc, USS820_REV);
+
+ if (temp < 0x13) {
+ USB_BUS_UNLOCK(&sc->sc_bus);
+ return (USB_ERR_INVAL);
+ }
+ /* enable interrupts */
+ USS820_WRITE_1(sc, USS820_SCR,
+ USS820_SCR_T_IRQ |
+ USS820_SCR_IE_RESET |
+ /* USS820_SCR_RWUPE | */
+ USS820_SCR_IE_SUSP |
+ USS820_SCR_IRQPOL);
+
+ /* enable interrupts */
+ USS820_WRITE_1(sc, USS820_SCRATCH,
+ USS820_SCRATCH_IE_RESUME);
+
+ /* enable features */
+ USS820_WRITE_1(sc, USS820_MCSR,
+ USS820_MCSR_BDFEAT |
+ USS820_MCSR_FEAT);
+
+ sc->sc_flags.mcsr_feat = 1;
+
+ /* disable interrupts */
+ USS820_WRITE_1(sc, USS820_SBIE, 0);
+
+ /* disable interrupts */
+ USS820_WRITE_1(sc, USS820_SBIE1, 0);
+
+ /* disable all endpoints */
+ for (n = 0; n != USS820_EP_MAX; n++) {
+
+ /* select endpoint */
+ USS820_WRITE_1(sc, USS820_EPINDEX, n);
+
+ /* disable endpoint */
+ uss820dci_update_shared_1(sc, USS820_EPCON, 0, 0);
+ }
+
+ /*
+ * Initialise default values for some registers that cannot be
+ * changed during operation!
+ */
+ for (n = 0; n != USS820_EP_MAX; n++) {
+
+ uss820dci_get_hw_ep_profile(NULL, &pf, n);
+
+ /* the maximum frame sizes should be the same */
+ if (pf->max_in_frame_size != pf->max_out_frame_size) {
+ DPRINTF("Max frame size mismatch %u != %u\n",
+ pf->max_in_frame_size, pf->max_out_frame_size);
+ }
+ if (pf->support_isochronous) {
+ if (pf->max_in_frame_size <= 64) {
+ temp = (USS820_TXCON_FFSZ_16_64 |
+ USS820_TXCON_TXISO |
+ USS820_TXCON_ATM);
+ } else if (pf->max_in_frame_size <= 256) {
+ temp = (USS820_TXCON_FFSZ_64_256 |
+ USS820_TXCON_TXISO |
+ USS820_TXCON_ATM);
+ } else if (pf->max_in_frame_size <= 512) {
+ temp = (USS820_TXCON_FFSZ_8_512 |
+ USS820_TXCON_TXISO |
+ USS820_TXCON_ATM);
+ } else { /* 1024 bytes */
+ temp = (USS820_TXCON_FFSZ_32_1024 |
+ USS820_TXCON_TXISO |
+ USS820_TXCON_ATM);
+ }
+ } else {
+ if ((pf->max_in_frame_size <= 8) &&
+ (sc->sc_flags.mcsr_feat)) {
+ temp = (USS820_TXCON_FFSZ_8_512 |
+ USS820_TXCON_ATM);
+ } else if (pf->max_in_frame_size <= 16) {
+ temp = (USS820_TXCON_FFSZ_16_64 |
+ USS820_TXCON_ATM);
+ } else if ((pf->max_in_frame_size <= 32) &&
+ (sc->sc_flags.mcsr_feat)) {
+ temp = (USS820_TXCON_FFSZ_32_1024 |
+ USS820_TXCON_ATM);
+ } else { /* 64 bytes */
+ temp = (USS820_TXCON_FFSZ_64_256 |
+ USS820_TXCON_ATM);
+ }
+ }
+
+ /* need to configure the chip early */
+
+ USS820_WRITE_1(sc, USS820_EPINDEX, n);
+ USS820_WRITE_1(sc, USS820_TXCON, temp);
+ USS820_WRITE_1(sc, USS820_RXCON, temp);
+
+ if (pf->support_control) {
+ temp = USS820_EPCON_CTLEP |
+ USS820_EPCON_RXSPM |
+ USS820_EPCON_RXIE |
+ USS820_EPCON_RXEPEN |
+ USS820_EPCON_TXOE |
+ USS820_EPCON_TXEPEN;
+ } else {
+ temp = USS820_EPCON_RXEPEN | USS820_EPCON_TXEPEN;
+ }
+
+ uss820dci_update_shared_1(sc, USS820_EPCON, 0xFF, temp);
+ }
+
+ USB_BUS_UNLOCK(&sc->sc_bus);
+
+ /* catch any lost interrupts */
+
+ uss820dci_do_poll(&sc->sc_bus);
+
+ return (0); /* success */
+}
+
+void
+uss820dci_uninit(struct uss820dci_softc *sc)
+{
+ uint8_t temp;
+
+ USB_BUS_LOCK(&sc->sc_bus);
+
+ /* disable all interrupts */
+ temp = USS820_READ_1(sc, USS820_SCR);
+ temp &= ~USS820_SCR_T_IRQ;
+ USS820_WRITE_1(sc, USS820_SCR, temp);
+
+ sc->sc_flags.port_powered = 0;
+ sc->sc_flags.status_vbus = 0;
+ sc->sc_flags.status_bus_reset = 0;
+ sc->sc_flags.status_suspend = 0;
+ sc->sc_flags.change_suspend = 0;
+ sc->sc_flags.change_connect = 1;
+
+ uss820dci_pull_down(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+void
+uss820dci_suspend(struct uss820dci_softc *sc)
+{
+ return;
+}
+
+void
+uss820dci_resume(struct uss820dci_softc *sc)
+{
+ return;
+}
+
+static void
+uss820dci_do_poll(struct usb2_bus *bus)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(bus);
+
+ USB_BUS_LOCK(&sc->sc_bus);
+ uss820dci_interrupt_poll(sc);
+ uss820dci_root_ctrl_poll(sc);
+ USB_BUS_UNLOCK(&sc->sc_bus);
+}
+
+/*------------------------------------------------------------------------*
+ * at91dci bulk support
+ *------------------------------------------------------------------------*/
+static void
+uss820dci_device_bulk_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_bulk_close(struct usb2_xfer *xfer)
+{
+ uss820dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uss820dci_device_bulk_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_bulk_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ uss820dci_setup_standard_chain(xfer);
+ uss820dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods uss820dci_device_bulk_methods =
+{
+ .open = uss820dci_device_bulk_open,
+ .close = uss820dci_device_bulk_close,
+ .enter = uss820dci_device_bulk_enter,
+ .start = uss820dci_device_bulk_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci control support
+ *------------------------------------------------------------------------*/
+static void
+uss820dci_device_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_ctrl_close(struct usb2_xfer *xfer)
+{
+ uss820dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uss820dci_device_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_ctrl_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ uss820dci_setup_standard_chain(xfer);
+ uss820dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods uss820dci_device_ctrl_methods =
+{
+ .open = uss820dci_device_ctrl_open,
+ .close = uss820dci_device_ctrl_close,
+ .enter = uss820dci_device_ctrl_enter,
+ .start = uss820dci_device_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci interrupt support
+ *------------------------------------------------------------------------*/
+static void
+uss820dci_device_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_intr_close(struct usb2_xfer *xfer)
+{
+ uss820dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uss820dci_device_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_intr_start(struct usb2_xfer *xfer)
+{
+ /* setup TDs */
+ uss820dci_setup_standard_chain(xfer);
+ uss820dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods uss820dci_device_intr_methods =
+{
+ .open = uss820dci_device_intr_open,
+ .close = uss820dci_device_intr_close,
+ .enter = uss820dci_device_intr_enter,
+ .start = uss820dci_device_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci full speed isochronous support
+ *------------------------------------------------------------------------*/
+static void
+uss820dci_device_isoc_fs_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_device_isoc_fs_close(struct usb2_xfer *xfer)
+{
+ uss820dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uss820dci_device_isoc_fs_enter(struct usb2_xfer *xfer)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+ uint32_t temp;
+ uint32_t nframes;
+
+ DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
+ xfer, xfer->pipe->isoc_next, xfer->nframes);
+
+ /* get the current frame index - we don't need the high bits */
+
+ nframes = USS820_READ_1(sc, USS820_SOFL);
+
+ /*
+ * check if the frame index is within the window where the
+ * frames will be inserted
+ */
+ temp = (nframes - xfer->pipe->isoc_next) & USS820_SOFL_MASK;
+
+ if ((xfer->pipe->is_synced == 0) ||
+ (temp < xfer->nframes)) {
+ /*
+ * If there is data underflow or the pipe queue is
+ * empty we schedule the transfer a few frames ahead
+ * of the current frame position. Else two isochronous
+ * transfers might overlap.
+ */
+ xfer->pipe->isoc_next = (nframes + 3) & USS820_SOFL_MASK;
+ xfer->pipe->is_synced = 1;
+ DPRINTFN(3, "start next=%d\n", xfer->pipe->isoc_next);
+ }
+ /*
+ * compute how many milliseconds the insertion is ahead of the
+ * current frame position:
+ */
+ temp = (xfer->pipe->isoc_next - nframes) & USS820_SOFL_MASK;
+
+ /*
+ * pre-compute when the isochronous transfer will be finished:
+ */
+ xfer->isoc_time_complete =
+ usb2_isoc_time_expand(&sc->sc_bus, nframes) + temp +
+ xfer->nframes;
+
+ /* compute frame number for next insertion */
+ xfer->pipe->isoc_next += xfer->nframes;
+
+ /* setup TDs */
+ uss820dci_setup_standard_chain(xfer);
+}
+
+static void
+uss820dci_device_isoc_fs_start(struct usb2_xfer *xfer)
+{
+ /* start TD chain */
+ uss820dci_start_standard_chain(xfer);
+}
+
+struct usb2_pipe_methods uss820dci_device_isoc_fs_methods =
+{
+ .open = uss820dci_device_isoc_fs_open,
+ .close = uss820dci_device_isoc_fs_close,
+ .enter = uss820dci_device_isoc_fs_enter,
+ .start = uss820dci_device_isoc_fs_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci root control support
+ *------------------------------------------------------------------------*
+ * simulate a hardware HUB by handling
+ * all the necessary requests
+ *------------------------------------------------------------------------*/
+
+static void
+uss820dci_root_ctrl_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_root_ctrl_close(struct usb2_xfer *xfer)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_ctrl.xfer == xfer) {
+ sc->sc_root_ctrl.xfer = NULL;
+ }
+ uss820dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+/*
+ * USB descriptors for the virtual Root HUB:
+ */
+
+static const struct usb2_device_descriptor uss820dci_devd = {
+ .bLength = sizeof(struct usb2_device_descriptor),
+ .bDescriptorType = UDESC_DEVICE,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_HSHUBSTT,
+ .bMaxPacketSize = 64,
+ .bcdDevice = {0x00, 0x01},
+ .iManufacturer = 1,
+ .iProduct = 2,
+ .bNumConfigurations = 1,
+};
+
+static const struct usb2_device_qualifier uss820dci_odevd = {
+ .bLength = sizeof(struct usb2_device_qualifier),
+ .bDescriptorType = UDESC_DEVICE_QUALIFIER,
+ .bcdUSB = {0x00, 0x02},
+ .bDeviceClass = UDCLASS_HUB,
+ .bDeviceSubClass = UDSUBCLASS_HUB,
+ .bDeviceProtocol = UDPROTO_FSHUB,
+ .bMaxPacketSize0 = 0,
+ .bNumConfigurations = 0,
+};
+
+static const struct uss820dci_config_desc uss820dci_confd = {
+ .confd = {
+ .bLength = sizeof(struct usb2_config_descriptor),
+ .bDescriptorType = UDESC_CONFIG,
+ .wTotalLength[0] = sizeof(uss820dci_confd),
+ .bNumInterface = 1,
+ .bConfigurationValue = 1,
+ .iConfiguration = 0,
+ .bmAttributes = UC_SELF_POWERED,
+ .bMaxPower = 0,
+ },
+ .ifcd = {
+ .bLength = sizeof(struct usb2_interface_descriptor),
+ .bDescriptorType = UDESC_INTERFACE,
+ .bNumEndpoints = 1,
+ .bInterfaceClass = UICLASS_HUB,
+ .bInterfaceSubClass = UISUBCLASS_HUB,
+ .bInterfaceProtocol = UIPROTO_HSHUBSTT,
+ },
+
+ .endpd = {
+ .bLength = sizeof(struct usb2_endpoint_descriptor),
+ .bDescriptorType = UDESC_ENDPOINT,
+ .bEndpointAddress = (UE_DIR_IN | USS820_DCI_INTR_ENDPT),
+ .bmAttributes = UE_INTERRUPT,
+ .wMaxPacketSize[0] = 8,
+ .bInterval = 255,
+ },
+};
+
+static const struct usb2_hub_descriptor_min uss820dci_hubd = {
+ .bDescLength = sizeof(uss820dci_hubd),
+ .bDescriptorType = UDESC_HUB,
+ .bNbrPorts = 1,
+ .wHubCharacteristics[0] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) & 0xFF,
+ .wHubCharacteristics[1] =
+ (UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL) >> 8,
+ .bPwrOn2PwrGood = 50,
+ .bHubContrCurrent = 0,
+ .DeviceRemovable = {0}, /* port is removable */
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_VENDOR \
+ 'A', 0, 'G', 0, 'E', 0, 'R', 0, 'E', 0
+
+#define STRING_PRODUCT \
+ 'D', 0, 'C', 0, 'I', 0, ' ', 0, 'R', 0, \
+ 'o', 0, 'o', 0, 't', 0, ' ', 0, 'H', 0, \
+ 'U', 0, 'B', 0,
+
+USB_MAKE_STRING_DESC(STRING_LANG, uss820dci_langtab);
+USB_MAKE_STRING_DESC(STRING_VENDOR, uss820dci_vendor);
+USB_MAKE_STRING_DESC(STRING_PRODUCT, uss820dci_product);
+
+static void
+uss820dci_root_ctrl_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_root_ctrl_start(struct usb2_xfer *xfer)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_ctrl.xfer = xfer;
+
+ usb2_bus_roothub_exec(xfer->xroot->bus);
+}
+
+static void
+uss820dci_root_ctrl_task(struct usb2_bus *bus)
+{
+ uss820dci_root_ctrl_poll(USS820_DCI_BUS2SC(bus));
+}
+
+static void
+uss820dci_root_ctrl_done(struct usb2_xfer *xfer,
+ struct usb2_sw_transfer *std)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+ uint16_t value;
+ uint16_t index;
+ uint8_t use_polling;
+
+ USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
+
+ if (std->state != USB_SW_TR_SETUP) {
+ if (std->state == USB_SW_TR_PRE_CALLBACK) {
+ /* transfer transferred */
+ uss820dci_device_done(xfer, std->err);
+ }
+ goto done;
+ }
+ /* buffer reset */
+ std->ptr = USB_ADD_BYTES(&sc->sc_hub_temp, 0);
+ std->len = 0;
+
+ value = UGETW(std->req.wValue);
+ index = UGETW(std->req.wIndex);
+
+ use_polling = mtx_owned(xfer->xroot->xfer_mtx) ? 1 : 0;
+
+ /* demultiplex the control request */
+
+ switch (std->req.bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (std->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 (std->req.bRequest) {
+ case UR_SET_ADDRESS:
+ goto tr_handle_set_address;
+ case UR_SET_CONFIG:
+ goto tr_handle_set_config;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_DESCRIPTOR:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_clear_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_clear_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SET_FEATURE:
+ switch (UGETW(std->req.wValue)) {
+ case UF_ENDPOINT_HALT:
+ goto tr_handle_set_halt;
+ case UF_DEVICE_REMOTE_WAKEUP:
+ goto tr_handle_set_wakeup;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UR_SYNCH_FRAME:
+ goto tr_valid; /* nop */
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_ENDPOINT:
+ switch (std->req.bRequest) {
+ case UR_GET_STATUS:
+ goto tr_handle_get_ep_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_SET_INTERFACE:
+ goto tr_handle_set_interface;
+ case UR_CLEAR_FEATURE:
+ goto tr_valid; /* nop */
+ case UR_SET_FEATURE:
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_INTERFACE:
+ switch (std->req.bRequest) {
+ case UR_GET_INTERFACE:
+ goto tr_handle_get_interface;
+ case UR_GET_STATUS:
+ goto tr_handle_get_iface_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_INTERFACE:
+ case UT_WRITE_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_READ_CLASS_INTERFACE:
+ case UT_READ_VENDOR_INTERFACE:
+ /* XXX forward */
+ break;
+
+ case UT_WRITE_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_valid;
+ case UR_SET_DESCRIPTOR:
+ case UR_SET_FEATURE:
+ break;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_WRITE_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_CLEAR_FEATURE:
+ goto tr_handle_clear_port_feature;
+ case UR_SET_FEATURE:
+ goto tr_handle_set_port_feature;
+ case UR_CLEAR_TT_BUFFER:
+ case UR_RESET_TT:
+ case UR_STOP_TT:
+ goto tr_valid;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_OTHER:
+ switch (std->req.bRequest) {
+ case UR_GET_TT_STATE:
+ goto tr_handle_get_tt_state;
+ case UR_GET_STATUS:
+ goto tr_handle_get_port_status;
+ default:
+ goto tr_stalled;
+ }
+ break;
+
+ case UT_READ_CLASS_DEVICE:
+ switch (std->req.bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ case UR_GET_STATUS:
+ goto tr_handle_get_class_status;
+
+ default:
+ goto tr_stalled;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_valid;
+
+tr_handle_get_descriptor:
+ switch (value >> 8) {
+ case UDESC_DEVICE:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(uss820dci_devd);
+ std->ptr = USB_ADD_BYTES(&uss820dci_devd, 0);
+ goto tr_valid;
+ case UDESC_CONFIG:
+ if (value & 0xff) {
+ goto tr_stalled;
+ }
+ std->len = sizeof(uss820dci_confd);
+ std->ptr = USB_ADD_BYTES(&uss820dci_confd, 0);
+ goto tr_valid;
+ case UDESC_STRING:
+ switch (value & 0xff) {
+ case 0: /* Language table */
+ std->len = sizeof(uss820dci_langtab);
+ std->ptr = USB_ADD_BYTES(&uss820dci_langtab, 0);
+ goto tr_valid;
+
+ case 1: /* Vendor */
+ std->len = sizeof(uss820dci_vendor);
+ std->ptr = USB_ADD_BYTES(&uss820dci_vendor, 0);
+ goto tr_valid;
+
+ case 2: /* Product */
+ std->len = sizeof(uss820dci_product);
+ std->ptr = USB_ADD_BYTES(&uss820dci_product, 0);
+ goto tr_valid;
+ default:
+ break;
+ }
+ break;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_config:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = sc->sc_conf;
+ goto tr_valid;
+
+tr_handle_get_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, UDS_SELF_POWERED);
+ goto tr_valid;
+
+tr_handle_set_address:
+ if (value & 0xFF00) {
+ goto tr_stalled;
+ }
+ sc->sc_rt_addr = value;
+ goto tr_valid;
+
+tr_handle_set_config:
+ if (value >= 2) {
+ goto tr_stalled;
+ }
+ sc->sc_conf = value;
+ goto tr_valid;
+
+tr_handle_get_interface:
+ std->len = 1;
+ sc->sc_hub_temp.wValue[0] = 0;
+ goto tr_valid;
+
+tr_handle_get_tt_state:
+tr_handle_get_class_status:
+tr_handle_get_iface_status:
+tr_handle_get_ep_status:
+ std->len = 2;
+ USETW(sc->sc_hub_temp.wValue, 0);
+ goto tr_valid;
+
+tr_handle_set_halt:
+tr_handle_set_interface:
+tr_handle_set_wakeup:
+tr_handle_clear_wakeup:
+tr_handle_clear_halt:
+ goto tr_valid;
+
+tr_handle_clear_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(9, "UR_CLEAR_PORT_FEATURE on port %d\n", index);
+
+ switch (value) {
+ case UHF_PORT_SUSPEND:
+ uss820dci_wakeup_peer(sc);
+ break;
+
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 0;
+ break;
+
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ case UHF_C_PORT_ENABLE:
+ case UHF_C_PORT_OVER_CURRENT:
+ case UHF_C_PORT_RESET:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 0;
+ uss820dci_pull_down(sc);
+ break;
+ case UHF_C_PORT_CONNECTION:
+ sc->sc_flags.change_connect = 0;
+ break;
+ case UHF_C_PORT_SUSPEND:
+ sc->sc_flags.change_suspend = 0;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_set_port_feature:
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ DPRINTFN(9, "UR_SET_PORT_FEATURE\n");
+
+ switch (value) {
+ case UHF_PORT_ENABLE:
+ sc->sc_flags.port_enabled = 1;
+ break;
+ case UHF_PORT_SUSPEND:
+ case UHF_PORT_RESET:
+ case UHF_PORT_TEST:
+ case UHF_PORT_INDICATOR:
+ /* nops */
+ break;
+ case UHF_PORT_POWER:
+ sc->sc_flags.port_powered = 1;
+ break;
+ default:
+ std->err = USB_ERR_IOERROR;
+ goto done;
+ }
+ goto tr_valid;
+
+tr_handle_get_port_status:
+
+ DPRINTFN(9, "UR_GET_PORT_STATUS\n");
+
+ if (index != 1) {
+ goto tr_stalled;
+ }
+ if (sc->sc_flags.status_vbus) {
+ uss820dci_pull_up(sc);
+ } else {
+ uss820dci_pull_down(sc);
+ }
+
+ /* Select FULL-speed and Device Side Mode */
+
+ value = UPS_PORT_MODE_DEVICE;
+
+ if (sc->sc_flags.port_powered) {
+ value |= UPS_PORT_POWER;
+ }
+ if (sc->sc_flags.port_enabled) {
+ value |= UPS_PORT_ENABLED;
+ }
+ if (sc->sc_flags.status_vbus &&
+ sc->sc_flags.status_bus_reset) {
+ value |= UPS_CURRENT_CONNECT_STATUS;
+ }
+ if (sc->sc_flags.status_suspend) {
+ value |= UPS_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortStatus, value);
+
+ value = 0;
+
+ if (sc->sc_flags.change_connect) {
+ value |= UPS_C_CONNECT_STATUS;
+ }
+ if (sc->sc_flags.change_suspend) {
+ value |= UPS_C_SUSPEND;
+ }
+ USETW(sc->sc_hub_temp.ps.wPortChange, value);
+ std->len = sizeof(sc->sc_hub_temp.ps);
+ goto tr_valid;
+
+tr_handle_get_class_descriptor:
+ if (value & 0xFF) {
+ goto tr_stalled;
+ }
+ std->ptr = USB_ADD_BYTES(&uss820dci_hubd, 0);
+ std->len = sizeof(uss820dci_hubd);
+ goto tr_valid;
+
+tr_stalled:
+ std->err = USB_ERR_STALLED;
+tr_valid:
+done:
+ return;
+}
+
+static void
+uss820dci_root_ctrl_poll(struct uss820dci_softc *sc)
+{
+ usb2_sw_transfer(&sc->sc_root_ctrl,
+ &uss820dci_root_ctrl_done);
+}
+
+struct usb2_pipe_methods uss820dci_root_ctrl_methods =
+{
+ .open = uss820dci_root_ctrl_open,
+ .close = uss820dci_root_ctrl_close,
+ .enter = uss820dci_root_ctrl_enter,
+ .start = uss820dci_root_ctrl_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 0,
+};
+
+/*------------------------------------------------------------------------*
+ * at91dci root interrupt support
+ *------------------------------------------------------------------------*/
+static void
+uss820dci_root_intr_open(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_root_intr_close(struct usb2_xfer *xfer)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+
+ if (sc->sc_root_intr.xfer == xfer) {
+ sc->sc_root_intr.xfer = NULL;
+ }
+ uss820dci_device_done(xfer, USB_ERR_CANCELLED);
+}
+
+static void
+uss820dci_root_intr_enter(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_root_intr_start(struct usb2_xfer *xfer)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(xfer->xroot->bus);
+
+ sc->sc_root_intr.xfer = xfer;
+}
+
+struct usb2_pipe_methods uss820dci_root_intr_methods =
+{
+ .open = uss820dci_root_intr_open,
+ .close = uss820dci_root_intr_close,
+ .enter = uss820dci_root_intr_enter,
+ .start = uss820dci_root_intr_start,
+ .enter_is_cancelable = 1,
+ .start_is_cancelable = 1,
+};
+
+static void
+uss820dci_xfer_setup(struct usb2_setup_params *parm)
+{
+ const struct usb2_hw_ep_profile *pf;
+ struct uss820dci_softc *sc;
+ struct usb2_xfer *xfer;
+ void *last_obj;
+ uint32_t ntd;
+ uint32_t n;
+ uint8_t ep_no;
+
+ sc = USS820_DCI_BUS2SC(parm->udev->bus);
+ xfer = parm->curr_xfer;
+
+ /*
+ * NOTE: This driver does not use any of the parameters that
+ * are computed from the following values. Just set some
+ * reasonable dummies:
+ */
+ parm->hc_max_packet_size = 0x500;
+ parm->hc_max_packet_count = 1;
+ parm->hc_max_frame_size = 0x500;
+
+ usb2_transfer_setup_sub(parm);
+
+ /*
+ * compute maximum number of TDs
+ */
+ if (parm->methods == &uss820dci_device_ctrl_methods) {
+
+ ntd = xfer->nframes + 1 /* STATUS */ + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &uss820dci_device_bulk_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &uss820dci_device_intr_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else if (parm->methods == &uss820dci_device_isoc_fs_methods) {
+
+ ntd = xfer->nframes + 1 /* SYNC */ ;
+
+ } else {
+
+ ntd = 0;
+ }
+
+ /*
+ * check if "usb2_transfer_setup_sub" set an error
+ */
+ if (parm->err) {
+ return;
+ }
+ /*
+ * allocate transfer descriptors
+ */
+ last_obj = NULL;
+
+ /*
+ * get profile stuff
+ */
+ if (ntd) {
+
+ ep_no = xfer->endpoint & UE_ADDR;
+ uss820dci_get_hw_ep_profile(parm->udev, &pf, ep_no);
+
+ if (pf == NULL) {
+ /* should not happen */
+ parm->err = USB_ERR_INVAL;
+ return;
+ }
+ } else {
+ ep_no = 0;
+ pf = NULL;
+ }
+
+ /* align data */
+ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
+
+ for (n = 0; n != ntd; n++) {
+
+ struct uss820dci_td *td;
+
+ if (parm->buf) {
+
+ td = USB_ADD_BYTES(parm->buf, parm->size[0]);
+
+ /* init TD */
+ td->io_tag = sc->sc_io_tag;
+ td->io_hdl = sc->sc_io_hdl;
+ td->max_packet_size = xfer->max_packet_size;
+ td->rx_stat_reg = USS820_GET_REG(sc, USS820_RXSTAT);
+ td->tx_stat_reg = USS820_GET_REG(sc, USS820_TXSTAT);
+ td->rx_flag_reg = USS820_GET_REG(sc, USS820_RXFLG);
+ td->tx_flag_reg = USS820_GET_REG(sc, USS820_TXFLG);
+ td->rx_fifo_reg = USS820_GET_REG(sc, USS820_RXDAT);
+ td->tx_fifo_reg = USS820_GET_REG(sc, USS820_TXDAT);
+ td->rx_count_low_reg = USS820_GET_REG(sc, USS820_RXCNTL);
+ td->rx_count_high_reg = USS820_GET_REG(sc, USS820_RXCNTH);
+ td->tx_count_low_reg = USS820_GET_REG(sc, USS820_TXCNTL);
+ td->tx_count_high_reg = USS820_GET_REG(sc, USS820_TXCNTH);
+ td->rx_cntl_reg = USS820_GET_REG(sc, USS820_RXCON);
+ td->tx_cntl_reg = USS820_GET_REG(sc, USS820_TXCON);
+ td->pend_reg = USS820_GET_REG(sc, USS820_PEND);
+ td->ep_reg = USS820_GET_REG(sc, USS820_EPINDEX);
+ td->ep_index = ep_no;
+ if (pf->support_multi_buffer &&
+ (parm->methods != &uss820dci_device_ctrl_methods)) {
+ td->support_multi_buffer = 1;
+ }
+ td->obj_next = last_obj;
+
+ last_obj = td;
+ }
+ parm->size[0] += sizeof(*td);
+ }
+
+ xfer->td_start[0] = last_obj;
+}
+
+static void
+uss820dci_xfer_unsetup(struct usb2_xfer *xfer)
+{
+ return;
+}
+
+static void
+uss820dci_pipe_init(struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc,
+ struct usb2_pipe *pipe)
+{
+ struct uss820dci_softc *sc = USS820_DCI_BUS2SC(udev->bus);
+
+ DPRINTFN(2, "pipe=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
+ pipe, udev->address,
+ edesc->bEndpointAddress, udev->flags.usb2_mode,
+ sc->sc_rt_addr);
+
+ if (udev->device_index == sc->sc_rt_addr) {
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bEndpointAddress) {
+ case USB_CONTROL_ENDPOINT:
+ pipe->methods = &uss820dci_root_ctrl_methods;
+ break;
+ case UE_DIR_IN | USS820_DCI_INTR_ENDPT:
+ pipe->methods = &uss820dci_root_intr_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ } else {
+
+ if (udev->flags.usb2_mode != USB_MODE_DEVICE) {
+ /* not supported */
+ return;
+ }
+ if (udev->speed != USB_SPEED_FULL) {
+ /* not supported */
+ return;
+ }
+ switch (edesc->bmAttributes & UE_XFERTYPE) {
+ case UE_CONTROL:
+ pipe->methods = &uss820dci_device_ctrl_methods;
+ break;
+ case UE_INTERRUPT:
+ pipe->methods = &uss820dci_device_intr_methods;
+ break;
+ case UE_ISOCHRONOUS:
+ pipe->methods = &uss820dci_device_isoc_fs_methods;
+ break;
+ case UE_BULK:
+ pipe->methods = &uss820dci_device_bulk_methods;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ }
+}
+
+struct usb2_bus_methods uss820dci_bus_methods =
+{
+ .pipe_init = &uss820dci_pipe_init,
+ .xfer_setup = &uss820dci_xfer_setup,
+ .xfer_unsetup = &uss820dci_xfer_unsetup,
+ .do_poll = &uss820dci_do_poll,
+ .get_hw_ep_profile = &uss820dci_get_hw_ep_profile,
+ .set_stall = &uss820dci_set_stall,
+ .clear_stall = &uss820dci_clear_stall,
+ .roothub_exec = &uss820dci_root_ctrl_task,
+};
diff --git a/sys/dev/usb/controller/uss820dci.h b/sys/dev/usb/controller/uss820dci.h
new file mode 100644
index 0000000..f99e2d5
--- /dev/null
+++ b/sys/dev/usb/controller/uss820dci.h
@@ -0,0 +1,377 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2007 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _USS820_DCI_H_
+#define _USS820_DCI_H_
+
+#define USS820_MAX_DEVICES (USB_MIN_DEVICES + 1)
+
+#define USS820_EP_MAX 8 /* maximum number of endpoints */
+
+#define USS820_TXDAT 0x00 /* Transmit FIFO data */
+
+#define USS820_TXCNTL 0x01 /* Transmit FIFO byte count low */
+#define USS820_TXCNTL_MASK 0xFF
+
+#define USS820_TXCNTH 0x02 /* Transmit FIFO byte count high */
+#define USS820_TXCNTH_MASK 0x03
+#define USS820_TXCNTH_UNUSED 0xFC
+
+#define USS820_TXCON 0x03 /* USB transmit FIFO control */
+#define USS820_TXCON_REVRP 0x01
+#define USS820_TXCON_ADVRM 0x02
+#define USS820_TXCON_ATM 0x04 /* Automatic Transmit Management */
+#define USS820_TXCON_TXISO 0x08 /* Transmit Isochronous Data */
+#define USS820_TXCON_UNUSED 0x10
+#define USS820_TXCON_FFSZ_16_64 0x00
+#define USS820_TXCON_FFSZ_64_256 0x20
+#define USS820_TXCON_FFSZ_8_512 0x40
+#define USS820_TXCON_FFSZ_32_1024 0x60
+#define USS820_TXCON_FFSZ_MASK 0x60
+#define USS820_TXCON_TXCLR 0x80 /* Transmit FIFO clear */
+
+#define USS820_TXFLG 0x04 /* Transmit FIFO flag (Read Only) */
+#define USS820_TXFLG_TXOVF 0x01 /* TX overrun */
+#define USS820_TXFLG_TXURF 0x02 /* TX underrun */
+#define USS820_TXFLG_TXFULL 0x04 /* TX full */
+#define USS820_TXFLG_TXEMP 0x08 /* TX empty */
+#define USS820_TXFLG_UNUSED 0x30
+#define USS820_TXFLG_TXFIF0 0x40
+#define USS820_TXFLG_TXFIF1 0x80
+
+#define USS820_RXDAT 0x05 /* Receive FIFO data */
+
+#define USS820_RXCNTL 0x06 /* Receive FIFO byte count low */
+#define USS820_RXCNTL_MASK 0xFF
+
+#define USS820_RXCNTH 0x07 /* Receive FIFO byte count high */
+#define USS820_RXCNTH_MASK 0x03
+#define USS820_RXCNTH_UNUSED 0xFC
+
+#define USS820_RXCON 0x08 /* Receive FIFO control */
+#define USS820_RXCON_REVWP 0x01
+#define USS820_RXCON_ADVWM 0x02
+#define USS820_RXCON_ARM 0x04 /* Auto Receive Management */
+#define USS820_RXCON_RXISO 0x08 /* Receive Isochronous Data */
+#define USS820_RXCON_RXFFRC 0x10 /* FIFO Read Complete */
+#define USS820_RXCON_FFSZ_16_64 0x00
+#define USS820_RXCON_FFSZ_64_256 0x20
+#define USS820_RXCON_FFSZ_8_512 0x40
+#define USS820_RXCON_FFSZ_32_1024 0x60
+#define USS820_RXCON_RXCLR 0x80 /* Receive FIFO clear */
+
+#define USS820_RXFLG 0x09 /* Receive FIFO flag (Read Only) */
+#define USS820_RXFLG_RXOVF 0x01 /* RX overflow */
+#define USS820_RXFLG_RXURF 0x02 /* RX underflow */
+#define USS820_RXFLG_RXFULL 0x04 /* RX full */
+#define USS820_RXFLG_RXEMP 0x08 /* RX empty */
+#define USS820_RXFLG_RXFLUSH 0x10 /* RX flush */
+#define USS820_RXFLG_UNUSED 0x20
+#define USS820_RXFLG_RXFIF0 0x40
+#define USS820_RXFLG_RXFIF1 0x80
+
+#define USS820_EPINDEX 0x0a /* Endpoint index selection */
+#define USS820_EPINDEX_MASK 0x07
+#define USS820_EPINDEX_UNUSED 0xF8
+
+#define USS820_EPCON 0x0b /* Endpoint control */
+#define USS820_EPCON_TXEPEN 0x01 /* Transmit Endpoint Enable */
+#define USS820_EPCON_TXOE 0x02 /* Transmit Output Enable */
+#define USS820_EPCON_RXEPEN 0x04 /* Receive Endpoint Enable */
+#define USS820_EPCON_RXIE 0x08 /* Receive Input Enable */
+#define USS820_EPCON_RXSPM 0x10 /* Receive Single-Packet Mode */
+#define USS820_EPCON_CTLEP 0x20 /* Control Endpoint */
+#define USS820_EPCON_TXSTL 0x40 /* Stall Transmit Endpoint */
+#define USS820_EPCON_RXSTL 0x80 /* Stall Receive Endpoint */
+
+#define USS820_TXSTAT 0x0c /* Transmit status */
+#define USS820_TXSTAT_TXACK 0x01 /* Transmit Acknowledge */
+#define USS820_TXSTAT_TXERR 0x02 /* Transmit Error */
+#define USS820_TXSTAT_TXVOID 0x04 /* Transmit Void */
+#define USS820_TXSTAT_TXSOVW 0x08 /* Transmit Data Sequence Overwrite
+ * Bit */
+#define USS820_TXSTAT_TXFLUSH 0x10 /* Transmit FIFO Packet Flushed */
+#define USS820_TXSTAT_TXNAKE 0x20 /* Transmit NAK Mode Enable */
+#define USS820_TXSTAT_TXDSAM 0x40 /* Transmit Data-Set-Available Mode */
+#define USS820_TXSTAT_TXSEQ 0x80 /* Transmitter Current Sequence Bit */
+
+#define USS820_RXSTAT 0x0d /* Receive status */
+#define USS820_RXSTAT_RXACK 0x01 /* Receive Acknowledge */
+#define USS820_RXSTAT_RXERR 0x02 /* Receive Error */
+#define USS820_RXSTAT_RXVOID 0x04 /* Receive Void */
+#define USS820_RXSTAT_RXSOVW 0x08 /* Receive Data Sequence Overwrite Bit */
+#define USS820_RXSTAT_EDOVW 0x10 /* End Overwrite Flag */
+#define USS820_RXSTAT_STOVW 0x20 /* Start Overwrite Flag */
+#define USS820_RXSTAT_RXSETUP 0x40 /* Received SETUP token */
+#define USS820_RXSTAT_RXSEQ 0x80 /* Receiver Endpoint Sequence Bit */
+
+#define USS820_SOFL 0x0e /* Start Of Frame counter low */
+#define USS820_SOFL_MASK 0xFF
+
+#define USS820_SOFH 0x0f /* Start Of Frame counter high */
+#define USS820_SOFH_MASK 0x07
+#define USS820_SOFH_SOFDIS 0x08 /* SOF Pin Output Disable */
+#define USS820_SOFH_FTLOCK 0x10 /* Frame Timer Lock */
+#define USS820_SOFH_SOFIE 0x20 /* SOF Interrupt Enable */
+#define USS820_SOFH_ASOF 0x40 /* Any Start of Frame */
+#define USS820_SOFH_SOFACK 0x80 /* SOF Token Received Without Error */
+
+#define USS820_FADDR 0x10 /* Function Address */
+#define USS820_FADDR_MASK 0x7F
+#define USS820_FADDR_UNUSED 0x80
+
+#define USS820_SCR 0x11 /* System Control */
+#define USS820_SCR_UNUSED 0x01
+#define USS820_SCR_T_IRQ 0x02 /* Global Interrupt Enable */
+#define USS820_SCR_IRQLVL 0x04 /* Interrupt Mode */
+#define USS820_SCR_SRESET 0x08 /* Software reset */
+#define USS820_SCR_IE_RESET 0x10 /* Enable Reset Interrupt */
+#define USS820_SCR_IE_SUSP 0x20 /* Enable Suspend Interrupt */
+#define USS820_SCR_RWUPE 0x40 /* Enable Remote Wake-Up Feature */
+#define USS820_SCR_IRQPOL 0x80 /* IRQ polarity */
+
+#define USS820_SSR 0x12 /* System Status */
+#define USS820_SSR_RESET 0x01 /* Reset Condition Detected on USB
+ * cable */
+#define USS820_SSR_SUSPEND 0x02 /* Suspend Detected */
+#define USS820_SSR_RESUME 0x04 /* Resume Detected */
+#define USS820_SSR_SUSPDIS 0x08 /* Suspend Disable */
+#define USS820_SSR_SUSPPO 0x10 /* Suspend Power Off */
+#define USS820_SSR_UNUSED 0xE0
+
+#define USS820_UNK0 0x13 /* Unknown */
+#define USS820_UNK0_UNUSED 0xFF
+
+#define USS820_SBI 0x14 /* Serial bus interrupt low */
+#define USS820_SBI_FTXD0 0x01 /* Function Transmit Done, EP 0 */
+#define USS820_SBI_FRXD0 0x02 /* Function Receive Done, EP 0 */
+#define USS820_SBI_FTXD1 0x04
+#define USS820_SBI_FRXD1 0x08
+#define USS820_SBI_FTXD2 0x10
+#define USS820_SBI_FRXD2 0x20
+#define USS820_SBI_FTXD3 0x40
+#define USS820_SBI_FRXD3 0x80
+
+#define USS820_SBI1 0x15 /* Serial bus interrupt high */
+#define USS820_SBI1_FTXD4 0x01
+#define USS820_SBI1_FRXD4 0x02
+#define USS820_SBI1_FTXD5 0x04
+#define USS820_SBI1_FRXD5 0x08
+#define USS820_SBI1_FTXD6 0x10
+#define USS820_SBI1_FRXD6 0x20
+#define USS820_SBI1_FTXD7 0x40
+#define USS820_SBI1_FRXD7 0x80
+
+#define USS820_SBIE 0x16 /* Serial bus interrupt enable low */
+#define USS820_SBIE_FTXIE0 0x01
+#define USS820_SBIE_FRXIE0 0x02
+#define USS820_SBIE_FTXIE1 0x04
+#define USS820_SBIE_FRXIE1 0x08
+#define USS820_SBIE_FTXIE2 0x10
+#define USS820_SBIE_FRXIE2 0x20
+#define USS820_SBIE_FTXIE3 0x40
+#define USS820_SBIE_FRXIE3 0x80
+
+#define USS820_SBIE1 0x17 /* Serial bus interrupt enable high */
+#define USS820_SBIE1_FTXIE4 0x01
+#define USS820_SBIE1_FRXIE4 0x02
+#define USS820_SBIE1_FTXIE5 0x04
+#define USS820_SBIE1_FRXIE5 0x08
+#define USS820_SBIE1_FTXIE6 0x10
+#define USS820_SBIE1_FRXIE6 0x20
+#define USS820_SBIE1_FTXIE7 0x40
+#define USS820_SBIE1_FRXIE7 0x80
+
+#define USS820_REV 0x18 /* Hardware revision */
+#define USS820_REV_MIN 0x0F
+#define USS820_REV_MAJ 0xF0
+
+#define USS820_LOCK 0x19 /* Suspend power-off locking */
+#define USS820_LOCK_UNLOCKED 0x01
+#define USS820_LOCK_UNUSED 0xFE
+
+#define USS820_PEND 0x1a /* Pend hardware status update */
+#define USS820_PEND_PEND 0x01
+#define USS820_PEND_UNUSED 0xFE
+
+#define USS820_SCRATCH 0x1b /* Scratch firmware information */
+#define USS820_SCRATCH_MASK 0x7F
+#define USS820_SCRATCH_IE_RESUME 0x80 /* Enable Resume Interrupt */
+
+#define USS820_MCSR 0x1c /* Miscellaneous control and status */
+#define USS820_MCSR_DPEN 0x01 /* DPLS Pull-Up Enable */
+#define USS820_MCSR_SUSPLOE 0x02 /* Suspend Lock Out Enable */
+#define USS820_MCSR_BDFEAT 0x04 /* Board Feature Enable */
+#define USS820_MCSR_FEAT 0x08 /* Feature Enable */
+#define USS820_MCSR_PKGID 0x10 /* Package Identification */
+#define USS820_MCSR_SUSPS 0x20 /* Suspend Status */
+#define USS820_MCSR_INIT 0x40 /* Device Initialized */
+#define USS820_MCSR_RWUPR 0x80 /* Remote Wakeup-Up Remember */
+
+#define USS820_DSAV 0x1d /* Data set available low (Read Only) */
+#define USS820_DSAV_TXAV0 0x01
+#define USS820_DSAV_RXAV0 0x02
+#define USS820_DSAV_TXAV1 0x04
+#define USS820_DSAV_RXAV1 0x08
+#define USS820_DSAV_TXAV2 0x10
+#define USS820_DSAV_RXAV2 0x20
+#define USS820_DSAV_TXAV3 0x40
+#define USS820_DSAV_RXAV3 0x80
+
+#define USS820_DSAV1 0x1e /* Data set available high */
+#define USS820_DSAV1_TXAV4 0x01
+#define USS820_DSAV1_RXAV4 0x02
+#define USS820_DSAV1_TXAV5 0x04
+#define USS820_DSAV1_RXAV5 0x08
+#define USS820_DSAV1_TXAV6 0x10
+#define USS820_DSAV1_RXAV6 0x20
+#define USS820_DSAV1_TXAV7 0x40
+#define USS820_DSAV1_RXAV7 0x80
+
+#define USS820_UNK1 0x1f /* Unknown */
+#define USS820_UNK1_UNKNOWN 0xFF
+
+#define USS820_GET_REG(sc,reg) \
+ ((reg) << (sc)->sc_reg_shift)
+
+#define USS820_READ_1(sc, reg) \
+ bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ USS820_GET_REG(sc,reg))
+
+#define USS820_WRITE_1(sc, reg, data) \
+ bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, \
+ USS820_GET_REG(sc,reg), data)
+
+struct uss820dci_td;
+
+typedef uint8_t (uss820dci_cmd_t)(struct uss820dci_td *td);
+
+struct uss820dci_td {
+ bus_space_tag_t io_tag;
+ bus_space_handle_t io_hdl;
+ struct uss820dci_td *obj_next;
+ uss820dci_cmd_t *func;
+ struct usb2_page_cache *pc;
+ uint32_t offset;
+ uint32_t remainder;
+ uint16_t max_packet_size;
+ uint8_t rx_stat_reg;
+ uint8_t tx_stat_reg;
+ uint8_t rx_flag_reg;
+ uint8_t tx_flag_reg;
+ uint8_t rx_fifo_reg;
+ uint8_t tx_fifo_reg;
+ uint8_t rx_count_low_reg;
+ uint8_t rx_count_high_reg;
+ uint8_t tx_count_low_reg;
+ uint8_t tx_count_high_reg;
+ uint8_t rx_cntl_reg;
+ uint8_t tx_cntl_reg;
+ uint8_t ep_reg;
+ uint8_t pend_reg;
+ uint8_t ep_index;
+ uint8_t error:1;
+ uint8_t alt_next:1;
+ uint8_t short_pkt:1;
+ uint8_t support_multi_buffer:1;
+ uint8_t did_stall:1;
+};
+
+struct uss820_std_temp {
+ uss820dci_cmd_t *func;
+ struct usb2_page_cache *pc;
+ struct uss820dci_td *td;
+ struct uss820dci_td *td_next;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t max_frame_size;
+ uint8_t short_pkt;
+ /*
+ * short_pkt = 0: transfer should be short terminated
+ * short_pkt = 1: transfer should not be short terminated
+ */
+ uint8_t setup_alt_next;
+};
+
+struct uss820dci_config_desc {
+ struct usb2_config_descriptor confd;
+ struct usb2_interface_descriptor ifcd;
+ struct usb2_endpoint_descriptor endpd;
+} __packed;
+
+union uss820_hub_temp {
+ uWord wValue;
+ struct usb2_port_status ps;
+};
+
+struct uss820_flags {
+ uint8_t change_connect:1;
+ uint8_t change_suspend:1;
+ uint8_t status_suspend:1; /* set if suspended */
+ uint8_t status_vbus:1; /* set if present */
+ uint8_t status_bus_reset:1; /* set if reset complete */
+ uint8_t clocks_off:1;
+ uint8_t port_powered:1;
+ uint8_t port_enabled:1;
+ uint8_t d_pulled_up:1;
+ uint8_t mcsr_feat:1;
+};
+
+struct uss820dci_softc {
+ struct usb2_bus sc_bus;
+ union uss820_hub_temp sc_hub_temp;
+ LIST_HEAD(, usb2_xfer) sc_interrupt_list_head;
+ struct usb2_sw_transfer sc_root_ctrl;
+ struct usb2_sw_transfer sc_root_intr;
+
+ struct usb2_device *sc_devices[USS820_MAX_DEVICES];
+ struct resource *sc_io_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_hdl;
+ bus_size_t sc_io_size;
+ bus_space_tag_t sc_io_tag;
+ bus_space_handle_t sc_io_hdl;
+
+ uint8_t sc_rt_addr; /* root HUB address */
+ uint8_t sc_dv_addr; /* device address */
+ uint8_t sc_conf; /* root HUB config */
+ uint8_t sc_reg_shift;
+
+ uint8_t sc_hub_idata[1];
+
+ struct uss820_flags sc_flags;
+};
+
+/* prototypes */
+
+usb2_error_t uss820dci_init(struct uss820dci_softc *sc);
+void uss820dci_uninit(struct uss820dci_softc *sc);
+void uss820dci_suspend(struct uss820dci_softc *sc);
+void uss820dci_resume(struct uss820dci_softc *sc);
+void uss820dci_interrupt(struct uss820dci_softc *sc);
+
+#endif /* _USS820_DCI_H_ */
diff --git a/sys/dev/usb/controller/uss820dci_atmelarm.c b/sys/dev/usb/controller/uss820dci_atmelarm.c
new file mode 100644
index 0000000..ddbffd7
--- /dev/null
+++ b/sys/dev/usb/controller/uss820dci_atmelarm.c
@@ -0,0 +1,238 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+#include <dev/usb/controller/uss820dci.h>
+
+#include <sys/rman.h>
+
+static device_probe_t uss820_atmelarm_probe;
+static device_attach_t uss820_atmelarm_attach;
+static device_detach_t uss820_atmelarm_detach;
+static device_suspend_t uss820_atmelarm_suspend;
+static device_resume_t uss820_atmelarm_resume;
+static device_shutdown_t uss820_atmelarm_shutdown;
+
+static device_method_t uss820dci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uss820_atmelarm_probe),
+ DEVMETHOD(device_attach, uss820_atmelarm_attach),
+ DEVMETHOD(device_detach, uss820_atmelarm_detach),
+ DEVMETHOD(device_suspend, uss820_atmelarm_suspend),
+ DEVMETHOD(device_resume, uss820_atmelarm_resume),
+ DEVMETHOD(device_shutdown, uss820_atmelarm_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+ {0, 0}
+};
+
+static driver_t uss820dci_driver = {
+ .name = "uss820",
+ .methods = uss820dci_methods,
+ .size = sizeof(struct uss820dci_softc),
+};
+
+static devclass_t uss820dci_devclass;
+
+DRIVER_MODULE(uss820, atmelarm, uss820dci_driver, uss820dci_devclass, 0, 0);
+MODULE_DEPEND(uss820, usb, 1, 1, 1);
+
+static const char *const uss820_desc = "USS820 USB Device Controller";
+
+static int
+uss820_atmelarm_suspend(device_t dev)
+{
+ struct uss820dci_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_suspend(dev);
+ if (err == 0) {
+ uss820dci_suspend(sc);
+ }
+ return (err);
+}
+
+static int
+uss820_atmelarm_resume(device_t dev)
+{
+ struct uss820dci_softc *sc = device_get_softc(dev);
+ int err;
+
+ uss820dci_resume(sc);
+
+ err = bus_generic_resume(dev);
+
+ return (err);
+}
+
+static int
+uss820_atmelarm_shutdown(device_t dev)
+{
+ struct uss820dci_softc *sc = device_get_softc(dev);
+ int err;
+
+ err = bus_generic_shutdown(dev);
+ if (err)
+ return (err);
+
+ uss820dci_uninit(sc);
+
+ return (0);
+}
+
+static int
+uss820_atmelarm_probe(device_t dev)
+{
+ device_set_desc(dev, uss820_desc);
+ return (0); /* success */
+}
+
+static int
+uss820_atmelarm_attach(device_t dev)
+{
+ struct uss820dci_softc *sc = device_get_softc(dev);
+ int err;
+ int rid;
+
+ /* initialise some bus fields */
+ sc->sc_bus.parent = dev;
+ sc->sc_bus.devices = sc->sc_devices;
+ sc->sc_bus.devices_max = USS820_MAX_DEVICES;
+
+ /* get all DMA memory */
+ if (usb2_bus_mem_alloc_all(&sc->sc_bus,
+ USB_GET_DMA_TAG(dev), NULL)) {
+ return (ENOMEM);
+ }
+ rid = 0;
+ sc->sc_io_res =
+ bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
+
+ if (!sc->sc_io_res) {
+ goto error;
+ }
+ sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+ sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+ sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+ /* multiply all addresses by 4 */
+ sc->sc_reg_shift = 2;
+
+ rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_SHAREABLE | RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ goto error;
+ }
+ sc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+ if (!(sc->sc_bus.bdev)) {
+ goto error;
+ }
+ device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus);
+
+#if (__FreeBSD_version >= 700031)
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ NULL, (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl);
+#else
+ err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
+ (void *)uss820dci_interrupt, sc, &sc->sc_intr_hdl);
+#endif
+ if (err) {
+ sc->sc_intr_hdl = NULL;
+ goto error;
+ }
+ err = uss820dci_init(sc);
+ if (err) {
+ device_printf(dev, "Init failed\n");
+ goto error;
+ }
+ err = device_probe_and_attach(sc->sc_bus.bdev);
+ if (err) {
+ device_printf(dev, "USB probe and attach failed\n");
+ goto error;
+ }
+ return (0);
+
+error:
+ uss820_atmelarm_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uss820_atmelarm_detach(device_t dev)
+{
+ struct uss820dci_softc *sc = device_get_softc(dev);
+ device_t bdev;
+ int err;
+
+ if (sc->sc_bus.bdev) {
+ bdev = sc->sc_bus.bdev;
+ device_detach(bdev);
+ device_delete_child(dev, bdev);
+ }
+ /* during module unload there are lots of children leftover */
+ device_delete_all_children(dev);
+
+ if (sc->sc_irq_res && sc->sc_intr_hdl) {
+ /*
+ * only call at91_udp_uninit() after at91_udp_init()
+ */
+ uss820dci_uninit(sc);
+
+ err = bus_teardown_intr(dev, sc->sc_irq_res,
+ sc->sc_intr_hdl);
+ sc->sc_intr_hdl = NULL;
+ }
+ if (sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, 0,
+ sc->sc_irq_res);
+ sc->sc_irq_res = NULL;
+ }
+ if (sc->sc_io_res) {
+ bus_release_resource(dev, SYS_RES_IOPORT, 0,
+ sc->sc_io_res);
+ sc->sc_io_res = NULL;
+ }
+ usb2_bus_mem_free_all(&sc->sc_bus, NULL);
+
+ return (0);
+}
diff --git a/sys/dev/usb/image/uscanner.c b/sys/dev/usb/image/uscanner.c
new file mode 100644
index 0000000..ca631b5
--- /dev/null
+++ b/sys/dev/usb/image/uscanner.c
@@ -0,0 +1,643 @@
+/* $NetBSD: uscanner.c,v 1.30 2002/07/11 21:14:36 augustss Exp$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uscanner.c,v 1.33 2002/09/23 05:51:24 simonb Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology
+ * and Nick Hibma (n_hibma@qubesoft.com).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR uscanner_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+
+#if USB_DEBUG
+static int uscanner_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uscanner, CTLFLAG_RW, 0, "USB uscanner");
+SYSCTL_INT(_hw_usb2_uscanner, OID_AUTO, debug, CTLFLAG_RW, &uscanner_debug,
+ 0, "uscanner debug level");
+#endif
+
+/*
+ * uscanner transfers macros definition.
+ */
+#define USCANNER_BSIZE (1 << 15)
+#define USCANNER_IFQ_MAXLEN 2
+
+/*
+ * Transfers stallings handling flags definition.
+ */
+#define USCANNER_FLAG_READ_STALL 0x01
+#define USCANNER_FLAG_WRITE_STALL 0x02
+
+/*
+ * uscanner_info flags definition.
+ */
+#define USCANNER_FLAG_KEEP_OPEN 0x04
+
+enum {
+ USCANNER_BULK_DT_WR,
+ USCANNER_BULK_DT_RD,
+ USCANNER_BULK_CS_WR,
+ USCANNER_BULK_CS_RD,
+ USCANNER_N_TRANSFER = 4,
+};
+
+struct uscanner_softc {
+ struct usb2_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+
+ struct usb2_xfer *sc_xfer[USCANNER_N_TRANSFER];
+
+ uint8_t sc_flags; /* Used to prevent stalls */
+};
+
+/*
+ * Prototypes for driver handling routines (sorted by use).
+ */
+static device_probe_t uscanner_probe;
+static device_attach_t uscanner_attach;
+static device_detach_t uscanner_detach;
+
+/*
+ * Prototypes for xfer transfer callbacks.
+ */
+static usb2_callback_t uscanner_read_callback;
+static usb2_callback_t uscanner_read_clear_stall_callback;
+static usb2_callback_t uscanner_write_callback;
+static usb2_callback_t uscanner_write_clear_stall_callback;
+
+/*
+ * Prototypes for the character device handling routines.
+ */
+static usb2_fifo_close_t uscanner_close;
+static usb2_fifo_cmd_t uscanner_start_read;
+static usb2_fifo_cmd_t uscanner_start_write;
+static usb2_fifo_cmd_t uscanner_stop_read;
+static usb2_fifo_cmd_t uscanner_stop_write;
+static usb2_fifo_open_t uscanner_open;
+
+static struct usb2_fifo_methods uscanner_fifo_methods = {
+ .f_close = &uscanner_close,
+ .f_open = &uscanner_open,
+ .f_start_read = &uscanner_start_read,
+ .f_start_write = &uscanner_start_write,
+ .f_stop_read = &uscanner_stop_read,
+ .f_stop_write = &uscanner_stop_write,
+ .basename[0] = "uscanner",
+};
+
+/*
+ * xfer transfers array. Resolve-stalling callbacks are marked as control
+ * transfers.
+ */
+static const struct usb2_config uscanner_config[USCANNER_N_TRANSFER] = {
+ [USCANNER_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = USCANNER_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,.force_short_xfer = 1,},
+ .mh.callback = &uscanner_write_callback,
+ },
+
+ [USCANNER_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = USCANNER_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uscanner_read_callback,
+ },
+
+ [USCANNER_BULK_CS_WR] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &uscanner_write_clear_stall_callback,
+ .mh.timeout = 1000,
+ .mh.interval = 50, /* 50ms */
+ },
+
+ [USCANNER_BULK_CS_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00,
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &uscanner_read_clear_stall_callback,
+ .mh.timeout = 1000,
+ .mh.interval = 50, /* 50ms */
+ },
+};
+
+static devclass_t uscanner_devclass;
+
+static device_method_t uscanner_methods[] = {
+ DEVMETHOD(device_probe, uscanner_probe),
+ DEVMETHOD(device_attach, uscanner_attach),
+ DEVMETHOD(device_detach, uscanner_detach),
+ {0, 0}
+};
+
+static driver_t uscanner_driver = {
+ .name = "uscanner",
+ .methods = uscanner_methods,
+ .size = sizeof(struct uscanner_softc),
+};
+
+DRIVER_MODULE(uscanner, ushub, uscanner_driver, uscanner_devclass, NULL, 0);
+MODULE_DEPEND(uscanner, usb, 1, 1, 1);
+
+/*
+ * USB scanners device IDs
+ */
+static const struct usb2_device_id uscanner_devs[] = {
+ /* Acer */
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_320U, 0)},
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640U, 0)},
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_640BT, 0)},
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_620U, 0)},
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_1240U, 0)},
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_C310U, 0)},
+ {USB_VPI(USB_VENDOR_ACERP, USB_PRODUCT_ACERP_ACERSCAN_4300U, 0)},
+ /* AGFA */
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1236U, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCAN1212U2, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANTOUCH, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE40, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE50, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE20, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE25, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE26, 0)},
+ {USB_VPI(USB_VENDOR_AGFA, USB_PRODUCT_AGFA_SNAPSCANE52, 0)},
+ /* Avision */
+ {USB_VPI(USB_VENDOR_AVISION, USB_PRODUCT_AVISION_1200U, 0)},
+ /* Canon */
+ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N656U, 0)},
+ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N676U, 0)},
+ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N1220U, 0)},
+ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_D660U, 0)},
+ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_N1240U, 0)},
+ {USB_VPI(USB_VENDOR_CANON, USB_PRODUCT_CANON_LIDE25, 0)},
+ /* Epson */
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_636, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_610, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1200, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1240, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1250, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1270, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1600, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1640, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_640U, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1650, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1660, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1670, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_1260, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_RX425, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3200, USCANNER_FLAG_KEEP_OPEN)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9700F, USCANNER_FLAG_KEEP_OPEN)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_CX5400, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX7400, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_GT9300UF, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_2480, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3500, USCANNER_FLAG_KEEP_OPEN)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_3590, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4200, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4800, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_4990, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_5000, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_6000, 0)},
+ {USB_VPI(USB_VENDOR_EPSON, USB_PRODUCT_EPSON_DX8400, 0)},
+ /* HP */
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2200C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_3300C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_3400CSE, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4100C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4200C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4300C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4470C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4670V, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_S20, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5200C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5300C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5400C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_6200C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_6300C, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_82x0C, 0)},
+ /* Kye */
+ {USB_VPI(USB_VENDOR_KYE, USB_PRODUCT_KYE_VIVIDPRO, 0)},
+ /* Microtek */
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_X6U, 0)},
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX, 0)},
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_336CX2, 0)},
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_C6, 0)},
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL, 0)},
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6USL2, 0)},
+ {USB_VPI(USB_VENDOR_MICROTEK, USB_PRODUCT_MICROTEK_V6UL, 0)},
+ /* Minolta */
+ {USB_VPI(USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_5400, 0)},
+ /* Mustek */
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CU, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200F, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_BEARPAW1200TA, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600USB, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_600CU, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USB, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200UB, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200USBPLUS, 0)},
+ {USB_VPI(USB_VENDOR_MUSTEK, USB_PRODUCT_MUSTEK_1200CUPLUS, 0)},
+ /* National */
+ {USB_VPI(USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW1200, 0)},
+ {USB_VPI(USB_VENDOR_NATIONAL, USB_PRODUCT_NATIONAL_BEARPAW2400, 0)},
+ /* Nikon */
+ {USB_VPI(USB_VENDOR_NIKON, USB_PRODUCT_NIKON_LS40, 0)},
+ /* Primax */
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2X300, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E300, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2300, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E3002, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_9600, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_600U, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_6200, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_19200, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_1200U, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G600, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_636I, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2600, 0)},
+ {USB_VPI(USB_VENDOR_PRIMAX, USB_PRODUCT_PRIMAX_G2E600, 0)},
+ /* Scanlogic */
+ {USB_VPI(USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_336CX, 0)},
+ /* Ultima */
+ {USB_VPI(USB_VENDOR_ULTIMA, USB_PRODUCT_ULTIMA_1200UBPLUS, 0)},
+ /* UMAX */
+ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1220U, 0)},
+ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA1236U, 0)},
+ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2000U, 0)},
+ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2100U, 0)},
+ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA2200U, 0)},
+ {USB_VPI(USB_VENDOR_UMAX, USB_PRODUCT_UMAX_ASTRA3400, 0)},
+ /* Visioneer */
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_3000, 0)},
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_5300, 0)},
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_7600, 0)},
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6100, 0)},
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_6200, 0)},
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8100, 0)},
+ {USB_VPI(USB_VENDOR_VISIONEER, USB_PRODUCT_VISIONEER_8600, 0)}
+};
+
+/*
+ * uscanner device probing method.
+ */
+static int
+uscanner_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa;
+
+ DPRINTFN(11, "\n");
+
+ uaa = device_get_ivars(dev);
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ /* Give other class drivers a chance for multifunctional scanners. */
+ if (uaa->use_generic == 0) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uscanner_devs, sizeof(uscanner_devs), uaa));
+}
+
+/*
+ * uscanner device attaching method.
+ */
+static int
+uscanner_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa;
+ struct uscanner_softc *sc;
+ int unit;
+ int error;
+
+ uaa = device_get_ivars(dev);
+ sc = device_get_softc(dev);
+ unit = device_get_unit(dev);
+
+ /*
+ * A first path softc structure filling. sc_fifo and
+ * sc_xfer are initialised later.
+ */
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+ mtx_init(&sc->sc_mtx, "uscanner mutex", NULL, MTX_DEF | MTX_RECURSE);
+
+ /*
+ * Announce the device:
+ */
+ device_set_usb2_desc(dev);
+
+ /*
+ * Setup the transfer.
+ */
+ if ((error = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer,
+ uscanner_config, USCANNER_N_TRANSFER, sc, &sc->sc_mtx))) {
+ device_printf(dev, "could not setup transfers, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &uscanner_fifo_methods, &sc->sc_fifo,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ uscanner_detach(dev);
+ return (ENOMEM);
+}
+
+/*
+ * uscanner device detaching method.
+ */
+static int
+uscanner_detach(device_t dev)
+{
+ struct uscanner_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+ usb2_transfer_unsetup(sc->sc_xfer, USCANNER_N_TRANSFER);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+/*
+ * Reading callback. Implemented as an "in" bulk transfer.
+ */
+static void
+uscanner_read_callback(struct usb2_xfer *xfer)
+{
+ struct uscanner_softc *sc;
+ struct usb2_fifo *f;
+
+ sc = xfer->priv_sc;
+ f = sc->sc_fifo.fp[USB_FIFO_RX];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_fifo_put_data(f, xfer->frbuffers, 0,
+ xfer->actlen, 1);
+
+ case USB_ST_SETUP:
+ /*
+ * If reading is in stall, just jump to clear stall callback and
+ * solve the situation.
+ */
+ if (sc->sc_flags & USCANNER_FLAG_READ_STALL) {
+ usb2_transfer_start(sc->sc_xfer[USCANNER_BULK_CS_RD]);
+ break;
+ }
+ if (usb2_fifo_put_bytes_max(f) != 0) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ sc->sc_flags |= USCANNER_FLAG_READ_STALL;
+ usb2_transfer_start(sc->sc_xfer[USCANNER_BULK_CS_RD]);
+ }
+ break;
+ }
+}
+
+/*
+ * Removing stall on reading callback.
+ */
+static void
+uscanner_read_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct uscanner_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[USCANNER_BULK_DT_RD];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~USCANNER_FLAG_READ_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+/*
+ * Writing callback. Implemented as an "out" bulk transfer.
+ */
+static void
+uscanner_write_callback(struct usb2_xfer *xfer)
+{
+ struct uscanner_softc *sc;
+ struct usb2_fifo *f;
+ uint32_t actlen;
+
+ sc = xfer->priv_sc;
+ f = sc->sc_fifo.fp[USB_FIFO_TX];
+
+ 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 (sc->sc_flags & USCANNER_FLAG_WRITE_STALL) {
+ usb2_transfer_start(sc->sc_xfer[USCANNER_BULK_CS_WR]);
+ break;
+ }
+ /*
+ * Write datas, 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) {
+ sc->sc_flags |= USCANNER_FLAG_WRITE_STALL;
+ usb2_transfer_start(sc->sc_xfer[USCANNER_BULK_CS_WR]);
+ }
+ break;
+ }
+}
+
+/*
+ * Removing stall on writing callback.
+ */
+static void
+uscanner_write_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct uscanner_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[USCANNER_BULK_DT_WR];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~USCANNER_FLAG_WRITE_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+/*
+ * uscanner character device opening method.
+ */
+static int
+uscanner_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct uscanner_softc *sc;
+
+ sc = fifo->priv_sc0;
+
+ if (!(sc->sc_flags & USCANNER_FLAG_KEEP_OPEN)) {
+ if (fflags & FWRITE) {
+ sc->sc_flags |= USCANNER_FLAG_WRITE_STALL;
+ }
+ if (fflags & FREAD) {
+ sc->sc_flags |= USCANNER_FLAG_READ_STALL;
+ }
+ }
+ if (fflags & FREAD) {
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[USCANNER_BULK_DT_RD]->max_data_length,
+ USCANNER_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+ if (fflags & FWRITE) {
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[USCANNER_BULK_DT_WR]->max_data_length,
+ USCANNER_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+ return (0);
+}
+
+static void
+uscanner_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ if (fflags & (FREAD | FWRITE)) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+/*
+ * uscanner character device start reading method.
+ */
+static void
+uscanner_start_read(struct usb2_fifo *fifo)
+{
+ struct uscanner_softc *sc;
+
+ sc = fifo->priv_sc0;
+ usb2_transfer_start(sc->sc_xfer[USCANNER_BULK_DT_RD]);
+}
+
+/*
+ * uscanner character device start writing method.
+ */
+static void
+uscanner_start_write(struct usb2_fifo *fifo)
+{
+ struct uscanner_softc *sc;
+
+ sc = fifo->priv_sc0;
+ usb2_transfer_start(sc->sc_xfer[USCANNER_BULK_DT_WR]);
+}
+
+/*
+ * uscanner character device stop reading method.
+ */
+static void
+uscanner_stop_read(struct usb2_fifo *fifo)
+{
+ struct uscanner_softc *sc;
+
+ sc = fifo->priv_sc0;
+ usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_CS_RD]);
+ usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_DT_RD]);
+}
+
+/*
+ * uscanner character device stop writing method.
+ */
+static void
+uscanner_stop_write(struct usb2_fifo *fifo)
+{
+ struct uscanner_softc *sc;
+
+ sc = fifo->priv_sc0;
+ usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_CS_WR]);
+ usb2_transfer_stop(sc->sc_xfer[USCANNER_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/input/uhid.c b/sys/dev/usb/input/uhid.c
new file mode 100644
index 0000000..563bd99
--- /dev/null
+++ b/sys/dev/usb/input/uhid.c
@@ -0,0 +1,789 @@
+/* $NetBSD: uhid.c,v 1.46 2001/11/13 06:24:55 lukem Exp $ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uhid.c,v 1.54 2002/09/23 05:51:21 simonb 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.
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usbhid.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uhid_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_hid.h>
+
+#include <dev/usb/input/usb_rdesc.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#if USB_DEBUG
+static int uhid_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uhid, CTLFLAG_RW, 0, "USB uhid");
+SYSCTL_INT(_hw_usb2_uhid, OID_AUTO, debug, CTLFLAG_RW,
+ &uhid_debug, 0, "Debug level");
+#endif
+
+#define UHID_BSIZE 1024 /* bytes, buffer size */
+#define UHID_FRAME_NUM 50 /* bytes, frame number */
+
+enum {
+ UHID_INTR_DT_RD,
+ UHID_CTRL_DT_WR,
+ UHID_CTRL_DT_RD,
+ UHID_N_TRANSFER,
+};
+
+struct uhid_softc {
+ struct usb2_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+
+ struct usb2_xfer *sc_xfer[UHID_N_TRANSFER];
+ struct usb2_device *sc_udev;
+ void *sc_repdesc_ptr;
+
+ uint32_t sc_isize;
+ uint32_t sc_osize;
+ uint32_t sc_fsize;
+
+ uint16_t sc_repdesc_size;
+
+ uint8_t sc_iface_no;
+ uint8_t sc_iface_index;
+ uint8_t sc_iid;
+ uint8_t sc_oid;
+ uint8_t sc_fid;
+ uint8_t sc_flags;
+#define UHID_FLAG_IMMED 0x01 /* set if read should be immediate */
+#define UHID_FLAG_STATIC_DESC 0x04 /* set if report descriptors are
+ * static */
+};
+
+static const uint8_t uhid_xb360gp_report_descr[] = {UHID_XB360GP_REPORT_DESCR()};
+static const uint8_t uhid_graphire_report_descr[] = {UHID_GRAPHIRE_REPORT_DESCR()};
+static const uint8_t uhid_graphire3_4x5_report_descr[] = {UHID_GRAPHIRE3_4X5_REPORT_DESCR()};
+
+/* prototypes */
+
+static device_probe_t uhid_probe;
+static device_attach_t uhid_attach;
+static device_detach_t uhid_detach;
+
+static usb2_callback_t uhid_intr_callback;
+static usb2_callback_t uhid_write_callback;
+static usb2_callback_t uhid_read_callback;
+
+static usb2_fifo_cmd_t uhid_start_read;
+static usb2_fifo_cmd_t uhid_stop_read;
+static usb2_fifo_cmd_t uhid_start_write;
+static usb2_fifo_cmd_t uhid_stop_write;
+static usb2_fifo_open_t uhid_open;
+static usb2_fifo_close_t uhid_close;
+static usb2_fifo_ioctl_t uhid_ioctl;
+
+static struct usb2_fifo_methods uhid_fifo_methods = {
+ .f_open = &uhid_open,
+ .f_close = &uhid_close,
+ .f_ioctl = &uhid_ioctl,
+ .f_start_read = &uhid_start_read,
+ .f_stop_read = &uhid_stop_read,
+ .f_start_write = &uhid_start_write,
+ .f_stop_write = &uhid_stop_write,
+ .basename[0] = "uhid",
+};
+
+static void
+uhid_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uhid_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("transferred!\n");
+
+ if (xfer->actlen >= sc->sc_isize) {
+ usb2_fifo_put_data(
+ sc->sc_fifo.fp[USB_FIFO_RX],
+ xfer->frbuffers,
+ 0, sc->sc_isize, 1);
+ } else {
+ /* ignore it */
+ DPRINTF("ignored short transfer, "
+ "%d bytes\n", xfer->actlen);
+ }
+
+ case USB_ST_SETUP:
+re_submit:
+ if (usb2_fifo_put_bytes_max(
+ sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
+ xfer->frlengths[0] = sc->sc_isize;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto re_submit;
+ }
+ return;
+ }
+}
+
+static void
+uhid_fill_set_report(struct usb2_device_request *req, uint8_t iface_no,
+ uint8_t type, uint8_t id, uint16_t size)
+{
+ req->bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req->bRequest = UR_SET_REPORT;
+ USETW2(req->wValue, type, id);
+ req->wIndex[0] = iface_no;
+ req->wIndex[1] = 0;
+ USETW(req->wLength, size);
+}
+
+static void
+uhid_fill_get_report(struct usb2_device_request *req, uint8_t iface_no,
+ uint8_t type, uint8_t id, uint16_t size)
+{
+ req->bmRequestType = UT_READ_CLASS_INTERFACE;
+ req->bRequest = UR_GET_REPORT;
+ USETW2(req->wValue, type, id);
+ req->wIndex[0] = iface_no;
+ req->wIndex[1] = 0;
+ USETW(req->wLength, size);
+}
+
+static void
+uhid_write_callback(struct usb2_xfer *xfer)
+{
+ struct uhid_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint32_t size = sc->sc_osize;
+ uint32_t actlen;
+ uint8_t id;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+ /* try to extract the ID byte */
+ if (sc->sc_oid) {
+
+ if (usb2_fifo_get_data(
+ sc->sc_fifo.fp[USB_FIFO_TX],
+ xfer->frbuffers,
+ 0, 1, &actlen, 0)) {
+ if (actlen != 1) {
+ goto tr_error;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &id, 1);
+
+ } else {
+ return;
+ }
+ if (size) {
+ size--;
+ }
+ } else {
+ id = 0;
+ }
+
+ if (usb2_fifo_get_data(
+ sc->sc_fifo.fp[USB_FIFO_TX],
+ xfer->frbuffers + 1,
+ 0, UHID_BSIZE, &actlen, 1)) {
+ if (actlen != size) {
+ goto tr_error;
+ }
+ uhid_fill_set_report
+ (&req, sc->sc_iface_no,
+ UHID_OUTPUT_REPORT, id, size);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = size;
+ xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default:
+tr_error:
+ /* bomb out */
+ usb2_fifo_get_data_error(sc->sc_fifo.fp[USB_FIFO_TX]);
+ return;
+ }
+}
+
+static void
+uhid_read_callback(struct usb2_xfer *xfer)
+{
+ struct uhid_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_fifo_put_data(sc->sc_fifo.fp[USB_FIFO_RX], xfer->frbuffers,
+ sizeof(req), sc->sc_isize, 1);
+ return;
+
+ case USB_ST_SETUP:
+
+ if (usb2_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) > 0) {
+
+ uhid_fill_get_report
+ (&req, sc->sc_iface_no, UHID_INPUT_REPORT,
+ sc->sc_iid, sc->sc_isize);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = sc->sc_isize;
+ xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ /* bomb out */
+ usb2_fifo_put_data_error(sc->sc_fifo.fp[USB_FIFO_RX]);
+ return;
+ }
+}
+
+static const struct usb2_config uhid_config[UHID_N_TRANSFER] = {
+
+ [UHID_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = UHID_BSIZE,
+ .mh.callback = &uhid_intr_callback,
+ },
+
+ [UHID_CTRL_DT_WR] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request) + UHID_BSIZE,
+ .mh.callback = &uhid_write_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+
+ [UHID_CTRL_DT_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request) + UHID_BSIZE,
+ .mh.callback = &uhid_read_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static void
+uhid_start_read(struct usb2_fifo *fifo)
+{
+ struct uhid_softc *sc = fifo->priv_sc0;
+
+ if (sc->sc_flags & UHID_FLAG_IMMED) {
+ usb2_transfer_start(sc->sc_xfer[UHID_CTRL_DT_RD]);
+ } else {
+ usb2_transfer_start(sc->sc_xfer[UHID_INTR_DT_RD]);
+ }
+}
+
+static void
+uhid_stop_read(struct usb2_fifo *fifo)
+{
+ struct uhid_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[UHID_INTR_DT_RD]);
+}
+
+static void
+uhid_start_write(struct usb2_fifo *fifo)
+{
+ struct uhid_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[UHID_CTRL_DT_WR]);
+}
+
+static void
+uhid_stop_write(struct usb2_fifo *fifo)
+{
+ struct uhid_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[UHID_CTRL_DT_WR]);
+}
+
+static int
+uhid_get_report(struct uhid_softc *sc, uint8_t type,
+ uint8_t id, void *kern_data, void *user_data,
+ uint16_t len)
+{
+ int err;
+ uint8_t free_data = 0;
+
+ if (kern_data == NULL) {
+ kern_data = malloc(len, M_USBDEV, M_WAITOK);
+ if (kern_data == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ free_data = 1;
+ }
+ err = usb2_req_get_report(sc->sc_udev, NULL, kern_data,
+ len, sc->sc_iface_index, type, id);
+ if (err) {
+ err = ENXIO;
+ goto done;
+ }
+ if (user_data) {
+ /* dummy buffer */
+ err = copyout(kern_data, user_data, len);
+ if (err) {
+ goto done;
+ }
+ }
+done:
+ if (free_data) {
+ free(kern_data, M_USBDEV);
+ }
+ return (err);
+}
+
+static int
+uhid_set_report(struct uhid_softc *sc, uint8_t type,
+ uint8_t id, void *kern_data, void *user_data,
+ uint16_t len)
+{
+ int err;
+ uint8_t free_data = 0;
+
+ if (kern_data == NULL) {
+ kern_data = malloc(len, M_USBDEV, M_WAITOK);
+ if (kern_data == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+ free_data = 1;
+ err = copyin(user_data, kern_data, len);
+ if (err) {
+ goto done;
+ }
+ }
+ err = usb2_req_set_report(sc->sc_udev, NULL, kern_data,
+ len, sc->sc_iface_index, type, id);
+ if (err) {
+ err = ENXIO;
+ goto done;
+ }
+done:
+ if (free_data) {
+ free(kern_data, M_USBDEV);
+ }
+ return (err);
+}
+
+static int
+uhid_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct uhid_softc *sc = fifo->priv_sc0;
+
+ /*
+ * The buffers are one byte larger than maximum so that one
+ * can detect too large read/writes and short transfers:
+ */
+ if (fflags & FREAD) {
+ /* reset flags */
+ sc->sc_flags &= ~UHID_FLAG_IMMED;
+
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_isize + 1, UHID_FRAME_NUM)) {
+ return (ENOMEM);
+ }
+ }
+ if (fflags & FWRITE) {
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_osize + 1, UHID_FRAME_NUM)) {
+ return (ENOMEM);
+ }
+ }
+ return (0);
+}
+
+static void
+uhid_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ if (fflags & (FREAD | FWRITE)) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+static int
+uhid_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr,
+ int fflags, struct thread *td)
+{
+ struct uhid_softc *sc = fifo->priv_sc0;
+ struct usb2_gen_descriptor *ugd;
+ uint32_t size;
+ int error = 0;
+ uint8_t id;
+
+ switch (cmd) {
+ case USB_GET_REPORT_DESC:
+ ugd = addr;
+ if (sc->sc_repdesc_size > ugd->ugd_maxlen) {
+ size = ugd->ugd_maxlen;
+ } else {
+ size = sc->sc_repdesc_size;
+ }
+ ugd->ugd_actlen = size;
+ if (ugd->ugd_data == NULL)
+ break; /* descriptor length only */
+ error = copyout(sc->sc_repdesc_ptr, ugd->ugd_data, size);
+ break;
+
+ case USB_SET_IMMED:
+ if (!(fflags & FREAD)) {
+ error = EPERM;
+ break;
+ }
+ if (*(int *)addr) {
+
+ /* do a test read */
+
+ error = uhid_get_report(sc, UHID_INPUT_REPORT,
+ sc->sc_iid, NULL, NULL, sc->sc_isize);
+ if (error) {
+ break;
+ }
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_flags |= UHID_FLAG_IMMED;
+ mtx_unlock(&sc->sc_mtx);
+ } else {
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_flags &= ~UHID_FLAG_IMMED;
+ mtx_unlock(&sc->sc_mtx);
+ }
+ break;
+
+ case USB_GET_REPORT:
+ if (!(fflags & FREAD)) {
+ error = EPERM;
+ break;
+ }
+ ugd = addr;
+ switch (ugd->ugd_report_type) {
+ case UHID_INPUT_REPORT:
+ size = sc->sc_isize;
+ id = sc->sc_iid;
+ break;
+ case UHID_OUTPUT_REPORT:
+ size = sc->sc_osize;
+ id = sc->sc_oid;
+ break;
+ case UHID_FEATURE_REPORT:
+ size = sc->sc_fsize;
+ id = sc->sc_fid;
+ break;
+ default:
+ return (EINVAL);
+ }
+ error = uhid_get_report(sc, ugd->ugd_report_type, id,
+ NULL, ugd->ugd_data, size);
+ break;
+
+ case USB_SET_REPORT:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ break;
+ }
+ ugd = addr;
+ switch (ugd->ugd_report_type) {
+ case UHID_INPUT_REPORT:
+ size = sc->sc_isize;
+ id = sc->sc_iid;
+ break;
+ case UHID_OUTPUT_REPORT:
+ size = sc->sc_osize;
+ id = sc->sc_oid;
+ break;
+ case UHID_FEATURE_REPORT:
+ size = sc->sc_fsize;
+ id = sc->sc_fid;
+ break;
+ default:
+ return (EINVAL);
+ }
+ error = uhid_set_report(sc, ugd->ugd_report_type, id,
+ NULL, ugd->ugd_data, size);
+ break;
+
+ case USB_GET_REPORT_ID:
+ *(int *)addr = 0; /* XXX: we only support reportid 0? */
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static int
+uhid_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->use_generic == 0) {
+ /* give Mouse and Keyboard drivers a try first */
+ return (ENXIO);
+ }
+ if (uaa->info.bInterfaceClass != UICLASS_HID) {
+
+ /* the Xbox 360 gamepad doesn't use the HID class */
+
+ if ((uaa->info.bInterfaceClass != UICLASS_VENDOR) ||
+ (uaa->info.bInterfaceSubClass != UISUBCLASS_XBOX360_CONTROLLER) ||
+ (uaa->info.bInterfaceProtocol != UIPROTO_XBOX360_GAMEPAD)) {
+ return (ENXIO);
+ }
+ }
+ if (usb2_test_quirk(uaa, UQ_HID_IGNORE)) {
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static int
+uhid_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uhid_softc *sc = device_get_softc(dev);
+ int unit = device_get_unit(dev);
+ int error = 0;
+
+ DPRINTFN(10, "sc=%p\n", sc);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "uhid lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ sc->sc_udev = uaa->device;
+
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+
+ error = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, uhid_config,
+ UHID_N_TRANSFER, sc, &sc->sc_mtx);
+
+ if (error) {
+ DPRINTF("error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ if (uaa->info.idVendor == USB_VENDOR_WACOM) {
+
+ /* the report descriptor for the Wacom Graphire is broken */
+
+ if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE) {
+
+ sc->sc_repdesc_size = sizeof(uhid_graphire_report_descr);
+ sc->sc_repdesc_ptr = USB_ADD_BYTES(uhid_graphire_report_descr, 0);
+ sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+
+ } else if (uaa->info.idProduct == USB_PRODUCT_WACOM_GRAPHIRE3_4X5) {
+
+ static uint8_t reportbuf[] = {2, 2, 2};
+
+ /*
+ * The Graphire3 needs 0x0202 to be written to
+ * feature report ID 2 before it'll start
+ * returning digitizer data.
+ */
+ error = usb2_req_set_report
+ (uaa->device, &Giant, reportbuf, sizeof(reportbuf),
+ uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, 2);
+
+ if (error) {
+ DPRINTF("set report failed, error=%s (ignored)\n",
+ usb2_errstr(error));
+ }
+ sc->sc_repdesc_size = sizeof(uhid_graphire3_4x5_report_descr);
+ sc->sc_repdesc_ptr = USB_ADD_BYTES(uhid_graphire3_4x5_report_descr, 0);
+ sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+ }
+ } else if ((uaa->info.bInterfaceClass == UICLASS_VENDOR) &&
+ (uaa->info.bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER) &&
+ (uaa->info.bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) {
+
+ /* the Xbox 360 gamepad has no report descriptor */
+ sc->sc_repdesc_size = sizeof(uhid_xb360gp_report_descr);
+ sc->sc_repdesc_ptr = USB_ADD_BYTES(uhid_xb360gp_report_descr, 0);
+ sc->sc_flags |= UHID_FLAG_STATIC_DESC;
+ }
+ if (sc->sc_repdesc_ptr == NULL) {
+
+ error = usb2_req_get_hid_desc
+ (uaa->device, &Giant, &sc->sc_repdesc_ptr,
+ &sc->sc_repdesc_size, M_USBDEV, uaa->info.bIfaceIndex);
+
+ if (error) {
+ device_printf(dev, "no report descriptor\n");
+ goto detach;
+ }
+ }
+ error = usb2_req_set_idle(uaa->device, &Giant,
+ uaa->info.bIfaceIndex, 0, 0);
+
+ if (error) {
+ DPRINTF("set idle failed, error=%s (ignored)\n",
+ usb2_errstr(error));
+ }
+ sc->sc_isize = hid_report_size
+ (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_input, &sc->sc_iid);
+
+ sc->sc_osize = hid_report_size
+ (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_output, &sc->sc_oid);
+
+ sc->sc_fsize = hid_report_size
+ (sc->sc_repdesc_ptr, sc->sc_repdesc_size, hid_feature, &sc->sc_fid);
+
+ if (sc->sc_isize > UHID_BSIZE) {
+ DPRINTF("input size is too large, "
+ "%d bytes (truncating)\n",
+ sc->sc_isize);
+ sc->sc_isize = UHID_BSIZE;
+ }
+ if (sc->sc_osize > UHID_BSIZE) {
+ DPRINTF("output size is too large, "
+ "%d bytes (truncating)\n",
+ sc->sc_osize);
+ sc->sc_osize = UHID_BSIZE;
+ }
+ if (sc->sc_fsize > UHID_BSIZE) {
+ DPRINTF("feature size is too large, "
+ "%d bytes (truncating)\n",
+ sc->sc_fsize);
+ sc->sc_fsize = UHID_BSIZE;
+ }
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &uhid_fifo_methods, &sc->sc_fifo,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ uhid_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+uhid_detach(device_t dev)
+{
+ struct uhid_softc *sc = device_get_softc(dev);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UHID_N_TRANSFER);
+
+ if (sc->sc_repdesc_ptr) {
+ if (!(sc->sc_flags & UHID_FLAG_STATIC_DESC)) {
+ free(sc->sc_repdesc_ptr, M_USBDEV);
+ }
+ }
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static devclass_t uhid_devclass;
+
+static device_method_t uhid_methods[] = {
+ DEVMETHOD(device_probe, uhid_probe),
+ DEVMETHOD(device_attach, uhid_attach),
+ DEVMETHOD(device_detach, uhid_detach),
+ {0, 0}
+};
+
+static driver_t uhid_driver = {
+ .name = "uhid",
+ .methods = uhid_methods,
+ .size = sizeof(struct uhid_softc),
+};
+
+DRIVER_MODULE(uhid, ushub, uhid_driver, uhid_devclass, NULL, 0);
+MODULE_DEPEND(uhid, usb, 1, 1, 1);
diff --git a/sys/dev/usb/input/ukbd.c b/sys/dev/usb/input/ukbd.c
new file mode 100644
index 0000000..c8350264
--- /dev/null
+++ b/sys/dev/usb/input/ukbd.c
@@ -0,0 +1,1489 @@
+#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.
+ *
+ */
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "opt_compat.h"
+#include "opt_kbd.h"
+#include "opt_ukbd.h"
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR ukbd_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_hid.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/kbio.h>
+
+#include <dev/kbd/kbdreg.h>
+
+/* the initial key map, accent map and fkey strings */
+#if defined(UKBD_DFLT_KEYMAP) && !defined(KLD_MODULE)
+#define KBD_DFLT_KEYMAP
+#include "ukbdmap.h"
+#endif
+
+/* the following file must be included after "ukbdmap.h" */
+#include <dev/kbd/kbdtables.h>
+
+#if USB_DEBUG
+static int ukbd_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ukbd, CTLFLAG_RW, 0, "USB ukbd");
+SYSCTL_INT(_hw_usb2_ukbd, OID_AUTO, debug, CTLFLAG_RW,
+ &ukbd_debug, 0, "Debug level");
+#endif
+
+#define UPROTO_BOOT_KEYBOARD 1
+
+#define UKBD_EMULATE_ATSCANCODE 1
+#define UKBD_DRIVER_NAME "ukbd"
+#define UKBD_NMOD 8 /* units */
+#define UKBD_NKEYCODE 6 /* units */
+#define UKBD_IN_BUF_SIZE (2*(UKBD_NMOD + (2*UKBD_NKEYCODE))) /* bytes */
+#define UKBD_IN_BUF_FULL (UKBD_IN_BUF_SIZE / 2) /* bytes */
+#define UKBD_NFKEY (sizeof(fkey_tab)/sizeof(fkey_tab[0])) /* units */
+
+struct ukbd_data {
+ uint8_t modifiers;
+#define MOD_CONTROL_L 0x01
+#define MOD_CONTROL_R 0x10
+#define MOD_SHIFT_L 0x02
+#define MOD_SHIFT_R 0x20
+#define MOD_ALT_L 0x04
+#define MOD_ALT_R 0x40
+#define MOD_WIN_L 0x08
+#define MOD_WIN_R 0x80
+ uint8_t reserved;
+ uint8_t keycode[UKBD_NKEYCODE];
+} __packed;
+
+enum {
+ UKBD_INTR_DT,
+ UKBD_INTR_CS,
+ UKBD_CTRL_LED,
+ UKBD_N_TRANSFER = 3,
+};
+
+struct ukbd_softc {
+ keyboard_t sc_kbd;
+ keymap_t sc_keymap;
+ accentmap_t sc_accmap;
+ fkeytab_t sc_fkeymap[UKBD_NFKEY];
+ struct usb2_callout sc_callout;
+ struct ukbd_data sc_ndata;
+ struct ukbd_data sc_odata;
+
+ struct usb2_device *sc_udev;
+ struct usb2_interface *sc_iface;
+ struct usb2_xfer *sc_xfer[UKBD_N_TRANSFER];
+
+ uint32_t sc_ntime[UKBD_NKEYCODE];
+ uint32_t sc_otime[UKBD_NKEYCODE];
+ uint32_t sc_input[UKBD_IN_BUF_SIZE]; /* input buffer */
+ uint32_t sc_time_ms;
+ uint32_t sc_composed_char; /* composed char code, if non-zero */
+#ifdef UKBD_EMULATE_ATSCANCODE
+ uint32_t sc_buffered_char[2];
+#endif
+ uint32_t sc_flags; /* flags */
+#define UKBD_FLAG_COMPOSE 0x0001
+#define UKBD_FLAG_POLLING 0x0002
+#define UKBD_FLAG_SET_LEDS 0x0004
+#define UKBD_FLAG_INTR_STALL 0x0008
+#define UKBD_FLAG_ATTACHED 0x0010
+#define UKBD_FLAG_GONE 0x0020
+
+ int32_t sc_mode; /* input mode (K_XLATE,K_RAW,K_CODE) */
+ int32_t sc_state; /* shift/lock key state */
+ int32_t sc_accents; /* accent key index (> 0) */
+
+ uint16_t sc_inputs;
+ uint16_t sc_inputhead;
+ uint16_t sc_inputtail;
+
+ uint8_t sc_leds; /* store for async led requests */
+ uint8_t sc_iface_index;
+ uint8_t sc_iface_no;
+};
+
+#define KEY_ERROR 0x01
+
+#define KEY_PRESS 0
+#define KEY_RELEASE 0x400
+#define KEY_INDEX(c) ((c) & 0xFF)
+
+#define SCAN_PRESS 0
+#define SCAN_RELEASE 0x80
+#define SCAN_PREFIX_E0 0x100
+#define SCAN_PREFIX_E1 0x200
+#define SCAN_PREFIX_CTL 0x400
+#define SCAN_PREFIX_SHIFT 0x800
+#define SCAN_PREFIX (SCAN_PREFIX_E0 | SCAN_PREFIX_E1 | \
+ SCAN_PREFIX_CTL | SCAN_PREFIX_SHIFT)
+#define SCAN_CHAR(c) ((c) & 0x7f)
+
+struct ukbd_mods {
+ uint32_t mask, key;
+};
+
+static const struct ukbd_mods ukbd_mods[UKBD_NMOD] = {
+ {MOD_CONTROL_L, 0xe0},
+ {MOD_CONTROL_R, 0xe4},
+ {MOD_SHIFT_L, 0xe1},
+ {MOD_SHIFT_R, 0xe5},
+ {MOD_ALT_L, 0xe2},
+ {MOD_ALT_R, 0xe6},
+ {MOD_WIN_L, 0xe3},
+ {MOD_WIN_R, 0xe7},
+};
+
+#define NN 0 /* no translation */
+/*
+ * Translate USB keycodes to AT keyboard scancodes.
+ */
+/*
+ * FIXME: Mac USB keyboard generates:
+ * 0x53: keypad NumLock/Clear
+ * 0x66: Power
+ * 0x67: keypad =
+ * 0x68: F13
+ * 0x69: F14
+ * 0x6a: F15
+ */
+static const uint8_t ukbd_trtab[256] = {
+ 0, 0, 0, 0, 30, 48, 46, 32, /* 00 - 07 */
+ 18, 33, 34, 35, 23, 36, 37, 38, /* 08 - 0F */
+ 50, 49, 24, 25, 16, 19, 31, 20, /* 10 - 17 */
+ 22, 47, 17, 45, 21, 44, 2, 3, /* 18 - 1F */
+ 4, 5, 6, 7, 8, 9, 10, 11, /* 20 - 27 */
+ 28, 1, 14, 15, 57, 12, 13, 26, /* 28 - 2F */
+ 27, 43, 43, 39, 40, 41, 51, 52, /* 30 - 37 */
+ 53, 58, 59, 60, 61, 62, 63, 64, /* 38 - 3F */
+ 65, 66, 67, 68, 87, 88, 92, 70, /* 40 - 47 */
+ 104, 102, 94, 96, 103, 99, 101, 98, /* 48 - 4F */
+ 97, 100, 95, 69, 91, 55, 74, 78,/* 50 - 57 */
+ 89, 79, 80, 81, 75, 76, 77, 71, /* 58 - 5F */
+ 72, 73, 82, 83, 86, 107, 122, NN, /* 60 - 67 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 68 - 6F */
+ NN, NN, NN, NN, 115, 108, 111, 113, /* 70 - 77 */
+ 109, 110, 112, 118, 114, 116, 117, 119, /* 78 - 7F */
+ 121, 120, NN, NN, NN, NN, NN, 115, /* 80 - 87 */
+ 112, 125, 121, 123, NN, NN, NN, NN, /* 88 - 8F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 90 - 97 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9F */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A0 - A7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* A8 - AF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B0 - B7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* B8 - BF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C0 - C7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* C8 - CF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D0 - D7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* D8 - DF */
+ 29, 42, 56, 105, 90, 54, 93, 106, /* E0 - E7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* E8 - EF */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F0 - F7 */
+ NN, NN, NN, NN, NN, NN, NN, NN, /* F8 - FF */
+};
+
+/* prototypes */
+static void ukbd_timeout(void *);
+static void ukbd_set_leds(struct ukbd_softc *, uint8_t);
+static int ukbd_set_typematic(keyboard_t *, int);
+#ifdef UKBD_EMULATE_ATSCANCODE
+static int ukbd_key2scan(struct ukbd_softc *, int, int, int);
+#endif
+static uint32_t ukbd_read_char(keyboard_t *, int);
+static void ukbd_clear_state(keyboard_t *);
+static int ukbd_ioctl(keyboard_t *, u_long, caddr_t);
+static int ukbd_enable(keyboard_t *);
+static int ukbd_disable(keyboard_t *);
+static void ukbd_interrupt(struct ukbd_softc *);
+
+static device_probe_t ukbd_probe;
+static device_attach_t ukbd_attach;
+static device_detach_t ukbd_detach;
+static device_resume_t ukbd_resume;
+
+static void
+ukbd_put_key(struct ukbd_softc *sc, uint32_t key)
+{
+ mtx_assert(&Giant, MA_OWNED);
+
+ DPRINTF("0x%02x (%d) %s\n", key, key,
+ (key & KEY_RELEASE) ? "released" : "pressed");
+
+ if (sc->sc_inputs < UKBD_IN_BUF_SIZE) {
+ sc->sc_input[sc->sc_inputtail] = key;
+ ++(sc->sc_inputs);
+ ++(sc->sc_inputtail);
+ if (sc->sc_inputtail >= UKBD_IN_BUF_SIZE) {
+ sc->sc_inputtail = 0;
+ }
+ } else {
+ DPRINTF("input buffer is full\n");
+ }
+}
+
+static int32_t
+ukbd_get_key(struct ukbd_softc *sc, uint8_t wait)
+{
+ int32_t c;
+
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (sc->sc_inputs == 0) {
+ /* start transfer, if not already started */
+ usb2_transfer_start(sc->sc_xfer[UKBD_INTR_DT]);
+ }
+ if (sc->sc_flags & UKBD_FLAG_POLLING) {
+ DPRINTFN(2, "polling\n");
+
+ while (sc->sc_inputs == 0) {
+
+ usb2_do_poll(sc->sc_xfer, UKBD_N_TRANSFER);
+
+ DELAY(1000); /* delay 1 ms */
+
+ sc->sc_time_ms++;
+
+ /* support repetition of keys: */
+
+ ukbd_interrupt(sc);
+
+ if (!wait) {
+ break;
+ }
+ }
+ }
+ if (sc->sc_inputs == 0) {
+ c = -1;
+ } else {
+ c = sc->sc_input[sc->sc_inputhead];
+ --(sc->sc_inputs);
+ ++(sc->sc_inputhead);
+ if (sc->sc_inputhead >= UKBD_IN_BUF_SIZE) {
+ sc->sc_inputhead = 0;
+ }
+ }
+ return (c);
+}
+
+static void
+ukbd_interrupt(struct ukbd_softc *sc)
+{
+ uint32_t n_mod;
+ uint32_t o_mod;
+ uint32_t now = sc->sc_time_ms;
+ uint32_t dtime;
+ uint32_t c;
+ uint8_t key;
+ uint8_t i;
+ uint8_t j;
+
+ if (sc->sc_ndata.keycode[0] == KEY_ERROR) {
+ goto done;
+ }
+ n_mod = sc->sc_ndata.modifiers;
+ o_mod = sc->sc_odata.modifiers;
+ if (n_mod != o_mod) {
+ for (i = 0; i < UKBD_NMOD; i++) {
+ if ((n_mod & ukbd_mods[i].mask) !=
+ (o_mod & ukbd_mods[i].mask)) {
+ ukbd_put_key(sc, ukbd_mods[i].key |
+ ((n_mod & ukbd_mods[i].mask) ?
+ KEY_PRESS : KEY_RELEASE));
+ }
+ }
+ }
+ /* Check for released keys. */
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ key = sc->sc_odata.keycode[i];
+ if (key == 0) {
+ continue;
+ }
+ for (j = 0; j < UKBD_NKEYCODE; j++) {
+ if (sc->sc_ndata.keycode[j] == 0) {
+ continue;
+ }
+ if (key == sc->sc_ndata.keycode[j]) {
+ goto rfound;
+ }
+ }
+ ukbd_put_key(sc, key | KEY_RELEASE);
+rfound: ;
+ }
+
+ /* Check for pressed keys. */
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ key = sc->sc_ndata.keycode[i];
+ if (key == 0) {
+ continue;
+ }
+ sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay1;
+ for (j = 0; j < UKBD_NKEYCODE; j++) {
+ if (sc->sc_odata.keycode[j] == 0) {
+ continue;
+ }
+ if (key == sc->sc_odata.keycode[j]) {
+
+ /* key is still pressed */
+
+ sc->sc_ntime[i] = sc->sc_otime[j];
+ dtime = (sc->sc_otime[j] - now);
+
+ if (!(dtime & 0x80000000)) {
+ /* time has not elapsed */
+ goto pfound;
+ }
+ sc->sc_ntime[i] = now + sc->sc_kbd.kb_delay2;
+ break;
+ }
+ }
+ ukbd_put_key(sc, key | KEY_PRESS);
+
+ /*
+ * If any other key is presently down, force its repeat to be
+ * well in the future (100s). This makes the last key to be
+ * pressed do the autorepeat.
+ */
+ for (j = 0; j != UKBD_NKEYCODE; j++) {
+ if (j != i)
+ sc->sc_ntime[j] = now + (100 * 1000);
+ }
+pfound: ;
+ }
+
+ sc->sc_odata = sc->sc_ndata;
+
+ bcopy(sc->sc_ntime, sc->sc_otime, sizeof(sc->sc_otime));
+
+ if (sc->sc_inputs == 0) {
+ goto done;
+ }
+ if (sc->sc_flags & UKBD_FLAG_POLLING) {
+ goto done;
+ }
+ if (KBD_IS_ACTIVE(&sc->sc_kbd) &&
+ KBD_IS_BUSY(&sc->sc_kbd)) {
+ /* let the callback function process the input */
+ (sc->sc_kbd.kb_callback.kc_func) (&sc->sc_kbd, KBDIO_KEYINPUT,
+ sc->sc_kbd.kb_callback.kc_arg);
+ } else {
+ /* read and discard the input, no one is waiting for it */
+ do {
+ c = ukbd_read_char(&sc->sc_kbd, 0);
+ } while (c != NOKEY);
+ }
+done:
+ return;
+}
+
+static void
+ukbd_timeout(void *arg)
+{
+ struct ukbd_softc *sc = arg;
+
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (!(sc->sc_flags & UKBD_FLAG_POLLING)) {
+ sc->sc_time_ms += 25; /* milliseconds */
+ }
+ ukbd_interrupt(sc);
+
+ usb2_callout_reset(&sc->sc_callout, hz / 40, &ukbd_timeout, sc);
+}
+
+static void
+ukbd_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct ukbd_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[UKBD_INTR_DT];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~UKBD_FLAG_INTR_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+static void
+ukbd_intr_callback(struct usb2_xfer *xfer)
+{
+ struct ukbd_softc *sc = xfer->priv_sc;
+ uint16_t len = xfer->actlen;
+ uint8_t i;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("actlen=%d bytes\n", len);
+
+ if (len > sizeof(sc->sc_ndata)) {
+ len = sizeof(sc->sc_ndata);
+ }
+ if (len) {
+ bzero(&sc->sc_ndata, sizeof(sc->sc_ndata));
+ usb2_copy_out(xfer->frbuffers, 0, &sc->sc_ndata, len);
+#if USB_DEBUG
+ if (sc->sc_ndata.modifiers) {
+ DPRINTF("mod: 0x%04x\n", sc->sc_ndata.modifiers);
+ }
+ for (i = 0; i < UKBD_NKEYCODE; i++) {
+ if (sc->sc_ndata.keycode[i]) {
+ DPRINTF("[%d] = %d\n", i, sc->sc_ndata.keycode[i]);
+ }
+ }
+#endif /* USB_DEBUG */
+ ukbd_interrupt(sc);
+ }
+ case USB_ST_SETUP:
+ if (sc->sc_flags & UKBD_FLAG_INTR_STALL) {
+ usb2_transfer_start(sc->sc_xfer[UKBD_INTR_CS]);
+ return;
+ }
+ if (sc->sc_inputs < UKBD_IN_BUF_FULL) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ } else {
+ DPRINTF("input queue is full!\n");
+ }
+ return;
+
+ default: /* Error */
+ DPRINTF("error=%s\n", usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ sc->sc_flags |= UKBD_FLAG_INTR_STALL;
+ usb2_transfer_start(sc->sc_xfer[UKBD_INTR_CS]);
+ }
+ return;
+ }
+}
+
+static void
+ukbd_set_leds_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_device_request req;
+ uint8_t buf[1];
+ struct ukbd_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+ if (sc->sc_flags & UKBD_FLAG_SET_LEDS) {
+ sc->sc_flags &= ~UKBD_FLAG_SET_LEDS;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, UHID_OUTPUT_REPORT, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 1);
+
+ buf[0] = sc->sc_leds;
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = sizeof(buf);
+ xfer->nframes = 2;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ DPRINTFN(0, "error=%s\n", usb2_errstr(xfer->error));
+ return;
+ }
+}
+
+static const struct usb2_config ukbd_config[UKBD_N_TRANSFER] = {
+
+ [UKBD_INTR_DT] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &ukbd_intr_callback,
+ },
+
+ [UKBD_INTR_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.callback = &ukbd_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+
+ [UKBD_CTRL_LED] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request) + 1,
+ .mh.callback = &ukbd_set_leds_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static int
+ukbd_probe(device_t dev)
+{
+ keyboard_switch_t *sw = kbd_get_switch(UKBD_DRIVER_NAME);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (sw == NULL) {
+ return (ENXIO);
+ }
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ /* check that the keyboard speaks the boot protocol: */
+ if ((uaa->info.bInterfaceClass == UICLASS_HID)
+ && (uaa->info.bInterfaceSubClass == UISUBCLASS_BOOT)
+ && (uaa->info.bInterfaceProtocol == UPROTO_BOOT_KEYBOARD)) {
+ if (usb2_test_quirk(uaa, UQ_KBD_IGNORE))
+ return (ENXIO);
+ else
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+ukbd_attach(device_t dev)
+{
+ struct ukbd_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ int32_t unit = device_get_unit(dev);
+ keyboard_t *kbd = &sc->sc_kbd;
+ usb2_error_t err;
+ uint16_t n;
+
+ mtx_assert(&Giant, MA_OWNED);
+
+ kbd_init_struct(kbd, UKBD_DRIVER_NAME, KB_OTHER, unit, 0, 0, 0);
+
+ kbd->kb_data = (void *)sc;
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+ sc->sc_iface = uaa->iface;
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_mode = K_XLATE;
+ sc->sc_iface = uaa->iface;
+
+ usb2_callout_init_mtx(&sc->sc_callout, &Giant, 0);
+
+ err = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, ukbd_config,
+ UKBD_N_TRANSFER, sc, &Giant);
+
+ if (err) {
+ DPRINTF("error=%s\n", usb2_errstr(err));
+ goto detach;
+ }
+ /* setup default keyboard maps */
+
+ sc->sc_keymap = key_map;
+ sc->sc_accmap = accent_map;
+ for (n = 0; n < UKBD_NFKEY; n++) {
+ sc->sc_fkeymap[n] = fkey_tab[n];
+ }
+
+ kbd_set_maps(kbd, &sc->sc_keymap, &sc->sc_accmap,
+ sc->sc_fkeymap, UKBD_NFKEY);
+
+ KBD_FOUND_DEVICE(kbd);
+
+ ukbd_clear_state(kbd);
+
+ /*
+ * FIXME: set the initial value for lock keys in "sc_state"
+ * according to the BIOS data?
+ */
+ KBD_PROBE_DONE(kbd);
+
+ /* ignore if SETIDLE fails, hence it is not crucial */
+ err = usb2_req_set_idle(sc->sc_udev, &Giant, sc->sc_iface_index, 0, 0);
+
+ ukbd_ioctl(kbd, KDSETLED, (caddr_t)&sc->sc_state);
+
+ KBD_INIT_DONE(kbd);
+
+ if (kbd_register(kbd) < 0) {
+ goto detach;
+ }
+ KBD_CONFIG_DONE(kbd);
+
+ ukbd_enable(kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (kbd_attach(kbd)) {
+ goto detach;
+ }
+#endif
+ sc->sc_flags |= UKBD_FLAG_ATTACHED;
+
+ if (bootverbose) {
+ genkbd_diag(kbd, bootverbose);
+ }
+ /* lock keyboard mutex */
+
+ mtx_lock(&Giant);
+
+ /* start the keyboard */
+
+ usb2_transfer_start(sc->sc_xfer[UKBD_INTR_DT]);
+
+ /* start the timer */
+
+ ukbd_timeout(sc);
+ mtx_unlock(&Giant);
+ return (0); /* success */
+
+detach:
+ ukbd_detach(dev);
+ return (ENXIO); /* error */
+}
+
+int
+ukbd_detach(device_t dev)
+{
+ struct ukbd_softc *sc = device_get_softc(dev);
+ int error;
+
+ mtx_assert(&Giant, MA_OWNED);
+
+ DPRINTF("\n");
+
+ if (sc->sc_flags & UKBD_FLAG_POLLING) {
+ panic("cannot detach polled keyboard!\n");
+ }
+ sc->sc_flags |= UKBD_FLAG_GONE;
+
+ usb2_callout_stop(&sc->sc_callout);
+
+ ukbd_disable(&sc->sc_kbd);
+
+#ifdef KBD_INSTALL_CDEV
+ if (sc->sc_flags & UKBD_FLAG_ATTACHED) {
+ error = kbd_detach(&sc->sc_kbd);
+ if (error) {
+ /* usb attach cannot return an error */
+ device_printf(dev, "WARNING: kbd_detach() "
+ "returned non-zero! (ignored)\n");
+ }
+ }
+#endif
+ if (KBD_IS_CONFIGURED(&sc->sc_kbd)) {
+ error = kbd_unregister(&sc->sc_kbd);
+ if (error) {
+ /* usb attach cannot return an error */
+ device_printf(dev, "WARNING: kbd_unregister() "
+ "returned non-zero! (ignored)\n");
+ }
+ }
+ sc->sc_kbd.kb_flags = 0;
+
+ usb2_transfer_unsetup(sc->sc_xfer, UKBD_N_TRANSFER);
+
+ usb2_callout_drain(&sc->sc_callout);
+
+ DPRINTF("%s: disconnected\n",
+ device_get_nameunit(dev));
+
+ return (0);
+}
+
+static int
+ukbd_resume(device_t dev)
+{
+ struct ukbd_softc *sc = device_get_softc(dev);
+
+ mtx_assert(&Giant, MA_OWNED);
+
+ ukbd_clear_state(&sc->sc_kbd);
+
+ return (0);
+}
+
+/* early keyboard probe, not supported */
+static int
+ukbd_configure(int flags)
+{
+ return (0);
+}
+
+/* detect a keyboard, not used */
+static int
+ukbd__probe(int unit, void *arg, int flags)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (ENXIO);
+}
+
+/* reset and initialize the device, not used */
+static int
+ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (ENXIO);
+}
+
+/* test the interface to the device, not used */
+static int
+ukbd_test_if(keyboard_t *kbd)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (0);
+}
+
+/* finish using this keyboard, not used */
+static int
+ukbd_term(keyboard_t *kbd)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (ENXIO);
+}
+
+/* keyboard interrupt routine, not used */
+static int
+ukbd_intr(keyboard_t *kbd, void *arg)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (0);
+}
+
+/* lock the access to the keyboard, not used */
+static int
+ukbd_lock(keyboard_t *kbd, int lock)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (1);
+}
+
+/*
+ * Enable the access to the device; until this function is called,
+ * the client cannot read from the keyboard.
+ */
+static int
+ukbd_enable(keyboard_t *kbd)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ KBD_ACTIVATE(kbd);
+ return (0);
+}
+
+/* disallow the access to the device */
+static int
+ukbd_disable(keyboard_t *kbd)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ KBD_DEACTIVATE(kbd);
+ return (0);
+}
+
+/* check if data is waiting */
+static int
+ukbd_check(keyboard_t *kbd)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ if (!mtx_owned(&Giant)) {
+ return (0); /* XXX */
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (!KBD_IS_ACTIVE(kbd)) {
+ return (0);
+ }
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (sc->sc_buffered_char[0]) {
+ return (1);
+ }
+#endif
+ if (sc->sc_inputs > 0) {
+ return (1);
+ }
+ return (0);
+}
+
+/* check if char is waiting */
+static int
+ukbd_check_char(keyboard_t *kbd)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ if (!mtx_owned(&Giant)) {
+ return (0); /* XXX */
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (!KBD_IS_ACTIVE(kbd)) {
+ return (0);
+ }
+ if ((sc->sc_composed_char > 0) &&
+ (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
+ return (1);
+ }
+ return (ukbd_check(kbd));
+}
+
+
+/* read one byte from the keyboard if it's allowed */
+static int
+ukbd_read(keyboard_t *kbd, int wait)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+ int32_t usbcode;
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ uint32_t keycode;
+ uint32_t scancode;
+
+#endif
+
+ if (!mtx_owned(&Giant)) {
+ return -1; /* XXX */
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ if (sc->sc_buffered_char[0]) {
+ scancode = sc->sc_buffered_char[0];
+ if (scancode & SCAN_PREFIX) {
+ sc->sc_buffered_char[0] &= ~SCAN_PREFIX;
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
+ sc->sc_buffered_char[1] = 0;
+ return (scancode);
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* XXX */
+ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
+ if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) {
+ return -1;
+ }
+ ++(kbd->kb_count);
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN) {
+ return -1;
+ }
+ return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
+ (usbcode & KEY_RELEASE)));
+#else /* !UKBD_EMULATE_ATSCANCODE */
+ return (usbcode);
+#endif /* UKBD_EMULATE_ATSCANCODE */
+}
+
+/* read char from the keyboard */
+static uint32_t
+ukbd_read_char(keyboard_t *kbd, int wait)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+ uint32_t action;
+ uint32_t keycode;
+ int32_t usbcode;
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ uint32_t scancode;
+
+#endif
+ if (!mtx_owned(&Giant)) {
+ return (NOKEY); /* XXX */
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+next_code:
+
+ /* do we have a composed char to return ? */
+
+ if ((sc->sc_composed_char > 0) &&
+ (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) {
+
+ action = sc->sc_composed_char;
+ sc->sc_composed_char = 0;
+
+ if (action > 0xFF) {
+ goto errkey;
+ }
+ goto done;
+ }
+#ifdef UKBD_EMULATE_ATSCANCODE
+
+ /* do we have a pending raw scan code? */
+
+ if (sc->sc_mode == K_RAW) {
+ scancode = sc->sc_buffered_char[0];
+ if (scancode) {
+ if (scancode & SCAN_PREFIX) {
+ sc->sc_buffered_char[0] = (scancode & ~SCAN_PREFIX);
+ return ((scancode & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ sc->sc_buffered_char[0] = sc->sc_buffered_char[1];
+ sc->sc_buffered_char[1] = 0;
+ return (scancode);
+ }
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ /* see if there is something in the keyboard port */
+ /* XXX */
+ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1);
+ if (usbcode == -1) {
+ return (NOKEY);
+ }
+ ++kbd->kb_count;
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+ /* USB key index -> key code -> AT scan code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN) {
+ return (NOKEY);
+ }
+ /* return an AT scan code for the K_RAW mode */
+ if (sc->sc_mode == K_RAW) {
+ return (ukbd_key2scan(sc, keycode, sc->sc_ndata.modifiers,
+ (usbcode & KEY_RELEASE)));
+ }
+#else /* !UKBD_EMULATE_ATSCANCODE */
+
+ /* return the byte as is for the K_RAW mode */
+ if (sc->sc_mode == K_RAW) {
+ return (usbcode);
+ }
+ /* USB key index -> key code */
+ keycode = ukbd_trtab[KEY_INDEX(usbcode)];
+ if (keycode == NN) {
+ return (NOKEY);
+ }
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+ switch (keycode) {
+ case 0x38: /* left alt (compose key) */
+ if (usbcode & KEY_RELEASE) {
+ if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
+ sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
+
+ if (sc->sc_composed_char > 0xFF) {
+ sc->sc_composed_char = 0;
+ }
+ }
+ } else {
+ if (!(sc->sc_flags & UKBD_FLAG_COMPOSE)) {
+ sc->sc_flags |= UKBD_FLAG_COMPOSE;
+ sc->sc_composed_char = 0;
+ }
+ }
+ break;
+ /* XXX: I don't like these... */
+ case 0x5c: /* print screen */
+ if (sc->sc_flags & ALTS) {
+ keycode = 0x54; /* sysrq */
+ }
+ break;
+ case 0x68: /* pause/break */
+ if (sc->sc_flags & CTLS) {
+ keycode = 0x6c; /* break */
+ }
+ break;
+ }
+
+ /* return the key code in the K_CODE mode */
+ if (usbcode & KEY_RELEASE) {
+ keycode |= SCAN_RELEASE;
+ }
+ if (sc->sc_mode == K_CODE) {
+ return (keycode);
+ }
+ /* compose a character code */
+ if (sc->sc_flags & UKBD_FLAG_COMPOSE) {
+ switch (keycode) {
+ /* key pressed, process it */
+ case 0x47:
+ case 0x48:
+ case 0x49: /* keypad 7,8,9 */
+ sc->sc_composed_char *= 10;
+ sc->sc_composed_char += keycode - 0x40;
+ goto check_composed;
+
+ case 0x4B:
+ case 0x4C:
+ case 0x4D: /* keypad 4,5,6 */
+ sc->sc_composed_char *= 10;
+ sc->sc_composed_char += keycode - 0x47;
+ goto check_composed;
+
+ case 0x4F:
+ case 0x50:
+ case 0x51: /* keypad 1,2,3 */
+ sc->sc_composed_char *= 10;
+ sc->sc_composed_char += keycode - 0x4E;
+ goto check_composed;
+
+ case 0x52: /* keypad 0 */
+ sc->sc_composed_char *= 10;
+ goto check_composed;
+
+ /* key released, no interest here */
+ case SCAN_RELEASE | 0x47:
+ case SCAN_RELEASE | 0x48:
+ case SCAN_RELEASE | 0x49: /* keypad 7,8,9 */
+ case SCAN_RELEASE | 0x4B:
+ case SCAN_RELEASE | 0x4C:
+ case SCAN_RELEASE | 0x4D: /* keypad 4,5,6 */
+ case SCAN_RELEASE | 0x4F:
+ case SCAN_RELEASE | 0x50:
+ case SCAN_RELEASE | 0x51: /* keypad 1,2,3 */
+ case SCAN_RELEASE | 0x52: /* keypad 0 */
+ goto next_code;
+
+ case 0x38: /* left alt key */
+ break;
+
+ default:
+ if (sc->sc_composed_char > 0) {
+ sc->sc_flags &= ~UKBD_FLAG_COMPOSE;
+ sc->sc_composed_char = 0;
+ goto errkey;
+ }
+ break;
+ }
+ }
+ /* keycode to key action */
+ action = genkbd_keyaction(kbd, SCAN_CHAR(keycode),
+ (keycode & SCAN_RELEASE),
+ &sc->sc_state, &sc->sc_accents);
+ if (action == NOKEY) {
+ goto next_code;
+ }
+done:
+ return (action);
+
+check_composed:
+ if (sc->sc_composed_char <= 0xFF) {
+ goto next_code;
+ }
+errkey:
+ return (ERRKEY);
+}
+
+/* some useful control functions */
+static int
+ukbd_ioctl(keyboard_t *kbd, u_long cmd, caddr_t arg)
+{
+ /* translate LED_XXX bits into the device specific bits */
+ static const uint8_t ledmap[8] = {
+ 0, 2, 1, 3, 4, 6, 5, 7,
+ };
+ struct ukbd_softc *sc = kbd->kb_data;
+ int i;
+
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ int ival;
+
+#endif
+ if (!mtx_owned(&Giant)) {
+ /*
+ * XXX big problem: If scroll lock is pressed and "printf()"
+ * is called, the CPU will get here, to un-scroll lock the
+ * keyboard. But if "printf()" acquires the "Giant" lock,
+ * there will be a locking order reversal problem, so the
+ * keyboard system must get out of "Giant" first, before the
+ * CPU can proceed here ...
+ */
+ return (EINVAL);
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ switch (cmd) {
+ case KDGKBMODE: /* get keyboard mode */
+ *(int *)arg = sc->sc_mode;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 7):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBMODE: /* set keyboard mode */
+ switch (*(int *)arg) {
+ case K_XLATE:
+ if (sc->sc_mode != K_XLATE) {
+ /* make lock key state and LED state match */
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= KBD_LED_VAL(kbd);
+ }
+ /* FALLTHROUGH */
+ case K_RAW:
+ case K_CODE:
+ if (sc->sc_mode != *(int *)arg) {
+ ukbd_clear_state(kbd);
+ sc->sc_mode = *(int *)arg;
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case KDGETLED: /* get keyboard LED */
+ *(int *)arg = KBD_LED_VAL(kbd);
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 66):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETLED: /* set keyboard LED */
+ /* NOTE: lock key state in "sc_state" won't be changed */
+ if (*(int *)arg & ~LOCK_MASK) {
+ return (EINVAL);
+ }
+ i = *(int *)arg;
+ /* replace CAPS LED with ALTGR LED for ALTGR keyboards */
+ if (sc->sc_mode == K_XLATE &&
+ kbd->kb_keymap->n_keys > ALTGR_OFFSET) {
+ if (i & ALKED)
+ i |= CLKED;
+ else
+ i &= ~CLKED;
+ }
+ if (KBD_HAS_DEVICE(kbd)) {
+ ukbd_set_leds(sc, ledmap[i & LED_MASK]);
+ }
+ KBD_LED_VAL(kbd) = *(int *)arg;
+ break;
+ case KDGKBSTATE: /* get lock key state */
+ *(int *)arg = sc->sc_state & LOCK_MASK;
+ break;
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 20):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSKBSTATE: /* set lock key state */
+ if (*(int *)arg & ~LOCK_MASK) {
+ return (EINVAL);
+ }
+ sc->sc_state &= ~LOCK_MASK;
+ sc->sc_state |= *(int *)arg;
+
+ /* set LEDs and quit */
+ return (ukbd_ioctl(kbd, KDSETLED, arg));
+
+ case KDSETREPEAT: /* set keyboard repeat rate (new
+ * interface) */
+ if (!KBD_HAS_DEVICE(kbd)) {
+ return (0);
+ }
+ if (((int *)arg)[1] < 0) {
+ return (EINVAL);
+ }
+ if (((int *)arg)[0] < 0) {
+ return (EINVAL);
+ }
+ if (((int *)arg)[0] < 200) /* fastest possible value */
+ kbd->kb_delay1 = 200;
+ else
+ kbd->kb_delay1 = ((int *)arg)[0];
+ kbd->kb_delay2 = ((int *)arg)[1];
+ return (0);
+
+#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \
+ defined(COMPAT_FREEBSD4) || defined(COMPAT_43)
+ case _IO('K', 67):
+ ival = IOCPARM_IVAL(arg);
+ arg = (caddr_t)&ival;
+ /* FALLTHROUGH */
+#endif
+ case KDSETRAD: /* set keyboard repeat rate (old
+ * interface) */
+ return (ukbd_set_typematic(kbd, *(int *)arg));
+
+ case PIO_KEYMAP: /* set keyboard translation table */
+ case PIO_KEYMAPENT: /* set keyboard translation table
+ * entry */
+ case PIO_DEADKEYMAP: /* set accent key translation table */
+ sc->sc_accents = 0;
+ /* FALLTHROUGH */
+ default:
+ return (genkbd_commonioctl(kbd, cmd, arg));
+ }
+
+ return (0);
+}
+
+/* clear the internal state of the keyboard */
+static void
+ukbd_clear_state(keyboard_t *kbd)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ if (!mtx_owned(&Giant)) {
+ return; /* XXX */
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ sc->sc_flags &= ~(UKBD_FLAG_COMPOSE | UKBD_FLAG_POLLING);
+ sc->sc_state &= LOCK_MASK; /* preserve locking key state */
+ sc->sc_accents = 0;
+ sc->sc_composed_char = 0;
+#ifdef UKBD_EMULATE_ATSCANCODE
+ sc->sc_buffered_char[0] = 0;
+ sc->sc_buffered_char[1] = 0;
+#endif
+ bzero(&sc->sc_ndata, sizeof(sc->sc_ndata));
+ bzero(&sc->sc_odata, sizeof(sc->sc_odata));
+ bzero(&sc->sc_ntime, sizeof(sc->sc_ntime));
+ bzero(&sc->sc_otime, sizeof(sc->sc_otime));
+}
+
+/* save the internal state, not used */
+static int
+ukbd_get_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (len == 0) ? 1 : -1;
+}
+
+/* set the internal state, not used */
+static int
+ukbd_set_state(keyboard_t *kbd, void *buf, size_t len)
+{
+ mtx_assert(&Giant, MA_OWNED);
+ return (EINVAL);
+}
+
+static int
+ukbd_poll(keyboard_t *kbd, int on)
+{
+ struct ukbd_softc *sc = kbd->kb_data;
+
+ if (!mtx_owned(&Giant)) {
+ return (0); /* XXX */
+ }
+ mtx_assert(&Giant, MA_OWNED);
+
+ if (on) {
+ sc->sc_flags |= UKBD_FLAG_POLLING;
+ } else {
+ sc->sc_flags &= ~UKBD_FLAG_POLLING;
+ }
+ return (0);
+}
+
+/* local functions */
+
+static void
+ukbd_set_leds(struct ukbd_softc *sc, uint8_t leds)
+{
+ DPRINTF("leds=0x%02x\n", leds);
+
+ sc->sc_leds = leds;
+ sc->sc_flags |= UKBD_FLAG_SET_LEDS;
+
+ /* start transfer, if not already started */
+
+ usb2_transfer_start(sc->sc_xfer[UKBD_CTRL_LED]);
+}
+
+static int
+ukbd_set_typematic(keyboard_t *kbd, int code)
+{
+ static const int delays[] = {250, 500, 750, 1000};
+ static const int rates[] = {34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504};
+
+ if (code & ~0x7f) {
+ return (EINVAL);
+ }
+ kbd->kb_delay1 = delays[(code >> 5) & 3];
+ kbd->kb_delay2 = rates[code & 0x1f];
+ return (0);
+}
+
+#ifdef UKBD_EMULATE_ATSCANCODE
+static int
+ukbd_key2scan(struct ukbd_softc *sc, int code, int shift, int up)
+{
+ static const int scan[] = {
+ 0x1c, 0x1d, 0x35,
+ 0x37 | SCAN_PREFIX_SHIFT, /* PrintScreen */
+ 0x38, 0x47, 0x48, 0x49, 0x4b, 0x4d, 0x4f,
+ 0x50, 0x51, 0x52, 0x53,
+ 0x46, /* XXX Pause/Break */
+ 0x5b, 0x5c, 0x5d,
+ /* SUN TYPE 6 USB KEYBOARD */
+ 0x68, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
+ 0x64, 0x65, 0x66, 0x67, 0x25, 0x1f, 0x1e,
+ 0x20,
+ };
+
+ if ((code >= 89) && (code < (89 + (sizeof(scan) / sizeof(scan[0]))))) {
+ code = scan[code - 89] | SCAN_PREFIX_E0;
+ }
+ /* Pause/Break */
+ if ((code == 104) && (!(shift & (MOD_CONTROL_L | MOD_CONTROL_R)))) {
+ code = (0x45 | SCAN_PREFIX_E1 | SCAN_PREFIX_CTL);
+ }
+ if (shift & (MOD_SHIFT_L | MOD_SHIFT_R)) {
+ code &= ~SCAN_PREFIX_SHIFT;
+ }
+ code |= (up ? SCAN_RELEASE : SCAN_PRESS);
+
+ if (code & SCAN_PREFIX) {
+ if (code & SCAN_PREFIX_CTL) {
+ /* Ctrl */
+ sc->sc_buffered_char[0] = (0x1d | (code & SCAN_RELEASE));
+ sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX);
+ } else if (code & SCAN_PREFIX_SHIFT) {
+ /* Shift */
+ sc->sc_buffered_char[0] = (0x2a | (code & SCAN_RELEASE));
+ sc->sc_buffered_char[1] = (code & ~SCAN_PREFIX_SHIFT);
+ } else {
+ sc->sc_buffered_char[0] = (code & ~SCAN_PREFIX);
+ sc->sc_buffered_char[1] = 0;
+ }
+ return ((code & SCAN_PREFIX_E0) ? 0xe0 : 0xe1);
+ }
+ return (code);
+
+}
+
+#endif /* UKBD_EMULATE_ATSCANCODE */
+
+keyboard_switch_t ukbdsw = {
+ .probe = &ukbd__probe,
+ .init = &ukbd_init,
+ .term = &ukbd_term,
+ .intr = &ukbd_intr,
+ .test_if = &ukbd_test_if,
+ .enable = &ukbd_enable,
+ .disable = &ukbd_disable,
+ .read = &ukbd_read,
+ .check = &ukbd_check,
+ .read_char = &ukbd_read_char,
+ .check_char = &ukbd_check_char,
+ .ioctl = &ukbd_ioctl,
+ .lock = &ukbd_lock,
+ .clear_state = &ukbd_clear_state,
+ .get_state = &ukbd_get_state,
+ .set_state = &ukbd_set_state,
+ .get_fkeystr = &genkbd_get_fkeystr,
+ .poll = &ukbd_poll,
+ .diag = &genkbd_diag,
+};
+
+KEYBOARD_DRIVER(ukbd, ukbdsw, ukbd_configure);
+
+static int
+ukbd_driver_load(module_t mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ kbd_add_driver(&ukbd_kbd_driver);
+ break;
+ case MOD_UNLOAD:
+ kbd_delete_driver(&ukbd_kbd_driver);
+ break;
+ }
+ return (0);
+}
+
+static devclass_t ukbd_devclass;
+
+static device_method_t ukbd_methods[] = {
+ DEVMETHOD(device_probe, ukbd_probe),
+ DEVMETHOD(device_attach, ukbd_attach),
+ DEVMETHOD(device_detach, ukbd_detach),
+ DEVMETHOD(device_resume, ukbd_resume),
+ {0, 0}
+};
+
+static driver_t ukbd_driver = {
+ .name = "ukbd",
+ .methods = ukbd_methods,
+ .size = sizeof(struct ukbd_softc),
+};
+
+DRIVER_MODULE(ukbd, ushub, ukbd_driver, ukbd_devclass, ukbd_driver_load, 0);
+MODULE_DEPEND(ukbd, usb, 1, 1, 1);
diff --git a/sys/dev/usb/input/ums.c b/sys/dev/usb/input/ums.c
new file mode 100644
index 0000000..b064aa9
--- /dev/null
+++ b/sys/dev/usb/input/ums.c
@@ -0,0 +1,901 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR ums_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_hid.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#include <sys/ioccom.h>
+#include <sys/filio.h>
+#include <sys/tty.h>
+#include <sys/mouse.h>
+
+#if USB_DEBUG
+static int ums_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ums, CTLFLAG_RW, 0, "USB ums");
+SYSCTL_INT(_hw_usb2_ums, OID_AUTO, debug, CTLFLAG_RW,
+ &ums_debug, 0, "Debug level");
+#endif
+
+#define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE)
+#define MOUSE_FLAGS (HIO_RELATIVE)
+
+#define UMS_BUF_SIZE 8 /* bytes */
+#define UMS_IFQ_MAXLEN 50 /* units */
+#define UMS_BUTTON_MAX 31 /* exclusive, must be less than 32 */
+#define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i))
+
+enum {
+ UMS_INTR_DT,
+ UMS_INTR_CS,
+ UMS_N_TRANSFER = 2,
+};
+
+struct ums_softc {
+ struct usb2_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+ struct usb2_callout sc_callout;
+ struct hid_location sc_loc_w;
+ struct hid_location sc_loc_x;
+ struct hid_location sc_loc_y;
+ struct hid_location sc_loc_z;
+ struct hid_location sc_loc_t;
+ struct hid_location sc_loc_btn[UMS_BUTTON_MAX];
+ mousehw_t sc_hw;
+ mousemode_t sc_mode;
+ mousestatus_t sc_status;
+
+ struct usb2_xfer *sc_xfer[UMS_N_TRANSFER];
+
+ uint32_t sc_flags;
+#define UMS_FLAG_X_AXIS 0x0001
+#define UMS_FLAG_Y_AXIS 0x0002
+#define UMS_FLAG_Z_AXIS 0x0004
+#define UMS_FLAG_T_AXIS 0x0008
+#define UMS_FLAG_SBU 0x0010 /* spurious button up events */
+#define UMS_FLAG_INTR_STALL 0x0020 /* set if transfer error */
+#define UMS_FLAG_REVZ 0x0040 /* Z-axis is reversed */
+#define UMS_FLAG_W_AXIS 0x0080
+
+ uint8_t sc_buttons;
+ uint8_t sc_iid;
+ uint8_t sc_temp[64];
+};
+
+static void ums_put_queue_timeout(void *__sc);
+
+static usb2_callback_t ums_clear_stall_callback;
+static usb2_callback_t ums_intr_callback;
+
+static device_probe_t ums_probe;
+static device_attach_t ums_attach;
+static device_detach_t ums_detach;
+
+static usb2_fifo_cmd_t ums_start_read;
+static usb2_fifo_cmd_t ums_stop_read;
+static usb2_fifo_open_t ums_open;
+static usb2_fifo_close_t ums_close;
+static usb2_fifo_ioctl_t ums_ioctl;
+
+static void ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, int32_t dz, int32_t dt, int32_t buttons);
+
+static struct usb2_fifo_methods ums_fifo_methods = {
+ .f_open = &ums_open,
+ .f_close = &ums_close,
+ .f_ioctl = &ums_ioctl,
+ .f_start_read = &ums_start_read,
+ .f_stop_read = &ums_stop_read,
+ .basename[0] = "ums",
+};
+
+static void
+ums_put_queue_timeout(void *__sc)
+{
+ struct ums_softc *sc = __sc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ ums_put_queue(sc, 0, 0, 0, 0, 0);
+}
+
+static void
+ums_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct ums_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[UMS_INTR_DT];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~UMS_FLAG_INTR_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+static void
+ums_intr_callback(struct usb2_xfer *xfer)
+{
+ struct ums_softc *sc = xfer->priv_sc;
+ uint8_t *buf = sc->sc_temp;
+ uint16_t len = xfer->actlen;
+ int32_t buttons = 0;
+ int32_t dw;
+ int32_t dx;
+ int32_t dy;
+ int32_t dz;
+ int32_t dt;
+ uint8_t i;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
+
+ if (len > sizeof(sc->sc_temp)) {
+ DPRINTFN(6, "truncating large packet to %zu bytes\n",
+ sizeof(sc->sc_temp));
+ len = sizeof(sc->sc_temp);
+ }
+ if (len == 0) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, len);
+
+ DPRINTFN(6, "data = %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n",
+ (len > 0) ? buf[0] : 0, (len > 1) ? buf[1] : 0,
+ (len > 2) ? buf[2] : 0, (len > 3) ? buf[3] : 0,
+ (len > 4) ? buf[4] : 0, (len > 5) ? buf[5] : 0,
+ (len > 6) ? buf[6] : 0, (len > 7) ? buf[7] : 0);
+
+ /*
+ * The M$ Wireless Intellimouse 2.0 sends 1 extra leading byte
+ * of data compared to most USB mice. This byte frequently
+ * switches from 0x01 (usual state) to 0x02. I assume it is to
+ * allow extra, non-standard, reporting (say battery-life).
+ *
+ * However at the same time it generates a left-click message
+ * on the button byte which causes spurious left-click's where
+ * there shouldn't be. This should sort that. Currently it's
+ * the only user of UMS_FLAG_T_AXIS so use it as an
+ * identifier.
+ *
+ *
+ * UPDATE: This problem affects the M$ Wireless Notebook Optical Mouse,
+ * too. However, the leading byte for this mouse is normally 0x11,
+ * and the phantom mouse click occurs when its 0x14.
+ *
+ * We probably should switch to some more official quirk.
+ */
+ if (sc->sc_iid) {
+ if (sc->sc_flags & UMS_FLAG_T_AXIS) {
+ if (*buf == 0x02) {
+ goto tr_setup;
+ }
+ } else {
+ if (*buf != sc->sc_iid) {
+ goto tr_setup;
+ }
+ }
+
+ len--;
+ buf++;
+
+ } else {
+ if (sc->sc_flags & UMS_FLAG_SBU) {
+ if ((*buf == 0x14) || (*buf == 0x15)) {
+ goto tr_setup;
+ }
+ }
+ }
+
+ dw = (sc->sc_flags & UMS_FLAG_W_AXIS) ?
+ hid_get_data(buf, len, &sc->sc_loc_w) : 0;
+
+ dx = (sc->sc_flags & UMS_FLAG_X_AXIS) ?
+ hid_get_data(buf, len, &sc->sc_loc_x) : 0;
+
+ dy = (sc->sc_flags & UMS_FLAG_Y_AXIS) ?
+ -hid_get_data(buf, len, &sc->sc_loc_y) : 0;
+
+ dz = (sc->sc_flags & UMS_FLAG_Z_AXIS) ?
+ -hid_get_data(buf, len, &sc->sc_loc_z) : 0;
+
+ if (sc->sc_flags & UMS_FLAG_REVZ) {
+ dz = -dz;
+ }
+ dt = (sc->sc_flags & UMS_FLAG_T_AXIS) ?
+ -hid_get_data(buf, len, &sc->sc_loc_t): 0;
+
+ for (i = 0; i < sc->sc_buttons; i++) {
+ if (hid_get_data(buf, len, &sc->sc_loc_btn[i])) {
+ buttons |= (1 << UMS_BUT(i));
+ }
+ }
+
+ if (dx || dy || dz || dt || dw ||
+ (buttons != sc->sc_status.button)) {
+
+ DPRINTFN(6, "x:%d y:%d z:%d t:%d w:%d buttons:0x%08x\n",
+ dx, dy, dz, dt, dw, buttons);
+
+ sc->sc_status.button = buttons;
+ sc->sc_status.dx += dx;
+ sc->sc_status.dy += dy;
+ sc->sc_status.dz += dz;
+ /*
+ * sc->sc_status.dt += dt;
+ * no way to export this yet
+ */
+
+ /*
+ * The Qtronix keyboard has a built in PS/2 port for a mouse.
+ * The firmware once in a while posts a spurious button up
+ * event. This event we ignore by doing a timeout for 50 msecs.
+ * If we receive dx=dy=dz=buttons=0 before we add the event to
+ * the queue.
+ * In any other case we delete the timeout event.
+ */
+ if ((sc->sc_flags & UMS_FLAG_SBU) &&
+ (dx == 0) && (dy == 0) && (dz == 0) && (dt == 0) &&
+ (dw == 0) && (buttons == 0)) {
+
+ usb2_callout_reset(&sc->sc_callout, hz / 20,
+ &ums_put_queue_timeout, sc);
+ } else {
+
+ usb2_callout_stop(&sc->sc_callout);
+
+ ums_put_queue(sc, dx, dy, dz, dt, buttons);
+ }
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ if (sc->sc_flags & UMS_FLAG_INTR_STALL) {
+ usb2_transfer_start(sc->sc_xfer[UMS_INTR_CS]);
+ } else {
+ /* check if we can put more data into the FIFO */
+ if (usb2_fifo_put_bytes_max(
+ sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
+ 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 |= UMS_FLAG_INTR_STALL;
+ usb2_transfer_start(sc->sc_xfer[UMS_INTR_CS]);
+ }
+ return;
+ }
+}
+
+static const struct usb2_config ums_config[UMS_N_TRANSFER] = {
+
+ [UMS_INTR_DT] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &ums_intr_callback,
+ },
+
+ [UMS_INTR_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.callback = &ums_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+};
+
+static int
+ums_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+ void *d_ptr;
+ int32_t error = 0;
+ uint16_t d_len;
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->iface == NULL) {
+ return (ENXIO);
+ }
+ id = usb2_get_interface_descriptor(uaa->iface);
+
+ if ((id == NULL) ||
+ (id->bInterfaceClass != UICLASS_HID)) {
+ return (ENXIO);
+ }
+ error = usb2_req_get_hid_desc
+ (uaa->device, &Giant,
+ &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (error) {
+ return (ENXIO);
+ }
+ if (hid_is_collection(d_ptr, d_len,
+ HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) {
+ error = 0;
+ } else if ((id->bInterfaceSubClass == UISUBCLASS_BOOT) &&
+ (id->bInterfaceProtocol == UIPROTO_MOUSE)) {
+ error = 0;
+ } else {
+ error = ENXIO;
+ }
+
+ free(d_ptr, M_TEMP);
+ return (error);
+}
+
+static int
+ums_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ums_softc *sc = device_get_softc(dev);
+ void *d_ptr = NULL;
+ int unit = device_get_unit(dev);
+ int32_t isize;
+ uint32_t flags;
+ int32_t err;
+ uint16_t d_len;
+ uint8_t i;
+
+ DPRINTFN(11, "sc=%p\n", sc);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "ums lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ usb2_callout_init_mtx(&sc->sc_callout, &sc->sc_mtx, 0);
+
+ /*
+ * Force the report (non-boot) protocol.
+ *
+ * Mice without boot protocol support may choose not to implement
+ * Set_Protocol at all; Ignore any error.
+ */
+ err = usb2_req_set_protocol(uaa->device, NULL, uaa->info.bIfaceIndex, 1);
+
+ err = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, ums_config,
+ UMS_N_TRANSFER, sc, &sc->sc_mtx);
+
+ if (err) {
+ DPRINTF("error=%s\n", usb2_errstr(err));
+ goto detach;
+ }
+ err = usb2_req_get_hid_desc
+ (uaa->device, &Giant, &d_ptr,
+ &d_len, M_TEMP, uaa->info.bIfaceIndex);
+
+ if (err) {
+ device_printf(dev, "error reading report description\n");
+ goto detach;
+ }
+ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
+ hid_input, &sc->sc_loc_x, &flags)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ sc->sc_flags |= UMS_FLAG_X_AXIS;
+ }
+ }
+ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
+ hid_input, &sc->sc_loc_y, &flags)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ sc->sc_flags |= UMS_FLAG_Y_AXIS;
+ }
+ }
+ /* Try the wheel first as the Z activator since it's tradition. */
+ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags) ||
+ hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_TWHEEL), hid_input, &sc->sc_loc_z, &flags)) {
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ sc->sc_flags |= UMS_FLAG_Z_AXIS;
+ }
+ /*
+ * We might have both a wheel and Z direction, if so put
+ * put the Z on the W coordinate.
+ */
+ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_Z), hid_input, &sc->sc_loc_w, &flags)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ sc->sc_flags |= UMS_FLAG_W_AXIS;
+ }
+ }
+ } else if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP,
+ HUG_Z), hid_input, &sc->sc_loc_z, &flags)) {
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ sc->sc_flags |= UMS_FLAG_Z_AXIS;
+ }
+ }
+ /*
+ * The Microsoft Wireless Intellimouse 2.0 reports it's wheel
+ * using 0x0048, which is HUG_TWHEEL, and seems to expect you
+ * to know that the byte after the wheel is the tilt axis.
+ * There are no other HID axis descriptors other than X,Y and
+ * TWHEEL
+ */
+ if (hid_locate(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_TWHEEL),
+ hid_input, &sc->sc_loc_t, &flags)) {
+
+ sc->sc_loc_t.pos += 8;
+
+ if ((flags & MOUSE_FLAGS_MASK) == MOUSE_FLAGS) {
+ sc->sc_flags |= UMS_FLAG_T_AXIS;
+ }
+ }
+ /* figure out the number of buttons */
+
+ for (i = 0; i < UMS_BUTTON_MAX; i++) {
+ if (!hid_locate(d_ptr, d_len, HID_USAGE2(HUP_BUTTON, (i + 1)),
+ hid_input, &sc->sc_loc_btn[i], NULL)) {
+ break;
+ }
+ }
+
+ sc->sc_buttons = i;
+
+ isize = hid_report_size(d_ptr, d_len, hid_input, &sc->sc_iid);
+
+ /*
+ * The Microsoft Wireless Notebook Optical Mouse seems to be in worse
+ * shape than the Wireless Intellimouse 2.0, as its X, Y, wheel, and
+ * all of its other button positions are all off. It also reports that
+ * it has two addional buttons and a tilt wheel.
+ */
+ if (usb2_test_quirk(uaa, UQ_MS_BAD_CLASS)) {
+ sc->sc_flags = (UMS_FLAG_X_AXIS |
+ UMS_FLAG_Y_AXIS |
+ UMS_FLAG_Z_AXIS |
+ UMS_FLAG_SBU);
+ sc->sc_buttons = 3;
+ isize = 5;
+ sc->sc_iid = 0;
+ /* 1st byte of descriptor report contains garbage */
+ sc->sc_loc_x.pos = 16;
+ sc->sc_loc_y.pos = 24;
+ sc->sc_loc_z.pos = 32;
+ sc->sc_loc_btn[0].pos = 8;
+ sc->sc_loc_btn[1].pos = 9;
+ sc->sc_loc_btn[2].pos = 10;
+ }
+ /*
+ * The Microsoft Wireless Notebook Optical Mouse 3000 Model 1049 has
+ * five Report IDs: 19 23 24 17 18 (in the order they appear in report
+ * descriptor), it seems that report id 17 contains the necessary
+ * mouse information(3-buttons,X,Y,wheel) so we specify it manually.
+ */
+ if ((uaa->info.idVendor == USB_VENDOR_MICROSOFT) &&
+ (uaa->info.idProduct == USB_PRODUCT_MICROSOFT_WLNOTEBOOK3)) {
+ sc->sc_flags = (UMS_FLAG_X_AXIS |
+ UMS_FLAG_Y_AXIS |
+ UMS_FLAG_Z_AXIS);
+ sc->sc_buttons = 3;
+ isize = 5;
+ sc->sc_iid = 17;
+ sc->sc_loc_x.pos = 8;
+ sc->sc_loc_y.pos = 16;
+ sc->sc_loc_z.pos = 24;
+ sc->sc_loc_btn[0].pos = 0;
+ sc->sc_loc_btn[1].pos = 1;
+ sc->sc_loc_btn[2].pos = 2;
+ }
+ if (usb2_test_quirk(uaa, UQ_MS_REVZ)) {
+ /* Some wheels need the Z axis reversed. */
+ sc->sc_flags |= UMS_FLAG_REVZ;
+ }
+ if (isize > sc->sc_xfer[UMS_INTR_DT]->max_frame_size) {
+ DPRINTF("WARNING: report size, %d bytes, is larger "
+ "than interrupt size, %d bytes!\n",
+ isize, sc->sc_xfer[UMS_INTR_DT]->max_frame_size);
+ }
+ /* announce information about the mouse */
+
+ device_printf(dev, "%d buttons and [%s%s%s%s%s] coordinates\n",
+ (sc->sc_buttons),
+ (sc->sc_flags & UMS_FLAG_X_AXIS) ? "X" : "",
+ (sc->sc_flags & UMS_FLAG_Y_AXIS) ? "Y" : "",
+ (sc->sc_flags & UMS_FLAG_Z_AXIS) ? "Z" : "",
+ (sc->sc_flags & UMS_FLAG_T_AXIS) ? "T" : "",
+ (sc->sc_flags & UMS_FLAG_W_AXIS) ? "W" : "");
+
+ free(d_ptr, M_TEMP);
+ d_ptr = NULL;
+
+#if USB_DEBUG
+ DPRINTF("sc=%p\n", sc);
+ DPRINTF("X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size);
+ DPRINTF("Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size);
+ DPRINTF("Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size);
+ DPRINTF("T\t%d/%d\n", sc->sc_loc_t.pos, sc->sc_loc_t.size);
+ DPRINTF("W\t%d/%d\n", sc->sc_loc_w.pos, sc->sc_loc_w.size);
+
+ for (i = 0; i < sc->sc_buttons; i++) {
+ DPRINTF("B%d\t%d/%d\n",
+ i + 1, sc->sc_loc_btn[i].pos, sc->sc_loc_btn[i].size);
+ }
+ DPRINTF("size=%d, id=%d\n", isize, sc->sc_iid);
+#endif
+
+ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+
+ sc->sc_hw.iftype = MOUSE_IF_USB;
+ sc->sc_hw.type = MOUSE_MOUSE;
+ sc->sc_hw.model = MOUSE_MODEL_GENERIC;
+ sc->sc_hw.hwid = 0;
+
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.rate = -1;
+ sc->sc_mode.resolution = MOUSE_RES_UNKNOWN;
+ sc->sc_mode.accelfactor = 0;
+ sc->sc_mode.level = 0;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+
+ sc->sc_status.flags = 0;
+ sc->sc_status.button = 0;
+ sc->sc_status.obutton = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ err = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &ums_fifo_methods, &sc->sc_fifo,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ if (err) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ if (d_ptr) {
+ free(d_ptr, M_TEMP);
+ }
+ ums_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+ums_detach(device_t self)
+{
+ struct ums_softc *sc = device_get_softc(self);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMS_N_TRANSFER);
+
+ usb2_callout_drain(&sc->sc_callout);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+ums_start_read(struct usb2_fifo *fifo)
+{
+ struct ums_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[UMS_INTR_DT]);
+}
+
+static void
+ums_stop_read(struct usb2_fifo *fifo)
+{
+ struct ums_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[UMS_INTR_CS]);
+ usb2_transfer_stop(sc->sc_xfer[UMS_INTR_DT]);
+ usb2_callout_stop(&sc->sc_callout);
+}
+
+
+#if ((MOUSE_SYS_PACKETSIZE != 8) || \
+ (MOUSE_MSC_PACKETSIZE != 5))
+#error "Software assumptions are not met. Please update code."
+#endif
+
+static void
+ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy,
+ int32_t dz, int32_t dt, int32_t buttons)
+{
+ uint8_t buf[8];
+
+ if (1) {
+
+ if (dx > 254)
+ dx = 254;
+ if (dx < -256)
+ dx = -256;
+ if (dy > 254)
+ dy = 254;
+ if (dy < -256)
+ dy = -256;
+ if (dz > 126)
+ dz = 126;
+ if (dz < -128)
+ dz = -128;
+ if (dt > 126)
+ dt = 126;
+ if (dt < -128)
+ dt = -128;
+
+ buf[0] = sc->sc_mode.syncmask[1];
+ buf[0] |= (~buttons) & MOUSE_MSC_BUTTONS;
+ buf[1] = dx >> 1;
+ buf[2] = dy >> 1;
+ buf[3] = dx - (dx >> 1);
+ buf[4] = dy - (dy >> 1);
+
+ if (sc->sc_mode.level == 1) {
+ buf[5] = dz >> 1;
+ buf[6] = dz - (dz >> 1);
+ buf[7] = (((~buttons) >> 3) & MOUSE_SYS_EXTBUTTONS);
+ }
+ usb2_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
+ sc->sc_mode.packetsize, 1);
+
+ } else {
+ DPRINTF("Buffer full, discarded packet\n");
+ }
+}
+
+static void
+ums_reset_buf(struct ums_softc *sc)
+{
+ /* reset read queue */
+ usb2_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
+}
+
+static int
+ums_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ums_softc *sc = fifo->priv_sc0;
+
+ DPRINTFN(2, "\n");
+
+ if (fflags & FREAD) {
+
+ /* reset status */
+
+ sc->sc_status.flags = 0;
+ sc->sc_status.button = 0;
+ sc->sc_status.obutton = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+ /* sc->sc_status.dt = 0; */
+
+ if (usb2_fifo_alloc_buffer(fifo,
+ UMS_BUF_SIZE, UMS_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+ return (0);
+}
+
+static void
+ums_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ if (fflags & FREAD) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+static int
+ums_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr,
+ int fflags, struct thread *td)
+{
+ struct ums_softc *sc = fifo->priv_sc0;
+ mousemode_t mode;
+ int error = 0;
+
+ DPRINTFN(2, "\n");
+
+ mtx_lock(&sc->sc_mtx);
+
+ switch (cmd) {
+ case MOUSE_GETHWINFO:
+ *(mousehw_t *)addr = sc->sc_hw;
+ break;
+
+ case MOUSE_GETMODE:
+ *(mousemode_t *)addr = sc->sc_mode;
+ break;
+
+ case MOUSE_SETMODE:
+ mode = *(mousemode_t *)addr;
+
+ if (mode.level == -1) {
+ /* don't change the current setting */
+ } else if ((mode.level < 0) || (mode.level > 1)) {
+ error = EINVAL;
+ goto done;
+ } else {
+ sc->sc_mode.level = mode.level;
+ }
+
+ if (sc->sc_mode.level == 0) {
+ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ ums_reset_buf(sc);
+ break;
+
+ case MOUSE_GETLEVEL:
+ *(int *)addr = sc->sc_mode.level;
+ break;
+
+ case MOUSE_SETLEVEL:
+ if (*(int *)addr < 0 || *(int *)addr > 1) {
+ error = EINVAL;
+ goto done;
+ }
+ sc->sc_mode.level = *(int *)addr;
+
+ if (sc->sc_mode.level == 0) {
+ if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_MSC;
+ sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
+ } else if (sc->sc_mode.level == 1) {
+ if (sc->sc_buttons > MOUSE_SYS_MAXBUTTON)
+ sc->sc_hw.buttons = MOUSE_SYS_MAXBUTTON;
+ else
+ sc->sc_hw.buttons = sc->sc_buttons;
+ sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
+ sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
+ sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
+ sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
+ }
+ ums_reset_buf(sc);
+ break;
+
+ case MOUSE_GETSTATUS:{
+ mousestatus_t *status = (mousestatus_t *)addr;
+
+ *status = sc->sc_status;
+ sc->sc_status.obutton = sc->sc_status.button;
+ sc->sc_status.button = 0;
+ sc->sc_status.dx = 0;
+ sc->sc_status.dy = 0;
+ sc->sc_status.dz = 0;
+ /* sc->sc_status.dt = 0; */
+
+ if (status->dx || status->dy || status->dz /* || status->dt */ ) {
+ status->flags |= MOUSE_POSCHANGED;
+ }
+ if (status->button != status->obutton) {
+ status->flags |= MOUSE_BUTTONSCHANGED;
+ }
+ break;
+ }
+ default:
+ error = ENOTTY;
+ }
+
+done:
+ mtx_unlock(&sc->sc_mtx);
+ return (error);
+}
+
+static devclass_t ums_devclass;
+
+static device_method_t ums_methods[] = {
+ DEVMETHOD(device_probe, ums_probe),
+ DEVMETHOD(device_attach, ums_attach),
+ DEVMETHOD(device_detach, ums_detach),
+ {0, 0}
+};
+
+static driver_t ums_driver = {
+ .name = "ums",
+ .methods = ums_methods,
+ .size = sizeof(struct ums_softc),
+};
+
+DRIVER_MODULE(ums, ushub, ums_driver, ums_devclass, NULL, 0);
+MODULE_DEPEND(ums, usb, 1, 1, 1);
diff --git a/sys/dev/usb/input/usb_rdesc.h b/sys/dev/usb/input/usb_rdesc.h
new file mode 100644
index 0000000..9f4363d
--- /dev/null
+++ b/sys/dev/usb/input/usb_rdesc.h
@@ -0,0 +1,276 @@
+/*-
+ * Copyright (c) 2000 Nick Hibma <n_hibma@freebsd.org>
+ * All rights reserved.
+ *
+ * Copyright (c) 2005 Ed Schouten <ed@fxq.nl>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ * This file contains replacements for broken HID report descriptors.
+ */
+
+#define UHID_GRAPHIRE_REPORT_DESCR(...) \
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Digitizer) */\
+ 0xa1, 0x01, /* COLLECTION (Application) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Digitizer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\
+ 0x09, 0x33, /* USAGE (Touch) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x44, /* USAGE (Barrel Switch) */\
+ 0x95, 0x02, /* REPORT_COUNT (2) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x95, 0x02, /* REPORT_COUNT (2) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */\
+ 0x09, 0x3c, /* USAGE (Invert) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x38, /* USAGE (Transducer Index) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x32, /* USAGE (In Range) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x30, /* USAGE (Tip Pressure) */\
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0xc0, /* END_COLLECTION */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x03, /* REPORT_ID (3) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0xc0, /* END_COLLECTION */\
+
+#define UHID_GRAPHIRE3_4X5_REPORT_DESCR(...) \
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x02, /* USAGE (Mouse) */\
+ 0xa1, 0x01, /* COLLECTION (Application) */\
+ 0x85, 0x01, /* REPORT_ID (1) */\
+ 0x09, 0x01, /* USAGE (Pointer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x05, 0x09, /* USAGE_PAGE (Button) */\
+ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */\
+ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x75, 0x05, /* REPORT_SIZE (5) */\
+ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */\
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x09, 0x38, /* USAGE (Wheel) */\
+ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */\
+ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */\
+ 0x75, 0x08, /* REPORT_SIZE (8) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x81, 0x06, /* INPUT (Data,Var,Rel) */\
+ 0xc0, /* END_COLLECTION */\
+ 0xc0, /* END_COLLECTION */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Pointer) */\
+ 0xa1, 0x01, /* COLLECTION (Applicaption) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x01, /* USAGE (Digitizer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x09, 0x33, /* USAGE (Touch) */\
+ 0x09, 0x44, /* USAGE (Barrel Switch) */\
+ 0x09, 0x44, /* USAGE (Barrel Switch) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x95, 0x02, /* REPORT_COUNT (2) */\
+ 0x81, 0x01, /* INPUT (Cnst,Ary,Abs) */\
+ 0x09, 0x3c, /* USAGE (Invert) */\
+ 0x09, 0x38, /* USAGE (Transducer Index) */\
+ 0x09, 0x32, /* USAGE (In Range) */\
+ 0x75, 0x01, /* REPORT_SIZE (1) */\
+ 0x95, 0x03, /* REPORT_COUNT (3) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */\
+ 0x26, 0xde, 0x27, /* LOGICAL_MAXIMUM (10206) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x26, 0xfe, 0x1c, /* LOGICAL_MAXIMUM (7422) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x30, /* USAGE (Tip Pressure) */\
+ 0x26, 0xff, 0x01, /* LOGICAL_MAXIMUM (511) */\
+ 0x75, 0x10, /* REPORT_SIZE (16) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0x81, 0x02, /* INPUT (Data,Var,Abs) */\
+ 0xc0, /* END_COLLECTION */\
+ 0x05, 0x0d, /* USAGE_PAGE (Digitizers) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x02, /* REPORT_ID (2) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0x09, 0x00, /* USAGE (Undefined) */\
+ 0x85, 0x03, /* REPORT_ID (3) */\
+ 0x95, 0x01, /* REPORT_COUNT (1) */\
+ 0xb1, 0x02, /* FEATURE (Data,Var,Abs) */\
+ 0xc0 /* END_COLLECTION */\
+
+/*
+ * The descriptor has no output report format, thus preventing you from
+ * controlling the LEDs and the built-in rumblers.
+ */
+#define UHID_XB360GP_REPORT_DESCR(...) \
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x05, /* USAGE (Gamepad) */\
+ 0xa1, 0x01, /* COLLECTION (Application) */\
+ /* Unused */\
+ 0x75, 0x08, /* REPORT SIZE (8) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ /* Byte count */\
+ 0x75, 0x08, /* REPORT SIZE (8) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x3b, /* USAGE (Byte Count) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ /* D-Pad */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x01, /* USAGE (Pointer) */\
+ 0xa1, 0x00, /* COLLECTION (Physical) */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\
+ 0x95, 0x04, /* REPORT COUNT (4) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x90, /* USAGE (D-Pad Up) */\
+ 0x09, 0x91, /* USAGE (D-Pad Down) */\
+ 0x09, 0x93, /* USAGE (D-Pad Left) */\
+ 0x09, 0x92, /* USAGE (D-Pad Right) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ 0xc0, /* END COLLECTION */\
+ /* Buttons 5-11 */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\
+ 0x95, 0x07, /* REPORT COUNT (7) */\
+ 0x05, 0x09, /* USAGE PAGE (Button) */\
+ 0x09, 0x08, /* USAGE (Button 8) */\
+ 0x09, 0x07, /* USAGE (Button 7) */\
+ 0x09, 0x09, /* USAGE (Button 9) */\
+ 0x09, 0x0a, /* USAGE (Button 10) */\
+ 0x09, 0x05, /* USAGE (Button 5) */\
+ 0x09, 0x06, /* USAGE (Button 6) */\
+ 0x09, 0x0b, /* USAGE (Button 11) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Unused */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ /* Buttons 1-4 */\
+ 0x75, 0x01, /* REPORT SIZE (1) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x25, 0x01, /* LOGICAL MAXIMUM (1) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x45, 0x01, /* PHYSICAL MAXIMUM (1) */\
+ 0x95, 0x04, /* REPORT COUNT (4) */\
+ 0x05, 0x09, /* USAGE PAGE (Button) */\
+ 0x19, 0x01, /* USAGE MINIMUM (Button 1) */\
+ 0x29, 0x04, /* USAGE MAXIMUM (Button 4) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Triggers */\
+ 0x75, 0x08, /* REPORT SIZE (8) */\
+ 0x15, 0x00, /* LOGICAL MINIMUM (0) */\
+ 0x26, 0xff, 0x00, /* LOGICAL MAXIMUM (255) */\
+ 0x35, 0x00, /* PHYSICAL MINIMUM (0) */\
+ 0x46, 0xff, 0x00, /* PHYSICAL MAXIMUM (255) */\
+ 0x95, 0x02, /* REPORT SIZE (2) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x32, /* USAGE (Z) */\
+ 0x09, 0x35, /* USAGE (Rz) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Sticks */\
+ 0x75, 0x10, /* REPORT SIZE (16) */\
+ 0x16, 0x00, 0x80, /* LOGICAL MINIMUM (-32768) */\
+ 0x26, 0xff, 0x7f, /* LOGICAL MAXIMUM (32767) */\
+ 0x36, 0x00, 0x80, /* PHYSICAL MINIMUM (-32768) */\
+ 0x46, 0xff, 0x7f, /* PHYSICAL MAXIMUM (32767) */\
+ 0x95, 0x04, /* REPORT COUNT (4) */\
+ 0x05, 0x01, /* USAGE PAGE (Generic Desktop) */\
+ 0x09, 0x30, /* USAGE (X) */\
+ 0x09, 0x31, /* USAGE (Y) */\
+ 0x09, 0x33, /* USAGE (Rx) */\
+ 0x09, 0x34, /* USAGE (Ry) */\
+ 0x81, 0x02, /* INPUT (Data, Variable, Absolute) */\
+ /* Unused */\
+ 0x75, 0x30, /* REPORT SIZE (48) */\
+ 0x95, 0x01, /* REPORT COUNT (1) */\
+ 0x81, 0x01, /* INPUT (Constant) */\
+ 0xc0 /* END COLLECTION */\
+
diff --git a/sys/dev/usb/misc/udbp.c b/sys/dev/usb/misc/udbp.c
new file mode 100644
index 0000000..b519058
--- /dev/null
+++ b/sys/dev/usb/misc/udbp.c
@@ -0,0 +1,853 @@
+/*-
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * 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.
+ * 3. Neither the name of author 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 NICK HIBMA AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* Driver for arbitrary double bulk pipe devices.
+ * The driver assumes that there will be the same driver on the other side.
+ *
+ * XXX Some more information on what the framing of the IP packets looks like.
+ *
+ * To take full advantage of bulk transmission, packets should be chosen
+ * between 1k and 5k in size (1k to make sure the sending side starts
+ * streaming, and <5k to avoid overflowing the system with small TDs).
+ */
+
+
+/* probe/attach/detach:
+ * Connect the driver to the hardware and netgraph
+ *
+ * The reason we submit a bulk in transfer is that USB does not know about
+ * interrupts. The bulk transfer continuously polls the device for data.
+ * While the device has no data available, the device NAKs the TDs. As soon
+ * as there is data, the transfer happens and the data comes flowing in.
+ *
+ * In case you were wondering, interrupt transfers happen exactly that way.
+ * It therefore doesn't make sense to use the interrupt pipe to signal
+ * 'data ready' and then schedule a bulk transfer to fetch it. That would
+ * incur a 2ms delay at least, without reducing bandwidth requirements.
+ *
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR udbp_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <sys/mbuf.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/bluetooth/include/ng_bluetooth.h>
+
+#include <dev/usb/misc/udbp.h>
+
+#if USB_DEBUG
+static int udbp_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, udbp, CTLFLAG_RW, 0, "USB udbp");
+SYSCTL_INT(_hw_usb2_udbp, OID_AUTO, debug, CTLFLAG_RW,
+ &udbp_debug, 0, "udbp debug level");
+#endif
+
+#define UDBP_TIMEOUT 2000 /* timeout on outbound transfers, in
+ * msecs */
+#define UDBP_BUFFERSIZE MCLBYTES /* maximum number of bytes in one
+ * transfer */
+#define UDBP_T_WR 0
+#define UDBP_T_RD 1
+#define UDBP_T_WR_CS 2
+#define UDBP_T_RD_CS 3
+#define UDBP_T_MAX 4
+#define UDBP_Q_MAXLEN 50
+
+struct udbp_softc {
+
+ struct mtx sc_mtx;
+ struct ng_bt_mbufq sc_xmitq_hipri; /* hi-priority transmit queue */
+ struct ng_bt_mbufq sc_xmitq; /* low-priority transmit queue */
+
+ struct usb2_xfer *sc_xfer[UDBP_T_MAX];
+ node_p sc_node; /* back pointer to node */
+ hook_p sc_hook; /* pointer to the hook */
+ struct mbuf *sc_bulk_in_buffer;
+
+ uint32_t sc_packets_in; /* packets in from downstream */
+ uint32_t sc_packets_out; /* packets out towards downstream */
+
+ uint8_t sc_flags;
+#define UDBP_FLAG_READ_STALL 0x01 /* read transfer stalled */
+#define UDBP_FLAG_WRITE_STALL 0x02 /* write transfer stalled */
+
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static int udbp_modload(module_t mod, int event, void *data);
+
+static device_probe_t udbp_probe;
+static device_attach_t udbp_attach;
+static device_detach_t udbp_detach;
+
+static usb2_callback_t udbp_bulk_read_callback;
+static usb2_callback_t udbp_bulk_read_clear_stall_callback;
+static usb2_callback_t udbp_bulk_write_callback;
+static usb2_callback_t udbp_bulk_write_clear_stall_callback;
+
+static void udbp_bulk_read_complete(node_p, hook_p, void *, int);
+
+static ng_constructor_t ng_udbp_constructor;
+static ng_rcvmsg_t ng_udbp_rcvmsg;
+static ng_shutdown_t ng_udbp_rmnode;
+static ng_newhook_t ng_udbp_newhook;
+static ng_connect_t ng_udbp_connect;
+static ng_rcvdata_t ng_udbp_rcvdata;
+static ng_disconnect_t ng_udbp_disconnect;
+
+/* Parse type for struct ngudbpstat */
+static const struct ng_parse_struct_field
+ ng_udbp_stat_type_fields[] = NG_UDBP_STATS_TYPE_INFO;
+
+static const struct ng_parse_type ng_udbp_stat_type = {
+ &ng_parse_struct_type,
+ &ng_udbp_stat_type_fields
+};
+
+/* List of commands and how to convert arguments to/from ASCII */
+static const struct ng_cmdlist ng_udbp_cmdlist[] = {
+ {
+ NGM_UDBP_COOKIE,
+ NGM_UDBP_GET_STATUS,
+ "getstatus",
+ NULL,
+ &ng_udbp_stat_type,
+ },
+ {
+ NGM_UDBP_COOKIE,
+ NGM_UDBP_SET_FLAG,
+ "setflag",
+ &ng_parse_int32_type,
+ NULL
+ },
+ {0}
+};
+
+/* Netgraph node type descriptor */
+static struct ng_type ng_udbp_typestruct = {
+ .version = NG_ABI_VERSION,
+ .name = NG_UDBP_NODE_TYPE,
+ .constructor = ng_udbp_constructor,
+ .rcvmsg = ng_udbp_rcvmsg,
+ .shutdown = ng_udbp_rmnode,
+ .newhook = ng_udbp_newhook,
+ .connect = ng_udbp_connect,
+ .rcvdata = ng_udbp_rcvdata,
+ .disconnect = ng_udbp_disconnect,
+ .cmdlist = ng_udbp_cmdlist,
+};
+
+/* USB config */
+static const struct usb2_config udbp_config[UDBP_T_MAX] = {
+
+ [UDBP_T_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UDBP_BUFFERSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &udbp_bulk_write_callback,
+ .mh.timeout = UDBP_TIMEOUT,
+ },
+
+ [UDBP_T_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UDBP_BUFFERSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &udbp_bulk_read_callback,
+ },
+
+ [UDBP_T_WR_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &udbp_bulk_write_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+
+ [UDBP_T_RD_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &udbp_bulk_read_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+};
+
+static devclass_t udbp_devclass;
+
+static device_method_t udbp_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, udbp_probe),
+ DEVMETHOD(device_attach, udbp_attach),
+ DEVMETHOD(device_detach, udbp_detach),
+ {0, 0}
+};
+
+static driver_t udbp_driver = {
+ .name = "udbp",
+ .methods = udbp_methods,
+ .size = sizeof(struct udbp_softc),
+};
+
+DRIVER_MODULE(udbp, ushub, udbp_driver, udbp_devclass, udbp_modload, 0);
+MODULE_DEPEND(udbp, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION);
+MODULE_DEPEND(udbp, usb, 1, 1, 1);
+
+static int
+udbp_modload(module_t mod, int event, void *data)
+{
+ int error;
+
+ switch (event) {
+ case MOD_LOAD:
+ error = ng_newtype(&ng_udbp_typestruct);
+ if (error != 0) {
+ printf("%s: Could not register "
+ "Netgraph node type, error=%d\n",
+ NG_UDBP_NODE_TYPE, error);
+ }
+ break;
+
+ case MOD_UNLOAD:
+ error = ng_rmtype(&ng_udbp_typestruct);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ return (error);
+}
+
+static int
+udbp_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ /*
+ * XXX Julian, add the id of the device if you have one to test
+ * things with. run 'usbdevs -v' and note the 3 ID's that appear.
+ * The Vendor Id and Product Id are in hex and the Revision Id is in
+ * bcd. But as usual if the revision is 0x101 then you should
+ * compare the revision id in the device descriptor with 0x101 Or go
+ * search the file usbdevs.h. Maybe the device is already in there.
+ */
+ if (((uaa->info.idVendor == USB_VENDOR_NETCHIP) &&
+ (uaa->info.idProduct == USB_PRODUCT_NETCHIP_TURBOCONNECT)))
+ return (0);
+
+ if (((uaa->info.idVendor == USB_VENDOR_PROLIFIC) &&
+ ((uaa->info.idProduct == USB_PRODUCT_PROLIFIC_PL2301) ||
+ (uaa->info.idProduct == USB_PRODUCT_PROLIFIC_PL2302))))
+ return (0);
+
+ if ((uaa->info.idVendor == USB_VENDOR_ANCHOR) &&
+ (uaa->info.idProduct == USB_PRODUCT_ANCHOR_EZLINK))
+ return (0);
+
+ if ((uaa->info.idVendor == USB_VENDOR_GENESYS) &&
+ (uaa->info.idProduct == USB_PRODUCT_GENESYS_GL620USB))
+ return (0);
+
+ return (ENXIO);
+}
+
+static int
+udbp_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct udbp_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ mtx_init(&sc->sc_mtx, "udbp lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ error = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
+ sc->sc_xfer, udbp_config, UDBP_T_MAX, sc, &sc->sc_mtx);
+ if (error) {
+ DPRINTF("error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ NG_BT_MBUFQ_INIT(&sc->sc_xmitq, UDBP_Q_MAXLEN);
+
+ NG_BT_MBUFQ_INIT(&sc->sc_xmitq_hipri, UDBP_Q_MAXLEN);
+
+ /* create Netgraph node */
+
+ if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) {
+ printf("%s: Could not create Netgraph node\n",
+ sc->sc_name);
+ sc->sc_node = NULL;
+ goto detach;
+ }
+ /* name node */
+
+ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) {
+ printf("%s: Could not name node\n",
+ sc->sc_name);
+ NG_NODE_UNREF(sc->sc_node);
+ sc->sc_node = NULL;
+ goto detach;
+ }
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+
+ /* the device is now operational */
+
+ return (0); /* success */
+
+detach:
+ udbp_detach(dev);
+ return (ENOMEM); /* failure */
+}
+
+static int
+udbp_detach(device_t dev)
+{
+ struct udbp_softc *sc = device_get_softc(dev);
+
+ /* destroy Netgraph node */
+
+ if (sc->sc_node != NULL) {
+ NG_NODE_SET_PRIVATE(sc->sc_node, NULL);
+ ng_rmnode_self(sc->sc_node);
+ sc->sc_node = NULL;
+ }
+ /* free USB transfers, if any */
+
+ usb2_transfer_unsetup(sc->sc_xfer, UDBP_T_MAX);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ /* destroy queues */
+
+ NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq);
+ NG_BT_MBUFQ_DESTROY(&sc->sc_xmitq_hipri);
+
+ /* extra check */
+
+ if (sc->sc_bulk_in_buffer) {
+ m_freem(sc->sc_bulk_in_buffer);
+ sc->sc_bulk_in_buffer = NULL;
+ }
+ return (0); /* success */
+}
+
+static void
+udbp_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct udbp_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ /* allocate new mbuf */
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+
+ if (m == NULL) {
+ goto tr_setup;
+ }
+ MCLGET(m, M_DONTWAIT);
+
+ if (!(m->m_flags & M_EXT)) {
+ m_freem(m);
+ goto tr_setup;
+ }
+ m->m_pkthdr.len = m->m_len = xfer->actlen;
+
+ usb2_copy_out(xfer->frbuffers, 0, m->m_data, xfer->actlen);
+
+ sc->sc_bulk_in_buffer = m;
+
+ DPRINTF("received package %d "
+ "bytes\n", xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (sc->sc_bulk_in_buffer) {
+ ng_send_fn(sc->sc_node, NULL, &udbp_bulk_read_complete, NULL, 0);
+ return;
+ }
+ if (sc->sc_flags & UDBP_FLAG_READ_STALL) {
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]);
+ return;
+ }
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ sc->sc_flags |= UDBP_FLAG_READ_STALL;
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_RD_CS]);
+ }
+ return;
+
+ }
+}
+
+static void
+udbp_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct udbp_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[UDBP_T_RD];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~UDBP_FLAG_READ_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+static void
+udbp_bulk_read_complete(node_p node, hook_p hook, void *arg1, int arg2)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(node);
+ struct mbuf *m;
+ int error;
+
+ if (sc == NULL) {
+ return;
+ }
+ mtx_lock(&sc->sc_mtx);
+
+ m = sc->sc_bulk_in_buffer;
+
+ if (m) {
+
+ sc->sc_bulk_in_buffer = NULL;
+
+ if ((sc->sc_hook == NULL) ||
+ NG_HOOK_NOT_VALID(sc->sc_hook)) {
+ DPRINTF("No upstream hook\n");
+ goto done;
+ }
+ sc->sc_packets_in++;
+
+ NG_SEND_DATA_ONLY(error, sc->sc_hook, m);
+
+ m = NULL;
+ }
+done:
+ if (m) {
+ m_freem(m);
+ }
+ /* start USB bulk-in transfer, if not already started */
+
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_RD]);
+
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static void
+udbp_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct udbp_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ sc->sc_packets_out++;
+
+ case USB_ST_SETUP:
+ if (sc->sc_flags & UDBP_FLAG_WRITE_STALL) {
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]);
+ return;
+ }
+ /* get next mbuf, if any */
+
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq_hipri, m);
+ if (m == NULL) {
+ NG_BT_MBUFQ_DEQUEUE(&sc->sc_xmitq, m);
+ if (m == NULL) {
+ DPRINTF("Data queue is empty\n");
+ return;
+ }
+ }
+ if (m->m_pkthdr.len > MCLBYTES) {
+ DPRINTF("truncating large packet "
+ "from %d to %d bytes\n", m->m_pkthdr.len,
+ MCLBYTES);
+ m->m_pkthdr.len = MCLBYTES;
+ }
+ usb2_m_copy_in(xfer->frbuffers, 0, m, 0, m->m_pkthdr.len);
+
+ xfer->frlengths[0] = m->m_pkthdr.len;
+
+ m_freem(m);
+
+ DPRINTF("packet out: %d bytes\n",
+ xfer->frlengths[0]);
+
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ sc->sc_flags |= UDBP_FLAG_WRITE_STALL;
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_WR_CS]);
+ }
+ return;
+
+ }
+}
+
+static void
+udbp_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct udbp_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[UDBP_T_WR];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~UDBP_FLAG_WRITE_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+/***********************************************************************
+ * Start of Netgraph methods
+ **********************************************************************/
+
+/*
+ * If this is a device node so this work is done in the attach()
+ * routine and the constructor will return EINVAL as you should not be able
+ * to create nodes that depend on hardware (unless you can add the hardware :)
+ */
+static int
+ng_udbp_constructor(node_p node)
+{
+ return (EINVAL);
+}
+
+/*
+ * Give our ok for a hook to be added...
+ * If we are not running this might kick a device into life.
+ * Possibly decode information out of the hook name.
+ * Add the hook's private info to the hook structure.
+ * (if we had some). In this example, we assume that there is a
+ * an array of structs, called 'channel' in the private info,
+ * one for each active channel. The private
+ * pointer of each hook points to the appropriate UDBP_hookinfo struct
+ * so that the source of an input packet is easily identified.
+ */
+static int
+ng_udbp_newhook(node_p node, hook_p hook, const char *name)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(node);
+ int32_t error = 0;
+
+ if (strcmp(name, NG_UDBP_HOOK_NAME)) {
+ return (EINVAL);
+ }
+ mtx_lock(&sc->sc_mtx);
+
+ if (sc->sc_hook != NULL) {
+ error = EISCONN;
+ } else {
+ sc->sc_hook = hook;
+ NG_HOOK_SET_PRIVATE(hook, NULL);
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+
+ return (error);
+}
+
+/*
+ * Get a netgraph control message.
+ * Check it is one we understand. If needed, send a response.
+ * We could save the address for an async action later, but don't here.
+ * Always free the message.
+ * The response should be in a malloc'd region that the caller can 'free'.
+ * A response is not required.
+ * Theoretically you could respond defferently to old message types if
+ * the cookie in the header didn't match what we consider to be current
+ * (so that old userland programs could continue to work).
+ */
+static int
+ng_udbp_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(node);
+ struct ng_mesg *resp = NULL;
+ int error = 0;
+ struct ng_mesg *msg;
+
+ NGI_GET_MSG(item, msg);
+ /* Deal with message according to cookie and command */
+ switch (msg->header.typecookie) {
+ case NGM_UDBP_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_UDBP_GET_STATUS:
+ {
+ struct ngudbpstat *stats;
+
+ NG_MKRESPONSE(resp, msg, sizeof(*stats), M_NOWAIT);
+ if (!resp) {
+ error = ENOMEM;
+ break;
+ }
+ stats = (struct ngudbpstat *)resp->data;
+ mtx_lock(&sc->sc_mtx);
+ stats->packets_in = sc->sc_packets_in;
+ stats->packets_out = sc->sc_packets_out;
+ mtx_unlock(&sc->sc_mtx);
+ break;
+ }
+ case NGM_UDBP_SET_FLAG:
+ if (msg->header.arglen != sizeof(uint32_t)) {
+ error = EINVAL;
+ break;
+ }
+ DPRINTF("flags = 0x%08x\n",
+ *((uint32_t *)msg->data));
+ break;
+ default:
+ error = EINVAL; /* unknown command */
+ break;
+ }
+ break;
+ default:
+ error = EINVAL; /* unknown cookie type */
+ break;
+ }
+
+ /* Take care of synchronous response, if any */
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+ return (error);
+}
+
+/*
+ * Accept data from the hook and queue it for output.
+ */
+static int
+ng_udbp_rcvdata(hook_p hook, item_p item)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ struct ng_bt_mbufq *queue_ptr;
+ struct mbuf *m;
+ struct ng_tag_prio *ptag;
+ int error;
+
+ if (sc == NULL) {
+ NG_FREE_ITEM(item);
+ return (EHOSTDOWN);
+ }
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /*
+ * Now queue the data for when it can be sent
+ */
+ ptag = (void *)m_tag_locate(m, NGM_GENERIC_COOKIE,
+ NG_TAG_PRIO, NULL);
+
+ if (ptag && (ptag->priority > NG_PRIO_CUTOFF))
+ queue_ptr = &sc->sc_xmitq_hipri;
+ else
+ queue_ptr = &sc->sc_xmitq;
+
+ mtx_lock(&sc->sc_mtx);
+
+ if (NG_BT_MBUFQ_FULL(queue_ptr)) {
+ NG_BT_MBUFQ_DROP(queue_ptr);
+ NG_FREE_M(m);
+ error = ENOBUFS;
+ } else {
+ NG_BT_MBUFQ_ENQUEUE(queue_ptr, m);
+ /*
+ * start bulk-out transfer, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_WR]);
+ error = 0;
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+
+ return (error);
+}
+
+/*
+ * Do local shutdown processing..
+ * We are a persistant device, we refuse to go away, and
+ * only remove our links and reset ourself.
+ */
+static int
+ng_udbp_rmnode(node_p node)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(node);
+
+ /* Let old node go */
+ NG_NODE_SET_PRIVATE(node, NULL);
+ NG_NODE_UNREF(node); /* forget it ever existed */
+
+ if (sc == NULL) {
+ goto done;
+ }
+ /* Create Netgraph node */
+ if (ng_make_node_common(&ng_udbp_typestruct, &sc->sc_node) != 0) {
+ printf("%s: Could not create Netgraph node\n",
+ sc->sc_name);
+ sc->sc_node = NULL;
+ goto done;
+ }
+ /* Name node */
+ if (ng_name_node(sc->sc_node, sc->sc_name) != 0) {
+ printf("%s: Could not name Netgraph node\n",
+ sc->sc_name);
+ NG_NODE_UNREF(sc->sc_node);
+ sc->sc_node = NULL;
+ goto done;
+ }
+ NG_NODE_SET_PRIVATE(sc->sc_node, sc);
+
+done:
+ if (sc) {
+ mtx_unlock(&sc->sc_mtx);
+ }
+ return (0);
+}
+
+/*
+ * This is called once we've already connected a new hook to the other node.
+ * It gives us a chance to balk at the last minute.
+ */
+static int
+ng_udbp_connect(hook_p hook)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+
+ /* probably not at splnet, force outward queueing */
+ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook));
+
+ mtx_lock(&sc->sc_mtx);
+
+ sc->sc_flags |= (UDBP_FLAG_READ_STALL |
+ UDBP_FLAG_WRITE_STALL);
+
+ /* start bulk-in transfer */
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_RD]);
+
+ /* start bulk-out transfer */
+ usb2_transfer_start(sc->sc_xfer[UDBP_T_WR]);
+
+ mtx_unlock(&sc->sc_mtx);
+
+ return (0);
+}
+
+/*
+ * Dook disconnection
+ *
+ * For this type, removal of the last link destroys the node
+ */
+static int
+ng_udbp_disconnect(hook_p hook)
+{
+ struct udbp_softc *sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
+ int error = 0;
+
+ if (sc != NULL) {
+
+ mtx_lock(&sc->sc_mtx);
+
+ if (hook != sc->sc_hook) {
+ error = EINVAL;
+ } else {
+
+ /* stop bulk-in transfer */
+ usb2_transfer_stop(sc->sc_xfer[UDBP_T_RD_CS]);
+ usb2_transfer_stop(sc->sc_xfer[UDBP_T_RD]);
+
+ /* stop bulk-out transfer */
+ usb2_transfer_stop(sc->sc_xfer[UDBP_T_WR_CS]);
+ usb2_transfer_stop(sc->sc_xfer[UDBP_T_WR]);
+
+ /* cleanup queues */
+ NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq);
+ NG_BT_MBUFQ_DRAIN(&sc->sc_xmitq_hipri);
+
+ if (sc->sc_bulk_in_buffer) {
+ m_freem(sc->sc_bulk_in_buffer);
+ sc->sc_bulk_in_buffer = NULL;
+ }
+ sc->sc_hook = NULL;
+ }
+
+ mtx_unlock(&sc->sc_mtx);
+ }
+ if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0)
+ && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
+ ng_rmnode_self(NG_HOOK_NODE(hook));
+
+ return (error);
+}
diff --git a/sys/dev/usb/misc/udbp.h b/sys/dev/usb/misc/udbp.h
new file mode 100644
index 0000000..e6fd853
--- /dev/null
+++ b/sys/dev/usb/misc/udbp.h
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1996-2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file was derived from src/sys/netgraph/ng_sample.h, revision 1.1
+ * written by Julian Elischer, Whistle Communications.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_UDBP_H_
+#define _NETGRAPH_UDBP_H_
+
+/* Node type name. This should be unique among all netgraph node types */
+#define NG_UDBP_NODE_TYPE "udbp"
+
+/* Node type cookie. Should also be unique. This value MUST change whenever
+ an incompatible change is made to this header file, to insure consistency.
+ The de facto method for generating cookies is to take the output of the
+ date command: date -u +'%s' */
+#define NGM_UDBP_COOKIE 944609300
+
+
+#define NG_UDBP_HOOK_NAME "data"
+
+/* Netgraph commands understood by this node type */
+enum {
+ NGM_UDBP_SET_FLAG = 1,
+ NGM_UDBP_GET_STATUS,
+};
+
+/* This structure is returned by the NGM_UDBP_GET_STATUS command */
+struct ngudbpstat {
+ uint32_t packets_in; /* packets in from downstream */
+ uint32_t packets_out; /* packets out towards downstream */
+};
+
+/*
+ * This is used to define the 'parse type' for a struct ngudbpstat, which
+ * is bascially a description of how to convert a binary struct ngudbpstat
+ * to an ASCII string and back. See ng_parse.h for more info.
+ *
+ * This needs to be kept in sync with the above structure definition
+ */
+#define NG_UDBP_STATS_TYPE_INFO { \
+ { "packets_in", &ng_parse_int32_type }, \
+ { "packets_out", &ng_parse_int32_type }, \
+ { NULL }, \
+}
+
+#endif /* _NETGRAPH_UDBP_H_ */
diff --git a/sys/dev/usb/misc/ufm.c b/sys/dev/usb/misc/ufm.c
new file mode 100644
index 0000000..81ce1b3
--- /dev/null
+++ b/sys/dev/usb/misc/ufm.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 2001 M. Warner Losh
+ * 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 code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/ufm_ioctl.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+
+#define UFM_CMD0 0x00
+#define UFM_CMD_SET_FREQ 0x01
+#define UFM_CMD2 0x02
+
+struct ufm_softc {
+ struct usb2_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+
+ struct usb2_device *sc_udev;
+
+ uint32_t sc_unit;
+ uint32_t sc_freq;
+
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t ufm_probe;
+static device_attach_t ufm_attach;
+static device_detach_t ufm_detach;
+
+static usb2_fifo_ioctl_t ufm_ioctl;
+static usb2_fifo_open_t ufm_open;
+
+static struct usb2_fifo_methods ufm_fifo_methods = {
+ .f_ioctl = &ufm_ioctl,
+ .f_open = &ufm_open,
+ .basename[0] = "ufm",
+};
+
+static int ufm_do_req(struct ufm_softc *, uint8_t, uint16_t, uint16_t,
+ uint8_t *);
+static int ufm_set_freq(struct ufm_softc *, void *);
+static int ufm_get_freq(struct ufm_softc *, void *);
+static int ufm_start(struct ufm_softc *, void *);
+static int ufm_stop(struct ufm_softc *, void *);
+static int ufm_get_stat(struct ufm_softc *, void *);
+
+static devclass_t ufm_devclass;
+
+static device_method_t ufm_methods[] = {
+ DEVMETHOD(device_probe, ufm_probe),
+ DEVMETHOD(device_attach, ufm_attach),
+ DEVMETHOD(device_detach, ufm_detach),
+ {0, 0}
+};
+
+static driver_t ufm_driver = {
+ .name = "ufm",
+ .methods = ufm_methods,
+ .size = sizeof(struct ufm_softc),
+};
+
+DRIVER_MODULE(ufm, ushub, ufm_driver, ufm_devclass, NULL, 0);
+MODULE_DEPEND(ufm, usb, 1, 1, 1);
+
+static int
+ufm_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if ((uaa->info.idVendor == USB_VENDOR_CYPRESS) &&
+ (uaa->info.idProduct == USB_PRODUCT_CYPRESS_FMRADIO)) {
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+ufm_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ufm_softc *sc = device_get_softc(dev);
+ int error;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_unit = device_get_unit(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
+ device_get_nameunit(dev));
+
+ mtx_init(&sc->sc_mtx, "ufm lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ device_set_usb2_desc(dev);
+
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &ufm_fifo_methods, &sc->sc_fifo,
+ device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ ufm_detach(dev);
+ return (ENXIO);
+}
+
+static int
+ufm_detach(device_t dev)
+{
+ struct ufm_softc *sc = device_get_softc(dev);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static int
+ufm_open(struct usb2_fifo *dev, int fflags, struct thread *td)
+{
+ if ((fflags & (FWRITE | FREAD)) != (FWRITE | FREAD)) {
+ return (EACCES);
+ }
+ return (0);
+}
+
+static int
+ufm_do_req(struct ufm_softc *sc, uint8_t request,
+ uint16_t value, uint16_t index, uint8_t *retbuf)
+{
+ int error;
+
+ struct usb2_device_request req;
+ uint8_t buf[1];
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = request;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 1);
+
+ error = usb2_do_request(sc->sc_udev, NULL, &req, buf);
+
+ if (retbuf) {
+ *retbuf = buf[0];
+ }
+ if (error) {
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static int
+ufm_set_freq(struct ufm_softc *sc, void *addr)
+{
+ int freq = *(int *)addr;
+
+ /*
+ * Freq now is in Hz. We need to convert it to the frequency
+ * that the radio wants. This frequency is 10.7MHz above
+ * the actual frequency. We then need to convert to
+ * units of 12.5kHz. We add one to the IFM to make rounding
+ * easier.
+ */
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_freq = freq;
+ mtx_unlock(&sc->sc_mtx);
+
+ freq = (freq + 10700001) / 12500;
+
+ /* This appears to set the frequency */
+ if (ufm_do_req(sc, UFM_CMD_SET_FREQ,
+ freq >> 8, freq, NULL) != 0) {
+ return (EIO);
+ }
+ /* Not sure what this does */
+ if (ufm_do_req(sc, UFM_CMD0,
+ 0x96, 0xb7, NULL) != 0) {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ufm_get_freq(struct ufm_softc *sc, void *addr)
+{
+ int *valp = (int *)addr;
+
+ mtx_lock(&sc->sc_mtx);
+ *valp = sc->sc_freq;
+ mtx_unlock(&sc->sc_mtx);
+ return (0);
+}
+
+static int
+ufm_start(struct ufm_softc *sc, void *addr)
+{
+ uint8_t ret;
+
+ if (ufm_do_req(sc, UFM_CMD0,
+ 0x00, 0xc7, &ret)) {
+ return (EIO);
+ }
+ if (ufm_do_req(sc, UFM_CMD2,
+ 0x01, 0x00, &ret)) {
+ return (EIO);
+ }
+ if (ret & 0x1) {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ufm_stop(struct ufm_softc *sc, void *addr)
+{
+ if (ufm_do_req(sc, UFM_CMD0,
+ 0x16, 0x1C, NULL)) {
+ return (EIO);
+ }
+ if (ufm_do_req(sc, UFM_CMD2,
+ 0x00, 0x00, NULL)) {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+ufm_get_stat(struct ufm_softc *sc, void *addr)
+{
+ uint8_t ret;
+
+ /*
+ * Note, there's a 240ms settle time before the status
+ * will be valid, so sleep that amount.
+ */
+ usb2_pause_mtx(NULL, hz / 4);
+
+ if (ufm_do_req(sc, UFM_CMD0,
+ 0x00, 0x24, &ret)) {
+ return (EIO);
+ }
+ *(int *)addr = ret;
+
+ return (0);
+}
+
+static int
+ufm_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr,
+ int fflags, struct thread *td)
+{
+ struct ufm_softc *sc = fifo->priv_sc0;
+ int error = 0;
+
+ switch (cmd) {
+ case FM_SET_FREQ:
+ error = ufm_set_freq(sc, addr);
+ break;
+ case FM_GET_FREQ:
+ error = ufm_get_freq(sc, addr);
+ break;
+ case FM_START:
+ error = ufm_start(sc, addr);
+ break;
+ case FM_STOP:
+ error = ufm_stop(sc, addr);
+ break;
+ case FM_GET_STAT:
+ error = ufm_get_stat(sc, addr);
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return (error);
+}
diff --git a/sys/dev/usb/net/if_aue.c b/sys/dev/usb/net/if_aue.c
new file mode 100644
index 0000000..026fa7c
--- /dev/null
+++ b/sys/dev/usb/net/if_aue.c
@@ -0,0 +1,1054 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Copyright (c) 2006
+ * Alfred Perlstein <alfred@freebsd.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ADMtek AN986 Pegasus and AN8511 Pegasus II USB to ethernet driver.
+ * Datasheet is available from http://www.admtek.com.tw.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ *
+ * SMP locking by Alfred Perlstein <alfred@freebsd.org>.
+ * RED Inc.
+ */
+
+/*
+ * The Pegasus chip uses four USB "endpoints" to provide 10/100 ethernet
+ * support: the control endpoint for reading/writing registers, burst
+ * read endpoint for packet reception, burst write for packet transmission
+ * and one for "interrupts." The chip uses the same RX filter scheme
+ * as the other ADMtek ethernet parts: one perfect filter entry for the
+ * the station address and a 64-bit multicast hash table. The chip supports
+ * both MII and HomePNA attachments.
+ *
+ * Since the maximum data transfer speed of USB is supposed to be 12Mbps,
+ * you're never really going to get 100Mbps speeds from this device. I
+ * think the idea is to allow the device to connect to 10 or 100Mbps
+ * networks, not necessarily to provide 100Mbps performance. Also, since
+ * the controller uses an external PHY chip, it's possible that board
+ * designers might simply choose a 10Mbps PHY.
+ *
+ * Registers are accessed using usb2_ether_do_request(). Packet
+ * transfers are done using usb2_transfer() and friends.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR aue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_auereg.h>
+
+#if USB_DEBUG
+static int aue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, aue, CTLFLAG_RW, 0, "USB aue");
+SYSCTL_INT(_hw_usb2_aue, OID_AUTO, debug, CTLFLAG_RW, &aue_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb2_device_id aue_devs[] = {
+ {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460B, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_DSB650TX_PNA, 0)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UFE1000, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX10, 0)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX1, AUE_FLAG_PNA | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX2, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX4, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX5, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX6, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX7, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX8, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_XX9, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SS1001, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_USB320_EC, 0)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_2, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_3, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII_4, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUSII, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ADMTEK, USB_PRODUCT_ADMTEK_PEGASUS, AUE_FLAG_PNA | AUE_FLAG_DUAL_PHY)},
+ {USB_VPI(USB_VENDOR_AEI, USB_PRODUCT_AEI_FASTETHERNET, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ALLIEDTELESYN, USB_PRODUCT_ALLIEDTELESYN_ATUSB100, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC110T, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2LAN, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB100, 0)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBE100, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBEL100, 0)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USBLP100, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXS, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TX, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX1, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX3, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX4, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX_PNA, AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650TX, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ELCON, USB_PRODUCT_ELCON_PLAN, AUE_FLAG_PNA | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSB20, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBLTX, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX0, 0)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX1, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX2, 0)},
+ {USB_VPI(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_LDUSBTX3, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_ELSA, USB_PRODUCT_ELSA_USB2ETHERNET, 0)},
+ {USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNBR402W, 0)},
+ {USB_VPI(USB_VENDOR_HAWKING, USB_PRODUCT_HAWKING_UF100, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HN210E, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTXS, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETTX, 0)},
+ {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_KNU101TX, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100H1, AUE_FLAG_LSYS | AUE_FLAG_PNA)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB100TX, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TA, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX1, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10TX2, AUE_FLAG_LSYS | AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, AUE_FLAG_LSYS)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUA2TX5, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX1, 0)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUATX5, 0)},
+ {USB_VPI(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_MN110, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA101, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SIEMENS, USB_PRODUCT_SIEMENS_SPEEDSTREAM, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_USBTOETHER, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTNIC, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2202USB, 0)},
+ {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2206USB, AUE_FLAG_PII)},
+ {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB100, 0)},
+ {USB_VPI(USB_VENDOR_SOHOWARE, USB_PRODUCT_SOHOWARE_NUB110, AUE_FLAG_PII)},
+};
+
+/* prototypes */
+
+static device_probe_t aue_probe;
+static device_attach_t aue_attach;
+static device_detach_t aue_detach;
+static device_shutdown_t aue_shutdown;
+static miibus_readreg_t aue_miibus_readreg;
+static miibus_writereg_t aue_miibus_writereg;
+static miibus_statchg_t aue_miibus_statchg;
+
+static usb2_callback_t aue_intr_callback;
+static usb2_callback_t aue_bulk_read_callback;
+static usb2_callback_t aue_bulk_write_callback;
+
+static usb2_ether_fn_t aue_attach_post;
+static usb2_ether_fn_t aue_init;
+static usb2_ether_fn_t aue_stop;
+static usb2_ether_fn_t aue_start;
+static usb2_ether_fn_t aue_tick;
+static usb2_ether_fn_t aue_setmulti;
+static usb2_ether_fn_t aue_setpromisc;
+
+static uint8_t aue_csr_read_1(struct aue_softc *, uint16_t);
+static uint16_t aue_csr_read_2(struct aue_softc *, uint16_t);
+static void aue_csr_write_1(struct aue_softc *, uint16_t, uint8_t);
+static void aue_csr_write_2(struct aue_softc *, uint16_t, uint16_t);
+static void aue_eeprom_getword(struct aue_softc *, int, uint16_t *);
+static void aue_read_eeprom(struct aue_softc *, uint8_t *, uint16_t,
+ uint16_t);
+static void aue_reset(struct aue_softc *);
+static void aue_reset_pegasus_II(struct aue_softc *);
+
+static int aue_ifmedia_upd(struct ifnet *);
+static void aue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static const struct usb2_config aue_config[AUE_N_TRANSFER] = {
+
+ [AUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = aue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [AUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 4 + ETHER_CRC_LEN),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = aue_bulk_read_callback,
+ },
+
+ [AUE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = aue_intr_callback,
+ },
+};
+
+static device_method_t aue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, aue_probe),
+ DEVMETHOD(device_attach, aue_attach),
+ DEVMETHOD(device_detach, aue_detach),
+ DEVMETHOD(device_shutdown, aue_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, aue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, aue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, aue_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t aue_driver = {
+ .name = "aue",
+ .methods = aue_methods,
+ .size = sizeof(struct aue_softc)
+};
+
+static devclass_t aue_devclass;
+
+DRIVER_MODULE(aue, ushub, aue_driver, aue_devclass, NULL, 0);
+DRIVER_MODULE(miibus, aue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(aue, uether, 1, 1, 1);
+MODULE_DEPEND(aue, usb, 1, 1, 1);
+MODULE_DEPEND(aue, ether, 1, 1, 1);
+MODULE_DEPEND(aue, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods aue_ue_methods = {
+ .ue_attach_post = aue_attach_post,
+ .ue_start = aue_start,
+ .ue_init = aue_init,
+ .ue_stop = aue_stop,
+ .ue_tick = aue_tick,
+ .ue_setmulti = aue_setmulti,
+ .ue_setpromisc = aue_setpromisc,
+ .ue_mii_upd = aue_ifmedia_upd,
+ .ue_mii_sts = aue_ifmedia_sts,
+};
+
+#define AUE_SETBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) | (x))
+
+#define AUE_CLRBIT(sc, reg, x) \
+ aue_csr_write_1(sc, reg, aue_csr_read_1(sc, reg) & ~(x))
+
+static uint8_t
+aue_csr_read_1(struct aue_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
+ if (err)
+ return (0);
+ return (val);
+}
+
+static uint16_t
+aue_csr_read_2(struct aue_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ err = usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
+ if (err)
+ return (0);
+ return (le16toh(val));
+}
+
+static void
+aue_csr_write_1(struct aue_softc *sc, uint16_t reg, uint8_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ req.wValue[0] = val;
+ req.wValue[1] = 0;
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* error ignored */
+ }
+}
+
+static void
+aue_csr_write_2(struct aue_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = AUE_UR_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ val = htole16(val);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* error ignored */
+ }
+}
+
+/*
+ * Read a word of data stored in the EEPROM at address 'addr.'
+ */
+static void
+aue_eeprom_getword(struct aue_softc *sc, int addr, uint16_t *dest)
+{
+ int i;
+ uint16_t word = 0;
+
+ aue_csr_write_1(sc, AUE_EE_REG, addr);
+ aue_csr_write_1(sc, AUE_EE_CTL, AUE_EECTL_READ);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_EE_CTL) & AUE_EECTL_DONE)
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "EEPROM read timed out\n");
+
+ word = aue_csr_read_2(sc, AUE_EE_DATA);
+ *dest = word;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static void
+aue_read_eeprom(struct aue_softc *sc, uint8_t *dest,
+ uint16_t off, uint16_t len)
+{
+ uint16_t *ptr = (uint16_t *)dest;
+ int i;
+
+ for (i = 0; i != len; i++, ptr++)
+ aue_eeprom_getword(sc, off + i, ptr);
+}
+
+static int
+aue_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ int i, locked;
+ uint16_t val = 0;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AUE_LOCK(sc);
+
+ /*
+ * The Am79C901 HomePNA PHY actually contains two transceivers: a 1Mbps
+ * HomePNA PHY and a 10Mbps full/half duplex ethernet PHY with NWAY
+ * autoneg. However in the ADMtek adapter, only the 1Mbps PHY is
+ * actually connected to anything, so we ignore the 10Mbps one. It
+ * happens to be configured for MII address 3, so we filter that out.
+ */
+ if (sc->sc_flags & AUE_FLAG_DUAL_PHY) {
+ if (phy == 3)
+ goto done;
+#if 0
+ if (phy != 1)
+ goto done;
+#endif
+ }
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_READ);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "MII read timed out\n");
+
+ val = aue_csr_read_2(sc, AUE_PHY_DATA);
+
+done:
+ if (!locked)
+ AUE_UNLOCK(sc);
+ return (val);
+}
+
+static int
+aue_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ int i;
+ int locked;
+
+ if (phy == 3)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AUE_LOCK(sc);
+
+ aue_csr_write_2(sc, AUE_PHY_DATA, data);
+ aue_csr_write_1(sc, AUE_PHY_ADDR, phy);
+ aue_csr_write_1(sc, AUE_PHY_CTL, reg | AUE_PHYCTL_WRITE);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (aue_csr_read_1(sc, AUE_PHY_CTL) & AUE_PHYCTL_DONE)
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "MII read timed out\n");
+
+ if (!locked)
+ AUE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+aue_miibus_statchg(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ int locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AUE_LOCK(sc);
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+ else
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_SPEEDSEL);
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+ else
+ AUE_CLRBIT(sc, AUE_CTL1, AUE_CTL1_DUPLEX);
+
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_RX_ENB | AUE_CTL0_TX_ENB);
+
+ /*
+ * Set the LED modes on the LinkSys adapter.
+ * This turns on the 'dual link LED' bin in the auxmode
+ * register of the Broadcom PHY.
+ */
+ if (sc->sc_flags & AUE_FLAG_LSYS) {
+ uint16_t auxmode;
+
+ auxmode = aue_miibus_readreg(dev, 0, 0x1b);
+ aue_miibus_writereg(dev, 0, 0x1b, auxmode | 0x04);
+ }
+ if (!locked)
+ AUE_UNLOCK(sc);
+}
+
+#define AUE_BITS 6
+static void
+aue_setmulti(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h = 0;
+ uint32_t i;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+ return;
+ }
+
+ AUE_CLRBIT(sc, AUE_CTL0, AUE_CTL0_ALLMULTI);
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_le(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) & ((1 << AUE_BITS) - 1);
+ hashtbl[(h >> 3)] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /* write the hashtable */
+ for (i = 0; i != 8; i++)
+ aue_csr_write_1(sc, AUE_MAR0 + i, hashtbl[i]);
+}
+
+static void
+aue_reset_pegasus_II(struct aue_softc *sc)
+{
+ /* Magic constants taken from Linux driver. */
+ aue_csr_write_1(sc, AUE_REG_1D, 0);
+ aue_csr_write_1(sc, AUE_REG_7B, 2);
+#if 0
+ if ((sc->sc_flags & HAS_HOME_PNA) && mii_mode)
+ aue_csr_write_1(sc, AUE_REG_81, 6);
+ else
+#endif
+ aue_csr_write_1(sc, AUE_REG_81, 2);
+}
+
+static void
+aue_reset(struct aue_softc *sc)
+{
+ int i;
+
+ AUE_SETBIT(sc, AUE_CTL1, AUE_CTL1_RESETMAC);
+
+ for (i = 0; i != AUE_TIMEOUT; i++) {
+ if (!(aue_csr_read_1(sc, AUE_CTL1) & AUE_CTL1_RESETMAC))
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ if (i == AUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "reset failed\n");
+
+ /*
+ * The PHY(s) attached to the Pegasus chip may be held
+ * in reset until we flip on the GPIO outputs. Make sure
+ * to set the GPIO pins high so that the PHY(s) will
+ * be enabled.
+ *
+ * Note: We force all of the GPIO pins low first, *then*
+ * enable the ones we want.
+ */
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0);
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_OUT0|AUE_GPIO_SEL0|AUE_GPIO_SEL1);
+
+ if (sc->sc_flags & AUE_FLAG_LSYS) {
+ /* Grrr. LinkSys has to be different from everyone else. */
+ aue_csr_write_1(sc, AUE_GPIO0, AUE_GPIO_SEL0|AUE_GPIO_SEL1);
+ aue_csr_write_1(sc, AUE_GPIO0,
+ AUE_GPIO_SEL0|AUE_GPIO_SEL1|AUE_GPIO_OUT0);
+ }
+ if (sc->sc_flags & AUE_FLAG_PII)
+ aue_reset_pegasus_II(sc);
+
+ /* Wait a little while for the chip to get its brains in order: */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+aue_attach_post(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+
+ /* reset the adapter */
+ aue_reset(sc);
+
+ /* get station address from the EEPROM */
+ aue_read_eeprom(sc, ue->ue_eaddr, 0, 3);
+}
+
+/*
+ * Probe for a Pegasus chip.
+ */
+static int
+aue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != AUE_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != AUE_IFACE_IDX)
+ return (ENXIO);
+ /*
+ * Belkin USB Bluetooth dongles of the F8T012xx1 model series conflict
+ * with older Belkin USB2LAN adapters. Skip if_aue if we detect one of
+ * the devices that look like Bluetooth adapters.
+ */
+ if (uaa->info.idVendor == USB_VENDOR_BELKIN &&
+ uaa->info.idProduct == USB_PRODUCT_BELKIN_F8T012 &&
+ uaa->info.bcdDevice == 0x0413)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(aue_devs, sizeof(aue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+aue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct aue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ if (uaa->info.bcdDevice >= 0x0201) {
+ /* XXX currently undocumented */
+ sc->sc_flags |= AUE_FLAG_VER_2;
+ }
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, aue_config, AUE_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &aue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ aue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+aue_detach(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, AUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+aue_intr_callback(struct usb2_xfer *xfer)
+{
+ struct aue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct aue_intrpkt pkt;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+ xfer->actlen >= sizeof(pkt)) {
+
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt));
+
+ if (pkt.aue_txstat0)
+ ifp->if_oerrors++;
+ if (pkt.aue_txstat0 & (AUE_TXSTAT0_LATECOLL &
+ AUE_TXSTAT0_EXCESSCOLL))
+ ifp->if_collisions++;
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct aue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct aue_rxpkt stat;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "received %d bytes\n", xfer->actlen);
+
+ if (sc->sc_flags & AUE_FLAG_VER_2) {
+
+ if (xfer->actlen == 0) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ } else {
+
+ if (xfer->actlen <= (sizeof(stat) + ETHER_CRC_LEN)) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers,
+ xfer->actlen - sizeof(stat), &stat, sizeof(stat));
+
+ /*
+ * turn off all the non-error bits in the rx status
+ * word:
+ */
+ stat.aue_rxstat &= AUE_RXSTAT_MASK;
+ if (stat.aue_rxstat) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ /* No errors; receive the packet. */
+ xfer->actlen -= (sizeof(stat) + ETHER_CRC_LEN);
+ }
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct aue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer of %d bytes complete\n", xfer->actlen);
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & AUE_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ if (sc->sc_flags & AUE_FLAG_VER_2) {
+
+ xfer->frlengths[0] = m->m_pkthdr.len;
+
+ usb2_m_copy_in(xfer->frbuffers, 0,
+ m, 0, m->m_pkthdr.len);
+
+ } else {
+
+ xfer->frlengths[0] = (m->m_pkthdr.len + 2);
+
+ /*
+ * The ADMtek documentation says that the
+ * packet length is supposed to be specified
+ * in the first two bytes of the transfer,
+ * however it actually seems to ignore this
+ * info and base the frame size on the bulk
+ * transfer length.
+ */
+ buf[0] = (uint8_t)(m->m_pkthdr.len);
+ buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+ }
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+aue_tick(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & AUE_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= AUE_FLAG_LINK;
+ aue_start(ue);
+ }
+}
+
+static void
+aue_start(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[AUE_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AUE_BULK_DT_WR]);
+}
+
+static void
+aue_init(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ int i;
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O
+ */
+ aue_reset(sc);
+
+ /* Set MAC address */
+ for (i = 0; i != ETHER_ADDR_LEN; i++)
+ aue_csr_write_1(sc, AUE_PAR0 + i, IF_LLADDR(ifp)[i]);
+
+ /* update promiscuous setting */
+ aue_setpromisc(ue);
+
+ /* Load the multicast filter. */
+ aue_setmulti(ue);
+
+ /* Enable RX and TX */
+ aue_csr_write_1(sc, AUE_CTL0, AUE_CTL0_RXSTAT_APPEND | AUE_CTL0_RX_ENB);
+ AUE_SETBIT(sc, AUE_CTL0, AUE_CTL0_TX_ENB);
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_EP3_CLR);
+
+ usb2_transfer_set_stall(sc->sc_xfer[AUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ aue_start(ue);
+}
+
+static void
+aue_setpromisc(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* if we want promiscuous mode, set the allframes bit: */
+ if (ifp->if_flags & IFF_PROMISC)
+ AUE_SETBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+ else
+ AUE_CLRBIT(sc, AUE_CTL2, AUE_CTL2_RX_PROMISC);
+}
+
+/*
+ * Set media options.
+ */
+static int
+aue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~AUE_FLAG_LINK;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+aue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct aue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ AUE_LOCK(sc);
+ mii_pollstat(mii);
+ AUE_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+aue_stop(struct usb2_ether *ue)
+{
+ struct aue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ AUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~AUE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[AUE_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[AUE_INTR_DT_RD]);
+
+ aue_csr_write_1(sc, AUE_CTL0, 0);
+ aue_csr_write_1(sc, AUE_CTL1, 0);
+ aue_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+aue_shutdown(device_t dev)
+{
+ struct aue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_auereg.h b/sys/dev/usb/net/if_auereg.h
new file mode 100644
index 0000000..249c913
--- /dev/null
+++ b/sys/dev/usb/net/if_auereg.h
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Register definitions for ADMtek Pegasus AN986 USB to Ethernet
+ * chip. The Pegasus uses a total of four USB endpoints: the control
+ * endpoint (0), a bulk read endpoint for receiving packets (1),
+ * a bulk write endpoint for sending packets (2) and an interrupt
+ * endpoint for passing RX and TX status (3). Endpoint 0 is used
+ * to read and write the ethernet module's registers. All registers
+ * are 8 bits wide.
+ *
+ * Packet transfer is done in 64 byte chunks. The last chunk in a
+ * transfer is denoted by having a length less that 64 bytes. For
+ * the RX case, the data includes an optional RX status word.
+ */
+
+#define AUE_UR_READREG 0xF0
+#define AUE_UR_WRITEREG 0xF1
+
+#define AUE_CONFIG_INDEX 0 /* config number 1 */
+#define AUE_IFACE_IDX 0
+
+/*
+ * Note that while the ADMtek technically has four endpoints, the control
+ * endpoint (endpoint 0) is regarded as special by the USB code and drivers
+ * don't have direct access to it (we access it using usb2_do_request()
+ * when reading/writing registers. Consequently, our endpoint indexes
+ * don't match those in the ADMtek Pegasus manual: we consider the RX data
+ * endpoint to be index 0 and work up from there.
+ */
+enum {
+ AUE_BULK_DT_WR,
+ AUE_BULK_DT_RD,
+ AUE_INTR_DT_RD,
+ AUE_N_TRANSFER,
+};
+
+#define AUE_INTR_PKTLEN 0x8
+
+#define AUE_CTL0 0x00
+#define AUE_CTL1 0x01
+#define AUE_CTL2 0x02
+#define AUE_MAR0 0x08
+#define AUE_MAR1 0x09
+#define AUE_MAR2 0x0A
+#define AUE_MAR3 0x0B
+#define AUE_MAR4 0x0C
+#define AUE_MAR5 0x0D
+#define AUE_MAR6 0x0E
+#define AUE_MAR7 0x0F
+#define AUE_MAR AUE_MAR0
+#define AUE_PAR0 0x10
+#define AUE_PAR1 0x11
+#define AUE_PAR2 0x12
+#define AUE_PAR3 0x13
+#define AUE_PAR4 0x14
+#define AUE_PAR5 0x15
+#define AUE_PAR AUE_PAR0
+#define AUE_PAUSE0 0x18
+#define AUE_PAUSE1 0x19
+#define AUE_PAUSE AUE_PAUSE0
+#define AUE_RX_FLOWCTL_CNT 0x1A
+#define AUE_RX_FLOWCTL_FIFO 0x1B
+#define AUE_REG_1D 0x1D
+#define AUE_EE_REG 0x20
+#define AUE_EE_DATA0 0x21
+#define AUE_EE_DATA1 0x22
+#define AUE_EE_DATA AUE_EE_DATA0
+#define AUE_EE_CTL 0x23
+#define AUE_PHY_ADDR 0x25
+#define AUE_PHY_DATA0 0x26
+#define AUE_PHY_DATA1 0x27
+#define AUE_PHY_DATA AUE_PHY_DATA0
+#define AUE_PHY_CTL 0x28
+#define AUE_USB_STS 0x2A
+#define AUE_TXSTAT0 0x2B
+#define AUE_TXSTAT1 0x2C
+#define AUE_TXSTAT AUE_TXSTAT0
+#define AUE_RXSTAT 0x2D
+#define AUE_PKTLOST0 0x2E
+#define AUE_PKTLOST1 0x2F
+#define AUE_PKTLOST AUE_PKTLOST0
+
+#define AUE_REG_7B 0x7B
+#define AUE_GPIO0 0x7E
+#define AUE_GPIO1 0x7F
+#define AUE_REG_81 0x81
+
+#define AUE_CTL0_INCLUDE_RXCRC 0x01
+#define AUE_CTL0_ALLMULTI 0x02
+#define AUE_CTL0_STOP_BACKOFF 0x04
+#define AUE_CTL0_RXSTAT_APPEND 0x08
+#define AUE_CTL0_WAKEON_ENB 0x10
+#define AUE_CTL0_RXPAUSE_ENB 0x20
+#define AUE_CTL0_RX_ENB 0x40
+#define AUE_CTL0_TX_ENB 0x80
+
+#define AUE_CTL1_HOMELAN 0x04
+#define AUE_CTL1_RESETMAC 0x08
+#define AUE_CTL1_SPEEDSEL 0x10 /* 0 = 10mbps, 1 = 100mbps */
+#define AUE_CTL1_DUPLEX 0x20 /* 0 = half, 1 = full */
+#define AUE_CTL1_DELAYHOME 0x40
+
+#define AUE_CTL2_EP3_CLR 0x01 /* reading EP3 clrs status regs */
+#define AUE_CTL2_RX_BADFRAMES 0x02
+#define AUE_CTL2_RX_PROMISC 0x04
+#define AUE_CTL2_LOOPBACK 0x08
+#define AUE_CTL2_EEPROMWR_ENB 0x10
+#define AUE_CTL2_EEPROM_LOAD 0x20
+
+#define AUE_EECTL_WRITE 0x01
+#define AUE_EECTL_READ 0x02
+#define AUE_EECTL_DONE 0x04
+
+#define AUE_PHYCTL_PHYREG 0x1F
+#define AUE_PHYCTL_WRITE 0x20
+#define AUE_PHYCTL_READ 0x40
+#define AUE_PHYCTL_DONE 0x80
+
+#define AUE_USBSTS_SUSPEND 0x01
+#define AUE_USBSTS_RESUME 0x02
+
+#define AUE_TXSTAT0_JABTIMO 0x04
+#define AUE_TXSTAT0_CARLOSS 0x08
+#define AUE_TXSTAT0_NOCARRIER 0x10
+#define AUE_TXSTAT0_LATECOLL 0x20
+#define AUE_TXSTAT0_EXCESSCOLL 0x40
+#define AUE_TXSTAT0_UNDERRUN 0x80
+
+#define AUE_TXSTAT1_PKTCNT 0x0F
+#define AUE_TXSTAT1_FIFO_EMPTY 0x40
+#define AUE_TXSTAT1_FIFO_FULL 0x80
+
+#define AUE_RXSTAT_OVERRUN 0x01
+#define AUE_RXSTAT_PAUSE 0x02
+
+#define AUE_GPIO_IN0 0x01
+#define AUE_GPIO_OUT0 0x02
+#define AUE_GPIO_SEL0 0x04
+#define AUE_GPIO_IN1 0x08
+#define AUE_GPIO_OUT1 0x10
+#define AUE_GPIO_SEL1 0x20
+
+#define AUE_TIMEOUT 100 /* 10*ms */
+#define AUE_MIN_FRAMELEN 60
+
+#define AUE_RXSTAT_MCAST 0x01
+#define AUE_RXSTAT_GIANT 0x02
+#define AUE_RXSTAT_RUNT 0x04
+#define AUE_RXSTAT_CRCERR 0x08
+#define AUE_RXSTAT_DRIBBLE 0x10
+#define AUE_RXSTAT_MASK 0x1E
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+struct aue_intrpkt {
+ uint8_t aue_txstat0;
+ uint8_t aue_txstat1;
+ uint8_t aue_rxstat;
+ uint8_t aue_rxlostpkt0;
+ uint8_t aue_rxlostpkt1;
+ uint8_t aue_wakeupstat;
+ uint8_t aue_rsvd;
+} __packed;
+
+struct aue_rxpkt {
+ uint16_t aue_pktlen;
+ uint8_t aue_rxstat;
+ uint8_t pad;
+} __packed;
+
+struct aue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[AUE_N_TRANSFER];
+
+ int sc_flags;
+#define AUE_FLAG_LSYS 0x0001 /* use Linksys reset */
+#define AUE_FLAG_PNA 0x0002 /* has Home PNA */
+#define AUE_FLAG_PII 0x0004 /* Pegasus II chip */
+#define AUE_FLAG_LINK 0x0008 /* wait for link to come up */
+#define AUE_FLAG_VER_2 0x0200 /* chip is version 2 */
+#define AUE_FLAG_DUAL_PHY 0x0400 /* chip has two transcivers */
+};
+
+#define AUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define AUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define AUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_axe.c b/sys/dev/usb/net/if_axe.c
new file mode 100644
index 0000000..0555e69
--- /dev/null
+++ b/sys/dev/usb/net/if_axe.c
@@ -0,0 +1,1076 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ASIX Electronics AX88172/AX88178/AX88778 USB 2.0 ethernet driver.
+ * Used in the LinkSys USB200M and various other adapters.
+ *
+ * Manuals available from:
+ * http://www.asix.com.tw/datasheet/mac/Ax88172.PDF
+ * Note: you need the manual for the AX88170 chip (USB 1.x ethernet
+ * controller) to find the definitions for the RX control register.
+ * http://www.asix.com.tw/datasheet/mac/Ax88170.PDF
+ *
+ * Written by Bill Paul <wpaul@windriver.com>
+ * Senior Engineer
+ * Wind River Systems
+ */
+
+/*
+ * The AX88172 provides USB ethernet supports at 10 and 100Mbps.
+ * It uses an external PHY (reference designs use a RealTek chip),
+ * and has a 64-bit multicast hash filter. There is some information
+ * missing from the manual which one needs to know in order to make
+ * the chip function:
+ *
+ * - You must set bit 7 in the RX control register, otherwise the
+ * chip won't receive any packets.
+ * - You must initialize all 3 IPG registers, or you won't be able
+ * to send any packets.
+ *
+ * Note that this device appears to only support loading the station
+ * address via autload from the EEPROM (i.e. there's no way to manaully
+ * set it).
+ *
+ * (Adam Weinberger wanted me to name this driver if_gir.c.)
+ */
+
+/*
+ * Ax88178 and Ax88772 support backported from the OpenBSD driver.
+ * 2007/02/12, J.R. Oldroyd, fbsd@opal.com
+ *
+ * Manual here:
+ * http://www.asix.com.tw/FrootAttach/datasheet/AX88178_datasheet_Rev10.pdf
+ * http://www.asix.com.tw/FrootAttach/datasheet/AX88772_datasheet_Rev10.pdf
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR axe_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_axereg.h>
+
+/*
+ * AXE_178_MAX_FRAME_BURST
+ * max frame burst size for Ax88178 and Ax88772
+ * 0 2048 bytes
+ * 1 4096 bytes
+ * 2 8192 bytes
+ * 3 16384 bytes
+ * use the largest your system can handle without USB stalling.
+ *
+ * NB: 88772 parts appear to generate lots of input errors with
+ * a 2K rx buffer and 8K is only slightly faster than 4K on an
+ * EHCI port on a T42 so change at your own risk.
+ */
+#define AXE_178_MAX_FRAME_BURST 1
+
+#if USB_DEBUG
+static int axe_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, axe, CTLFLAG_RW, 0, "USB axe");
+SYSCTL_INT(_hw_usb2_axe, OID_AUTO, debug, CTLFLAG_RW, &axe_debug, 0,
+ "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb2_device_id axe_devs[] = {
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_UF200, 0)},
+ {USB_VPI(USB_VENDOR_ACERCM, USB_PRODUCT_ACERCM_EP1427X2, 0)},
+ {USB_VPI(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ETHERNET, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88172, 0)},
+ {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88178, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_ASIX, USB_PRODUCT_ASIX_AX88772, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC210T, 0)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D5055, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_BILLIONTON, USB_PRODUCT_BILLIONTON_USB2AR, 0)},
+ {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_USB200MV2, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB2_TX, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DUBE100B1, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_GWUSB2E, 0)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_ETGUS2, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_JVC, USB_PRODUCT_JVC_MP_PRX1, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS2, USB_PRODUCT_LINKSYS2_USB200M, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS4, USB_PRODUCT_LINKSYS4_USB1000, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAU2KTX, 0)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_FA120, 0)},
+ {USB_VPI(USB_VENDOR_OQO, USB_PRODUCT_OQO_ETHER01PLUS, AXE_FLAG_772)},
+ {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GU1000T, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_LN029, 0)},
+ {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_LN028, AXE_FLAG_178)},
+ {USB_VPI(USB_VENDOR_SYSTEMTALKS, USB_PRODUCT_SYSTEMTALKS_SGCX2UL, 0)},
+};
+
+static device_probe_t axe_probe;
+static device_attach_t axe_attach;
+static device_detach_t axe_detach;
+static device_shutdown_t axe_shutdown;
+
+static usb2_callback_t axe_intr_callback;
+static usb2_callback_t axe_bulk_read_callback;
+static usb2_callback_t axe_bulk_write_callback;
+
+static miibus_readreg_t axe_miibus_readreg;
+static miibus_writereg_t axe_miibus_writereg;
+static miibus_statchg_t axe_miibus_statchg;
+
+static usb2_ether_fn_t axe_attach_post;
+static usb2_ether_fn_t axe_init;
+static usb2_ether_fn_t axe_stop;
+static usb2_ether_fn_t axe_start;
+static usb2_ether_fn_t axe_tick;
+static usb2_ether_fn_t axe_setmulti;
+static usb2_ether_fn_t axe_setpromisc;
+
+static int axe_ifmedia_upd(struct ifnet *);
+static void axe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static int axe_cmd(struct axe_softc *, int, int, int, void *);
+static void axe_ax88178_init(struct axe_softc *);
+static void axe_ax88772_init(struct axe_softc *);
+static int axe_get_phyno(struct axe_softc *, int);
+
+static const struct usb2_config axe_config[AXE_N_TRANSFER] = {
+
+ [AXE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = AXE_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = axe_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [AXE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+#if (MCLBYTES < 2048)
+#error "(MCLBYTES < 2048)"
+#endif
+ .mh.bufsize = MCLBYTES,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = axe_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+
+ [AXE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = axe_intr_callback,
+ },
+};
+
+static device_method_t axe_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, axe_probe),
+ DEVMETHOD(device_attach, axe_attach),
+ DEVMETHOD(device_detach, axe_detach),
+ DEVMETHOD(device_shutdown, axe_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, axe_miibus_readreg),
+ DEVMETHOD(miibus_writereg, axe_miibus_writereg),
+ DEVMETHOD(miibus_statchg, axe_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t axe_driver = {
+ .name = "axe",
+ .methods = axe_methods,
+ .size = sizeof(struct axe_softc),
+};
+
+static devclass_t axe_devclass;
+
+DRIVER_MODULE(axe, ushub, axe_driver, axe_devclass, NULL, 0);
+DRIVER_MODULE(miibus, axe, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(axe, uether, 1, 1, 1);
+MODULE_DEPEND(axe, usb, 1, 1, 1);
+MODULE_DEPEND(axe, ether, 1, 1, 1);
+MODULE_DEPEND(axe, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods axe_ue_methods = {
+ .ue_attach_post = axe_attach_post,
+ .ue_start = axe_start,
+ .ue_init = axe_init,
+ .ue_stop = axe_stop,
+ .ue_tick = axe_tick,
+ .ue_setmulti = axe_setmulti,
+ .ue_setpromisc = axe_setpromisc,
+ .ue_mii_upd = axe_ifmedia_upd,
+ .ue_mii_sts = axe_ifmedia_sts,
+};
+
+static int
+axe_cmd(struct axe_softc *sc, int cmd, int index, int val, void *buf)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ req.bmRequestType = (AXE_CMD_IS_WRITE(cmd) ?
+ UT_WRITE_VENDOR_DEVICE :
+ UT_READ_VENDOR_DEVICE);
+ req.bRequest = AXE_CMD_CMD(cmd);
+ USETW(req.wValue, val);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, AXE_CMD_LEN(cmd));
+
+ err = usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000);
+
+ return (err);
+}
+
+static int
+axe_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ uint16_t val;
+ int locked;
+
+ if (sc->sc_phyno != phy)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ axe_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+
+ val = le16toh(val);
+ if ((sc->sc_flags & AXE_FLAG_772) != 0 && reg == MII_BMSR) {
+ /*
+ * BMSR of AX88772 indicates that it supports extended
+ * capability but the extended status register is
+ * revered for embedded ethernet PHY. So clear the
+ * extended capability bit of BMSR.
+ */
+ val &= ~BMSR_EXTCAP;
+ }
+
+ if (!locked)
+ AXE_UNLOCK(sc);
+ return (val);
+}
+
+static int
+axe_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ int locked;
+
+ val = htole16(val);
+
+ if (sc->sc_phyno != phy)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL);
+ axe_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val);
+ axe_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL);
+
+ if (!locked)
+ AXE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+axe_miibus_statchg(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ struct ifnet *ifp;
+ uint16_t val;
+ int err, locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ AXE_LOCK(sc);
+
+ ifp = usb2_ether_getifp(&sc->sc_ue);
+ if (mii == NULL || ifp == NULL ||
+ (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ goto done;
+
+ sc->sc_flags &= ~AXE_FLAG_LINK;
+ if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+ (IFM_ACTIVE | IFM_AVALID)) {
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_10_T:
+ case IFM_100_TX:
+ sc->sc_flags |= AXE_FLAG_LINK;
+ break;
+ case IFM_1000_T:
+ if ((sc->sc_flags & AXE_FLAG_178) == 0)
+ break;
+ sc->sc_flags |= AXE_FLAG_LINK;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Lost link, do nothing. */
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0)
+ goto done;
+
+ val = 0;
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ val |= AXE_MEDIA_FULL_DUPLEX;
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
+ val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC;
+ if ((sc->sc_flags & AXE_FLAG_178) != 0)
+ val |= AXE_178_MEDIA_ENCK;
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK;
+ break;
+ case IFM_100_TX:
+ val |= AXE_178_MEDIA_100TX;
+ break;
+ case IFM_10_T:
+ /* doesn't need to be handled */
+ break;
+ }
+ }
+ err = axe_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL);
+ if (err)
+ device_printf(dev, "media change failed, error %d\n", err);
+done:
+ if (!locked)
+ AXE_UNLOCK(sc);
+}
+
+/*
+ * Set media options.
+ */
+static int
+axe_ifmedia_upd(struct ifnet *ifp)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+ int error;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ error = mii_mediachg(mii);
+ return (error);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+axe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct axe_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ AXE_LOCK(sc);
+ mii_pollstat(mii);
+ AXE_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+axe_setmulti(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h = 0;
+ uint16_t rxmode;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode);
+ rxmode = le16toh(rxmode);
+
+ if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) {
+ rxmode |= AXE_RXCMD_ALLMULTI;
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+ return;
+ }
+ rxmode &= ~AXE_RXCMD_ALLMULTI;
+
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ axe_cmd(sc, AXE_CMD_WRITE_MCAST, 0, 0, (void *)&hashtbl);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+}
+
+static int
+axe_get_phyno(struct axe_softc *sc, int sel)
+{
+ int phyno;
+
+ switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) {
+ case PHY_TYPE_100_HOME:
+ case PHY_TYPE_GIG:
+ phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]);
+ break;
+ case PHY_TYPE_SPECIAL:
+ /* FALLTHROUGH */
+ case PHY_TYPE_RSVD:
+ /* FALLTHROUGH */
+ case PHY_TYPE_NON_SUP:
+ /* FALLTHROUGH */
+ default:
+ phyno = -1;
+ break;
+ }
+
+ return (phyno);
+}
+
+static void
+axe_ax88178_init(struct axe_softc *sc)
+{
+ int gpio0 = 0, phymode = 0;
+ uint16_t eeprom;
+
+ axe_cmd(sc, AXE_CMD_SROM_WR_ENABLE, 0, 0, NULL);
+ /* XXX magic */
+ axe_cmd(sc, AXE_CMD_SROM_READ, 0, 0x0017, &eeprom);
+ eeprom = le16toh(eeprom);
+ axe_cmd(sc, AXE_CMD_SROM_WR_DISABLE, 0, 0, NULL);
+
+ /* if EEPROM is invalid we have to use to GPIO0 */
+ if (eeprom == 0xffff) {
+ phymode = 0;
+ gpio0 = 1;
+ } else {
+ phymode = eeprom & 7;
+ gpio0 = (eeprom & 0x80) ? 0 : 1;
+ }
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x008c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 16);
+
+ if ((eeprom >> 8) != 0x01) {
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x001c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 3);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x003c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+ } else {
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x0004, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x000c, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 32);
+ }
+
+ /* soft reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_CLEAR, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+ /* Enable MII/GMII/RGMII interface to work with external PHY. */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_ax88772_init(struct axe_softc *sc)
+{
+ axe_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 16);
+
+ if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) {
+ /* ask for the embedded PHY */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 64);
+
+ /* power down and reset state, pin reset state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_CLEAR, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 16);
+
+ /* power down/reset state, pin operating state */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+
+ /* power up, reset */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL, NULL);
+
+ /* power up, operating */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPRL | AXE_SW_RESET_PRL, NULL);
+ } else {
+ /* ask for external PHY */
+ axe_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x00, NULL);
+ usb2_ether_pause(&sc->sc_ue, hz / 64);
+
+ /* power down internal PHY */
+ axe_cmd(sc, AXE_CMD_SW_RESET_REG, 0,
+ AXE_SW_RESET_IPPD | AXE_SW_RESET_PRL, NULL);
+ }
+
+ usb2_ether_pause(&sc->sc_ue, hz / 4);
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL);
+}
+
+static void
+axe_reset(struct axe_softc *sc)
+{
+ struct usb2_config_descriptor *cd;
+ usb2_error_t err;
+
+ cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usb2_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (err)
+ DPRINTF("reset failed (ignored)\n");
+
+ /* Wait a little while for the chip to get its brains in order. */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+axe_attach_post(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * Load PHY indexes first. Needed by axe_xxx_init().
+ */
+ axe_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs);
+#if 1
+ device_printf(sc->sc_ue.ue_dev, "PHYADDR 0x%02x:0x%02x\n",
+ sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]);
+#endif
+ sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI);
+ if (sc->sc_phyno == -1)
+ sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC);
+ if (sc->sc_phyno == -1) {
+ device_printf(sc->sc_ue.ue_dev,
+ "no valid PHY address found, assuming PHY address 0\n");
+ sc->sc_phyno = 0;
+ }
+
+ if (sc->sc_flags & AXE_FLAG_178)
+ axe_ax88178_init(sc);
+ else if (sc->sc_flags & AXE_FLAG_772)
+ axe_ax88772_init(sc);
+
+ /*
+ * Get station address.
+ */
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772))
+ axe_cmd(sc, AXE_178_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+ else
+ axe_cmd(sc, AXE_172_CMD_READ_NODEID, 0, 0, ue->ue_eaddr);
+
+ /*
+ * Fetch IPG values.
+ */
+ axe_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs);
+}
+
+/*
+ * Probe for a AX88172 chip.
+ */
+static int
+axe_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != AXE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != AXE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(axe_devs, sizeof(axe_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+axe_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct axe_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = AXE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
+ axe_config, AXE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &axe_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ axe_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+axe_detach(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, AXE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+axe_intr_callback(struct usb2_xfer *xfer)
+{
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+#if (AXE_BULK_BUF_SIZE >= 0x10000)
+#error "Please update axe_bulk_read_callback()!"
+#endif
+
+static void
+axe_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct axe_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct axe_sframe_hdr hdr;
+ int error, pos, len, adjust;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ pos = 0;
+ while (1) {
+ if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
+ if (xfer->actlen < sizeof(hdr)) {
+ /* too little data */
+ break;
+ }
+ usb2_copy_out(xfer->frbuffers, pos, &hdr, sizeof(hdr));
+
+ if ((hdr.len ^ hdr.ilen) != 0xFFFF) {
+ /* we lost sync */
+ break;
+ }
+ xfer->actlen -= sizeof(hdr);
+ pos += sizeof(hdr);
+
+ len = le16toh(hdr.len);
+ if (len > xfer->actlen) {
+ /* invalid length */
+ break;
+ }
+ adjust = (len & 1);
+
+ } else {
+ len = xfer->actlen;
+ adjust = 0;
+ }
+ error = usb2_ether_rxbuf(ue, xfer->frbuffers, pos, len);
+ if (error)
+ break;
+
+ pos += len;
+ xfer->actlen -= len;
+
+ if (xfer->actlen <= adjust) {
+ /* we are finished */
+ goto tr_setup;
+ }
+ pos += adjust;
+ xfer->actlen -= adjust;
+ }
+
+ /* count an error */
+ ifp->if_ierrors++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+#if ((AXE_BULK_BUF_SIZE >= 0x10000) || (AXE_BULK_BUF_SIZE < (MCLBYTES+4)))
+#error "Please update axe_bulk_write_callback()!"
+#endif
+
+static void
+axe_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct axe_softc *sc = xfer->priv_sc;
+ struct axe_sframe_hdr hdr;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int pos;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ pos = 0;
+
+ while (1) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL) {
+ if (pos > 0)
+ break; /* send out data */
+ return;
+ }
+ if (m->m_pkthdr.len > MCLBYTES) {
+ m->m_pkthdr.len = MCLBYTES;
+ }
+ if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
+
+ hdr.len = htole16(m->m_pkthdr.len);
+ hdr.ilen = ~hdr.len;
+
+ usb2_copy_in(xfer->frbuffers, pos, &hdr, sizeof(hdr));
+
+ pos += sizeof(hdr);
+
+ /*
+ * NOTE: Some drivers force a short packet
+ * by appending a dummy header with zero
+ * length at then end of the USB transfer.
+ * This driver uses the
+ * USB_FORCE_SHORT_XFER flag instead.
+ */
+ }
+ usb2_m_copy_in(xfer->frbuffers, pos,
+ m, 0, m->m_pkthdr.len);
+
+ pos += m->m_pkthdr.len;
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) {
+ if (pos > (AXE_BULK_BUF_SIZE - MCLBYTES - sizeof(hdr))) {
+ /* send out frame(s) */
+ break;
+ }
+ } else {
+ /* send out frame */
+ break;
+ }
+ }
+
+ xfer->frlengths[0] = pos;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+axe_tick(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & AXE_FLAG_LINK) == 0) {
+ axe_miibus_statchg(ue->ue_dev);
+ if ((sc->sc_flags & AXE_FLAG_LINK) != 0)
+ axe_start(ue);
+ }
+}
+
+static void
+axe_start(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[AXE_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[AXE_BULK_DT_WR]);
+}
+
+static void
+axe_init(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t rxmode;
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Cancel pending I/O */
+ axe_stop(ue);
+
+#ifdef notdef
+ /* Set MAC address */
+ axe_mac(sc, IF_LLADDR(ifp), 1);
+#endif
+
+ /* Set transmitter IPG values */
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
+ axe_cmd(sc, AXE_178_CMD_WRITE_IPG012, sc->sc_ipgs[2],
+ (sc->sc_ipgs[1] << 8) | (sc->sc_ipgs[0]), NULL);
+ } else {
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG0, 0, sc->sc_ipgs[0], NULL);
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG1, 0, sc->sc_ipgs[1], NULL);
+ axe_cmd(sc, AXE_172_CMD_WRITE_IPG2, 0, sc->sc_ipgs[2], NULL);
+ }
+
+ /* Enable receiver, set RX mode */
+ rxmode = (AXE_RXCMD_MULTICAST | AXE_RXCMD_ENABLE);
+ if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) {
+ rxmode |= AXE_178_RXCMD_MFB_2048; /* chip default */
+ } else {
+ rxmode |= AXE_172_RXCMD_UNICAST;
+ }
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= AXE_RXCMD_PROMISC;
+
+ if (ifp->if_flags & IFF_BROADCAST)
+ rxmode |= AXE_RXCMD_BROADCAST;
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+
+ /* Load the multicast filter. */
+ axe_setmulti(ue);
+
+ usb2_transfer_set_stall(sc->sc_xfer[AXE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ axe_start(ue);
+}
+
+static void
+axe_setpromisc(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t rxmode;
+
+ axe_cmd(sc, AXE_CMD_RXCTL_READ, 0, 0, &rxmode);
+
+ rxmode = le16toh(rxmode);
+
+ if (ifp->if_flags & IFF_PROMISC) {
+ rxmode |= AXE_RXCMD_PROMISC;
+ } else {
+ rxmode &= ~AXE_RXCMD_PROMISC;
+ }
+
+ axe_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, rxmode, NULL);
+
+ axe_setmulti(ue);
+}
+
+static void
+axe_stop(struct usb2_ether *ue)
+{
+ struct axe_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ AXE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~AXE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[AXE_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[AXE_INTR_DT_RD]);
+
+ axe_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+axe_shutdown(device_t dev)
+{
+ struct axe_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_axereg.h b/sys/dev/usb/net/if_axereg.h
new file mode 100644
index 0000000..dc063e3
--- /dev/null
+++ b/sys/dev/usb/net/if_axereg.h
@@ -0,0 +1,196 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003
+ * Bill Paul <wpaul@windriver.com>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Definitions for the ASIX Electronics AX88172, AX88178
+ * and AX88772 to ethernet controllers.
+ */
+
+/*
+ * Vendor specific commands. ASIX conveniently doesn't document the 'set
+ * NODEID' command in their datasheet (thanks a lot guys).
+ * To make handling these commands easier, I added some extra data which is
+ * decided by the axe_cmd() routine. Commands are encoded in 16 bits, with
+ * the format: LDCC. L and D are both nibbles in the high byte. L represents
+ * the data length (0 to 15) and D represents the direction (0 for vendor read,
+ * 1 for vendor write). CC is the command byte, as specified in the manual.
+ */
+
+#define AXE_CMD_IS_WRITE(x) (((x) & 0x0F00) >> 8)
+#define AXE_CMD_LEN(x) (((x) & 0xF000) >> 12)
+#define AXE_CMD_CMD(x) ((x) & 0x00FF)
+
+#define AXE_172_CMD_READ_RXTX_SRAM 0x2002
+#define AXE_182_CMD_READ_RXTX_SRAM 0x8002
+#define AXE_172_CMD_WRITE_RX_SRAM 0x0103
+#define AXE_182_CMD_WRITE_RXTX_SRAM 0x8103
+#define AXE_172_CMD_WRITE_TX_SRAM 0x0104
+#define AXE_CMD_MII_OPMODE_SW 0x0106
+#define AXE_CMD_MII_READ_REG 0x2007
+#define AXE_CMD_MII_WRITE_REG 0x2108
+#define AXE_CMD_MII_READ_OPMODE 0x1009
+#define AXE_CMD_MII_OPMODE_HW 0x010A
+#define AXE_CMD_SROM_READ 0x200B
+#define AXE_CMD_SROM_WRITE 0x010C
+#define AXE_CMD_SROM_WR_ENABLE 0x010D
+#define AXE_CMD_SROM_WR_DISABLE 0x010E
+#define AXE_CMD_RXCTL_READ 0x200F
+#define AXE_CMD_RXCTL_WRITE 0x0110
+#define AXE_CMD_READ_IPG012 0x3011
+#define AXE_172_CMD_WRITE_IPG0 0x0112
+#define AXE_178_CMD_WRITE_IPG012 0x0112
+#define AXE_172_CMD_WRITE_IPG1 0x0113
+#define AXE_178_CMD_READ_NODEID 0x6013
+#define AXE_172_CMD_WRITE_IPG2 0x0114
+#define AXE_178_CMD_WRITE_NODEID 0x6114
+#define AXE_CMD_READ_MCAST 0x8015
+#define AXE_CMD_WRITE_MCAST 0x8116
+#define AXE_172_CMD_READ_NODEID 0x6017
+#define AXE_172_CMD_WRITE_NODEID 0x6118
+
+#define AXE_CMD_READ_PHYID 0x2019
+#define AXE_172_CMD_READ_MEDIA 0x101A
+#define AXE_178_CMD_READ_MEDIA 0x201A
+#define AXE_CMD_WRITE_MEDIA 0x011B
+#define AXE_CMD_READ_MONITOR_MODE 0x101C
+#define AXE_CMD_WRITE_MONITOR_MODE 0x011D
+#define AXE_CMD_READ_GPIO 0x101E
+#define AXE_CMD_WRITE_GPIO 0x011F
+
+#define AXE_CMD_SW_RESET_REG 0x0120
+#define AXE_CMD_SW_PHY_STATUS 0x0021
+#define AXE_CMD_SW_PHY_SELECT 0x0122
+
+#define AXE_SW_RESET_CLEAR 0x00
+#define AXE_SW_RESET_RR 0x01
+#define AXE_SW_RESET_RT 0x02
+#define AXE_SW_RESET_PRTE 0x04
+#define AXE_SW_RESET_PRL 0x08
+#define AXE_SW_RESET_BZ 0x10
+#define AXE_SW_RESET_IPRL 0x20
+#define AXE_SW_RESET_IPPD 0x40
+
+/* AX88178 documentation says to always write this bit... */
+#define AXE_178_RESET_MAGIC 0x40
+
+#define AXE_178_MEDIA_GMII 0x0001
+#define AXE_MEDIA_FULL_DUPLEX 0x0002
+#define AXE_172_MEDIA_TX_ABORT_ALLOW 0x0004
+
+/* AX88178/88772 documentation says to always write 1 to bit 2 */
+#define AXE_178_MEDIA_MAGIC 0x0004
+/* AX88772 documentation says to always write 0 to bit 3 */
+#define AXE_178_MEDIA_ENCK 0x0008
+#define AXE_172_MEDIA_FLOW_CONTROL_EN 0x0010
+#define AXE_178_MEDIA_RXFLOW_CONTROL_EN 0x0010
+#define AXE_178_MEDIA_TXFLOW_CONTROL_EN 0x0020
+#define AXE_178_MEDIA_JUMBO_EN 0x0040
+#define AXE_178_MEDIA_LTPF_ONLY 0x0080
+#define AXE_178_MEDIA_RX_EN 0x0100
+#define AXE_178_MEDIA_100TX 0x0200
+#define AXE_178_MEDIA_SBP 0x0800
+#define AXE_178_MEDIA_SUPERMAC 0x1000
+
+#define AXE_RXCMD_PROMISC 0x0001
+#define AXE_RXCMD_ALLMULTI 0x0002
+#define AXE_172_RXCMD_UNICAST 0x0004
+#define AXE_178_RXCMD_KEEP_INVALID_CRC 0x0004
+#define AXE_RXCMD_BROADCAST 0x0008
+#define AXE_RXCMD_MULTICAST 0x0010
+#define AXE_RXCMD_ENABLE 0x0080
+#define AXE_178_RXCMD_MFB_MASK 0x0300
+#define AXE_178_RXCMD_MFB_2048 0x0000
+#define AXE_178_RXCMD_MFB_4096 0x0100
+#define AXE_178_RXCMD_MFB_8192 0x0200
+#define AXE_178_RXCMD_MFB_16384 0x0300
+
+#define AXE_PHY_SEL_PRI 1
+#define AXE_PHY_SEL_SEC 0
+#define AXE_PHY_TYPE_MASK 0xE0
+#define AXE_PHY_TYPE_SHIFT 5
+#define AXE_PHY_TYPE(x) \
+ (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT)
+
+#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */
+#define PHY_TYPE_GIG 1 /* Gigabit PHY */
+#define PHY_TYPE_SPECIAL 4 /* Special case */
+#define PHY_TYPE_RSVD 5 /* Reserved */
+#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */
+
+#define AXE_PHY_NO_MASK 0x1F
+#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK)
+
+#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */
+
+#define AXE_BULK_BUF_SIZE 16384 /* bytes */
+
+#define AXE_CTL_READ 0x01
+#define AXE_CTL_WRITE 0x02
+
+#define AXE_CONFIG_IDX 0 /* config number 1 */
+#define AXE_IFACE_IDX 0
+
+struct axe_sframe_hdr {
+ uint16_t len;
+ uint16_t ilen;
+} __packed;
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+/* The interrupt endpoint is currently unused by the ASIX part. */
+enum {
+ AXE_BULK_DT_WR,
+ AXE_BULK_DT_RD,
+ AXE_INTR_DT_RD,
+ AXE_N_TRANSFER,
+};
+
+struct axe_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[AXE_N_TRANSFER];
+ int sc_phyno;
+
+ int sc_flags;
+#define AXE_FLAG_LINK 0x0001
+#define AXE_FLAG_772 0x1000 /* AX88772 */
+#define AXE_FLAG_178 0x2000 /* AX88178 */
+
+ uint8_t sc_ipgs[3];
+ uint8_t sc_phyaddrs[2];
+};
+
+#define AXE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define AXE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define AXE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_cdce.c b/sys/dev/usb/net/if_cdce.c
new file mode 100644
index 0000000..4097bde
--- /dev/null
+++ b/sys/dev/usb/net/if_cdce.c
@@ -0,0 +1,771 @@
+/* $NetBSD: if_cdce.c,v 1.4 2004/10/24 12:50:54 augustss Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000-2003 Bill Paul <wpaul@windriver.com>
+ * Copyright (c) 2003-2005 Craig Boston
+ * Copyright (c) 2004 Daniel Hartmeier
+ * Copyright (c) 2009 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul, THE VOICES IN HIS HEAD OR
+ * THE 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 Communication Device Class (Ethernet Networking Control Model)
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR cdce_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_cdcereg.h>
+
+static device_probe_t cdce_probe;
+static device_attach_t cdce_attach;
+static device_detach_t cdce_detach;
+static device_shutdown_t cdce_shutdown;
+static device_suspend_t cdce_suspend;
+static device_resume_t cdce_resume;
+static usb_handle_request_t cdce_handle_request;
+
+static usb2_callback_t cdce_bulk_write_callback;
+static usb2_callback_t cdce_bulk_read_callback;
+static usb2_callback_t cdce_intr_read_callback;
+static usb2_callback_t cdce_intr_write_callback;
+
+static usb2_ether_fn_t cdce_attach_post;
+static usb2_ether_fn_t cdce_init;
+static usb2_ether_fn_t cdce_stop;
+static usb2_ether_fn_t cdce_start;
+static usb2_ether_fn_t cdce_setmulti;
+static usb2_ether_fn_t cdce_setpromisc;
+
+static uint32_t cdce_m_crc32(struct mbuf *, uint32_t, uint32_t);
+
+#if USB_DEBUG
+static int cdce_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, cdce, CTLFLAG_RW, 0, "USB CDC-Ethernet");
+SYSCTL_INT(_hw_usb2_cdce, OID_AUTO, debug, CTLFLAG_RW, &cdce_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb2_config cdce_config[CDCE_N_TRANSFER] = {
+
+ [CDCE_BULK_A] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 0,
+ /* Host Mode */
+ .mh.frames = CDCE_FRAMES_MAX,
+ .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+ .mh.callback = cdce_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ /* Device Mode */
+ .md.frames = CDCE_FRAMES_MAX,
+ .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .md.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
+ .md.callback = cdce_bulk_read_callback,
+ .md.timeout = 0, /* no timeout */
+ },
+
+ [CDCE_BULK_B] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 0,
+ /* Host Mode */
+ .mh.frames = CDCE_FRAMES_MAX,
+ .mh.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .mh.flags = {.pipe_bof = 1,.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,},
+ .mh.callback = cdce_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ /* Device Mode */
+ .md.frames = CDCE_FRAMES_MAX,
+ .md.bufsize = (CDCE_FRAMES_MAX * MCLBYTES),
+ .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.ext_buffer = 1,},
+ .md.callback = cdce_bulk_write_callback,
+ .md.timeout = 10000, /* 10 seconds */
+ },
+
+ [CDCE_INTR] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 1,
+ /* Host Mode */
+ .mh.bufsize = CDCE_IND_SIZE_MAX,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+ .mh.callback = cdce_intr_read_callback,
+ .mh.timeout = 0,
+ /* Device Mode */
+ .md.bufsize = CDCE_IND_SIZE_MAX,
+ .md.flags = {.pipe_bof = 1,.force_short_xfer = 1,.no_pipe_ok = 1,},
+ .md.callback = cdce_intr_write_callback,
+ .md.timeout = 10000, /* 10 seconds */
+ },
+};
+
+static device_method_t cdce_methods[] = {
+ /* USB interface */
+ DEVMETHOD(usb_handle_request, cdce_handle_request),
+
+ /* Device interface */
+ DEVMETHOD(device_probe, cdce_probe),
+ DEVMETHOD(device_attach, cdce_attach),
+ DEVMETHOD(device_detach, cdce_detach),
+ DEVMETHOD(device_suspend, cdce_suspend),
+ DEVMETHOD(device_resume, cdce_resume),
+ DEVMETHOD(device_shutdown, cdce_shutdown),
+
+ {0, 0}
+};
+
+static driver_t cdce_driver = {
+ .name = "cdce",
+ .methods = cdce_methods,
+ .size = sizeof(struct cdce_softc),
+};
+
+static devclass_t cdce_devclass;
+
+DRIVER_MODULE(cdce, ushub, cdce_driver, cdce_devclass, NULL, 0);
+MODULE_VERSION(cdce, 1);
+MODULE_DEPEND(cdce, uether, 1, 1, 1);
+MODULE_DEPEND(cdce, usb, 1, 1, 1);
+MODULE_DEPEND(cdce, ether, 1, 1, 1);
+
+static const struct usb2_ether_methods cdce_ue_methods = {
+ .ue_attach_post = cdce_attach_post,
+ .ue_start = cdce_start,
+ .ue_init = cdce_init,
+ .ue_stop = cdce_stop,
+ .ue_setmulti = cdce_setmulti,
+ .ue_setpromisc = cdce_setpromisc,
+};
+
+static const struct usb2_device_id cdce_devs[] = {
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL, 0)},
+ {USB_IF_CSI(UICLASS_CDC, UISUBCLASS_MOBILE_DIRECT_LINE_MODEL, 0)},
+
+ {USB_VPI(USB_VENDOR_ACERLABS, USB_PRODUCT_ACERLABS_M5632, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_AMBIT, USB_PRODUCT_AMBIT_NTL_250, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQLINUX, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_GMATE, USB_PRODUCT_GMATE_YP3X00, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_USBLAN2, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_ETHERNETGADGET, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2501, CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5500, CDCE_FLAG_ZAURUS)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SL5600, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLA300, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC700, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_SLC750, CDCE_FLAG_ZAURUS | CDCE_FLAG_NO_UNION)},
+};
+
+static int
+cdce_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ return (usb2_lookup_id_by_uaa(cdce_devs, sizeof(cdce_devs), uaa));
+}
+
+static void
+cdce_attach_post(struct usb2_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static int
+cdce_attach(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface *iface;
+ const struct usb2_cdc_union_descriptor *ud;
+ const struct usb2_interface_descriptor *id;
+ const struct usb2_cdc_ethernet_descriptor *ued;
+ int error;
+ uint8_t i;
+ char eaddr_str[5 * ETHER_ADDR_LEN]; /* approx */
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ if (sc->sc_flags & CDCE_FLAG_NO_UNION) {
+ sc->sc_ifaces_index[0] = uaa->info.bIfaceIndex;
+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
+ sc->sc_data_iface_no = 0; /* not used */
+ goto alloc_transfers;
+ }
+ ud = usb2_find_descriptor
+ (uaa->device, NULL, uaa->info.bIfaceIndex,
+ UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_UNION, 0 - 1);
+
+ if ((ud == NULL) || (ud->bLength < sizeof(*ud))) {
+ device_printf(dev, "no union descriptor!\n");
+ goto detach;
+ }
+ sc->sc_data_iface_no = ud->bSlaveInterface[0];
+
+ for (i = 0;; i++) {
+
+ iface = usb2_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usb2_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber ==
+ sc->sc_data_iface_no)) {
+ sc->sc_ifaces_index[0] = i;
+ sc->sc_ifaces_index[1] = uaa->info.bIfaceIndex;
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+ break;
+ }
+ } else {
+ device_printf(dev, "no data interface found!\n");
+ goto detach;
+ }
+ }
+
+ /*
+ * <quote>
+ *
+ * The Data Class interface of a networking device shall have
+ * a minimum of two interface settings. The first setting
+ * (the default interface setting) includes no endpoints and
+ * therefore no networking traffic is exchanged whenever the
+ * default interface setting is selected. One or more
+ * additional interface settings are used for normal
+ * operation, and therefore each includes a pair of endpoints
+ * (one IN, and one OUT) to exchange network traffic. Select
+ * an alternate interface setting to initialize the network
+ * aspects of the device and to enable the exchange of
+ * network traffic.
+ *
+ * </quote>
+ *
+ * Some devices, most notably cable modems, include interface
+ * settings that have no IN or OUT endpoint, therefore loop
+ * through the list of all available interface settings
+ * looking for one with both IN and OUT endpoints.
+ */
+
+alloc_transfers:
+
+ for (i = 0; i != 32; i++) {
+
+ error = usb2_set_alt_interface_index
+ (uaa->device, sc->sc_ifaces_index[0], i);
+
+ if (error) {
+ device_printf(dev, "no valid alternate "
+ "setting found!\n");
+ goto detach;
+ }
+ error = usb2_transfer_setup
+ (uaa->device, sc->sc_ifaces_index,
+ sc->sc_xfer, cdce_config, CDCE_N_TRANSFER,
+ sc, &sc->sc_mtx);
+
+ if (error == 0) {
+ break;
+ }
+ }
+
+ ued = usb2_find_descriptor
+ (uaa->device, NULL, uaa->info.bIfaceIndex,
+ UDESC_CS_INTERFACE, 0 - 1, UDESCSUB_CDC_ENF, 0 - 1);
+
+ if ((ued == NULL) || (ued->bLength < sizeof(*ued))) {
+ error = USB_ERR_INVAL;
+ } else {
+ error = usb2_req_get_string_any(uaa->device, NULL,
+ eaddr_str, sizeof(eaddr_str), ued->iMacAddress);
+ }
+
+ if (error) {
+
+ /* fake MAC address */
+
+ device_printf(dev, "faking MAC address\n");
+ sc->sc_ue.ue_eaddr[0] = 0x2a;
+ memcpy(&sc->sc_ue.ue_eaddr[1], &ticks, sizeof(uint32_t));
+ sc->sc_ue.ue_eaddr[5] = device_get_unit(dev);
+
+ } else {
+
+ bzero(sc->sc_ue.ue_eaddr, sizeof(sc->sc_ue.ue_eaddr));
+
+ for (i = 0; i != (ETHER_ADDR_LEN * 2); i++) {
+
+ char c = eaddr_str[i];
+
+ if ('0' <= c && c <= '9')
+ c -= '0';
+ else if (c != 0)
+ c -= 'A' - 10;
+ else
+ break;
+
+ c &= 0xf;
+
+ if ((i & 1) == 0)
+ c <<= 4;
+ sc->sc_ue.ue_eaddr[i / 2] |= c;
+ }
+
+ if (uaa->usb2_mode == USB_MODE_DEVICE) {
+ /*
+ * Do not use the same MAC address like the peer !
+ */
+ sc->sc_ue.ue_eaddr[5] ^= 0xFF;
+ }
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &cdce_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ cdce_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+cdce_detach(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ /* stop all USB transfers first */
+ usb2_transfer_unsetup(sc->sc_xfer, CDCE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+cdce_start(struct usb2_ether *ue)
+{
+ struct cdce_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * Start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[CDCE_BULK_B]);
+ usb2_transfer_start(sc->sc_xfer[CDCE_BULK_A]);
+}
+
+static void
+cdce_free_queue(struct mbuf **ppm, uint8_t n)
+{
+ uint8_t x;
+ for (x = 0; x != n; x++) {
+ if (ppm[x] != NULL) {
+ m_freem(ppm[x]);
+ ppm[x] = NULL;
+ }
+ }
+}
+
+static void
+cdce_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct cdce_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ struct mbuf *mt;
+ uint32_t crc;
+ uint8_t x;
+
+ DPRINTFN(1, "\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete: "
+ "%u bytes in %u frames\n", xfer->actlen,
+ xfer->aframes);
+
+ ifp->if_opackets++;
+
+ /* free all previous TX buffers */
+ cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ for (x = 0; x != CDCE_FRAMES_MAX; x++) {
+
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ break;
+
+ if (sc->sc_flags & CDCE_FLAG_ZAURUS) {
+ /*
+ * Zaurus wants a 32-bit CRC appended
+ * to every frame
+ */
+
+ crc = cdce_m_crc32(m, 0, m->m_pkthdr.len);
+ crc = htole32(crc);
+
+ if (!m_append(m, 4, (void *)&crc)) {
+ m_freem(m);
+ ifp->if_oerrors++;
+ continue;
+ }
+ }
+ if (m->m_len != m->m_pkthdr.len) {
+ mt = m_defrag(m, M_DONTWAIT);
+ if (mt == NULL) {
+ m_freem(m);
+ ifp->if_oerrors++;
+ continue;
+ }
+ m = mt;
+ }
+ if (m->m_pkthdr.len > MCLBYTES) {
+ m->m_pkthdr.len = MCLBYTES;
+ }
+ sc->sc_tx_buf[x] = m;
+ xfer->frlengths[x] = m->m_len;
+ usb2_set_frame_data(xfer, m->m_data, x);
+
+ /*
+ * If there's a BPF listener, bounce a copy of
+ * this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+ }
+ if (x != 0) {
+ xfer->nframes = x;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ /* free all previous TX buffers */
+ cdce_free_queue(sc->sc_tx_buf, CDCE_FRAMES_MAX);
+
+ /* count output errors */
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int32_t
+cdce_m_crc32_cb(void *arg, void *src, uint32_t count)
+{
+ uint32_t *p_crc = arg;
+
+ *p_crc = crc32_raw(src, count, *p_crc);
+ return (0);
+}
+
+static uint32_t
+cdce_m_crc32(struct mbuf *m, uint32_t src_offset, uint32_t src_len)
+{
+ uint32_t crc = 0xFFFFFFFF;
+ int error;
+
+ error = m_apply(m, src_offset, src_len, cdce_m_crc32_cb, &crc);
+ return (crc ^ 0xFFFFFFFF);
+}
+
+static void
+cdce_init(struct usb2_ether *ue)
+{
+ struct cdce_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CDCE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+ /* start interrupt transfer */
+ usb2_transfer_start(sc->sc_xfer[CDCE_INTR]);
+
+ /* stall data write direction, which depends on USB mode */
+ if (usb2_get_mode(sc->sc_ue.ue_udev) == USB_MODE_HOST)
+ usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_A]);
+ else
+ usb2_transfer_set_stall(sc->sc_xfer[CDCE_BULK_B]);
+
+ /* start data transfers */
+ cdce_start(ue);
+}
+
+static void
+cdce_stop(struct usb2_ether *ue)
+{
+ struct cdce_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CDCE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_A]);
+ usb2_transfer_stop(sc->sc_xfer[CDCE_BULK_B]);
+ usb2_transfer_stop(sc->sc_xfer[CDCE_INTR]);
+}
+
+static void
+cdce_setmulti(struct usb2_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static void
+cdce_setpromisc(struct usb2_ether *ue)
+{
+ /* no-op */
+ return;
+}
+
+static int
+cdce_shutdown(device_t dev)
+{
+ struct cdce_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
+
+static int
+cdce_suspend(device_t dev)
+{
+ device_printf(dev, "Suspending\n");
+ return (0);
+}
+
+static int
+cdce_resume(device_t dev)
+{
+ device_printf(dev, "Resuming\n");
+ return (0);
+}
+
+static void
+cdce_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct cdce_softc *sc = xfer->priv_sc;
+ struct mbuf *m;
+ uint8_t x;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("received %u bytes in %u frames\n",
+ xfer->actlen, xfer->aframes);
+
+ for (x = 0; x != xfer->aframes; x++) {
+
+ m = sc->sc_rx_buf[x];
+ sc->sc_rx_buf[x] = NULL;
+
+ /* Strip off CRC added by Zaurus, if any */
+ if ((sc->sc_flags & CDCE_FLAG_ZAURUS) &&
+ (xfer->frlengths[x] >= 14))
+ xfer->frlengths[x] -= 4;
+
+ if (xfer->frlengths[x] < sizeof(struct ether_header)) {
+ m_freem(m);
+ continue;
+ }
+ /* queue up mbuf */
+ usb2_ether_rxmbuf(&sc->sc_ue, m, xfer->frlengths[x]);
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+ /*
+ * TODO: Implement support for multi frame transfers,
+ * when the USB hardware supports it.
+ */
+ for (x = 0; x != 1; x++) {
+ if (sc->sc_rx_buf[x] == NULL) {
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL)
+ goto tr_stall;
+ sc->sc_rx_buf[x] = m;
+ /* adjust for ethernet */
+ m->m_len = m->m_pkthdr.len = MCLBYTES;
+ m_adj(m, ETHER_ALIGN);
+ } else {
+ m = sc->sc_rx_buf[x];
+ }
+
+ usb2_set_frame_data(xfer, m->m_data, x);
+ xfer->frlengths[x] = m->m_len;
+ }
+ /* set number of frames and start hardware */
+ xfer->nframes = x;
+ usb2_start_hardware(xfer);
+ /* flush any received frames */
+ usb2_ether_rxflush(&sc->sc_ue);
+ break;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+tr_stall:
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ xfer->nframes = 0;
+ usb2_start_hardware(xfer);
+ break;
+ }
+
+ /* need to free the RX-mbufs when we are cancelled */
+ cdce_free_queue(sc->sc_rx_buf, CDCE_FRAMES_MAX);
+ break;
+ }
+}
+
+static void
+cdce_intr_read_callback(struct usb2_xfer *xfer)
+{
+ ; /* style fix */
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("Received %d bytes\n",
+ xfer->actlen);
+
+ /* TODO: decode some indications */
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* start clear stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+cdce_intr_write_callback(struct usb2_xfer *xfer)
+{
+ ; /* style fix */
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("Transferred %d bytes\n", xfer->actlen);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+#if 0
+ xfer->frlengths[0] = XXX;
+ usb2_start_hardware(xfer);
+#endif
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* start clear stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int
+cdce_handle_request(device_t dev,
+ const void *req, void **pptr, uint16_t *plen,
+ uint16_t offset, uint8_t is_complete)
+{
+ return (ENXIO); /* use builtin handler */
+}
diff --git a/sys/dev/usb/net/if_cdcereg.h b/sys/dev/usb/net/if_cdcereg.h
new file mode 100644
index 0000000..dac5121
--- /dev/null
+++ b/sys/dev/usb/net/if_cdcereg.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2003-2005 Craig Boston
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul, THE VOICES IN HIS HEAD OR
+ * THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _USB_IF_CDCEREG_H_
+#define _USB_IF_CDCEREG_H_
+
+#define CDCE_FRAMES_MAX 8 /* units */
+#define CDCE_IND_SIZE_MAX 32 /* bytes */
+
+enum {
+ CDCE_BULK_A,
+ CDCE_BULK_B,
+ CDCE_INTR,
+ CDCE_N_TRANSFER,
+};
+
+struct cdce_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[CDCE_N_TRANSFER];
+ struct mbuf *sc_rx_buf[CDCE_FRAMES_MAX];
+ struct mbuf *sc_tx_buf[CDCE_FRAMES_MAX];
+
+ int sc_flags;
+#define CDCE_FLAG_ZAURUS 0x0001
+#define CDCE_FLAG_NO_UNION 0x0002
+#define CDCE_FLAG_RX_DATA 0x0010
+
+ uint8_t sc_eaddr_str_index;
+ uint8_t sc_data_iface_no;
+ uint8_t sc_ifaces_index[2];
+};
+
+#define CDCE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define CDCE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define CDCE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
+#endif /* _USB_IF_CDCEREG_H_ */
diff --git a/sys/dev/usb/net/if_cue.c b/sys/dev/usb/net/if_cue.c
new file mode 100644
index 0000000..72ec92d
--- /dev/null
+++ b/sys/dev/usb/net/if_cue.c
@@ -0,0 +1,645 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * CATC USB-EL1210A USB to ethernet driver. Used in the CATC Netmate
+ * adapters and others.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The CATC USB-EL1210A provides USB ethernet support at 10Mbps. The
+ * RX filter uses a 512-bit multicast hash table, single perfect entry
+ * for the station address, and promiscuous mode. Unlike the ADMtek
+ * and KLSI chips, the CATC ASIC supports read and write combining
+ * mode where multiple packets can be transfered using a single bulk
+ * transaction, which helps performance a great deal.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR cue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_cuereg.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+
+/* Belkin F5U111 adapter covered by NETMATE entry */
+
+static const struct usb2_device_id cue_devs[] = {
+ {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE, 0)},
+ {USB_VPI(USB_VENDOR_CATC, USB_PRODUCT_CATC_NETMATE2, 0)},
+ {USB_VPI(USB_VENDOR_SMARTBRIDGES, USB_PRODUCT_SMARTBRIDGES_SMARTLINK, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t cue_probe;
+static device_attach_t cue_attach;
+static device_detach_t cue_detach;
+static device_shutdown_t cue_shutdown;
+
+static usb2_callback_t cue_bulk_read_callback;
+static usb2_callback_t cue_bulk_write_callback;
+
+static usb2_ether_fn_t cue_attach_post;
+static usb2_ether_fn_t cue_init;
+static usb2_ether_fn_t cue_stop;
+static usb2_ether_fn_t cue_start;
+static usb2_ether_fn_t cue_tick;
+static usb2_ether_fn_t cue_setmulti;
+static usb2_ether_fn_t cue_setpromisc;
+
+static uint8_t cue_csr_read_1(struct cue_softc *, uint16_t);
+static uint16_t cue_csr_read_2(struct cue_softc *, uint8_t);
+static int cue_csr_write_1(struct cue_softc *, uint16_t, uint16_t);
+static int cue_mem(struct cue_softc *, uint8_t, uint16_t, void *, int);
+static int cue_getmac(struct cue_softc *, void *);
+static uint32_t cue_mchash(const uint8_t *);
+static void cue_reset(struct cue_softc *);
+
+#if USB_DEBUG
+static int cue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, cue, CTLFLAG_RW, 0, "USB cue");
+SYSCTL_INT(_hw_usb2_cue, OID_AUTO, debug, CTLFLAG_RW, &cue_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb2_config cue_config[CUE_N_TRANSFER] = {
+
+ [CUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,},
+ .mh.callback = cue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [CUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = cue_bulk_read_callback,
+ },
+};
+
+static device_method_t cue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, cue_probe),
+ DEVMETHOD(device_attach, cue_attach),
+ DEVMETHOD(device_detach, cue_detach),
+ DEVMETHOD(device_shutdown, cue_shutdown),
+
+ {0, 0}
+};
+
+static driver_t cue_driver = {
+ .name = "cue",
+ .methods = cue_methods,
+ .size = sizeof(struct cue_softc),
+};
+
+static devclass_t cue_devclass;
+
+DRIVER_MODULE(cue, ushub, cue_driver, cue_devclass, NULL, 0);
+MODULE_DEPEND(cue, uether, 1, 1, 1);
+MODULE_DEPEND(cue, usb, 1, 1, 1);
+MODULE_DEPEND(cue, ether, 1, 1, 1);
+
+static const struct usb2_ether_methods cue_ue_methods = {
+ .ue_attach_post = cue_attach_post,
+ .ue_start = cue_start,
+ .ue_init = cue_init,
+ .ue_stop = cue_stop,
+ .ue_tick = cue_tick,
+ .ue_setmulti = cue_setmulti,
+ .ue_setpromisc = cue_setpromisc,
+};
+
+#define CUE_SETBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) | (x))
+
+#define CUE_CLRBIT(sc, reg, x) \
+ cue_csr_write_1(sc, reg, cue_csr_read_1(sc, reg) & ~(x))
+
+static uint8_t
+cue_csr_read_1(struct cue_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ uint8_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000)) {
+ /* ignore any errors */
+ }
+ return (val);
+}
+
+static uint16_t
+cue_csr_read_2(struct cue_softc *sc, uint8_t reg)
+{
+ struct usb2_device_request req;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_READREG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 2);
+
+ (void)usb2_ether_do_request(&sc->sc_ue, &req, &val, 1000);
+ return (le16toh(val));
+}
+
+static int
+cue_csr_write_1(struct cue_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_WRITEREG;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+
+static int
+cue_mem(struct cue_softc *sc, uint8_t cmd, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ if (cmd == CUE_CMD_READSRAM)
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = cmd;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+cue_getmac(struct cue_softc *sc, void *buf)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_GET_MACADDR;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, ETHER_ADDR_LEN);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+#define CUE_BITS 9
+
+static uint32_t
+cue_mchash(const uint8_t *addr)
+{
+ uint32_t crc;
+
+ /* Compute CRC for the address value. */
+ crc = ether_crc32_le(addr, ETHER_ADDR_LEN);
+
+ return (crc & ((1 << CUE_BITS) - 1));
+}
+
+static void
+cue_setpromisc(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* if we want promiscuous mode, set the allframes bit */
+ if (ifp->if_flags & IFF_PROMISC)
+ CUE_SETBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+ else
+ CUE_CLRBIT(sc, CUE_ETHCTL, CUE_ETHCTL_PROMISC);
+
+ /* write multicast hash-bits */
+ cue_setmulti(ue);
+}
+
+static void
+cue_setmulti(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ uint32_t h = 0, i;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ for (i = 0; i < 8; i++)
+ hashtbl[i] = 0xff;
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR,
+ &hashtbl, 8);
+ return;
+ }
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = cue_mchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
+ hashtbl[h >> 3] |= 1 << (h & 0x7);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /*
+ * Also include the broadcast address in the filter
+ * so we can receive broadcast frames.
+ */
+ if (ifp->if_flags & IFF_BROADCAST) {
+ h = cue_mchash(ifp->if_broadcastaddr);
+ hashtbl[h >> 3] |= 1 << (h & 0x7);
+ }
+
+ cue_mem(sc, CUE_CMD_WRITESRAM, CUE_MCAST_TABLE_ADDR, &hashtbl, 8);
+}
+
+static void
+cue_reset(struct cue_softc *sc)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = CUE_CMD_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ if (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000)) {
+ /* ignore any errors */
+ }
+
+ /*
+ * wait a little while for the chip to get its brains in order:
+ */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+cue_attach_post(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+
+ cue_getmac(sc, ue->ue_eaddr);
+}
+
+static int
+cue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != CUE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != CUE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(cue_devs, sizeof(cue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+cue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct cue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = CUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, cue_config, CUE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &cue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ cue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+cue_detach(device_t dev)
+{
+ struct cue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+cue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct cue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint8_t buf[2];
+ int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen <= (2 + sizeof(struct ether_header))) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+ xfer->actlen -= 2;
+ len = buf[0] | (buf[1] << 8);
+ len = min(xfer->actlen, len);
+
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+cue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct cue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ xfer->frlengths[0] = (m->m_pkthdr.len + 2);
+
+ /* the first two bytes are the frame length */
+
+ buf[0] = (uint8_t)(m->m_pkthdr.len);
+ buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+
+ /*
+ * If there's a BPF listener, bounce a copy of this frame
+ * to him.
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+cue_tick(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_SINGLECOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_MULTICOLL);
+ ifp->if_collisions += cue_csr_read_2(sc, CUE_TX_EXCESSCOLL);
+
+ if (cue_csr_read_2(sc, CUE_RX_FRAMEERR))
+ ifp->if_ierrors++;
+}
+
+static void
+cue_start(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[CUE_BULK_DT_WR]);
+}
+
+static void
+cue_init(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ int i;
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O and free all RX/TX buffers.
+ */
+ cue_stop(ue);
+#if 0
+ cue_reset(sc);
+#endif
+ /* Set MAC address */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ cue_csr_write_1(sc, CUE_PAR0 - i, IF_LLADDR(ifp)[i]);
+
+ /* Enable RX logic. */
+ cue_csr_write_1(sc, CUE_ETHCTL, CUE_ETHCTL_RX_ON | CUE_ETHCTL_MCAST_ON);
+
+ /* Load the multicast filter */
+ cue_setpromisc(ue);
+
+ /*
+ * Set the number of RX and TX buffers that we want
+ * to reserve inside the ASIC.
+ */
+ cue_csr_write_1(sc, CUE_RX_BUFPKTS, CUE_RX_FRAMES);
+ cue_csr_write_1(sc, CUE_TX_BUFPKTS, CUE_TX_FRAMES);
+
+ /* Set advanced operation modes. */
+ cue_csr_write_1(sc, CUE_ADVANCED_OPMODES,
+ CUE_AOP_EMBED_RXLEN | 0x01);/* 1 wait state */
+
+ /* Program the LED operation. */
+ cue_csr_write_1(sc, CUE_LEDCTL, CUE_LEDCTL_FOLLOW_LINK);
+
+ usb2_transfer_set_stall(sc->sc_xfer[CUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ cue_start(ue);
+}
+
+/*
+ * Stop the adapter and free any mbufs allocated to the
+ * RX and TX lists.
+ */
+static void
+cue_stop(struct usb2_ether *ue)
+{
+ struct cue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ CUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[CUE_BULK_DT_RD]);
+
+ cue_csr_write_1(sc, CUE_ETHCTL, 0);
+ cue_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+cue_shutdown(device_t dev)
+{
+ struct cue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_cuereg.h b/sys/dev/usb/net/if_cuereg.h
new file mode 100644
index 0000000..ca3a816
--- /dev/null
+++ b/sys/dev/usb/net/if_cuereg.h
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Definitions for the CATC Netmate II USB to ethernet controller.
+ */
+
+/* Vendor specific control commands. */
+#define CUE_CMD_RESET 0xF4
+#define CUE_CMD_GET_MACADDR 0xF2
+#define CUE_CMD_WRITEREG 0xFA
+#define CUE_CMD_READREG 0xFB
+#define CUE_CMD_READSRAM 0xF1
+#define CUE_CMD_WRITESRAM 0xFC
+/* Internal registers. */
+#define CUE_TX_BUFCNT 0x20
+#define CUE_RX_BUFCNT 0x21
+#define CUE_ADVANCED_OPMODES 0x22
+#define CUE_TX_BUFPKTS 0x23
+#define CUE_RX_BUFPKTS 0x24
+#define CUE_RX_MAXCHAIN 0x25
+#define CUE_ETHCTL 0x60
+#define CUE_ETHSTS 0x61
+#define CUE_PAR5 0x62
+#define CUE_PAR4 0x63
+#define CUE_PAR3 0x64
+#define CUE_PAR2 0x65
+#define CUE_PAR1 0x66
+#define CUE_PAR0 0x67
+/* Error counters, all 16 bits wide. */
+#define CUE_TX_SINGLECOLL 0x69
+#define CUE_TX_MULTICOLL 0x6B
+#define CUE_TX_EXCESSCOLL 0x6D
+#define CUE_RX_FRAMEERR 0x6F
+#define CUE_LEDCTL 0x81
+/* Advenced operating mode register. */
+#define CUE_AOP_SRAMWAITS 0x03
+#define CUE_AOP_EMBED_RXLEN 0x08
+#define CUE_AOP_RXCOMBINE 0x10
+#define CUE_AOP_TXCOMBINE 0x20
+#define CUE_AOP_EVEN_PKT_READS 0x40
+#define CUE_AOP_LOOPBK 0x80
+/* Ethernet control register. */
+#define CUE_ETHCTL_RX_ON 0x01
+#define CUE_ETHCTL_LINK_POLARITY 0x02
+#define CUE_ETHCTL_LINK_FORCE_OK 0x04
+#define CUE_ETHCTL_MCAST_ON 0x08
+#define CUE_ETHCTL_PROMISC 0x10
+/* Ethernet status register. */
+#define CUE_ETHSTS_NO_CARRIER 0x01
+#define CUE_ETHSTS_LATECOLL 0x02
+#define CUE_ETHSTS_EXCESSCOLL 0x04
+#define CUE_ETHSTS_TXBUF_AVAIL 0x08
+#define CUE_ETHSTS_BAD_POLARITY 0x10
+#define CUE_ETHSTS_LINK_OK 0x20
+/* LED control register. */
+#define CUE_LEDCTL_BLINK_1X 0x00
+#define CUE_LEDCTL_BLINK_2X 0x01
+#define CUE_LEDCTL_BLINK_QUARTER_ON 0x02
+#define CUE_LEDCTL_BLINK_QUARTER_OFF 0x03
+#define CUE_LEDCTL_OFF 0x04
+#define CUE_LEDCTL_FOLLOW_LINK 0x08
+
+/*
+ * Address in ASIC's internal SRAM where the multicast hash table lives.
+ * The table is 64 bytes long, giving us a 512-bit table. We have to set
+ * the bit that corresponds to the broadcast address in order to enable
+ * reception of broadcast frames.
+ */
+#define CUE_MCAST_TABLE_ADDR 0xFA80
+
+#define CUE_TIMEOUT 1000
+#define CUE_MIN_FRAMELEN 60
+#define CUE_RX_FRAMES 1
+#define CUE_TX_FRAMES 1
+
+#define CUE_CTL_READ 0x01
+#define CUE_CTL_WRITE 0x02
+
+#define CUE_CONFIG_IDX 0 /* config number 1 */
+#define CUE_IFACE_IDX 0
+
+/* The interrupt endpoint is currently unused by the KLSI part. */
+enum {
+ CUE_BULK_DT_WR,
+ CUE_BULK_DT_RD,
+ CUE_N_TRANSFER,
+};
+
+struct cue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[CUE_N_TRANSFER];
+
+ int sc_flags;
+#define CUE_FLAG_LINK 0x0001 /* got a link */
+};
+
+#define CUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define CUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define CUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_kue.c b/sys/dev/usb/net/if_kue.c
new file mode 100644
index 0000000..b97922f
--- /dev/null
+++ b/sys/dev/usb/net/if_kue.c
@@ -0,0 +1,704 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Kawasaki LSI KL5KUSB101B USB to ethernet adapter driver.
+ *
+ * Written by Bill Paul <wpaul@ee.columbia.edu>
+ * Electrical Engineering Department
+ * Columbia University, New York City
+ */
+
+/*
+ * The KLSI USB to ethernet adapter chip contains an USB serial interface,
+ * ethernet MAC and embedded microcontroller (called the QT Engine).
+ * The chip must have firmware loaded into it before it will operate.
+ * Packets are passed between the chip and host via bulk transfers.
+ * There is an interrupt endpoint mentioned in the software spec, however
+ * it's currently unused. This device is 10Mbps half-duplex only, hence
+ * there is no media selection logic. The MAC supports a 128 entry
+ * multicast filter, though the exact size of the filter can depend
+ * on the firmware. Curiously, while the software spec describes various
+ * ethernet statistics counters, my sample adapter and firmware combination
+ * claims not to support any statistics counters at all.
+ *
+ * Note that once we load the firmware in the device, we have to be
+ * careful not to load it again: if you restart your computer but
+ * leave the adapter attached to the USB controller, it may remain
+ * powered on and retain its firmware. In this case, we don't need
+ * to load the firmware a second time.
+ *
+ * Special thanks to Rob Furr for providing an ADS Technologies
+ * adapter for development and testing. No monkeys were harmed during
+ * the development of this driver.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR kue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_kuereg.h>
+#include <dev/usb/net/if_kuefw.h>
+
+/*
+ * Various supported device vendors/products.
+ */
+static const struct usb2_device_id kue_devs[] = {
+ {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C19250, 0)},
+ {USB_VPI(USB_VENDOR_3COM, USB_PRODUCT_3COM_3C460, 0)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_URE450, 0)},
+ {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BT, 0)},
+ {USB_VPI(USB_VENDOR_ADS, USB_PRODUCT_ADS_UBS10BTX, 0)},
+ {USB_VPI(USB_VENDOR_AOX, USB_PRODUCT_AOX_USB101, 0)},
+ {USB_VPI(USB_VENDOR_ASANTE, USB_PRODUCT_ASANTE_EA, 0)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_DSB650C, 0)},
+ {USB_VPI(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC10T, 0)},
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_ETHER_USB_T, 0)},
+ {USB_VPI(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DSB650C, 0)},
+ {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_E45, 0)},
+ {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX1, 0)},
+ {USB_VPI(USB_VENDOR_ENTREGA, USB_PRODUCT_ENTREGA_XX2, 0)},
+ {USB_VPI(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBETT, 0)},
+ {USB_VPI(USB_VENDOR_JATON, USB_PRODUCT_JATON_EDA, 0)},
+ {USB_VPI(USB_VENDOR_KINGSTON, USB_PRODUCT_KINGSTON_XX1, 0)},
+ {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_AOX_USB101, 0)},
+ {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BT, 0)},
+ {USB_VPI(USB_VENDOR_KLSI, USB_PRODUCT_KLSI_DUH3E10BTN, 0)},
+ {USB_VPI(USB_VENDOR_LINKSYS, USB_PRODUCT_LINKSYS_USB10T, 0)},
+ {USB_VPI(USB_VENDOR_MOBILITY, USB_PRODUCT_MOBILITY_EA, 0)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101, 0)},
+ {USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_EA101X, 0)},
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET, 0)},
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET2, 0)},
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_ENET3, 0)},
+ {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA8, 0)},
+ {USB_VPI(USB_VENDOR_PORTGEAR, USB_PRODUCT_PORTGEAR_EA9, 0)},
+ {USB_VPI(USB_VENDOR_PORTSMITH, USB_PRODUCT_PORTSMITH_EEA, 0)},
+ {USB_VPI(USB_VENDOR_SHARK, USB_PRODUCT_SHARK_PA, 0)},
+ {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_GPE, 0)},
+ {USB_VPI(USB_VENDOR_SILICOM, USB_PRODUCT_SILICOM_U2E, 0)},
+ {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2102USB, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t kue_probe;
+static device_attach_t kue_attach;
+static device_detach_t kue_detach;
+static device_shutdown_t kue_shutdown;
+
+static usb2_callback_t kue_bulk_read_callback;
+static usb2_callback_t kue_bulk_write_callback;
+
+static usb2_ether_fn_t kue_attach_post;
+static usb2_ether_fn_t kue_init;
+static usb2_ether_fn_t kue_stop;
+static usb2_ether_fn_t kue_start;
+static usb2_ether_fn_t kue_setmulti;
+static usb2_ether_fn_t kue_setpromisc;
+
+static int kue_do_request(struct kue_softc *,
+ struct usb2_device_request *, void *);
+static int kue_setword(struct kue_softc *, uint8_t, uint16_t);
+static int kue_ctl(struct kue_softc *, uint8_t, uint8_t, uint16_t,
+ void *, int);
+static int kue_load_fw(struct kue_softc *);
+static void kue_reset(struct kue_softc *);
+
+#if USB_DEBUG
+static int kue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, kue, CTLFLAG_RW, 0, "USB kue");
+SYSCTL_INT(_hw_usb2_kue, OID_AUTO, debug, CTLFLAG_RW, &kue_debug, 0,
+ "Debug level");
+#endif
+
+static const struct usb2_config kue_config[KUE_N_TRANSFER] = {
+
+ [KUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2 + 64),
+ .mh.flags = {.pipe_bof = 1,},
+ .mh.callback = kue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [KUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = kue_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+};
+
+static device_method_t kue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, kue_probe),
+ DEVMETHOD(device_attach, kue_attach),
+ DEVMETHOD(device_detach, kue_detach),
+ DEVMETHOD(device_shutdown, kue_shutdown),
+
+ {0, 0}
+};
+
+static driver_t kue_driver = {
+ .name = "kue",
+ .methods = kue_methods,
+ .size = sizeof(struct kue_softc),
+};
+
+static devclass_t kue_devclass;
+
+DRIVER_MODULE(kue, ushub, kue_driver, kue_devclass, NULL, 0);
+MODULE_DEPEND(kue, uether, 1, 1, 1);
+MODULE_DEPEND(kue, usb, 1, 1, 1);
+MODULE_DEPEND(kue, ether, 1, 1, 1);
+
+static const struct usb2_ether_methods kue_ue_methods = {
+ .ue_attach_post = kue_attach_post,
+ .ue_start = kue_start,
+ .ue_init = kue_init,
+ .ue_stop = kue_stop,
+ .ue_setmulti = kue_setmulti,
+ .ue_setpromisc = kue_setpromisc,
+};
+
+/*
+ * We have a custom do_request function which is almost like the
+ * regular do_request function, except it has a much longer timeout.
+ * Why? Because we need to make requests over the control endpoint
+ * to download the firmware to the device, which can take longer
+ * than the default timeout.
+ */
+static int
+kue_do_request(struct kue_softc *sc, struct usb2_device_request *req,
+ void *data)
+{
+ usb2_error_t err;
+
+ err = usb2_ether_do_request(&sc->sc_ue, req, data, 60000);
+
+ return (err);
+}
+
+static int
+kue_setword(struct kue_softc *sc, uint8_t breq, uint16_t word)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = breq;
+ USETW(req.wValue, word);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ return (kue_do_request(sc, &req, NULL));
+}
+
+static int
+kue_ctl(struct kue_softc *sc, uint8_t rw, uint8_t breq,
+ uint16_t val, void *data, int len)
+{
+ struct usb2_device_request req;
+
+ if (rw == KUE_CTL_WRITE)
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ else
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+
+
+ req.bRequest = breq;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (kue_do_request(sc, &req, data));
+}
+
+static int
+kue_load_fw(struct kue_softc *sc)
+{
+ struct usb2_device_descriptor *dd;
+ uint16_t hwrev;
+ usb2_error_t err;
+
+ dd = usb2_get_device_descriptor(sc->sc_ue.ue_udev);
+ hwrev = UGETW(dd->bcdDevice);
+
+ /*
+ * First, check if we even need to load the firmware.
+ * If the device was still attached when the system was
+ * rebooted, it may already have firmware loaded in it.
+ * If this is the case, we don't need to do it again.
+ * And in fact, if we try to load it again, we'll hang,
+ * so we have to avoid this condition if we don't want
+ * to look stupid.
+ *
+ * We can test this quickly by checking the bcdRevision
+ * code. The NIC will return a different revision code if
+ * it's probed while the firmware is still loaded and
+ * running.
+ */
+ if (hwrev == 0x0202)
+ return(0);
+
+ /* Load code segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_code_seg, sizeof(kue_code_seg));
+ if (err) {
+ device_printf(sc->sc_ue.ue_dev, "failed to load code segment: %s\n",
+ usb2_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Load fixup segment */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_fix_seg, sizeof(kue_fix_seg));
+ if (err) {
+ device_printf(sc->sc_ue.ue_dev, "failed to load fixup segment: %s\n",
+ usb2_errstr(err));
+ return(ENXIO);
+ }
+
+ /* Send trigger command. */
+ err = kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SEND_SCAN,
+ 0, kue_trig_seg, sizeof(kue_trig_seg));
+ if (err) {
+ device_printf(sc->sc_ue.ue_dev, "failed to load trigger segment: %s\n",
+ usb2_errstr(err));
+ return(ENXIO);
+ }
+
+ return (0);
+}
+
+static void
+kue_setpromisc(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_PROMISC)
+ sc->sc_rxfilt |= KUE_RXFILT_PROMISC;
+ else
+ sc->sc_rxfilt &= ~KUE_RXFILT_PROMISC;
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt);
+}
+
+static void
+kue_setmulti(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct ifmultiaddr *ifma;
+ int i = 0;
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI;
+ sc->sc_rxfilt &= ~KUE_RXFILT_MULTICAST;
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt);
+ return;
+ }
+
+ sc->sc_rxfilt &= ~KUE_RXFILT_ALLMULTI;
+
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ /*
+ * If there are too many addresses for the
+ * internal filter, switch over to allmulti mode.
+ */
+ if (i == KUE_MCFILTCNT(sc))
+ break;
+ bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
+ KUE_MCFILT(sc, i), ETHER_ADDR_LEN);
+ i++;
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ if (i == KUE_MCFILTCNT(sc))
+ sc->sc_rxfilt |= KUE_RXFILT_ALLMULTI;
+ else {
+ sc->sc_rxfilt |= KUE_RXFILT_MULTICAST;
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MCAST_FILTERS,
+ i, sc->sc_mcfilters, i * ETHER_ADDR_LEN);
+ }
+
+ kue_setword(sc, KUE_CMD_SET_PKT_FILTER, sc->sc_rxfilt);
+}
+
+/*
+ * Issue a SET_CONFIGURATION command to reset the MAC. This should be
+ * done after the firmware is loaded into the adapter in order to
+ * bring it into proper operation.
+ */
+static void
+kue_reset(struct kue_softc *sc)
+{
+ struct usb2_config_descriptor *cd;
+ usb2_error_t err;
+
+ cd = usb2_get_config_descriptor(sc->sc_ue.ue_udev);
+
+ err = usb2_req_set_config(sc->sc_ue.ue_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (err)
+ DPRINTF("reset failed (ignored)\n");
+
+ /* wait a little while for the chip to get its brains in order */
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+kue_attach_post(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ int error;
+
+ /* load the firmware into the NIC */
+ error = kue_load_fw(sc);
+ if (error) {
+ device_printf(sc->sc_ue.ue_dev, "could not load firmware\n");
+ /* ignore the error */
+ }
+
+ /* reset the adapter */
+ kue_reset(sc);
+
+ /* read ethernet descriptor */
+ kue_ctl(sc, KUE_CTL_READ, KUE_CMD_GET_ETHER_DESCRIPTOR,
+ 0, &sc->sc_desc, sizeof(sc->sc_desc));
+
+ /* copy in ethernet address */
+ memcpy(ue->ue_eaddr, sc->sc_desc.kue_macaddr, sizeof(ue->ue_eaddr));
+}
+
+/*
+ * Probe for a KLSI chip.
+ */
+static int
+kue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != KUE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != KUE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(kue_devs, sizeof(kue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do
+ * setup and ethernet/BPF attach.
+ */
+static int
+kue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct kue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = KUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, kue_config, KUE_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ sc->sc_mcfilters = malloc(KUE_MCFILTCNT(sc) * ETHER_ADDR_LEN,
+ M_USBDEV, M_WAITOK);
+ if (sc->sc_mcfilters == NULL) {
+ device_printf(dev, "failed allocating USB memory!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &kue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ kue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+kue_detach(device_t dev)
+{
+ struct kue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+ free(sc->sc_mcfilters, M_USBDEV);
+
+ return (0);
+}
+
+/*
+ * A frame has been uploaded: pass the resulting mbuf chain up to
+ * the higher level protocols.
+ */
+static void
+kue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct kue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint8_t buf[2];
+ int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen <= (2 + sizeof(struct ether_header))) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+ xfer->actlen -= 2;
+ len = buf[0] | (buf[1] << 8);
+ len = min(xfer->actlen, len);
+
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 2, len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+kue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct kue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int total_len;
+ int temp_len;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ temp_len = (m->m_pkthdr.len + 2);
+ total_len = (temp_len + (64 - (temp_len % 64)));
+
+ /* the first two bytes are the frame length */
+
+ buf[0] = (uint8_t)(m->m_pkthdr.len);
+ buf[1] = (uint8_t)(m->m_pkthdr.len >> 8);
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+
+ usb2_bzero(xfer->frbuffers, temp_len,
+ total_len - temp_len);
+
+ xfer->frlengths[0] = total_len;
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+kue_start(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[KUE_BULK_DT_WR]);
+}
+
+static void
+kue_init(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* set MAC address */
+ kue_ctl(sc, KUE_CTL_WRITE, KUE_CMD_SET_MAC,
+ 0, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ /* I'm not sure how to tune these. */
+#if 0
+ /*
+ * Leave this one alone for now; setting it
+ * wrong causes lockups on some machines/controllers.
+ */
+ kue_setword(sc, KUE_CMD_SET_SOFS, 1);
+#endif
+ kue_setword(sc, KUE_CMD_SET_URB_SIZE, 64);
+
+ /* load the multicast filter */
+ kue_setpromisc(ue);
+
+ usb2_transfer_set_stall(sc->sc_xfer[KUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ kue_start(ue);
+}
+
+static void
+kue_stop(struct usb2_ether *ue)
+{
+ struct kue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ KUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[KUE_BULK_DT_RD]);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+kue_shutdown(device_t dev)
+{
+ struct kue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_kuefw.h b/sys/dev/usb/net/if_kuefw.h
new file mode 100644
index 0000000..2b055a9
--- /dev/null
+++ b/sys/dev/usb/net/if_kuefw.h
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * This file contains the firmware needed to make the KLSI chip work,
+ * along with a few constants related to the QT Engine microcontroller
+ * embedded in the KLSI part.
+ *
+ * Firmware is loaded using the vendor-specific 'send scan data'
+ * command (0xFF). The basic operation is that we must load the
+ * firmware, then issue some trigger commands to fix it up and start
+ * it running. There are three transfers: load the binary code,
+ * load the 'fixup' (data segment?), then issue a command to
+ * start the code firmware running. The data itself is prefixed by
+ * a 16-bit signature word, a 16-bit length value, a type byte
+ * and an interrupt (command) byte. The code segment is of type
+ * 0x02 (replacement interrupt vector data) and the fixup segment
+ * is of type 0x03 (replacement interrupt fixup data). The interrupt
+ * code is 0x64 (load new code). The length word is the total length
+ * of the segment minus 7. I precomputed the values and stuck them
+ * into the appropriate locations within the segments to save some
+ * work in the driver.
+ */
+
+/* QT controller data block types. */
+/* Write data into specific memory location. */
+#define KUE_QTBTYPE_WRITE_DATA 0x00
+/* Write data into interrupt vector location */
+#define KUE_QTBTYPE_WRITE_INTVEC 0x01
+/* Replace interrupt vector with this data */
+#define KUE_QTBTYPE_REPL_INTVEC 0x02
+/* Fixup interrupt vector code with this data */
+#define KUE_QTBTYPE_FIXUP_INTVEC 0x03
+/* Force jump to location */
+#define KUE_QTBTYPE_JUMP 0x04
+/* Force call to location */
+#define KUE_QTBTYPE_CALL 0x05
+/* Force interrupt call */
+#define KUE_QTBTYPE_CALLINTR 0x06
+/*
+ * Cause data to be written using the specified QT engine
+ * interrupt, from starting location in memory for a specified
+ * number of bytes.
+ */
+#define KUE_QTBTYPE_WRITE_WITH_INTR 0x07
+/* Cause data from stream to be written using specified QT interrupt. */
+#define KUE_QTBTYPE_WRITE_STR_WITH_INTR 0x08
+/* Cause data to be written to config locations. */
+/* Addresses assume 0xc000 offset. */
+#define KUE_QTBTYPE_WRITE_CONFIG 0x09
+
+#define KUE_QTINTR_LOAD_CODE 0x64
+#define KUE_QTINTR_TRIGGER_CODE 0x3B
+#define KUE_QTINTR_LOAD_CODE_HIGH 0x9C
+
+/* Firmware code segment */
+static unsigned char kue_code_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xf7, 0x0e, 0x02, 0x64,
+ 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00,
+ 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f,
+ 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09,
+ 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08,
+ 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08,
+ 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1,
+ 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00,
+ 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf,
+ 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09,
+ 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0,
+ 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08,
+ 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08,
+ 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57,
+ 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00,
+ 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09,
+ 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59,
+ 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00,
+ 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1,
+ 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03,
+ 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1,
+ 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08,
+ 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04,
+ 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09,
+ 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00,
+ 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08,
+ 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08,
+ 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00,
+ 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00,
+ 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf,
+ 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17,
+ 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08,
+ 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0,
+ 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37,
+ 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60,
+ 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9,
+ 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17,
+ 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00,
+ 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04,
+ 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf,
+ 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf,
+ 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf,
+ 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57,
+ 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02,
+ 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01,
+ 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02,
+ 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1,
+ 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02,
+ 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77,
+ 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07,
+ 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00,
+ 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77,
+ 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda,
+ 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0,
+ 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf,
+ 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8,
+ 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77,
+ 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50,
+ 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00,
+ 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02,
+ 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0,
+ 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00,
+ 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07,
+ 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e,
+ 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08,
+ 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07,
+ 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08,
+ 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09,
+ 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00,
+ 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf,
+ 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08,
+ 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05,
+ 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67,
+ 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0,
+ 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00,
+ 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08,
+ 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80,
+ 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08,
+ 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07,
+ 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0,
+ 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00,
+ 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00,
+ 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17,
+ 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07,
+ 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03,
+ 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0,
+ 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07,
+ 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00,
+ 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08,
+ 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08,
+ 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05,
+ 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08,
+ 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00,
+ 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+ 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+ 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00,
+ 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+ 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09,
+ 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52,
+ 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08,
+ 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17,
+ 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05,
+ 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04,
+ 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62,
+ 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02,
+ 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77,
+ 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9,
+ 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1,
+ 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27,
+ 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00,
+ 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff,
+ 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17,
+ 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07,
+ 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1,
+ 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59,
+ 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf,
+ 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08,
+ 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07,
+ 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77,
+ 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00,
+ 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00,
+ 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00,
+ 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0,
+ 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77,
+ 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf,
+ 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1,
+ 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1,
+ 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf,
+ 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
+ 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09,
+ 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53,
+ 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05,
+ 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8,
+ 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf,
+ 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30,
+ 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf,
+ 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30,
+ 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf,
+ 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09,
+ 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09,
+ 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0,
+ 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01,
+ 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07,
+ 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37,
+ 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08,
+ 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08,
+ 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08,
+ 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00,
+ 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02,
+ 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00,
+ 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00,
+ 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0,
+ 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04,
+ 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09,
+ 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00,
+ 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02,
+ 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05,
+ 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf,
+ 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0,
+ 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+ 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09,
+ 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0,
+ 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17,
+ 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07,
+ 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00,
+ 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf,
+ 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17,
+ 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57,
+ 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77,
+ 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00,
+ 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+ 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1,
+ 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02,
+ 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77,
+ 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+ 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00,
+ 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf,
+ 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00,
+ 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57,
+ 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57,
+ 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8,
+ 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27,
+ 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17,
+ 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00,
+ 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08,
+ 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09,
+ 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77,
+ 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57,
+ 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19,
+ 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17,
+ 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf,
+ 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57,
+ 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b,
+ 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05,
+ 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09,
+ 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04,
+ 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf,
+ 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc,
+ 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80,
+ 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e,
+ 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00,
+ 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09,
+ 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00,
+ 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07,
+ 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13,
+ 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08,
+ 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09,
+ 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09,
+ 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b,
+ 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00,
+ 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90,
+ 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00,
+ 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b,
+ 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00,
+ 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07,
+ 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00,
+ 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00,
+ 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf,
+ 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf,
+ 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08,
+ 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf,
+ 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94,
+ 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09,
+ 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf,
+ 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00,
+ 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00,
+ 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00,
+ 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00,
+ 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00,
+ 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0,
+ 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0,
+ 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a,
+ 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a,
+ 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b,
+ 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02,
+ 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00,
+ 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57,
+ 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37,
+ 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2,
+ 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00,
+ 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57,
+ 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b,
+ 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09,
+ 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07,
+ 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08,
+ 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08,
+ 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0,
+ 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00,
+ 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf,
+ 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09,
+ 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08,
+ 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c,
+ 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf,
+ 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2,
+ 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08,
+ 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07,
+ 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb,
+ 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf,
+ 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57,
+ 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07,
+ 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08,
+ 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07,
+ 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf,
+ 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00,
+ 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf,
+ 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1,
+ 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf,
+ 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf,
+ 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07,
+ 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00,
+ 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02,
+ 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08,
+ 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80,
+ 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+ 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08,
+ 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf,
+ 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf,
+ 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf,
+ 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07,
+ 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1,
+ 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00,
+ 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde,
+ 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2,
+ 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08,
+ 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf,
+ 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08,
+ 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde,
+ 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57,
+ 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67,
+ 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00,
+ 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0,
+ 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf,
+ 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf,
+ 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08,
+ 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf,
+ 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0,
+ 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00,
+ 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08,
+ 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00,
+ 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07,
+ 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00,
+ 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09,
+ 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07,
+ 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1,
+ 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf,
+ 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07,
+ 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08,
+ 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00,
+ 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00,
+ 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09,
+ 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02,
+ 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda,
+ 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08,
+ 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08,
+ 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02,
+ 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1,
+ 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0,
+ 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08,
+ 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08,
+ 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08,
+ 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08,
+ 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07,
+ 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00,
+ 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00,
+ 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+ 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07,
+ 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+ 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07,
+ 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+ 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09,
+ 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00,
+ 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0,
+ 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00,
+ 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08,
+ 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf,
+ 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf,
+ 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+ 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00,
+ 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d,
+ 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1,
+ 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87,
+ 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08,
+ 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0,
+ 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0,
+ 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87,
+ 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7,
+ 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0,
+ 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0,
+ 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09,
+ 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2,
+ 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf,
+ 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02,
+ 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08,
+ 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77,
+ 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf,
+ 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07,
+ 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e,
+ 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+ 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0,
+ 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77,
+ 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57,
+ 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb,
+ 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00,
+ 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb,
+ 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09,
+ 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00,
+ 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08,
+ 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09,
+ 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02,
+ 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05,
+ 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1,
+ 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02,
+ 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00,
+ 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04,
+ 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00,
+ 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf,
+ 0, 0
+};
+
+/* Firmware fixup (data?) segment */
+static unsigned char kue_fix_seg[] =
+{
+ /******************************************/
+ /* NOTE: B6/C3 is data header signature */
+ /* 0xAA/0xBB is data length = total */
+ /* bytes - 7, 0xCC is type, 0xDD is */
+ /* interrupt to use. */
+ /******************************************/
+ 0xB6, 0xC3, 0xc9, 0x02, 0x03, 0x64,
+ 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00,
+ 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00,
+ 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00,
+ 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00,
+ 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00,
+ 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00,
+ 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00,
+ 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00,
+ 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00,
+ 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00,
+ 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01,
+ 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01,
+ 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01,
+ 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01,
+ 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01,
+ 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01,
+ 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02,
+ 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02,
+ 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02,
+ 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02,
+ 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03,
+ 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03,
+ 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03,
+ 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03,
+ 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03,
+ 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03,
+ 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03,
+ 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04,
+ 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04,
+ 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04,
+ 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04,
+ 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04,
+ 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05,
+ 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05,
+ 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05,
+ 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05,
+ 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06,
+ 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06,
+ 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06,
+ 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06,
+ 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06,
+ 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06,
+ 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06,
+ 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07,
+ 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07,
+ 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08,
+ 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08,
+ 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08,
+ 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08,
+ 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09,
+ 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09,
+ 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09,
+ 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09,
+ 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09,
+ 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09,
+ 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09,
+ 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09,
+ 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a,
+ 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a,
+ 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a,
+ 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a,
+ 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a,
+ 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a,
+ 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a,
+ 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a,
+ 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a,
+ 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a,
+ 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b,
+ 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b,
+ 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b,
+ 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b,
+ 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b,
+ 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b,
+ 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c,
+ 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c,
+ 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c,
+ 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c,
+ 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c,
+ 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c,
+ 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c,
+ 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c,
+ 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d,
+ 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d,
+ 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d,
+ 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d,
+ 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d,
+ 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e,
+ 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e,
+ 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e,
+ 0, 0
+};
+
+/* Fixup command. */
+#define KUE_TRIGCMD_OFFSET 5
+static unsigned char kue_trig_seg[] = {
+ 0xb6, 0xc3, 0x01, 0x00, 0x06, 0x64, 0x00, 0x00
+};
diff --git a/sys/dev/usb/net/if_kuereg.h b/sys/dev/usb/net/if_kuereg.h
new file mode 100644
index 0000000..8650687
--- /dev/null
+++ b/sys/dev/usb/net/if_kuereg.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * 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$
+ */
+
+/*
+ * Definitions for the KLSI KL5KUSB101B USB to ethernet controller.
+ * The KLSI part is controlled via vendor control requests, the structure
+ * of which depend a bit on the firmware running on the internal
+ * microcontroller. The one exception is the 'send scan data' command,
+ * which is used to load the firmware.
+ */
+
+#define KUE_CMD_GET_ETHER_DESCRIPTOR 0x00
+#define KUE_CMD_SET_MCAST_FILTERS 0x01
+#define KUE_CMD_SET_PKT_FILTER 0x02
+#define KUE_CMD_GET_ETHERSTATS 0x03
+#define KUE_CMD_GET_GPIO 0x04
+#define KUE_CMD_SET_GPIO 0x05
+#define KUE_CMD_SET_MAC 0x06
+#define KUE_CMD_GET_MAC 0x07
+#define KUE_CMD_SET_URB_SIZE 0x08
+#define KUE_CMD_SET_SOFS 0x09
+#define KUE_CMD_SET_EVEN_PKTS 0x0A
+#define KUE_CMD_SEND_SCAN 0xFF
+
+struct kue_ether_desc {
+ uint8_t kue_len;
+ uint8_t kue_rsvd0;
+ uint8_t kue_rsvd1;
+ uint8_t kue_macaddr[ETHER_ADDR_LEN];
+ uint8_t kue_etherstats[4];
+ uint8_t kue_maxseg[2];
+ uint8_t kue_mcastfilt[2];
+ uint8_t kue_rsvd2;
+} __packed;
+
+#define KUE_ETHERSTATS(x) UGETDW((x)->sc_desc.kue_etherstats)
+#define KUE_MAXSEG(x) UGETW((x)->sc_desc.kue_maxseg)
+#define KUE_MCFILTCNT(x) (UGETW((x)->sc_desc.kue_mcastfilt) & 0x7FFF)
+#define KUE_MCFILT(x, y) \
+ (char *)&(sc->sc_mcfilters[y * ETHER_ADDR_LEN])
+
+#define KUE_STAT_TX_OK 0x00000001
+#define KUE_STAT_RX_OK 0x00000002
+#define KUE_STAT_TX_ERR 0x00000004
+#define KUE_STAT_RX_ERR 0x00000008
+#define KUE_STAT_RX_NOBUF 0x00000010
+#define KUE_STAT_TX_UCAST_BYTES 0x00000020
+#define KUE_STAT_TX_UCAST_FRAMES 0x00000040
+#define KUE_STAT_TX_MCAST_BYTES 0x00000080
+#define KUE_STAT_TX_MCAST_FRAMES 0x00000100
+#define KUE_STAT_TX_BCAST_BYTES 0x00000200
+#define KUE_STAT_TX_BCAST_FRAMES 0x00000400
+#define KUE_STAT_RX_UCAST_BYTES 0x00000800
+#define KUE_STAT_RX_UCAST_FRAMES 0x00001000
+#define KUE_STAT_RX_MCAST_BYTES 0x00002000
+#define KUE_STAT_RX_MCAST_FRAMES 0x00004000
+#define KUE_STAT_RX_BCAST_BYTES 0x00008000
+#define KUE_STAT_RX_BCAST_FRAMES 0x00010000
+#define KUE_STAT_RX_CRCERR 0x00020000
+#define KUE_STAT_TX_QUEUE_LENGTH 0x00040000
+#define KUE_STAT_RX_ALIGNERR 0x00080000
+#define KUE_STAT_TX_SINGLECOLL 0x00100000
+#define KUE_STAT_TX_MULTICOLL 0x00200000
+#define KUE_STAT_TX_DEFERRED 0x00400000
+#define KUE_STAT_TX_MAXCOLLS 0x00800000
+#define KUE_STAT_RX_OVERRUN 0x01000000
+#define KUE_STAT_TX_UNDERRUN 0x02000000
+#define KUE_STAT_TX_SQE_ERR 0x04000000
+#define KUE_STAT_TX_CARRLOSS 0x08000000
+#define KUE_STAT_RX_LATECOLL 0x10000000
+
+#define KUE_RXFILT_PROMISC 0x0001
+#define KUE_RXFILT_ALLMULTI 0x0002
+#define KUE_RXFILT_UNICAST 0x0004
+#define KUE_RXFILT_BROADCAST 0x0008
+#define KUE_RXFILT_MULTICAST 0x0010
+
+#define KUE_TIMEOUT 1000
+#define KUE_MIN_FRAMELEN 60
+
+#define KUE_CTL_READ 0x01
+#define KUE_CTL_WRITE 0x02
+
+#define KUE_CONFIG_IDX 0 /* config number 1 */
+#define KUE_IFACE_IDX 0
+
+/* The interrupt endpoint is currently unused by the KLSI part. */
+#define KUE_ENDPT_MAX 4
+enum {
+ KUE_BULK_DT_WR,
+ KUE_BULK_DT_RD,
+ KUE_N_TRANSFER,
+};
+
+struct kue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct kue_ether_desc sc_desc;
+ struct usb2_xfer *sc_xfer[KUE_N_TRANSFER];
+ uint8_t *sc_mcfilters;
+
+ int sc_flags;
+#define KUE_FLAG_LINK 0x0001
+
+ uint16_t sc_rxfilt;
+};
+
+#define KUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define KUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define KUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_rue.c b/sys/dev/usb/net/if_rue.c
new file mode 100644
index 0000000..1d0f6ee
--- /dev/null
+++ b/sys/dev/usb/net/if_rue.c
@@ -0,0 +1,913 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * Copyright (c) 1997, 1998, 1999, 2000 Bill Paul <wpaul@ee.columbia.edu>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * RealTek RTL8150 USB to fast ethernet controller driver.
+ * Datasheet is available from
+ * ftp://ftp.realtek.com.tw/lancard/data_sheet/8150/.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR rue_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_ruereg.h>
+
+#if USB_DEBUG
+static int rue_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, rue, CTLFLAG_RW, 0, "USB rue");
+SYSCTL_INT(_hw_usb2_rue, OID_AUTO, debug, CTLFLAG_RW,
+ &rue_debug, 0, "Debug level");
+#endif
+
+/*
+ * Various supported device vendors/products.
+ */
+
+static const struct usb2_device_id rue_devs[] = {
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_LUAKTX, 0)},
+ {USB_VPI(USB_VENDOR_REALTEK, USB_PRODUCT_REALTEK_USBKR100, 0)},
+};
+
+/* prototypes */
+
+static device_probe_t rue_probe;
+static device_attach_t rue_attach;
+static device_detach_t rue_detach;
+static device_shutdown_t rue_shutdown;
+
+static miibus_readreg_t rue_miibus_readreg;
+static miibus_writereg_t rue_miibus_writereg;
+static miibus_statchg_t rue_miibus_statchg;
+
+static usb2_callback_t rue_intr_callback;
+static usb2_callback_t rue_bulk_read_callback;
+static usb2_callback_t rue_bulk_write_callback;
+
+static usb2_ether_fn_t rue_attach_post;
+static usb2_ether_fn_t rue_init;
+static usb2_ether_fn_t rue_stop;
+static usb2_ether_fn_t rue_start;
+static usb2_ether_fn_t rue_tick;
+static usb2_ether_fn_t rue_setmulti;
+static usb2_ether_fn_t rue_setpromisc;
+
+static int rue_read_mem(struct rue_softc *, uint16_t, void *, int);
+static int rue_write_mem(struct rue_softc *, uint16_t, void *, int);
+static uint8_t rue_csr_read_1(struct rue_softc *, uint16_t);
+static uint16_t rue_csr_read_2(struct rue_softc *, uint16_t);
+static int rue_csr_write_1(struct rue_softc *, uint16_t, uint8_t);
+static int rue_csr_write_2(struct rue_softc *, uint16_t, uint16_t);
+static int rue_csr_write_4(struct rue_softc *, int, uint32_t);
+
+static void rue_reset(struct rue_softc *);
+static int rue_ifmedia_upd(struct ifnet *);
+static void rue_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static const struct usb2_config rue_config[RUE_N_TRANSFER] = {
+
+ [RUE_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = MCLBYTES,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = rue_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [RUE_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 4),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = rue_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+
+ [RUE_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = rue_intr_callback,
+ },
+};
+
+static device_method_t rue_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rue_probe),
+ DEVMETHOD(device_attach, rue_attach),
+ DEVMETHOD(device_detach, rue_detach),
+ DEVMETHOD(device_shutdown, rue_shutdown),
+
+ /* Bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, rue_miibus_readreg),
+ DEVMETHOD(miibus_writereg, rue_miibus_writereg),
+ DEVMETHOD(miibus_statchg, rue_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t rue_driver = {
+ .name = "rue",
+ .methods = rue_methods,
+ .size = sizeof(struct rue_softc),
+};
+
+static devclass_t rue_devclass;
+
+DRIVER_MODULE(rue, ushub, rue_driver, rue_devclass, NULL, 0);
+DRIVER_MODULE(miibus, rue, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(rue, uether, 1, 1, 1);
+MODULE_DEPEND(rue, usb, 1, 1, 1);
+MODULE_DEPEND(rue, ether, 1, 1, 1);
+MODULE_DEPEND(rue, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods rue_ue_methods = {
+ .ue_attach_post = rue_attach_post,
+ .ue_start = rue_start,
+ .ue_init = rue_init,
+ .ue_stop = rue_stop,
+ .ue_tick = rue_tick,
+ .ue_setmulti = rue_setmulti,
+ .ue_setpromisc = rue_setpromisc,
+ .ue_mii_upd = rue_ifmedia_upd,
+ .ue_mii_sts = rue_ifmedia_sts,
+};
+
+#define RUE_SETBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) | (x))
+
+#define RUE_CLRBIT(sc, reg, x) \
+ rue_csr_write_1(sc, reg, rue_csr_read_1(sc, reg) & ~(x))
+
+static int
+rue_read_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+rue_write_mem(struct rue_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UR_SET_ADDRESS;
+ USETW(req.wValue, addr);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static uint8_t
+rue_csr_read_1(struct rue_softc *sc, uint16_t reg)
+{
+ uint8_t val;
+
+ rue_read_mem(sc, reg, &val, 1);
+ return (val);
+}
+
+static uint16_t
+rue_csr_read_2(struct rue_softc *sc, uint16_t reg)
+{
+ uint8_t val[2];
+
+ rue_read_mem(sc, reg, &val, 2);
+ return (UGETW(val));
+}
+
+static int
+rue_csr_write_1(struct rue_softc *sc, uint16_t reg, uint8_t val)
+{
+ return (rue_write_mem(sc, reg, &val, 1));
+}
+
+static int
+rue_csr_write_2(struct rue_softc *sc, uint16_t reg, uint16_t val)
+{
+ uint8_t temp[2];
+
+ USETW(temp, val);
+ return (rue_write_mem(sc, reg, &temp, 2));
+}
+
+static int
+rue_csr_write_4(struct rue_softc *sc, int reg, uint32_t val)
+{
+ uint8_t temp[4];
+
+ USETDW(temp, val);
+ return (rue_write_mem(sc, reg, &temp, 4));
+}
+
+static int
+rue_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ uint16_t rval;
+ uint16_t ruereg;
+ int locked;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ RUE_LOCK(sc);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ rval = 0;
+ goto done;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rval = rue_csr_read_1(sc, reg);
+ goto done;
+ }
+ device_printf(sc->sc_ue.ue_dev, "bad phy register\n");
+ rval = 0;
+ goto done;
+ }
+
+ rval = rue_csr_read_2(sc, ruereg);
+done:
+ if (!locked)
+ RUE_UNLOCK(sc);
+ return (rval);
+}
+
+static int
+rue_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ uint16_t ruereg;
+ int locked;
+
+ if (phy != 0) /* RTL8150 supports PHY == 0, only */
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ RUE_LOCK(sc);
+
+ switch (reg) {
+ case MII_BMCR:
+ ruereg = RUE_BMCR;
+ break;
+ case MII_BMSR:
+ ruereg = RUE_BMSR;
+ break;
+ case MII_ANAR:
+ ruereg = RUE_ANAR;
+ break;
+ case MII_ANER:
+ ruereg = RUE_AER;
+ break;
+ case MII_ANLPAR:
+ ruereg = RUE_ANLP;
+ break;
+ case MII_PHYIDR1:
+ case MII_PHYIDR2:
+ goto done;
+ default:
+ if (RUE_REG_MIN <= reg && reg <= RUE_REG_MAX) {
+ rue_csr_write_1(sc, reg, data);
+ goto done;
+ }
+ device_printf(sc->sc_ue.ue_dev, " bad phy register\n");
+ goto done;
+ }
+ rue_csr_write_2(sc, ruereg, data);
+done:
+ if (!locked)
+ RUE_UNLOCK(sc);
+ return (0);
+}
+
+static void
+rue_miibus_statchg(device_t dev)
+{
+ /*
+ * When the code below is enabled the card starts doing weird
+ * things after link going from UP to DOWN and back UP.
+ *
+ * Looks like some of register writes below messes up PHY
+ * interface.
+ *
+ * No visible regressions were found after commenting this code
+ * out, so that disable it for good.
+ */
+#if 0
+ struct rue_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = GET_MII(sc);
+ uint16_t bmcr;
+ int locked;
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ RUE_LOCK(sc);
+
+ RUE_CLRBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ bmcr = rue_csr_read_2(sc, RUE_BMCR);
+
+ if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+ bmcr |= RUE_BMCR_SPD_SET;
+ else
+ bmcr &= ~RUE_BMCR_SPD_SET;
+
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
+ bmcr |= RUE_BMCR_DUPLEX;
+ else
+ bmcr &= ~RUE_BMCR_DUPLEX;
+
+ rue_csr_write_2(sc, RUE_BMCR, bmcr);
+
+ RUE_SETBIT(sc, RUE_CR, (RUE_CR_RE | RUE_CR_TE));
+
+ if (!locked)
+ RUE_UNLOCK(sc);
+#endif
+}
+
+static void
+rue_setpromisc(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* If we want promiscuous mode, set the allframes bit. */
+ if (ifp->if_flags & IFF_PROMISC)
+ RUE_SETBIT(sc, RUE_RCR, RUE_RCR_AAP);
+ else
+ RUE_CLRBIT(sc, RUE_RCR, RUE_RCR_AAP);
+}
+
+/*
+ * Program the 64-bit multicast hash filter.
+ */
+static void
+rue_setmulti(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t rxcfg;
+ int h = 0;
+ uint32_t hashes[2] = { 0, 0 };
+ struct ifmultiaddr *ifma;
+ int mcnt = 0;
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ rxcfg = rue_csr_read_2(sc, RUE_RCR);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ rxcfg |= (RUE_RCR_AAM | RUE_RCR_AAP);
+ rxcfg &= ~RUE_RCR_AM;
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, 0xFFFFFFFF);
+ rue_csr_write_4(sc, RUE_MAR4, 0xFFFFFFFF);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ rue_csr_write_4(sc, RUE_MAR0, 0);
+ rue_csr_write_4(sc, RUE_MAR4, 0);
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH (ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ if (h < 32)
+ hashes[0] |= (1 << h);
+ else
+ hashes[1] |= (1 << (h - 32));
+ mcnt++;
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ if (mcnt)
+ rxcfg |= RUE_RCR_AM;
+ else
+ rxcfg &= ~RUE_RCR_AM;
+
+ rxcfg &= ~(RUE_RCR_AAM | RUE_RCR_AAP);
+
+ rue_csr_write_2(sc, RUE_RCR, rxcfg);
+ rue_csr_write_4(sc, RUE_MAR0, hashes[0]);
+ rue_csr_write_4(sc, RUE_MAR4, hashes[1]);
+}
+
+static void
+rue_reset(struct rue_softc *sc)
+{
+ int i;
+
+ rue_csr_write_1(sc, RUE_CR, RUE_CR_SOFT_RST);
+
+ for (i = 0; i != RUE_TIMEOUT; i++) {
+ if (usb2_ether_pause(&sc->sc_ue, hz / 1000))
+ break;
+ if (!(rue_csr_read_1(sc, RUE_CR) & RUE_CR_SOFT_RST))
+ break;
+ }
+ if (i == RUE_TIMEOUT)
+ device_printf(sc->sc_ue.ue_dev, "reset never completed!\n");
+
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+static void
+rue_attach_post(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+
+ /* reset the adapter */
+ rue_reset(sc);
+
+ /* get station address from the EEPROM */
+ rue_read_mem(sc, RUE_EEPROM_IDR0, ue->ue_eaddr, ETHER_ADDR_LEN);
+}
+
+/*
+ * Probe for a RTL8150 chip.
+ */
+static int
+rue_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != RUE_CONFIG_IDX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RUE_IFACE_IDX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(rue_devs, sizeof(rue_devs), uaa));
+}
+
+/*
+ * Attach the interface. Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+rue_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct rue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = RUE_IFACE_IDX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, rue_config, RUE_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &rue_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ rue_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+rue_detach(device_t dev)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, RUE_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+rue_intr_callback(struct usb2_xfer *xfer)
+{
+ struct rue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct rue_intrpkt pkt;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (ifp && (ifp->if_drv_flags & IFF_DRV_RUNNING) &&
+ (xfer->actlen >= sizeof(pkt))) {
+
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, sizeof(pkt));
+
+ ifp->if_ierrors += pkt.rue_rxlost_cnt;
+ ifp->if_ierrors += pkt.rue_crcerr_cnt;
+ ifp->if_collisions += pkt.rue_col_cnt;
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct rue_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ uint16_t status;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < 4) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, xfer->actlen - 4,
+ &status, sizeof(status));
+ xfer->actlen -= 4;
+
+ /* check recieve packet was valid or not */
+ status = le16toh(status);
+ if ((status & RUE_RXSTAT_VALID) == 0) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_ether_rxbuf(ue, xfer->frbuffers, 0, xfer->actlen);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct rue_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int temp_len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & RUE_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ temp_len = m->m_pkthdr.len;
+
+ usb2_m_copy_in(xfer->frbuffers, 0,
+ m, 0, m->m_pkthdr.len);
+
+ /*
+ * This is an undocumented behavior.
+ * RTL8150 chip doesn't send frame length smaller than
+ * RUE_MIN_FRAMELEN (60) byte packet.
+ */
+ if (temp_len < RUE_MIN_FRAMELEN) {
+ usb2_bzero(xfer->frbuffers, temp_len,
+ RUE_MIN_FRAMELEN - temp_len);
+ temp_len = RUE_MIN_FRAMELEN;
+ }
+ xfer->frlengths[0] = temp_len;
+
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ usb2_start_hardware(xfer);
+
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+rue_tick(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct mii_data *mii = GET_MII(sc);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & RUE_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= RUE_FLAG_LINK;
+ rue_start(ue);
+ }
+}
+
+static void
+rue_start(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[RUE_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[RUE_BULK_DT_WR]);
+}
+
+static void
+rue_init(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O
+ */
+ rue_reset(sc);
+
+ /* Set MAC address */
+ rue_write_mem(sc, RUE_IDR0, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ rue_stop(ue);
+
+ /*
+ * Set the initial TX and RX configuration.
+ */
+ rue_csr_write_1(sc, RUE_TCR, RUE_TCR_CONFIG);
+ rue_csr_write_2(sc, RUE_RCR, RUE_RCR_CONFIG|RUE_RCR_AB);
+
+ /* Load the multicast filter */
+ rue_setpromisc(ue);
+ /* Load the multicast filter. */
+ rue_setmulti(ue);
+
+ /* Enable RX and TX */
+ rue_csr_write_1(sc, RUE_CR, (RUE_CR_TE | RUE_CR_RE | RUE_CR_EP3CLREN));
+
+ usb2_transfer_set_stall(sc->sc_xfer[RUE_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ rue_start(ue);
+}
+
+/*
+ * Set media options.
+ */
+static int
+rue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~RUE_FLAG_LINK;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ return (0);
+}
+
+/*
+ * Report current media status.
+ */
+static void
+rue_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct rue_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ RUE_LOCK(sc);
+ mii_pollstat(mii);
+ RUE_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+rue_stop(struct usb2_ether *ue)
+{
+ struct rue_softc *sc = usb2_ether_getsc(ue);
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+
+ RUE_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~RUE_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[RUE_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[RUE_INTR_DT_RD]);
+
+ rue_csr_write_1(sc, RUE_CR, 0x00);
+
+ rue_reset(sc);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+rue_shutdown(device_t dev)
+{
+ struct rue_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_ruereg.h b/sys/dev/usb/net/if_ruereg.h
new file mode 100644
index 0000000..a94d45a
--- /dev/null
+++ b/sys/dev/usb/net/if_ruereg.h
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2001-2003, Shunsuke Akiyama <akiyama@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define RUE_CONFIG_IDX 0 /* config number 1 */
+#define RUE_IFACE_IDX 0
+
+#define RUE_INTR_PKTLEN 0x8
+
+#define RUE_TIMEOUT 50
+#define RUE_MIN_FRAMELEN 60
+
+/* Registers. */
+#define RUE_IDR0 0x0120
+#define RUE_IDR1 0x0121
+#define RUE_IDR2 0x0122
+#define RUE_IDR3 0x0123
+#define RUE_IDR4 0x0124
+#define RUE_IDR5 0x0125
+
+#define RUE_MAR0 0x0126
+#define RUE_MAR1 0x0127
+#define RUE_MAR2 0x0128
+#define RUE_MAR3 0x0129
+#define RUE_MAR4 0x012A
+#define RUE_MAR5 0x012B
+#define RUE_MAR6 0x012C
+#define RUE_MAR7 0x012D
+
+#define RUE_CR 0x012E /* B, R/W */
+#define RUE_CR_SOFT_RST 0x10
+#define RUE_CR_RE 0x08
+#define RUE_CR_TE 0x04
+#define RUE_CR_EP3CLREN 0x02
+
+#define RUE_TCR 0x012F /* B, R/W */
+#define RUE_TCR_TXRR1 0x80
+#define RUE_TCR_TXRR0 0x40
+#define RUE_TCR_IFG1 0x10
+#define RUE_TCR_IFG0 0x08
+#define RUE_TCR_NOCRC 0x01
+#define RUE_TCR_CONFIG (RUE_TCR_TXRR1 | RUE_TCR_TXRR0 | \
+ RUE_TCR_IFG1 | RUE_TCR_IFG0)
+
+#define RUE_RCR 0x0130 /* W, R/W */
+#define RUE_RCR_TAIL 0x80
+#define RUE_RCR_AER 0x40
+#define RUE_RCR_AR 0x20
+#define RUE_RCR_AM 0x10
+#define RUE_RCR_AB 0x08
+#define RUE_RCR_AD 0x04
+#define RUE_RCR_AAM 0x02
+#define RUE_RCR_AAP 0x01
+#define RUE_RCR_CONFIG (RUE_RCR_TAIL | RUE_RCR_AD)
+
+#define RUE_TSR 0x0132
+#define RUE_RSR 0x0133
+#define RUE_CON0 0x0135
+#define RUE_CON1 0x0136
+#define RUE_MSR 0x0137
+#define RUE_PHYADD 0x0138
+#define RUE_PHYDAT 0x0139
+
+#define RUE_PHYCNT 0x013B /* B, R/W */
+#define RUE_PHYCNT_PHYOWN 0x40
+#define RUE_PHYCNT_RWCR 0x20
+
+#define RUE_GPPC 0x013D
+#define RUE_WAKECNT 0x013E
+
+#define RUE_BMCR 0x0140
+#define RUE_BMCR_SPD_SET 0x2000
+#define RUE_BMCR_DUPLEX 0x0100
+
+#define RUE_BMSR 0x0142
+
+#define RUE_ANAR 0x0144 /* W, R/W */
+#define RUE_ANAR_PAUSE 0x0400
+
+#define RUE_ANLP 0x0146 /* W, R/O */
+#define RUE_ANLP_PAUSE 0x0400
+
+#define RUE_AER 0x0148
+
+#define RUE_NWAYT 0x014A
+#define RUE_CSCR 0x014C
+
+#define RUE_CRC0 0x014E
+#define RUE_CRC1 0x0150
+#define RUE_CRC2 0x0152
+#define RUE_CRC3 0x0154
+#define RUE_CRC4 0x0156
+
+#define RUE_BYTEMASK0 0x0158
+#define RUE_BYTEMASK1 0x0160
+#define RUE_BYTEMASK2 0x0168
+#define RUE_BYTEMASK3 0x0170
+#define RUE_BYTEMASK4 0x0178
+
+#define RUE_PHY1 0x0180
+#define RUE_PHY2 0x0184
+
+#define RUE_TW1 0x0186
+
+#define RUE_REG_MIN 0x0120
+#define RUE_REG_MAX 0x0189
+
+/* EEPROM address declarations. */
+#define RUE_EEPROM_BASE 0x1200
+#define RUE_EEPROM_IDR0 (RUE_EEPROM_BASE + 0x02)
+#define RUE_EEPROM_IDR1 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR2 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR3 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR4 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_IDR5 (RUE_EEPROM_BASE + 0x03)
+#define RUE_EEPROM_INTERVAL (RUE_EEPROM_BASE + 0x17)
+
+#define RUE_RXSTAT_VALID (0x01 << 12)
+#define RUE_RXSTAT_RUNT (0x02 << 12)
+#define RUE_RXSTAT_PMATCH (0x04 << 12)
+#define RUE_RXSTAT_MCAST (0x08 << 12)
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+struct rue_intrpkt {
+ uint8_t rue_tsr;
+ uint8_t rue_rsr;
+ uint8_t rue_gep_msr;
+ uint8_t rue_waksr;
+ uint8_t rue_txok_cnt;
+ uint8_t rue_rxlost_cnt;
+ uint8_t rue_crcerr_cnt;
+ uint8_t rue_col_cnt;
+} __packed;
+
+struct rue_type {
+ uint16_t rue_vid;
+ uint16_t rue_did;
+};
+
+enum {
+ RUE_BULK_DT_WR,
+ RUE_BULK_DT_RD,
+ RUE_INTR_DT_RD,
+ RUE_N_TRANSFER,
+};
+
+struct rue_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[RUE_N_TRANSFER];
+
+ int sc_flags;
+#define RUE_FLAG_LINK 0x0001
+};
+
+#define RUE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define RUE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define RUE_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/if_udav.c b/sys/dev/usb/net/if_udav.c
new file mode 100644
index 0000000..82cec80
--- /dev/null
+++ b/sys/dev/usb/net/if_udav.c
@@ -0,0 +1,856 @@
+/* $NetBSD: if_udav.c,v 1.2 2003/09/04 15:17:38 tsutsui Exp $ */
+/* $nabe: if_udav.c,v 1.3 2003/08/21 16:57:19 nabe Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * DM9601(DAVICOM USB to Ethernet MAC Controller with Integrated 10/100 PHY)
+ * The spec can be found at the following url.
+ * http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf
+ */
+
+/*
+ * TODO:
+ * Interrupt Endpoint support
+ * External PHYs
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR udav_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+#include <dev/usb/net/if_udavreg.h>
+
+/* prototypes */
+
+static device_probe_t udav_probe;
+static device_attach_t udav_attach;
+static device_detach_t udav_detach;
+static device_shutdown_t udav_shutdown;
+
+static usb2_callback_t udav_bulk_write_callback;
+static usb2_callback_t udav_bulk_read_callback;
+static usb2_callback_t udav_intr_callback;
+
+static usb2_ether_fn_t udav_attach_post;
+static usb2_ether_fn_t udav_init;
+static usb2_ether_fn_t udav_stop;
+static usb2_ether_fn_t udav_start;
+static usb2_ether_fn_t udav_tick;
+static usb2_ether_fn_t udav_setmulti;
+static usb2_ether_fn_t udav_setpromisc;
+
+static int udav_csr_read(struct udav_softc *, uint16_t, void *, int);
+static int udav_csr_write(struct udav_softc *, uint16_t, void *, int);
+static uint8_t udav_csr_read1(struct udav_softc *, uint16_t);
+static int udav_csr_write1(struct udav_softc *, uint16_t, uint8_t);
+static void udav_reset(struct udav_softc *);
+static int udav_ifmedia_upd(struct ifnet *);
+static void udav_ifmedia_status(struct ifnet *, struct ifmediareq *);
+
+static miibus_readreg_t udav_miibus_readreg;
+static miibus_writereg_t udav_miibus_writereg;
+static miibus_statchg_t udav_miibus_statchg;
+
+static const struct usb2_config udav_config[UDAV_N_TRANSFER] = {
+
+ [UDAV_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + 2),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = udav_bulk_write_callback,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+
+ [UDAV_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + 3),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = udav_bulk_read_callback,
+ .mh.timeout = 0, /* no timeout */
+ },
+
+ [UDAV_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = udav_intr_callback,
+ },
+};
+
+static device_method_t udav_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, udav_probe),
+ DEVMETHOD(device_attach, udav_attach),
+ DEVMETHOD(device_detach, udav_detach),
+ DEVMETHOD(device_shutdown, udav_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, udav_miibus_readreg),
+ DEVMETHOD(miibus_writereg, udav_miibus_writereg),
+ DEVMETHOD(miibus_statchg, udav_miibus_statchg),
+
+ {0, 0}
+};
+
+static driver_t udav_driver = {
+ .name = "udav",
+ .methods = udav_methods,
+ .size = sizeof(struct udav_softc),
+};
+
+static devclass_t udav_devclass;
+
+DRIVER_MODULE(udav, ushub, udav_driver, udav_devclass, NULL, 0);
+DRIVER_MODULE(miibus, udav, miibus_driver, miibus_devclass, 0, 0);
+MODULE_DEPEND(udav, uether, 1, 1, 1);
+MODULE_DEPEND(udav, usb, 1, 1, 1);
+MODULE_DEPEND(udav, ether, 1, 1, 1);
+MODULE_DEPEND(udav, miibus, 1, 1, 1);
+
+static const struct usb2_ether_methods udav_ue_methods = {
+ .ue_attach_post = udav_attach_post,
+ .ue_start = udav_start,
+ .ue_init = udav_init,
+ .ue_stop = udav_stop,
+ .ue_tick = udav_tick,
+ .ue_setmulti = udav_setmulti,
+ .ue_setpromisc = udav_setpromisc,
+ .ue_mii_upd = udav_ifmedia_upd,
+ .ue_mii_sts = udav_ifmedia_status,
+};
+
+#if USB_DEBUG
+static int udav_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, udav, CTLFLAG_RW, 0, "USB udav");
+SYSCTL_INT(_hw_usb2_udav, OID_AUTO, debug, CTLFLAG_RW, &udav_debug, 0,
+ "Debug level");
+#endif
+
+#define UDAV_SETBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) | (x))
+
+#define UDAV_CLRBIT(sc, reg, x) \
+ udav_csr_write1(sc, reg, udav_csr_read1(sc, reg) & ~(x))
+
+static const struct usb2_device_id udav_devs[] = {
+ /* ShanTou DM9601 USB NIC */
+ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_DM9601, 0)},
+ /* ShanTou ST268 USB NIC */
+ {USB_VPI(USB_VENDOR_SHANTOU, USB_PRODUCT_SHANTOU_ST268, 0)},
+ /* Corega USB-TXC */
+ {USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_FETHER_USB_TXC, 0)},
+};
+
+static void
+udav_attach_post(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = usb2_ether_getsc(ue);
+
+ /* reset the adapter */
+ udav_reset(sc);
+
+ /* Get Ethernet Address */
+ udav_csr_read(sc, UDAV_PAR, ue->ue_eaddr, ETHER_ADDR_LEN);
+}
+
+static int
+udav_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != UDAV_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != UDAV_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(udav_devs, sizeof(udav_devs), uaa));
+}
+
+static int
+udav_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct udav_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+ uint8_t iface_index;
+ int error;
+
+ sc->sc_flags = USB_GET_DRIVER_INFO(uaa);
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+ iface_index = UDAV_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, udav_config, UDAV_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "allocating USB transfers failed!\n");
+ goto detach;
+ }
+
+ ue->ue_sc = sc;
+ ue->ue_dev = dev;
+ ue->ue_udev = uaa->device;
+ ue->ue_mtx = &sc->sc_mtx;
+ ue->ue_methods = &udav_ue_methods;
+
+ error = usb2_ether_ifattach(ue);
+ if (error) {
+ device_printf(dev, "could not attach interface\n");
+ goto detach;
+ }
+
+ return (0); /* success */
+
+detach:
+ udav_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+udav_detach(device_t dev)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+ struct usb2_ether *ue = &sc->sc_ue;
+
+ usb2_transfer_unsetup(sc->sc_xfer, UDAV_N_TRANSFER);
+ usb2_ether_ifdetach(ue);
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+#if 0
+static int
+udav_mem_read(struct udav_softc *sc, uint16_t offset, void *buf,
+ int len)
+{
+ struct usb2_device_request req;
+
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_mem_write(struct udav_softc *sc, uint16_t offset, void *buf,
+ int len)
+{
+ struct usb2_device_request req;
+
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_mem_write1(struct udav_softc *sc, uint16_t offset,
+ uint8_t ch)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_MEM_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+#endif
+
+static int
+udav_csr_read(struct udav_softc *sc, uint16_t offset, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ len &= 0xff;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_READ;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static int
+udav_csr_write(struct udav_softc *sc, uint16_t offset, void *buf, int len)
+{
+ struct usb2_device_request req;
+
+ offset &= 0xff;
+ len &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE;
+ USETW(req.wValue, 0x0000);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, len);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, buf, 1000));
+}
+
+static uint8_t
+udav_csr_read1(struct udav_softc *sc, uint16_t offset)
+{
+ uint8_t val;
+
+ udav_csr_read(sc, offset, &val, 1);
+ return (val);
+}
+
+static int
+udav_csr_write1(struct udav_softc *sc, uint16_t offset,
+ uint8_t ch)
+{
+ struct usb2_device_request req;
+
+ offset &= 0xff;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UDAV_REQ_REG_WRITE1;
+ USETW(req.wValue, ch);
+ USETW(req.wIndex, offset);
+ USETW(req.wLength, 0x0000);
+
+ return (usb2_ether_do_request(&sc->sc_ue, &req, NULL, 1000));
+}
+
+static void
+udav_init(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ /*
+ * Cancel pending I/O
+ */
+ udav_stop(ue);
+
+ /* set MAC address */
+ udav_csr_write(sc, UDAV_PAR, IF_LLADDR(ifp), ETHER_ADDR_LEN);
+
+ /* initialize network control register */
+
+ /* disable loopback */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_LBK0 | UDAV_NCR_LBK1);
+
+ /* Initialize RX control register */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_DIS_LONG | UDAV_RCR_DIS_CRC);
+
+ /* load multicast filter and update promiscious mode bit */
+ udav_setpromisc(ue);
+
+ /* enable RX */
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_RXEN);
+
+ /* clear POWER_DOWN state of internal PHY */
+ UDAV_SETBIT(sc, UDAV_GPCR, UDAV_GPCR_GEP_CNTL0);
+ UDAV_CLRBIT(sc, UDAV_GPR, UDAV_GPR_GEPIO0);
+
+ usb2_transfer_set_stall(sc->sc_xfer[UDAV_BULK_DT_WR]);
+
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ udav_start(ue);
+}
+
+static void
+udav_reset(struct udav_softc *sc)
+{
+ int i;
+
+ /* Select PHY */
+#if 1
+ /*
+ * XXX: force select internal phy.
+ * external phy routines are not tested.
+ */
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+#else
+ if (sc->sc_flags & UDAV_EXT_PHY)
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+ else
+ UDAV_CLRBIT(sc, UDAV_NCR, UDAV_NCR_EXT_PHY);
+#endif
+
+ UDAV_SETBIT(sc, UDAV_NCR, UDAV_NCR_RST);
+
+ for (i = 0; i < UDAV_TX_TIMEOUT; i++) {
+ if (!(udav_csr_read1(sc, UDAV_NCR) & UDAV_NCR_RST))
+ break;
+ if (usb2_ether_pause(&sc->sc_ue, hz / 100))
+ break;
+ }
+
+ usb2_ether_pause(&sc->sc_ue, hz / 100);
+}
+
+#define UDAV_BITS 6
+static void
+udav_setmulti(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct ifmultiaddr *ifma;
+ uint8_t hashtbl[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ int h = 0;
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
+ UDAV_SETBIT(sc, UDAV_RCR, UDAV_RCR_ALL|UDAV_RCR_PRMSC);
+ return;
+ }
+
+ /* first, zot all the existing hash bits */
+ memset(hashtbl, 0x00, sizeof(hashtbl));
+ hashtbl[7] |= 0x80; /* broadcast address */
+ udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
+
+ /* now program new ones */
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
+ {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr), ETHER_ADDR_LEN) >> 26;
+ hashtbl[h / 8] |= 1 << (h % 8);
+ }
+ IF_ADDR_UNLOCK(ifp);
+
+ /* disable all multicast */
+ UDAV_CLRBIT(sc, UDAV_RCR, UDAV_RCR_ALL);
+
+ /* write hash value to the register */
+ udav_csr_write(sc, UDAV_MAR, hashtbl, sizeof(hashtbl));
+}
+
+static void
+udav_setpromisc(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ uint8_t rxmode;
+
+ rxmode = udav_csr_read1(sc, UDAV_RCR);
+ rxmode &= ~(UDAV_RCR_ALL | UDAV_RCR_PRMSC);
+
+ if (ifp->if_flags & IFF_PROMISC)
+ rxmode |= UDAV_RCR_ALL | UDAV_RCR_PRMSC;
+ else if (ifp->if_flags & IFF_ALLMULTI)
+ rxmode |= UDAV_RCR_ALL;
+
+ /* write new mode bits */
+ udav_csr_write1(sc, UDAV_RCR, rxmode);
+}
+
+static void
+udav_start(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+
+ /*
+ * start the USB transfers, if not already started:
+ */
+ usb2_transfer_start(sc->sc_xfer[UDAV_INTR_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_RD]);
+ usb2_transfer_start(sc->sc_xfer[UDAV_BULK_DT_WR]);
+}
+
+static void
+udav_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct udav_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+ struct mbuf *m;
+ int extra_len;
+ int temp_len;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete\n");
+ ifp->if_opackets++;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ if ((sc->sc_flags & UDAV_FLAG_LINK) == 0) {
+ /*
+ * don't send anything if there is no link !
+ */
+ return;
+ }
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+
+ if (m == NULL)
+ return;
+ if (m->m_pkthdr.len > MCLBYTES)
+ m->m_pkthdr.len = MCLBYTES;
+ if (m->m_pkthdr.len < UDAV_MIN_FRAME_LEN) {
+ extra_len = UDAV_MIN_FRAME_LEN - m->m_pkthdr.len;
+ } else {
+ extra_len = 0;
+ }
+
+ temp_len = (m->m_pkthdr.len + extra_len);
+
+ /*
+ * the frame length is specified in the first 2 bytes of the
+ * buffer
+ */
+ buf[0] = (uint8_t)(temp_len);
+ buf[1] = (uint8_t)(temp_len >> 8);
+
+ temp_len += 2;
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 2);
+
+ usb2_m_copy_in(xfer->frbuffers, 2,
+ m, 0, m->m_pkthdr.len);
+
+ if (extra_len) {
+ usb2_bzero(xfer->frbuffers, temp_len - extra_len,
+ extra_len);
+ }
+ /*
+ * if there's a BPF listener, bounce a copy
+ * of this frame to him:
+ */
+ BPF_MTAP(ifp, m);
+
+ m_freem(m);
+
+ xfer->frlengths[0] = temp_len;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct udav_softc *sc = xfer->priv_sc;
+ struct usb2_ether *ue = &sc->sc_ue;
+ struct ifnet *ifp = usb2_ether_getifp(ue);
+ struct udav_rxpkt stat;
+ int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < sizeof(stat) + ETHER_CRC_LEN) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &stat, sizeof(stat));
+ xfer->actlen -= sizeof(stat);
+ len = min(xfer->actlen, le16toh(stat.pktlen));
+ len -= ETHER_CRC_LEN;
+
+ if (stat.rxstat & UDAV_RSR_LCS) {
+ ifp->if_collisions++;
+ goto tr_setup;
+ }
+ if (stat.rxstat & UDAV_RSR_ERR) {
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_ether_rxbuf(ue, xfer->frbuffers, sizeof(stat), len);
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ usb2_ether_rxflush(ue);
+ return;
+
+ default: /* Error */
+ DPRINTF("bulk read error, %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_intr_callback(struct usb2_xfer *xfer)
+{
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+udav_stop(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct ifnet *ifp = usb2_ether_getifp(&sc->sc_ue);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ sc->sc_flags &= ~UDAV_FLAG_LINK;
+
+ /*
+ * stop all the transfers, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_WR]);
+ usb2_transfer_stop(sc->sc_xfer[UDAV_BULK_DT_RD]);
+ usb2_transfer_stop(sc->sc_xfer[UDAV_INTR_DT_RD]);
+
+ udav_reset(sc);
+}
+
+static int
+udav_ifmedia_upd(struct ifnet *ifp)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ sc->sc_flags &= ~UDAV_FLAG_LINK;
+ if (mii->mii_instance) {
+ struct mii_softc *miisc;
+
+ LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+ mii_phy_reset(miisc);
+ }
+ mii_mediachg(mii);
+ return (0);
+}
+
+static void
+udav_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct udav_softc *sc = ifp->if_softc;
+ struct mii_data *mii = GET_MII(sc);
+
+ UDAV_LOCK(sc);
+ mii_pollstat(mii);
+ UDAV_UNLOCK(sc);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
+}
+
+static void
+udav_tick(struct usb2_ether *ue)
+{
+ struct udav_softc *sc = ue->ue_sc;
+ struct mii_data *mii = GET_MII(sc);
+
+ UDAV_LOCK_ASSERT(sc, MA_OWNED);
+
+ mii_tick(mii);
+ if ((sc->sc_flags & UDAV_FLAG_LINK) == 0
+ && mii->mii_media_status & IFM_ACTIVE &&
+ IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+ sc->sc_flags |= UDAV_FLAG_LINK;
+ udav_start(ue);
+ }
+}
+
+static int
+udav_miibus_readreg(device_t dev, int phy, int reg)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+ uint16_t data16;
+ uint8_t val[2];
+ int locked;
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ UDAV_LOCK(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* select PHY operation and start read command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRR);
+
+ /* XXX: should we wait? */
+
+ /* end read command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRR);
+
+ /* retrieve the result from data registers */
+ udav_csr_read(sc, UDAV_EPDRL, val, 2);
+
+ data16 = (val[0] | (val[1] << 8));
+
+ DPRINTFN(11, "phy=%d reg=0x%04x => 0x%04x\n",
+ phy, reg, data16);
+
+ if (!locked)
+ UDAV_UNLOCK(sc);
+ return (data16);
+}
+
+static int
+udav_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+ uint8_t val[2];
+ int locked;
+
+ /* XXX: one PHY only for the internal PHY */
+ if (phy != 0)
+ return (0);
+
+ locked = mtx_owned(&sc->sc_mtx);
+ if (!locked)
+ UDAV_LOCK(sc);
+
+ /* select internal PHY and set PHY register address */
+ udav_csr_write1(sc, UDAV_EPAR,
+ UDAV_EPAR_PHY_ADR0 | (reg & UDAV_EPAR_EROA_MASK));
+
+ /* put the value to the data registers */
+ val[0] = (data & 0xff);
+ val[1] = (data >> 8) & 0xff;
+ udav_csr_write(sc, UDAV_EPDRL, val, 2);
+
+ /* select PHY operation and start write command */
+ udav_csr_write1(sc, UDAV_EPCR, UDAV_EPCR_EPOS | UDAV_EPCR_ERPRW);
+
+ /* XXX: should we wait? */
+
+ /* end write command */
+ UDAV_CLRBIT(sc, UDAV_EPCR, UDAV_EPCR_ERPRW);
+
+ if (!locked)
+ UDAV_UNLOCK(sc);
+ return (0);
+}
+
+static void
+udav_miibus_statchg(device_t dev)
+{
+ /* nothing to do */
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+udav_shutdown(device_t dev)
+{
+ struct udav_softc *sc = device_get_softc(dev);
+
+ usb2_ether_ifshutdown(&sc->sc_ue);
+
+ return (0);
+}
diff --git a/sys/dev/usb/net/if_udavreg.h b/sys/dev/usb/net/if_udavreg.h
new file mode 100644
index 0000000..d652f5b
--- /dev/null
+++ b/sys/dev/usb/net/if_udavreg.h
@@ -0,0 +1,166 @@
+/* $NetBSD: if_udavreg.h,v 1.2 2003/09/04 15:17:39 tsutsui Exp $ */
+/* $nabe: if_udavreg.h,v 1.2 2003/08/21 16:26:40 nabe Exp $ */
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2003
+ * Shingo WATANABE <nabe@nabechan.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * 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 UDAV_IFACE_INDEX 0
+#define UDAV_CONFIG_INDEX 0 /* config number 1 */
+
+#define UDAV_TX_TIMEOUT 1000
+#define UDAV_TIMEOUT 10000
+
+#define UDAV_TX_TIMEOUT 1000
+#define UDAV_TIMEOUT 10000
+
+/* Packet length */
+#define UDAV_MIN_FRAME_LEN 60
+
+/* Request */
+#define UDAV_REQ_REG_READ 0x00 /* Read from register(s) */
+#define UDAV_REQ_REG_WRITE 0x01 /* Write to register(s) */
+#define UDAV_REQ_REG_WRITE1 0x03 /* Write to a register */
+
+#define UDAV_REQ_MEM_READ 0x02 /* Read from memory */
+#define UDAV_REQ_MEM_WRITE 0x05 /* Write to memory */
+#define UDAV_REQ_MEM_WRITE1 0x07 /* Write a byte to memory */
+
+/* Registers */
+#define UDAV_NCR 0x00 /* Network Control Register */
+#define UDAV_NCR_EXT_PHY (1<<7) /* Select External PHY */
+#define UDAV_NCR_WAKEEN (1<<6) /* Wakeup Event Enable */
+#define UDAV_NCR_FCOL (1<<4) /* Force Collision Mode */
+#define UDAV_NCR_FDX (1<<3) /* Full-Duplex Mode (RO on Int. PHY) */
+#define UDAV_NCR_LBK1 (1<<2) /* Lookback Mode */
+#define UDAV_NCR_LBK0 (1<<1) /* Lookback Mode */
+#define UDAV_NCR_RST (1<<0) /* Software reset */
+
+#define UDAV_RCR 0x05 /* RX Control Register */
+#define UDAV_RCR_WTDIS (1<<6) /* Watchdog Timer Disable */
+#define UDAV_RCR_DIS_LONG (1<<5) /* Discard Long Packet(over 1522Byte) */
+#define UDAV_RCR_DIS_CRC (1<<4) /* Discard CRC Error Packet */
+#define UDAV_RCR_ALL (1<<3) /* Pass All Multicast */
+#define UDAV_RCR_RUNT (1<<2) /* Pass Runt Packet */
+#define UDAV_RCR_PRMSC (1<<1) /* Promiscuous Mode */
+#define UDAV_RCR_RXEN (1<<0) /* RX Enable */
+
+#define UDAV_RSR 0x06 /* RX Status Register */
+#define UDAV_RSR_RF (1<<7) /* Runt Frame */
+#define UDAV_RSR_MF (1<<6) /* Multicast Frame */
+#define UDAV_RSR_LCS (1<<5) /* Late Collision Seen */
+#define UDAV_RSR_RWTO (1<<4) /* Receive Watchdog Time-Out */
+#define UDAV_RSR_PLE (1<<3) /* Physical Layer Error */
+#define UDAV_RSR_AE (1<<2) /* Alignment Error */
+#define UDAV_RSR_CE (1<<1) /* CRC Error */
+#define UDAV_RSR_FOE (1<<0) /* FIFO Overflow Error */
+#define UDAV_RSR_ERR (UDAV_RSR_RF | UDAV_RSR_LCS | \
+ UDAV_RSR_RWTO | UDAV_RSR_PLE | \
+ UDAV_RSR_AE | UDAV_RSR_CE | UDAV_RSR_FOE)
+
+#define UDAV_EPCR 0x0b /* EEPROM & PHY Control Register */
+#define UDAV_EPCR_REEP (1<<5) /* Reload EEPROM */
+#define UDAV_EPCR_WEP (1<<4) /* Write EEPROM enable */
+#define UDAV_EPCR_EPOS (1<<3) /* EEPROM or PHY Operation Select */
+#define UDAV_EPCR_ERPRR (1<<2) /* EEPROM/PHY Register Read Command */
+#define UDAV_EPCR_ERPRW (1<<1) /* EEPROM/PHY Register Write Command */
+#define UDAV_EPCR_ERRE (1<<0) /* EEPROM/PHY Access Status */
+
+#define UDAV_EPAR 0x0c /* EEPROM & PHY Control Register */
+#define UDAV_EPAR_PHY_ADR1 (1<<7) /* PHY Address bit 1 */
+#define UDAV_EPAR_PHY_ADR0 (1<<6) /* PHY Address bit 0 */
+#define UDAV_EPAR_EROA (1<<0) /* EEPROM Word/PHY Register Address */
+#define UDAV_EPAR_EROA_MASK (0x1f) /* [5:0] */
+
+#define UDAV_EPDRL 0x0d /* EEPROM & PHY Data Register */
+#define UDAV_EPDRH 0x0e /* EEPROM & PHY Data Register */
+
+#define UDAV_PAR0 0x10 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR1 0x11 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR2 0x12 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR3 0x13 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR4 0x14 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR5 0x15 /* Ethernet Address, load from EEPROM */
+#define UDAV_PAR UDAV_PAR0
+
+#define UDAV_MAR0 0x16 /* Multicast Register */
+#define UDAV_MAR1 0x17 /* Multicast Register */
+#define UDAV_MAR2 0x18 /* Multicast Register */
+#define UDAV_MAR3 0x19 /* Multicast Register */
+#define UDAV_MAR4 0x1a /* Multicast Register */
+#define UDAV_MAR5 0x1b /* Multicast Register */
+#define UDAV_MAR6 0x1c /* Multicast Register */
+#define UDAV_MAR7 0x1d /* Multicast Register */
+#define UDAV_MAR UDAV_MAR0
+
+#define UDAV_GPCR 0x1e /* General purpose control register */
+#define UDAV_GPCR_GEP_CNTL6 (1<<6) /* General purpose control 6 */
+#define UDAV_GPCR_GEP_CNTL5 (1<<5) /* General purpose control 5 */
+#define UDAV_GPCR_GEP_CNTL4 (1<<4) /* General purpose control 4 */
+#define UDAV_GPCR_GEP_CNTL3 (1<<3) /* General purpose control 3 */
+#define UDAV_GPCR_GEP_CNTL2 (1<<2) /* General purpose control 2 */
+#define UDAV_GPCR_GEP_CNTL1 (1<<1) /* General purpose control 1 */
+#define UDAV_GPCR_GEP_CNTL0 (1<<0) /* General purpose control 0 */
+
+#define UDAV_GPR 0x1f /* General purpose register */
+#define UDAV_GPR_GEPIO6 (1<<6) /* General purpose 6 */
+#define UDAV_GPR_GEPIO5 (1<<5) /* General purpose 5 */
+#define UDAV_GPR_GEPIO4 (1<<4) /* General purpose 4 */
+#define UDAV_GPR_GEPIO3 (1<<3) /* General purpose 3 */
+#define UDAV_GPR_GEPIO2 (1<<2) /* General purpose 2 */
+#define UDAV_GPR_GEPIO1 (1<<1) /* General purpose 1 */
+#define UDAV_GPR_GEPIO0 (1<<0) /* General purpose 0 */
+
+#define GET_MII(sc) usb2_ether_getmii(&(sc)->sc_ue)
+
+struct udav_rxpkt {
+ uint8_t rxstat;
+ uint16_t pktlen;
+} __packed;
+
+enum {
+ UDAV_BULK_DT_WR,
+ UDAV_BULK_DT_RD,
+ UDAV_INTR_DT_RD,
+ UDAV_N_TRANSFER,
+};
+
+struct udav_softc {
+ struct usb2_ether sc_ue;
+ struct mtx sc_mtx;
+ struct usb2_xfer *sc_xfer[UDAV_N_TRANSFER];
+
+ int sc_flags;
+#define UDAV_FLAG_LINK 0x0001
+#define UDAV_FLAG_EXT_PHY 0x0040
+};
+
+#define UDAV_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
+#define UDAV_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define UDAV_LOCK_ASSERT(_sc, t) mtx_assert(&(_sc)->sc_mtx, t)
diff --git a/sys/dev/usb/net/usb_ethernet.c b/sys/dev/usb/net/usb_ethernet.c
new file mode 100644
index 0000000..ac4b701
--- /dev/null
+++ b/sys/dev/usb/net/usb_ethernet.c
@@ -0,0 +1,587 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2009 Andrew Thompson (thompsa@FreeBSD.org)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_endian.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/net/usb_ethernet.h>
+
+SYSCTL_NODE(_net, OID_AUTO, ue, CTLFLAG_RD, 0, "USB Ethernet parameters");
+
+#define UE_LOCK(_ue) mtx_lock((_ue)->ue_mtx)
+#define UE_UNLOCK(_ue) mtx_unlock((_ue)->ue_mtx)
+#define UE_LOCK_ASSERT(_ue, t) mtx_assert((_ue)->ue_mtx, t)
+
+MODULE_DEPEND(uether, usb, 1, 1, 1);
+MODULE_DEPEND(uether, miibus, 1, 1, 1);
+
+static struct unrhdr *ueunit;
+
+static usb2_proc_callback_t ue_attach_post_task;
+static usb2_proc_callback_t ue_promisc_task;
+static usb2_proc_callback_t ue_setmulti_task;
+static usb2_proc_callback_t ue_ifmedia_task;
+static usb2_proc_callback_t ue_tick_task;
+static usb2_proc_callback_t ue_start_task;
+static usb2_proc_callback_t ue_stop_task;
+
+static void ue_init(void *);
+static void ue_start(struct ifnet *);
+static int ue_ifmedia_upd(struct ifnet *);
+static void ue_watchdog(void *);
+
+/*
+ * Return values:
+ * 0: success
+ * Else: device has been detached
+ */
+uint8_t
+usb2_ether_pause(struct usb2_ether *ue, unsigned int _ticks)
+{
+ if (usb2_proc_is_gone(&ue->ue_tq)) {
+ /* nothing to do */
+ return (1);
+ }
+ usb2_pause_mtx(ue->ue_mtx, _ticks);
+ return (0);
+}
+
+static void
+ue_queue_command(struct usb2_ether *ue,
+ usb2_proc_callback_t *fn,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct usb2_ether_cfg_task *task;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ if (usb2_proc_is_gone(&ue->ue_tq)) {
+ return; /* nothing to do */
+ }
+ /*
+ * NOTE: The task cannot get executed before we drop the
+ * "sc_mtx" mutex. It is safe to update fields in the message
+ * structure after that the message got queued.
+ */
+ task = (struct usb2_ether_cfg_task *)
+ usb2_proc_msignal(&ue->ue_tq, t0, t1);
+
+ /* Setup callback and self pointers */
+ task->hdr.pm_callback = fn;
+ task->ue = ue;
+
+ /*
+ * Start and stop must be synchronous!
+ */
+ if ((fn == ue_start_task) || (fn == ue_stop_task))
+ usb2_proc_mwait(&ue->ue_tq, t0, t1);
+}
+
+struct ifnet *
+usb2_ether_getifp(struct usb2_ether *ue)
+{
+ return (ue->ue_ifp);
+}
+
+struct mii_data *
+usb2_ether_getmii(struct usb2_ether *ue)
+{
+ return (device_get_softc(ue->ue_miibus));
+}
+
+void *
+usb2_ether_getsc(struct usb2_ether *ue)
+{
+ return (ue->ue_sc);
+}
+
+static int
+ue_sysctl_parent(SYSCTL_HANDLER_ARGS)
+{
+ struct usb2_ether *ue = arg1;
+ const char *name;
+
+ name = device_get_nameunit(ue->ue_dev);
+ return SYSCTL_OUT(req, name, strlen(name));
+}
+
+int
+usb2_ether_ifattach(struct usb2_ether *ue)
+{
+ int error;
+
+ /* check some critical parameters */
+ if ((ue->ue_dev == NULL) ||
+ (ue->ue_udev == NULL) ||
+ (ue->ue_mtx == NULL) ||
+ (ue->ue_methods == NULL))
+ return (EINVAL);
+
+ error = usb2_proc_create(&ue->ue_tq, ue->ue_mtx,
+ device_get_nameunit(ue->ue_dev), USB_PRI_MED);
+ if (error) {
+ device_printf(ue->ue_dev, "could not setup taskqueue\n");
+ goto error;
+ }
+
+ /* fork rest of the attach code */
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_attach_post_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ UE_UNLOCK(ue);
+
+error:
+ return (error);
+}
+
+static void
+ue_attach_post_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp;
+ int error;
+ char num[14]; /* sufficient for 32 bits */
+
+ /* first call driver's post attach routine */
+ ue->ue_methods->ue_attach_post(ue);
+
+ UE_UNLOCK(ue);
+
+ ue->ue_unit = alloc_unr(ueunit);
+ usb2_callout_init_mtx(&ue->ue_watchdog, ue->ue_mtx, 0);
+ sysctl_ctx_init(&ue->ue_sysctl_ctx);
+
+ ifp = if_alloc(IFT_ETHER);
+ if (ifp == NULL) {
+ device_printf(ue->ue_dev, "could not allocate ifnet\n");
+ goto error;
+ }
+
+ ifp->if_softc = ue;
+ if_initname(ifp, "ue", ue->ue_unit);
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ if (ue->ue_methods->ue_ioctl != NULL)
+ ifp->if_ioctl = ue->ue_methods->ue_ioctl;
+ else
+ ifp->if_ioctl = usb2_ether_ioctl;
+ ifp->if_start = ue_start;
+ ifp->if_init = ue_init;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+ ue->ue_ifp = ifp;
+
+ if (ue->ue_methods->ue_mii_upd != NULL &&
+ ue->ue_methods->ue_mii_sts != NULL) {
+ mtx_lock(&Giant); /* device_xxx() depends on this */
+ error = mii_phy_probe(ue->ue_dev, &ue->ue_miibus,
+ ue_ifmedia_upd, ue->ue_methods->ue_mii_sts);
+ mtx_unlock(&Giant);
+ if (error) {
+ device_printf(ue->ue_dev, "MII without any PHY\n");
+ goto error;
+ }
+ }
+
+ if_printf(ifp, "<USB Ethernet> on %s\n", device_get_nameunit(ue->ue_dev));
+ ether_ifattach(ifp, ue->ue_eaddr);
+
+ snprintf(num, sizeof(num), "%u", ue->ue_unit);
+ ue->ue_sysctl_oid = SYSCTL_ADD_NODE(&ue->ue_sysctl_ctx,
+ &SYSCTL_NODE_CHILDREN(_net, ue),
+ OID_AUTO, num, CTLFLAG_RD, NULL, "");
+ SYSCTL_ADD_PROC(&ue->ue_sysctl_ctx,
+ SYSCTL_CHILDREN(ue->ue_sysctl_oid), OID_AUTO,
+ "%parent", CTLFLAG_RD, ue, 0,
+ ue_sysctl_parent, "A", "parent device");
+
+ UE_LOCK(ue);
+ return;
+
+error:
+ free_unr(ueunit, ue->ue_unit);
+ if (ue->ue_ifp != NULL) {
+ if_free(ue->ue_ifp);
+ ue->ue_ifp = NULL;
+ }
+ UE_LOCK(ue);
+ return;
+}
+
+void
+usb2_ether_ifdetach(struct usb2_ether *ue)
+{
+ struct ifnet *ifp;
+
+ /* wait for any post attach or other command to complete */
+ usb2_proc_drain(&ue->ue_tq);
+
+ /* read "ifnet" pointer after taskqueue drain */
+ ifp = ue->ue_ifp;
+
+ if (ifp != NULL) {
+
+ /* we are not running any more */
+ UE_LOCK(ue);
+ ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
+ UE_UNLOCK(ue);
+
+ /* drain any callouts */
+ usb2_callout_drain(&ue->ue_watchdog);
+
+ /* detach miibus */
+ if (ue->ue_miibus != NULL) {
+ mtx_lock(&Giant); /* device_xxx() depends on this */
+ device_delete_child(ue->ue_dev, ue->ue_miibus);
+ mtx_unlock(&Giant);
+ }
+
+ /* detach ethernet */
+ ether_ifdetach(ifp);
+
+ /* free interface instance */
+ if_free(ifp);
+
+ /* free sysctl */
+ sysctl_ctx_free(&ue->ue_sysctl_ctx);
+
+ /* free unit */
+ free_unr(ueunit, ue->ue_unit);
+ }
+
+ /* free taskqueue, if any */
+ usb2_proc_free(&ue->ue_tq);
+}
+
+void
+usb2_ether_ifshutdown(struct usb2_ether *ue)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK(ue);
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ue_queue_command(ue, ue_stop_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ UE_UNLOCK(ue);
+}
+
+uint8_t
+usb2_ether_is_gone(struct usb2_ether *ue)
+{
+ return (usb2_proc_is_gone(&ue->ue_tq));
+}
+
+static void
+ue_init(void *arg)
+{
+ struct usb2_ether *ue = arg;
+
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_start_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ UE_UNLOCK(ue);
+}
+
+static void
+ue_start_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ ue->ue_methods->ue_init(ue);
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ if (ue->ue_methods->ue_tick != NULL)
+ usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
+}
+
+static void
+ue_stop_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ usb2_callout_stop(&ue->ue_watchdog);
+
+ ue->ue_methods->ue_stop(ue);
+}
+
+static void
+ue_start(struct ifnet *ifp)
+{
+ struct usb2_ether *ue = ifp->if_softc;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ UE_LOCK(ue);
+ ue->ue_methods->ue_start(ue);
+ UE_UNLOCK(ue);
+}
+
+static void
+ue_promisc_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+
+ ue->ue_methods->ue_setpromisc(ue);
+}
+
+static void
+ue_setmulti_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+
+ ue->ue_methods->ue_setmulti(ue);
+}
+
+static int
+ue_ifmedia_upd(struct ifnet *ifp)
+{
+ struct usb2_ether *ue = ifp->if_softc;
+
+ /* Defer to process context */
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_ifmedia_task,
+ &ue->ue_media_task[0].hdr,
+ &ue->ue_media_task[1].hdr);
+ UE_UNLOCK(ue);
+
+ return (0);
+}
+
+static void
+ue_ifmedia_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ ue->ue_methods->ue_mii_upd(ifp);
+}
+
+static void
+ue_watchdog(void *arg)
+{
+ struct usb2_ether *ue = arg;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ ue_queue_command(ue, ue_tick_task,
+ &ue->ue_tick_task[0].hdr,
+ &ue->ue_tick_task[1].hdr);
+
+ usb2_callout_reset(&ue->ue_watchdog, hz, ue_watchdog, ue);
+}
+
+static void
+ue_tick_task(struct usb2_proc_msg *_task)
+{
+ struct usb2_ether_cfg_task *task =
+ (struct usb2_ether_cfg_task *)_task;
+ struct usb2_ether *ue = task->ue;
+ struct ifnet *ifp = ue->ue_ifp;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ ue->ue_methods->ue_tick(ue);
+}
+
+int
+usb2_ether_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct usb2_ether *ue = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct mii_data *mii;
+ int error = 0;
+
+ switch (command) {
+ case SIOCSIFFLAGS:
+ UE_LOCK(ue);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ue_queue_command(ue, ue_promisc_task,
+ &ue->ue_promisc_task[0].hdr,
+ &ue->ue_promisc_task[1].hdr);
+ else
+ ue_queue_command(ue, ue_start_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ } else {
+ ue_queue_command(ue, ue_stop_task,
+ &ue->ue_sync_task[0].hdr,
+ &ue->ue_sync_task[1].hdr);
+ }
+ UE_UNLOCK(ue);
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ UE_LOCK(ue);
+ ue_queue_command(ue, ue_setmulti_task,
+ &ue->ue_multi_task[0].hdr,
+ &ue->ue_multi_task[1].hdr);
+ UE_UNLOCK(ue);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ if (ue->ue_miibus != NULL) {
+ mii = device_get_softc(ue->ue_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
+ } else
+ error = ether_ioctl(ifp, command, data);
+ break;
+ default:
+ error = ether_ioctl(ifp, command, data);
+ break;
+ }
+ return (error);
+}
+
+static int
+usb2_ether_modevent(module_t mod, int type, void *data)
+{
+
+ switch (type) {
+ case MOD_LOAD:
+ ueunit = new_unrhdr(0, INT_MAX, NULL);
+ break;
+ case MOD_UNLOAD:
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+static moduledata_t usb2_ether_mod = {
+ "uether",
+ usb2_ether_modevent,
+ 0
+};
+
+int
+usb2_ether_rxmbuf(struct usb2_ether *ue, struct mbuf *m,
+ unsigned int len)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ /* finalize mbuf */
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /* enqueue for later when the lock can be released */
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+ return (0);
+}
+
+int
+usb2_ether_rxbuf(struct usb2_ether *ue, struct usb2_page_cache *pc,
+ unsigned int offset, unsigned int len)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+ struct mbuf *m;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ if (len < ETHER_HDR_LEN || len > MCLBYTES)
+ return (1);
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ ifp->if_ierrors++;
+ return (ENOMEM);
+ }
+
+ m_adj(m, ETHER_ALIGN);
+ usb2_copy_out(pc, offset, mtod(m, uint8_t *), len);
+
+ /* finalize mbuf */
+ ifp->if_ipackets++;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = len;
+
+ /* enqueue for later when the lock can be released */
+ _IF_ENQUEUE(&ue->ue_rxq, m);
+ return (0);
+}
+
+void
+usb2_ether_rxflush(struct usb2_ether *ue)
+{
+ struct ifnet *ifp = ue->ue_ifp;
+ struct mbuf *m;
+
+ UE_LOCK_ASSERT(ue, MA_OWNED);
+
+ for (;;) {
+ _IF_DEQUEUE(&ue->ue_rxq, m);
+ if (m == NULL)
+ break;
+
+ /*
+ * The USB xfer has been resubmitted so its safe to unlock now.
+ */
+ UE_UNLOCK(ue);
+ ifp->if_input(ifp, m);
+ UE_LOCK(ue);
+ }
+}
+
+DECLARE_MODULE(uether, usb2_ether_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
+MODULE_VERSION(uether, 1);
diff --git a/sys/dev/usb/net/usb_ethernet.h b/sys/dev/usb/net/usb_ethernet.h
new file mode 100644
index 0000000..0ee36f2
--- /dev/null
+++ b/sys/dev/usb/net/usb_ethernet.h
@@ -0,0 +1,122 @@
+/* $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_ETHERNET_H_
+#define _USB2_ETHERNET_H_
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/limits.h>
+
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/bpf.h>
+#include <net/ethernet.h>
+
+#include "miibus_if.h"
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+struct usb2_ether;
+struct usb2_device_request;
+
+typedef void (usb2_ether_fn_t)(struct usb2_ether *);
+
+struct usb2_ether_methods {
+ usb2_ether_fn_t *ue_attach_post;
+ usb2_ether_fn_t *ue_start;
+ usb2_ether_fn_t *ue_init;
+ usb2_ether_fn_t *ue_stop;
+ usb2_ether_fn_t *ue_setmulti;
+ usb2_ether_fn_t *ue_setpromisc;
+ usb2_ether_fn_t *ue_tick;
+ int (*ue_mii_upd)(struct ifnet *);
+ void (*ue_mii_sts)(struct ifnet *,
+ struct ifmediareq *);
+ int (*ue_ioctl)(struct ifnet *, u_long, caddr_t);
+
+};
+
+struct usb2_ether_cfg_task {
+ struct usb2_proc_msg hdr;
+ struct usb2_ether *ue;
+};
+
+struct usb2_ether {
+ /* NOTE: the "ue_ifp" pointer must be first --hps */
+ struct ifnet *ue_ifp;
+ struct mtx *ue_mtx;
+ const struct usb2_ether_methods *ue_methods;
+ struct sysctl_oid *ue_sysctl_oid;
+ void *ue_sc;
+ struct usb2_device *ue_udev; /* used by usb2_ether_do_request() */
+ device_t ue_dev;
+ device_t ue_miibus;
+
+ struct usb2_process ue_tq;
+ struct sysctl_ctx_list ue_sysctl_ctx;
+ struct ifqueue ue_rxq;
+ struct usb2_callout ue_watchdog;
+ struct usb2_ether_cfg_task ue_sync_task[2];
+ struct usb2_ether_cfg_task ue_media_task[2];
+ struct usb2_ether_cfg_task ue_multi_task[2];
+ struct usb2_ether_cfg_task ue_promisc_task[2];
+ struct usb2_ether_cfg_task ue_tick_task[2];
+
+ int ue_unit;
+
+ /* ethernet address from eeprom */
+ uint8_t ue_eaddr[ETHER_ADDR_LEN];
+};
+
+#define usb2_ether_do_request(ue,req,data,timo) \
+ usb2_do_request_proc((ue)->ue_udev,&(ue)->ue_tq,req,data,0,NULL,timo)
+
+uint8_t usb2_ether_pause(struct usb2_ether *, unsigned int);
+struct ifnet *usb2_ether_getifp(struct usb2_ether *);
+struct mii_data *usb2_ether_getmii(struct usb2_ether *);
+void *usb2_ether_getsc(struct usb2_ether *);
+int usb2_ether_ifattach(struct usb2_ether *);
+void usb2_ether_ifdetach(struct usb2_ether *);
+int usb2_ether_ioctl(struct ifnet *, u_long, caddr_t);
+int usb2_ether_rxmbuf(struct usb2_ether *, struct mbuf *,
+ unsigned int);
+int usb2_ether_rxbuf(struct usb2_ether *,
+ struct usb2_page_cache *,
+ unsigned int, unsigned int);
+void usb2_ether_rxflush(struct usb2_ether *);
+void usb2_ether_ifshutdown(struct usb2_ether *);
+uint8_t usb2_ether_is_gone(struct usb2_ether *);
+#endif /* _USB2_ETHERNET_H_ */
diff --git a/sys/dev/usb/quirk/usb_quirk.c b/sys/dev/usb/quirk/usb_quirk.c
new file mode 100644
index 0000000..fe49ec7
--- /dev/null
+++ b/sys/dev/usb/quirk/usb_quirk.c
@@ -0,0 +1,397 @@
+/* $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/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usb_mfunc.h>
+#include "usbdevs.h"
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_dynamic.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+MODULE_DEPEND(usb_quirk, usb, 1, 1, 1);
+MODULE_VERSION(usb_quirk, 1);
+
+/*
+ * The following macro adds one or more quirks for a USB device:
+ */
+#define USB_QUIRK_ENTRY(v,p,l,h,...) \
+ .vid = (v), .pid = (p), .lo_rev = (l), .hi_rev = (h), .quirks = { __VA_ARGS__ }
+
+#define USB_DEV_QUIRKS_MAX 128
+#define USB_SUB_QUIRKS_MAX 8
+
+struct usb2_quirk_entry {
+ uint16_t vid;
+ uint16_t pid;
+ uint16_t lo_rev;
+ uint16_t hi_rev;
+ uint16_t quirks[USB_SUB_QUIRKS_MAX];
+};
+
+static struct mtx usb2_quirk_mtx;
+
+static struct usb2_quirk_entry usb2_quirks[USB_DEV_QUIRKS_MAX] = {
+ {USB_QUIRK_ENTRY(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_LCM, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_INSIDEOUT, USB_PRODUCT_INSIDEOUT_EDGEPORT4, 0x094, 0x094, UQ_SWAP_UNICODE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, 0x0a2, UQ_BAD_ADC, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_DALLAS, USB_PRODUCT_DALLAS_J6502, 0x0a2, 0x0a2, UQ_AU_NO_XU, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ADA70, 0x103, 0x103, UQ_BAD_ADC, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_ALTEC, USB_PRODUCT_ALTEC_ASC495, 0x000, 0x000, UQ_BAD_AUDIO, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0x110, 0x110, UQ_SPUR_BUT_UP, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_ALCOR2, USB_PRODUCT_ALCOR2_KBD_HUB, 0x001, 0x001, UQ_SPUR_BUT_UP, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MCT, USB_PRODUCT_MCT_HUB0100, 0x102, 0x102, UQ_BUS_POWERED, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0x102, 0x102, UQ_BUS_POWERED, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_TI, USB_PRODUCT_TI_UTUSB41, 0x110, 0x110, UQ_POWER_CLAIM, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_TELEX, USB_PRODUCT_TELEX_MIC1, 0x009, 0x009, UQ_AU_NO_FRAC, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_SILICONPORTALS, USB_PRODUCT_SILICONPORTALS_YAPPHONE, 0x100, 0x100, UQ_AU_INP_ASYNC, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_LOGITECH, USB_PRODUCT_LOGITECH_UN53B, 0x0000, 0xFFFF, UQ_NO_STRINGS, UQ_NONE)},
+
+ /*
+ * XXX The following quirks should have a more specific revision
+ * number:
+ */
+ {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_895C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_880C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_815C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_810C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_830C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_HP, USB_PRODUCT_HP_1220C, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_XEROX, USB_PRODUCT_XEROX_WCM15, 0x0000, 0xFFFF, UQ_BROKEN_BIDIR, UQ_NONE)},
+ /* Devices which should be ignored by uhid */
+ {USB_QUIRK_ENTRY(USB_VENDOR_APC, USB_PRODUCT_APC_UPS, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F6C550AVR, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_1500CAVRLCD, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD2X20, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_ITUNERNET, USB_PRODUCT_ITUNERNET_USBLCD4X20, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE_3G, 0x0000, 0xFFFF, UQ_HID_IGNORE, UQ_NONE)},
+ /* Devices which should be ignored by both ukbd and uhid */
+ {USB_QUIRK_ENTRY(USB_VENDOR_CYPRESS, USB_PRODUCT_CYPRESS_WISPY1A, 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY1B, 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_TENX, USB_PRODUCT_TENX_UAUDIO0, 0x0101, 0x0101, UQ_AUDIO_SWAP_LR, UQ_NONE)},
+ /* MS keyboards do weird things */
+ {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, UQ_MS_LEADING_BYTE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLNOTEBOOK2, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, UQ_MS_LEADING_BYTE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE, 0x0000, 0xFFFF, UQ_MS_LEADING_BYTE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_COMFORT3000, 0x0000, 0xFFFF, UQ_MS_BAD_CLASS, UQ_MS_LEADING_BYTE, UQ_NONE)},
+ {USB_QUIRK_ENTRY(USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY24X, 0x0000, 0xFFFF, UQ_KBD_IGNORE, UQ_HID_IGNORE, UQ_NONE)},
+};
+
+static const char *usb_quirk_str[USB_QUIRK_MAX] = {
+ [UQ_NONE] = "UQ_NONE",
+ [UQ_AUDIO_SWAP_LR] = "UQ_AUDIO_SWAP_LR",
+ [UQ_AU_INP_ASYNC] = "UQ_AU_INP_ASYNC",
+ [UQ_AU_NO_FRAC] = "UQ_AU_NO_FRAC",
+ [UQ_AU_NO_XU] = "UQ_AU_NO_XU",
+ [UQ_BAD_ADC] = "UQ_BAD_ADC",
+ [UQ_BAD_AUDIO] = "UQ_BAD_AUDIO",
+ [UQ_BROKEN_BIDIR] = "UQ_BROKEN_BIDIR",
+ [UQ_BUS_POWERED] = "UQ_BUS_POWERED",
+ [UQ_HID_IGNORE] = "UQ_HID_IGNORE",
+ [UQ_KBD_IGNORE] = "UQ_KBD_IGNORE",
+ [UQ_MS_BAD_CLASS] = "UQ_MS_BAD_CLASS",
+ [UQ_MS_LEADING_BYTE] = "UQ_MS_LEADING_BYTE",
+ [UQ_MS_REVZ] = "UQ_MS_REVZ",
+ [UQ_NO_STRINGS] = "UQ_NO_STRINGS",
+ [UQ_OPEN_CLEARSTALL] = "UQ_OPEN_CLEARSTALL",
+ [UQ_POWER_CLAIM] = "UQ_POWER_CLAIM",
+ [UQ_SPUR_BUT_UP] = "UQ_SPUR_BUT_UP",
+ [UQ_SWAP_UNICODE] = "UQ_SWAP_UNICODE",
+ [UQ_CFG_INDEX_1] = "UQ_CFG_INDEX_1",
+ [UQ_CFG_INDEX_2] = "UQ_CFG_INDEX_2",
+ [UQ_CFG_INDEX_3] = "UQ_CFG_INDEX_3",
+ [UQ_CFG_INDEX_4] = "UQ_CFG_INDEX_4",
+ [UQ_CFG_INDEX_0] = "UQ_CFG_INDEX_0",
+};
+
+/*------------------------------------------------------------------------*
+ * usb2_quirkstr
+ *
+ * This function converts an USB quirk code into a string.
+ *------------------------------------------------------------------------*/
+static const char *
+usb2_quirkstr(uint16_t quirk)
+{
+ return ((quirk < USB_QUIRK_MAX) ?
+ usb_quirk_str[quirk] : "USB_QUIRK_UNKNOWN");
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_test_quirk_by_info
+ *
+ * Returns:
+ * 0: Quirk not found
+ * Else: Quirk found
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_test_quirk_by_info(const struct usb2_lookup_info *info, uint16_t quirk)
+{
+ uint16_t x;
+ uint16_t y;
+
+ if (quirk == UQ_NONE) {
+ return (0);
+ }
+ mtx_lock(&usb2_quirk_mtx);
+
+ for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) {
+ /* see if quirk information does not match */
+ if ((usb2_quirks[x].vid != info->idVendor) ||
+ (usb2_quirks[x].pid != info->idProduct) ||
+ (usb2_quirks[x].lo_rev > info->bcdDevice) ||
+ (usb2_quirks[x].hi_rev < info->bcdDevice)) {
+ continue;
+ }
+ /* lookup quirk */
+ for (y = 0; y != USB_SUB_QUIRKS_MAX; y++) {
+ if (usb2_quirks[x].quirks[y] == quirk) {
+ mtx_unlock(&usb2_quirk_mtx);
+ DPRINTF("Found quirk '%s'.\n", usb2_quirkstr(quirk));
+ return (1);
+ }
+ }
+ /* no quirk found */
+ break;
+ }
+ mtx_unlock(&usb2_quirk_mtx);
+ return (0);
+}
+
+static struct usb2_quirk_entry *
+usb2_quirk_get_entry(uint16_t vid, uint16_t pid,
+ uint16_t lo_rev, uint16_t hi_rev, uint8_t do_alloc)
+{
+ uint16_t x;
+
+ mtx_assert(&usb2_quirk_mtx, MA_OWNED);
+
+ if ((vid | pid | lo_rev | hi_rev) == 0) {
+ /* all zero - special case */
+ return (usb2_quirks + USB_DEV_QUIRKS_MAX - 1);
+ }
+ /* search for an existing entry */
+ for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) {
+ /* see if quirk information does not match */
+ if ((usb2_quirks[x].vid != vid) ||
+ (usb2_quirks[x].pid != pid) ||
+ (usb2_quirks[x].lo_rev != lo_rev) ||
+ (usb2_quirks[x].hi_rev != hi_rev)) {
+ continue;
+ }
+ return (usb2_quirks + x);
+ }
+
+ if (do_alloc == 0) {
+ /* no match */
+ return (NULL);
+ }
+ /* search for a free entry */
+ for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) {
+ /* see if quirk information does not match */
+ if ((usb2_quirks[x].vid |
+ usb2_quirks[x].pid |
+ usb2_quirks[x].lo_rev |
+ usb2_quirks[x].hi_rev) != 0) {
+ continue;
+ }
+ usb2_quirks[x].vid = vid;
+ usb2_quirks[x].pid = pid;
+ usb2_quirks[x].lo_rev = lo_rev;
+ usb2_quirks[x].hi_rev = hi_rev;
+
+ return (usb2_quirks + x);
+ }
+
+ /* no entry found */
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_quirk_ioctl - handle quirk IOCTLs
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static int
+usb2_quirk_ioctl(unsigned long cmd, caddr_t data,
+ int fflag, struct thread *td)
+{
+ struct usb2_gen_quirk *pgq;
+ struct usb2_quirk_entry *pqe;
+ uint32_t x;
+ uint32_t y;
+ int err;
+
+ switch (cmd) {
+ case USB_DEV_QUIRK_GET:
+ pgq = (void *)data;
+ x = pgq->index % USB_SUB_QUIRKS_MAX;
+ y = pgq->index / USB_SUB_QUIRKS_MAX;
+ if (y >= USB_DEV_QUIRKS_MAX) {
+ return (EINVAL);
+ }
+ mtx_lock(&usb2_quirk_mtx);
+ /* copy out data */
+ pgq->vid = usb2_quirks[y].vid;
+ pgq->pid = usb2_quirks[y].pid;
+ pgq->bcdDeviceLow = usb2_quirks[y].lo_rev;
+ pgq->bcdDeviceHigh = usb2_quirks[y].hi_rev;
+ strlcpy(pgq->quirkname,
+ usb2_quirkstr(usb2_quirks[y].quirks[x]),
+ sizeof(pgq->quirkname));
+ mtx_unlock(&usb2_quirk_mtx);
+ return (0); /* success */
+
+ case USB_QUIRK_NAME_GET:
+ pgq = (void *)data;
+ x = pgq->index;
+ if (x >= USB_QUIRK_MAX) {
+ return (EINVAL);
+ }
+ strlcpy(pgq->quirkname,
+ usb2_quirkstr(x), sizeof(pgq->quirkname));
+ return (0); /* success */
+
+ case USB_DEV_QUIRK_ADD:
+ pgq = (void *)data;
+
+ /* check privileges */
+ err = priv_check(curthread, PRIV_DRIVER);
+ if (err) {
+ return (err);
+ }
+ /* convert quirk string into numerical */
+ for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) {
+ if (strcmp(pgq->quirkname, usb2_quirkstr(y)) == 0) {
+ break;
+ }
+ }
+ if (y == USB_DEV_QUIRKS_MAX) {
+ return (EINVAL);
+ }
+ if (y == UQ_NONE) {
+ return (EINVAL);
+ }
+ mtx_lock(&usb2_quirk_mtx);
+ pqe = usb2_quirk_get_entry(pgq->vid, pgq->pid,
+ pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 1);
+ for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) {
+ if (pqe->quirks[x] == UQ_NONE) {
+ pqe->quirks[x] = y;
+ break;
+ }
+ }
+ mtx_unlock(&usb2_quirk_mtx);
+ if (x == USB_SUB_QUIRKS_MAX) {
+ return (ENOMEM);
+ }
+ return (0); /* success */
+
+ case USB_DEV_QUIRK_REMOVE:
+ pgq = (void *)data;
+ /* check privileges */
+ err = priv_check(curthread, PRIV_DRIVER);
+ if (err) {
+ return (err);
+ }
+ /* convert quirk string into numerical */
+ for (y = 0; y != USB_DEV_QUIRKS_MAX; y++) {
+ if (strcmp(pgq->quirkname, usb2_quirkstr(y)) == 0) {
+ break;
+ }
+ }
+ if (y == USB_DEV_QUIRKS_MAX) {
+ return (EINVAL);
+ }
+ if (y == UQ_NONE) {
+ return (EINVAL);
+ }
+ mtx_lock(&usb2_quirk_mtx);
+ pqe = usb2_quirk_get_entry(pgq->vid, pgq->pid,
+ pgq->bcdDeviceLow, pgq->bcdDeviceHigh, 0);
+ for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) {
+ if (pqe->quirks[x] == y) {
+ pqe->quirks[x] = UQ_NONE;
+ break;
+ }
+ }
+ if (x == USB_SUB_QUIRKS_MAX) {
+ mtx_unlock(&usb2_quirk_mtx);
+ return (ENOMEM);
+ }
+ for (x = 0; x != USB_SUB_QUIRKS_MAX; x++) {
+ if (pqe->quirks[x] != UQ_NONE) {
+ break;
+ }
+ }
+ if (x == USB_SUB_QUIRKS_MAX) {
+ /* all quirk entries are unused - release */
+ memset(pqe, 0, sizeof(pqe));
+ }
+ mtx_unlock(&usb2_quirk_mtx);
+ return (0); /* success */
+
+ default:
+ break;
+ }
+ return (ENOIOCTL);
+}
+
+static void
+usb2_quirk_init(void *arg)
+{
+ /* initialize mutex */
+ mtx_init(&usb2_quirk_mtx, "USB quirk", NULL, MTX_DEF);
+
+ /* register our function */
+ usb2_test_quirk_p = &usb2_test_quirk_by_info;
+ usb2_quirk_ioctl_p = &usb2_quirk_ioctl;
+}
+
+static void
+usb2_quirk_uninit(void *arg)
+{
+ usb2_quirk_unload(arg);
+
+ /* destroy mutex */
+ mtx_destroy(&usb2_quirk_mtx);
+}
+
+SYSINIT(usb2_quirk_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb2_quirk_init, NULL);
+SYSUNINIT(usb2_quirk_uninit, SI_SUB_LOCK, SI_ORDER_ANY, usb2_quirk_uninit, NULL);
diff --git a/sys/dev/usb/quirk/usb_quirk.h b/sys/dev/usb/quirk/usb_quirk.h
new file mode 100644
index 0000000..c9223e8
--- /dev/null
+++ b/sys/dev/usb/quirk/usb_quirk.h
@@ -0,0 +1,59 @@
+/* $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_QUIRK_H_
+#define _USB2_QUIRK_H_
+
+/* NOTE: UQ_NONE is not a valid quirk */
+enum { /* keep in sync with usb_quirk_str table */
+ UQ_NONE,
+ UQ_AUDIO_SWAP_LR, /* left and right sound channels are swapped */
+ UQ_AU_INP_ASYNC, /* input is async despite claim of adaptive */
+ UQ_AU_NO_FRAC, /* don't adjust for fractional samples */
+ UQ_AU_NO_XU, /* audio device has broken extension unit */
+ UQ_BAD_ADC, /* bad audio spec version number */
+ UQ_BAD_AUDIO, /* device claims audio class, but isn't */
+ UQ_BROKEN_BIDIR, /* printer has broken bidir mode */
+ UQ_BUS_POWERED, /* device is bus powered, despite claim */
+ UQ_HID_IGNORE, /* device should be ignored by hid class */
+ UQ_KBD_IGNORE, /* device should be ignored by kbd class */
+ UQ_MS_BAD_CLASS, /* doesn't identify properly */
+ UQ_MS_LEADING_BYTE, /* mouse sends an unknown leading byte */
+ UQ_MS_REVZ, /* mouse has Z-axis reversed */
+ UQ_NO_STRINGS, /* string descriptors are broken */
+ UQ_OPEN_CLEARSTALL, /* device needs clear endpoint stall */
+ UQ_POWER_CLAIM, /* hub lies about power status */
+ UQ_SPUR_BUT_UP, /* spurious mouse button up events */
+ UQ_SWAP_UNICODE, /* has some Unicode strings swapped */
+ UQ_CFG_INDEX_1, /* select configuration index 1 by default */
+ UQ_CFG_INDEX_2, /* select configuration index 2 by default */
+ UQ_CFG_INDEX_3, /* select configuration index 3 by default */
+ UQ_CFG_INDEX_4, /* select configuration index 4 by default */
+ UQ_CFG_INDEX_0, /* select configuration index 0 by default */
+ USB_QUIRK_MAX
+};
+
+#endif /* _USB2_QUIRK_H_ */
diff --git a/sys/dev/usb/serial/u3g.c b/sys/dev/usb/serial/u3g.c
new file mode 100644
index 0000000..ce963d5
--- /dev/null
+++ b/sys/dev/usb/serial/u3g.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2008 AnyWi Technologies
+ * Author: Andrea Guzzo <aguzzo@anywi.com>
+ * * based on uark.c 1.1 2006/08/14 08:30:22 jsg *
+ * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk *
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * NOTE:
+ *
+ * - The detour through the tty layer is ridiculously expensive wrt
+ * buffering due to the high speeds.
+ *
+ * We should consider adding a simple r/w device which allows
+ * attaching of PPP in a more efficient way.
+ *
+ * NOTE:
+ *
+ * - The device ID's are stored in "core/usb2_msctest.c"
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR u3g_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_msctest.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int u3g_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, u3g, CTLFLAG_RW, 0, "USB u3g");
+SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW,
+ &u3g_debug, 0, "u3g debug level");
+#endif
+
+#define U3G_MAXPORTS 4
+#define U3G_CONFIG_INDEX 0
+#define U3G_BSIZE 2048
+
+#define U3GSP_GPRS 0
+#define U3GSP_EDGE 1
+#define U3GSP_CDMA 2
+#define U3GSP_UMTS 3
+#define U3GSP_HSDPA 4
+#define U3GSP_HSUPA 5
+#define U3GSP_HSPA 6
+#define U3GSP_MAX 7
+
+#define U3GFL_NONE 0x00 /* No flags */
+#define U3GFL_HUAWEI_INIT 0x01 /* Init command required */
+#define U3GFL_SCSI_EJECT 0x02 /* SCSI eject command required */
+#define U3GFL_SIERRA_INIT 0x04 /* Init command required */
+
+struct u3g_speeds_s {
+ uint32_t ispeed;
+ uint32_t ospeed;
+};
+
+enum {
+ U3G_BULK_WR,
+ U3G_BULK_RD,
+ U3G_N_TRANSFER,
+};
+
+struct u3g_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom[U3G_MAXPORTS];
+
+ struct usb2_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* U3G status register */
+ uint8_t sc_numports;
+};
+
+static device_probe_t u3g_probe;
+static device_attach_t u3g_attach;
+static device_detach_t u3g_detach;
+
+static usb2_callback_t u3g_write_callback;
+static usb2_callback_t u3g_read_callback;
+
+static void u3g_start_read(struct usb2_com_softc *ucom);
+static void u3g_stop_read(struct usb2_com_softc *ucom);
+static void u3g_start_write(struct usb2_com_softc *ucom);
+static void u3g_stop_write(struct usb2_com_softc *ucom);
+
+static int u3g_driver_loaded(struct module *mod, int what, void *arg);
+
+static const struct usb2_config u3g_config[U3G_N_TRANSFER] = {
+
+ [U3G_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = U3G_BSIZE,/* bytes */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &u3g_write_callback,
+ },
+
+ [U3G_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = U3G_BSIZE,/* bytes */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &u3g_read_callback,
+ },
+};
+
+static const struct usb2_com_callback u3g_callback = {
+ .usb2_com_start_read = &u3g_start_read,
+ .usb2_com_stop_read = &u3g_stop_read,
+ .usb2_com_start_write = &u3g_start_write,
+ .usb2_com_stop_write = &u3g_stop_write,
+};
+
+#if 0
+static const struct u3g_speeds_s u3g_speeds[U3GSP_MAX] = {
+ [U3GSP_GPRS] = {64000, 64000},
+ [U3GSP_EDGE] = {384000, 64000},
+ [U3GSP_CDMA] = {384000, 64000},
+ [U3GSP_UMTS] = {384000, 64000},
+ [U3GSP_HSDPA] = {1200000, 384000},
+ [U3GSP_HSUPA] = {1200000, 384000},
+ [U3GSP_HSPA] = {7200000, 384000},
+};
+#endif
+
+static device_method_t u3g_methods[] = {
+ DEVMETHOD(device_probe, u3g_probe),
+ DEVMETHOD(device_attach, u3g_attach),
+ DEVMETHOD(device_detach, u3g_detach),
+ {0, 0}
+};
+
+static devclass_t u3g_devclass;
+
+static driver_t u3g_driver = {
+ .name = "u3g",
+ .methods = u3g_methods,
+ .size = sizeof(struct u3g_softc),
+};
+
+DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0);
+MODULE_DEPEND(u3g, ucom, 1, 1, 1);
+MODULE_DEPEND(u3g, usb, 1, 1, 1);
+
+/* Huawei specific defines */
+
+#define U3GINFO(flag,speed) ((flag)|((speed) * 256))
+#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256)
+
+/*
+ * NOTE: The entries marked with XXX should be checked for the correct
+ * speed indication to set the buffer sizes.
+ */
+static const struct usb2_device_id u3g_devs[] = {
+ /* OEM: Option */
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))},
+ /* OEM: Qualcomm, Inc. */
+ {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
+ /* OEM: Huawei */
+ {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))},
+ {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))},
+ /* OEM: Novatel */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))},
+ {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))},
+ /* OEM: Merlin */
+ {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ /* OEM: Sierra Wireless: */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))},
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSPA, U3GFL_NONE))}, /* XXX */
+ /* Sierra TruInstaller device ID */
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))},
+};
+
+static void
+u3g_sierra_init(struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+
+ DPRINTFN(0, "\n");
+
+ req.bmRequestType = UT_VENDOR;
+ req.bRequest = UR_SET_INTERFACE;
+ USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+ USETW(req.wIndex, UHF_PORT_CONNECTION);
+ USETW(req.wLength, 0);
+
+ if (usb2_do_request_flags(udev, NULL, &req,
+ NULL, 0, NULL, USB_MS_HZ)) {
+ /* ignore any errors */
+ }
+ return;
+}
+
+static void
+u3g_huawei_init(struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+
+ DPRINTFN(0, "\n");
+
+ req.bmRequestType = UT_WRITE_DEVICE;
+ req.bRequest = UR_SET_FEATURE;
+ USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP);
+ USETW(req.wIndex, UHF_PORT_SUSPEND);
+ USETW(req.wLength, 0);
+
+ if (usb2_do_request_flags(udev, NULL, &req,
+ NULL, 0, NULL, USB_MS_HZ)) {
+ /* ignore any errors */
+ }
+ return;
+}
+
+static int
+u3g_lookup_huawei(struct usb2_attach_arg *uaa)
+{
+ /* Calling the lookup function will also set the driver info! */
+ return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
+}
+
+/*
+ * The following function handles 3G modem devices (E220, Mobile,
+ * etc.) with auto-install flash disks for Windows/MacOSX on the first
+ * interface. After some command or some delay they change appearance
+ * to a modem.
+ */
+static usb2_error_t
+u3g_test_huawei_autoinst(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ uint32_t flags;
+
+ if (udev == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ iface = usb2_get_iface(udev, 0);
+ if (iface == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ id = iface->idesc;
+ if (id == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ if (id->bInterfaceClass != UICLASS_MASS) {
+ return (USB_ERR_INVAL);
+ }
+ if (u3g_lookup_huawei(uaa)) {
+ /* no device match */
+ return (USB_ERR_INVAL);
+ }
+ flags = USB_GET_DRIVER_INFO(uaa);
+
+ if (flags & U3GFL_HUAWEI_INIT) {
+ u3g_huawei_init(udev);
+ } else if (flags & U3GFL_SCSI_EJECT) {
+ return (usb2_test_autoinstall(udev, 0, 1));
+ } else if (flags & U3GFL_SIERRA_INIT) {
+ u3g_sierra_init(udev);
+ } else {
+ /* no quirks */
+ return (USB_ERR_INVAL);
+ }
+ return (0); /* success */
+}
+
+static int
+u3g_driver_loaded(struct module *mod, int what, void *arg)
+{
+ switch (what) {
+ case MOD_LOAD:
+ /* register our autoinstall handler */
+ usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst;
+ break;
+ case MOD_UNLOAD:
+ usb2_test_huawei_unload(NULL);
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+static int
+u3g_probe(device_t self)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bInterfaceClass != UICLASS_VENDOR) {
+ return (ENXIO);
+ }
+ return (u3g_lookup_huawei(uaa));
+}
+
+static int
+u3g_attach(device_t dev)
+{
+ struct usb2_config u3g_config_tmp[U3G_N_TRANSFER];
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct u3g_softc *sc = device_get_softc(dev);
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ uint8_t m;
+ uint8_t n;
+ uint8_t i;
+ uint8_t x;
+ int error;
+
+ DPRINTF("sc=%p\n", sc);
+
+ /* copy in USB config */
+ for (n = 0; n != U3G_N_TRANSFER; n++)
+ u3g_config_tmp[n] = u3g_config[n];
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ x = 0; /* interface index */
+ i = 0; /* endpoint index */
+ m = 0; /* number of ports */
+
+ while (m != U3G_MAXPORTS) {
+
+ /* update BULK endpoint index */
+ for (n = 0; n != U3G_N_TRANSFER; n++)
+ u3g_config_tmp[n].ep_index = i;
+
+ iface = usb2_get_iface(uaa->device, x);
+ if (iface == NULL) {
+ if (m != 0)
+ break; /* end of interfaces */
+ DPRINTF("did not find any modem endpoints\n");
+ goto detach;
+ }
+
+ id = usb2_get_interface_descriptor(iface);
+ if ((id == NULL) ||
+ (id->bInterfaceClass != UICLASS_VENDOR)) {
+ /* next interface */
+ x++;
+ i = 0;
+ continue;
+ }
+
+ /* try to allocate a set of BULK endpoints */
+ error = usb2_transfer_setup(uaa->device, &x,
+ sc->sc_xfer[m], u3g_config_tmp, U3G_N_TRANSFER,
+ &sc->sc_ucom[m], &Giant);
+ if (error) {
+ /* next interface */
+ x++;
+ i = 0;
+ continue;
+ }
+
+ /* grab other interface, if any */
+ if (x != uaa->info.bIfaceIndex)
+ usb2_set_parent_iface(uaa->device, x,
+ uaa->info.bIfaceIndex);
+
+ /* set stall by default */
+ usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[m][U3G_BULK_RD]);
+
+ m++; /* found one port */
+ i++; /* next endpoint index */
+ }
+
+ sc->sc_numports = m;
+
+ error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom,
+ sc->sc_numports, sc, &u3g_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ if (sc->sc_numports != 1) {
+ /* be verbose */
+ device_printf(dev, "Found %u ports.\n",
+ (unsigned int)sc->sc_numports);
+ }
+ return (0);
+
+detach:
+ u3g_detach(dev);
+ return (ENXIO);
+}
+
+static int
+u3g_detach(device_t dev)
+{
+ struct u3g_softc *sc = device_get_softc(dev);
+ uint8_t m;
+
+ DPRINTF("sc=%p\n", sc);
+
+ /* NOTE: It is not dangerous to detach more ports than attached! */
+ usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, U3G_MAXPORTS);
+
+ for (m = 0; m != U3G_MAXPORTS; m++)
+ usb2_transfer_unsetup(sc->sc_xfer[m], U3G_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+u3g_start_read(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
+ return;
+}
+
+static void
+u3g_stop_read(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_RD]);
+ return;
+}
+
+static void
+u3g_start_write(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
+ return;
+}
+
+static void
+u3g_stop_write(struct usb2_com_softc *ucom)
+{
+ struct u3g_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[ucom->sc_local_unit][U3G_BULK_WR]);
+ return;
+}
+
+static void
+u3g_write_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_com_softc *ucom = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_com_get_data(ucom, xfer->frbuffers, 0,
+ U3G_BSIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* do a builtin clear-stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+ return;
+}
+
+static void
+u3g_read_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_com_softc *ucom = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* do a builtin clear-stall */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+ return;
+}
diff --git a/sys/dev/usb/serial/uark.c b/sys/dev/usb/serial/uark.c
new file mode 100644
index 0000000..ce76854
--- /dev/null
+++ b/sys/dev/usb/serial/uark.c
@@ -0,0 +1,407 @@
+/* $OpenBSD: uark.c,v 1.1 2006/08/14 08:30:22 jsg Exp $ */
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * NOTE: all function names beginning like "uark_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define UARK_BUF_SIZE 1024 /* bytes */
+
+#define UARK_SET_DATA_BITS(x) ((x) - 5)
+
+#define UARK_PARITY_NONE 0x00
+#define UARK_PARITY_ODD 0x08
+#define UARK_PARITY_EVEN 0x18
+
+#define UARK_STOP_BITS_1 0x00
+#define UARK_STOP_BITS_2 0x04
+
+#define UARK_BAUD_REF 3000000
+
+#define UARK_WRITE 0x40
+#define UARK_READ 0xc0
+
+#define UARK_REQUEST 0xfe
+
+#define UARK_CONFIG_INDEX 0
+#define UARK_IFACE_INDEX 0
+
+enum {
+ UARK_BULK_DT_WR,
+ UARK_BULK_DT_RD,
+ UARK_N_TRANSFER,
+};
+
+struct uark_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UARK_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_msr;
+ uint8_t sc_lsr;
+};
+
+/* prototypes */
+
+static device_probe_t uark_probe;
+static device_attach_t uark_attach;
+static device_detach_t uark_detach;
+
+static usb2_callback_t uark_bulk_write_callback;
+static usb2_callback_t uark_bulk_read_callback;
+
+static void uark_start_read(struct usb2_com_softc *);
+static void uark_stop_read(struct usb2_com_softc *);
+static void uark_start_write(struct usb2_com_softc *);
+static void uark_stop_write(struct usb2_com_softc *);
+static int uark_pre_param(struct usb2_com_softc *, struct termios *);
+static void uark_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uark_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uark_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void uark_cfg_write(struct uark_softc *, uint16_t, uint16_t);
+
+static const struct usb2_config
+ uark_xfer_config[UARK_N_TRANSFER] = {
+
+ [UARK_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UARK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uark_bulk_write_callback,
+ },
+
+ [UARK_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UARK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uark_bulk_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uark_callback = {
+ .usb2_com_cfg_get_status = &uark_cfg_get_status,
+ .usb2_com_cfg_set_break = &uark_cfg_set_break,
+ .usb2_com_cfg_param = &uark_cfg_param,
+ .usb2_com_pre_param = &uark_pre_param,
+ .usb2_com_start_read = &uark_start_read,
+ .usb2_com_stop_read = &uark_stop_read,
+ .usb2_com_start_write = &uark_start_write,
+ .usb2_com_stop_write = &uark_stop_write,
+};
+
+static device_method_t uark_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_probe, uark_probe),
+ DEVMETHOD(device_attach, uark_attach),
+ DEVMETHOD(device_detach, uark_detach),
+ {0, 0}
+};
+
+static devclass_t uark_devclass;
+
+static driver_t uark_driver = {
+ .name = "uark",
+ .methods = uark_methods,
+ .size = sizeof(struct uark_softc),
+};
+
+DRIVER_MODULE(uark, ushub, uark_driver, uark_devclass, NULL, 0);
+MODULE_DEPEND(uark, ucom, 1, 1, 1);
+MODULE_DEPEND(uark, usb, 1, 1, 1);
+
+static const struct usb2_device_id uark_devs[] = {
+ {USB_VPI(USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116, 0)},
+};
+
+static int
+uark_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != 0) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UARK_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uark_devs, sizeof(uark_devs), uaa));
+}
+
+static int
+uark_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uark_softc *sc = device_get_softc(dev);
+ int32_t error;
+ uint8_t iface_index;
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ iface_index = UARK_IFACE_INDEX;
+ error = usb2_transfer_setup
+ (uaa->device, &iface_index, sc->sc_xfer,
+ uark_xfer_config, UARK_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating control USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uark_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ uark_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+uark_detach(device_t dev)
+{
+ struct uark_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UARK_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uark_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct uark_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UARK_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+uark_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct uark_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uark_start_read(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_RD]);
+}
+
+static void
+uark_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_RD]);
+}
+
+static void
+uark_start_write(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UARK_BULK_DT_WR]);
+}
+
+static void
+uark_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UARK_BULK_DT_WR]);
+}
+
+static int
+uark_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ if ((t->c_ospeed < 300) || (t->c_ospeed > 115200))
+ return (EINVAL);
+ return (0);
+}
+
+static void
+uark_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+ uint32_t speed = t->c_ospeed;
+ uint16_t data;
+
+ /*
+ * NOTE: When reverse computing the baud rate from the "data" all
+ * allowed baud rates are within 3% of the initial baud rate.
+ */
+ data = (UARK_BAUD_REF + (speed / 2)) / speed;
+
+ uark_cfg_write(sc, 3, 0x83);
+ uark_cfg_write(sc, 0, data & 0xFF);
+ uark_cfg_write(sc, 1, data >> 8);
+ uark_cfg_write(sc, 3, 0x03);
+
+ if (t->c_cflag & CSTOPB)
+ data = UARK_STOP_BITS_2;
+ else
+ data = UARK_STOP_BITS_1;
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD)
+ data |= UARK_PARITY_ODD;
+ else
+ data |= UARK_PARITY_EVEN;
+ } else
+ data |= UARK_PARITY_NONE;
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ data |= UARK_SET_DATA_BITS(5);
+ break;
+ case CS6:
+ data |= UARK_SET_DATA_BITS(6);
+ break;
+ case CS7:
+ data |= UARK_SET_DATA_BITS(7);
+ break;
+ default:
+ case CS8:
+ data |= UARK_SET_DATA_BITS(8);
+ break;
+ }
+ uark_cfg_write(sc, 3, 0x00);
+ uark_cfg_write(sc, 3, data);
+}
+
+static void
+uark_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uark_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uark_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff=%d\n", onoff);
+
+ uark_cfg_write(sc, 4, onoff ? 0x01 : 0x00);
+}
+
+static void
+uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ req.bmRequestType = UARK_WRITE;
+ req.bRequest = UARK_REQUEST;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 0);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
diff --git a/sys/dev/usb/serial/ubsa.c b/sys/dev/usb/serial/ubsa.c
new file mode 100644
index 0000000..718df6d
--- /dev/null
+++ b/sys/dev/usb/serial/ubsa.c
@@ -0,0 +1,634 @@
+/*-
+ * Copyright (c) 2002, Alexander Kabaev <kan.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR ubsa_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int ubsa_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ubsa, CTLFLAG_RW, 0, "USB ubsa");
+SYSCTL_INT(_hw_usb2_ubsa, OID_AUTO, debug, CTLFLAG_RW,
+ &ubsa_debug, 0, "ubsa debug level");
+#endif
+
+#define UBSA_BSIZE 1024 /* bytes */
+
+#define UBSA_CONFIG_INDEX 0
+#define UBSA_IFACE_INDEX 0
+
+#define UBSA_REG_BAUDRATE 0x00
+#define UBSA_REG_STOP_BITS 0x01
+#define UBSA_REG_DATA_BITS 0x02
+#define UBSA_REG_PARITY 0x03
+#define UBSA_REG_DTR 0x0A
+#define UBSA_REG_RTS 0x0B
+#define UBSA_REG_BREAK 0x0C
+#define UBSA_REG_FLOW_CTRL 0x10
+
+#define UBSA_PARITY_NONE 0x00
+#define UBSA_PARITY_EVEN 0x01
+#define UBSA_PARITY_ODD 0x02
+#define UBSA_PARITY_MARK 0x03
+#define UBSA_PARITY_SPACE 0x04
+
+#define UBSA_FLOW_NONE 0x0000
+#define UBSA_FLOW_OCTS 0x0001
+#define UBSA_FLOW_ODSR 0x0002
+#define UBSA_FLOW_IDSR 0x0004
+#define UBSA_FLOW_IDTR 0x0008
+#define UBSA_FLOW_IRTS 0x0010
+#define UBSA_FLOW_ORTS 0x0020
+#define UBSA_FLOW_UNKNOWN 0x0040
+#define UBSA_FLOW_OXON 0x0080
+#define UBSA_FLOW_IXON 0x0100
+
+/* line status register */
+#define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */
+#define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */
+#define UBSA_LSR_BI 0x10 /* Break detected */
+#define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */
+#define UBSA_LSR_PE 0x04 /* Parity error */
+#define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */
+#define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */
+#define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */
+
+/* modem status register */
+/* All deltas are from the last read of the MSR. */
+#define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */
+#define UBSA_MSR_RI 0x40 /* Current Ring Indicator */
+#define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */
+#define UBSA_MSR_CTS 0x10 /* Current Clear to Send */
+#define UBSA_MSR_DDCD 0x08 /* DCD has changed state */
+#define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */
+#define UBSA_MSR_DDSR 0x02 /* DSR has changed state */
+#define UBSA_MSR_DCTS 0x01 /* CTS has changed state */
+
+enum {
+ UBSA_BULK_DT_WR,
+ UBSA_BULK_DT_RD,
+ UBSA_INTR_DT_RD,
+ UBSA_N_TRANSFER,
+};
+
+struct ubsa_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UBSA_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_iface_no; /* interface number */
+ uint8_t sc_iface_index; /* interface index */
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* UBSA status register */
+};
+
+static device_probe_t ubsa_probe;
+static device_attach_t ubsa_attach;
+static device_detach_t ubsa_detach;
+
+static usb2_callback_t ubsa_write_callback;
+static usb2_callback_t ubsa_read_callback;
+static usb2_callback_t ubsa_intr_callback;
+
+static void ubsa_cfg_request(struct ubsa_softc *, uint8_t, uint16_t);
+static void ubsa_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void ubsa_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void ubsa_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int ubsa_pre_param(struct usb2_com_softc *, struct termios *);
+static void ubsa_cfg_param(struct usb2_com_softc *, struct termios *);
+static void ubsa_start_read(struct usb2_com_softc *);
+static void ubsa_stop_read(struct usb2_com_softc *);
+static void ubsa_start_write(struct usb2_com_softc *);
+static void ubsa_stop_write(struct usb2_com_softc *);
+static void ubsa_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+
+static const struct usb2_config ubsa_config[UBSA_N_TRANSFER] = {
+
+ [UBSA_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UBSA_BSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ubsa_write_callback,
+ },
+
+ [UBSA_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UBSA_BSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ubsa_read_callback,
+ },
+
+ [UBSA_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &ubsa_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback ubsa_callback = {
+ .usb2_com_cfg_get_status = &ubsa_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &ubsa_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &ubsa_cfg_set_rts,
+ .usb2_com_cfg_set_break = &ubsa_cfg_set_break,
+ .usb2_com_cfg_param = &ubsa_cfg_param,
+ .usb2_com_pre_param = &ubsa_pre_param,
+ .usb2_com_start_read = &ubsa_start_read,
+ .usb2_com_stop_read = &ubsa_stop_read,
+ .usb2_com_start_write = &ubsa_start_write,
+ .usb2_com_stop_write = &ubsa_stop_write,
+};
+
+static const struct usb2_device_id ubsa_devs[] = {
+ /* AnyData ADU-500A */
+ {USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_500A, 0)},
+ /* AnyData ADU-E100A/H */
+ {USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_E100X, 0)},
+ /* Axesstel MV100H */
+ {USB_VPI(USB_VENDOR_AXESSTEL, USB_PRODUCT_AXESSTEL_DATAMODEM, 0)},
+ /* BELKIN F5U103 */
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103, 0)},
+ /* BELKIN F5U120 */
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120, 0)},
+ /* GoHubs GO-COM232 */
+ {USB_VPI(USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM, 0)},
+ /* GoHubs GO-COM232 */
+ {USB_VPI(USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232, 0)},
+ /* Peracom */
+ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, 0)},
+};
+
+static device_method_t ubsa_methods[] = {
+ DEVMETHOD(device_probe, ubsa_probe),
+ DEVMETHOD(device_attach, ubsa_attach),
+ DEVMETHOD(device_detach, ubsa_detach),
+ {0, 0}
+};
+
+static devclass_t ubsa_devclass;
+
+static driver_t ubsa_driver = {
+ .name = "ubsa",
+ .methods = ubsa_methods,
+ .size = sizeof(struct ubsa_softc),
+};
+
+DRIVER_MODULE(ubsa, ushub, ubsa_driver, ubsa_devclass, NULL, 0);
+MODULE_DEPEND(ubsa, ucom, 1, 1, 1);
+MODULE_DEPEND(ubsa, usb, 1, 1, 1);
+
+static int
+ubsa_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UBSA_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UBSA_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(ubsa_devs, sizeof(ubsa_devs), uaa));
+}
+
+static int
+ubsa_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ubsa_softc *sc = device_get_softc(dev);
+ int error;
+
+ DPRINTF("sc=%p\n", sc);
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = UBSA_IFACE_INDEX;
+
+ error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, ubsa_config, UBSA_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ DPRINTF("could not allocate all pipes\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &ubsa_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ return (0);
+
+detach:
+ ubsa_detach(dev);
+ return (ENXIO);
+}
+
+static int
+ubsa_detach(device_t dev)
+{
+ struct ubsa_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UBSA_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+ubsa_cfg_request(struct ubsa_softc *sc, uint8_t index, uint16_t value)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = index;
+ USETW(req.wValue, value);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
+
+static void
+ubsa_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ubsa_cfg_request(sc, UBSA_REG_DTR, onoff ? 1 : 0);
+}
+
+static void
+ubsa_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ubsa_cfg_request(sc, UBSA_REG_RTS, onoff ? 1 : 0);
+}
+
+static void
+ubsa_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ubsa_cfg_request(sc, UBSA_REG_BREAK, onoff ? 1 : 0);
+}
+
+static int
+ubsa_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ DPRINTF("sc = %p\n", sc);
+
+ switch (t->c_ospeed) {
+ case B0:
+ case B300:
+ case B600:
+ case B1200:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B230400:
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static void
+ubsa_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+ uint16_t value = 0;
+
+ DPRINTF("sc = %p\n", sc);
+
+ switch (t->c_ospeed) {
+ case B0:
+ ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, 0);
+ ubsa_cfg_set_dtr(&sc->sc_ucom, 0);
+ ubsa_cfg_set_rts(&sc->sc_ucom, 0);
+ break;
+ case B300:
+ case B600:
+ case B1200:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ case B230400:
+ value = B230400 / t->c_ospeed;
+ ubsa_cfg_request(sc, UBSA_REG_BAUDRATE, value);
+ break;
+ default:
+ return;
+ }
+
+ if (t->c_cflag & PARENB)
+ value = (t->c_cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN;
+ else
+ value = UBSA_PARITY_NONE;
+
+ ubsa_cfg_request(sc, UBSA_REG_PARITY, value);
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ value = 0;
+ break;
+ case CS6:
+ value = 1;
+ break;
+ case CS7:
+ value = 2;
+ break;
+ default:
+ case CS8:
+ value = 3;
+ break;
+ }
+
+ ubsa_cfg_request(sc, UBSA_REG_DATA_BITS, value);
+
+ value = (t->c_cflag & CSTOPB) ? 1 : 0;
+
+ ubsa_cfg_request(sc, UBSA_REG_STOP_BITS, value);
+
+ value = 0;
+ if (t->c_cflag & CRTSCTS)
+ value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS;
+
+ if (t->c_iflag & (IXON | IXOFF))
+ value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON;
+
+ ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, value);
+}
+
+static void
+ubsa_start_read(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UBSA_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_RD]);
+}
+
+static void
+ubsa_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UBSA_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_RD]);
+}
+
+static void
+ubsa_start_write(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UBSA_BULK_DT_WR]);
+}
+
+static void
+ubsa_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_WR]);
+}
+
+static void
+ubsa_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct ubsa_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+ubsa_write_callback(struct usb2_xfer *xfer)
+{
+ struct ubsa_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UBSA_BSIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubsa_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubsa_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubsa_intr_callback(struct usb2_xfer *xfer)
+{
+ struct ubsa_softc *sc = xfer->priv_sc;
+ uint8_t buf[4];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen >= sizeof(buf)) {
+
+ usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
+
+ /*
+ * incidentally, Belkin adapter status bits match
+ * UART 16550 bits
+ */
+ sc->sc_lsr = buf[2];
+ sc->sc_msr = buf[3];
+
+ DPRINTF("lsr = 0x%02x, msr = 0x%02x\n",
+ sc->sc_lsr, sc->sc_msr);
+
+ usb2_com_status_change(&sc->sc_ucom);
+ } else {
+ DPRINTF("ignoring short packet, %d bytes\n",
+ xfer->actlen);
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
diff --git a/sys/dev/usb/serial/ubser.c b/sys/dev/usb/serial/ubser.c
new file mode 100644
index 0000000..1de4f82
--- /dev/null
+++ b/sys/dev/usb/serial/ubser.c
@@ -0,0 +1,518 @@
+/*-
+ * Copyright (c) 2004 Bernd Walter <ticso@freebsd.org>
+ *
+ * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $
+ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $
+ * $Author: ticso $
+ * $Rev: 1127 $
+ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * BWCT serial adapter driver
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR ubser_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define UBSER_UNIT_MAX 32
+
+/* Vendor Interface Requests */
+#define VENDOR_GET_NUMSER 0x01
+#define VENDOR_SET_BREAK 0x02
+#define VENDOR_CLEAR_BREAK 0x03
+
+#if USB_DEBUG
+static int ubser_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ubser, CTLFLAG_RW, 0, "USB ubser");
+SYSCTL_INT(_hw_usb2_ubser, OID_AUTO, debug, CTLFLAG_RW,
+ &ubser_debug, 0, "ubser debug level");
+#endif
+
+enum {
+ UBSER_BULK_DT_WR,
+ UBSER_BULK_DT_RD,
+ UBSER_N_TRANSFER,
+};
+
+struct ubser_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom[UBSER_UNIT_MAX];
+
+ struct usb2_xfer *sc_xfer[UBSER_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint16_t sc_tx_size;
+
+ uint8_t sc_numser;
+ uint8_t sc_iface_no;
+ uint8_t sc_iface_index;
+ uint8_t sc_curr_tx_unit;
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t ubser_probe;
+static device_attach_t ubser_attach;
+static device_detach_t ubser_detach;
+
+static usb2_callback_t ubser_write_callback;
+static usb2_callback_t ubser_read_callback;
+
+static int ubser_pre_param(struct usb2_com_softc *, struct termios *);
+static void ubser_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void ubser_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void ubser_start_read(struct usb2_com_softc *);
+static void ubser_stop_read(struct usb2_com_softc *);
+static void ubser_start_write(struct usb2_com_softc *);
+static void ubser_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config ubser_config[UBSER_N_TRANSFER] = {
+
+ [UBSER_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ubser_write_callback,
+ },
+
+ [UBSER_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ubser_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ubser_callback = {
+ .usb2_com_cfg_set_break = &ubser_cfg_set_break,
+ .usb2_com_cfg_get_status = &ubser_cfg_get_status,
+ .usb2_com_pre_param = &ubser_pre_param,
+ .usb2_com_start_read = &ubser_start_read,
+ .usb2_com_stop_read = &ubser_stop_read,
+ .usb2_com_start_write = &ubser_start_write,
+ .usb2_com_stop_write = &ubser_stop_write,
+};
+
+static device_method_t ubser_methods[] = {
+ DEVMETHOD(device_probe, ubser_probe),
+ DEVMETHOD(device_attach, ubser_attach),
+ DEVMETHOD(device_detach, ubser_detach),
+ {0, 0}
+};
+
+static devclass_t ubser_devclass;
+
+static driver_t ubser_driver = {
+ .name = "ubser",
+ .methods = ubser_methods,
+ .size = sizeof(struct ubser_softc),
+};
+
+DRIVER_MODULE(ubser, ushub, ubser_driver, ubser_devclass, NULL, 0);
+MODULE_DEPEND(ubser, ucom, 1, 1, 1);
+MODULE_DEPEND(ubser, usb, 1, 1, 1);
+
+static int
+ubser_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ /* check if this is a BWCT vendor specific ubser interface */
+ if ((strcmp(uaa->device->manufacturer, "BWCT") == 0) &&
+ (uaa->info.bInterfaceClass == 0xff) &&
+ (uaa->info.bInterfaceSubClass == 0x00))
+ return (0);
+
+ return (ENXIO);
+}
+
+static int
+ubser_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ubser_softc *sc = device_get_softc(dev);
+ struct usb2_device_request req;
+ uint8_t n;
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
+ device_get_nameunit(dev));
+
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+ sc->sc_udev = uaa->device;
+
+ /* get number of serials */
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = VENDOR_GET_NUMSER;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 1);
+ error = usb2_do_request_flags
+ (uaa->device, &Giant, &req, &sc->sc_numser,
+ 0, NULL, USB_DEFAULT_TIMEOUT);
+
+ if (error || (sc->sc_numser == 0)) {
+ device_printf(dev, "failed to get number "
+ "of serial ports: %s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+ if (sc->sc_numser > UBSER_UNIT_MAX)
+ sc->sc_numser = UBSER_UNIT_MAX;
+
+ device_printf(dev, "found %i serials\n", sc->sc_numser);
+
+ error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &Giant);
+ if (error) {
+ goto detach;
+ }
+ sc->sc_tx_size = sc->sc_xfer[UBSER_BULK_DT_WR]->max_data_length;
+
+ if (sc->sc_tx_size == 0) {
+ DPRINTFN(0, "invalid tx_size!\n");
+ goto detach;
+ }
+ /* initialize port numbers */
+
+ for (n = 0; n < sc->sc_numser; n++) {
+ sc->sc_ucom[n].sc_portno = n;
+ }
+
+ error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom,
+ sc->sc_numser, sc, &ubser_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ mtx_lock(&Giant);
+
+ usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]);
+
+ usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
+
+ mtx_unlock(&Giant);
+
+ return (0); /* success */
+
+detach:
+ ubser_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+ubser_detach(device_t dev)
+{
+ struct ubser_softc *sc = device_get_softc(dev);
+
+ DPRINTF("\n");
+
+ usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numser);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER);
+
+ return (0);
+}
+
+static int
+ubser_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ DPRINTF("\n");
+
+ /*
+ * The firmware on our devices can only do 8n1@9600bps
+ * without handshake.
+ * We refuse to accept other configurations.
+ */
+
+ /* ensure 9600bps */
+ switch (t->c_ospeed) {
+ case 9600:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* 2 stop bits not possible */
+ if (t->c_cflag & CSTOPB)
+ return (EINVAL);
+
+ /* XXX parity handling not possible with current firmware */
+ if (t->c_cflag & PARENB)
+ return (EINVAL);
+
+ /* we can only do 8 data bits */
+ switch (t->c_cflag & CSIZE) {
+ case CS8:
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ /* we can't do any kind of hardware handshaking */
+ if ((t->c_cflag &
+ (CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW)) != 0)
+ return (EINVAL);
+
+ /*
+ * XXX xon/xoff not supported by the firmware!
+ * This is handled within FreeBSD only and may overflow buffers
+ * because of delayed reaction due to device buffering.
+ */
+
+ return (0);
+}
+
+static __inline void
+ubser_inc_tx_unit(struct ubser_softc *sc)
+{
+ sc->sc_curr_tx_unit++;
+ if (sc->sc_curr_tx_unit >= sc->sc_numser) {
+ sc->sc_curr_tx_unit = 0;
+ }
+}
+
+static void
+ubser_write_callback(struct usb2_xfer *xfer)
+{
+ struct ubser_softc *sc = xfer->priv_sc;
+ uint8_t buf[1];
+ uint8_t first_unit = sc->sc_curr_tx_unit;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ do {
+ if (usb2_com_get_data(sc->sc_ucom + sc->sc_curr_tx_unit,
+ xfer->frbuffers, 1, sc->sc_tx_size - 1,
+ &actlen)) {
+
+ buf[0] = sc->sc_curr_tx_unit;
+
+ usb2_copy_in(xfer->frbuffers, 0, buf, 1);
+
+ xfer->frlengths[0] = actlen + 1;
+ usb2_start_hardware(xfer);
+
+ ubser_inc_tx_unit(sc); /* round robin */
+
+ break;
+ }
+ ubser_inc_tx_unit(sc);
+
+ } while (sc->sc_curr_tx_unit != first_unit);
+
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubser_read_callback(struct usb2_xfer *xfer)
+{
+ struct ubser_softc *sc = xfer->priv_sc;
+ uint8_t buf[1];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 1) {
+ DPRINTF("invalid actlen=0!\n");
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 1);
+
+ if (buf[0] >= sc->sc_numser) {
+ DPRINTF("invalid serial number!\n");
+ goto tr_setup;
+ }
+ usb2_com_put_data(sc->sc_ucom + buf[0],
+ xfer->frbuffers, 1, xfer->actlen - 1);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+ubser_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+ uint8_t x = ucom->sc_portno;
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ if (onoff) {
+
+ req.bmRequestType = UT_READ_VENDOR_INTERFACE;
+ req.bRequest = VENDOR_SET_BREAK;
+ req.wValue[0] = x;
+ req.wValue[1] = 0;
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "send break failed, error=%s\n",
+ usb2_errstr(err));
+ }
+ }
+}
+
+static void
+ubser_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ /* fake status bits */
+ *lsr = 0;
+ *msr = SER_DCD;
+}
+
+static void
+ubser_start_read(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
+}
+
+static void
+ubser_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]);
+}
+
+static void
+ubser_start_write(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]);
+}
+
+static void
+ubser_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ubser_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/serial/uchcom.c b/sys/dev/usb/serial/uchcom.c
new file mode 100644
index 0000000..c8238c4
--- /dev/null
+++ b/sys/dev/usb/serial/uchcom.c
@@ -0,0 +1,883 @@
+/* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */
+
+/*-
+ * Copyright (c) 2007, Takanori Watanabe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Takuya SHIOZAKI (tshiozak@netbsd.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * driver for WinChipHead CH341/340, the worst USB-serial chip in the world.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uchcom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uchcom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uchcom, CTLFLAG_RW, 0, "USB uchcom");
+SYSCTL_INT(_hw_usb2_uchcom, OID_AUTO, debug, CTLFLAG_RW,
+ &uchcom_debug, 0, "uchcom debug level");
+#endif
+
+#define UCHCOM_IFACE_INDEX 0
+#define UCHCOM_CONFIG_INDEX 0
+
+#define UCHCOM_REV_CH340 0x0250
+#define UCHCOM_INPUT_BUF_SIZE 8
+
+#define UCHCOM_REQ_GET_VERSION 0x5F
+#define UCHCOM_REQ_READ_REG 0x95
+#define UCHCOM_REQ_WRITE_REG 0x9A
+#define UCHCOM_REQ_RESET 0xA1
+#define UCHCOM_REQ_SET_DTRRTS 0xA4
+
+#define UCHCOM_REG_STAT1 0x06
+#define UCHCOM_REG_STAT2 0x07
+#define UCHCOM_REG_BPS_PRE 0x12
+#define UCHCOM_REG_BPS_DIV 0x13
+#define UCHCOM_REG_BPS_MOD 0x14
+#define UCHCOM_REG_BPS_PAD 0x0F
+#define UCHCOM_REG_BREAK1 0x05
+#define UCHCOM_REG_BREAK2 0x18
+#define UCHCOM_REG_LCR1 0x18
+#define UCHCOM_REG_LCR2 0x25
+
+#define UCHCOM_VER_20 0x20
+
+#define UCHCOM_BASE_UNKNOWN 0
+#define UCHCOM_BPS_MOD_BASE 20000000
+#define UCHCOM_BPS_MOD_BASE_OFS 1100
+
+#define UCHCOM_DTR_MASK 0x20
+#define UCHCOM_RTS_MASK 0x40
+
+#define UCHCOM_BRK1_MASK 0x01
+#define UCHCOM_BRK2_MASK 0x40
+
+#define UCHCOM_LCR1_MASK 0xAF
+#define UCHCOM_LCR2_MASK 0x07
+#define UCHCOM_LCR1_PARENB 0x80
+#define UCHCOM_LCR2_PAREVEN 0x07
+#define UCHCOM_LCR2_PARODD 0x06
+#define UCHCOM_LCR2_PARMARK 0x05
+#define UCHCOM_LCR2_PARSPACE 0x04
+
+#define UCHCOM_INTR_STAT1 0x02
+#define UCHCOM_INTR_STAT2 0x03
+#define UCHCOM_INTR_LEAST 4
+
+#define UCHCOM_BULK_BUF_SIZE 1024 /* bytes */
+
+enum {
+ UCHCOM_BULK_DT_WR,
+ UCHCOM_BULK_DT_RD,
+ UCHCOM_INTR_DT_RD,
+ UCHCOM_N_TRANSFER,
+};
+
+struct uchcom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UCHCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_dtr; /* local copy */
+ uint8_t sc_rts; /* local copy */
+ uint8_t sc_version;
+ uint8_t sc_msr;
+ uint8_t sc_lsr; /* local status register */
+};
+
+struct uchcom_divider {
+ uint8_t dv_prescaler;
+ uint8_t dv_div;
+ uint8_t dv_mod;
+};
+
+struct uchcom_divider_record {
+ uint32_t dvr_high;
+ uint32_t dvr_low;
+ uint32_t dvr_base_clock;
+ struct uchcom_divider dvr_divider;
+};
+
+static const struct uchcom_divider_record dividers[] =
+{
+ {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}},
+ {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}},
+ {2999999, 23530, 6000000, {3, 0, 0}},
+ {23529, 2942, 750000, {2, 0, 0}},
+ {2941, 368, 93750, {1, 0, 0}},
+ {367, 1, 11719, {0, 0, 0}},
+};
+
+#define NUM_DIVIDERS (sizeof (dividers) / sizeof (dividers[0]))
+
+static const struct usb2_device_id uchcom_devs[] = {
+ {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
+};
+
+/* protypes */
+
+static int uchcom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uchcom_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uchcom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void uchcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uchcom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uchcom_start_read(struct usb2_com_softc *);
+static void uchcom_start_write(struct usb2_com_softc *);
+static void uchcom_stop_read(struct usb2_com_softc *);
+static void uchcom_stop_write(struct usb2_com_softc *);
+static void uchcom_update_version(struct uchcom_softc *);
+static void uchcom_convert_status(struct uchcom_softc *, uint8_t);
+static void uchcom_update_status(struct uchcom_softc *);
+static void uchcom_set_dtrrts(struct uchcom_softc *);
+static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
+static void uchcom_set_dte_rate(struct uchcom_softc *, uint32_t);
+static void uchcom_set_line_control(struct uchcom_softc *, tcflag_t);
+static void uchcom_clear_chip(struct uchcom_softc *);
+static void uchcom_reset_chip(struct uchcom_softc *);
+
+static device_probe_t uchcom_probe;
+static device_attach_t uchcom_attach;
+static device_detach_t uchcom_detach;
+
+static usb2_callback_t uchcom_intr_callback;
+static usb2_callback_t uchcom_write_callback;
+static usb2_callback_t uchcom_read_callback;
+
+static const struct usb2_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
+
+ [UCHCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UCHCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uchcom_write_callback,
+ },
+
+ [UCHCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UCHCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uchcom_read_callback,
+ },
+
+ [UCHCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uchcom_intr_callback,
+ },
+};
+
+struct usb2_com_callback uchcom_callback = {
+ .usb2_com_cfg_get_status = &uchcom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uchcom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uchcom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uchcom_cfg_set_break,
+ .usb2_com_cfg_param = &uchcom_cfg_param,
+ .usb2_com_pre_param = &uchcom_pre_param,
+ .usb2_com_start_read = &uchcom_start_read,
+ .usb2_com_stop_read = &uchcom_stop_read,
+ .usb2_com_start_write = &uchcom_start_write,
+ .usb2_com_stop_write = &uchcom_stop_write,
+};
+
+/* ----------------------------------------------------------------------
+ * driver entry points
+ */
+
+static int
+uchcom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa));
+}
+
+static int
+uchcom_attach(device_t dev)
+{
+ struct uchcom_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ int error;
+ uint8_t iface_index;
+
+ DPRINTFN(11, "\n");
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ switch (uaa->info.bcdDevice) {
+ case UCHCOM_REV_CH340:
+ device_printf(dev, "CH340 detected\n");
+ break;
+ default:
+ device_printf(dev, "CH341 detected\n");
+ break;
+ }
+
+ iface_index = UCHCOM_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device,
+ &iface_index, sc->sc_xfer, uchcom_config_data,
+ UCHCOM_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ DPRINTF("one or more missing USB endpoints, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ /*
+ * Do the initialization during attach so that the system does not
+ * sleep during open:
+ */
+ uchcom_update_version(sc);
+ uchcom_clear_chip(sc);
+ uchcom_reset_chip(sc);
+ uchcom_update_status(sc);
+
+ sc->sc_dtr = 1;
+ sc->sc_rts = 1;
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uchcom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ uchcom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uchcom_detach(device_t dev)
+{
+ struct uchcom_softc *sc = device_get_softc(dev);
+
+ DPRINTFN(11, "\n");
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER);
+
+ return (0);
+}
+
+/* ----------------------------------------------------------------------
+ * low level i/o
+ */
+
+static void
+uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno,
+ uint16_t value, uint16_t index)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev,
+ &sc->sc_ucom, &req, NULL, 0, 1000);
+}
+
+static void
+uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno,
+ uint16_t value, uint16_t index, void *buf, uint16_t buflen)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = reqno;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, buflen);
+
+ usb2_com_cfg_do_request(sc->sc_udev,
+ &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000);
+}
+
+static void
+uchcom_write_reg(struct uchcom_softc *sc,
+ uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2)
+{
+ DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n",
+ (unsigned)reg1, (unsigned)val1,
+ (unsigned)reg2, (unsigned)val2);
+ uchcom_ctrl_write(
+ sc, UCHCOM_REQ_WRITE_REG,
+ reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8));
+}
+
+static void
+uchcom_read_reg(struct uchcom_softc *sc,
+ uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2)
+{
+ uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
+
+ uchcom_ctrl_read(
+ sc, UCHCOM_REQ_READ_REG,
+ reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf));
+
+ DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n",
+ (unsigned)reg1, (unsigned)buf[0],
+ (unsigned)reg2, (unsigned)buf[1]);
+
+ if (rval1)
+ *rval1 = buf[0];
+ if (rval2)
+ *rval2 = buf[1];
+}
+
+static void
+uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver)
+{
+ uint8_t buf[UCHCOM_INPUT_BUF_SIZE];
+
+ uchcom_ctrl_read(
+ sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf));
+
+ if (rver)
+ *rver = buf[0];
+}
+
+static void
+uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval)
+{
+ uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL);
+}
+
+static void
+uchcom_set_dtrrts_10(struct uchcom_softc *sc, uint8_t val)
+{
+ uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val);
+}
+
+static void
+uchcom_set_dtrrts_20(struct uchcom_softc *sc, uint8_t val)
+{
+ uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0);
+}
+
+
+/* ----------------------------------------------------------------------
+ * middle layer
+ */
+
+static void
+uchcom_update_version(struct uchcom_softc *sc)
+{
+ uchcom_get_version(sc, &sc->sc_version);
+}
+
+static void
+uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
+{
+ sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
+ sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
+
+ cur = ~cur & 0x0F;
+ sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
+}
+
+static void
+uchcom_update_status(struct uchcom_softc *sc)
+{
+ uint8_t cur;
+
+ uchcom_get_status(sc, &cur);
+ uchcom_convert_status(sc, cur);
+}
+
+
+static void
+uchcom_set_dtrrts(struct uchcom_softc *sc)
+{
+ uint8_t val = 0;
+
+ if (sc->sc_dtr)
+ val |= UCHCOM_DTR_MASK;
+ if (sc->sc_rts)
+ val |= UCHCOM_RTS_MASK;
+
+ if (sc->sc_version < UCHCOM_VER_20)
+ uchcom_set_dtrrts_10(sc, ~val);
+ else
+ uchcom_set_dtrrts_20(sc, ~val);
+}
+
+static void
+uchcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+ uint8_t brk1;
+ uint8_t brk2;
+
+ uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2);
+ if (onoff) {
+ /* on - clear bits */
+ brk1 &= ~UCHCOM_BRK1_MASK;
+ brk2 &= ~UCHCOM_BRK2_MASK;
+ } else {
+ /* off - set bits */
+ brk1 |= UCHCOM_BRK1_MASK;
+ brk2 |= UCHCOM_BRK2_MASK;
+ }
+ uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2);
+}
+
+static int
+uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
+{
+ const struct uchcom_divider_record *rp;
+ uint32_t div;
+ uint32_t rem;
+ uint32_t mod;
+ uint8_t i;
+
+ /* find record */
+ for (i = 0; i != NUM_DIVIDERS; i++) {
+ if (dividers[i].dvr_high >= rate &&
+ dividers[i].dvr_low <= rate) {
+ rp = &dividers[i];
+ goto found;
+ }
+ }
+ return (-1);
+
+found:
+ dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
+ if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
+ dp->dv_div = rp->dvr_divider.dv_div;
+ else {
+ div = rp->dvr_base_clock / rate;
+ rem = rp->dvr_base_clock % rate;
+ if (div == 0 || div >= 0xFF)
+ return (-1);
+ if ((rem << 1) >= rate)
+ div += 1;
+ dp->dv_div = (uint8_t)-div;
+ }
+
+ mod = UCHCOM_BPS_MOD_BASE / rate + UCHCOM_BPS_MOD_BASE_OFS;
+ mod = mod + mod / 2;
+
+ dp->dv_mod = mod / 0x100;
+
+ return (0);
+}
+
+static void
+uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
+{
+ struct uchcom_divider dv;
+
+ if (uchcom_calc_divider_settings(&dv, rate))
+ return;
+
+ uchcom_write_reg(sc,
+ UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
+ UCHCOM_REG_BPS_DIV, dv.dv_div);
+ uchcom_write_reg(sc,
+ UCHCOM_REG_BPS_MOD, dv.dv_mod,
+ UCHCOM_REG_BPS_PAD, 0);
+}
+
+static void
+uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
+{
+ uint8_t lcr1 = 0;
+ uint8_t lcr2 = 0;
+
+ uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
+
+ lcr1 &= ~UCHCOM_LCR1_MASK;
+ lcr2 &= ~UCHCOM_LCR2_MASK;
+
+ /*
+ * XXX: it is difficult to handle the line control appropriately:
+ * - CS8, !CSTOPB and any parity mode seems ok, but
+ * - the chip doesn't have the function to calculate parity
+ * in !CS8 mode.
+ * - it is unclear that the chip supports CS5,6 mode.
+ * - it is unclear how to handle stop bits.
+ */
+
+ if (cflag & PARENB) {
+ lcr1 |= UCHCOM_LCR1_PARENB;
+ if (cflag & PARODD)
+ lcr2 |= UCHCOM_LCR2_PARODD;
+ else
+ lcr2 |= UCHCOM_LCR2_PAREVEN;
+ }
+ uchcom_write_reg(sc, UCHCOM_REG_LCR1, lcr1, UCHCOM_REG_LCR2, lcr2);
+}
+
+static void
+uchcom_clear_chip(struct uchcom_softc *sc)
+{
+ DPRINTF("\n");
+ uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
+}
+
+static void
+uchcom_reset_chip(struct uchcom_softc *sc)
+{
+ uint16_t val;
+ uint16_t idx;
+ uint8_t lcr1;
+ uint8_t lcr2;
+ uint8_t pre;
+ uint8_t div;
+ uint8_t mod;
+
+ uchcom_read_reg(sc, UCHCOM_REG_LCR1, &lcr1, UCHCOM_REG_LCR2, &lcr2);
+ uchcom_read_reg(sc, UCHCOM_REG_BPS_PRE, &pre, UCHCOM_REG_BPS_DIV, &div);
+ uchcom_read_reg(sc, UCHCOM_REG_BPS_MOD, &mod, UCHCOM_REG_BPS_PAD, NULL);
+
+ val = 0;
+ idx = 0;
+ val |= (uint16_t)(lcr1 & 0xF0) << 8;
+ val |= 0x01;
+ val |= (uint16_t)(lcr2 & 0x0F) << 8;
+ val |= 0x02;
+ idx |= pre & 0x07;
+ val |= 0x04;
+ idx |= (uint16_t)div << 8;
+ val |= 0x08;
+ idx |= mod & 0xF8;
+ val |= 0x10;
+
+ DPRINTF("reset v=0x%04X, i=0x%04X\n", val, idx);
+
+ uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, val, idx);
+}
+
+/* ----------------------------------------------------------------------
+ * methods for ucom
+ */
+static void
+uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ sc->sc_dtr = onoff;
+ uchcom_set_dtrrts(sc);
+}
+
+static void
+uchcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ sc->sc_rts = onoff;
+ uchcom_set_dtrrts(sc);
+}
+
+static int
+uchcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uchcom_divider dv;
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ case CS6:
+ case CS7:
+ return (EIO);
+ default:
+ break;
+ }
+
+ if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
+ return (EIO);
+ }
+ return (0); /* success */
+}
+
+static void
+uchcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ uchcom_set_line_control(sc, t->c_cflag);
+ uchcom_set_dte_rate(sc, t->c_ospeed);
+}
+
+static void
+uchcom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+}
+
+static void
+uchcom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
+}
+
+static void
+uchcom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+}
+
+static void
+uchcom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uchcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
+}
+
+/* ----------------------------------------------------------------------
+ * callback when the modem status is changed.
+ */
+static void
+uchcom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uchcom_softc *sc = xfer->priv_sc;
+ uint8_t buf[UCHCOM_INTR_LEAST];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen = %u\n", xfer->actlen);
+
+ if (xfer->actlen >= UCHCOM_INTR_LEAST) {
+ usb2_copy_out(xfer->frbuffers, 0, buf,
+ UCHCOM_INTR_LEAST);
+
+ DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ (unsigned)buf[0], (unsigned)buf[1],
+ (unsigned)buf[2], (unsigned)buf[3]);
+
+ uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+uchcom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uchcom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UCHCOM_BULK_BUF_SIZE, &actlen)) {
+
+ DPRINTF("actlen = %d\n", actlen);
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+uchcom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uchcom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static device_method_t uchcom_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uchcom_probe),
+ DEVMETHOD(device_attach, uchcom_attach),
+ DEVMETHOD(device_detach, uchcom_detach),
+
+ {0, 0}
+};
+
+static driver_t uchcom_driver = {
+ "ucom",
+ uchcom_methods,
+ sizeof(struct uchcom_softc)
+};
+
+static devclass_t uchcom_devclass;
+
+DRIVER_MODULE(uchcom, ushub, uchcom_driver, uchcom_devclass, NULL, 0);
+MODULE_DEPEND(uchcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uchcom, usb, 1, 1, 1);
diff --git a/sys/dev/usb/serial/ucycom.c b/sys/dev/usb/serial/ucycom.c
new file mode 100644
index 0000000..cabb7fb
--- /dev/null
+++ b/sys/dev/usb/serial/ucycom.c
@@ -0,0 +1,564 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2004 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Device driver for Cypress CY7C637xx and CY7C640/1xx series USB to
+ * RS232 bridges.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_hid.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define UCYCOM_MAX_IOLEN (1024 + 2) /* bytes */
+
+#define UCYCOM_IFACE_INDEX 0
+
+enum {
+ UCYCOM_CTRL_RD,
+ UCYCOM_INTR_RD,
+ UCYCOM_N_TRANSFER,
+};
+
+struct ucycom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[UCYCOM_N_TRANSFER];
+
+ uint32_t sc_model;
+#define MODEL_CY7C63743 0x63743
+#define MODEL_CY7C64013 0x64013
+
+ uint16_t sc_flen; /* feature report length */
+ uint16_t sc_ilen; /* input report length */
+ uint16_t sc_olen; /* output report length */
+
+ uint8_t sc_fid; /* feature report id */
+ uint8_t sc_iid; /* input report id */
+ uint8_t sc_oid; /* output report id */
+ uint8_t sc_cfg;
+#define UCYCOM_CFG_RESET 0x80
+#define UCYCOM_CFG_PARODD 0x20
+#define UCYCOM_CFG_PAREN 0x10
+#define UCYCOM_CFG_STOPB 0x08
+#define UCYCOM_CFG_DATAB 0x03
+ uint8_t sc_ist; /* status flags from last input */
+ uint8_t sc_name[16];
+ uint8_t sc_iface_no;
+ uint8_t sc_temp_cfg[32];
+};
+
+/* prototypes */
+
+static device_probe_t ucycom_probe;
+static device_attach_t ucycom_attach;
+static device_detach_t ucycom_detach;
+
+static usb2_callback_t ucycom_ctrl_write_callback;
+static usb2_callback_t ucycom_intr_read_callback;
+
+static void ucycom_cfg_open(struct usb2_com_softc *);
+static void ucycom_start_read(struct usb2_com_softc *);
+static void ucycom_stop_read(struct usb2_com_softc *);
+static void ucycom_start_write(struct usb2_com_softc *);
+static void ucycom_stop_write(struct usb2_com_softc *);
+static void ucycom_cfg_write(struct ucycom_softc *, uint32_t, uint8_t);
+static int ucycom_pre_param(struct usb2_com_softc *, struct termios *);
+static void ucycom_cfg_param(struct usb2_com_softc *, struct termios *);
+
+static const struct usb2_config ucycom_config[UCYCOM_N_TRANSFER] = {
+
+ [UCYCOM_CTRL_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + UCYCOM_MAX_IOLEN),
+ .mh.flags = {},
+ .mh.callback = &ucycom_ctrl_write_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+
+ [UCYCOM_INTR_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = UCYCOM_MAX_IOLEN,
+ .mh.callback = &ucycom_intr_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ucycom_callback = {
+ .usb2_com_cfg_param = &ucycom_cfg_param,
+ .usb2_com_cfg_open = &ucycom_cfg_open,
+ .usb2_com_pre_param = &ucycom_pre_param,
+ .usb2_com_start_read = &ucycom_start_read,
+ .usb2_com_stop_read = &ucycom_stop_read,
+ .usb2_com_start_write = &ucycom_start_write,
+ .usb2_com_stop_write = &ucycom_stop_write,
+};
+
+static device_method_t ucycom_methods[] = {
+ DEVMETHOD(device_probe, ucycom_probe),
+ DEVMETHOD(device_attach, ucycom_attach),
+ DEVMETHOD(device_detach, ucycom_detach),
+ {0, 0}
+};
+
+static devclass_t ucycom_devclass;
+
+static driver_t ucycom_driver = {
+ .name = "ucycom",
+ .methods = ucycom_methods,
+ .size = sizeof(struct ucycom_softc),
+};
+
+DRIVER_MODULE(ucycom, ushub, ucycom_driver, ucycom_devclass, NULL, 0);
+MODULE_DEPEND(ucycom, ucom, 1, 1, 1);
+MODULE_DEPEND(ucycom, usb, 1, 1, 1);
+
+/*
+ * Supported devices
+ */
+static const struct usb2_device_id ucycom_devs[] = {
+ {USB_VPI(USB_VENDOR_DELORME, USB_PRODUCT_DELORME_EARTHMATE, MODEL_CY7C64013)},
+};
+
+#define UCYCOM_DEFAULT_RATE 4800
+#define UCYCOM_DEFAULT_CFG 0x03 /* N-8-1 */
+
+static int
+ucycom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != 0) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UCYCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(ucycom_devs, sizeof(ucycom_devs), uaa));
+}
+
+static int
+ucycom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ucycom_softc *sc = device_get_softc(dev);
+ void *urd_ptr = NULL;
+ int32_t error;
+ uint16_t urd_len;
+ uint8_t iface_index;
+
+ sc->sc_udev = uaa->device;
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ DPRINTF("\n");
+
+ /* get chip model */
+ sc->sc_model = USB_GET_DRIVER_INFO(uaa);
+ if (sc->sc_model == 0) {
+ device_printf(dev, "unsupported device\n");
+ goto detach;
+ }
+ device_printf(dev, "Cypress CY7C%X USB to RS232 bridge\n", sc->sc_model);
+
+ /* get report descriptor */
+
+ error = usb2_req_get_hid_desc
+ (uaa->device, &Giant,
+ &urd_ptr, &urd_len, M_USBDEV,
+ UCYCOM_IFACE_INDEX);
+
+ if (error) {
+ device_printf(dev, "failed to get report "
+ "descriptor: %s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+ /* get report sizes */
+
+ sc->sc_flen = hid_report_size(urd_ptr, urd_len, hid_feature, &sc->sc_fid);
+ sc->sc_ilen = hid_report_size(urd_ptr, urd_len, hid_input, &sc->sc_iid);
+ sc->sc_olen = hid_report_size(urd_ptr, urd_len, hid_output, &sc->sc_oid);
+
+ if ((sc->sc_ilen > UCYCOM_MAX_IOLEN) || (sc->sc_ilen < 1) ||
+ (sc->sc_olen > UCYCOM_MAX_IOLEN) || (sc->sc_olen < 2) ||
+ (sc->sc_flen > UCYCOM_MAX_IOLEN) || (sc->sc_flen < 5)) {
+ device_printf(dev, "invalid report size i=%d, o=%d, f=%d, max=%d\n",
+ sc->sc_ilen, sc->sc_olen, sc->sc_flen,
+ UCYCOM_MAX_IOLEN);
+ goto detach;
+ }
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+
+ iface_index = UCYCOM_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, ucycom_config, UCYCOM_N_TRANSFER,
+ sc, &Giant);
+ if (error) {
+ device_printf(dev, "allocating USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &ucycom_callback, &Giant);
+
+ if (error) {
+ goto detach;
+ }
+ if (urd_ptr) {
+ free(urd_ptr, M_USBDEV);
+ }
+ return (0); /* success */
+
+detach:
+ if (urd_ptr) {
+ free(urd_ptr, M_USBDEV);
+ }
+ ucycom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+ucycom_detach(device_t dev)
+{
+ struct ucycom_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UCYCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+ucycom_cfg_open(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ /* set default configuration */
+ ucycom_cfg_write(sc, UCYCOM_DEFAULT_RATE, UCYCOM_DEFAULT_CFG);
+}
+
+static void
+ucycom_start_read(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UCYCOM_INTR_RD]);
+}
+
+static void
+ucycom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UCYCOM_INTR_RD]);
+}
+
+static void
+ucycom_start_write(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UCYCOM_CTRL_RD]);
+}
+
+static void
+ucycom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UCYCOM_CTRL_RD]);
+}
+
+static void
+ucycom_ctrl_write_callback(struct usb2_xfer *xfer)
+{
+ struct ucycom_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint8_t data[2];
+ uint8_t offset;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ case USB_ST_SETUP:
+
+ switch (sc->sc_model) {
+ case MODEL_CY7C63743:
+ offset = 1;
+ break;
+ case MODEL_CY7C64013:
+ offset = 2;
+ break;
+ default:
+ offset = 0;
+ break;
+ }
+
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1, offset,
+ sc->sc_olen - offset, &actlen)) {
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, UHID_OUTPUT_REPORT, sc->sc_oid);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, sc->sc_olen);
+
+ switch (sc->sc_model) {
+ case MODEL_CY7C63743:
+ data[0] = actlen;
+ break;
+ case MODEL_CY7C64013:
+ data[0] = 0;
+ data[1] = actlen;
+ break;
+ default:
+ break;
+ }
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_copy_in(xfer->frbuffers + 1, 0, data, offset);
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = sc->sc_olen;
+ xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ }
+ DPRINTF("error=%s\n",
+ usb2_errstr(xfer->error));
+ goto tr_transferred;
+ }
+}
+
+static void
+ucycom_cfg_write(struct ucycom_softc *sc, uint32_t baud, uint8_t cfg)
+{
+ struct usb2_device_request req;
+ uint16_t len;
+ usb2_error_t err;
+
+ len = sc->sc_flen;
+ if (len > sizeof(sc->sc_temp_cfg)) {
+ len = sizeof(sc->sc_temp_cfg);
+ }
+ sc->sc_cfg = cfg;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_SET_REPORT;
+ USETW2(req.wValue, UHID_FEATURE_REPORT, sc->sc_fid);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, len);
+
+ sc->sc_temp_cfg[0] = (baud & 0xff);
+ sc->sc_temp_cfg[1] = (baud >> 8) & 0xff;
+ sc->sc_temp_cfg[2] = (baud >> 16) & 0xff;
+ sc->sc_temp_cfg[3] = (baud >> 24) & 0xff;
+ sc->sc_temp_cfg[4] = cfg;
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, sc->sc_temp_cfg, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
+
+static int
+ucycom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ switch (t->c_ospeed) {
+ case 600:
+ case 1200:
+ case 2400:
+ case 4800:
+ case 9600:
+ case 19200:
+ case 38400:
+ case 57600:
+#if 0
+ /*
+ * Stock chips only support standard baud rates in the 600 - 57600
+ * range, but higher rates can be achieved using custom firmware.
+ */
+ case 115200:
+ case 153600:
+ case 192000:
+#endif
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static void
+ucycom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct ucycom_softc *sc = ucom->sc_parent;
+ uint8_t cfg;
+
+ DPRINTF("\n");
+
+ if (t->c_cflag & CIGNORE) {
+ cfg = sc->sc_cfg;
+ } else {
+ cfg = 0;
+ switch (t->c_cflag & CSIZE) {
+ default:
+ case CS8:
+ ++cfg;
+ case CS7:
+ ++cfg;
+ case CS6:
+ ++cfg;
+ case CS5:
+ break;
+ }
+
+ if (t->c_cflag & CSTOPB)
+ cfg |= UCYCOM_CFG_STOPB;
+ if (t->c_cflag & PARENB)
+ cfg |= UCYCOM_CFG_PAREN;
+ if (t->c_cflag & PARODD)
+ cfg |= UCYCOM_CFG_PARODD;
+ }
+
+ ucycom_cfg_write(sc, t->c_ospeed, cfg);
+}
+
+static void
+ucycom_intr_read_callback(struct usb2_xfer *xfer)
+{
+ struct ucycom_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+ uint32_t offset;
+ uint32_t len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ switch (sc->sc_model) {
+ case MODEL_CY7C63743:
+ if (xfer->actlen < 1) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 1);
+
+ sc->sc_ist = buf[0] & ~0x07;
+ len = buf[0] & 0x07;
+
+ (xfer->actlen)--;
+
+ offset = 1;
+
+ break;
+
+ case MODEL_CY7C64013:
+ if (xfer->actlen < 2) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+
+ sc->sc_ist = buf[0] & ~0x07;
+ len = buf[1];
+
+ (xfer->actlen) -= 2;
+
+ offset = 2;
+
+ break;
+
+ default:
+ DPRINTFN(0, "unsupported model number!\n");
+ goto tr_setup;
+ }
+
+ if (len > xfer->actlen) {
+ len = xfer->actlen;
+ }
+ if (len) {
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
+ offset, len);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = sc->sc_ilen;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
diff --git a/sys/dev/usb/serial/ufoma.c b/sys/dev/usb/serial/ufoma.c
new file mode 100644
index 0000000..1077676
--- /dev/null
+++ b/sys/dev/usb/serial/ufoma.c
@@ -0,0 +1,1212 @@
+/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#define UFOMA_HANDSFREE
+/*-
+ * Copyright (c) 2005, Takanori Watanabe
+ * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Implement a Call Device for modems without multiplexed commands.
+ */
+
+/*
+ * NOTE: all function names beginning like "ufoma_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+#include <sys/sysctl.h>
+#include <sys/sbuf.h>
+
+typedef struct ufoma_mobile_acm_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bType;
+ uint8_t bMode[1];
+} __packed usb2_mcpc_acm_descriptor;
+
+#define UISUBCLASS_MCPC 0x88
+
+#define UDESC_VS_INTERFACE 0x44
+#define UDESCSUB_MCPC_ACM 0x11
+
+#define UMCPC_ACM_TYPE_AB1 0x1
+#define UMCPC_ACM_TYPE_AB2 0x2
+#define UMCPC_ACM_TYPE_AB5 0x5
+#define UMCPC_ACM_TYPE_AB6 0x6
+
+#define UMCPC_ACM_MODE_DEACTIVATED 0x0
+#define UMCPC_ACM_MODE_MODEM 0x1
+#define UMCPC_ACM_MODE_ATCOMMAND 0x2
+#define UMCPC_ACM_MODE_OBEX 0x60
+#define UMCPC_ACM_MODE_VENDOR1 0xc0
+#define UMCPC_ACM_MODE_VENDOR2 0xfe
+#define UMCPC_ACM_MODE_UNLINKED 0xff
+
+#define UMCPC_CM_MOBILE_ACM 0x0
+
+#define UMCPC_ACTIVATE_MODE 0x60
+#define UMCPC_GET_MODETABLE 0x61
+#define UMCPC_SET_LINK 0x62
+#define UMCPC_CLEAR_LINK 0x63
+
+#define UMCPC_REQUEST_ACKNOWLEDGE 0x31
+
+#define UFOMA_MAX_TIMEOUT 15 /* standard says 10 seconds */
+#define UFOMA_CMD_BUF_SIZE 64 /* bytes */
+
+#define UFOMA_BULK_BUF_SIZE 1024 /* bytes */
+
+enum {
+ UFOMA_CTRL_ENDPT_INTR,
+ UFOMA_CTRL_ENDPT_READ,
+ UFOMA_CTRL_ENDPT_WRITE,
+ UFOMA_CTRL_ENDPT_MAX,
+};
+
+enum {
+ UFOMA_BULK_ENDPT_WRITE,
+ UFOMA_BULK_ENDPT_READ,
+ UFOMA_BULK_ENDPT_MAX,
+};
+
+struct ufoma_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+ struct cv sc_cv;
+
+ struct usb2_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX];
+ struct usb2_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX];
+ uint8_t *sc_modetable;
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+
+ uint32_t sc_unit;
+
+ uint16_t sc_line;
+
+ uint8_t sc_num_msg;
+ uint8_t sc_nobulk;
+ uint8_t sc_ctrl_iface_no;
+ uint8_t sc_ctrl_iface_index;
+ uint8_t sc_data_iface_no;
+ uint8_t sc_data_iface_index;
+ uint8_t sc_cm_cap;
+ uint8_t sc_acm_cap;
+ uint8_t sc_lsr;
+ uint8_t sc_msr;
+ uint8_t sc_modetoactivate;
+ uint8_t sc_currentmode;
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t ufoma_probe;
+static device_attach_t ufoma_attach;
+static device_detach_t ufoma_detach;
+
+static usb2_callback_t ufoma_ctrl_read_callback;
+static usb2_callback_t ufoma_ctrl_write_callback;
+static usb2_callback_t ufoma_intr_callback;
+static usb2_callback_t ufoma_bulk_write_callback;
+static usb2_callback_t ufoma_bulk_read_callback;
+
+static void *ufoma_get_intconf(struct usb2_config_descriptor *,
+ struct usb2_interface_descriptor *, uint8_t, uint8_t);
+static void ufoma_cfg_link_state(struct ufoma_softc *);
+static void ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t);
+static void ufoma_cfg_open(struct usb2_com_softc *);
+static void ufoma_cfg_close(struct usb2_com_softc *);
+static void ufoma_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void ufoma_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void ufoma_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void ufoma_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static int ufoma_pre_param(struct usb2_com_softc *, struct termios *);
+static void ufoma_cfg_param(struct usb2_com_softc *, struct termios *);
+static int ufoma_modem_setup(device_t, struct ufoma_softc *,
+ struct usb2_attach_arg *);
+static void ufoma_start_read(struct usb2_com_softc *);
+static void ufoma_stop_read(struct usb2_com_softc *);
+static void ufoma_start_write(struct usb2_com_softc *);
+static void ufoma_stop_write(struct usb2_com_softc *);
+
+/*sysctl stuff*/
+static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS);
+static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS);
+static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS);
+
+
+static const struct usb2_config
+ ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = {
+
+ [UFOMA_CTRL_ENDPT_INTR] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = sizeof(struct usb2_cdc_notification),
+ .mh.callback = &ufoma_intr_callback,
+ },
+
+ [UFOMA_CTRL_ENDPT_READ] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + UFOMA_CMD_BUF_SIZE),
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &ufoma_ctrl_read_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+
+ [UFOMA_CTRL_ENDPT_WRITE] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + 1),
+ .mh.flags = {},
+ .mh.callback = &ufoma_ctrl_write_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static const struct usb2_config
+ ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = {
+
+ [UFOMA_BULK_ENDPT_WRITE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UFOMA_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ufoma_bulk_write_callback,
+ },
+
+ [UFOMA_BULK_ENDPT_READ] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UFOMA_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ufoma_bulk_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ufoma_callback = {
+ .usb2_com_cfg_get_status = &ufoma_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &ufoma_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &ufoma_cfg_set_rts,
+ .usb2_com_cfg_set_break = &ufoma_cfg_set_break,
+ .usb2_com_cfg_param = &ufoma_cfg_param,
+ .usb2_com_cfg_open = &ufoma_cfg_open,
+ .usb2_com_cfg_close = &ufoma_cfg_close,
+ .usb2_com_pre_param = &ufoma_pre_param,
+ .usb2_com_start_read = &ufoma_start_read,
+ .usb2_com_stop_read = &ufoma_stop_read,
+ .usb2_com_start_write = &ufoma_start_write,
+ .usb2_com_stop_write = &ufoma_stop_write,
+};
+
+static device_method_t ufoma_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_probe, ufoma_probe),
+ DEVMETHOD(device_attach, ufoma_attach),
+ DEVMETHOD(device_detach, ufoma_detach),
+ {0, 0}
+};
+
+static devclass_t ufoma_devclass;
+
+static driver_t ufoma_driver = {
+ .name = "ufoma",
+ .methods = ufoma_methods,
+ .size = sizeof(struct ufoma_softc),
+};
+
+DRIVER_MODULE(ufoma, ushub, ufoma_driver, ufoma_devclass, NULL, 0);
+MODULE_DEPEND(ufoma, ucom, 1, 1, 1);
+MODULE_DEPEND(ufoma, usb, 1, 1, 1);
+
+static int
+ufoma_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+ struct usb2_config_descriptor *cd;
+ usb2_mcpc_acm_descriptor *mad;
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ id = usb2_get_interface_descriptor(uaa->iface);
+ cd = usb2_get_config_descriptor(uaa->device);
+
+ if ((id == NULL) ||
+ (cd == NULL) ||
+ (id->bInterfaceClass != UICLASS_CDC) ||
+ (id->bInterfaceSubClass != UISUBCLASS_MCPC)) {
+ return (ENXIO);
+ }
+ mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
+ if (mad == NULL) {
+ return (ENXIO);
+ }
+#ifndef UFOMA_HANDSFREE
+ if ((mad->bType == UMCPC_ACM_TYPE_AB5) ||
+ (mad->bType == UMCPC_ACM_TYPE_AB6)) {
+ return (ENXIO);
+ }
+#endif
+ return (0);
+}
+
+static int
+ufoma_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ufoma_softc *sc = device_get_softc(dev);
+ struct usb2_config_descriptor *cd;
+ struct usb2_interface_descriptor *id;
+ struct sysctl_ctx_list *sctx;
+ struct sysctl_oid *soid;
+
+ usb2_mcpc_acm_descriptor *mad;
+ uint8_t elements;
+ int32_t error;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = dev;
+ sc->sc_unit = device_get_unit(dev);
+
+ usb2_cv_init(&sc->sc_cv, "CWAIT");
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ DPRINTF("\n");
+
+ /* setup control transfers */
+
+ cd = usb2_get_config_descriptor(uaa->device);
+ id = usb2_get_interface_descriptor(uaa->iface);
+ sc->sc_ctrl_iface_no = id->bInterfaceNumber;
+ sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex;
+
+ error = usb2_transfer_setup(uaa->device,
+ &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer,
+ ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating control USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM);
+ if (mad == NULL) {
+ goto detach;
+ }
+ if (mad->bFunctionLength < sizeof(*mad)) {
+ device_printf(dev, "invalid MAD descriptor\n");
+ goto detach;
+ }
+ if ((mad->bType == UMCPC_ACM_TYPE_AB5) ||
+ (mad->bType == UMCPC_ACM_TYPE_AB6)) {
+ sc->sc_nobulk = 1;
+ } else {
+ sc->sc_nobulk = 0;
+ if (ufoma_modem_setup(dev, sc, uaa)) {
+ goto detach;
+ }
+ }
+
+ elements = (mad->bFunctionLength - sizeof(*mad) + 1);
+
+ /* initialize mode variables */
+
+ sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK);
+
+ if (sc->sc_modetable == NULL) {
+ goto detach;
+ }
+ sc->sc_modetable[0] = (elements + 1);
+ bcopy(mad->bMode, &sc->sc_modetable[1], elements);
+
+ sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED;
+ sc->sc_modetoactivate = mad->bMode[0];
+
+ /* clear stall at first run, if any */
+ usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+ usb2_transfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &ufoma_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ /*Sysctls*/
+ sctx = device_get_sysctl_ctx(dev);
+ soid = device_get_sysctl_tree(dev);
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode",
+ CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support,
+ "A", "Supporting port role");
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode",
+ CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current,
+ "A", "Current port role");
+
+ SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode",
+ CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open,
+ "A", "Mode to transit when port is opened");
+ SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit",
+ CTLFLAG_RD, &(sc->sc_ucom.sc_unit), 0,
+ "Unit number as USB serial");
+
+ return (0); /* success */
+
+detach:
+ ufoma_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+ufoma_detach(device_t dev)
+{
+ struct ufoma_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);
+
+ usb2_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX);
+
+ if (sc->sc_modetable) {
+ free(sc->sc_modetable, M_USBDEV);
+ }
+ usb2_cv_destroy(&sc->sc_cv);
+
+ return (0);
+}
+
+static void *
+ufoma_get_intconf(struct usb2_config_descriptor *cd, struct usb2_interface_descriptor *id,
+ uint8_t type, uint8_t subtype)
+{
+ struct usb2_descriptor *desc = (void *)id;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ if (desc->bDescriptorType == UDESC_INTERFACE) {
+ return (NULL);
+ }
+ if ((desc->bDescriptorType == type) &&
+ (desc->bDescriptorSubtype == subtype)) {
+ break;
+ }
+ }
+ return (desc);
+}
+
+static void
+ufoma_cfg_link_state(struct ufoma_softc *sc)
+{
+ struct usb2_device_request req;
+ int32_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+ req.bRequest = UMCPC_SET_LINK;
+ USETW(req.wValue, UMCPC_CM_MOBILE_ACM);
+ USETW(req.wIndex, sc->sc_ctrl_iface_no);
+ USETW(req.wLength, sc->sc_modetable[0]);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, sc->sc_modetable, 0, 1000);
+
+ error = usb2_cv_timedwait(&sc->sc_cv, &Giant, hz);
+
+ if (error) {
+ DPRINTF("NO response\n");
+ }
+}
+
+static void
+ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state)
+{
+ struct usb2_device_request req;
+ int32_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_INTERFACE;
+ req.bRequest = UMCPC_ACTIVATE_MODE;
+ USETW(req.wValue, state);
+ USETW(req.wIndex, sc->sc_ctrl_iface_no);
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ error = usb2_cv_timedwait(&sc->sc_cv, &Giant,
+ (UFOMA_MAX_TIMEOUT * hz));
+ if (error) {
+ DPRINTF("No response\n");
+ }
+}
+
+static void
+ufoma_ctrl_read_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ if (xfer->aframes != xfer->nframes) {
+ goto tr_setup;
+ }
+ if (xfer->frlengths[1] > 0) {
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers + 1,
+ 0, xfer->frlengths[1]);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ if (sc->sc_num_msg) {
+ sc->sc_num_msg--;
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
+ USETW(req.wIndex, sc->sc_ctrl_iface_no);
+ USETW(req.wValue, 0);
+ USETW(req.wLength, UFOMA_CMD_BUF_SIZE);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = UFOMA_CMD_BUF_SIZE;
+ xfer->nframes = 2;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ } else {
+ goto tr_setup;
+ }
+
+ goto tr_transferred;
+ }
+}
+
+static void
+ufoma_ctrl_write_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers + 1,
+ 0, 1, &actlen)) {
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
+ USETW(req.wIndex, sc->sc_ctrl_iface_no);
+ USETW(req.wValue, 0);
+ USETW(req.wLength, 1);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = 1;
+ xfer->nframes = 2;
+
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ DPRINTF("error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ } else {
+ goto tr_setup;
+ }
+
+ goto tr_transferred;
+ }
+}
+
+static void
+ufoma_intr_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ struct usb2_cdc_notification pkt;
+ uint16_t wLen;
+ uint16_t temp;
+ uint8_t mstatus;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 8) {
+ DPRINTF("too short message\n");
+ goto tr_setup;
+ }
+ if (xfer->actlen > sizeof(pkt)) {
+ DPRINTF("truncating message\n");
+ xfer->actlen = sizeof(pkt);
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
+
+ xfer->actlen -= 8;
+
+ wLen = UGETW(pkt.wLength);
+ if (xfer->actlen > wLen) {
+ xfer->actlen = wLen;
+ }
+ if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) &&
+ (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) {
+ temp = UGETW(pkt.wValue);
+ sc->sc_currentmode = (temp >> 8);
+ if (!(temp & 0xff)) {
+ DPRINTF("Mode change failed!\n");
+ }
+ usb2_cv_signal(&sc->sc_cv);
+ }
+ if (pkt.bmRequestType != UCDC_NOTIFICATION) {
+ goto tr_setup;
+ }
+ switch (pkt.bNotification) {
+ case UCDC_N_RESPONSE_AVAILABLE:
+ if (!(sc->sc_nobulk)) {
+ DPRINTF("Wrong serial state!\n");
+ break;
+ }
+ if (sc->sc_num_msg != 0xFF) {
+ sc->sc_num_msg++;
+ }
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+ break;
+
+ case UCDC_N_SERIAL_STATE:
+ if (sc->sc_nobulk) {
+ DPRINTF("Wrong serial state!\n");
+ break;
+ }
+ /*
+ * Set the serial state in ucom driver based on
+ * the bits from the notify message
+ */
+ if (xfer->actlen < 2) {
+ DPRINTF("invalid notification "
+ "length, %d bytes!\n", xfer->actlen);
+ break;
+ }
+ DPRINTF("notify bytes = 0x%02x, 0x%02x\n",
+ pkt.data[0], pkt.data[1]);
+
+ /* currently, lsr is always zero. */
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+
+ mstatus = pkt.data[0];
+
+ if (mstatus & UCDC_N_SERIAL_RI) {
+ sc->sc_msr |= SER_RI;
+ }
+ if (mstatus & UCDC_N_SERIAL_DSR) {
+ sc->sc_msr |= SER_DSR;
+ }
+ if (mstatus & UCDC_N_SERIAL_DCD) {
+ sc->sc_msr |= SER_DCD;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+ break;
+
+ default:
+ break;
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ufoma_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UFOMA_BULK_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ufoma_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct ufoma_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ufoma_cfg_open(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ /* empty input queue */
+
+ if (sc->sc_num_msg != 0xFF) {
+ sc->sc_num_msg++;
+ }
+ if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) {
+ ufoma_cfg_link_state(sc);
+ }
+ if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) {
+ ufoma_cfg_activate_state(sc, sc->sc_modetoactivate);
+ }
+}
+
+static void
+ufoma_cfg_close(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED);
+}
+
+static void
+ufoma_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t wValue;
+
+ if (sc->sc_nobulk ||
+ (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
+ return;
+ }
+ if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) {
+ return;
+ }
+ wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, wValue);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+ufoma_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+ufoma_cfg_set_line_state(struct ufoma_softc *sc)
+{
+ struct usb2_device_request req;
+
+ /* Don't send line state emulation request for OBEX port */
+ if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) {
+ return;
+ }
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+ufoma_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ if (sc->sc_nobulk) {
+ return;
+ }
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_DTR;
+ else
+ sc->sc_line &= ~UCDC_LINE_DTR;
+
+ ufoma_cfg_set_line_state(sc);
+}
+
+static void
+ufoma_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ if (sc->sc_nobulk) {
+ return;
+ }
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_RTS;
+ else
+ sc->sc_line &= ~UCDC_LINE_RTS;
+
+ ufoma_cfg_set_line_state(sc);
+}
+
+static int
+ufoma_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ return (0); /* we accept anything */
+}
+
+static void
+ufoma_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ struct usb2_cdc_line_state ls;
+
+ if (sc->sc_nobulk ||
+ (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) {
+ return;
+ }
+ DPRINTF("\n");
+
+ bzero(&ls, sizeof(ls));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+
+ if (t->c_cflag & CSTOPB) {
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ } else {
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ }
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD) {
+ ls.bParityType = UCDC_PARITY_ODD;
+ } else {
+ ls.bParityType = UCDC_PARITY_EVEN;
+ }
+ } else {
+ ls.bParityType = UCDC_PARITY_NONE;
+ }
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, &ls, 0, 1000);
+}
+
+static int
+ufoma_modem_setup(device_t dev, struct ufoma_softc *sc,
+ struct usb2_attach_arg *uaa)
+{
+ struct usb2_config_descriptor *cd;
+ struct usb2_cdc_acm_descriptor *acm;
+ struct usb2_cdc_cm_descriptor *cmd;
+ struct usb2_interface_descriptor *id;
+ struct usb2_interface *iface;
+ uint8_t i;
+ int32_t error;
+
+ cd = usb2_get_config_descriptor(uaa->device);
+ id = usb2_get_interface_descriptor(uaa->iface);
+
+ cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+
+ if ((cmd == NULL) ||
+ (cmd->bLength < sizeof(*cmd))) {
+ return (EINVAL);
+ }
+ sc->sc_cm_cap = cmd->bmCapabilities;
+ sc->sc_data_iface_no = cmd->bDataInterface;
+
+ acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+
+ if ((acm == NULL) ||
+ (acm->bLength < sizeof(*acm))) {
+ return (EINVAL);
+ }
+ sc->sc_acm_cap = acm->bmCapabilities;
+
+ device_printf(dev, "data interface %d, has %sCM over data, "
+ "has %sbreak\n",
+ sc->sc_data_iface_no,
+ sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+ sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+ /* get the data interface too */
+
+ for (i = 0;; i++) {
+
+ iface = usb2_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usb2_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
+ sc->sc_data_iface_index = i;
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+ break;
+ }
+ } else {
+ device_printf(dev, "no data interface!\n");
+ return (EINVAL);
+ }
+ }
+
+ error = usb2_transfer_setup(uaa->device,
+ &sc->sc_data_iface_index, sc->sc_bulk_xfer,
+ ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating BULK USB "
+ "transfers failed!\n");
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static void
+ufoma_start_read(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ /* start interrupt transfer */
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
+
+ /* start data transfer */
+ if (sc->sc_nobulk) {
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+ } else {
+ usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+ }
+}
+
+static void
+ufoma_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt transfer */
+ usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]);
+
+ /* stop data transfer */
+ if (sc->sc_nobulk) {
+ usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]);
+ } else {
+ usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
+ }
+}
+
+static void
+ufoma_start_write(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ if (sc->sc_nobulk) {
+ usb2_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
+ } else {
+ usb2_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+ }
+}
+
+static void
+ufoma_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ufoma_softc *sc = ucom->sc_parent;
+
+ if (sc->sc_nobulk) {
+ usb2_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]);
+ } else {
+ usb2_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
+ }
+}
+
+struct umcpc_modetostr_tab{
+ int mode;
+ char *str;
+}umcpc_modetostr_tab[]={
+ {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"},
+ {UMCPC_ACM_MODE_MODEM, "modem"},
+ {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"},
+ {UMCPC_ACM_MODE_OBEX, "obex"},
+ {UMCPC_ACM_MODE_VENDOR1, "vendor1"},
+ {UMCPC_ACM_MODE_VENDOR2, "vendor2"},
+ {UMCPC_ACM_MODE_UNLINKED, "unlinked"},
+ {0, NULL}
+};
+
+static char *ufoma_mode_to_str(int mode)
+{
+ int i;
+ for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
+ if(umcpc_modetostr_tab[i].mode == mode){
+ return umcpc_modetostr_tab[i].str;
+ }
+ }
+ return NULL;
+}
+
+static int ufoma_str_to_mode(char *str)
+{
+ int i;
+ for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){
+ if(strcmp(str, umcpc_modetostr_tab[i].str)==0){
+ return umcpc_modetostr_tab[i].mode;
+ }
+ }
+ return -1;
+}
+
+static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS)
+{
+ struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+ struct sbuf sb;
+ int i;
+ char *mode;
+
+ sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND);
+ for(i = 1; i < sc->sc_modetable[0]; i++){
+ mode = ufoma_mode_to_str(sc->sc_modetable[i]);
+ if(mode !=NULL){
+ sbuf_cat(&sb, mode);
+ }else{
+ sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]);
+ }
+ if(i < (sc->sc_modetable[0]-1))
+ sbuf_cat(&sb, ",");
+ }
+ sbuf_trim(&sb);
+ sbuf_finish(&sb);
+ sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
+ sbuf_delete(&sb);
+
+ return 0;
+}
+static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS)
+{
+ struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+ char *mode;
+ char subbuf[]="(XXX)";
+ mode = ufoma_mode_to_str(sc->sc_currentmode);
+ if(!mode){
+ mode = subbuf;
+ snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode);
+ }
+ sysctl_handle_string(oidp, mode, strlen(mode), req);
+
+ return 0;
+
+}
+static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS)
+{
+ struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1;
+ char *mode;
+ char subbuf[40];
+ int newmode;
+ int error;
+ int i;
+
+ mode = ufoma_mode_to_str(sc->sc_modetoactivate);
+ if(mode){
+ strncpy(subbuf, mode, sizeof(subbuf));
+ }else{
+ snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate);
+ }
+ error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req);
+ if(error != 0 || req->newptr == NULL){
+ return error;
+ }
+
+ if((newmode = ufoma_str_to_mode(subbuf)) == -1){
+ return EINVAL;
+ }
+
+ for(i = 1 ; i < sc->sc_modetable[0] ; i++){
+ if(sc->sc_modetable[i] == newmode){
+ sc->sc_modetoactivate = newmode;
+ return 0;
+ }
+ }
+
+ return EINVAL;
+}
diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c
new file mode 100644
index 0000000..8ff7250
--- /dev/null
+++ b/sys/dev/usb/serial/uftdi.c
@@ -0,0 +1,784 @@
+/* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * NOTE: all function names beginning like "uftdi_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+/*
+ * FTDI FT8U100AX serial adapter driver
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR uftdi_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+#include <dev/usb/serial/uftdi_reg.h>
+
+#if USB_DEBUG
+static int uftdi_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uftdi, CTLFLAG_RW, 0, "USB uftdi");
+SYSCTL_INT(_hw_usb2_uftdi, OID_AUTO, debug, CTLFLAG_RW,
+ &uftdi_debug, 0, "Debug level");
+#endif
+
+#define UFTDI_CONFIG_INDEX 0
+#define UFTDI_IFACE_INDEX 0
+
+#define UFTDI_IBUFSIZE 64 /* bytes, maximum number of bytes per
+ * frame */
+#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to
+ * do size encoding */
+
+enum {
+ UFTDI_BULK_DT_WR,
+ UFTDI_BULK_DT_RD,
+ UFTDI_N_TRANSFER,
+};
+
+struct uftdi_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[UFTDI_N_TRANSFER];
+ device_t sc_dev;
+
+ uint32_t sc_unit;
+ enum uftdi_type sc_type;
+
+ uint16_t sc_last_lcr;
+
+ uint8_t sc_iface_index;
+ uint8_t sc_hdrlen;
+ uint8_t sc_msr;
+ uint8_t sc_lsr;
+
+ uint8_t sc_name[16];
+};
+
+struct uftdi_param_config {
+ uint16_t rate;
+ uint16_t lcr;
+ uint8_t v_start;
+ uint8_t v_stop;
+ uint8_t v_flow;
+};
+
+/* prototypes */
+
+static device_probe_t uftdi_probe;
+static device_attach_t uftdi_attach;
+static device_detach_t uftdi_detach;
+
+static usb2_callback_t uftdi_write_callback;
+static usb2_callback_t uftdi_read_callback;
+
+static void uftdi_cfg_open(struct usb2_com_softc *);
+static void uftdi_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uftdi_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uftdi_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int uftdi_set_parm_soft(struct termios *,
+ struct uftdi_param_config *, uint8_t);
+static int uftdi_pre_param(struct usb2_com_softc *, struct termios *);
+static void uftdi_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uftdi_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uftdi_start_read(struct usb2_com_softc *);
+static void uftdi_stop_read(struct usb2_com_softc *);
+static void uftdi_start_write(struct usb2_com_softc *);
+static void uftdi_stop_write(struct usb2_com_softc *);
+static uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *);
+
+static const struct usb2_config uftdi_config[UFTDI_N_TRANSFER] = {
+
+ [UFTDI_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UFTDI_OBUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uftdi_write_callback,
+ },
+
+ [UFTDI_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UFTDI_IBUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uftdi_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uftdi_callback = {
+ .usb2_com_cfg_get_status = &uftdi_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uftdi_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uftdi_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uftdi_cfg_set_break,
+ .usb2_com_cfg_param = &uftdi_cfg_param,
+ .usb2_com_cfg_open = &uftdi_cfg_open,
+ .usb2_com_pre_param = &uftdi_pre_param,
+ .usb2_com_start_read = &uftdi_start_read,
+ .usb2_com_stop_read = &uftdi_stop_read,
+ .usb2_com_start_write = &uftdi_start_write,
+ .usb2_com_stop_write = &uftdi_stop_write,
+};
+
+static device_method_t uftdi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, uftdi_probe),
+ DEVMETHOD(device_attach, uftdi_attach),
+ DEVMETHOD(device_detach, uftdi_detach),
+
+ {0, 0}
+};
+
+static devclass_t uftdi_devclass;
+
+static driver_t uftdi_driver = {
+ .name = "uftdi",
+ .methods = uftdi_methods,
+ .size = sizeof(struct uftdi_softc),
+};
+
+DRIVER_MODULE(uftdi, ushub, uftdi_driver, uftdi_devclass, NULL, 0);
+MODULE_DEPEND(uftdi, ucom, 1, 1, 1);
+MODULE_DEPEND(uftdi, usb, 1, 1, 1);
+
+static struct usb2_device_id uftdi_devs[] = {
+ {USB_VPI(USB_VENDOR_DRESDENELEKTRONIK, USB_PRODUCT_DRESDENELEKTRONIK_SENSORTERMINALBOARD, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U100AX, UFTDI_TYPE_SIO)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_2232C, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SERIAL_8U232AM, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_SEMC_DSS20, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_631, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_632, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_633, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_634, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_CFA_635, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX2_3, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MX4_5, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK202, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_LK204, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13M, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13S, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_TACTRIX_OPENPORT_13U, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EISCOU, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_UOPTBR, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_BBELECTRONICS, USB_PRODUCT_BBELECTRONICS_USOTL4, UFTDI_TYPE_8U232AM)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_PCOPRS1, UFTDI_TYPE_8U232AM)},
+};
+
+static int
+uftdi_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ /* attach to all present interfaces */
+
+ return (usb2_lookup_id_by_uaa(uftdi_devs, sizeof(uftdi_devs), uaa));
+}
+
+static int
+uftdi_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uftdi_softc *sc = device_get_softc(dev);
+ int error;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = dev;
+ sc->sc_unit = device_get_unit(dev);
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ DPRINTF("\n");
+
+ sc->sc_iface_index = uaa->info.bIfaceIndex;
+ sc->sc_type = USB_GET_DRIVER_INFO(uaa);
+
+ switch (sc->sc_type) {
+ case UFTDI_TYPE_SIO:
+ sc->sc_hdrlen = 1;
+ break;
+ case UFTDI_TYPE_8U232AM:
+ default:
+ sc->sc_hdrlen = 0;
+ break;
+ }
+
+ error = usb2_transfer_setup(uaa->device,
+ &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
+ UFTDI_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+
+ /* set a valid "lcr" value */
+
+ sc->sc_last_lcr =
+ (FTDI_SIO_SET_DATA_STOP_BITS_2 |
+ FTDI_SIO_SET_DATA_PARITY_NONE |
+ FTDI_SIO_SET_DATA_BITS(8));
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uftdi_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ uftdi_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uftdi_detach(device_t dev)
+{
+ struct uftdi_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uftdi_cfg_open(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ struct usb2_device_request req;
+
+ DPRINTF("");
+
+ /* perform a full reset on the device */
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_RESET;
+ USETW(req.wValue, FTDI_SIO_RESET_SIO);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ /* turn on RTS/CTS flow control */
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW(req.wValue, 0);
+ USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ /*
+ * NOTE: with the new UCOM layer there will always be a
+ * "uftdi_cfg_param()" call after "open()", so there is no need for
+ * "open()" to configure anything
+ */
+}
+
+static void
+uftdi_write_callback(struct usb2_xfer *xfer)
+{
+ struct uftdi_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+ uint8_t buf[1];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers,
+ sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
+ &actlen)) {
+
+ if (sc->sc_hdrlen > 0) {
+ buf[0] =
+ FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
+ usb2_copy_in(xfer->frbuffers, 0, buf, 1);
+ }
+ xfer->frlengths[0] = actlen + sc->sc_hdrlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uftdi_read_callback(struct usb2_xfer *xfer)
+{
+ struct uftdi_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+ uint8_t ftdi_msr;
+ uint8_t msr;
+ uint8_t lsr;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < 2) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, 2);
+
+ ftdi_msr = FTDI_GET_MSR(buf);
+ lsr = FTDI_GET_LSR(buf);
+
+ msr = 0;
+ if (ftdi_msr & FTDI_SIO_CTS_MASK)
+ msr |= SER_CTS;
+ if (ftdi_msr & FTDI_SIO_DSR_MASK)
+ msr |= SER_DSR;
+ if (ftdi_msr & FTDI_SIO_RI_MASK)
+ msr |= SER_RI;
+ if (ftdi_msr & FTDI_SIO_RLSD_MASK)
+ msr |= SER_DCD;
+
+ if ((sc->sc_msr != msr) ||
+ ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) {
+ DPRINTF("status change msr=0x%02x (0x%02x) "
+ "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr,
+ lsr, sc->sc_lsr);
+
+ sc->sc_msr = msr;
+ sc->sc_lsr = lsr;
+
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ xfer->actlen -= 2;
+
+ if (xfer->actlen > 0) {
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 2,
+ xfer->actlen);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uftdi_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ uint16_t wValue;
+ struct usb2_device_request req;
+
+ wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_MODEM_CTRL;
+ USETW(req.wValue, wValue);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ uint16_t wValue;
+ struct usb2_device_request req;
+
+ wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_MODEM_CTRL;
+ USETW(req.wValue, wValue);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ uint16_t wValue;
+ struct usb2_device_request req;
+
+ if (onoff) {
+ sc->sc_last_lcr |= FTDI_SIO_SET_BREAK;
+ } else {
+ sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK;
+ }
+
+ wValue = sc->sc_last_lcr;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_DATA;
+ USETW(req.wValue, wValue);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static int
+uftdi_set_parm_soft(struct termios *t,
+ struct uftdi_param_config *cfg, uint8_t type)
+{
+ bzero(cfg, sizeof(*cfg));
+
+ switch (type) {
+ case UFTDI_TYPE_SIO:
+ switch (t->c_ospeed) {
+ case 300:
+ cfg->rate = ftdi_sio_b300;
+ break;
+ case 600:
+ cfg->rate = ftdi_sio_b600;
+ break;
+ case 1200:
+ cfg->rate = ftdi_sio_b1200;
+ break;
+ case 2400:
+ cfg->rate = ftdi_sio_b2400;
+ break;
+ case 4800:
+ cfg->rate = ftdi_sio_b4800;
+ break;
+ case 9600:
+ cfg->rate = ftdi_sio_b9600;
+ break;
+ case 19200:
+ cfg->rate = ftdi_sio_b19200;
+ break;
+ case 38400:
+ cfg->rate = ftdi_sio_b38400;
+ break;
+ case 57600:
+ cfg->rate = ftdi_sio_b57600;
+ break;
+ case 115200:
+ cfg->rate = ftdi_sio_b115200;
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case UFTDI_TYPE_8U232AM:
+ if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ if (t->c_cflag & CSTOPB)
+ cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
+ else
+ cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1;
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD) {
+ cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD;
+ } else {
+ cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN;
+ }
+ } else {
+ cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE;
+ }
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5);
+ break;
+
+ case CS6:
+ cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6);
+ break;
+
+ case CS7:
+ cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7);
+ break;
+
+ case CS8:
+ cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8);
+ break;
+ }
+
+ if (t->c_cflag & CRTSCTS) {
+ cfg->v_flow = FTDI_SIO_RTS_CTS_HS;
+ } else if (t->c_iflag & (IXON | IXOFF)) {
+ cfg->v_flow = FTDI_SIO_XON_XOFF_HS;
+ cfg->v_start = t->c_cc[VSTART];
+ cfg->v_stop = t->c_cc[VSTOP];
+ } else {
+ cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL;
+ }
+
+ return (0);
+}
+
+static int
+uftdi_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ struct uftdi_param_config cfg;
+
+ DPRINTF("\n");
+
+ return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
+}
+
+static void
+uftdi_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ uint16_t wIndex = ucom->sc_portno;
+ struct uftdi_param_config cfg;
+ struct usb2_device_request req;
+
+ if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
+ /* should not happen */
+ return;
+ }
+ sc->sc_last_lcr = cfg.lcr;
+
+ DPRINTF("\n");
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_BAUD_RATE;
+ USETW(req.wValue, cfg.rate);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_DATA;
+ USETW(req.wValue, cfg.lcr);
+ USETW(req.wIndex, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
+ USETW2(req.wValue, cfg.v_stop, cfg.v_start);
+ USETW2(req.wIndex, cfg.v_flow, wIndex);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uftdi_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ DPRINTF("msr=0x%02x lsr=0x%02x\n",
+ sc->sc_msr, sc->sc_lsr);
+
+ *msr = sc->sc_msr;
+ *lsr = sc->sc_lsr;
+}
+
+static void
+uftdi_start_read(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+}
+
+static void
+uftdi_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]);
+}
+
+static void
+uftdi_start_write(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+}
+
+static void
+uftdi_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
+}
+
+/*------------------------------------------------------------------------*
+ * uftdi_8u232am_getrate
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
+{
+ /* Table of the nearest even powers-of-2 for values 0..15. */
+ static const uint8_t roundoff[16] = {
+ 0, 2, 2, 4, 4, 4, 8, 8,
+ 8, 8, 8, 8, 16, 16, 16, 16,
+ };
+ uint32_t d;
+ uint32_t freq;
+ uint16_t result;
+
+ if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
+ return (1); /* prevent numerical overflow */
+
+ /* Special cases for 2M and 3M. */
+ if ((speed >= ((3000000 * 100) / 103)) &&
+ (speed <= ((3000000 * 100) / 97))) {
+ result = 0;
+ goto done;
+ }
+ if ((speed >= ((2000000 * 100) / 103)) &&
+ (speed <= ((2000000 * 100) / 97))) {
+ result = 1;
+ goto done;
+ }
+ d = (FTDI_8U232AM_FREQ << 4) / speed;
+ d = (d & ~15) + roundoff[d & 15];
+
+ if (d < FTDI_8U232AM_MIN_DIV)
+ d = FTDI_8U232AM_MIN_DIV;
+ else if (d > FTDI_8U232AM_MAX_DIV)
+ d = FTDI_8U232AM_MAX_DIV;
+
+ /*
+ * Calculate the frequency needed for "d" to exactly divide down to
+ * our target "speed", and check that the actual frequency is within
+ * 3% of this.
+ */
+ freq = (speed * d);
+ if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
+ (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
+ return (1);
+
+ /*
+ * Pack the divisor into the resultant value. The lower 14-bits
+ * hold the integral part, while the upper 2 bits encode the
+ * fractional component: either 0, 0.5, 0.25, or 0.125.
+ */
+ result = (d >> 4);
+ if (d & 8)
+ result |= 0x4000;
+ else if (d & 4)
+ result |= 0x8000;
+ else if (d & 2)
+ result |= 0xc000;
+
+done:
+ *rate = result;
+ return (0);
+}
diff --git a/sys/dev/usb/serial/uftdi_reg.h b/sys/dev/usb/serial/uftdi_reg.h
new file mode 100644
index 0000000..0074bc5
--- /dev/null
+++ b/sys/dev/usb/serial/uftdi_reg.h
@@ -0,0 +1,340 @@
+/* $NetBSD: uftdireg.h,v 1.6 2002/07/11 21:14:28 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Definitions for the FTDI USB Single Port Serial Converter -
+ * known as FTDI_SIO (Serial Input/Output application of the chipset)
+ *
+ * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side,
+ * USB on the other.
+ *
+ * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details
+ * of the protocol required to talk to the device and ongoing assistence
+ * during development.
+ *
+ * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc. is the original
+ * author of this file.
+ */
+/* Modified by Lennart Augustsson */
+
+/* Vendor Request Interface */
+#define FTDI_SIO_RESET 0 /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */
+#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the
+ * port */
+#define FTDI_SIO_GET_STATUS 5 /* Retrieve current value of status
+ * reg */
+#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */
+
+/* Port Identifier Table */
+#define FTDI_PIT_DEFAULT 0 /* SIOA */
+#define FTDI_PIT_SIOA 1 /* SIOA */
+#define FTDI_PIT_SIOB 2 /* SIOB */
+#define FTDI_PIT_PARALLEL 3 /* Parallel */
+
+enum uftdi_type {
+ UFTDI_TYPE_SIO,
+ UFTDI_TYPE_8U232AM
+};
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_RESET
+ * wValue: Control Value
+ * 0 = Reset SIO
+ * 1 = Purge RX buffer
+ * 2 = Purge TX buffer
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * The Reset SIO command has this effect:
+ *
+ * Sets flow control set to 'none'
+ * Event char = 0x0d
+ * Event trigger = disabled
+ * Purge RX buffer
+ * Purge TX buffer
+ * Clear DTR
+ * Clear RTS
+ * baud and data format not reset
+ *
+ * The Purge RX and TX buffer commands affect nothing except the buffers
+ *
+ */
+/* FTDI_SIO_RESET */
+#define FTDI_SIO_RESET_SIO 0
+#define FTDI_SIO_RESET_PURGE_RX 1
+#define FTDI_SIO_RESET_PURGE_TX 2
+
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_BAUDRATE
+ * wValue: BaudRate value - see below
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ */
+/* FTDI_SIO_SET_BAUDRATE */
+enum {
+ ftdi_sio_b300 = 0,
+ ftdi_sio_b600 = 1,
+ ftdi_sio_b1200 = 2,
+ ftdi_sio_b2400 = 3,
+ ftdi_sio_b4800 = 4,
+ ftdi_sio_b9600 = 5,
+ ftdi_sio_b19200 = 6,
+ ftdi_sio_b38400 = 7,
+ ftdi_sio_b57600 = 8,
+ ftdi_sio_b115200 = 9
+};
+
+#define FTDI_8U232AM_FREQ 3000000
+
+/* Bounds for normal divisors as 4-bit fixed precision ints. */
+#define FTDI_8U232AM_MIN_DIV 0x20
+#define FTDI_8U232AM_MAX_DIV 0x3fff8
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_SET_DATA
+ * wValue: Data characteristics (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: No
+ *
+ * Data characteristics
+ *
+ * B0..7 Number of data bits
+ * B8..10 Parity
+ * 0 = None
+ * 1 = Odd
+ * 2 = Even
+ * 3 = Mark
+ * 4 = Space
+ * B11..13 Stop Bits
+ * 0 = 1
+ * 1 = 1.5
+ * 2 = 2
+ * B14..15 Reserved
+ *
+ */
+/* FTDI_SIO_SET_DATA */
+#define FTDI_SIO_SET_DATA_BITS(n) (n)
+#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8)
+#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8)
+#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11)
+#define FTDI_SIO_SET_BREAK (0x1 << 14)
+
+
+/*
+ * BmRequestType: 0100 0000B
+ * bRequest: FTDI_SIO_MODEM_CTRL
+ * wValue: ControlValue (see below)
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * NOTE: If the device is in RTS/CTS flow control, the RTS set by this
+ * command will be IGNORED without an error being returned
+ * Also - you can not set DTR and RTS with one control message
+ *
+ * ControlValue
+ * B0 DTR state
+ * 0 = reset
+ * 1 = set
+ * B1 RTS state
+ * 0 = reset
+ * 1 = set
+ * B2..7 Reserved
+ * B8 DTR state enable
+ * 0 = ignore
+ * 1 = use DTR state
+ * B9 RTS state enable
+ * 0 = ignore
+ * 1 = use RTS state
+ * B10..15 Reserved
+ */
+/* FTDI_SIO_MODEM_CTRL */
+#define FTDI_SIO_SET_DTR_MASK 0x1
+#define FTDI_SIO_SET_DTR_HIGH (1 | ( FTDI_SIO_SET_DTR_MASK << 8))
+#define FTDI_SIO_SET_DTR_LOW (0 | ( FTDI_SIO_SET_DTR_MASK << 8))
+#define FTDI_SIO_SET_RTS_MASK 0x2
+#define FTDI_SIO_SET_RTS_HIGH (2 | ( FTDI_SIO_SET_RTS_MASK << 8))
+#define FTDI_SIO_SET_RTS_LOW (0 | ( FTDI_SIO_SET_RTS_MASK << 8))
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_FLOW_CTRL
+ * wValue: Xoff/Xon
+ * wIndex: Protocol/Port - hIndex is protocl / lIndex is port
+ * wLength: 0
+ * Data: None
+ *
+ * hIndex protocol is:
+ * B0 Output handshaking using RTS/CTS
+ * 0 = disabled
+ * 1 = enabled
+ * B1 Output handshaking using DTR/DSR
+ * 0 = disabled
+ * 1 = enabled
+ * B2 Xon/Xoff handshaking
+ * 0 = disabled
+ * 1 = enabled
+ *
+ * A value of zero in the hIndex field disables handshaking
+ *
+ * If Xon/Xoff handshaking is specified, the hValue field should contain the
+ * XOFF character and the lValue field contains the XON character.
+ */
+/* FTDI_SIO_SET_FLOW_CTRL */
+#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0
+#define FTDI_SIO_RTS_CTS_HS 0x1
+#define FTDI_SIO_DTR_DSR_HS 0x2
+#define FTDI_SIO_XON_XOFF_HS 0x4
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_EVENT_CHAR
+ * wValue: Event Char
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * wValue:
+ * B0..7 Event Character
+ * B8 Event Character Processing
+ * 0 = disabled
+ * 1 = enabled
+ * B9..15 Reserved
+ *
+ * FTDI_SIO_SET_EVENT_CHAR
+ *
+ * Set the special event character for the specified communications port.
+ * If the device sees this character it will immediately return the
+ * data read so far - rather than wait 40ms or until 62 bytes are read
+ * which is what normally happens.
+ */
+
+
+
+/*
+ * BmRequestType: 0100 0000b
+ * bRequest: FTDI_SIO_SET_ERROR_CHAR
+ * wValue: Error Char
+ * wIndex: Port
+ * wLength: 0
+ * Data: None
+ *
+ * Error Char
+ * B0..7 Error Character
+ * B8 Error Character Processing
+ * 0 = disabled
+ * 1 = enabled
+ * B9..15 Reserved
+ *
+ *
+ * FTDI_SIO_SET_ERROR_CHAR
+ * Set the parity error replacement character for the specified communications
+ * port.
+ */
+
+
+/*
+ * BmRequestType: 1100 0000b
+ * bRequest: FTDI_SIO_GET_MODEM_STATUS
+ * wValue: zero
+ * wIndex: Port
+ * wLength: 1
+ * Data: Status
+ *
+ * One byte of data is returned
+ * B0..3 0
+ * B4 CTS
+ * 0 = inactive
+ * 1 = active
+ * B5 DSR
+ * 0 = inactive
+ * 1 = active
+ * B6 Ring Indicator (RI)
+ * 0 = inactive
+ * 1 = active
+ * B7 Receive Line Signal Detect (RLSD)
+ * 0 = inactive
+ * 1 = active
+ *
+ * FTDI_SIO_GET_MODEM_STATUS
+ * Retrieve the current value of the modem status register.
+ */
+#define FTDI_SIO_CTS_MASK 0x10
+#define FTDI_SIO_DSR_MASK 0x20
+#define FTDI_SIO_RI_MASK 0x40
+#define FTDI_SIO_RLSD_MASK 0x80
+
+
+
+/*
+ *
+ * DATA FORMAT
+ *
+ * IN Endpoint
+ *
+ * The device reserves the first two bytes of data on this endpoint to contain
+ * the current values of the modem and line status registers. In the absence of
+ * data, the device generates a message consisting of these two status bytes
+ * every 40 ms.
+ *
+ * Byte 0: Modem Status
+ * NOTE: 4 upper bits have same layout as the MSR register in a 16550
+ *
+ * Offset Description
+ * B0..3 Port
+ * B4 Clear to Send (CTS)
+ * B5 Data Set Ready (DSR)
+ * B6 Ring Indicator (RI)
+ * B7 Receive Line Signal Detect (RLSD)
+ *
+ * Byte 1: Line Status
+ * NOTE: same layout as the LSR register in a 16550
+ *
+ * Offset Description
+ * B0 Data Ready (DR)
+ * B1 Overrun Error (OE)
+ * B2 Parity Error (PE)
+ * B3 Framing Error (FE)
+ * B4 Break Interrupt (BI)
+ * B5 Transmitter Holding Register (THRE)
+ * B6 Transmitter Empty (TEMT)
+ * B7 Error in RCVR FIFO
+ *
+ *
+ * OUT Endpoint
+ *
+ * This device reserves the first bytes of data on this endpoint contain the
+ * length and port identifier of the message. For the FTDI USB Serial converter
+ * the port identifier is always 1.
+ *
+ * Byte 0: Port & length
+ *
+ * Offset Description
+ * B0..1 Port
+ * B2..7 Length of message - (not including Byte 0)
+ *
+ */
+#define FTDI_PORT_MASK 0x0f
+#define FTDI_MSR_MASK 0xf0
+#define FTDI_GET_MSR(p) (((p)[0]) & FTDI_MSR_MASK)
+#define FTDI_GET_LSR(p) ((p)[1])
+#define FTDI_LSR_MASK (~0x60) /* interesting bits */
+#define FTDI_OUT_TAG(len, port) (((len) << 2) | (port))
diff --git a/sys/dev/usb/serial/ugensa.c b/sys/dev/usb/serial/ugensa.c
new file mode 100644
index 0000000..79676d6
--- /dev/null
+++ b/sys/dev/usb/serial/ugensa.c
@@ -0,0 +1,352 @@
+/* $FreeBSD$ */
+/* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roland C. Dowdeswell <elric@netbsd.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * NOTE: all function names beginning like "ugensa_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define UGENSA_BUF_SIZE 2048 /* bytes */
+#define UGENSA_CONFIG_INDEX 0
+#define UGENSA_IFACE_INDEX 0
+#define UGENSA_IFACE_MAX 8 /* exclusivly */
+
+enum {
+ UGENSA_BULK_DT_WR,
+ UGENSA_BULK_DT_RD,
+ UGENSA_N_TRANSFER,
+};
+
+struct ugensa_sub_softc {
+ struct usb2_com_softc *sc_usb2_com_ptr;
+ struct usb2_xfer *sc_xfer[UGENSA_N_TRANSFER];
+};
+
+struct ugensa_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom[UGENSA_IFACE_MAX];
+ struct ugensa_sub_softc sc_sub[UGENSA_IFACE_MAX];
+
+ struct mtx sc_mtx;
+ uint8_t sc_niface;
+};
+
+/* prototypes */
+
+static device_probe_t ugensa_probe;
+static device_attach_t ugensa_attach;
+static device_detach_t ugensa_detach;
+
+static usb2_callback_t ugensa_bulk_write_callback;
+static usb2_callback_t ugensa_bulk_read_callback;
+
+static void ugensa_start_read(struct usb2_com_softc *);
+static void ugensa_stop_read(struct usb2_com_softc *);
+static void ugensa_start_write(struct usb2_com_softc *);
+static void ugensa_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config
+ ugensa_xfer_config[UGENSA_N_TRANSFER] = {
+
+ [UGENSA_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UGENSA_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &ugensa_bulk_write_callback,
+ },
+
+ [UGENSA_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UGENSA_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &ugensa_bulk_read_callback,
+ },
+};
+
+static const struct usb2_com_callback ugensa_callback = {
+ .usb2_com_start_read = &ugensa_start_read,
+ .usb2_com_stop_read = &ugensa_stop_read,
+ .usb2_com_start_write = &ugensa_start_write,
+ .usb2_com_stop_write = &ugensa_stop_write,
+};
+
+static device_method_t ugensa_methods[] = {
+ /* Device methods */
+ DEVMETHOD(device_probe, ugensa_probe),
+ DEVMETHOD(device_attach, ugensa_attach),
+ DEVMETHOD(device_detach, ugensa_detach),
+ {0, 0}
+};
+
+static devclass_t ugensa_devclass;
+
+static driver_t ugensa_driver = {
+ .name = "ugensa",
+ .methods = ugensa_methods,
+ .size = sizeof(struct ugensa_softc),
+};
+
+DRIVER_MODULE(ugensa, ushub, ugensa_driver, ugensa_devclass, NULL, 0);
+MODULE_DEPEND(ugensa, ucom, 1, 1, 1);
+MODULE_DEPEND(ugensa, usb, 1, 1, 1);
+
+static const struct usb2_device_id ugensa_devs[] = {
+ {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 0)},
+ {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 0)},
+ {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 0)},
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 0)},
+ {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 0)},
+};
+
+static int
+ugensa_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != 0) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa));
+}
+
+static int
+ugensa_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ugensa_softc *sc = device_get_softc(dev);
+ struct ugensa_sub_softc *ssc;
+ struct usb2_interface *iface;
+ int32_t error;
+ uint8_t iface_index;
+ int x, cnt;
+
+ device_set_usb2_desc(dev);
+ mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF);
+
+ /* Figure out how many interfaces this device has got */
+ for (cnt = 0; cnt < UGENSA_IFACE_MAX; cnt++) {
+ if ((usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 0) == NULL) ||
+ (usb2_get_pipe(uaa->device, cnt, ugensa_xfer_config + 1) == NULL)) {
+ /* we have reached the end */
+ break;
+ }
+ }
+
+ if (cnt == 0) {
+ device_printf(dev, "No interfaces!\n");
+ goto detach;
+ }
+ for (x = 0; x < cnt; x++) {
+ iface = usb2_get_iface(uaa->device, x);
+ if (iface->idesc->bInterfaceClass != UICLASS_VENDOR)
+ /* Not a serial port, most likely a SD reader */
+ continue;
+
+ ssc = sc->sc_sub + sc->sc_niface;
+ ssc->sc_usb2_com_ptr = sc->sc_ucom + sc->sc_niface;
+
+ iface_index = (UGENSA_IFACE_INDEX + x);
+ error = usb2_transfer_setup(uaa->device,
+ &iface_index, ssc->sc_xfer, ugensa_xfer_config,
+ UGENSA_N_TRANSFER, ssc, &sc->sc_mtx);
+
+ if (error) {
+ device_printf(dev, "allocating USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+ usb2_transfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+
+ /* initialize port number */
+ ssc->sc_usb2_com_ptr->sc_portno = sc->sc_niface;
+ sc->sc_niface++;
+ if (x != uaa->info.bIfaceIndex)
+ usb2_set_parent_iface(uaa->device, x,
+ uaa->info.bIfaceIndex);
+ }
+ device_printf(dev, "Found %d interfaces.\n", sc->sc_niface);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface, sc,
+ &ugensa_callback, &sc->sc_mtx);
+ if (error) {
+ DPRINTF("attach failed\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ ugensa_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+ugensa_detach(device_t dev)
+{
+ struct ugensa_softc *sc = device_get_softc(dev);
+ uint8_t x;
+
+ usb2_com_detach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_niface);
+
+ for (x = 0; x < sc->sc_niface; x++) {
+ usb2_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER);
+ }
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static void
+ugensa_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct ugensa_sub_softc *ssc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
+ UGENSA_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ugensa_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct ugensa_sub_softc *ssc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(ssc->sc_usb2_com_ptr, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+ugensa_start_read(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+}
+
+static void
+ugensa_stop_read(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
+}
+
+static void
+ugensa_start_write(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+}
+
+static void
+ugensa_stop_write(struct usb2_com_softc *ucom)
+{
+ struct ugensa_softc *sc = ucom->sc_parent;
+ struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
+
+ usb2_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/serial/uipaq.c b/sys/dev/usb/serial/uipaq.c
new file mode 100644
index 0000000..e2a88be
--- /dev/null
+++ b/sys/dev/usb/serial/uipaq.c
@@ -0,0 +1,1314 @@
+/* $NetBSD: uipaq.c,v 1.4 2006/11/16 01:33:27 christos Exp $ */
+/* $OpenBSD: uipaq.c,v 1.1 2005/06/17 23:50:33 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2000-2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * iPAQ driver
+ *
+ * 19 July 2003: Incorporated changes suggested by Sam Lawrance from
+ * the uppc module
+ *
+ *
+ * Contact isis@cs.umd.edu if you have any questions/comments about this driver
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#define UIPAQ_CONFIG_INDEX 0 /* config number 1 */
+#define UIPAQ_IFACE_INDEX 0
+
+#define UIPAQ_BUF_SIZE 1024
+
+enum {
+ UIPAQ_BULK_DT_WR,
+ UIPAQ_BULK_DT_RD,
+ UIPAQ_N_TRANSFER,
+};
+
+struct uipaq_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UIPAQ_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint16_t sc_line;
+
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* modem status register */
+};
+
+static device_probe_t uipaq_probe;
+static device_attach_t uipaq_attach;
+static device_detach_t uipaq_detach;
+
+static usb2_callback_t uipaq_write_callback;
+static usb2_callback_t uipaq_read_callback;
+
+static void uipaq_start_read(struct usb2_com_softc *);
+static void uipaq_stop_read(struct usb2_com_softc *);
+static void uipaq_start_write(struct usb2_com_softc *);
+static void uipaq_stop_write(struct usb2_com_softc *);
+static void uipaq_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uipaq_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uipaq_cfg_set_break(struct usb2_com_softc *, uint8_t);
+
+static const struct usb2_config uipaq_config_data[UIPAQ_N_TRANSFER] = {
+
+ [UIPAQ_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UIPAQ_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uipaq_write_callback,
+ },
+
+ [UIPAQ_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UIPAQ_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uipaq_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uipaq_callback = {
+ .usb2_com_cfg_set_dtr = &uipaq_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uipaq_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uipaq_cfg_set_break,
+ .usb2_com_start_read = &uipaq_start_read,
+ .usb2_com_stop_read = &uipaq_stop_read,
+ .usb2_com_start_write = &uipaq_start_write,
+ .usb2_com_stop_write = &uipaq_stop_write,
+};
+
+/*
+ * Much of this list is generated from lists of other drivers that
+ * support the same hardware. Numeric values are used where no usbdevs
+ * entries exist.
+ */
+static const struct usb2_device_id uipaq_devs[] = {
+ /* Socket USB Sync */
+ {USB_VPI(0x0104, 0x00be, 0)},
+ /* USB Sync 0301 */
+ {USB_VPI(0x04ad, 0x0301, 0)},
+ /* USB Sync 0302 */
+ {USB_VPI(0x04ad, 0x0302, 0)},
+ /* USB Sync 0303 */
+ {USB_VPI(0x04ad, 0x0303, 0)},
+ /* GPS Pocket PC USB Sync */
+ {USB_VPI(0x04ad, 0x0306, 0)},
+ /* HHP PDT */
+ {USB_VPI(0x0536, 0x01a0, 0)},
+ /* Intermec Mobile Computer */
+ {USB_VPI(0x067e, 0x1001, 0)},
+ /* Linkup Systems USB Sync */
+ {USB_VPI(0x094b, 0x0001, 0)},
+ /* BCOM USB Sync 0065 */
+ {USB_VPI(0x0960, 0x0065, 0)},
+ /* BCOM USB Sync 0066 */
+ {USB_VPI(0x0960, 0x0066, 0)},
+ /* BCOM USB Sync 0067 */
+ {USB_VPI(0x0960, 0x0067, 0)},
+ /* Portatec USB Sync */
+ {USB_VPI(0x0961, 0x0010, 0)},
+ /* Trimble GeoExplorer */
+ {USB_VPI(0x099e, 0x0052, 0)},
+ /* TDS Data Collector */
+ {USB_VPI(0x099e, 0x4000, 0)},
+ /* Motorola iDEN Smartphone */
+ {USB_VPI(0x0c44, 0x03a2, 0)},
+ /* Cesscom Luxian Series */
+ {USB_VPI(0x0c8e, 0x6000, 0)},
+ /* Motorola PowerPad Pocket PCDevice */
+ {USB_VPI(0x0cad, 0x9001, 0)},
+ /* Freedom Scientific USB Sync */
+ {USB_VPI(0x0f4e, 0x0200, 0)},
+ /* Cyberbank USB Sync */
+ {USB_VPI(0x0f98, 0x0201, 0)},
+ /* Wistron USB Sync */
+ {USB_VPI(0x0fb8, 0x3001, 0)},
+ /* Wistron USB Sync */
+ {USB_VPI(0x0fb8, 0x3002, 0)},
+ /* Wistron USB Sync */
+ {USB_VPI(0x0fb8, 0x3003, 0)},
+ /* Wistron USB Sync */
+ {USB_VPI(0x0fb8, 0x4001, 0)},
+ /* E-TEN USB Sync */
+ {USB_VPI(0x1066, 0x00ce, 0)},
+ /* E-TEN P3XX Pocket PC */
+ {USB_VPI(0x1066, 0x0300, 0)},
+ /* E-TEN P5XX Pocket PC */
+ {USB_VPI(0x1066, 0x0500, 0)},
+ /* E-TEN P6XX Pocket PC */
+ {USB_VPI(0x1066, 0x0600, 0)},
+ /* E-TEN P7XX Pocket PC */
+ {USB_VPI(0x1066, 0x0700, 0)},
+ /* Psion Teklogix Sync 753x */
+ {USB_VPI(0x1114, 0x0001, 0)},
+ /* Psion Teklogix Sync netBookPro */
+ {USB_VPI(0x1114, 0x0004, 0)},
+ /* Psion Teklogix Sync 7525 */
+ {USB_VPI(0x1114, 0x0006, 0)},
+ /* VES USB Sync */
+ {USB_VPI(0x1182, 0x1388, 0)},
+ /* Rugged Pocket PC 2003 */
+ {USB_VPI(0x11d9, 0x1002, 0)},
+ /* Rugged Pocket PC 2003 */
+ {USB_VPI(0x11d9, 0x1003, 0)},
+ /* USB Sync 03 */
+ {USB_VPI(0x1231, 0xce01, 0)},
+ /* USB Sync 03 */
+ {USB_VPI(0x1231, 0xce02, 0)},
+ /* Mio DigiWalker PPC StrongARM */
+ {USB_VPI(0x3340, 0x011c, 0)},
+ /* Mio DigiWalker 338 */
+ {USB_VPI(0x3340, 0x0326, 0)},
+ /* Mio DigiWalker 338 */
+ {USB_VPI(0x3340, 0x0426, 0)},
+ /* Mio DigiWalker USB Sync */
+ {USB_VPI(0x3340, 0x043a, 0)},
+ /* MiTAC USB Sync 528 */
+ {USB_VPI(0x3340, 0x051c, 0)},
+ /* Mio DigiWalker SmartPhone USB Sync */
+ {USB_VPI(0x3340, 0x053a, 0)},
+ /* MiTAC USB Sync */
+ {USB_VPI(0x3340, 0x071c, 0)},
+ /* Generic PPC StrongARM */
+ {USB_VPI(0x3340, 0x0b1c, 0)},
+ /* Generic PPC USB Sync */
+ {USB_VPI(0x3340, 0x0e3a, 0)},
+ /* Itautec USB Sync */
+ {USB_VPI(0x3340, 0x0f1c, 0)},
+ /* Generic SmartPhone USB Sync */
+ {USB_VPI(0x3340, 0x0f3a, 0)},
+ /* Itautec USB Sync */
+ {USB_VPI(0x3340, 0x1326, 0)},
+ /* YAKUMO USB Sync */
+ {USB_VPI(0x3340, 0x191c, 0)},
+ /* Vobis USB Sync */
+ {USB_VPI(0x3340, 0x2326, 0)},
+ /* MEDION Winodws Moble USB Sync */
+ {USB_VPI(0x3340, 0x3326, 0)},
+ /* Legend USB Sync */
+ {USB_VPI(0x3708, 0x20ce, 0)},
+ /* Lenovo USB Sync */
+ {USB_VPI(0x3708, 0x21ce, 0)},
+ /* Mobile Media Technology USB Sync */
+ {USB_VPI(0x4113, 0x0210, 0)},
+ /* Mobile Media Technology USB Sync */
+ {USB_VPI(0x4113, 0x0211, 0)},
+ /* Mobile Media Technology USB Sync */
+ {USB_VPI(0x4113, 0x0400, 0)},
+ /* Mobile Media Technology USB Sync */
+ {USB_VPI(0x4113, 0x0410, 0)},
+ /* Smartphone */
+ {USB_VPI(0x4505, 0x0010, 0)},
+ /* SAGEM Wireless Assistant */
+ {USB_VPI(0x5e04, 0xce00, 0)},
+ /* c10 Series */
+ {USB_VPI(USB_VENDOR_ACER, 0x1631, 0)},
+ /* c20 Series */
+ {USB_VPI(USB_VENDOR_ACER, 0x1632, 0)},
+ /* Acer n10 Handheld USB Sync */
+ {USB_VPI(USB_VENDOR_ACER, 0x16e1, 0)},
+ /* Acer n20 Handheld USB Sync */
+ {USB_VPI(USB_VENDOR_ACER, 0x16e2, 0)},
+ /* Acer n30 Handheld USB Sync */
+ {USB_VPI(USB_VENDOR_ACER, 0x16e3, 0)},
+ /* ASUS USB Sync */
+ {USB_VPI(USB_VENDOR_ASUS, 0x4200, 0)},
+ /* ASUS USB Sync */
+ {USB_VPI(USB_VENDOR_ASUS, 0x4201, 0)},
+ /* ASUS USB Sync */
+ {USB_VPI(USB_VENDOR_ASUS, 0x4202, 0)},
+ /* ASUS USB Sync */
+ {USB_VPI(USB_VENDOR_ASUS, 0x9200, 0)},
+ /* ASUS USB Sync */
+ {USB_VPI(USB_VENDOR_ASUS, 0x9202, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_P535, 0)},
+ /* CASIO USB Sync 2001 */
+ {USB_VPI(USB_VENDOR_CASIO, 0x2001, 0)},
+ /* CASIO USB Sync 2003 */
+ {USB_VPI(USB_VENDOR_CASIO, 0x2003, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_CASIO, USB_PRODUCT_CASIO_BE300, 0)},
+ /* MyGuide 7000 XL USB Sync */
+ {USB_VPI(USB_VENDOR_COMPAL, 0x0531, 0)},
+ /* Compaq iPAQ USB Sync */
+ {USB_VPI(USB_VENDOR_COMPAQ, 0x0032, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQPOCKETPC, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4001, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4002, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4003, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4004, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4005, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4006, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4007, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4008, 0)},
+ /* Dell Axim USB Sync */
+ {USB_VPI(USB_VENDOR_DELL, 0x4009, 0)},
+ /* Fujitsu Siemens Computers USB Sync */
+ {USB_VPI(USB_VENDOR_FSC, 0x1001, 0)},
+ /* FUJITSU USB Sync */
+ {USB_VPI(USB_VENDOR_FUJITSU, 0x1058, 0)},
+ /* FUJITSU USB Sync */
+ {USB_VPI(USB_VENDOR_FUJITSU, 0x1079, 0)},
+ /* Askey USB Sync */
+ {USB_VPI(USB_VENDOR_GIGASET, 0x0601, 0)},
+ /* Hitachi USB Sync */
+ {USB_VPI(USB_VENDOR_HITACHI, 0x0014, 0)},
+ /* HP USB Sync 1612 */
+ {USB_VPI(USB_VENDOR_HP, 0x1216, 0)},
+ /* HP USB Sync 1620 */
+ {USB_VPI(USB_VENDOR_HP, 0x2016, 0)},
+ /* HP USB Sync 1621 */
+ {USB_VPI(USB_VENDOR_HP, 0x2116, 0)},
+ /* HP USB Sync 1622 */
+ {USB_VPI(USB_VENDOR_HP, 0x2216, 0)},
+ /* HP USB Sync 1630 */
+ {USB_VPI(USB_VENDOR_HP, 0x3016, 0)},
+ /* HP USB Sync 1631 */
+ {USB_VPI(USB_VENDOR_HP, 0x3116, 0)},
+ /* HP USB Sync 1632 */
+ {USB_VPI(USB_VENDOR_HP, 0x3216, 0)},
+ /* HP USB Sync 1640 */
+ {USB_VPI(USB_VENDOR_HP, 0x4016, 0)},
+ /* HP USB Sync 1641 */
+ {USB_VPI(USB_VENDOR_HP, 0x4116, 0)},
+ /* HP USB Sync 1642 */
+ {USB_VPI(USB_VENDOR_HP, 0x4216, 0)},
+ /* HP USB Sync 1650 */
+ {USB_VPI(USB_VENDOR_HP, 0x5016, 0)},
+ /* HP USB Sync 1651 */
+ {USB_VPI(USB_VENDOR_HP, 0x5116, 0)},
+ /* HP USB Sync 1652 */
+ {USB_VPI(USB_VENDOR_HP, 0x5216, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2215, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_568J, 0)},
+ /* HTC USB Modem */
+ {USB_VPI(USB_VENDOR_HTC, 0x00cf, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a01, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a02, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a03, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a04, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a05, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a06, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a07, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a08, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a09, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a0a, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a0b, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a0c, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a0d, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a0e, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a0f, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a10, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a11, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a12, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a13, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a14, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a15, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a16, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a17, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a18, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a19, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a1a, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a1b, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a1c, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a1d, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a1e, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a1f, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a20, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a21, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a22, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a23, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a24, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a25, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a26, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a27, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a28, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a29, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a2a, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a2b, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a2c, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a2d, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a2e, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a2f, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a30, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a31, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a32, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a33, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a34, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a35, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a36, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a37, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a38, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a39, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a3a, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a3b, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a3c, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a3d, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a3e, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a3f, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a40, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a41, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a42, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a43, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a44, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a45, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a46, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a47, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a48, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a49, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a4a, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a4b, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a4c, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a4d, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a4e, 0)},
+ /* PocketPC USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a4f, 0)},
+ /* HTC SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a50, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a52, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a53, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a54, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a55, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a56, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a57, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a58, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a59, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a5a, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a5b, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a5c, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a5d, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a5e, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a5f, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a60, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a61, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a62, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a63, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a64, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a65, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a66, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a67, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a68, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a69, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a6a, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a6b, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a6c, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a6d, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a6e, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a6f, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a70, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a71, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a72, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a73, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a74, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a75, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a76, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a77, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a78, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a79, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a7a, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a7b, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a7c, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a7d, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a7e, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a7f, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a80, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a81, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a82, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a83, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a84, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a85, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a86, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a87, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a88, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a89, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a8a, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a8b, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a8c, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a8d, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a8e, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a8f, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a90, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a91, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a92, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a93, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a94, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a95, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a96, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a97, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a98, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a99, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a9a, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a9b, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a9c, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a9d, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a9e, 0)},
+ /* SmartPhone USB Sync */
+ {USB_VPI(USB_VENDOR_HTC, 0x0a9f, 0)},
+ /* "High Tech Computer Corp" */
+ {USB_VPI(USB_VENDOR_HTC, 0x0bce, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_PPC6700MODEM, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_SMARTPHONE, 0)},
+ /**/
+ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_WINMOBILE, 0)},
+ /* JVC USB Sync */
+ {USB_VPI(USB_VENDOR_JVC, 0x3011, 0)},
+ /* JVC USB Sync */
+ {USB_VPI(USB_VENDOR_JVC, 0x3012, 0)},
+ /* LGE USB Sync */
+ {USB_VPI(USB_VENDOR_LG, 0x9c01, 0)},
+ /* Microsoft USB Sync */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x00ce, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0400, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0401, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0402, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0403, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0404, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0405, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0406, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0407, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0408, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0409, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040a, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040b, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040c, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040d, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040e, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040f, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0410, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0411, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0412, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0413, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0414, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0415, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0416, 0)},
+ /* Windows Pocket PC 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0417, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0432, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0433, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0434, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0435, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0436, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0437, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0438, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0439, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043a, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043b, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043c, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043d, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043e, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043f, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0440, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0441, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0442, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0443, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0444, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0445, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0446, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0447, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0448, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0449, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044a, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044b, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044c, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044d, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044e, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044f, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0450, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0451, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0452, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0453, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0454, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0455, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0456, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0457, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0458, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0459, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045a, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045b, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045c, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045d, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045e, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045f, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0460, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0461, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0462, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0463, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0464, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0465, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0466, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0467, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0468, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0469, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046a, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046b, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046c, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046d, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046e, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046f, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0470, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0471, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0472, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0473, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0474, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0475, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0476, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0477, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0478, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0479, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x047a, 0)},
+ /* Windows Pocket PC 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x047b, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04c8, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04c9, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ca, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cb, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cc, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cd, 0)},
+ /* Windows Smartphone 2002 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ce, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d7, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d8, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d9, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04da, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04db, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04dc, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04dd, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04de, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04df, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e0, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e1, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e2, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e3, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e4, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e5, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e6, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e7, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e8, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e9, 0)},
+ /* Windows Smartphone 2003 */
+ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ea, 0)},
+ /* Motorola MPx200 Smartphone */
+ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4204, 0)},
+ /* Motorola MPc GSM */
+ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4214, 0)},
+ /* Motorola MPx220 Smartphone */
+ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4224, 0)},
+ /* Motorola MPc CDMA */
+ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4234, 0)},
+ /* Motorola MPx100 Smartphone */
+ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4244, 0)},
+ /* NEC USB Sync */
+ {USB_VPI(USB_VENDOR_NEC, 0x00d5, 0)},
+ /* NEC USB Sync */
+ {USB_VPI(USB_VENDOR_NEC, 0x00d6, 0)},
+ /* NEC USB Sync */
+ {USB_VPI(USB_VENDOR_NEC, 0x00d7, 0)},
+ /* NEC USB Sync */
+ {USB_VPI(USB_VENDOR_NEC, 0x8024, 0)},
+ /* NEC USB Sync */
+ {USB_VPI(USB_VENDOR_NEC, 0x8025, 0)},
+ /* Panasonic USB Sync */
+ {USB_VPI(USB_VENDOR_PANASONIC, 0x2500, 0)},
+ /* Samsung NEXiO USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f00, 0)},
+ /* Samsung NEXiO USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f01, 0)},
+ /* Samsung NEXiO USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f02, 0)},
+ /* Samsung NEXiO USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f03, 0)},
+ /* Samsung NEXiO USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f04, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6611, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6613, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6615, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6617, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6619, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x661b, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x662e, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6630, 0)},
+ /* Samsung MITs USB Sync */
+ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6632, 0)},
+ /* SHARP WS003SH USB Modem */
+ {USB_VPI(USB_VENDOR_SHARP, 0x9102, 0)},
+ /* SHARP WS004SH USB Modem */
+ {USB_VPI(USB_VENDOR_SHARP, 0x9121, 0)},
+ /* SHARP S01SH USB Modem */
+ {USB_VPI(USB_VENDOR_SHARP, 0x9151, 0)},
+/**/
+ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ES, 0)},
+ /* Symbol USB Sync */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2000, 0)},
+ /* Symbol USB Sync 0x2001 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2001, 0)},
+ /* Symbol USB Sync 0x2002 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2002, 0)},
+ /* Symbol USB Sync 0x2003 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2003, 0)},
+ /* Symbol USB Sync 0x2004 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2004, 0)},
+ /* Symbol USB Sync 0x2005 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2005, 0)},
+ /* Symbol USB Sync 0x2006 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2006, 0)},
+ /* Symbol USB Sync 0x2007 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2007, 0)},
+ /* Symbol USB Sync 0x2008 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2008, 0)},
+ /* Symbol USB Sync 0x2009 */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x2009, 0)},
+ /* Symbol USB Sync 0x200a */
+ {USB_VPI(USB_VENDOR_SYMBOL, 0x200a, 0)},
+ /* TOSHIBA USB Sync 0700 */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0700, 0)},
+ /* TOSHIBA Pocket PC e310 */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0705, 0)},
+ /* TOSHIBA Pocket PC e330 Series */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0707, 0)},
+ /* TOSHIBA Pocket PC e350Series */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0708, 0)},
+ /* TOSHIBA Pocket PC e750 Series */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0709, 0)},
+ /* TOSHIBA Pocket PC e400 Series */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x070a, 0)},
+ /* TOSHIBA Pocket PC e800 Series */
+ {USB_VPI(USB_VENDOR_TOSHIBA, 0x070b, 0)},
+ /* TOSHIBA Pocket PC e740 */
+ {USB_VPI(USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740, 0)},
+ /* ViewSonic Color Pocket PC V35 */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x0ed9, 0)},
+ /* ViewSonic Color Pocket PC V36 */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1527, 0)},
+ /* ViewSonic Color Pocket PC V37 */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1529, 0)},
+ /* ViewSonic Color Pocket PC V38 */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x152b, 0)},
+ /* ViewSonic Pocket PC */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x152e, 0)},
+ /* ViewSonic Communicator Pocket PC */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1921, 0)},
+ /* ViewSonic Smartphone */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1922, 0)},
+ /* ViewSonic Pocket PC V30 */
+ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1923, 0)},
+};
+
+static device_method_t uipaq_methods[] = {
+ DEVMETHOD(device_probe, uipaq_probe),
+ DEVMETHOD(device_attach, uipaq_attach),
+ DEVMETHOD(device_detach, uipaq_detach),
+ {0, 0}
+};
+
+static devclass_t uipaq_devclass;
+
+static driver_t uipaq_driver = {
+ .name = "uipaq",
+ .methods = uipaq_methods,
+ .size = sizeof(struct uipaq_softc),
+};
+
+DRIVER_MODULE(uipaq, ushub, uipaq_driver, uipaq_devclass, NULL, 0);
+MODULE_DEPEND(uipaq, ucom, 1, 1, 1);
+MODULE_DEPEND(uipaq, usb, 1, 1, 1);
+
+static int
+uipaq_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UIPAQ_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UIPAQ_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uipaq_devs, sizeof(uipaq_devs), uaa));
+}
+
+static int
+uipaq_attach(device_t dev)
+{
+ struct usb2_device_request req;
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uipaq_softc *sc = device_get_softc(dev);
+ int error;
+ uint8_t iface_index;
+ uint8_t i;
+
+ sc->sc_udev = uaa->device;
+
+ device_set_usb2_desc(dev);
+
+ /*
+ * Send magic bytes, cribbed from Linux ipaq driver that
+ * claims to have sniffed them from Win98. Wait for driver to
+ * become ready on device side?
+ */
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, UCDC_LINE_DTR);
+ USETW(req.wIndex, 0x0);
+ USETW(req.wLength, 0);
+ for (i = 0; i != 64; i++) {
+ error =
+ usb2_do_request_flags(uaa->device, NULL, &req,
+ NULL, 0, NULL, 100);
+ if (error == 0)
+ break;
+ usb2_pause_mtx(NULL, hz / 10);
+ }
+
+ iface_index = UIPAQ_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, uipaq_config_data,
+ UIPAQ_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uipaq_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ uipaq_detach(dev);
+ return (ENXIO);
+}
+
+int
+uipaq_detach(device_t dev)
+{
+ struct uipaq_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UIPAQ_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uipaq_start_read(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+}
+
+static void
+uipaq_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
+}
+
+static void
+uipaq_start_write(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+}
+
+static void
+uipaq_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
+}
+
+static void
+uipaq_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ DPRINTF("onoff=%d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_DTR;
+ else
+ sc->sc_line &= ~UCDC_LINE_DTR;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = UIPAQ_IFACE_INDEX;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ DPRINTF("onoff=%d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_RTS;
+ else
+ sc->sc_line &= ~UCDC_LINE_RTS;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = UIPAQ_IFACE_INDEX;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uipaq_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t temp;
+
+ temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, temp);
+ req.wIndex[0] = UIPAQ_IFACE_INDEX;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uipaq_write_callback(struct usb2_xfer *xfer)
+{
+ struct uipaq_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UIPAQ_BUF_SIZE, &actlen)) {
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uipaq_read_callback(struct usb2_xfer *xfer)
+{
+ struct uipaq_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/ulpt.c b/sys/dev/usb/serial/ulpt.c
new file mode 100644
index 0000000..36e082b
--- /dev/null
+++ b/sys/dev/usb/serial/ulpt.c
@@ -0,0 +1,726 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF
+ * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR ulpt_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_parse.h>
+
+#include <sys/syslog.h>
+
+#if USB_DEBUG
+static int ulpt_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ulpt, CTLFLAG_RW, 0, "USB ulpt");
+SYSCTL_INT(_hw_usb2_ulpt, OID_AUTO, debug, CTLFLAG_RW,
+ &ulpt_debug, 0, "Debug level");
+#endif
+
+#define ULPT_BSIZE (1<<15) /* bytes */
+#define ULPT_IFQ_MAXLEN 2 /* units */
+
+#define UR_GET_DEVICE_ID 0x00
+#define UR_GET_PORT_STATUS 0x01
+#define UR_SOFT_RESET 0x02
+
+#define LPS_NERR 0x08 /* printer no error */
+#define LPS_SELECT 0x10 /* printer selected */
+#define LPS_NOPAPER 0x20 /* printer out of paper */
+#define LPS_INVERT (LPS_SELECT|LPS_NERR)
+#define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER)
+
+enum {
+ ULPT_BULK_DT_WR,
+ ULPT_BULK_DT_RD,
+ ULPT_INTR_DT_RD,
+ ULPT_N_TRANSFER,
+};
+
+struct ulpt_softc {
+ struct usb2_fifo_sc sc_fifo;
+ struct usb2_fifo_sc sc_fifo_noreset;
+ struct mtx sc_mtx;
+ struct usb2_callout sc_watchdog;
+
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct usb2_fifo *sc_fifo_open[2];
+ struct usb2_xfer *sc_xfer[ULPT_N_TRANSFER];
+
+ int sc_fflags; /* current open flags, FREAD and
+ * FWRITE */
+ uint8_t sc_iface_no;
+ uint8_t sc_last_status;
+ uint8_t sc_zlps; /* number of consequtive zero length
+ * packets received */
+};
+
+/* prototypes */
+
+static device_probe_t ulpt_probe;
+static device_attach_t ulpt_attach;
+static device_detach_t ulpt_detach;
+
+static usb2_callback_t ulpt_write_callback;
+static usb2_callback_t ulpt_read_callback;
+static usb2_callback_t ulpt_status_callback;
+
+static void ulpt_reset(struct ulpt_softc *);
+static void ulpt_watchdog(void *);
+
+static usb2_fifo_close_t ulpt_close;
+static usb2_fifo_cmd_t ulpt_start_read;
+static usb2_fifo_cmd_t ulpt_start_write;
+static usb2_fifo_cmd_t ulpt_stop_read;
+static usb2_fifo_cmd_t ulpt_stop_write;
+static usb2_fifo_ioctl_t ulpt_ioctl;
+static usb2_fifo_open_t ulpt_open;
+static usb2_fifo_open_t unlpt_open;
+
+static struct usb2_fifo_methods ulpt_fifo_methods = {
+ .f_close = &ulpt_close,
+ .f_ioctl = &ulpt_ioctl,
+ .f_open = &ulpt_open,
+ .f_start_read = &ulpt_start_read,
+ .f_start_write = &ulpt_start_write,
+ .f_stop_read = &ulpt_stop_read,
+ .f_stop_write = &ulpt_stop_write,
+ .basename[0] = "ulpt",
+};
+
+static struct usb2_fifo_methods unlpt_fifo_methods = {
+ .f_close = &ulpt_close,
+ .f_ioctl = &ulpt_ioctl,
+ .f_open = &unlpt_open,
+ .f_start_read = &ulpt_start_read,
+ .f_start_write = &ulpt_start_write,
+ .f_stop_read = &ulpt_stop_read,
+ .f_stop_write = &ulpt_stop_write,
+ .basename[0] = "unlpt",
+};
+
+static void
+ulpt_reset(struct ulpt_softc *sc)
+{
+ struct usb2_device_request req;
+
+ DPRINTFN(2, "\n");
+
+ req.bRequest = UR_SOFT_RESET;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, sc->sc_iface_no);
+ USETW(req.wLength, 0);
+
+ /*
+ * There was a mistake in the USB printer 1.0 spec that gave the
+ * request type as UT_WRITE_CLASS_OTHER; it should have been
+ * UT_WRITE_CLASS_INTERFACE. Many printers use the old one,
+ * so we try both.
+ */
+
+ mtx_lock(&sc->sc_mtx);
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ if (usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.0 */
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ if (usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx,
+ &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.1 */
+ /* ignore error */
+ }
+ }
+ mtx_unlock(&sc->sc_mtx);
+}
+
+static void
+ulpt_write_callback(struct usb2_xfer *xfer)
+{
+ struct ulpt_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_TX];
+ uint32_t actlen;
+
+ if (f == NULL) {
+ /* should not happen */
+ DPRINTF("no FIFO\n");
+ return;
+ }
+ DPRINTF("state=0x%x\n", USB_GET_STATE(xfer));
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_fifo_get_data(f, xfer->frbuffers,
+ 0, xfer->max_data_length, &actlen, 0)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+ulpt_read_callback(struct usb2_xfer *xfer)
+{
+ struct ulpt_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo_open[USB_FIFO_RX];
+
+ if (f == NULL) {
+ /* should not happen */
+ DPRINTF("no FIFO\n");
+ return;
+ }
+ DPRINTF("state=0x%x\n", USB_GET_STATE(xfer));
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen == 0) {
+
+ if (sc->sc_zlps == 4) {
+ /* enable BULK throttle */
+ xfer->interval = 500; /* ms */
+ } else {
+ sc->sc_zlps++;
+ }
+ } else {
+ /* disable BULK throttle */
+
+ xfer->interval = 0;
+ sc->sc_zlps = 0;
+ }
+
+ usb2_fifo_put_data(f, xfer->frbuffers,
+ 0, xfer->actlen, 1);
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (usb2_fifo_put_bytes_max(f) != 0) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ /* disable BULK throttle */
+ xfer->interval = 0;
+ sc->sc_zlps = 0;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+ulpt_status_callback(struct usb2_xfer *xfer)
+{
+ struct ulpt_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint8_t cur_status;
+ uint8_t new_status;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ usb2_copy_out(xfer->frbuffers + 1, 0, &cur_status, 1);
+
+ cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK;
+ new_status = cur_status & ~sc->sc_last_status;
+ sc->sc_last_status = cur_status;
+
+ if (new_status & LPS_SELECT)
+ log(LOG_NOTICE, "%s: offline\n",
+ device_get_nameunit(sc->sc_dev));
+ else if (new_status & LPS_NOPAPER)
+ log(LOG_NOTICE, "%s: out of paper\n",
+ device_get_nameunit(sc->sc_dev));
+ else if (new_status & LPS_NERR)
+ log(LOG_NOTICE, "%s: output error\n",
+ device_get_nameunit(sc->sc_dev));
+ break;
+
+ case USB_ST_SETUP:
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_PORT_STATUS;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 1);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = 1;
+ xfer->nframes = 2;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ DPRINTF("error=%s\n", usb2_errstr(xfer->error));
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* wait for next watchdog timeout */
+ }
+ break;
+ }
+}
+
+static const struct usb2_config ulpt_config[ULPT_N_TRANSFER] = {
+ [ULPT_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = ULPT_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1},
+ .mh.callback = &ulpt_write_callback,
+ },
+
+ [ULPT_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = ULPT_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1},
+ .mh.callback = &ulpt_read_callback,
+ },
+
+ [ULPT_INTR_DT_RD] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request) + 1,
+ .mh.callback = &ulpt_status_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static void
+ulpt_start_read(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]);
+}
+
+static void
+ulpt_stop_read(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]);
+}
+
+static void
+ulpt_start_write(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]);
+}
+
+static void
+ulpt_stop_write(struct usb2_fifo *fifo)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]);
+}
+
+static int
+ulpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ /* we assume that open is a serial process */
+
+ if (sc->sc_fflags == 0) {
+ ulpt_reset(sc);
+ }
+ return (unlpt_open(fifo, fflags, td));
+}
+
+static int
+unlpt_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ if (sc->sc_fflags & fflags) {
+ return (EBUSY);
+ }
+ if (fflags & FREAD) {
+ /* clear stall first */
+ mtx_lock(&sc->sc_mtx);
+ usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]);
+ mtx_unlock(&sc->sc_mtx);
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[ULPT_BULK_DT_RD]->max_data_length,
+ ULPT_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ /* set which FIFO is opened */
+ sc->sc_fifo_open[USB_FIFO_RX] = fifo;
+ }
+ if (fflags & FWRITE) {
+ /* clear stall first */
+ mtx_lock(&sc->sc_mtx);
+ usb2_transfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]);
+ mtx_unlock(&sc->sc_mtx);
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[ULPT_BULK_DT_WR]->max_data_length,
+ ULPT_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ /* set which FIFO is opened */
+ sc->sc_fifo_open[USB_FIFO_TX] = fifo;
+ }
+ sc->sc_fflags |= fflags & (FREAD | FWRITE);
+ return (0);
+}
+
+static void
+ulpt_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct ulpt_softc *sc = fifo->priv_sc0;
+
+ sc->sc_fflags &= ~(fflags & (FREAD | FWRITE));
+
+ if (fflags & (FREAD | FWRITE)) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+static int
+ulpt_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
+ int fflags, struct thread *td)
+{
+ return (ENODEV);
+}
+
+static int
+ulpt_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if ((uaa->info.bInterfaceClass == UICLASS_PRINTER) &&
+ (uaa->info.bInterfaceSubClass == UISUBCLASS_PRINTER) &&
+ ((uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_UNI) ||
+ (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_BI) ||
+ (uaa->info.bInterfaceProtocol == UIPROTO_PRINTER_1284))) {
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+ulpt_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct ulpt_softc *sc = device_get_softc(dev);
+ struct usb2_interface_descriptor *id;
+ int unit = device_get_unit(dev);
+ int error;
+ uint8_t iface_index = uaa->info.bIfaceIndex;
+ uint8_t alt_index;
+
+ DPRINTFN(11, "sc=%p\n", sc);
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ usb2_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
+
+ /* search through all the descriptors looking for bidir mode */
+
+ id = usb2_get_interface_descriptor(uaa->iface);
+ alt_index = 0 - 1;
+ while (1) {
+ if (id == NULL) {
+ break;
+ }
+ if ((id->bDescriptorType == UDESC_INTERFACE) &&
+ (id->bLength >= sizeof(*id))) {
+ if (id->bInterfaceNumber != uaa->info.bIfaceNum) {
+ break;
+ } else {
+ alt_index++;
+ if ((id->bInterfaceClass == UICLASS_PRINTER) &&
+ (id->bInterfaceSubClass == UISUBCLASS_PRINTER) &&
+ (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) {
+ goto found;
+ }
+ }
+ }
+ id = (void *)usb2_desc_foreach(
+ usb2_get_config_descriptor(uaa->device), (void *)id);
+ }
+ goto detach;
+
+found:
+
+ DPRINTF("setting alternate "
+ "config number: %d\n", alt_index);
+
+ if (alt_index) {
+
+ error = usb2_set_alt_interface_index
+ (uaa->device, iface_index, alt_index);
+
+ if (error) {
+ DPRINTF("could not set alternate "
+ "config, error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ }
+ sc->sc_iface_no = id->bInterfaceNumber;
+
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER,
+ sc, &sc->sc_mtx);
+ if (error) {
+ DPRINTF("error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ device_printf(sc->sc_dev, "using bi-directional mode\n");
+
+#if 0
+/*
+ * This code is disabled because for some mysterious reason it causes
+ * printing not to work. But only sometimes, and mostly with
+ * UHCI and less often with OHCI. *sigh*
+ */
+ {
+ struct usb2_config_descriptor *cd = usb2_get_config_descriptor(dev);
+ struct usb2_device_request req;
+ int len, alen;
+
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_GET_DEVICE_ID;
+ USETW(req.wValue, cd->bConfigurationValue);
+ USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting);
+ USETW(req.wLength, sizeof devinfo - 1);
+ error = usb2_do_request_flags(dev, &req, devinfo, USB_SHORT_XFER_OK,
+ &alen, USB_DEFAULT_TIMEOUT);
+ if (error) {
+ device_printf(sc->sc_dev, "cannot get device id\n");
+ } else if (alen <= 2) {
+ device_printf(sc->sc_dev, "empty device id, no "
+ "printer connected?\n");
+ } else {
+ /* devinfo now contains an IEEE-1284 device ID */
+ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff);
+ if (len > sizeof devinfo - 3)
+ len = sizeof devinfo - 3;
+ devinfo[len] = 0;
+ printf("%s: device id <", device_get_nameunit(sc->sc_dev));
+ ieee1284_print_id(devinfo + 2);
+ printf(">\n");
+ }
+ }
+#endif
+
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &ulpt_fifo_methods, &sc->sc_fifo,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &unlpt_fifo_methods, &sc->sc_fifo_noreset,
+ unit, 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ /* start reading of status */
+
+ mtx_lock(&sc->sc_mtx);
+ ulpt_watchdog(sc);
+ mtx_unlock(&sc->sc_mtx);
+ return (0);
+
+detach:
+ ulpt_detach(dev);
+ return (ENOMEM);
+}
+
+static int
+ulpt_detach(device_t dev)
+{
+ struct ulpt_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_fifo_detach(&sc->sc_fifo);
+ usb2_fifo_detach(&sc->sc_fifo_noreset);
+
+ mtx_lock(&sc->sc_mtx);
+ usb2_callout_stop(&sc->sc_watchdog);
+ mtx_unlock(&sc->sc_mtx);
+
+ usb2_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER);
+
+ usb2_callout_drain(&sc->sc_watchdog);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+#if 0
+/* XXX This does not belong here. */
+
+/*
+ * Compare two strings until the second ends.
+ */
+
+static uint8_t
+ieee1284_compare(const char *a, const char *b)
+{
+ while (1) {
+
+ if (*b == 0) {
+ break;
+ }
+ if (*a != *b) {
+ return 1;
+ }
+ b++;
+ a++;
+ }
+ return 0;
+}
+
+/*
+ * Print select parts of an IEEE 1284 device ID.
+ */
+void
+ieee1284_print_id(char *str)
+{
+ char *p, *q;
+
+ for (p = str - 1; p; p = strchr(p, ';')) {
+ p++; /* skip ';' */
+ if (ieee1284_compare(p, "MFG:") == 0 ||
+ ieee1284_compare(p, "MANUFACTURER:") == 0 ||
+ ieee1284_compare(p, "MDL:") == 0 ||
+ ieee1284_compare(p, "MODEL:") == 0) {
+ q = strchr(p, ';');
+ if (q)
+ printf("%.*s", (int)(q - p + 1), p);
+ }
+ }
+}
+
+#endif
+
+static void
+ulpt_watchdog(void *arg)
+{
+ struct ulpt_softc *sc = arg;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ usb2_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]);
+
+ usb2_callout_reset(&sc->sc_watchdog,
+ hz, &ulpt_watchdog, sc);
+}
+
+static devclass_t ulpt_devclass;
+
+static device_method_t ulpt_methods[] = {
+ DEVMETHOD(device_probe, ulpt_probe),
+ DEVMETHOD(device_attach, ulpt_attach),
+ DEVMETHOD(device_detach, ulpt_detach),
+ {0, 0}
+};
+
+static driver_t ulpt_driver = {
+ .name = "ulpt",
+ .methods = ulpt_methods,
+ .size = sizeof(struct ulpt_softc),
+};
+
+DRIVER_MODULE(ulpt, ushub, ulpt_driver, ulpt_devclass, NULL, 0);
+MODULE_DEPEND(ulpt, usb, 1, 1, 1);
+MODULE_DEPEND(ulpt, ucom, 1, 1, 1);
diff --git a/sys/dev/usb/serial/umct.c b/sys/dev/usb/serial/umct.c
new file mode 100644
index 0000000..a511e2e
--- /dev/null
+++ b/sys/dev/usb/serial/umct.c
@@ -0,0 +1,579 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2003 Scott Long
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Driver for the MCT (Magic Control Technology) USB-RS232 Converter.
+ * Based on the superb documentation from the linux mct_u232 driver by
+ * Wolfgang Grandeggar <wolfgang@cec.ch>.
+ * This device smells a lot like the Belkin F5U103, except that it has
+ * suffered some mild brain-damage. This driver is based off of the ubsa.c
+ * driver from Alexander Kabaev <kan@freebsd.org>. Merging the two together
+ * might be useful, though the subtle differences might lead to lots of
+ * #ifdef's.
+ */
+
+/*
+ * NOTE: all function names beginning like "umct_cfg_" can only
+ * be called from within the config thread function !
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+/* The UMCT advertises the standard 8250 UART registers */
+#define UMCT_GET_MSR 2 /* Get Modem Status Register */
+#define UMCT_GET_MSR_SIZE 1
+#define UMCT_GET_LCR 6 /* Get Line Control Register */
+#define UMCT_GET_LCR_SIZE 1
+#define UMCT_SET_BAUD 5 /* Set the Baud Rate Divisor */
+#define UMCT_SET_BAUD_SIZE 4
+#define UMCT_SET_LCR 7 /* Set Line Control Register */
+#define UMCT_SET_LCR_SIZE 1
+#define UMCT_SET_MCR 10 /* Set Modem Control Register */
+#define UMCT_SET_MCR_SIZE 1
+
+#define UMCT_INTR_INTERVAL 100
+#define UMCT_IFACE_INDEX 0
+#define UMCT_CONFIG_INDEX 1
+
+enum {
+ UMCT_BULK_DT_WR,
+ UMCT_BULK_DT_RD,
+ UMCT_INTR_DT_RD,
+ UMCT_N_TRANSFER,
+};
+
+struct umct_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[UMCT_N_TRANSFER];
+
+ uint32_t sc_unit;
+
+ uint16_t sc_obufsize;
+
+ uint8_t sc_lsr;
+ uint8_t sc_msr;
+ uint8_t sc_lcr;
+ uint8_t sc_mcr;
+ uint8_t sc_iface_no;
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t umct_probe;
+static device_attach_t umct_attach;
+static device_detach_t umct_detach;
+
+static usb2_callback_t umct_intr_callback;
+static usb2_callback_t umct_write_callback;
+static usb2_callback_t umct_read_callback;
+
+static void umct_cfg_do_request(struct umct_softc *sc, uint8_t request,
+ uint16_t len, uint32_t value);
+static void umct_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void umct_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void umct_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void umct_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static uint8_t umct_calc_baud(uint32_t);
+static int umct_pre_param(struct usb2_com_softc *, struct termios *);
+static void umct_cfg_param(struct usb2_com_softc *, struct termios *);
+static void umct_start_read(struct usb2_com_softc *);
+static void umct_stop_read(struct usb2_com_softc *);
+static void umct_start_write(struct usb2_com_softc *);
+static void umct_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config umct_config[UMCT_N_TRANSFER] = {
+
+ [UMCT_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &umct_write_callback,
+ },
+
+ [UMCT_BULK_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umct_read_callback,
+ .ep_index = 0, /* first interrupt endpoint */
+ },
+
+ [UMCT_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umct_intr_callback,
+ .ep_index = 1, /* second interrupt endpoint */
+ },
+};
+
+static const struct usb2_com_callback umct_callback = {
+ .usb2_com_cfg_get_status = &umct_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &umct_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &umct_cfg_set_rts,
+ .usb2_com_cfg_set_break = &umct_cfg_set_break,
+ .usb2_com_cfg_param = &umct_cfg_param,
+ .usb2_com_pre_param = &umct_pre_param,
+ .usb2_com_start_read = &umct_start_read,
+ .usb2_com_stop_read = &umct_stop_read,
+ .usb2_com_start_write = &umct_start_write,
+ .usb2_com_stop_write = &umct_stop_write,
+};
+
+static const struct usb2_device_id umct_devs[] = {
+ {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_USB232, 0)},
+ {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, 0)},
+ {USB_VPI(USB_VENDOR_MCT, USB_PRODUCT_MCT_DU_H3SP_USB232, 0)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U109, 0)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U409, 0)},
+};
+
+static device_method_t umct_methods[] = {
+ DEVMETHOD(device_probe, umct_probe),
+ DEVMETHOD(device_attach, umct_attach),
+ DEVMETHOD(device_detach, umct_detach),
+ {0, 0}
+};
+
+static devclass_t umct_devclass;
+
+static driver_t umct_driver = {
+ .name = "umct",
+ .methods = umct_methods,
+ .size = sizeof(struct umct_softc),
+};
+
+DRIVER_MODULE(umct, ushub, umct_driver, umct_devclass, NULL, 0);
+MODULE_DEPEND(umct, ucom, 1, 1, 1);
+MODULE_DEPEND(umct, usb, 1, 1, 1);
+
+static int
+umct_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UMCT_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UMCT_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(umct_devs, sizeof(umct_devs), uaa));
+}
+
+static int
+umct_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umct_softc *sc = device_get_softc(dev);
+ int32_t error;
+ uint16_t maxp;
+ uint8_t iface_index;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_unit = device_get_unit(dev);
+
+ device_set_usb2_desc(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+
+ iface_index = UMCT_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, umct_config, UMCT_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ device_printf(dev, "allocating USB "
+ "transfers failed!\n");
+ goto detach;
+ }
+ /*
+ * The real bulk-in endpoint is also marked as an interrupt.
+ * The only way to differentiate it from the real interrupt
+ * endpoint is to look at the wMaxPacketSize field.
+ */
+ maxp = UGETW(sc->sc_xfer[UMCT_BULK_DT_RD]->pipe->edesc->wMaxPacketSize);
+ if (maxp == 0x2) {
+
+ /* guessed wrong - switch around endpoints */
+
+ struct usb2_xfer *temp = sc->sc_xfer[UMCT_INTR_DT_RD];
+
+ sc->sc_xfer[UMCT_INTR_DT_RD] = sc->sc_xfer[UMCT_BULK_DT_RD];
+ sc->sc_xfer[UMCT_BULK_DT_RD] = temp;
+
+ sc->sc_xfer[UMCT_BULK_DT_RD]->callback = &umct_read_callback;
+ sc->sc_xfer[UMCT_INTR_DT_RD]->callback = &umct_intr_callback;
+ }
+ sc->sc_obufsize = sc->sc_xfer[UMCT_BULK_DT_WR]->max_data_length;
+
+ if (uaa->info.idProduct == USB_PRODUCT_MCT_SITECOM_USB232) {
+ if (sc->sc_obufsize > 16) {
+ sc->sc_obufsize = 16;
+ }
+ }
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &umct_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ umct_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+umct_detach(device_t dev)
+{
+ struct umct_softc *sc = device_get_softc(dev);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMCT_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+umct_cfg_do_request(struct umct_softc *sc, uint8_t request,
+ uint16_t len, uint32_t value)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t temp[4];
+
+ if (len > 4)
+ len = 4;
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = request;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, len);
+ USETDW(temp, value);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, temp, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+ return;
+}
+
+static void
+umct_intr_callback(struct usb2_xfer *xfer)
+{
+ struct umct_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 2) {
+ DPRINTF("too short message\n");
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
+
+ sc->sc_msr = buf[0];
+ sc->sc_lsr = buf[1];
+
+ usb2_com_status_change(&sc->sc_ucom);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umct_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+umct_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ if (onoff)
+ sc->sc_lcr |= 0x40;
+ else
+ sc->sc_lcr &= ~0x40;
+
+ umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr);
+}
+
+static void
+umct_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ if (onoff)
+ sc->sc_mcr |= 0x01;
+ else
+ sc->sc_mcr &= ~0x01;
+
+ umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+}
+
+static void
+umct_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ if (onoff)
+ sc->sc_mcr |= 0x02;
+ else
+ sc->sc_mcr &= ~0x02;
+
+ umct_cfg_do_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
+}
+
+static uint8_t
+umct_calc_baud(uint32_t baud)
+{
+ switch (baud) {
+ case B300:return (0x1);
+ case B600:
+ return (0x2);
+ case B1200:
+ return (0x3);
+ case B2400:
+ return (0x4);
+ case B4800:
+ return (0x6);
+ case B9600:
+ return (0x8);
+ case B19200:
+ return (0x9);
+ case B38400:
+ return (0xa);
+ case B57600:
+ return (0xb);
+ case 115200:
+ return (0xc);
+ case B0:
+ default:
+ break;
+ }
+ return (0x0);
+}
+
+static int
+umct_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ return (0); /* we accept anything */
+}
+
+static void
+umct_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+ uint32_t value;
+
+ value = umct_calc_baud(t->c_ospeed);
+ umct_cfg_do_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value);
+
+ value = (sc->sc_lcr & 0x40);
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ value |= 0x0;
+ break;
+ case CS6:
+ value |= 0x1;
+ break;
+ case CS7:
+ value |= 0x2;
+ break;
+ default:
+ case CS8:
+ value |= 0x3;
+ break;
+ }
+
+ value |= (t->c_cflag & CSTOPB) ? 0x4 : 0;
+ if (t->c_cflag & PARENB) {
+ value |= 0x8;
+ value |= (t->c_cflag & PARODD) ? 0x0 : 0x10;
+ }
+ /*
+ * XXX There doesn't seem to be a way to tell the device
+ * to use flow control.
+ */
+
+ sc->sc_lcr = value;
+ umct_cfg_do_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value);
+}
+
+static void
+umct_start_read(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMCT_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_RD]);
+}
+
+static void
+umct_stop_read(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMCT_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_RD]);
+}
+
+static void
+umct_start_write(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UMCT_BULK_DT_WR]);
+}
+
+static void
+umct_stop_write(struct usb2_com_softc *ucom)
+{
+ struct umct_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UMCT_BULK_DT_WR]);
+}
+
+static void
+umct_write_callback(struct usb2_xfer *xfer)
+{
+ struct umct_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ sc->sc_obufsize, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umct_read_callback(struct usb2_xfer *xfer)
+{
+ struct umct_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers,
+ 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/umodem.c b/sys/dev/usb/serial/umodem.c
new file mode 100644
index 0000000..44813dd
--- /dev/null
+++ b/sys/dev/usb/serial/umodem.c
@@ -0,0 +1,788 @@
+/* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2003, M. Warner Losh <imp@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf
+ * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf
+ */
+
+/*
+ * TODO:
+ * - Add error recovery in various places; the big problem is what
+ * to do in a callback if there is an error.
+ * - Implement a Call Device for modems without multiplexed commands.
+ *
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR umodem_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int umodem_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, umodem, CTLFLAG_RW, 0, "USB umodem");
+SYSCTL_INT(_hw_usb2_umodem, OID_AUTO, debug, CTLFLAG_RW,
+ &umodem_debug, 0, "Debug level");
+#endif
+
+static const struct usb2_device_id umodem_devs[] = {
+ /* Generic Modem class match */
+ {USB_IFACE_CLASS(UICLASS_CDC),
+ USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL),
+ USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)},
+ /* Kyocera AH-K3001V */
+ {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)},
+ {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)},
+ {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)},
+};
+
+/*
+ * As speeds for umodem deivces increase, these numbers will need to
+ * be increased. They should be good for G3 speeds and below.
+ *
+ * TODO: The TTY buffers should be increased!
+ */
+#define UMODEM_BUF_SIZE 1024
+
+enum {
+ UMODEM_BULK_WR,
+ UMODEM_BULK_RD,
+ UMODEM_INTR_RD,
+ UMODEM_N_TRANSFER,
+};
+
+#define UMODEM_MODVER 1 /* module version */
+
+struct umodem_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UMODEM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint16_t sc_line;
+
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* modem status register */
+ uint8_t sc_ctrl_iface_no;
+ uint8_t sc_data_iface_no;
+ uint8_t sc_iface_index[2];
+ uint8_t sc_cm_over_data;
+ uint8_t sc_cm_cap; /* CM capabilities */
+ uint8_t sc_acm_cap; /* ACM capabilities */
+};
+
+static device_probe_t umodem_probe;
+static device_attach_t umodem_attach;
+static device_detach_t umodem_detach;
+
+static usb2_callback_t umodem_intr_callback;
+static usb2_callback_t umodem_write_callback;
+static usb2_callback_t umodem_read_callback;
+
+static void umodem_start_read(struct usb2_com_softc *);
+static void umodem_stop_read(struct usb2_com_softc *);
+static void umodem_start_write(struct usb2_com_softc *);
+static void umodem_stop_write(struct usb2_com_softc *);
+static void umodem_get_caps(struct usb2_attach_arg *, uint8_t *, uint8_t *);
+static void umodem_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static int umodem_pre_param(struct usb2_com_softc *, struct termios *);
+static void umodem_cfg_param(struct usb2_com_softc *, struct termios *);
+static int umodem_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int,
+ struct thread *);
+static void umodem_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void umodem_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void umodem_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void *umodem_get_desc(struct usb2_attach_arg *, uint8_t, uint8_t);
+static usb2_error_t umodem_set_comm_feature(struct usb2_device *, uint8_t,
+ uint16_t, uint16_t);
+
+static const struct usb2_config umodem_config[UMODEM_N_TRANSFER] = {
+
+ [UMODEM_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .if_index = 0,
+ .mh.bufsize = UMODEM_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &umodem_write_callback,
+ },
+
+ [UMODEM_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 0,
+ .mh.bufsize = UMODEM_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &umodem_read_callback,
+ },
+
+ [UMODEM_INTR_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .if_index = 1,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umodem_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback umodem_callback = {
+ .usb2_com_cfg_get_status = &umodem_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &umodem_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &umodem_cfg_set_rts,
+ .usb2_com_cfg_set_break = &umodem_cfg_set_break,
+ .usb2_com_cfg_param = &umodem_cfg_param,
+ .usb2_com_pre_param = &umodem_pre_param,
+ .usb2_com_ioctl = &umodem_ioctl,
+ .usb2_com_start_read = &umodem_start_read,
+ .usb2_com_stop_read = &umodem_stop_read,
+ .usb2_com_start_write = &umodem_start_write,
+ .usb2_com_stop_write = &umodem_stop_write,
+};
+
+static device_method_t umodem_methods[] = {
+ DEVMETHOD(device_probe, umodem_probe),
+ DEVMETHOD(device_attach, umodem_attach),
+ DEVMETHOD(device_detach, umodem_detach),
+ {0, 0}
+};
+
+static devclass_t umodem_devclass;
+
+static driver_t umodem_driver = {
+ .name = "umodem",
+ .methods = umodem_methods,
+ .size = sizeof(struct umodem_softc),
+};
+
+DRIVER_MODULE(umodem, ushub, umodem_driver, umodem_devclass, NULL, 0);
+MODULE_DEPEND(umodem, ucom, 1, 1, 1);
+MODULE_DEPEND(umodem, usb, 1, 1, 1);
+MODULE_VERSION(umodem, UMODEM_MODVER);
+
+static int
+umodem_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ uint8_t cm;
+ uint8_t acm;
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ error = usb2_lookup_id_by_uaa(umodem_devs, sizeof(umodem_devs), uaa);
+ if (error) {
+ return (error);
+ }
+ if (uaa->driver_info == NULL) {
+ /* some modems do not have any capabilities */
+ return (error);
+ }
+ umodem_get_caps(uaa, &cm, &acm);
+ if (!(cm & USB_CDC_CM_DOES_CM) ||
+ !(cm & USB_CDC_CM_OVER_DATA) ||
+ !(acm & USB_CDC_ACM_HAS_LINE)) {
+ error = ENXIO;
+ }
+ return (error);
+}
+
+static int
+umodem_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umodem_softc *sc = device_get_softc(dev);
+ struct usb2_cdc_cm_descriptor *cmd;
+ uint8_t i;
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index[1] = uaa->info.bIfaceIndex;
+ sc->sc_udev = uaa->device;
+
+ umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap);
+
+ /* get the data interface number */
+
+ cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+
+ if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
+ device_printf(dev, "no CM descriptor!\n");
+ goto detach;
+ }
+ sc->sc_data_iface_no = cmd->bDataInterface;
+
+ device_printf(dev, "data interface %d, has %sCM over "
+ "data, has %sbreak\n",
+ sc->sc_data_iface_no,
+ sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ",
+ sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no ");
+
+ /* get the data interface too */
+
+ for (i = 0;; i++) {
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+
+ iface = usb2_get_iface(uaa->device, i);
+
+ if (iface) {
+
+ id = usb2_get_interface_descriptor(iface);
+
+ if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) {
+ sc->sc_iface_index[0] = i;
+ usb2_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex);
+ break;
+ }
+ } else {
+ device_printf(dev, "no data interface!\n");
+ goto detach;
+ }
+ }
+
+ if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) {
+ if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) {
+
+ error = umodem_set_comm_feature
+ (uaa->device, sc->sc_ctrl_iface_no,
+ UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED);
+
+ /* ignore any errors */
+ }
+ sc->sc_cm_over_data = 1;
+ }
+ error = usb2_transfer_setup(uaa->device,
+ sc->sc_iface_index, sc->sc_xfer,
+ umodem_config, UMODEM_N_TRANSFER,
+ sc, &Giant);
+ if (error) {
+ goto detach;
+ }
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &umodem_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ umodem_detach(dev);
+ return (ENXIO);
+}
+
+static void
+umodem_start_read(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint, if any */
+ usb2_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]);
+}
+
+static void
+umodem_stop_read(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint, if any */
+ usb2_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]);
+}
+
+static void
+umodem_start_write(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]);
+}
+
+static void
+umodem_stop_write(struct usb2_com_softc *ucom)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]);
+}
+
+static void
+umodem_get_caps(struct usb2_attach_arg *uaa, uint8_t *cm, uint8_t *acm)
+{
+ struct usb2_cdc_cm_descriptor *cmd;
+ struct usb2_cdc_acm_descriptor *cad;
+
+ *cm = *acm = 0;
+
+ cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM);
+ if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) {
+ DPRINTF("no CM desc\n");
+ return;
+ }
+ *cm = cmd->bmCapabilities;
+
+ cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM);
+ if ((cad == NULL) || (cad->bLength < sizeof(*cad))) {
+ DPRINTF("no ACM desc\n");
+ return;
+ }
+ *acm = cad->bmCapabilities;
+}
+
+static void
+umodem_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static int
+umodem_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ return (0); /* we accept anything */
+}
+
+static void
+umodem_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_cdc_line_state ls;
+ struct usb2_device_request req;
+
+ DPRINTF("sc=%p\n", sc);
+
+ bzero(&ls, sizeof(ls));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+
+ ls.bCharFormat = (t->c_cflag & CSTOPB) ?
+ UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1;
+
+ ls.bParityType = (t->c_cflag & PARENB) ?
+ ((t->c_cflag & PARODD) ?
+ UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE;
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
+ UGETDW(ls.dwDTERate), ls.bCharFormat,
+ ls.bParityType, ls.bDataBits);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, sizeof(ls));
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, &ls, 0, 1000);
+}
+
+static int
+umodem_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data,
+ int flag, struct thread *td)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ int error = 0;
+
+ DPRINTF("cmd=0x%08x\n", cmd);
+
+ switch (cmd) {
+ case USB_GET_CM_OVER_DATA:
+ *(int *)data = sc->sc_cm_over_data;
+ break;
+
+ case USB_SET_CM_OVER_DATA:
+ if (*(int *)data != sc->sc_cm_over_data) {
+ /* XXX change it */
+ }
+ break;
+
+ default:
+ DPRINTF("unknown\n");
+ error = ENOIOCTL;
+ break;
+ }
+
+ return (error);
+}
+
+static void
+umodem_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ DPRINTF("onoff=%d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_DTR;
+ else
+ sc->sc_line &= ~UCDC_LINE_DTR;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+umodem_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ DPRINTF("onoff=%d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_RTS;
+ else
+ sc->sc_line &= ~UCDC_LINE_RTS;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+umodem_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umodem_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t temp;
+
+ DPRINTF("onoff=%d\n", onoff);
+
+ if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) {
+
+ temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, temp);
+ req.wIndex[0] = sc->sc_ctrl_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ }
+}
+
+static void
+umodem_intr_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_cdc_notification pkt;
+ struct umodem_softc *sc = xfer->priv_sc;
+ uint16_t wLen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < 8) {
+ DPRINTF("received short packet, "
+ "%d bytes\n", xfer->actlen);
+ goto tr_setup;
+ }
+ if (xfer->actlen > sizeof(pkt)) {
+ DPRINTF("truncating message\n");
+ xfer->actlen = sizeof(pkt);
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &pkt, xfer->actlen);
+
+ xfer->actlen -= 8;
+
+ wLen = UGETW(pkt.wLength);
+ if (xfer->actlen > wLen) {
+ xfer->actlen = wLen;
+ }
+ if (pkt.bmRequestType != UCDC_NOTIFICATION) {
+ DPRINTF("unknown message type, "
+ "0x%02x, on notify pipe!\n",
+ pkt.bmRequestType);
+ goto tr_setup;
+ }
+ switch (pkt.bNotification) {
+ case UCDC_N_SERIAL_STATE:
+ /*
+ * Set the serial state in ucom driver based on
+ * the bits from the notify message
+ */
+ if (xfer->actlen < 2) {
+ DPRINTF("invalid notification "
+ "length, %d bytes!\n", xfer->actlen);
+ break;
+ }
+ DPRINTF("notify bytes = %02x%02x\n",
+ pkt.data[0],
+ pkt.data[1]);
+
+ /* Currently, lsr is always zero. */
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+
+ if (pkt.data[0] & UCDC_N_SERIAL_RI) {
+ sc->sc_msr |= SER_RI;
+ }
+ if (pkt.data[0] & UCDC_N_SERIAL_DSR) {
+ sc->sc_msr |= SER_DSR;
+ }
+ if (pkt.data[0] & UCDC_N_SERIAL_DCD) {
+ sc->sc_msr |= SER_DCD;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+ break;
+
+ default:
+ DPRINTF("unknown notify message: 0x%02x\n",
+ pkt.bNotification);
+ break;
+ }
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+
+ }
+}
+
+static void
+umodem_write_callback(struct usb2_xfer *xfer)
+{
+ struct umodem_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UMODEM_BUF_SIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umodem_read_callback(struct usb2_xfer *xfer)
+{
+ struct umodem_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen=%d\n", xfer->actlen);
+
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void *
+umodem_get_desc(struct usb2_attach_arg *uaa, uint8_t type, uint8_t subtype)
+{
+ return (usb2_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex,
+ type, 0 - 1, subtype, 0 - 1));
+}
+
+static usb2_error_t
+umodem_set_comm_feature(struct usb2_device *udev, uint8_t iface_no,
+ uint16_t feature, uint16_t state)
+{
+ struct usb2_device_request req;
+ struct usb2_cdc_abstract_state ast;
+
+ DPRINTF("feature=%d state=%d\n",
+ feature, state);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_COMM_FEATURE;
+ USETW(req.wValue, feature);
+ req.wIndex[0] = iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH);
+ USETW(ast.wState, state);
+
+ return (usb2_do_request(udev, &Giant, &req, &ast));
+}
+
+static int
+umodem_detach(device_t dev)
+{
+ struct umodem_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER);
+
+ return (0);
+}
diff --git a/sys/dev/usb/serial/umoscom.c b/sys/dev/usb/serial/umoscom.c
new file mode 100644
index 0000000..344f4a1
--- /dev/null
+++ b/sys/dev/usb/serial/umoscom.c
@@ -0,0 +1,672 @@
+/* $FreeBSD$ */
+/* $OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $ */
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR umoscom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int umoscom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, umoscom, CTLFLAG_RW, 0, "USB umoscom");
+SYSCTL_INT(_hw_usb2_umoscom, OID_AUTO, debug, CTLFLAG_RW,
+ &umoscom_debug, 0, "Debug level");
+#endif
+
+#define UMOSCOM_BUFSIZE 1024 /* bytes */
+
+#define UMOSCOM_CONFIG_INDEX 0
+#define UMOSCOM_IFACE_INDEX 0
+
+/* interrupt packet */
+#define UMOSCOM_IIR_RLS 0x06
+#define UMOSCOM_IIR_RDA 0x04
+#define UMOSCOM_IIR_CTI 0x0c
+#define UMOSCOM_IIR_THR 0x02
+#define UMOSCOM_IIR_MS 0x00
+
+/* registers */
+#define UMOSCOM_READ 0x0d
+#define UMOSCOM_WRITE 0x0e
+#define UMOSCOM_UART_REG 0x0300
+#define UMOSCOM_VEND_REG 0x0000
+
+#define UMOSCOM_TXBUF 0x00 /* Write */
+#define UMOSCOM_RXBUF 0x00 /* Read */
+#define UMOSCOM_INT 0x01
+#define UMOSCOM_FIFO 0x02 /* Write */
+#define UMOSCOM_ISR 0x02 /* Read */
+#define UMOSCOM_LCR 0x03
+#define UMOSCOM_MCR 0x04
+#define UMOSCOM_LSR 0x05
+#define UMOSCOM_MSR 0x06
+#define UMOSCOM_SCRATCH 0x07
+#define UMOSCOM_DIV_LO 0x08
+#define UMOSCOM_DIV_HI 0x09
+#define UMOSCOM_EFR 0x0a
+#define UMOSCOM_XON1 0x0b
+#define UMOSCOM_XON2 0x0c
+#define UMOSCOM_XOFF1 0x0d
+#define UMOSCOM_XOFF2 0x0e
+
+#define UMOSCOM_BAUDLO 0x00
+#define UMOSCOM_BAUDHI 0x01
+
+#define UMOSCOM_INT_RXEN 0x01
+#define UMOSCOM_INT_TXEN 0x02
+#define UMOSCOM_INT_RSEN 0x04
+#define UMOSCOM_INT_MDMEM 0x08
+#define UMOSCOM_INT_SLEEP 0x10
+#define UMOSCOM_INT_XOFF 0x20
+#define UMOSCOM_INT_RTS 0x40
+
+#define UMOSCOM_FIFO_EN 0x01
+#define UMOSCOM_FIFO_RXCLR 0x02
+#define UMOSCOM_FIFO_TXCLR 0x04
+#define UMOSCOM_FIFO_DMA_BLK 0x08
+#define UMOSCOM_FIFO_TXLVL_MASK 0x30
+#define UMOSCOM_FIFO_TXLVL_8 0x00
+#define UMOSCOM_FIFO_TXLVL_16 0x10
+#define UMOSCOM_FIFO_TXLVL_32 0x20
+#define UMOSCOM_FIFO_TXLVL_56 0x30
+#define UMOSCOM_FIFO_RXLVL_MASK 0xc0
+#define UMOSCOM_FIFO_RXLVL_8 0x00
+#define UMOSCOM_FIFO_RXLVL_16 0x40
+#define UMOSCOM_FIFO_RXLVL_56 0x80
+#define UMOSCOM_FIFO_RXLVL_80 0xc0
+
+#define UMOSCOM_ISR_MDM 0x00
+#define UMOSCOM_ISR_NONE 0x01
+#define UMOSCOM_ISR_TX 0x02
+#define UMOSCOM_ISR_RX 0x04
+#define UMOSCOM_ISR_LINE 0x06
+#define UMOSCOM_ISR_RXTIMEOUT 0x0c
+#define UMOSCOM_ISR_RX_XOFF 0x10
+#define UMOSCOM_ISR_RTSCTS 0x20
+#define UMOSCOM_ISR_FIFOEN 0xc0
+
+#define UMOSCOM_LCR_DBITS(x) ((x) - 5)
+#define UMOSCOM_LCR_STOP_BITS_1 0x00
+#define UMOSCOM_LCR_STOP_BITS_2 0x04 /* 2 if 6-8 bits/char or 1.5 if 5 */
+#define UMOSCOM_LCR_PARITY_NONE 0x00
+#define UMOSCOM_LCR_PARITY_ODD 0x08
+#define UMOSCOM_LCR_PARITY_EVEN 0x18
+#define UMOSCOM_LCR_BREAK 0x40
+#define UMOSCOM_LCR_DIVLATCH_EN 0x80
+
+#define UMOSCOM_MCR_DTR 0x01
+#define UMOSCOM_MCR_RTS 0x02
+#define UMOSCOM_MCR_LOOP 0x04
+#define UMOSCOM_MCR_INTEN 0x08
+#define UMOSCOM_MCR_LOOPBACK 0x10
+#define UMOSCOM_MCR_XONANY 0x20
+#define UMOSCOM_MCR_IRDA_EN 0x40
+#define UMOSCOM_MCR_BAUD_DIV4 0x80
+
+#define UMOSCOM_LSR_RXDATA 0x01
+#define UMOSCOM_LSR_RXOVER 0x02
+#define UMOSCOM_LSR_RXPAR_ERR 0x04
+#define UMOSCOM_LSR_RXFRM_ERR 0x08
+#define UMOSCOM_LSR_RXBREAK 0x10
+#define UMOSCOM_LSR_TXEMPTY 0x20
+#define UMOSCOM_LSR_TXALLEMPTY 0x40
+#define UMOSCOM_LSR_TXFIFO_ERR 0x80
+
+#define UMOSCOM_MSR_CTS_CHG 0x01
+#define UMOSCOM_MSR_DSR_CHG 0x02
+#define UMOSCOM_MSR_RI_CHG 0x04
+#define UMOSCOM_MSR_CD_CHG 0x08
+#define UMOSCOM_MSR_CTS 0x10
+#define UMOSCOM_MSR_RTS 0x20
+#define UMOSCOM_MSR_RI 0x40
+#define UMOSCOM_MSR_CD 0x80
+
+#define UMOSCOM_BAUD_REF 115200
+
+enum {
+ UMOSCOM_BULK_DT_WR,
+ UMOSCOM_BULK_DT_RD,
+ UMOSCOM_INTR_DT_RD,
+ UMOSCOM_N_TRANSFER,
+};
+
+struct umoscom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UMOSCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_mcr;
+ uint8_t sc_lcr;
+};
+
+/* prototypes */
+
+static device_probe_t umoscom_probe;
+static device_attach_t umoscom_attach;
+static device_detach_t umoscom_detach;
+
+static usb2_callback_t umoscom_write_callback;
+static usb2_callback_t umoscom_read_callback;
+static usb2_callback_t umoscom_intr_callback;
+
+static void umoscom_cfg_open(struct usb2_com_softc *);
+static void umoscom_cfg_close(struct usb2_com_softc *);
+static void umoscom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static void umoscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void umoscom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static int umoscom_pre_param(struct usb2_com_softc *, struct termios *);
+static void umoscom_cfg_param(struct usb2_com_softc *, struct termios *);
+static void umoscom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t);
+static uint8_t umoscom_cfg_read(struct umoscom_softc *, uint16_t);
+static void umoscom_start_read(struct usb2_com_softc *);
+static void umoscom_stop_read(struct usb2_com_softc *);
+static void umoscom_start_write(struct usb2_com_softc *);
+static void umoscom_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config umoscom_config_data[UMOSCOM_N_TRANSFER] = {
+
+ [UMOSCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UMOSCOM_BUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &umoscom_write_callback,
+ },
+
+ [UMOSCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UMOSCOM_BUFSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &umoscom_read_callback,
+ },
+
+ [UMOSCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &umoscom_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback umoscom_callback = {
+ /* configuration callbacks */
+ .usb2_com_cfg_get_status = &umoscom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &umoscom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &umoscom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &umoscom_cfg_set_break,
+ .usb2_com_cfg_param = &umoscom_cfg_param,
+ .usb2_com_cfg_open = &umoscom_cfg_open,
+ .usb2_com_cfg_close = &umoscom_cfg_close,
+
+ /* other callbacks */
+ .usb2_com_pre_param = &umoscom_pre_param,
+ .usb2_com_start_read = &umoscom_start_read,
+ .usb2_com_stop_read = &umoscom_stop_read,
+ .usb2_com_start_write = &umoscom_start_write,
+ .usb2_com_stop_write = &umoscom_stop_write,
+};
+
+static device_method_t umoscom_methods[] = {
+ DEVMETHOD(device_probe, umoscom_probe),
+ DEVMETHOD(device_attach, umoscom_attach),
+ DEVMETHOD(device_detach, umoscom_detach),
+ {0, 0}
+};
+
+static devclass_t umoscom_devclass;
+
+static driver_t umoscom_driver = {
+ .name = "umoscom",
+ .methods = umoscom_methods,
+ .size = sizeof(struct umoscom_softc),
+};
+
+DRIVER_MODULE(umoscom, ushub, umoscom_driver, umoscom_devclass, NULL, 0);
+MODULE_DEPEND(umoscom, ucom, 1, 1, 1);
+MODULE_DEPEND(umoscom, usb, 1, 1, 1);
+
+static const struct usb2_device_id umoscom_devs[] = {
+ {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)}
+};
+
+static int
+umoscom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa));
+}
+
+static int
+umoscom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umoscom_softc *sc = device_get_softc(dev);
+ int error;
+ uint8_t iface_index;
+
+ sc->sc_udev = uaa->device;
+ sc->sc_mcr = 0x08; /* enable interrupts */
+
+ /* XXX the device doesn't provide any ID string, so set a static one */
+ device_set_desc(dev, "MOSCHIP USB Serial Port Adapter");
+ device_printf(dev, "<MOSCHIP USB Serial Port Adapter>\n");
+
+ iface_index = UMOSCOM_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, umoscom_config_data,
+ UMOSCOM_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &umoscom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ device_printf(dev, "attach error: %s\n", usb2_errstr(error));
+ umoscom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+umoscom_detach(device_t dev)
+{
+ struct umoscom_softc *sc = device_get_softc(dev);
+
+ mtx_lock(&Giant);
+
+ mtx_unlock(&Giant);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+umoscom_cfg_open(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ /* Purge FIFOs or odd things happen */
+ umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG);
+
+ /* Enable FIFO */
+ umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN |
+ UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR |
+ UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK |
+ UMOSCOM_UART_REG);
+
+ /* Enable Interrupt Registers */
+ umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG);
+
+ /* Magic */
+ umoscom_cfg_write(sc, 0x01, 0x08);
+
+ /* Magic */
+ umoscom_cfg_write(sc, 0x00, 0x02);
+}
+
+static void
+umoscom_cfg_close(struct usb2_com_softc *ucom)
+{
+ return;
+}
+
+static void
+umoscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+ uint16_t val;
+
+ val = sc->sc_lcr;
+ if (onoff)
+ val |= UMOSCOM_LCR_BREAK;
+
+ umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG);
+}
+
+static void
+umoscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ if (onoff)
+ sc->sc_mcr |= UMOSCOM_MCR_DTR;
+ else
+ sc->sc_mcr &= ~UMOSCOM_MCR_DTR;
+
+ umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
+}
+
+static void
+umoscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ if (onoff)
+ sc->sc_mcr |= UMOSCOM_MCR_RTS;
+ else
+ sc->sc_mcr &= ~UMOSCOM_MCR_RTS;
+
+ umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG);
+}
+
+static int
+umoscom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200))
+ return (EINVAL);
+
+ return (0);
+}
+
+static void
+umoscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+ uint16_t data;
+
+ DPRINTF("speed=%d\n", t->c_ospeed);
+
+ data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed);
+
+ if (data == 0) {
+ DPRINTF("invalid baud rate!\n");
+ return;
+ }
+ umoscom_cfg_write(sc, UMOSCOM_LCR,
+ UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG);
+
+ umoscom_cfg_write(sc, UMOSCOM_BAUDLO,
+ (data & 0xFF) | UMOSCOM_UART_REG);
+
+ umoscom_cfg_write(sc, UMOSCOM_BAUDHI,
+ ((data >> 8) & 0xFF) | UMOSCOM_UART_REG);
+
+ if (t->c_cflag & CSTOPB)
+ data = UMOSCOM_LCR_STOP_BITS_2;
+ else
+ data = UMOSCOM_LCR_STOP_BITS_1;
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD)
+ data |= UMOSCOM_LCR_PARITY_ODD;
+ else
+ data |= UMOSCOM_LCR_PARITY_EVEN;
+ } else
+ data |= UMOSCOM_LCR_PARITY_NONE;
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ data |= UMOSCOM_LCR_DBITS(5);
+ break;
+ case CS6:
+ data |= UMOSCOM_LCR_DBITS(6);
+ break;
+ case CS7:
+ data |= UMOSCOM_LCR_DBITS(7);
+ break;
+ case CS8:
+ data |= UMOSCOM_LCR_DBITS(8);
+ break;
+ }
+
+ sc->sc_lcr = data;
+ umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG);
+}
+
+static void
+umoscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+ uint8_t lsr;
+ uint8_t msr;
+
+ DPRINTFN(5, "\n");
+
+ /* read status registers */
+
+ lsr = umoscom_cfg_read(sc, UMOSCOM_LSR);
+ msr = umoscom_cfg_read(sc, UMOSCOM_MSR);
+
+ /* translate bits */
+
+ if (msr & UMOSCOM_MSR_CTS)
+ *p_msr |= SER_CTS;
+
+ if (msr & UMOSCOM_MSR_CD)
+ *p_msr |= SER_DCD;
+
+ if (msr & UMOSCOM_MSR_RI)
+ *p_msr |= SER_RI;
+
+ if (msr & UMOSCOM_MSR_RTS)
+ *p_msr |= SER_DSR;
+}
+
+static void
+umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UMOSCOM_WRITE;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static uint8_t
+umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ uint8_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UMOSCOM_READ;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 1);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, &val, 0, 1000);
+
+ DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val);
+
+ return (val);
+}
+
+static void
+umoscom_start_read(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+#if 0
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
+#endif
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+}
+
+static void
+umoscom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt transfer */
+ usb2_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
+}
+
+static void
+umoscom_start_write(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+}
+
+static void
+umoscom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct umoscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
+}
+
+static void
+umoscom_write_callback(struct usb2_xfer *xfer)
+{
+ struct umoscom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ DPRINTF("\n");
+
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UMOSCOM_BUFSIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ DPRINTFN(0, "transfer failed\n");
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umoscom_read_callback(struct usb2_xfer *xfer)
+{
+ struct umoscom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("got %d bytes\n", xfer->actlen);
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ DPRINTF("\n");
+
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ DPRINTFN(0, "transfer failed\n");
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+umoscom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct umoscom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen < 2) {
+ DPRINTF("too short message\n");
+ goto tr_setup;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ DPRINTFN(0, "transfer failed\n");
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/uplcom.c b/sys/dev/usb/serial/uplcom.c
new file mode 100644
index 0000000..5f37417
--- /dev/null
+++ b/sys/dev/usb/serial/uplcom.c
@@ -0,0 +1,831 @@
+/* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Ichiro FUKUHARA (ichiro@ichiro.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This driver supports several USB-to-RS232 serial adapters driven by
+ * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
+ * bridge chip. The adapters are sold under many different brand
+ * names.
+ *
+ * Datasheets are available at Prolific www site at
+ * http://www.prolific.com.tw. The datasheets don't contain full
+ * programming information for the chip.
+ *
+ * PL-2303HX is probably programmed the same as PL-2303X.
+ *
+ * There are several differences between PL-2303 and PL-2303(H)X.
+ * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
+ * different command for controlling CRTSCTS and needs special
+ * sequence of commands for initialization which aren't also
+ * documented in the datasheet.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR uplcom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uplcom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uplcom, CTLFLAG_RW, 0, "USB uplcom");
+SYSCTL_INT(_hw_usb2_uplcom, OID_AUTO, debug, CTLFLAG_RW,
+ &uplcom_debug, 0, "Debug level");
+#endif
+
+#define UPLCOM_MODVER 1 /* module version */
+
+#define UPLCOM_CONFIG_INDEX 0
+#define UPLCOM_IFACE_INDEX 0
+#define UPLCOM_SECOND_IFACE_INDEX 1
+
+#ifndef UPLCOM_INTR_INTERVAL
+#define UPLCOM_INTR_INTERVAL 0 /* default */
+#endif
+
+#define UPLCOM_BULK_BUF_SIZE 1024 /* bytes */
+
+#define UPLCOM_SET_REQUEST 0x01
+#define UPLCOM_SET_CRTSCTS 0x41
+#define UPLCOM_SET_CRTSCTS_PL2303X 0x61
+#define RSAQ_STATUS_CTS 0x80
+#define RSAQ_STATUS_DSR 0x02
+#define RSAQ_STATUS_DCD 0x01
+
+#define TYPE_PL2303 0
+#define TYPE_PL2303X 1
+
+enum {
+ UPLCOM_BULK_DT_WR,
+ UPLCOM_BULK_DT_RD,
+ UPLCOM_INTR_DT_RD,
+ UPLCOM_N_TRANSFER,
+};
+
+struct uplcom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UPLCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint16_t sc_line;
+
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* uplcom status register */
+ uint8_t sc_chiptype; /* type of chip */
+ uint8_t sc_ctrl_iface_no;
+ uint8_t sc_data_iface_no;
+ uint8_t sc_iface_index[2];
+};
+
+/* prototypes */
+
+static usb2_error_t uplcom_reset(struct uplcom_softc *, struct usb2_device *);
+static int uplcom_pl2303x_init(struct usb2_device *);
+static void uplcom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uplcom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uplcom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int uplcom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uplcom_cfg_param(struct usb2_com_softc *, struct termios *);
+static void uplcom_start_read(struct usb2_com_softc *);
+static void uplcom_stop_read(struct usb2_com_softc *);
+static void uplcom_start_write(struct usb2_com_softc *);
+static void uplcom_stop_write(struct usb2_com_softc *);
+static void uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+
+static device_probe_t uplcom_probe;
+static device_attach_t uplcom_attach;
+static device_detach_t uplcom_detach;
+
+static usb2_callback_t uplcom_intr_callback;
+static usb2_callback_t uplcom_write_callback;
+static usb2_callback_t uplcom_read_callback;
+
+static const struct usb2_config uplcom_config_data[UPLCOM_N_TRANSFER] = {
+
+ [UPLCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UPLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uplcom_write_callback,
+ .if_index = 0,
+ },
+
+ [UPLCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UPLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uplcom_read_callback,
+ .if_index = 0,
+ },
+
+ [UPLCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uplcom_intr_callback,
+ .if_index = 1,
+ },
+};
+
+struct usb2_com_callback uplcom_callback = {
+ .usb2_com_cfg_get_status = &uplcom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uplcom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uplcom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uplcom_cfg_set_break,
+ .usb2_com_cfg_param = &uplcom_cfg_param,
+ .usb2_com_pre_param = &uplcom_pre_param,
+ .usb2_com_start_read = &uplcom_start_read,
+ .usb2_com_stop_read = &uplcom_stop_read,
+ .usb2_com_start_write = &uplcom_start_write,
+ .usb2_com_stop_write = &uplcom_stop_write,
+};
+
+#define USB_UPL(v,p,rl,rh,t) \
+ USB_VENDOR(v), USB_PRODUCT(p), USB_DEV_BCD_GTEQ(rl), \
+ USB_DEV_BCD_LTEQ(rh), USB_DRIVER_INFO(t)
+
+static const struct usb2_device_id uplcom_devs[] = {
+ /* Belkin F5U257 */
+ {USB_UPL(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U257, 0, 0xFFFF, TYPE_PL2303X)},
+ /* I/O DATA USB-RSAQ */
+ {USB_UPL(USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, 0, 0xFFFF, TYPE_PL2303)},
+ /* I/O DATA USB-RSAQ2 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, 0, 0xFFFF, TYPE_PL2303)},
+ /* I/O DATA USB-RSAQ3 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, 0, 0xFFFF, TYPE_PL2303X)},
+ /* PLANEX USB-RS232 URS-03 */
+ {USB_UPL(USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, 0, 0xFFFF, TYPE_PL2303)},
+ /* TrendNet TU-S9 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0400, 0xFFFF, TYPE_PL2303X)},
+ /* ST Lab USB-SERIAL-4 */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0x0300, 0x03FF, TYPE_PL2303X)},
+ /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, 0, 0x02FF, TYPE_PL2303)},
+ /* TDK USB-PHS Adapter UHA6400 */
+ {USB_UPL(USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, 0, 0xFFFF, TYPE_PL2303)},
+ /* RATOC REX-USB60 */
+ {USB_UPL(USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, 0, 0xFFFF, TYPE_PL2303)},
+ /* ELECOM UC-SGT */
+ {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, 0, 0xFFFF, TYPE_PL2303)},
+ {USB_UPL(USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, 0, 0xFFFF, TYPE_PL2303)},
+ /* Sagem USB-Serial Controller */
+ {USB_UPL(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_USBSERIAL, 0, 0xFFFF, TYPE_PL2303X)},
+ /* Sony Ericsson USB Cable */
+ {USB_UPL(USB_VENDOR_SONYERICSSON, USB_PRODUCT_SONYERICSSON_DCU10, 0, 0xFFFF, TYPE_PL2303)},
+ /* SOURCENEXT KeikaiDenwa 8 */
+ {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8, 0, 0xFFFF, TYPE_PL2303)},
+ /* SOURCENEXT KeikaiDenwa 8 with charger */
+ {USB_UPL(USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG, 0, 0, TYPE_PL2303)},
+ /* HAL Corporation Crossam2+USB */
+ {USB_UPL(USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, 0, 0xFFFF, TYPE_PL2303)},
+ /* Sitecom USB to Serial */
+ {USB_UPL(USB_VENDOR_SITECOM, USB_PRODUCT_SITECOM_SERIAL, 0, 0xFFFF, TYPE_PL2303)},
+ /* Tripp-Lite U209-000-R */
+ {USB_UPL(USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, 0, 0xFFFF, TYPE_PL2303X)},
+ {USB_UPL(USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, 0, 0xFFFF, TYPE_PL2303)},
+ /* Prolific Pharos */
+ {USB_UPL(USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PHAROS, 0, 0xFFFF, TYPE_PL2303)},
+ /* Willcom W-SIM */
+ {USB_UPL(USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, 0, 0xFFFF, TYPE_PL2303X)},
+};
+
+static device_method_t uplcom_methods[] = {
+ DEVMETHOD(device_probe, uplcom_probe),
+ DEVMETHOD(device_attach, uplcom_attach),
+ DEVMETHOD(device_detach, uplcom_detach),
+ {0, 0}
+};
+
+static devclass_t uplcom_devclass;
+
+static driver_t uplcom_driver = {
+ .name = "uplcom",
+ .methods = uplcom_methods,
+ .size = sizeof(struct uplcom_softc),
+};
+
+DRIVER_MODULE(uplcom, ushub, uplcom_driver, uplcom_devclass, NULL, 0);
+MODULE_DEPEND(uplcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uplcom, usb, 1, 1, 1);
+MODULE_VERSION(uplcom, UPLCOM_MODVER);
+
+static int
+uplcom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa));
+}
+
+static int
+uplcom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uplcom_softc *sc = device_get_softc(dev);
+ struct usb2_interface *iface;
+ struct usb2_interface_descriptor *id;
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ device_set_usb2_desc(dev);
+
+ DPRINTF("sc = %p\n", sc);
+
+ sc->sc_chiptype = USB_GET_DRIVER_INFO(uaa);
+ sc->sc_udev = uaa->device;
+
+ DPRINTF("chiptype: %s\n",
+ (sc->sc_chiptype == TYPE_PL2303X) ?
+ "2303X" : "2303");
+
+ /*
+ * USB-RSAQ1 has two interface
+ *
+ * USB-RSAQ1 | USB-RSAQ2
+ * -----------------+-----------------
+ * Interface 0 |Interface 0
+ * Interrupt(0x81) | Interrupt(0x81)
+ * -----------------+ BulkIN(0x02)
+ * Interface 1 | BulkOUT(0x83)
+ * BulkIN(0x02) |
+ * BulkOUT(0x83) |
+ */
+
+ sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX;
+
+ iface = usb2_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX);
+ if (iface) {
+ id = usb2_get_interface_descriptor(iface);
+ if (id == NULL) {
+ device_printf(dev, "no interface descriptor (2)!\n");
+ goto detach;
+ }
+ sc->sc_data_iface_no = id->bInterfaceNumber;
+ sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX;
+ usb2_set_parent_iface(uaa->device,
+ UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex);
+ } else {
+ sc->sc_data_iface_no = sc->sc_ctrl_iface_no;
+ sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX;
+ }
+
+ error = usb2_transfer_setup(uaa->device,
+ sc->sc_iface_index, sc->sc_xfer, uplcom_config_data,
+ UPLCOM_N_TRANSFER, sc, &Giant);
+ if (error) {
+ DPRINTF("one or more missing USB endpoints, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ error = uplcom_reset(sc, uaa->device);
+ if (error) {
+ device_printf(dev, "reset failed, error=%s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uplcom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ /*
+ * do the initialization during attach so that the system does not
+ * sleep during open:
+ */
+ if (sc->sc_chiptype == TYPE_PL2303X) {
+ if (uplcom_pl2303x_init(uaa->device)) {
+ device_printf(dev, "init failed!\n");
+ goto detach;
+ }
+ }
+ return (0);
+
+detach:
+ uplcom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uplcom_detach(device_t dev)
+{
+ struct uplcom_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static usb2_error_t
+uplcom_reset(struct uplcom_softc *sc, struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_data_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ return (usb2_do_request(udev, &Giant, &req, NULL));
+}
+
+struct pl2303x_init {
+ uint8_t req_type;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+};
+
+static const struct pl2303x_init pl2303x[] = {
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1},
+ {UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0},
+ {UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0},
+};
+
+#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0]))
+
+static int
+uplcom_pl2303x_init(struct usb2_device *udev)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t buf[4];
+ uint8_t i;
+
+ for (i = 0; i != N_PL2302X_INIT; i++) {
+ req.bmRequestType = pl2303x[i].req_type;
+ req.bRequest = pl2303x[i].request;
+ USETW(req.wValue, pl2303x[i].value);
+ USETW(req.wIndex, pl2303x[i].index);
+ USETW(req.wLength, pl2303x[i].length);
+
+ err = usb2_do_request(udev, &Giant, &req, buf);
+ if (err) {
+ DPRINTF("error=%s\n", usb2_errstr(err));
+ return (EIO);
+ }
+ }
+ return (0);
+}
+
+static void
+uplcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_DTR;
+ else
+ sc->sc_line &= ~UCDC_LINE_DTR;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = sc->sc_data_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uplcom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UCDC_LINE_RTS;
+ else
+ sc->sc_line &= ~UCDC_LINE_RTS;
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
+ USETW(req.wValue, sc->sc_line);
+ req.wIndex[0] = sc->sc_data_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static void
+uplcom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t temp;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SEND_BREAK;
+ USETW(req.wValue, temp);
+ req.wIndex[0] = sc->sc_data_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+}
+
+static const int32_t uplcom_rates[] = {
+ 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
+ 19200, 28800, 38400, 57600, 115200,
+ /*
+ * Higher speeds are probably possible. PL2303X supports up to
+ * 6Mb and can set any rate
+ */
+ 230400, 460800, 614400, 921600, 1228800
+};
+
+#define N_UPLCOM_RATES (sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
+
+static int
+uplcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ uint8_t i;
+
+ DPRINTF("\n");
+
+ /* check requested baud rate */
+
+ for (i = 0;; i++) {
+
+ if (i != N_UPLCOM_RATES) {
+ if (uplcom_rates[i] == t->c_ospeed) {
+ break;
+ }
+ } else {
+ DPRINTF("invalid baud rate (%d)\n", t->c_ospeed);
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
+static void
+uplcom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+ struct usb2_cdc_line_state ls;
+ struct usb2_device_request req;
+
+ DPRINTF("sc = %p\n", sc);
+
+ bzero(&ls, sizeof(ls));
+
+ USETDW(ls.dwDTERate, t->c_ospeed);
+
+ if (t->c_cflag & CSTOPB) {
+ ls.bCharFormat = UCDC_STOP_BIT_2;
+ } else {
+ ls.bCharFormat = UCDC_STOP_BIT_1;
+ }
+
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD) {
+ ls.bParityType = UCDC_PARITY_ODD;
+ } else {
+ ls.bParityType = UCDC_PARITY_EVEN;
+ }
+ } else {
+ ls.bParityType = UCDC_PARITY_NONE;
+ }
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ ls.bDataBits = 5;
+ break;
+ case CS6:
+ ls.bDataBits = 6;
+ break;
+ case CS7:
+ ls.bDataBits = 7;
+ break;
+ case CS8:
+ ls.bDataBits = 8;
+ break;
+ }
+
+ DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n",
+ UGETDW(ls.dwDTERate), ls.bCharFormat,
+ ls.bParityType, ls.bDataBits);
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UCDC_SET_LINE_CODING;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_data_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, UCDC_LINE_STATE_LENGTH);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, &ls, 0, 1000);
+
+ if (t->c_cflag & CRTSCTS) {
+
+ DPRINTF("crtscts = on\n");
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ if (sc->sc_chiptype == TYPE_PL2303X)
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
+ else
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
+ USETW(req.wLength, 0);
+
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ } else {
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = UPLCOM_SET_REQUEST;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+ usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ }
+}
+
+static void
+uplcom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ /* start interrupt endpoint */
+ usb2_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+}
+
+static void
+uplcom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ /* stop interrupt endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]);
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
+}
+
+static void
+uplcom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+}
+
+static void
+uplcom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
+}
+
+static void
+uplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uplcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uplcom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uplcom_softc *sc = xfer->priv_sc;
+ uint8_t buf[9];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen = %u\n", xfer->actlen);
+
+ if (xfer->actlen >= 9) {
+
+ usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
+
+ DPRINTF("status = 0x%02x\n", buf[8]);
+
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+
+ if (buf[8] & RSAQ_STATUS_CTS) {
+ sc->sc_msr |= SER_CTS;
+ }
+ if (buf[8] & RSAQ_STATUS_DSR) {
+ sc->sc_msr |= SER_DSR;
+ }
+ if (buf[8] & RSAQ_STATUS_DCD) {
+ sc->sc_msr |= SER_DCD;
+ }
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uplcom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uplcom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UPLCOM_BULK_BUF_SIZE, &actlen)) {
+
+ DPRINTF("actlen = %d\n", actlen);
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uplcom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uplcom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/usb_serial.c b/sys/dev/usb/serial/usb_serial.c
new file mode 100644
index 0000000..38fd818
--- /dev/null
+++ b/sys/dev/usb/serial/usb_serial.c
@@ -0,0 +1,1127 @@
+/* $NetBSD: ucom.c,v 1.40 2001/11/13 06:24:54 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2001-2003, 2005, 2008
+ * Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR usb2_com_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int usb2_com_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ucom, CTLFLAG_RW, 0, "USB ucom");
+SYSCTL_INT(_hw_usb2_ucom, OID_AUTO, debug, CTLFLAG_RW,
+ &usb2_com_debug, 0, "ucom debug level");
+#endif
+
+static usb2_proc_callback_t usb2_com_cfg_start_transfers;
+static usb2_proc_callback_t usb2_com_cfg_open;
+static usb2_proc_callback_t usb2_com_cfg_close;
+static usb2_proc_callback_t usb2_com_cfg_line_state;
+static usb2_proc_callback_t usb2_com_cfg_status_change;
+static usb2_proc_callback_t usb2_com_cfg_param;
+
+static uint8_t usb2_com_units_alloc(uint32_t, uint32_t *);
+static void usb2_com_units_free(uint32_t, uint32_t);
+static int usb2_com_attach_tty(struct usb2_com_softc *, uint32_t);
+static void usb2_com_detach_tty(struct usb2_com_softc *);
+static void usb2_com_queue_command(struct usb2_com_softc *,
+ usb2_proc_callback_t *, struct termios *pt,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1);
+static void usb2_com_shutdown(struct usb2_com_softc *);
+static void usb2_com_break(struct usb2_com_softc *, uint8_t);
+static void usb2_com_dtr(struct usb2_com_softc *, uint8_t);
+static void usb2_com_rts(struct usb2_com_softc *, uint8_t);
+
+static tsw_open_t usb2_com_open;
+static tsw_close_t usb2_com_close;
+static tsw_ioctl_t usb2_com_ioctl;
+static tsw_modem_t usb2_com_modem;
+static tsw_param_t usb2_com_param;
+static tsw_outwakeup_t usb2_com_outwakeup;
+static tsw_free_t usb2_com_free;
+
+static struct ttydevsw usb2_com_class = {
+ .tsw_flags = TF_INITLOCK | TF_CALLOUT,
+ .tsw_open = usb2_com_open,
+ .tsw_close = usb2_com_close,
+ .tsw_outwakeup = usb2_com_outwakeup,
+ .tsw_ioctl = usb2_com_ioctl,
+ .tsw_param = usb2_com_param,
+ .tsw_modem = usb2_com_modem,
+ .tsw_free = usb2_com_free,
+};
+
+MODULE_DEPEND(ucom, usb, 1, 1, 1);
+MODULE_VERSION(ucom, 1);
+
+#define UCOM_UNIT_MAX 0x1000 /* exclusive */
+#define UCOM_SUB_UNIT_MAX 0x100 /* exclusive */
+
+static uint8_t usb2_com_bitmap[(UCOM_UNIT_MAX + 7) / 8];
+
+static uint8_t
+usb2_com_units_alloc(uint32_t sub_units, uint32_t *p_root_unit)
+{
+ uint32_t n;
+ uint32_t o;
+ uint32_t x;
+ uint32_t max = UCOM_UNIT_MAX - (UCOM_UNIT_MAX % sub_units);
+ uint8_t error = 1;
+
+ mtx_lock(&Giant);
+
+ for (n = 0; n < max; n += sub_units) {
+
+ /* check for free consecutive bits */
+
+ for (o = 0; o < sub_units; o++) {
+
+ x = n + o;
+
+ if (usb2_com_bitmap[x / 8] & (1 << (x % 8))) {
+ goto skip;
+ }
+ }
+
+ /* allocate */
+
+ for (o = 0; o < sub_units; o++) {
+
+ x = n + o;
+
+ usb2_com_bitmap[x / 8] |= (1 << (x % 8));
+ }
+
+ error = 0;
+
+ break;
+
+skip: ;
+ }
+
+ mtx_unlock(&Giant);
+
+ /*
+ * Always set the variable pointed to by "p_root_unit" so that
+ * the compiler does not think that it is used uninitialised:
+ */
+ *p_root_unit = n;
+
+ return (error);
+}
+
+static void
+usb2_com_units_free(uint32_t root_unit, uint32_t sub_units)
+{
+ uint32_t x;
+
+ mtx_lock(&Giant);
+
+ while (sub_units--) {
+ x = root_unit + sub_units;
+ usb2_com_bitmap[x / 8] &= ~(1 << (x % 8));
+ }
+
+ mtx_unlock(&Giant);
+}
+
+/*
+ * "N" sub_units are setup at a time. All sub-units will
+ * be given sequential unit numbers. The number of
+ * sub-units can be used to differentiate among
+ * different types of devices.
+ *
+ * The mutex pointed to by "mtx" is applied before all
+ * callbacks are called back. Also "mtx" must be applied
+ * before calling into the ucom-layer!
+ */
+int
+usb2_com_attach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
+ uint32_t sub_units, void *parent,
+ const struct usb2_com_callback *callback, struct mtx *mtx)
+{
+ uint32_t n;
+ uint32_t root_unit;
+ int error = 0;
+
+ if ((sc == NULL) ||
+ (sub_units == 0) ||
+ (sub_units > UCOM_SUB_UNIT_MAX) ||
+ (callback == NULL)) {
+ return (EINVAL);
+ }
+
+ /* XXX unit management does not really belong here */
+ if (usb2_com_units_alloc(sub_units, &root_unit)) {
+ return (ENOMEM);
+ }
+
+ error = usb2_proc_create(&ssc->sc_tq, mtx, "ucom", USB_PRI_MED);
+ if (error) {
+ usb2_com_units_free(root_unit, sub_units);
+ return (error);
+ }
+
+ for (n = 0; n != sub_units; n++, sc++) {
+ sc->sc_unit = root_unit + n;
+ sc->sc_local_unit = n;
+ sc->sc_super = ssc;
+ sc->sc_mtx = mtx;
+ sc->sc_parent = parent;
+ sc->sc_callback = callback;
+
+ error = usb2_com_attach_tty(sc, sub_units);
+ if (error) {
+ usb2_com_detach(ssc, sc - n, n);
+ usb2_com_units_free(root_unit + n, sub_units - n);
+ return (error);
+ }
+ sc->sc_flag |= UCOM_FLAG_ATTACHED;
+ }
+ return (0);
+}
+
+/*
+ * NOTE: the following function will do nothing if
+ * the structure pointed to by "ssc" and "sc" is zero.
+ */
+void
+usb2_com_detach(struct usb2_com_super_softc *ssc, struct usb2_com_softc *sc,
+ uint32_t sub_units)
+{
+ uint32_t n;
+
+ usb2_proc_drain(&ssc->sc_tq);
+
+ for (n = 0; n != sub_units; n++, sc++) {
+ if (sc->sc_flag & UCOM_FLAG_ATTACHED) {
+
+ usb2_com_detach_tty(sc);
+
+ usb2_com_units_free(sc->sc_unit, 1);
+
+ /* avoid duplicate detach: */
+ sc->sc_flag &= ~UCOM_FLAG_ATTACHED;
+ }
+ }
+ usb2_proc_free(&ssc->sc_tq);
+}
+
+static int
+usb2_com_attach_tty(struct usb2_com_softc *sc, uint32_t sub_units)
+{
+ struct tty *tp;
+ int error = 0;
+ char buf[32]; /* temporary TTY device name buffer */
+
+ tp = tty_alloc(&usb2_com_class, sc, sc->sc_mtx);
+ if (tp == NULL) {
+ error = ENOMEM;
+ goto done;
+ }
+ DPRINTF("tp = %p, unit = %d\n", tp, sc->sc_unit);
+
+ buf[0] = 0; /* set some default value */
+
+ /* Check if the client has a custom TTY name */
+ if (sc->sc_callback->usb2_com_tty_name) {
+ sc->sc_callback->usb2_com_tty_name(sc, buf,
+ sizeof(buf), sc->sc_local_unit);
+ }
+ if (buf[0] == 0) {
+ /* Use default TTY name */
+ if (sub_units > 1) {
+ /* multiple modems in one */
+ if (snprintf(buf, sizeof(buf), "U%u.%u",
+ sc->sc_unit - sc->sc_local_unit,
+ sc->sc_local_unit)) {
+ /* ignore */
+ }
+ } else {
+ /* single modem */
+ if (snprintf(buf, sizeof(buf), "U%u", sc->sc_unit)) {
+ /* ignore */
+ }
+ }
+ }
+ tty_makedev(tp, NULL, "%s", buf);
+
+ sc->sc_tty = tp;
+
+ DPRINTF("ttycreate: %s\n", buf);
+ usb2_cv_init(&sc->sc_cv, "usb2_com");
+
+done:
+ return (error);
+}
+
+static void
+usb2_com_detach_tty(struct usb2_com_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ DPRINTF("sc = %p, tp = %p\n", sc, sc->sc_tty);
+
+ /* the config thread has been stopped when we get here */
+
+ mtx_lock(sc->sc_mtx);
+ sc->sc_flag |= UCOM_FLAG_GONE;
+ sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
+ UCOM_FLAG_LL_READY);
+ mtx_unlock(sc->sc_mtx);
+ if (tp) {
+ tty_lock(tp);
+
+ usb2_com_close(tp); /* close, if any */
+
+ tty_rel_gone(tp);
+
+ mtx_lock(sc->sc_mtx);
+ /* Wait for the callback after the TTY is torn down */
+ while (sc->sc_ttyfreed == 0)
+ usb2_cv_wait(&sc->sc_cv, sc->sc_mtx);
+ /*
+ * make sure that read and write transfers are stopped
+ */
+ if (sc->sc_callback->usb2_com_stop_read) {
+ (sc->sc_callback->usb2_com_stop_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_stop_write) {
+ (sc->sc_callback->usb2_com_stop_write) (sc);
+ }
+ mtx_unlock(sc->sc_mtx);
+ }
+ usb2_cv_destroy(&sc->sc_cv);
+}
+
+static void
+usb2_com_queue_command(struct usb2_com_softc *sc,
+ usb2_proc_callback_t *fn, struct termios *pt,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct usb2_com_super_softc *ssc = sc->sc_super;
+ struct usb2_com_param_task *task;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (usb2_proc_is_gone(&ssc->sc_tq)) {
+ DPRINTF("proc is gone\n");
+ return; /* nothing to do */
+ }
+ /*
+ * NOTE: The task cannot get executed before we drop the
+ * "sc_mtx" mutex. It is safe to update fields in the message
+ * structure after that the message got queued.
+ */
+ task = (struct usb2_com_param_task *)
+ usb2_proc_msignal(&ssc->sc_tq, t0, t1);
+
+ /* Setup callback and softc pointers */
+ task->hdr.pm_callback = fn;
+ task->sc = sc;
+
+ /*
+ * Make a copy of the termios. This field is only present if
+ * the "pt" field is not NULL.
+ */
+ if (pt != NULL)
+ task->termios_copy = *pt;
+
+ /*
+ * Closing the device should be synchronous.
+ */
+ if (fn == usb2_com_cfg_close)
+ usb2_proc_mwait(&ssc->sc_tq, t0, t1);
+
+}
+
+static void
+usb2_com_shutdown(struct usb2_com_softc *sc)
+{
+ struct tty *tp = sc->sc_tty;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ DPRINTF("\n");
+
+ /*
+ * Hang up if necessary:
+ */
+ if (tp->t_termios.c_cflag & HUPCL) {
+ usb2_com_modem(tp, 0, SER_DTR);
+ }
+}
+
+/*
+ * Return values:
+ * 0: normal
+ * else: taskqueue is draining or gone
+ */
+uint8_t
+usb2_com_cfg_is_gone(struct usb2_com_softc *sc)
+{
+ struct usb2_com_super_softc *ssc = sc->sc_super;
+
+ return (usb2_proc_is_gone(&ssc->sc_tq));
+}
+
+static void
+usb2_com_cfg_start_transfers(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+
+ if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+ return;
+ }
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ /* TTY device closed */
+ return;
+ }
+ sc->sc_flag |= UCOM_FLAG_GP_DATA;
+
+ if (sc->sc_callback->usb2_com_start_read) {
+ (sc->sc_callback->usb2_com_start_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_start_write) {
+ (sc->sc_callback->usb2_com_start_write) (sc);
+ }
+}
+
+static void
+usb2_com_start_transfers(struct usb2_com_softc *sc)
+{
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return;
+ }
+ /*
+ * Make sure that data transfers are started in both
+ * directions:
+ */
+ if (sc->sc_callback->usb2_com_start_read) {
+ (sc->sc_callback->usb2_com_start_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_start_write) {
+ (sc->sc_callback->usb2_com_start_write) (sc);
+ }
+}
+
+static void
+usb2_com_cfg_open(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+
+ DPRINTF("\n");
+
+ if (sc->sc_flag & UCOM_FLAG_LL_READY) {
+
+ /* already opened */
+
+ } else {
+
+ sc->sc_flag |= UCOM_FLAG_LL_READY;
+
+ if (sc->sc_callback->usb2_com_cfg_open) {
+ (sc->sc_callback->usb2_com_cfg_open) (sc);
+
+ /* wait a little */
+ usb2_pause_mtx(sc->sc_mtx, hz / 10);
+ }
+ }
+}
+
+static int
+usb2_com_open(struct tty *tp)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ int error;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (sc->sc_flag & UCOM_FLAG_GONE) {
+ return (ENXIO);
+ }
+ if (sc->sc_flag & UCOM_FLAG_HL_READY) {
+ /* already opened */
+ return (0);
+ }
+ DPRINTF("tp = %p\n", tp);
+
+ if (sc->sc_callback->usb2_com_pre_open) {
+ /*
+ * give the lower layer a chance to disallow TTY open, for
+ * example if the device is not present:
+ */
+ error = (sc->sc_callback->usb2_com_pre_open) (sc);
+ if (error) {
+ return (error);
+ }
+ }
+ sc->sc_flag |= UCOM_FLAG_HL_READY;
+
+ /* Disable transfers */
+ sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
+
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+ sc->sc_mcr = 0;
+
+ /* reset programmed line state */
+ sc->sc_pls_curr = 0;
+ sc->sc_pls_set = 0;
+ sc->sc_pls_clr = 0;
+
+ usb2_com_queue_command(sc, usb2_com_cfg_open, NULL,
+ &sc->sc_open_task[0].hdr,
+ &sc->sc_open_task[1].hdr);
+
+ /* Queue transfer enable command last */
+ usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
+ &sc->sc_start_task[0].hdr,
+ &sc->sc_start_task[1].hdr);
+
+ usb2_com_modem(tp, SER_DTR | SER_RTS, 0);
+
+ usb2_com_break(sc, 0);
+
+ usb2_com_status_change(sc);
+
+ return (0);
+}
+
+static void
+usb2_com_cfg_close(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+
+ DPRINTF("\n");
+
+ if (sc->sc_flag & UCOM_FLAG_LL_READY) {
+
+ sc->sc_flag &= ~(UCOM_FLAG_LL_READY |
+ UCOM_FLAG_GP_DATA);
+
+ if (sc->sc_callback->usb2_com_cfg_close) {
+ (sc->sc_callback->usb2_com_cfg_close) (sc);
+ }
+ } else {
+ /* already closed */
+ }
+}
+
+static void
+usb2_com_close(struct tty *tp)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ DPRINTF("tp=%p\n", tp);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ DPRINTF("tp=%p already closed\n", tp);
+ return;
+ }
+ usb2_com_shutdown(sc);
+
+ usb2_com_queue_command(sc, usb2_com_cfg_close, NULL,
+ &sc->sc_close_task[0].hdr,
+ &sc->sc_close_task[1].hdr);
+
+ sc->sc_flag &= ~(UCOM_FLAG_HL_READY |
+ UCOM_FLAG_WR_START |
+ UCOM_FLAG_RTS_IFLOW);
+
+ if (sc->sc_callback->usb2_com_stop_read) {
+ (sc->sc_callback->usb2_com_stop_read) (sc);
+ }
+ if (sc->sc_callback->usb2_com_stop_write) {
+ (sc->sc_callback->usb2_com_stop_write) (sc);
+ }
+}
+
+static int
+usb2_com_ioctl(struct tty *tp, u_long cmd, caddr_t data, struct thread *td)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ int error;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return (EIO);
+ }
+ DPRINTF("cmd = 0x%08lx\n", cmd);
+
+ switch (cmd) {
+ case TIOCSBRK:
+ usb2_com_break(sc, 1);
+ error = 0;
+ break;
+ case TIOCCBRK:
+ usb2_com_break(sc, 0);
+ error = 0;
+ break;
+ default:
+ if (sc->sc_callback->usb2_com_ioctl) {
+ error = (sc->sc_callback->usb2_com_ioctl)
+ (sc, cmd, data, 0, td);
+ } else {
+ error = ENOIOCTL;
+ }
+ break;
+ }
+ return (error);
+}
+
+static int
+usb2_com_modem(struct tty *tp, int sigon, int sigoff)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ uint8_t onoff;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return (0);
+ }
+ if ((sigon == 0) && (sigoff == 0)) {
+
+ if (sc->sc_mcr & SER_DTR) {
+ sigon |= SER_DTR;
+ }
+ if (sc->sc_mcr & SER_RTS) {
+ sigon |= SER_RTS;
+ }
+ if (sc->sc_msr & SER_CTS) {
+ sigon |= SER_CTS;
+ }
+ if (sc->sc_msr & SER_DCD) {
+ sigon |= SER_DCD;
+ }
+ if (sc->sc_msr & SER_DSR) {
+ sigon |= SER_DSR;
+ }
+ if (sc->sc_msr & SER_RI) {
+ sigon |= SER_RI;
+ }
+ return (sigon);
+ }
+ if (sigon & SER_DTR) {
+ sc->sc_mcr |= SER_DTR;
+ }
+ if (sigoff & SER_DTR) {
+ sc->sc_mcr &= ~SER_DTR;
+ }
+ if (sigon & SER_RTS) {
+ sc->sc_mcr |= SER_RTS;
+ }
+ if (sigoff & SER_RTS) {
+ sc->sc_mcr &= ~SER_RTS;
+ }
+ onoff = (sc->sc_mcr & SER_DTR) ? 1 : 0;
+ usb2_com_dtr(sc, onoff);
+
+ onoff = (sc->sc_mcr & SER_RTS) ? 1 : 0;
+ usb2_com_rts(sc, onoff);
+
+ return (0);
+}
+
+static void
+usb2_com_cfg_line_state(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+ uint8_t notch_bits;
+ uint8_t any_bits;
+ uint8_t prev_value;
+ uint8_t last_value;
+ uint8_t mask;
+
+ if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+ return;
+ }
+
+ mask = 0;
+ /* compute callback mask */
+ if (sc->sc_callback->usb2_com_cfg_set_dtr)
+ mask |= UCOM_LS_DTR;
+ if (sc->sc_callback->usb2_com_cfg_set_rts)
+ mask |= UCOM_LS_RTS;
+ if (sc->sc_callback->usb2_com_cfg_set_break)
+ mask |= UCOM_LS_BREAK;
+
+ /* compute the bits we are to program */
+ notch_bits = (sc->sc_pls_set & sc->sc_pls_clr) & mask;
+ any_bits = (sc->sc_pls_set | sc->sc_pls_clr) & mask;
+ prev_value = sc->sc_pls_curr ^ notch_bits;
+ last_value = sc->sc_pls_curr;
+
+ /* reset programmed line state */
+ sc->sc_pls_curr = 0;
+ sc->sc_pls_set = 0;
+ sc->sc_pls_clr = 0;
+
+ /* ensure that we don't loose any levels */
+ if (notch_bits & UCOM_LS_DTR)
+ sc->sc_callback->usb2_com_cfg_set_dtr(sc,
+ (prev_value & UCOM_LS_DTR) ? 1 : 0);
+ if (notch_bits & UCOM_LS_RTS)
+ sc->sc_callback->usb2_com_cfg_set_rts(sc,
+ (prev_value & UCOM_LS_RTS) ? 1 : 0);
+ if (notch_bits & UCOM_LS_BREAK)
+ sc->sc_callback->usb2_com_cfg_set_break(sc,
+ (prev_value & UCOM_LS_BREAK) ? 1 : 0);
+
+ /* set last value */
+ if (any_bits & UCOM_LS_DTR)
+ sc->sc_callback->usb2_com_cfg_set_dtr(sc,
+ (last_value & UCOM_LS_DTR) ? 1 : 0);
+ if (any_bits & UCOM_LS_RTS)
+ sc->sc_callback->usb2_com_cfg_set_rts(sc,
+ (last_value & UCOM_LS_RTS) ? 1 : 0);
+ if (any_bits & UCOM_LS_BREAK)
+ sc->sc_callback->usb2_com_cfg_set_break(sc,
+ (last_value & UCOM_LS_BREAK) ? 1 : 0);
+}
+
+static void
+usb2_com_line_state(struct usb2_com_softc *sc,
+ uint8_t set_bits, uint8_t clear_bits)
+{
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return;
+ }
+
+ DPRINTF("on=0x%02x, off=0x%02x\n", set_bits, clear_bits);
+
+ /* update current programmed line state */
+ sc->sc_pls_curr |= set_bits;
+ sc->sc_pls_curr &= ~clear_bits;
+ sc->sc_pls_set |= set_bits;
+ sc->sc_pls_clr |= clear_bits;
+
+ /* defer driver programming */
+ usb2_com_queue_command(sc, usb2_com_cfg_line_state, NULL,
+ &sc->sc_line_state_task[0].hdr,
+ &sc->sc_line_state_task[1].hdr);
+}
+
+static void
+usb2_com_break(struct usb2_com_softc *sc, uint8_t onoff)
+{
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ usb2_com_line_state(sc, UCOM_LS_BREAK, 0);
+ else
+ usb2_com_line_state(sc, 0, UCOM_LS_BREAK);
+}
+
+static void
+usb2_com_dtr(struct usb2_com_softc *sc, uint8_t onoff)
+{
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ usb2_com_line_state(sc, UCOM_LS_DTR, 0);
+ else
+ usb2_com_line_state(sc, 0, UCOM_LS_DTR);
+}
+
+static void
+usb2_com_rts(struct usb2_com_softc *sc, uint8_t onoff)
+{
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ usb2_com_line_state(sc, UCOM_LS_RTS, 0);
+ else
+ usb2_com_line_state(sc, 0, UCOM_LS_RTS);
+}
+
+static void
+usb2_com_cfg_status_change(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_cfg_task *task =
+ (struct usb2_com_cfg_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+ struct tty *tp;
+ uint8_t new_msr;
+ uint8_t new_lsr;
+ uint8_t onoff;
+
+ tp = sc->sc_tty;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+ return;
+ }
+ if (sc->sc_callback->usb2_com_cfg_get_status == NULL) {
+ return;
+ }
+ /* get status */
+
+ new_msr = 0;
+ new_lsr = 0;
+
+ (sc->sc_callback->usb2_com_cfg_get_status) (sc, &new_lsr, &new_msr);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ /* TTY device closed */
+ return;
+ }
+ onoff = ((sc->sc_msr ^ new_msr) & SER_DCD);
+
+ sc->sc_msr = new_msr;
+ sc->sc_lsr = new_lsr;
+
+ if (onoff) {
+
+ onoff = (sc->sc_msr & SER_DCD) ? 1 : 0;
+
+ DPRINTF("DCD changed to %d\n", onoff);
+
+ ttydisc_modem(tp, onoff);
+ }
+}
+
+void
+usb2_com_status_change(struct usb2_com_softc *sc)
+{
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ return;
+ }
+ DPRINTF("\n");
+
+ usb2_com_queue_command(sc, usb2_com_cfg_status_change, NULL,
+ &sc->sc_status_task[0].hdr,
+ &sc->sc_status_task[1].hdr);
+}
+
+static void
+usb2_com_cfg_param(struct usb2_proc_msg *_task)
+{
+ struct usb2_com_param_task *task =
+ (struct usb2_com_param_task *)_task;
+ struct usb2_com_softc *sc = task->sc;
+
+ if (!(sc->sc_flag & UCOM_FLAG_LL_READY)) {
+ return;
+ }
+ if (sc->sc_callback->usb2_com_cfg_param == NULL) {
+ return;
+ }
+
+ (sc->sc_callback->usb2_com_cfg_param) (sc, &task->termios_copy);
+
+ /* wait a little */
+ usb2_pause_mtx(sc->sc_mtx, hz / 10);
+}
+
+static int
+usb2_com_param(struct tty *tp, struct termios *t)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+ uint8_t opened;
+ int error;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ opened = 0;
+ error = 0;
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+
+ /* XXX the TTY layer should call "open()" first! */
+
+ error = usb2_com_open(tp);
+ if (error) {
+ goto done;
+ }
+ opened = 1;
+ }
+ DPRINTF("sc = %p\n", sc);
+
+ /* Check requested parameters. */
+ if (t->c_ospeed < 0) {
+ DPRINTF("negative ospeed\n");
+ error = EINVAL;
+ goto done;
+ }
+ if (t->c_ispeed && (t->c_ispeed != t->c_ospeed)) {
+ DPRINTF("mismatch ispeed and ospeed\n");
+ error = EINVAL;
+ goto done;
+ }
+ t->c_ispeed = t->c_ospeed;
+
+ if (sc->sc_callback->usb2_com_pre_param) {
+ /* Let the lower layer verify the parameters */
+ error = (sc->sc_callback->usb2_com_pre_param) (sc, t);
+ if (error) {
+ DPRINTF("callback error = %d\n", error);
+ goto done;
+ }
+ }
+
+ /* Disable transfers */
+ sc->sc_flag &= ~UCOM_FLAG_GP_DATA;
+
+ /* Queue baud rate programming command first */
+ usb2_com_queue_command(sc, usb2_com_cfg_param, t,
+ &sc->sc_param_task[0].hdr,
+ &sc->sc_param_task[1].hdr);
+
+ /* Queue transfer enable command last */
+ usb2_com_queue_command(sc, usb2_com_cfg_start_transfers, NULL,
+ &sc->sc_start_task[0].hdr,
+ &sc->sc_start_task[1].hdr);
+
+ if (t->c_cflag & CRTS_IFLOW) {
+ sc->sc_flag |= UCOM_FLAG_RTS_IFLOW;
+ } else if (sc->sc_flag & UCOM_FLAG_RTS_IFLOW) {
+ sc->sc_flag &= ~UCOM_FLAG_RTS_IFLOW;
+ usb2_com_modem(tp, SER_RTS, 0);
+ }
+done:
+ if (error) {
+ if (opened) {
+ usb2_com_close(tp);
+ }
+ }
+ return (error);
+}
+
+static void
+usb2_com_outwakeup(struct tty *tp)
+{
+ struct usb2_com_softc *sc = tty_softc(tp);
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ DPRINTF("sc = %p\n", sc);
+
+ if (!(sc->sc_flag & UCOM_FLAG_HL_READY)) {
+ /* The higher layer is not ready */
+ return;
+ }
+ sc->sc_flag |= UCOM_FLAG_WR_START;
+
+ usb2_com_start_transfers(sc);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_com_get_data
+ *
+ * Return values:
+ * 0: No data is available.
+ * Else: Data is available.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_com_get_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
+ uint32_t offset, uint32_t len, uint32_t *actlen)
+{
+ struct usb2_page_search res;
+ struct tty *tp = sc->sc_tty;
+ uint32_t cnt;
+ uint32_t offset_orig;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
+ (!(sc->sc_flag & UCOM_FLAG_GP_DATA)) ||
+ (!(sc->sc_flag & UCOM_FLAG_WR_START))) {
+ actlen[0] = 0;
+ return (0); /* multiport device polling */
+ }
+ offset_orig = offset;
+
+ while (len != 0) {
+
+ usb2_get_page(pc, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ /* copy data directly into USB buffer */
+ cnt = ttydisc_getc(tp, res.buffer, res.length);
+
+ offset += cnt;
+ len -= cnt;
+
+ if (cnt < res.length) {
+ /* end of buffer */
+ break;
+ }
+ }
+
+ actlen[0] = offset - offset_orig;
+
+ DPRINTF("cnt=%d\n", actlen[0]);
+
+ if (actlen[0] == 0) {
+ return (0);
+ }
+ return (1);
+}
+
+void
+usb2_com_put_data(struct usb2_com_softc *sc, struct usb2_page_cache *pc,
+ uint32_t offset, uint32_t len)
+{
+ struct usb2_page_search res;
+ struct tty *tp = sc->sc_tty;
+ char *buf;
+ uint32_t cnt;
+
+ mtx_assert(sc->sc_mtx, MA_OWNED);
+
+ if ((!(sc->sc_flag & UCOM_FLAG_HL_READY)) ||
+ (!(sc->sc_flag & UCOM_FLAG_GP_DATA))) {
+ return; /* multiport device polling */
+ }
+ if (len == 0)
+ return; /* no data */
+
+ /* set a flag to prevent recursation ? */
+
+ while (len > 0) {
+
+ usb2_get_page(pc, offset, &res);
+
+ if (res.length > len) {
+ res.length = len;
+ }
+ len -= res.length;
+ offset += res.length;
+
+ /* pass characters to tty layer */
+
+ buf = res.buffer;
+ cnt = res.length;
+
+ /* first check if we can pass the buffer directly */
+
+ if (ttydisc_can_bypass(tp)) {
+ if (ttydisc_rint_bypass(tp, buf, cnt) != cnt) {
+ DPRINTF("tp=%p, data lost\n", tp);
+ }
+ continue;
+ }
+ /* need to loop */
+
+ for (cnt = 0; cnt != res.length; cnt++) {
+ if (ttydisc_rint(tp, buf[cnt], 0) == -1) {
+ /* XXX what should we do? */
+
+ DPRINTF("tp=%p, lost %d "
+ "chars\n", tp, res.length - cnt);
+ break;
+ }
+ }
+ }
+ ttydisc_rint_done(tp);
+}
+
+static void
+usb2_com_free(void *xsc)
+{
+ struct usb2_com_softc *sc = xsc;
+
+ mtx_lock(sc->sc_mtx);
+ sc->sc_ttyfreed = 1;
+ usb2_cv_signal(&sc->sc_cv);
+ mtx_unlock(sc->sc_mtx);
+}
diff --git a/sys/dev/usb/serial/usb_serial.h b/sys/dev/usb/serial/usb_serial.h
new file mode 100644
index 0000000..c7d57a0
--- /dev/null
+++ b/sys/dev/usb/serial/usb_serial.h
@@ -0,0 +1,198 @@
+/* $NetBSD: ucomvar.h,v 1.9 2001/01/23 21:56:17 augustss Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _USB2_SERIAL_H_
+#define _USB2_SERIAL_H_
+
+#include <sys/tty.h>
+#include <sys/serial.h>
+#include <sys/fcntl.h>
+#include <sys/termios.h>
+
+/* Module interface related macros */
+#define UCOM_MODVER 1
+
+#define UCOM_MINVER 1
+#define UCOM_PREFVER UCOM_MODVER
+#define UCOM_MAXVER 1
+
+struct usb2_device;
+struct usb2_com_softc;
+struct usb2_device_request;
+struct thread;
+
+/*
+ * NOTE: There is no guarantee that "usb2_com_cfg_close()" will
+ * be called after "usb2_com_cfg_open()" if the device is detached
+ * while it is open!
+ */
+struct usb2_com_callback {
+ void (*usb2_com_cfg_get_status) (struct usb2_com_softc *, uint8_t *plsr, uint8_t *pmsr);
+ void (*usb2_com_cfg_set_dtr) (struct usb2_com_softc *, uint8_t);
+ void (*usb2_com_cfg_set_rts) (struct usb2_com_softc *, uint8_t);
+ void (*usb2_com_cfg_set_break) (struct usb2_com_softc *, uint8_t);
+ void (*usb2_com_cfg_param) (struct usb2_com_softc *, struct termios *);
+ void (*usb2_com_cfg_open) (struct usb2_com_softc *);
+ void (*usb2_com_cfg_close) (struct usb2_com_softc *);
+ int (*usb2_com_pre_open) (struct usb2_com_softc *);
+ int (*usb2_com_pre_param) (struct usb2_com_softc *, struct termios *);
+ int (*usb2_com_ioctl) (struct usb2_com_softc *, uint32_t, caddr_t, int, struct thread *);
+ void (*usb2_com_start_read) (struct usb2_com_softc *);
+ void (*usb2_com_stop_read) (struct usb2_com_softc *);
+ void (*usb2_com_start_write) (struct usb2_com_softc *);
+ void (*usb2_com_stop_write) (struct usb2_com_softc *);
+ void (*usb2_com_tty_name) (struct usb2_com_softc *, char *pbuf, uint16_t buflen, uint16_t local_subunit);
+};
+
+/* Line status register */
+#define ULSR_RCV_FIFO 0x80
+#define ULSR_TSRE 0x40 /* Transmitter empty: byte sent */
+#define ULSR_TXRDY 0x20 /* Transmitter buffer empty */
+#define ULSR_BI 0x10 /* Break detected */
+#define ULSR_FE 0x08 /* Framing error: bad stop bit */
+#define ULSR_PE 0x04 /* Parity error */
+#define ULSR_OE 0x02 /* Overrun, lost incoming byte */
+#define ULSR_RXRDY 0x01 /* Byte ready in Receive Buffer */
+#define ULSR_RCV_MASK 0x1f /* Mask for incoming data or error */
+
+struct usb2_com_cfg_task {
+ struct usb2_proc_msg hdr;
+ struct usb2_com_softc *sc;
+};
+
+struct usb2_com_param_task {
+ struct usb2_proc_msg hdr;
+ struct usb2_com_softc *sc;
+ struct termios termios_copy;
+};
+
+struct usb2_com_super_softc {
+ struct usb2_process sc_tq;
+};
+
+struct usb2_com_softc {
+ /*
+ * NOTE: To avoid loosing level change information we use two
+ * tasks instead of one for all commands.
+ *
+ * Level changes are transitions like:
+ *
+ * ON->OFF
+ * OFF->ON
+ * OPEN->CLOSE
+ * CLOSE->OPEN
+ */
+ struct usb2_com_cfg_task sc_start_task[2];
+ struct usb2_com_cfg_task sc_open_task[2];
+ struct usb2_com_cfg_task sc_close_task[2];
+ struct usb2_com_cfg_task sc_line_state_task[2];
+ struct usb2_com_cfg_task sc_status_task[2];
+ struct usb2_com_param_task sc_param_task[2];
+ struct cv sc_cv;
+ const struct usb2_com_callback *sc_callback;
+ struct usb2_com_super_softc *sc_super;
+ struct tty *sc_tty;
+ struct mtx *sc_mtx;
+ void *sc_parent;
+ uint32_t sc_unit;
+ uint32_t sc_local_unit;
+ uint16_t sc_portno;
+ uint8_t sc_flag;
+#define UCOM_FLAG_RTS_IFLOW 0x01 /* use RTS input flow control */
+#define UCOM_FLAG_GONE 0x02 /* the device is gone */
+#define UCOM_FLAG_ATTACHED 0x04 /* set if attached */
+#define UCOM_FLAG_GP_DATA 0x08 /* set if get and put data is possible */
+#define UCOM_FLAG_WR_START 0x10 /* set if write start was issued */
+#define UCOM_FLAG_LL_READY 0x20 /* set if low layer is ready */
+#define UCOM_FLAG_HL_READY 0x40 /* set if high layer is ready */
+ uint8_t sc_lsr;
+ uint8_t sc_msr;
+ uint8_t sc_mcr;
+ uint8_t sc_ttyfreed; /* set when TTY has been freed */
+ /* programmed line state bits */
+ uint8_t sc_pls_set; /* set bits */
+ uint8_t sc_pls_clr; /* cleared bits */
+ uint8_t sc_pls_curr; /* last state */
+#define UCOM_LS_DTR 0x01
+#define UCOM_LS_RTS 0x02
+#define UCOM_LS_BREAK 0x04
+};
+
+#define usb2_com_cfg_do_request(udev,com,req,ptr,flags,timo) \
+ usb2_do_request_proc(udev,&(com)->sc_super->sc_tq,req,ptr,flags,NULL,timo)
+
+int usb2_com_attach(struct usb2_com_super_softc *,
+ struct usb2_com_softc *, uint32_t, void *,
+ const struct usb2_com_callback *callback, struct mtx *);
+void usb2_com_detach(struct usb2_com_super_softc *,
+ struct usb2_com_softc *, uint32_t);
+void usb2_com_status_change(struct usb2_com_softc *);
+uint8_t usb2_com_get_data(struct usb2_com_softc *, struct usb2_page_cache *,
+ uint32_t, uint32_t, uint32_t *);
+void usb2_com_put_data(struct usb2_com_softc *, struct usb2_page_cache *,
+ uint32_t, uint32_t);
+uint8_t usb2_com_cfg_is_gone(struct usb2_com_softc *);
+#endif /* _USB2_SERIAL_H_ */
diff --git a/sys/dev/usb/serial/uslcom.c b/sys/dev/usb/serial/uslcom.c
new file mode 100644
index 0000000..3145151
--- /dev/null
+++ b/sys/dev/usb/serial/uslcom.c
@@ -0,0 +1,539 @@
+/* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Copyright (c) 2006 Jonathan Gray <jsg@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR uslcom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uslcom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uslcom, CTLFLAG_RW, 0, "USB uslcom");
+SYSCTL_INT(_hw_usb2_uslcom, OID_AUTO, debug, CTLFLAG_RW,
+ &uslcom_debug, 0, "Debug level");
+#endif
+
+#define USLCOM_BULK_BUF_SIZE 1024
+#define USLCOM_CONFIG_INDEX 0
+#define USLCOM_IFACE_INDEX 0
+
+#define USLCOM_SET_DATA_BITS(x) ((x) << 8)
+
+#define USLCOM_WRITE 0x41
+#define USLCOM_READ 0xc1
+
+#define USLCOM_UART 0x00
+#define USLCOM_BAUD_RATE 0x01
+#define USLCOM_DATA 0x03
+#define USLCOM_BREAK 0x05
+#define USLCOM_CTRL 0x07
+
+#define USLCOM_UART_DISABLE 0x00
+#define USLCOM_UART_ENABLE 0x01
+
+#define USLCOM_CTRL_DTR_ON 0x0001
+#define USLCOM_CTRL_DTR_SET 0x0100
+#define USLCOM_CTRL_RTS_ON 0x0002
+#define USLCOM_CTRL_RTS_SET 0x0200
+#define USLCOM_CTRL_CTS 0x0010
+#define USLCOM_CTRL_DSR 0x0020
+#define USLCOM_CTRL_DCD 0x0080
+
+#define USLCOM_BAUD_REF 0x384000
+
+#define USLCOM_STOP_BITS_1 0x00
+#define USLCOM_STOP_BITS_2 0x02
+
+#define USLCOM_PARITY_NONE 0x00
+#define USLCOM_PARITY_ODD 0x10
+#define USLCOM_PARITY_EVEN 0x20
+
+#define USLCOM_PORT_NO 0xFFFF /* XXX think this should be 0 --hps */
+
+#define USLCOM_BREAK_OFF 0x00
+#define USLCOM_BREAK_ON 0x01
+
+enum {
+ USLCOM_BULK_DT_WR,
+ USLCOM_BULK_DT_RD,
+ USLCOM_N_TRANSFER,
+};
+
+struct uslcom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[USLCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint8_t sc_msr;
+ uint8_t sc_lsr;
+};
+
+static device_probe_t uslcom_probe;
+static device_attach_t uslcom_attach;
+static device_detach_t uslcom_detach;
+
+static usb2_callback_t uslcom_write_callback;
+static usb2_callback_t uslcom_read_callback;
+
+static void uslcom_open(struct usb2_com_softc *);
+static void uslcom_close(struct usb2_com_softc *);
+static void uslcom_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uslcom_set_rts(struct usb2_com_softc *, uint8_t);
+static void uslcom_set_break(struct usb2_com_softc *, uint8_t);
+static int uslcom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uslcom_param(struct usb2_com_softc *, struct termios *);
+static void uslcom_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *);
+static void uslcom_start_read(struct usb2_com_softc *);
+static void uslcom_stop_read(struct usb2_com_softc *);
+static void uslcom_start_write(struct usb2_com_softc *);
+static void uslcom_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config uslcom_config[USLCOM_N_TRANSFER] = {
+
+ [USLCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = USLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uslcom_write_callback,
+ },
+
+ [USLCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = USLCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uslcom_read_callback,
+ },
+};
+
+struct usb2_com_callback uslcom_callback = {
+ .usb2_com_cfg_open = &uslcom_open,
+ .usb2_com_cfg_close = &uslcom_close,
+ .usb2_com_cfg_get_status = &uslcom_get_status,
+ .usb2_com_cfg_set_dtr = &uslcom_set_dtr,
+ .usb2_com_cfg_set_rts = &uslcom_set_rts,
+ .usb2_com_cfg_set_break = &uslcom_set_break,
+ .usb2_com_cfg_param = &uslcom_param,
+ .usb2_com_pre_param = &uslcom_pre_param,
+ .usb2_com_start_read = &uslcom_start_read,
+ .usb2_com_stop_read = &uslcom_stop_read,
+ .usb2_com_start_write = &uslcom_start_write,
+ .usb2_com_stop_write = &uslcom_stop_write,
+};
+
+static const struct usb2_device_id uslcom_devs[] = {
+ { USB_VPI(USB_VENDOR_BALTECH, USB_PRODUCT_BALTECH_CARDREADER, 0) },
+ { USB_VPI(USB_VENDOR_DYNASTREAM, USB_PRODUCT_DYNASTREAM_ANTDEVBOARD, 0) },
+ { USB_VPI(USB_VENDOR_JABLOTRON, USB_PRODUCT_JABLOTRON_PC60B, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_ARGUSISP, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CRUMB128, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_DEGREE, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_BURNSIDE, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_HELICOM, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_HARP, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_JTAG, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_LIPOWSKY_LIN, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_POLOLU, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP2102, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_CP210X_2, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_SUUNTO, 0) },
+ { USB_VPI(USB_VENDOR_SILABS, USB_PRODUCT_SILABS_TRAQMATE, 0) },
+ { USB_VPI(USB_VENDOR_SILABS2, USB_PRODUCT_SILABS2_DCU11CLONE, 0) },
+ { USB_VPI(USB_VENDOR_USI, USB_PRODUCT_USI_MC60, 0) },
+};
+
+static device_method_t uslcom_methods[] = {
+ DEVMETHOD(device_probe, uslcom_probe),
+ DEVMETHOD(device_attach, uslcom_attach),
+ DEVMETHOD(device_detach, uslcom_detach),
+ {0, 0}
+};
+
+static devclass_t uslcom_devclass;
+
+static driver_t uslcom_driver = {
+ .name = "uslcom",
+ .methods = uslcom_methods,
+ .size = sizeof(struct uslcom_softc),
+};
+
+DRIVER_MODULE(uslcom, ushub, uslcom_driver, uslcom_devclass, NULL, 0);
+MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
+MODULE_DEPEND(uslcom, usb, 1, 1, 1);
+MODULE_VERSION(uslcom, 1);
+
+static int
+uslcom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ DPRINTFN(11, "\n");
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != USLCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa));
+}
+
+static int
+uslcom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uslcom_softc *sc = device_get_softc(dev);
+ int error;
+
+ DPRINTFN(11, "\n");
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ error = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config,
+ USLCOM_N_TRANSFER, sc, &Giant);
+ if (error) {
+ DPRINTF("one or more missing USB endpoints, "
+ "error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uslcom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ return (0);
+
+detach:
+ uslcom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uslcom_detach(device_t dev)
+{
+ struct uslcom_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uslcom_open(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_UART;
+ USETW(req.wValue, USLCOM_UART_ENABLE);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("UART enable failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_close(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_UART;
+ USETW(req.wValue, USLCOM_UART_DISABLE);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("UART disable failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t ctl;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ctl = onoff ? USLCOM_CTRL_DTR_ON : 0;
+ ctl |= USLCOM_CTRL_DTR_SET;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Setting DTR failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t ctl;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ ctl = onoff ? USLCOM_CTRL_RTS_ON : 0;
+ ctl |= USLCOM_CTRL_RTS_SET;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_CTRL;
+ USETW(req.wValue, ctl);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Setting DTR failed (ignored)\n");
+ }
+}
+
+static int
+uslcom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ if (t->c_ospeed <= 0 || t->c_ospeed > 921600)
+ return (EINVAL);
+ return (0);
+}
+
+static void
+uslcom_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t data;
+
+ DPRINTF("\n");
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_BAUD_RATE;
+ USETW(req.wValue, USLCOM_BAUD_REF / t->c_ospeed);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Set baudrate failed (ignored)\n");
+ }
+
+ if (t->c_cflag & CSTOPB)
+ data = USLCOM_STOP_BITS_2;
+ else
+ data = USLCOM_STOP_BITS_1;
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD)
+ data |= USLCOM_PARITY_ODD;
+ else
+ data |= USLCOM_PARITY_EVEN;
+ } else
+ data |= USLCOM_PARITY_NONE;
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ data |= USLCOM_SET_DATA_BITS(5);
+ break;
+ case CS6:
+ data |= USLCOM_SET_DATA_BITS(6);
+ break;
+ case CS7:
+ data |= USLCOM_SET_DATA_BITS(7);
+ break;
+ case CS8:
+ data |= USLCOM_SET_DATA_BITS(8);
+ break;
+ }
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_DATA;
+ USETW(req.wValue, data);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Set format failed (ignored)\n");
+ }
+ return;
+}
+
+static void
+uslcom_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("\n");
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uslcom_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+ struct usb2_device_request req;
+ uint16_t brk = onoff ? USLCOM_BREAK_ON : USLCOM_BREAK_OFF;
+
+ req.bmRequestType = USLCOM_WRITE;
+ req.bRequest = USLCOM_BREAK;
+ USETW(req.wValue, brk);
+ USETW(req.wIndex, USLCOM_PORT_NO);
+ USETW(req.wLength, 0);
+
+ if (usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000)) {
+ DPRINTF("Set BREAK failed (ignored)\n");
+ }
+}
+
+static void
+uslcom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uslcom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ USLCOM_BULK_BUF_SIZE, &actlen)) {
+
+ DPRINTF("actlen = %d\n", actlen);
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uslcom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uslcom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uslcom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ /* start read endpoint */
+ usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+}
+
+static void
+uslcom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ /* stop read endpoint */
+ usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+}
+
+static void
+uslcom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+}
+
+static void
+uslcom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uslcom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]);
+}
diff --git a/sys/dev/usb/serial/uvisor.c b/sys/dev/usb/serial/uvisor.c
new file mode 100644
index 0000000..854f972
--- /dev/null
+++ b/sys/dev/usb/serial/uvisor.c
@@ -0,0 +1,610 @@
+/* $NetBSD: uvisor.c,v 1.9 2001/01/23 14:04:14 augustss Exp $ */
+/* $FreeBSD$ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: uvisor.c,v 1.12 2001/11/13 06:24:57 lukem Exp $
+ * $NetBSD: uvisor.c,v 1.13 2002/02/11 15:11:49 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.14 2002/02/27 23:00:03 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.15 2002/06/16 15:01:31 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.16 2002/07/11 21:14:36 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.17 2002/08/13 11:38:15 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.18 2003/02/05 00:50:14 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.19 2003/02/07 18:12:37 augustss Exp $
+ * $NetBSD: uvisor.c,v 1.20 2003/04/11 01:30:10 simonb Exp $
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Handspring Visor (Palmpilot compatible PDA) driver
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uvisor_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uvisor_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uvisor, CTLFLAG_RW, 0, "USB uvisor");
+SYSCTL_INT(_hw_usb2_uvisor, OID_AUTO, debug, CTLFLAG_RW,
+ &uvisor_debug, 0, "Debug level");
+#endif
+
+#define UVISOR_CONFIG_INDEX 0
+#define UVISOR_IFACE_INDEX 0
+#define UVISOR_BUFSIZE 1024 /* bytes */
+
+/* From the Linux driver */
+/*
+ * UVISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ */
+#define UVISOR_REQUEST_BYTES_AVAILABLE 0x01
+
+/*
+ * UVISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ */
+#define UVISOR_CLOSE_NOTIFICATION 0x02
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ */
+#define UVISOR_GET_CONNECTION_INFORMATION 0x03
+
+/*
+ * UVISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ */
+#define UVISOR_MAX_CONN 8
+struct uvisor_connection_info {
+ uWord num_ports;
+ struct {
+ uByte port_function_id;
+ uByte port;
+ } __packed connections[UVISOR_MAX_CONN];
+} __packed;
+
+#define UVISOR_CONNECTION_INFO_SIZE 18
+
+/* struct uvisor_connection_info.connection[x].port defines: */
+#define UVISOR_ENDPOINT_1 0x01
+#define UVISOR_ENDPOINT_2 0x02
+
+/* struct uvisor_connection_info.connection[x].port_function_id defines: */
+#define UVISOR_FUNCTION_GENERIC 0x00
+#define UVISOR_FUNCTION_DEBUGGER 0x01
+#define UVISOR_FUNCTION_HOTSYNC 0x02
+#define UVISOR_FUNCTION_CONSOLE 0x03
+#define UVISOR_FUNCTION_REMOTE_FILE_SYS 0x04
+
+/*
+ * Unknown PalmOS stuff.
+ */
+#define UVISOR_GET_PALM_INFORMATION 0x04
+#define UVISOR_GET_PALM_INFORMATION_LEN 0x44
+
+struct uvisor_palm_connection_info {
+ uByte num_ports;
+ uByte endpoint_numbers_different;
+ uWord reserved1;
+ struct {
+ uDWord port_function_id;
+ uByte port;
+ uByte end_point_info;
+ uWord reserved;
+ } __packed connections[UVISOR_MAX_CONN];
+} __packed;
+
+enum {
+ UVISOR_BULK_DT_WR,
+ UVISOR_BULK_DT_RD,
+ UVISOR_N_TRANSFER,
+};
+
+struct uvisor_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UVISOR_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint16_t sc_flag;
+#define UVISOR_FLAG_PALM4 0x0001
+#define UVISOR_FLAG_VISOR 0x0002
+#define UVISOR_FLAG_PALM35 0x0004
+#define UVISOR_FLAG_SEND_NOTIFY 0x0008
+
+ uint8_t sc_iface_no;
+ uint8_t sc_iface_index;
+};
+
+/* prototypes */
+
+static device_probe_t uvisor_probe;
+static device_attach_t uvisor_attach;
+static device_detach_t uvisor_detach;
+
+static usb2_callback_t uvisor_write_callback;
+static usb2_callback_t uvisor_read_callback;
+
+static usb2_error_t uvisor_init(struct uvisor_softc *, struct usb2_device *,
+ struct usb2_config *);
+static void uvisor_cfg_open(struct usb2_com_softc *);
+static void uvisor_cfg_close(struct usb2_com_softc *);
+static void uvisor_start_read(struct usb2_com_softc *);
+static void uvisor_stop_read(struct usb2_com_softc *);
+static void uvisor_start_write(struct usb2_com_softc *);
+static void uvisor_stop_write(struct usb2_com_softc *);
+
+static const struct usb2_config uvisor_config[UVISOR_N_TRANSFER] = {
+
+ [UVISOR_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UVISOR_BUFSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uvisor_write_callback,
+ },
+
+ [UVISOR_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UVISOR_BUFSIZE, /* bytes */
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uvisor_read_callback,
+ },
+};
+
+static const struct usb2_com_callback uvisor_callback = {
+ .usb2_com_cfg_open = &uvisor_cfg_open,
+ .usb2_com_cfg_close = &uvisor_cfg_close,
+ .usb2_com_start_read = &uvisor_start_read,
+ .usb2_com_stop_read = &uvisor_stop_read,
+ .usb2_com_start_write = &uvisor_start_write,
+ .usb2_com_stop_write = &uvisor_stop_write,
+};
+
+static device_method_t uvisor_methods[] = {
+ DEVMETHOD(device_probe, uvisor_probe),
+ DEVMETHOD(device_attach, uvisor_attach),
+ DEVMETHOD(device_detach, uvisor_detach),
+ {0, 0}
+};
+
+static devclass_t uvisor_devclass;
+
+static driver_t uvisor_driver = {
+ .name = "uvisor",
+ .methods = uvisor_methods,
+ .size = sizeof(struct uvisor_softc),
+};
+
+DRIVER_MODULE(uvisor, ushub, uvisor_driver, uvisor_devclass, NULL, 0);
+MODULE_DEPEND(uvisor, ucom, 1, 1, 1);
+MODULE_DEPEND(uvisor, usb, 1, 1, 1);
+
+static const struct usb2_device_id uvisor_devs[] = {
+ {USB_VPI(USB_VENDOR_ACEECA, USB_PRODUCT_ACEECA_MEZ1000, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_GARMIN, USB_PRODUCT_GARMIN_IQUE_3600, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_FOSSIL, USB_PRODUCT_FOSSIL_WRISTPDA, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_VISOR, UVISOR_FLAG_VISOR)},
+ {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_HANDSPRING, USB_PRODUCT_HANDSPRING_TREO600, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M500, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M505, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M515, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_I705, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M125, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_M130, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_Z, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_TUNGSTEN_T, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_PALM, USB_PRODUCT_PALM_ZIRE31, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_I500, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40, 0)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_41, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_S360, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_NX60, UVISOR_FLAG_PALM4)},
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_35, UVISOR_FLAG_PALM35)},
+/* {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_25, UVISOR_FLAG_PALM4 )}, */
+ {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TJ37, UVISOR_FLAG_PALM4)},
+/* {USB_VPI(USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_TH55, UVISOR_FLAG_PALM4 )}, See PR 80935 */
+ {USB_VPI(USB_VENDOR_TAPWAVE, USB_PRODUCT_TAPWAVE_ZODIAC, UVISOR_FLAG_PALM4)},
+};
+
+static int
+uvisor_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UVISOR_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UVISOR_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uvisor_devs, sizeof(uvisor_devs), uaa));
+}
+
+static int
+uvisor_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uvisor_softc *sc = device_get_softc(dev);
+ struct usb2_config uvisor_config_copy[UVISOR_N_TRANSFER];
+ int error;
+
+ DPRINTF("sc=%p\n", sc);
+ bcopy(uvisor_config, uvisor_config_copy,
+ sizeof(uvisor_config_copy));
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ /* configure the device */
+
+ sc->sc_flag = USB_GET_DRIVER_INFO(uaa);
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = UVISOR_IFACE_INDEX;
+
+ error = uvisor_init(sc, uaa->device, uvisor_config_copy);
+
+ if (error) {
+ DPRINTF("init failed, error=%s\n",
+ usb2_errstr(error));
+ goto detach;
+ }
+ error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, uvisor_config_copy, UVISOR_N_TRANSFER,
+ sc, &Giant);
+ if (error) {
+ DPRINTF("could not allocate all pipes\n");
+ goto detach;
+ }
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uvisor_callback, &Giant);
+ if (error) {
+ DPRINTF("usb2_com_attach failed\n");
+ goto detach;
+ }
+ return (0);
+
+detach:
+ uvisor_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uvisor_detach(device_t dev)
+{
+ struct uvisor_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UVISOR_N_TRANSFER);
+
+ return (0);
+}
+
+static usb2_error_t
+uvisor_init(struct uvisor_softc *sc, struct usb2_device *udev, struct usb2_config *config)
+{
+ usb2_error_t err = 0;
+ struct usb2_device_request req;
+ struct uvisor_connection_info coninfo;
+ struct uvisor_palm_connection_info pconinfo;
+ uint16_t actlen;
+ uWord wAvail;
+ uint8_t buffer[256];
+
+ if (sc->sc_flag & UVISOR_FLAG_VISOR) {
+ DPRINTF("getting connection info\n");
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_CONNECTION_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+ err = usb2_do_request_flags
+ (udev, &Giant, &req, &coninfo, USB_SHORT_XFER_OK,
+ &actlen, USB_DEFAULT_TIMEOUT);
+
+ if (err) {
+ goto done;
+ }
+ }
+#if USB_DEBUG
+ if (sc->sc_flag & UVISOR_FLAG_VISOR) {
+ uint16_t i, np;
+ const char *desc;
+
+ np = UGETW(coninfo.num_ports);
+ if (np > UVISOR_MAX_CONN) {
+ np = UVISOR_MAX_CONN;
+ }
+ DPRINTF("Number of ports: %d\n", np);
+
+ for (i = 0; i < np; ++i) {
+ switch (coninfo.connections[i].port_function_id) {
+ case UVISOR_FUNCTION_GENERIC:
+ desc = "Generic";
+ break;
+ case UVISOR_FUNCTION_DEBUGGER:
+ desc = "Debugger";
+ break;
+ case UVISOR_FUNCTION_HOTSYNC:
+ desc = "HotSync";
+ break;
+ case UVISOR_FUNCTION_REMOTE_FILE_SYS:
+ desc = "Remote File System";
+ break;
+ default:
+ desc = "unknown";
+ break;
+ }
+ DPRINTF("Port %d is for %s\n",
+ coninfo.connections[i].port, desc);
+ }
+ }
+#endif
+
+ if (sc->sc_flag & UVISOR_FLAG_PALM4) {
+ uint8_t port;
+
+ /* Palm OS 4.0 Hack */
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_PALM_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+
+ err = usb2_do_request_flags
+ (udev, &Giant, &req, &pconinfo, USB_SHORT_XFER_OK,
+ &actlen, USB_DEFAULT_TIMEOUT);
+
+ if (err) {
+ goto done;
+ }
+ if (actlen < 12) {
+ DPRINTF("too little data\n");
+ err = USB_ERR_INVAL;
+ goto done;
+ }
+ if (pconinfo.endpoint_numbers_different) {
+ port = pconinfo.connections[0].end_point_info;
+ config[0].endpoint = (port & 0xF); /* output */
+ config[1].endpoint = (port >> 4); /* input */
+ } else {
+ port = pconinfo.connections[0].port;
+ config[0].endpoint = (port & 0xF); /* output */
+ config[1].endpoint = (port & 0xF); /* input */
+ }
+#if 0
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_GET_PALM_INFORMATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_GET_PALM_INFORMATION_LEN);
+ err = usb2_do_request(udev, &req, buffer);
+ if (err) {
+ goto done;
+ }
+#endif
+ }
+ if (sc->sc_flag & UVISOR_FLAG_PALM35) {
+ /* get the config number */
+ DPRINTF("getting config info\n");
+ req.bmRequestType = UT_READ;
+ req.bRequest = UR_GET_CONFIG;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+
+ err = usb2_do_request(udev, &Giant, &req, buffer);
+ if (err) {
+ goto done;
+ }
+ /* get the interface number */
+ DPRINTF("get the interface number\n");
+ req.bmRequestType = UT_READ_DEVICE;
+ req.bRequest = UR_GET_INTERFACE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 1);
+ err = usb2_do_request(udev, &Giant, &req, buffer);
+ if (err) {
+ goto done;
+ }
+ }
+ DPRINTF("getting available bytes\n");
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT;
+ req.bRequest = UVISOR_REQUEST_BYTES_AVAILABLE;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 5);
+ USETW(req.wLength, sizeof(wAvail));
+ err = usb2_do_request(udev, &Giant, &req, &wAvail);
+ if (err) {
+ goto done;
+ }
+ DPRINTF("avail=%d\n", UGETW(wAvail));
+
+ DPRINTF("done\n");
+done:
+ return (err);
+}
+
+static void
+uvisor_cfg_open(struct usb2_com_softc *ucom)
+{
+ return;
+}
+
+static void
+uvisor_cfg_close(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+ uint8_t buffer[UVISOR_CONNECTION_INFO_SIZE];
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ req.bmRequestType = UT_READ_VENDOR_ENDPOINT; /* XXX read? */
+ req.bRequest = UVISOR_CLOSE_NOTIFICATION;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, UVISOR_CONNECTION_INFO_SIZE);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, buffer, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "close notification failed, error=%s\n",
+ usb2_errstr(err));
+ }
+}
+
+static void
+uvisor_start_read(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+}
+
+static void
+uvisor_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_RD]);
+}
+
+static void
+uvisor_start_write(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+}
+
+static void
+uvisor_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uvisor_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVISOR_BULK_DT_WR]);
+}
+
+static void
+uvisor_write_callback(struct usb2_xfer *xfer)
+{
+ struct uvisor_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UVISOR_BUFSIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvisor_read_callback(struct usb2_xfer *xfer)
+{
+ struct uvisor_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
diff --git a/sys/dev/usb/serial/uvscom.c b/sys/dev/usb/serial/uvscom.c
new file mode 100644
index 0000000..15a9eba
--- /dev/null
+++ b/sys/dev/usb/serial/uvscom.c
@@ -0,0 +1,707 @@
+/* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * uvscom: SUNTAC Slipper U VS-10U driver.
+ * Slipper U is a PC Card to USB converter for data communication card
+ * adapter. It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in,
+ * P-in m@ater and various data communication card adapters.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_cdc.h>
+
+#define USB_DEBUG_VAR uvscom_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/serial/usb_serial.h>
+
+#if USB_DEBUG
+static int uvscom_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uvscom, CTLFLAG_RW, 0, "USB uvscom");
+SYSCTL_INT(_hw_usb2_uvscom, OID_AUTO, debug, CTLFLAG_RW,
+ &uvscom_debug, 0, "Debug level");
+#endif
+
+#define UVSCOM_MODVER 1 /* module version */
+
+#define UVSCOM_CONFIG_INDEX 0
+#define UVSCOM_IFACE_INDEX 0
+
+/* Request */
+#define UVSCOM_SET_SPEED 0x10
+#define UVSCOM_LINE_CTL 0x11
+#define UVSCOM_SET_PARAM 0x12
+#define UVSCOM_READ_STATUS 0xd0
+#define UVSCOM_SHUTDOWN 0xe0
+
+/* UVSCOM_SET_SPEED parameters */
+#define UVSCOM_SPEED_150BPS 0x00
+#define UVSCOM_SPEED_300BPS 0x01
+#define UVSCOM_SPEED_600BPS 0x02
+#define UVSCOM_SPEED_1200BPS 0x03
+#define UVSCOM_SPEED_2400BPS 0x04
+#define UVSCOM_SPEED_4800BPS 0x05
+#define UVSCOM_SPEED_9600BPS 0x06
+#define UVSCOM_SPEED_19200BPS 0x07
+#define UVSCOM_SPEED_38400BPS 0x08
+#define UVSCOM_SPEED_57600BPS 0x09
+#define UVSCOM_SPEED_115200BPS 0x0a
+
+/* UVSCOM_LINE_CTL parameters */
+#define UVSCOM_BREAK 0x40
+#define UVSCOM_RTS 0x02
+#define UVSCOM_DTR 0x01
+#define UVSCOM_LINE_INIT 0x08
+
+/* UVSCOM_SET_PARAM parameters */
+#define UVSCOM_DATA_MASK 0x03
+#define UVSCOM_DATA_BIT_8 0x03
+#define UVSCOM_DATA_BIT_7 0x02
+#define UVSCOM_DATA_BIT_6 0x01
+#define UVSCOM_DATA_BIT_5 0x00
+
+#define UVSCOM_STOP_MASK 0x04
+#define UVSCOM_STOP_BIT_2 0x04
+#define UVSCOM_STOP_BIT_1 0x00
+
+#define UVSCOM_PARITY_MASK 0x18
+#define UVSCOM_PARITY_EVEN 0x18
+#define UVSCOM_PARITY_ODD 0x08
+#define UVSCOM_PARITY_NONE 0x00
+
+/* Status bits */
+#define UVSCOM_TXRDY 0x04
+#define UVSCOM_RXRDY 0x01
+
+#define UVSCOM_DCD 0x08
+#define UVSCOM_NOCARD 0x04
+#define UVSCOM_DSR 0x02
+#define UVSCOM_CTS 0x01
+#define UVSCOM_USTAT_MASK (UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS)
+
+#define UVSCOM_BULK_BUF_SIZE 1024 /* bytes */
+
+enum {
+ UVSCOM_BULK_DT_WR,
+ UVSCOM_BULK_DT_RD,
+ UVSCOM_INTR_DT_RD,
+ UVSCOM_N_TRANSFER,
+};
+
+struct uvscom_softc {
+ struct usb2_com_super_softc sc_super_ucom;
+ struct usb2_com_softc sc_ucom;
+
+ struct usb2_xfer *sc_xfer[UVSCOM_N_TRANSFER];
+ struct usb2_device *sc_udev;
+
+ uint16_t sc_line; /* line control register */
+
+ uint8_t sc_iface_no; /* interface number */
+ uint8_t sc_iface_index; /* interface index */
+ uint8_t sc_lsr; /* local status register */
+ uint8_t sc_msr; /* uvscom status register */
+ uint8_t sc_unit_status; /* unit status */
+};
+
+/* prototypes */
+
+static device_probe_t uvscom_probe;
+static device_attach_t uvscom_attach;
+static device_detach_t uvscom_detach;
+
+static usb2_callback_t uvscom_write_callback;
+static usb2_callback_t uvscom_read_callback;
+static usb2_callback_t uvscom_intr_callback;
+
+static void uvscom_cfg_set_dtr(struct usb2_com_softc *, uint8_t);
+static void uvscom_cfg_set_rts(struct usb2_com_softc *, uint8_t);
+static void uvscom_cfg_set_break(struct usb2_com_softc *, uint8_t);
+static int uvscom_pre_param(struct usb2_com_softc *, struct termios *);
+static void uvscom_cfg_param(struct usb2_com_softc *, struct termios *);
+static int uvscom_pre_open(struct usb2_com_softc *);
+static void uvscom_cfg_open(struct usb2_com_softc *);
+static void uvscom_cfg_close(struct usb2_com_softc *);
+static void uvscom_start_read(struct usb2_com_softc *);
+static void uvscom_stop_read(struct usb2_com_softc *);
+static void uvscom_start_write(struct usb2_com_softc *);
+static void uvscom_stop_write(struct usb2_com_softc *);
+static void uvscom_cfg_get_status(struct usb2_com_softc *, uint8_t *,
+ uint8_t *);
+static void uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t);
+static uint16_t uvscom_cfg_read_status(struct uvscom_softc *);
+
+static const struct usb2_config uvscom_config[UVSCOM_N_TRANSFER] = {
+
+ [UVSCOM_BULK_DT_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UVSCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = &uvscom_write_callback,
+ },
+
+ [UVSCOM_BULK_DT_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UVSCOM_BULK_BUF_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &uvscom_read_callback,
+ },
+
+ [UVSCOM_INTR_DT_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.bufsize = 0, /* use wMaxPacketSize */
+ .mh.callback = &uvscom_intr_callback,
+ },
+};
+
+static const struct usb2_com_callback uvscom_callback = {
+ .usb2_com_cfg_get_status = &uvscom_cfg_get_status,
+ .usb2_com_cfg_set_dtr = &uvscom_cfg_set_dtr,
+ .usb2_com_cfg_set_rts = &uvscom_cfg_set_rts,
+ .usb2_com_cfg_set_break = &uvscom_cfg_set_break,
+ .usb2_com_cfg_param = &uvscom_cfg_param,
+ .usb2_com_cfg_open = &uvscom_cfg_open,
+ .usb2_com_cfg_close = &uvscom_cfg_close,
+ .usb2_com_pre_open = &uvscom_pre_open,
+ .usb2_com_pre_param = &uvscom_pre_param,
+ .usb2_com_start_read = &uvscom_start_read,
+ .usb2_com_stop_read = &uvscom_stop_read,
+ .usb2_com_start_write = &uvscom_start_write,
+ .usb2_com_stop_write = &uvscom_stop_write,
+};
+
+static const struct usb2_device_id uvscom_devs[] = {
+ /* SUNTAC U-Cable type A4 */
+ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4, 0)},
+ /* SUNTAC U-Cable type D2 */
+ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L, 0)},
+ /* SUNTAC Ir-Trinity */
+ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U, 0)},
+ /* SUNTAC U-Cable type P1 */
+ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1, 0)},
+ /* SUNTAC Slipper U */
+ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U, 0)},
+};
+
+static device_method_t uvscom_methods[] = {
+ DEVMETHOD(device_probe, uvscom_probe),
+ DEVMETHOD(device_attach, uvscom_attach),
+ DEVMETHOD(device_detach, uvscom_detach),
+ {0, 0}
+};
+
+static devclass_t uvscom_devclass;
+
+static driver_t uvscom_driver = {
+ .name = "uvscom",
+ .methods = uvscom_methods,
+ .size = sizeof(struct uvscom_softc),
+};
+
+DRIVER_MODULE(uvscom, ushub, uvscom_driver, uvscom_devclass, NULL, 0);
+MODULE_DEPEND(uvscom, ucom, 1, 1, 1);
+MODULE_DEPEND(uvscom, usb, 1, 1, 1);
+MODULE_VERSION(uvscom, UVSCOM_MODVER);
+
+static int
+uvscom_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->info.bConfigIndex != UVSCOM_CONFIG_INDEX) {
+ return (ENXIO);
+ }
+ if (uaa->info.bIfaceIndex != UVSCOM_IFACE_INDEX) {
+ return (ENXIO);
+ }
+ return (usb2_lookup_id_by_uaa(uvscom_devs, sizeof(uvscom_devs), uaa));
+}
+
+static int
+uvscom_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uvscom_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ DPRINTF("sc=%p\n", sc);
+
+ sc->sc_iface_no = uaa->info.bIfaceNum;
+ sc->sc_iface_index = UVSCOM_IFACE_INDEX;
+
+ error = usb2_transfer_setup(uaa->device, &sc->sc_iface_index,
+ sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &Giant);
+
+ if (error) {
+ DPRINTF("could not allocate all USB transfers!\n");
+ goto detach;
+ }
+ sc->sc_line = UVSCOM_LINE_INIT;
+
+ /* clear stall at first run */
+ usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+ usb2_transfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+
+ error = usb2_com_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
+ &uvscom_callback, &Giant);
+ if (error) {
+ goto detach;
+ }
+ /* start interrupt pipe */
+ mtx_lock(&Giant);
+ usb2_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
+ mtx_unlock(&Giant);
+
+ return (0);
+
+detach:
+ uvscom_detach(dev);
+ return (ENXIO);
+}
+
+static int
+uvscom_detach(device_t dev)
+{
+ struct uvscom_softc *sc = device_get_softc(dev);
+
+ DPRINTF("sc=%p\n", sc);
+
+ /* stop interrupt pipe */
+
+ if (sc->sc_xfer[UVSCOM_INTR_DT_RD]) {
+ usb2_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]);
+ }
+ usb2_com_detach(&sc->sc_super_ucom, &sc->sc_ucom, 1);
+
+ usb2_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER);
+
+ return (0);
+}
+
+static void
+uvscom_write_callback(struct usb2_xfer *xfer)
+{
+ struct uvscom_softc *sc = xfer->priv_sc;
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_SETUP:
+ case USB_ST_TRANSFERRED:
+tr_setup:
+ if (usb2_com_get_data(&sc->sc_ucom, xfer->frbuffers, 0,
+ UVSCOM_BULK_BUF_SIZE, &actlen)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvscom_read_callback(struct usb2_xfer *xfer)
+{
+ struct uvscom_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_com_put_data(&sc->sc_ucom, xfer->frbuffers, 0, xfer->actlen);
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvscom_intr_callback(struct usb2_xfer *xfer)
+{
+ struct uvscom_softc *sc = xfer->priv_sc;
+ uint8_t buf[2];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (xfer->actlen >= 2) {
+
+ usb2_copy_out(xfer->frbuffers, 0, buf, sizeof(buf));
+
+ sc->sc_lsr = 0;
+ sc->sc_msr = 0;
+ sc->sc_unit_status = buf[1];
+
+ if (buf[0] & UVSCOM_TXRDY) {
+ sc->sc_lsr |= ULSR_TXRDY;
+ }
+ if (buf[0] & UVSCOM_RXRDY) {
+ sc->sc_lsr |= ULSR_RXRDY;
+ }
+ if (buf[1] & UVSCOM_CTS) {
+ sc->sc_msr |= SER_CTS;
+ }
+ if (buf[1] & UVSCOM_DSR) {
+ sc->sc_msr |= SER_DSR;
+ }
+ if (buf[1] & UVSCOM_DCD) {
+ sc->sc_msr |= SER_DCD;
+ }
+ /*
+ * the UCOM layer will ignore this call if the TTY
+ * device is closed!
+ */
+ usb2_com_status_change(&sc->sc_ucom);
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static void
+uvscom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UVSCOM_DTR;
+ else
+ sc->sc_line &= ~UVSCOM_DTR;
+
+ uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
+}
+
+static void
+uvscom_cfg_set_rts(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UVSCOM_RTS;
+ else
+ sc->sc_line &= ~UVSCOM_RTS;
+
+ uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
+}
+
+static void
+uvscom_cfg_set_break(struct usb2_com_softc *ucom, uint8_t onoff)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("onoff = %d\n", onoff);
+
+ if (onoff)
+ sc->sc_line |= UVSCOM_BREAK;
+ else
+ sc->sc_line &= ~UVSCOM_BREAK;
+
+ uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line);
+}
+
+static int
+uvscom_pre_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ switch (t->c_ospeed) {
+ case B150:
+ case B300:
+ case B600:
+ case B1200:
+ case B2400:
+ case B4800:
+ case B9600:
+ case B19200:
+ case B38400:
+ case B57600:
+ case B115200:
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static void
+uvscom_cfg_param(struct usb2_com_softc *ucom, struct termios *t)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+ uint16_t value;
+
+ DPRINTF("\n");
+
+ switch (t->c_ospeed) {
+ case B150:
+ value = UVSCOM_SPEED_150BPS;
+ break;
+ case B300:
+ value = UVSCOM_SPEED_300BPS;
+ break;
+ case B600:
+ value = UVSCOM_SPEED_600BPS;
+ break;
+ case B1200:
+ value = UVSCOM_SPEED_1200BPS;
+ break;
+ case B2400:
+ value = UVSCOM_SPEED_2400BPS;
+ break;
+ case B4800:
+ value = UVSCOM_SPEED_4800BPS;
+ break;
+ case B9600:
+ value = UVSCOM_SPEED_9600BPS;
+ break;
+ case B19200:
+ value = UVSCOM_SPEED_19200BPS;
+ break;
+ case B38400:
+ value = UVSCOM_SPEED_38400BPS;
+ break;
+ case B57600:
+ value = UVSCOM_SPEED_57600BPS;
+ break;
+ case B115200:
+ value = UVSCOM_SPEED_115200BPS;
+ break;
+ default:
+ return;
+ }
+
+ uvscom_cfg_write(sc, UVSCOM_SET_SPEED, value);
+
+ value = 0;
+
+ if (t->c_cflag & CSTOPB) {
+ value |= UVSCOM_STOP_BIT_2;
+ }
+ if (t->c_cflag & PARENB) {
+ if (t->c_cflag & PARODD) {
+ value |= UVSCOM_PARITY_ODD;
+ } else {
+ value |= UVSCOM_PARITY_EVEN;
+ }
+ } else {
+ value |= UVSCOM_PARITY_NONE;
+ }
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ value |= UVSCOM_DATA_BIT_5;
+ break;
+ case CS6:
+ value |= UVSCOM_DATA_BIT_6;
+ break;
+ case CS7:
+ value |= UVSCOM_DATA_BIT_7;
+ break;
+ default:
+ case CS8:
+ value |= UVSCOM_DATA_BIT_8;
+ break;
+ }
+
+ uvscom_cfg_write(sc, UVSCOM_SET_PARAM, value);
+}
+
+static int
+uvscom_pre_open(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("sc = %p\n", sc);
+
+ /* check if PC card was inserted */
+
+ if (sc->sc_unit_status & UVSCOM_NOCARD) {
+ DPRINTF("no PC card!\n");
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static void
+uvscom_cfg_open(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("sc = %p\n", sc);
+
+ uvscom_cfg_read_status(sc);
+}
+
+static void
+uvscom_cfg_close(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ DPRINTF("sc=%p\n", sc);
+
+ uvscom_cfg_write(sc, UVSCOM_SHUTDOWN, 0);
+}
+
+static void
+uvscom_start_read(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+}
+
+static void
+uvscom_stop_read(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
+}
+
+static void
+uvscom_start_write(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+}
+
+static void
+uvscom_stop_write(struct usb2_com_softc *ucom)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ usb2_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
+}
+
+static void
+uvscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr)
+{
+ struct uvscom_softc *sc = ucom->sc_parent;
+
+ *lsr = sc->sc_lsr;
+ *msr = sc->sc_msr;
+}
+
+static void
+uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = index;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, NULL, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+}
+
+static uint16_t
+uvscom_cfg_read_status(struct uvscom_softc *sc)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t data[2];
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = UVSCOM_READ_STATUS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 2);
+
+ err = usb2_com_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
+ &req, data, 0, 1000);
+ if (err) {
+ DPRINTFN(0, "device request failed, err=%s "
+ "(ignored)\n", usb2_errstr(err));
+ }
+ return (data[0] | (data[1] << 8));
+}
diff --git a/sys/dev/usb/sound/uaudio.c b/sys/dev/usb/sound/uaudio.c
new file mode 100644
index 0000000..b79e398
--- /dev/null
+++ b/sys/dev/usb/sound/uaudio.c
@@ -0,0 +1,3750 @@
+/* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf
+ * http://www.usb.org/developers/devclass_docs/frmts10.pdf
+ * http://www.usb.org/developers/devclass_docs/termt10.pdf
+ */
+
+/*
+ * Also merged:
+ * $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $
+ * $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $
+ * $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $
+ * $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR uaudio_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_dynamic.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+#include <dev/usb/sound/uaudio_reg.h>
+#include <dev/usb/sound/uaudio.h>
+
+#include <sys/reboot.h> /* for bootverbose */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+#include "feeder_if.h"
+
+static int uaudio_default_rate = 96000;
+static int uaudio_default_bits = 32;
+static int uaudio_default_channels = 2;
+
+#if USB_DEBUG
+static int uaudio_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio");
+SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, debug, CTLFLAG_RW,
+ &uaudio_debug, 0, "uaudio debug level");
+SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_rate, CTLFLAG_RW,
+ &uaudio_default_rate, 0, "uaudio default sample rate");
+SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_bits, CTLFLAG_RW,
+ &uaudio_default_bits, 0, "uaudio default sample bits");
+SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_channels, CTLFLAG_RW,
+ &uaudio_default_channels, 0, "uaudio default sample channels");
+#endif
+
+#define UAUDIO_MINFRAMES 16 /* must be factor of 8 due HS-USB */
+#define UAUDIO_NCHANBUFS 2 /* number of outstanding request */
+#define UAUDIO_RECURSE_LIMIT 24 /* rounds */
+
+#define MAKE_WORD(h,l) (((h) << 8) | (l))
+#define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1)
+
+struct uaudio_mixer_node {
+ int32_t minval;
+ int32_t maxval;
+#define MIX_MAX_CHAN 8
+ int32_t wValue[MIX_MAX_CHAN]; /* using nchan */
+ uint32_t delta;
+ uint32_t mul;
+ uint32_t ctl;
+
+ uint16_t wData[MIX_MAX_CHAN]; /* using nchan */
+ uint16_t wIndex;
+
+ uint8_t update[(MIX_MAX_CHAN + 7) / 8];
+ uint8_t nchan;
+ uint8_t type;
+#define MIX_ON_OFF 1
+#define MIX_SIGNED_16 2
+#define MIX_UNSIGNED_16 3
+#define MIX_SIGNED_8 4
+#define MIX_SELECTOR 5
+#define MIX_UNKNOWN 6
+#define MIX_SIZE(n) ((((n) == MIX_SIGNED_16) || \
+ ((n) == MIX_UNSIGNED_16)) ? 2 : 1)
+#define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16)
+
+#define MAX_SELECTOR_INPUT_PIN 256
+ uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN];
+ uint8_t class;
+
+ struct uaudio_mixer_node *next;
+};
+
+struct uaudio_chan {
+ struct pcmchan_caps pcm_cap; /* capabilities */
+
+ struct snd_dbuf *pcm_buf;
+ const struct usb2_config *usb2_cfg;
+ struct mtx *pcm_mtx; /* lock protecting this structure */
+ struct uaudio_softc *priv_sc;
+ struct pcm_channel *pcm_ch;
+ struct usb2_xfer *xfer[UAUDIO_NCHANBUFS];
+ const struct usb2_audio_streaming_interface_descriptor *p_asid;
+ const struct usb2_audio_streaming_type1_descriptor *p_asf1d;
+ const struct usb2_audio_streaming_endpoint_descriptor *p_sed;
+ const usb2_endpoint_descriptor_audio_t *p_ed1;
+ const usb2_endpoint_descriptor_audio_t *p_ed2;
+ const struct uaudio_format *p_fmt;
+
+ uint8_t *buf; /* pointer to buffer */
+ uint8_t *start; /* upper layer buffer start */
+ uint8_t *end; /* upper layer buffer end */
+ uint8_t *cur; /* current position in upper layer
+ * buffer */
+
+ uint32_t intr_size; /* in bytes */
+ uint32_t block_size;
+ uint32_t sample_rate;
+ uint32_t format;
+ uint32_t pcm_format[2];
+
+ uint16_t bytes_per_frame;
+
+ uint8_t valid;
+ uint8_t iface_index;
+ uint8_t iface_alt_index;
+};
+
+#define UMIDI_N_TRANSFER 4 /* units */
+#define UMIDI_CABLES_MAX 16 /* units */
+#define UMIDI_BULK_SIZE 1024 /* bytes */
+
+struct umidi_sub_chan {
+ struct usb2_fifo_sc fifo;
+ uint8_t *temp_cmd;
+ uint8_t temp_0[4];
+ uint8_t temp_1[4];
+ uint8_t state;
+#define UMIDI_ST_UNKNOWN 0 /* scan for command */
+#define UMIDI_ST_1PARAM 1
+#define UMIDI_ST_2PARAM_1 2
+#define UMIDI_ST_2PARAM_2 3
+#define UMIDI_ST_SYSEX_0 4
+#define UMIDI_ST_SYSEX_1 5
+#define UMIDI_ST_SYSEX_2 6
+
+ uint8_t read_open:1;
+ uint8_t write_open:1;
+ uint8_t unused:6;
+};
+
+struct umidi_chan {
+
+ struct umidi_sub_chan sub[UMIDI_CABLES_MAX];
+ struct mtx mtx;
+
+ struct usb2_xfer *xfer[UMIDI_N_TRANSFER];
+
+ uint8_t iface_index;
+ uint8_t iface_alt_index;
+
+ uint8_t flags;
+#define UMIDI_FLAG_READ_STALL 0x01
+#define UMIDI_FLAG_WRITE_STALL 0x02
+
+ uint8_t read_open_refcount;
+ uint8_t write_open_refcount;
+
+ uint8_t curr_cable;
+ uint8_t max_cable;
+ uint8_t valid;
+};
+
+struct uaudio_softc {
+ struct sbuf sc_sndstat;
+ struct sndcard_func sc_sndcard_func;
+ struct uaudio_chan sc_rec_chan;
+ struct uaudio_chan sc_play_chan;
+ struct umidi_chan sc_midi_chan;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_mixer_xfer[1];
+ struct uaudio_mixer_node *sc_mixer_root;
+ struct uaudio_mixer_node *sc_mixer_curr;
+
+ uint32_t sc_mix_info;
+ uint32_t sc_recsrc_info;
+
+ uint16_t sc_audio_rev;
+ uint16_t sc_mixer_count;
+
+ uint8_t sc_sndstat_valid;
+ uint8_t sc_mixer_iface_index;
+ uint8_t sc_mixer_iface_no;
+ uint8_t sc_mixer_chan;
+ uint8_t sc_pcm_registered:1;
+ uint8_t sc_mixer_init:1;
+ uint8_t sc_uq_audio_swap_lr:1;
+ uint8_t sc_uq_au_inp_async:1;
+ uint8_t sc_uq_au_no_xu:1;
+ uint8_t sc_uq_bad_adc:1;
+};
+
+struct uaudio_search_result {
+ uint8_t bit_input[(256 + 7) / 8];
+ uint8_t bit_output[(256 + 7) / 8];
+ uint8_t bit_visited[(256 + 7) / 8];
+ uint8_t recurse_level;
+ uint8_t id_max;
+};
+
+struct uaudio_terminal_node {
+ union {
+ const struct usb2_descriptor *desc;
+ const struct usb2_audio_input_terminal *it;
+ const struct usb2_audio_output_terminal *ot;
+ const struct usb2_audio_mixer_unit_0 *mu;
+ const struct usb2_audio_selector_unit *su;
+ const struct usb2_audio_feature_unit *fu;
+ const struct usb2_audio_processing_unit_0 *pu;
+ const struct usb2_audio_extension_unit_0 *eu;
+ } u;
+ struct uaudio_search_result usr;
+ struct uaudio_terminal_node *root;
+};
+
+struct uaudio_format {
+ uint16_t wFormat;
+ uint8_t bPrecision;
+ uint32_t freebsd_fmt;
+ const char *description;
+};
+
+static const struct uaudio_format uaudio_formats[] = {
+
+ {UA_FMT_PCM8, 8, AFMT_U8, "8-bit U-LE PCM"},
+ {UA_FMT_PCM8, 16, AFMT_U16_LE, "16-bit U-LE PCM"},
+ {UA_FMT_PCM8, 24, AFMT_U24_LE, "24-bit U-LE PCM"},
+ {UA_FMT_PCM8, 32, AFMT_U32_LE, "32-bit U-LE PCM"},
+
+ {UA_FMT_PCM, 8, AFMT_S8, "8-bit S-LE PCM"},
+ {UA_FMT_PCM, 16, AFMT_S16_LE, "16-bit S-LE PCM"},
+ {UA_FMT_PCM, 24, AFMT_S24_LE, "24-bit S-LE PCM"},
+ {UA_FMT_PCM, 32, AFMT_S32_LE, "32-bit S-LE PCM"},
+
+ {UA_FMT_ALAW, 8, AFMT_A_LAW, "8-bit A-Law"},
+ {UA_FMT_MULAW, 8, AFMT_MU_LAW, "8-bit mu-Law"},
+
+ {0, 0, 0, NULL}
+};
+
+#define UAC_OUTPUT 0
+#define UAC_INPUT 1
+#define UAC_EQUAL 2
+#define UAC_RECORD 3
+#define UAC_NCLASSES 4
+
+#if USB_DEBUG
+static const char *uac_names[] = {
+ "outputs", "inputs", "equalization", "record"
+};
+
+#endif
+
+/* prototypes */
+
+static device_probe_t uaudio_probe;
+static device_attach_t uaudio_attach;
+static device_detach_t uaudio_detach;
+
+static usb2_callback_t uaudio_chan_play_callback;
+static usb2_callback_t uaudio_chan_record_callback;
+static usb2_callback_t uaudio_mixer_write_cfg_callback;
+static usb2_callback_t umidi_read_clear_stall_callback;
+static usb2_callback_t umidi_bulk_read_callback;
+static usb2_callback_t umidi_write_clear_stall_callback;
+static usb2_callback_t umidi_bulk_write_callback;
+
+static void uaudio_chan_fill_info_sub(struct uaudio_softc *,
+ struct usb2_device *, uint32_t, uint16_t, uint8_t, uint8_t);
+static void uaudio_chan_fill_info(struct uaudio_softc *,
+ struct usb2_device *);
+static void uaudio_mixer_add_ctl_sub(struct uaudio_softc *,
+ struct uaudio_mixer_node *);
+static void uaudio_mixer_add_ctl(struct uaudio_softc *,
+ struct uaudio_mixer_node *);
+static void uaudio_mixer_add_input(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio_mixer_add_output(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio_mixer_add_mixer(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio_mixer_add_selector(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static uint32_t uaudio_mixer_feature_get_bmaControls(
+ const struct usb2_audio_feature_unit *, uint8_t);
+static void uaudio_mixer_add_feature(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio_mixer_add_processing_updown(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio_mixer_add_processing(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static void uaudio_mixer_add_extension(struct uaudio_softc *,
+ const struct uaudio_terminal_node *, int);
+static struct usb2_audio_cluster uaudio_mixer_get_cluster(uint8_t,
+ const struct uaudio_terminal_node *);
+static uint16_t uaudio_mixer_determine_class(const struct uaudio_terminal_node *,
+ struct uaudio_mixer_node *);
+static uint16_t uaudio_mixer_feature_name(const struct uaudio_terminal_node *,
+ struct uaudio_mixer_node *);
+static const struct uaudio_terminal_node *uaudio_mixer_get_input(
+ const struct uaudio_terminal_node *, uint8_t);
+static const struct uaudio_terminal_node *uaudio_mixer_get_output(
+ const struct uaudio_terminal_node *, uint8_t);
+static void uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *,
+ const uint8_t *, uint8_t, struct uaudio_search_result *);
+static void uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *,
+ uint8_t, uint8_t, struct uaudio_search_result *);
+static void uaudio_mixer_fill_info(struct uaudio_softc *,
+ struct usb2_device *, void *);
+static uint16_t uaudio_mixer_get(struct usb2_device *, uint8_t,
+ struct uaudio_mixer_node *);
+static void uaudio_mixer_ctl_set(struct uaudio_softc *,
+ struct uaudio_mixer_node *, uint8_t, int32_t val);
+static usb2_error_t uaudio_set_speed(struct usb2_device *, uint8_t, uint32_t);
+static int uaudio_mixer_signext(uint8_t, int);
+static int uaudio_mixer_bsd2value(struct uaudio_mixer_node *, int32_t val);
+static const void *uaudio_mixer_verify_desc(const void *, uint32_t);
+static void uaudio_mixer_init(struct uaudio_softc *);
+static uint8_t umidi_convert_to_usb(struct umidi_sub_chan *, uint8_t, uint8_t);
+static struct umidi_sub_chan *umidi_sub_by_fifo(struct usb2_fifo *);
+static void umidi_start_read(struct usb2_fifo *);
+static void umidi_stop_read(struct usb2_fifo *);
+static void umidi_start_write(struct usb2_fifo *);
+static void umidi_stop_write(struct usb2_fifo *);
+static int umidi_open(struct usb2_fifo *, int, struct thread *);
+static int umidi_ioctl(struct usb2_fifo *, u_long cmd, void *, int, struct thread *);
+static void umidi_close(struct usb2_fifo *, int, struct thread *);
+static void umidi_init(device_t dev);
+static int32_t umidi_probe(device_t dev);
+static int32_t umidi_detach(device_t dev);
+
+#if USB_DEBUG
+static void uaudio_chan_dump_ep_desc(
+ const usb2_endpoint_descriptor_audio_t *);
+static void uaudio_mixer_dump_cluster(uint8_t,
+ const struct uaudio_terminal_node *);
+static const char *uaudio_mixer_get_terminal_name(uint16_t);
+#endif
+
+static const struct usb2_config
+ uaudio_cfg_record[UAUDIO_NCHANBUFS] = {
+ [0] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UAUDIO_MINFRAMES,
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &uaudio_chan_record_callback,
+ },
+
+ [1] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UAUDIO_MINFRAMES,
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &uaudio_chan_record_callback,
+ },
+};
+
+static const struct usb2_config
+ uaudio_cfg_play[UAUDIO_NCHANBUFS] = {
+ [0] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UAUDIO_MINFRAMES,
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &uaudio_chan_play_callback,
+ },
+
+ [1] = {
+ .type = UE_ISOCHRONOUS,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */
+ .mh.frames = UAUDIO_MINFRAMES,
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &uaudio_chan_play_callback,
+ },
+};
+
+static const struct usb2_config
+ uaudio_mixer_config[1] = {
+ [0] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) + 4),
+ .mh.callback = &uaudio_mixer_write_cfg_callback,
+ .mh.timeout = 1000, /* 1 second */
+ },
+};
+
+static const
+uint8_t umidi_cmd_to_len[16] = {
+ [0x0] = 0, /* reserved */
+ [0x1] = 0, /* reserved */
+ [0x2] = 2, /* bytes */
+ [0x3] = 3, /* bytes */
+ [0x4] = 3, /* bytes */
+ [0x5] = 1, /* bytes */
+ [0x6] = 2, /* bytes */
+ [0x7] = 3, /* bytes */
+ [0x8] = 3, /* bytes */
+ [0x9] = 3, /* bytes */
+ [0xA] = 3, /* bytes */
+ [0xB] = 3, /* bytes */
+ [0xC] = 2, /* bytes */
+ [0xD] = 2, /* bytes */
+ [0xE] = 3, /* bytes */
+ [0xF] = 1, /* bytes */
+};
+
+static const struct usb2_config
+ umidi_config[UMIDI_N_TRANSFER] = {
+ [0] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UMIDI_BULK_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &umidi_bulk_write_callback,
+ },
+
+ [1] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UMIDI_BULK_SIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = &umidi_bulk_read_callback,
+ },
+
+ [2] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umidi_write_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+
+ [3] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umidi_read_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+};
+
+static devclass_t uaudio_devclass;
+
+static device_method_t uaudio_methods[] = {
+ DEVMETHOD(device_probe, uaudio_probe),
+ DEVMETHOD(device_attach, uaudio_attach),
+ DEVMETHOD(device_detach, uaudio_detach),
+ DEVMETHOD(device_suspend, bus_generic_suspend),
+ DEVMETHOD(device_resume, bus_generic_resume),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ {0, 0}
+};
+
+static driver_t uaudio_driver = {
+ .name = "uaudio",
+ .methods = uaudio_methods,
+ .size = sizeof(struct uaudio_softc),
+};
+
+static int
+uaudio_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+
+ if (uaa->use_generic == 0)
+ return (ENXIO);
+
+ /* trigger on the control interface */
+
+ if ((uaa->info.bInterfaceClass == UICLASS_AUDIO) &&
+ (uaa->info.bInterfaceSubClass == UISUBCLASS_AUDIOCONTROL)) {
+ if (usb2_test_quirk(uaa, UQ_BAD_AUDIO))
+ return (ENXIO);
+ else
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+uaudio_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct uaudio_softc *sc = device_get_softc(dev);
+ struct usb2_interface_descriptor *id;
+ device_t child;
+
+ sc->sc_play_chan.priv_sc = sc;
+ sc->sc_rec_chan.priv_sc = sc;
+ sc->sc_udev = uaa->device;
+
+ if (usb2_test_quirk(uaa, UQ_AUDIO_SWAP_LR))
+ sc->sc_uq_audio_swap_lr = 1;
+
+ if (usb2_test_quirk(uaa, UQ_AU_INP_ASYNC))
+ sc->sc_uq_au_inp_async = 1;
+
+ if (usb2_test_quirk(uaa, UQ_AU_NO_XU))
+ sc->sc_uq_au_no_xu = 1;
+
+ if (usb2_test_quirk(uaa, UQ_BAD_ADC))
+ sc->sc_uq_bad_adc = 1;
+
+ umidi_init(dev);
+
+ device_set_usb2_desc(dev);
+
+ id = usb2_get_interface_descriptor(uaa->iface);
+
+ uaudio_chan_fill_info(sc, uaa->device);
+
+ uaudio_mixer_fill_info(sc, uaa->device, id);
+
+ sc->sc_mixer_iface_index = uaa->info.bIfaceIndex;
+ sc->sc_mixer_iface_no = uaa->info.bIfaceNum;
+
+ DPRINTF("audio rev %d.%02x\n",
+ sc->sc_audio_rev >> 8,
+ sc->sc_audio_rev & 0xff);
+
+ DPRINTF("%d mixer controls\n",
+ sc->sc_mixer_count);
+
+ if (sc->sc_play_chan.valid) {
+ device_printf(dev, "Play: %d Hz, %d ch, %s format\n",
+ sc->sc_play_chan.sample_rate,
+ sc->sc_play_chan.p_asf1d->bNrChannels,
+ sc->sc_play_chan.p_fmt->description);
+ } else {
+ device_printf(dev, "No playback!\n");
+ }
+
+ if (sc->sc_rec_chan.valid) {
+ device_printf(dev, "Record: %d Hz, %d ch, %s format\n",
+ sc->sc_rec_chan.sample_rate,
+ sc->sc_rec_chan.p_asf1d->bNrChannels,
+ sc->sc_rec_chan.p_fmt->description);
+ } else {
+ device_printf(dev, "No recording!\n");
+ }
+
+ if (sc->sc_midi_chan.valid) {
+
+ if (umidi_probe(dev)) {
+ goto detach;
+ }
+ device_printf(dev, "MIDI sequencer\n");
+ } else {
+ device_printf(dev, "No midi sequencer\n");
+ }
+
+ DPRINTF("doing child attach\n");
+
+ /* attach the children */
+
+ sc->sc_sndcard_func.func = SCF_PCM;
+
+ child = device_add_child(dev, "pcm", -1);
+
+ if (child == NULL) {
+ DPRINTF("out of memory\n");
+ goto detach;
+ }
+ device_set_ivars(child, &sc->sc_sndcard_func);
+
+ if (bus_generic_attach(dev)) {
+ DPRINTF("child attach failed\n");
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ uaudio_detach(dev);
+ return (ENXIO);
+}
+
+static void
+uaudio_pcm_setflags(device_t dev, uint32_t flags)
+{
+ pcm_setflags(dev, pcm_getflags(dev) | flags);
+}
+
+int
+uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_class)
+{
+ struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
+ char status[SND_STATUSLEN];
+
+ uaudio_mixer_init(sc);
+
+ if (sc->sc_uq_audio_swap_lr) {
+ DPRINTF("hardware has swapped left and right\n");
+ uaudio_pcm_setflags(dev, SD_F_PSWAPLR);
+ }
+ if (!(sc->sc_mix_info & SOUND_MASK_PCM)) {
+
+ DPRINTF("emulating master volume\n");
+
+ /*
+ * Emulate missing pcm mixer controller
+ * through FEEDER_VOLUME
+ */
+ uaudio_pcm_setflags(dev, SD_F_SOFTPCMVOL);
+ }
+ if (mixer_init(dev, mixer_class, sc)) {
+ goto detach;
+ }
+ sc->sc_mixer_init = 1;
+
+ snprintf(status, sizeof(status), "at ? %s", PCM_KLDSTRING(snd_uaudio));
+
+ if (pcm_register(dev, sc,
+ sc->sc_play_chan.valid ? 1 : 0,
+ sc->sc_rec_chan.valid ? 1 : 0)) {
+ goto detach;
+ }
+ sc->sc_pcm_registered = 1;
+
+ if (sc->sc_play_chan.valid) {
+ pcm_addchan(dev, PCMDIR_PLAY, chan_class, sc);
+ }
+ if (sc->sc_rec_chan.valid) {
+ pcm_addchan(dev, PCMDIR_REC, chan_class, sc);
+ }
+ pcm_setstatus(dev, status);
+
+ return (0); /* success */
+
+detach:
+ uaudio_detach_sub(dev);
+ return (ENXIO);
+}
+
+int
+uaudio_detach_sub(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(device_get_parent(dev));
+ int error = 0;
+
+repeat:
+ if (sc->sc_pcm_registered) {
+ error = pcm_unregister(dev);
+ } else {
+ if (sc->sc_mixer_init) {
+ error = mixer_uninit(dev);
+ }
+ }
+
+ if (error) {
+ device_printf(dev, "Waiting for sound application to exit!\n");
+ usb2_pause_mtx(NULL, 2 * hz);
+ goto repeat; /* try again */
+ }
+ return (0); /* success */
+}
+
+static int
+uaudio_detach(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+
+ if (bus_generic_detach(dev)) {
+ DPRINTF("detach failed!\n");
+ }
+ sbuf_delete(&sc->sc_sndstat);
+ sc->sc_sndstat_valid = 0;
+
+ umidi_detach(dev);
+
+ return (0);
+}
+
+/*========================================================================*
+ * AS - Audio Stream - routines
+ *========================================================================*/
+
+#if USB_DEBUG
+static void
+uaudio_chan_dump_ep_desc(const usb2_endpoint_descriptor_audio_t *ed)
+{
+ if (ed) {
+ DPRINTF("endpoint=%p bLength=%d bDescriptorType=%d \n"
+ "bEndpointAddress=%d bmAttributes=0x%x \n"
+ "wMaxPacketSize=%d bInterval=%d \n"
+ "bRefresh=%d bSynchAddress=%d\n",
+ ed, ed->bLength, ed->bDescriptorType,
+ ed->bEndpointAddress, ed->bmAttributes,
+ UGETW(ed->wMaxPacketSize), ed->bInterval,
+ ed->bRefresh, ed->bSynchAddress);
+ }
+}
+
+#endif
+
+static void
+uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb2_device *udev,
+ uint32_t rate, uint16_t fps, uint8_t channels,
+ uint8_t bit_resolution)
+{
+ struct usb2_descriptor *desc = NULL;
+ const struct usb2_audio_streaming_interface_descriptor *asid = NULL;
+ const struct usb2_audio_streaming_type1_descriptor *asf1d = NULL;
+ const struct usb2_audio_streaming_endpoint_descriptor *sed = NULL;
+ const usb2_endpoint_descriptor_audio_t *ed1 = NULL;
+ const usb2_endpoint_descriptor_audio_t *ed2 = NULL;
+ struct usb2_config_descriptor *cd = usb2_get_config_descriptor(udev);
+ struct usb2_interface_descriptor *id;
+ const struct uaudio_format *p_fmt;
+ struct uaudio_chan *chan;
+ uint16_t curidx = 0xFFFF;
+ uint16_t lastidx = 0xFFFF;
+ uint16_t alt_index = 0;
+ uint16_t wFormat;
+ uint8_t ep_dir;
+ uint8_t ep_type;
+ uint8_t ep_sync;
+ uint8_t bChannels;
+ uint8_t bBitResolution;
+ uint8_t x;
+ uint8_t audio_if = 0;
+ uint8_t sample_size;
+
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ if ((desc->bDescriptorType == UDESC_INTERFACE) &&
+ (desc->bLength >= sizeof(*id))) {
+
+ id = (void *)desc;
+
+ if (id->bInterfaceNumber != lastidx) {
+ lastidx = id->bInterfaceNumber;
+ curidx++;
+ alt_index = 0;
+
+ } else {
+ alt_index++;
+ }
+
+ if ((id->bInterfaceClass == UICLASS_AUDIO) &&
+ (id->bInterfaceSubClass == UISUBCLASS_AUDIOSTREAM)) {
+ audio_if = 1;
+ } else {
+ audio_if = 0;
+ }
+
+ if ((id->bInterfaceClass == UICLASS_AUDIO) &&
+ (id->bInterfaceSubClass == UISUBCLASS_MIDISTREAM)) {
+
+ /*
+ * XXX could allow multiple MIDI interfaces
+ * XXX
+ */
+
+ if ((sc->sc_midi_chan.valid == 0) &&
+ usb2_get_iface(udev, curidx)) {
+ sc->sc_midi_chan.iface_index = curidx;
+ sc->sc_midi_chan.iface_alt_index = alt_index;
+ sc->sc_midi_chan.valid = 1;
+ }
+ }
+ asid = NULL;
+ asf1d = NULL;
+ ed1 = NULL;
+ ed2 = NULL;
+ sed = NULL;
+ }
+ if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+ (desc->bDescriptorSubtype == AS_GENERAL) &&
+ (desc->bLength >= sizeof(*asid))) {
+ if (asid == NULL) {
+ asid = (void *)desc;
+ }
+ }
+ if ((desc->bDescriptorType == UDESC_CS_INTERFACE) &&
+ (desc->bDescriptorSubtype == FORMAT_TYPE) &&
+ (desc->bLength >= sizeof(*asf1d))) {
+ if (asf1d == NULL) {
+ asf1d = (void *)desc;
+ if (asf1d->bFormatType != FORMAT_TYPE_I) {
+ DPRINTFN(11, "ignored bFormatType = %d\n",
+ asf1d->bFormatType);
+ asf1d = NULL;
+ continue;
+ }
+ if (asf1d->bLength < (sizeof(*asf1d) +
+ (asf1d->bSamFreqType == 0) ? 6 :
+ (asf1d->bSamFreqType * 3))) {
+ DPRINTFN(11, "'asf1d' descriptor is too short\n");
+ asf1d = NULL;
+ continue;
+ }
+ }
+ }
+ if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
+ (desc->bLength >= sizeof(*ed1))) {
+ if (ed1 == NULL) {
+ ed1 = (void *)desc;
+ if (UE_GET_XFERTYPE(ed1->bmAttributes) != UE_ISOCHRONOUS) {
+ ed1 = NULL;
+ }
+ } else {
+ if (ed2 == NULL) {
+ ed2 = (void *)desc;
+ if (UE_GET_XFERTYPE(ed2->bmAttributes) != UE_ISOCHRONOUS) {
+ ed2 = NULL;
+ continue;
+ }
+ if (ed2->bSynchAddress != 0) {
+ DPRINTFN(11, "invalid endpoint: bSynchAddress != 0\n");
+ ed2 = NULL;
+ continue;
+ }
+ if (ed2->bEndpointAddress != ed1->bSynchAddress) {
+ DPRINTFN(11, "invalid endpoint addresses: "
+ "ep[0]->bSynchAddress=0x%x "
+ "ep[1]->bEndpointAddress=0x%x\n",
+ ed1->bSynchAddress,
+ ed2->bEndpointAddress);
+ ed2 = NULL;
+ continue;
+ }
+ }
+ }
+ }
+ if ((desc->bDescriptorType == UDESC_CS_ENDPOINT) &&
+ (desc->bDescriptorSubtype == AS_GENERAL) &&
+ (desc->bLength >= sizeof(*sed))) {
+ if (sed == NULL) {
+ sed = (void *)desc;
+ }
+ }
+ if (audio_if && asid && asf1d && ed1 && sed) {
+
+ ep_dir = UE_GET_DIR(ed1->bEndpointAddress);
+ ep_type = UE_GET_ISO_TYPE(ed1->bmAttributes);
+ ep_sync = 0;
+
+ if ((sc->sc_uq_au_inp_async) &&
+ (ep_dir == UE_DIR_IN) && (ep_type == UE_ISO_ADAPT)) {
+ ep_type = UE_ISO_ASYNC;
+ }
+ if ((ep_dir == UE_DIR_IN) && (ep_type == UE_ISO_ADAPT)) {
+ ep_sync = 1;
+ }
+ if ((ep_dir != UE_DIR_IN) && (ep_type == UE_ISO_ASYNC)) {
+ ep_sync = 1;
+ }
+ /* Ignore sync endpoint information until further. */
+#if 0
+ if (ep_sync && (!ed2)) {
+ continue;
+ }
+ /*
+ * we can't handle endpoints that need a sync pipe
+ * yet
+ */
+
+ if (ep_sync) {
+ DPRINTF("skipped sync interface\n");
+ audio_if = 0;
+ continue;
+ }
+#endif
+
+ wFormat = UGETW(asid->wFormatTag);
+ bChannels = asf1d->bNrChannels;
+ bBitResolution = asf1d->bBitResolution;
+
+ if (asf1d->bSamFreqType == 0) {
+ DPRINTFN(16, "Sample rate: %d-%dHz\n",
+ UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d));
+
+ if ((rate >= UA_SAMP_LO(asf1d)) &&
+ (rate <= UA_SAMP_HI(asf1d))) {
+ goto found_rate;
+ }
+ } else {
+
+ for (x = 0; x < asf1d->bSamFreqType; x++) {
+ DPRINTFN(16, "Sample rate = %dHz\n",
+ UA_GETSAMP(asf1d, x));
+
+ if (rate == UA_GETSAMP(asf1d, x)) {
+ goto found_rate;
+ }
+ }
+ }
+
+ audio_if = 0;
+ continue;
+
+ found_rate:
+
+ for (p_fmt = uaudio_formats;
+ p_fmt->wFormat;
+ p_fmt++) {
+ if ((p_fmt->wFormat == wFormat) &&
+ (p_fmt->bPrecision == bBitResolution)) {
+ goto found_format;
+ }
+ }
+
+ audio_if = 0;
+ continue;
+
+ found_format:
+
+ if ((bChannels == channels) &&
+ (bBitResolution == bit_resolution)) {
+
+ chan = (ep_dir == UE_DIR_IN) ?
+ &sc->sc_rec_chan :
+ &sc->sc_play_chan;
+
+ if ((chan->valid == 0) && usb2_get_iface(udev, curidx)) {
+
+ chan->valid = 1;
+#if USB_DEBUG
+ uaudio_chan_dump_ep_desc(ed1);
+ uaudio_chan_dump_ep_desc(ed2);
+
+ if (sed->bmAttributes & UA_SED_FREQ_CONTROL) {
+ DPRINTFN(2, "FREQ_CONTROL\n");
+ }
+ if (sed->bmAttributes & UA_SED_PITCH_CONTROL) {
+ DPRINTFN(2, "PITCH_CONTROL\n");
+ }
+#endif
+ DPRINTF("Sample rate = %dHz, channels = %d, "
+ "bits = %d, format = %s\n", rate, channels,
+ bit_resolution, p_fmt->description);
+
+ chan->sample_rate = rate;
+ chan->p_asid = asid;
+ chan->p_asf1d = asf1d;
+ chan->p_ed1 = ed1;
+ chan->p_ed2 = ed2;
+ chan->p_fmt = p_fmt;
+ chan->p_sed = sed;
+ chan->iface_index = curidx;
+ chan->iface_alt_index = alt_index;
+
+ if (ep_dir == UE_DIR_IN)
+ chan->usb2_cfg =
+ uaudio_cfg_record;
+ else
+ chan->usb2_cfg =
+ uaudio_cfg_play;
+
+ sample_size = ((chan->p_asf1d->bNrChannels *
+ chan->p_asf1d->bBitResolution) / 8);
+
+ /*
+ * NOTE: "chan->bytes_per_frame"
+ * should not be zero!
+ */
+ chan->bytes_per_frame = ((rate / fps) * sample_size);
+
+ if (sc->sc_sndstat_valid) {
+ sbuf_printf(&sc->sc_sndstat, "\n\t"
+ "mode %d.%d:(%s) %dch, %d/%dbit, %s, %dHz",
+ curidx, alt_index,
+ (ep_dir == UE_DIR_IN) ? "input" : "output",
+ asf1d->bNrChannels, asf1d->bBitResolution,
+ asf1d->bSubFrameSize * 8,
+ p_fmt->description, rate);
+ }
+ }
+ }
+ audio_if = 0;
+ continue;
+ }
+ }
+}
+
+static void
+uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb2_device *udev)
+{
+ uint32_t rate = uaudio_default_rate;
+ uint32_t z;
+ uint16_t fps = usb2_get_isoc_fps(udev);
+ uint8_t bits = uaudio_default_bits;
+ uint8_t y;
+ uint8_t channels = uaudio_default_channels;
+ uint8_t x;
+
+ bits -= (bits % 8);
+ if ((bits == 0) || (bits > 32)) {
+ /* set a valid value */
+ bits = 32;
+ }
+ rate -= (rate % fps);
+ if ((rate == 0) || (rate > 192000)) {
+ /* set a valid value */
+ rate = 192000 - (192000 % fps);
+ }
+ if ((channels == 0) || (channels > 2)) {
+ /* set a valid value */
+ channels = 2;
+ }
+ if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) {
+ sc->sc_sndstat_valid = 1;
+ }
+ /* try to search for a valid config */
+
+ for (x = channels; x; x--) {
+ for (y = bits; y; y -= 8) {
+ for (z = rate; z; z -= fps) {
+ uaudio_chan_fill_info_sub(sc, udev, z, fps, x, y);
+
+ if (sc->sc_rec_chan.valid &&
+ sc->sc_play_chan.valid) {
+ goto done;
+ }
+ }
+ }
+ }
+
+done:
+ if (sc->sc_sndstat_valid) {
+ sbuf_finish(&sc->sc_sndstat);
+ }
+}
+
+static void
+uaudio_chan_play_callback(struct usb2_xfer *xfer)
+{
+ struct uaudio_chan *ch = xfer->priv_sc;
+ uint32_t *p_len = xfer->frlengths;
+ uint32_t total;
+ uint32_t blockcount;
+ uint32_t n;
+ uint32_t offset;
+
+ /* allow dynamic sizing of play buffer */
+ total = ch->intr_size;
+
+ /* allow dynamic sizing of play buffer */
+ blockcount = total / ch->bytes_per_frame;
+
+ /* align units */
+ blockcount -= (blockcount % UAUDIO_MINFRAMES);
+
+ /* range check - min */
+ if (blockcount == 0) {
+ blockcount = UAUDIO_MINFRAMES;
+ }
+ /* range check - max */
+ if (blockcount > xfer->max_frame_count) {
+ blockcount = xfer->max_frame_count;
+ }
+ /* compute the total length */
+ total = blockcount * ch->bytes_per_frame;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ if (xfer->actlen < xfer->sumlen) {
+ DPRINTF("short transfer, "
+ "%d of %d bytes\n", xfer->actlen, total);
+ }
+ chn_intr(ch->pcm_ch);
+
+ case USB_ST_SETUP:
+ if (ch->bytes_per_frame > xfer->max_frame_size) {
+ DPRINTF("bytes per transfer, %d, "
+ "exceeds maximum, %d!\n",
+ ch->bytes_per_frame,
+ xfer->max_frame_size);
+ break;
+ }
+ /* setup frame length */
+ xfer->nframes = blockcount;
+ for (n = 0; n != blockcount; n++) {
+ p_len[n] = ch->bytes_per_frame;
+ }
+
+ if (ch->end == ch->start) {
+ DPRINTF("no buffer!\n");
+ break;
+ }
+ DPRINTFN(6, "transfer %d bytes\n", total);
+
+ offset = 0;
+
+ while (total > 0) {
+
+ n = (ch->end - ch->cur);
+ if (n > total) {
+ n = total;
+ }
+ usb2_copy_in(xfer->frbuffers, offset, ch->cur, n);
+
+ total -= n;
+ ch->cur += n;
+ offset += n;
+
+ if (ch->cur >= ch->end) {
+ ch->cur = ch->start;
+ }
+ }
+
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ goto tr_transferred;
+ }
+}
+
+static void
+uaudio_chan_record_callback(struct usb2_xfer *xfer)
+{
+ struct uaudio_chan *ch = xfer->priv_sc;
+ uint32_t *p_len = xfer->frlengths;
+ uint32_t n;
+ uint32_t m;
+ uint32_t total;
+ uint32_t blockcount;
+ uint32_t offset0;
+ uint32_t offset1;
+
+ /* allow dynamic sizing of play buffer */
+ total = ch->intr_size;
+
+ /* allow dynamic sizing of play buffer */
+ blockcount = total / ch->bytes_per_frame;
+
+ /* align units */
+ blockcount -= (blockcount % UAUDIO_MINFRAMES);
+
+ /* range check - min */
+ if (blockcount == 0) {
+ blockcount = UAUDIO_MINFRAMES;
+ }
+ /* range check - max */
+ if (blockcount > xfer->max_frame_count) {
+ blockcount = xfer->max_frame_count;
+ }
+ /* compute the total length */
+ total = blockcount * ch->bytes_per_frame;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ if (xfer->actlen < total) {
+ DPRINTF("short transfer, "
+ "%d of %d bytes\n", xfer->actlen, total);
+ } else {
+ DPRINTFN(6, "transferred %d bytes\n", xfer->actlen);
+ }
+
+ offset0 = 0;
+
+ for (n = 0; n != xfer->nframes; n++) {
+
+ offset1 = offset0;
+
+ while (p_len[n] > 0) {
+
+ m = (ch->end - ch->cur);
+
+ if (m > p_len[n]) {
+ m = p_len[n];
+ }
+ usb2_copy_out(xfer->frbuffers, offset1, ch->cur, m);
+
+ p_len[n] -= m;
+ offset1 += m;
+ ch->cur += m;
+
+ if (ch->cur >= ch->end) {
+ ch->cur = ch->start;
+ }
+ }
+
+ offset0 += ch->bytes_per_frame;
+ }
+
+ chn_intr(ch->pcm_ch);
+
+ case USB_ST_SETUP:
+ if (ch->bytes_per_frame > xfer->max_frame_size) {
+ DPRINTF("bytes per transfer, %d, "
+ "exceeds maximum, %d!\n",
+ ch->bytes_per_frame,
+ xfer->max_frame_size);
+ return;
+ }
+ xfer->nframes = blockcount;
+ for (n = 0; n != xfer->nframes; n++) {
+ p_len[n] = ch->bytes_per_frame;
+ }
+
+ if (ch->end == ch->start) {
+ DPRINTF("no buffer!\n");
+ return;
+ }
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ return;
+ }
+ goto tr_transferred;
+ }
+}
+
+void *
+uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ?
+ &sc->sc_play_chan : &sc->sc_rec_chan);
+ uint32_t buf_size;
+ uint8_t endpoint;
+ uint8_t iface_index;
+ uint8_t alt_index;
+ usb2_error_t err;
+
+ /* compute required buffer size */
+ buf_size = (ch->bytes_per_frame * UAUDIO_MINFRAMES);
+
+ /* setup interrupt interval */
+ ch->intr_size = buf_size;
+
+ /* double buffering */
+ buf_size *= 2;
+
+ ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (ch->buf == NULL) {
+ goto error;
+ }
+ if (sndbuf_setup(b, ch->buf, buf_size) != 0) {
+ goto error;
+ }
+ ch->start = ch->buf;
+ ch->end = ch->buf + buf_size;
+ ch->cur = ch->buf;
+ ch->pcm_ch = c;
+ ch->pcm_mtx = c->lock;
+ ch->pcm_buf = b;
+
+ if (ch->pcm_mtx == NULL) {
+ DPRINTF("ERROR: PCM channels does not have a mutex!\n");
+ goto error;
+ }
+ /* setup play/record format */
+
+ ch->pcm_cap.fmtlist = ch->pcm_format;
+
+ ch->pcm_format[0] = 0;
+ ch->pcm_format[1] = 0;
+
+ ch->pcm_cap.minspeed = ch->sample_rate;
+ ch->pcm_cap.maxspeed = ch->sample_rate;
+
+ ch->pcm_cap.fmtlist[0] = ch->p_fmt->freebsd_fmt;
+
+ if (ch->p_asf1d->bNrChannels == 2) {
+ ch->pcm_cap.fmtlist[0] |= AFMT_STEREO;
+ }
+ ch->pcm_cap.fmtlist[1] = 0;
+
+
+ /* set alternate interface corresponding to the mode */
+
+ endpoint = ch->p_ed1->bEndpointAddress;
+ iface_index = ch->iface_index;
+ alt_index = ch->iface_alt_index;
+
+ DPRINTF("endpoint=0x%02x, speed=%d, iface=%d alt=%d\n",
+ endpoint, ch->sample_rate, iface_index, alt_index);
+
+ err = usb2_set_alt_interface_index(sc->sc_udev, iface_index, alt_index);
+ if (err) {
+ DPRINTF("setting of alternate index failed: %s!\n",
+ usb2_errstr(err));
+ goto error;
+ }
+ usb2_set_parent_iface(sc->sc_udev, iface_index, sc->sc_mixer_iface_index);
+
+ /*
+ * If just one sampling rate is supported,
+ * no need to call "uaudio_set_speed()".
+ * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request.
+ */
+ if (ch->p_asf1d->bSamFreqType != 1) {
+ if (uaudio_set_speed(sc->sc_udev, endpoint, ch->sample_rate)) {
+ /*
+ * If the endpoint is adaptive setting the speed may
+ * fail.
+ */
+ DPRINTF("setting of sample rate failed! (continuing anyway)\n");
+ }
+ }
+ if (usb2_transfer_setup(sc->sc_udev, &iface_index, ch->xfer,
+ ch->usb2_cfg, UAUDIO_NCHANBUFS, ch, ch->pcm_mtx)) {
+ DPRINTF("could not allocate USB transfers!\n");
+ goto error;
+ }
+ return (ch);
+
+error:
+ uaudio_chan_free(ch);
+ return (NULL);
+}
+
+int
+uaudio_chan_free(struct uaudio_chan *ch)
+{
+ if (ch->buf != NULL) {
+ free(ch->buf, M_DEVBUF);
+ ch->buf = NULL;
+ }
+ usb2_transfer_unsetup(ch->xfer, UAUDIO_NCHANBUFS);
+
+ ch->valid = 0;
+
+ return (0);
+}
+
+int
+uaudio_chan_set_param_blocksize(struct uaudio_chan *ch, uint32_t blocksize)
+{
+ uaudio_chan_set_param_fragments(ch, blocksize, 0 - 1);
+
+ return (ch->block_size);
+}
+
+int
+uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize,
+ uint32_t blockcount)
+{
+ /* we only support one size */
+ blocksize = ch->intr_size;
+ blockcount = 2;
+
+ if ((sndbuf_getblksz(ch->pcm_buf) != blocksize) ||
+ (sndbuf_getblkcnt(ch->pcm_buf) != blockcount)) {
+ DPRINTFN(1, "resizing to %u x "
+ "%u bytes\n", blockcount, blocksize);
+ if (sndbuf_resize(ch->pcm_buf, blockcount, blocksize)) {
+ DPRINTFN(0, "failed to resize sound buffer, count=%u, "
+ "size=%u\n", blockcount, blocksize);
+ }
+ }
+ ch->block_size = sndbuf_getblksz(ch->pcm_buf);
+
+ return (1);
+}
+
+int
+uaudio_chan_set_param_speed(struct uaudio_chan *ch, uint32_t speed)
+{
+ if (speed != ch->sample_rate) {
+ DPRINTF("rate conversion required\n");
+ }
+ return (ch->sample_rate);
+}
+
+int
+uaudio_chan_getptr(struct uaudio_chan *ch)
+{
+ return (ch->cur - ch->start);
+}
+
+struct pcmchan_caps *
+uaudio_chan_getcaps(struct uaudio_chan *ch)
+{
+ return (&ch->pcm_cap);
+}
+
+int
+uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format)
+{
+ ch->format = format;
+ return (0);
+}
+
+int
+uaudio_chan_start(struct uaudio_chan *ch)
+{
+ ch->cur = ch->start;
+
+#if (UAUDIO_NCHANBUFS != 2)
+#error "please update code"
+#endif
+ if (ch->xfer[0]) {
+ usb2_transfer_start(ch->xfer[0]);
+ }
+ if (ch->xfer[1]) {
+ usb2_transfer_start(ch->xfer[1]);
+ }
+ return (0);
+}
+
+int
+uaudio_chan_stop(struct uaudio_chan *ch)
+{
+#if (UAUDIO_NCHANBUFS != 2)
+#error "please update code"
+#endif
+ usb2_transfer_stop(ch->xfer[0]);
+ usb2_transfer_stop(ch->xfer[1]);
+ return (0);
+}
+
+/*========================================================================*
+ * AC - Audio Controller - routines
+ *========================================================================*/
+
+static void
+uaudio_mixer_add_ctl_sub(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
+{
+ struct uaudio_mixer_node *p_mc_new =
+ malloc(sizeof(*p_mc_new), M_USBDEV, M_WAITOK);
+
+ if (p_mc_new) {
+ bcopy(mc, p_mc_new, sizeof(*p_mc_new));
+ p_mc_new->next = sc->sc_mixer_root;
+ sc->sc_mixer_root = p_mc_new;
+ sc->sc_mixer_count++;
+ } else {
+ DPRINTF("out of memory\n");
+ }
+}
+
+static void
+uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct uaudio_mixer_node *mc)
+{
+ int32_t res;
+
+ if (mc->class < UAC_NCLASSES) {
+ DPRINTF("adding %s.%d\n",
+ uac_names[mc->class], mc->ctl);
+ } else {
+ DPRINTF("adding %d\n", mc->ctl);
+ }
+
+ mc->delta = 0;
+ if (mc->type == MIX_ON_OFF) {
+ mc->minval = 0;
+ mc->maxval = 1;
+ } else if (mc->type == MIX_SELECTOR) {
+
+ } else {
+
+ /* determine min and max values */
+
+ mc->minval = uaudio_mixer_get(sc->sc_udev, GET_MIN, mc);
+
+ mc->minval = uaudio_mixer_signext(mc->type, mc->minval);
+
+ mc->maxval = uaudio_mixer_get(sc->sc_udev, GET_MAX, mc);
+
+ mc->maxval = 1 + uaudio_mixer_signext(mc->type, mc->maxval);
+
+ mc->mul = mc->maxval - mc->minval;
+ if (mc->mul == 0) {
+ mc->mul = 1;
+ }
+ res = uaudio_mixer_get(sc->sc_udev, GET_RES, mc);
+ if (res > 0) {
+ mc->delta = ((res * 255) + (mc->mul / 2)) / mc->mul;
+ }
+ }
+
+ if (mc->maxval < mc->minval) {
+ mc->maxval = mc->minval;
+ }
+ uaudio_mixer_add_ctl_sub(sc, mc);
+
+#if USB_DEBUG
+ if (uaudio_debug > 2) {
+ uint8_t i;
+
+ for (i = 0; i < mc->nchan; i++) {
+ DPRINTF("[mix] wValue=%04x\n", mc->wValue[0]);
+ }
+ DPRINTF("[mix] wIndex=%04x type=%d ctl='%d' "
+ "min=%d max=%d\n",
+ mc->wIndex, mc->type, mc->ctl,
+ mc->minval, mc->maxval);
+ }
+#endif
+}
+
+static void
+uaudio_mixer_add_input(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+#if USB_DEBUG
+ const struct usb2_audio_input_terminal *d = iot[id].u.it;
+
+ DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x "
+ "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d "
+ "iChannelNames=%d\n",
+ d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
+ d->bNrChannels, UGETW(d->wChannelConfig),
+ d->iChannelNames);
+#endif
+}
+
+static void
+uaudio_mixer_add_output(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+#if USB_DEBUG
+ const struct usb2_audio_output_terminal *d = iot[id].u.ot;
+
+ DPRINTFN(3, "bTerminalId=%d wTerminalType=0x%04x "
+ "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n",
+ d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal,
+ d->bSourceId, d->iTerminal);
+#endif
+}
+
+static void
+uaudio_mixer_add_mixer(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+ struct uaudio_mixer_node mix;
+
+ const struct usb2_audio_mixer_unit_0 *d0 = iot[id].u.mu;
+ const struct usb2_audio_mixer_unit_1 *d1;
+
+ uint32_t bno; /* bit number */
+ uint32_t p; /* bit number accumulator */
+ uint32_t mo; /* matching outputs */
+ uint32_t mc; /* matching channels */
+ uint32_t ichs; /* input channels */
+ uint32_t ochs; /* output channels */
+ uint32_t c;
+ uint32_t chs; /* channels */
+ uint32_t i;
+ uint32_t o;
+
+ DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
+ d0->bUnitId, d0->bNrInPins);
+
+ /* compute the number of input channels */
+
+ ichs = 0;
+ for (i = 0; i < d0->bNrInPins; i++) {
+ ichs += (uaudio_mixer_get_cluster(d0->baSourceId[i], iot)
+ .bNrChannels);
+ }
+
+ d1 = (const void *)(d0->baSourceId + d0->bNrInPins);
+
+ /* and the number of output channels */
+
+ ochs = d1->bNrChannels;
+
+ DPRINTFN(3, "ichs=%d ochs=%d\n", ichs, ochs);
+
+ bzero(&mix, sizeof(mix));
+
+ mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+ uaudio_mixer_determine_class(&iot[id], &mix);
+ mix.type = MIX_SIGNED_16;
+
+ if (uaudio_mixer_verify_desc(d0, ((ichs * ochs) + 7) / 8) == NULL) {
+ return;
+ }
+ for (p = i = 0; i < d0->bNrInPins; i++) {
+ chs = uaudio_mixer_get_cluster(d0->baSourceId[i], iot).bNrChannels;
+ mc = 0;
+ for (c = 0; c < chs; c++) {
+ mo = 0;
+ for (o = 0; o < ochs; o++) {
+ bno = ((p + c) * ochs) + o;
+ if (BIT_TEST(d1->bmControls, bno)) {
+ mo++;
+ }
+ }
+ if (mo == 1) {
+ mc++;
+ }
+ }
+ if ((mc == chs) && (chs <= MIX_MAX_CHAN)) {
+
+ /* repeat bit-scan */
+
+ mc = 0;
+ for (c = 0; c < chs; c++) {
+ for (o = 0; o < ochs; o++) {
+ bno = ((p + c) * ochs) + o;
+ if (BIT_TEST(d1->bmControls, bno)) {
+ mix.wValue[mc++] = MAKE_WORD(p + c + 1, o + 1);
+ }
+ }
+ }
+ mix.nchan = chs;
+ uaudio_mixer_add_ctl(sc, &mix);
+ } else {
+ /* XXX */
+ }
+ p += chs;
+ }
+}
+
+static void
+uaudio_mixer_add_selector(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+ const struct usb2_audio_selector_unit *d = iot[id].u.su;
+ struct uaudio_mixer_node mix;
+ uint16_t i;
+
+ DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
+ d->bUnitId, d->bNrInPins);
+
+ if (d->bNrInPins == 0) {
+ return;
+ }
+ bzero(&mix, sizeof(mix));
+
+ mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+ mix.wValue[0] = MAKE_WORD(0, 0);
+ uaudio_mixer_determine_class(&iot[id], &mix);
+ mix.nchan = 1;
+ mix.type = MIX_SELECTOR;
+
+ mix.ctl = SOUND_MIXER_NRDEVICES;
+ mix.minval = 1;
+ mix.maxval = d->bNrInPins;
+
+ if (mix.maxval > MAX_SELECTOR_INPUT_PIN) {
+ mix.maxval = MAX_SELECTOR_INPUT_PIN;
+ }
+ mix.mul = (mix.maxval - mix.minval);
+ for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) {
+ mix.slctrtype[i] = SOUND_MIXER_NRDEVICES;
+ }
+
+ for (i = 0; i < mix.maxval; i++) {
+ mix.slctrtype[i] = uaudio_mixer_feature_name
+ (&iot[d->baSourceId[i]], &mix);
+ }
+
+ mix.class = 0; /* not used */
+
+ uaudio_mixer_add_ctl(sc, &mix);
+}
+
+static uint32_t
+uaudio_mixer_feature_get_bmaControls(const struct usb2_audio_feature_unit *d,
+ uint8_t index)
+{
+ uint32_t temp = 0;
+ uint32_t offset = (index * d->bControlSize);
+
+ if (d->bControlSize > 0) {
+ temp |= d->bmaControls[offset];
+ if (d->bControlSize > 1) {
+ temp |= d->bmaControls[offset + 1] << 8;
+ if (d->bControlSize > 2) {
+ temp |= d->bmaControls[offset + 2] << 16;
+ if (d->bControlSize > 3) {
+ temp |= d->bmaControls[offset + 3] << 24;
+ }
+ }
+ }
+ }
+ return (temp);
+}
+
+static void
+uaudio_mixer_add_feature(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+ const struct usb2_audio_feature_unit *d = iot[id].u.fu;
+ struct uaudio_mixer_node mix;
+ uint32_t fumask;
+ uint32_t mmask;
+ uint32_t cmask;
+ uint16_t mixernumber;
+ uint8_t nchan;
+ uint8_t chan;
+ uint8_t ctl;
+ uint8_t i;
+
+ if (d->bControlSize == 0) {
+ return;
+ }
+ bzero(&mix, sizeof(mix));
+
+ nchan = (d->bLength - 7) / d->bControlSize;
+ mmask = uaudio_mixer_feature_get_bmaControls(d, 0);
+ cmask = 0;
+
+ if (nchan == 0) {
+ return;
+ }
+ /* figure out what we can control */
+
+ for (chan = 1; chan < nchan; chan++) {
+ DPRINTFN(10, "chan=%d mask=%x\n",
+ chan, uaudio_mixer_feature_get_bmaControls(d, chan));
+
+ cmask |= uaudio_mixer_feature_get_bmaControls(d, chan);
+ }
+
+ if (nchan > MIX_MAX_CHAN) {
+ nchan = MIX_MAX_CHAN;
+ }
+ mix.wIndex = MAKE_WORD(d->bUnitId, sc->sc_mixer_iface_no);
+
+ for (ctl = 1; ctl <= LOUDNESS_CONTROL; ctl++) {
+
+ fumask = FU_MASK(ctl);
+
+ DPRINTFN(5, "ctl=%d fumask=0x%04x\n",
+ ctl, fumask);
+
+ if (mmask & fumask) {
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE_WORD(ctl, 0);
+ } else if (cmask & fumask) {
+ mix.nchan = nchan - 1;
+ for (i = 1; i < nchan; i++) {
+ if (uaudio_mixer_feature_get_bmaControls(d, i) & fumask)
+ mix.wValue[i - 1] = MAKE_WORD(ctl, i);
+ else
+ mix.wValue[i - 1] = -1;
+ }
+ } else {
+ continue;
+ }
+
+ mixernumber = uaudio_mixer_feature_name(&iot[id], &mix);
+
+ switch (ctl) {
+ case MUTE_CONTROL:
+ mix.type = MIX_ON_OFF;
+ mix.ctl = SOUND_MIXER_NRDEVICES;
+ break;
+
+ case VOLUME_CONTROL:
+ mix.type = MIX_SIGNED_16;
+ mix.ctl = mixernumber;
+ break;
+
+ case BASS_CONTROL:
+ mix.type = MIX_SIGNED_8;
+ mix.ctl = SOUND_MIXER_BASS;
+ break;
+
+ case MID_CONTROL:
+ mix.type = MIX_SIGNED_8;
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+ break;
+
+ case TREBLE_CONTROL:
+ mix.type = MIX_SIGNED_8;
+ mix.ctl = SOUND_MIXER_TREBLE;
+ break;
+
+ case GRAPHIC_EQUALIZER_CONTROL:
+ continue; /* XXX don't add anything */
+ break;
+
+ case AGC_CONTROL:
+ mix.type = MIX_ON_OFF;
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+ break;
+
+ case DELAY_CONTROL:
+ mix.type = MIX_UNSIGNED_16;
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+ break;
+
+ case BASS_BOOST_CONTROL:
+ mix.type = MIX_ON_OFF;
+ mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */
+ break;
+
+ case LOUDNESS_CONTROL:
+ mix.type = MIX_ON_OFF;
+ mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */
+ break;
+
+ default:
+ mix.type = MIX_UNKNOWN;
+ break;
+ }
+
+ if (mix.type != MIX_UNKNOWN) {
+ uaudio_mixer_add_ctl(sc, &mix);
+ }
+ }
+}
+
+static void
+uaudio_mixer_add_processing_updown(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+ const struct usb2_audio_processing_unit_0 *d0 = iot[id].u.pu;
+ const struct usb2_audio_processing_unit_1 *d1 =
+ (const void *)(d0->baSourceId + d0->bNrInPins);
+ const struct usb2_audio_processing_unit_updown *ud =
+ (const void *)(d1->bmControls + d1->bControlSize);
+ struct uaudio_mixer_node mix;
+ uint8_t i;
+
+ if (uaudio_mixer_verify_desc(d0, sizeof(*ud)) == NULL) {
+ return;
+ }
+ if (uaudio_mixer_verify_desc(d0, sizeof(*ud) + (2 * ud->bNrModes))
+ == NULL) {
+ return;
+ }
+ DPRINTFN(3, "bUnitId=%d bNrModes=%d\n",
+ d0->bUnitId, ud->bNrModes);
+
+ if (!(d1->bmControls[0] & UA_PROC_MASK(UD_MODE_SELECT_CONTROL))) {
+ DPRINTF("no mode select\n");
+ return;
+ }
+ bzero(&mix, sizeof(mix));
+
+ mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE_WORD(UD_MODE_SELECT_CONTROL, 0);
+ uaudio_mixer_determine_class(&iot[id], &mix);
+ mix.type = MIX_ON_OFF; /* XXX */
+
+ for (i = 0; i < ud->bNrModes; i++) {
+ DPRINTFN(3, "i=%d bm=0x%x\n", i, UGETW(ud->waModes[i]));
+ /* XXX */
+ }
+
+ uaudio_mixer_add_ctl(sc, &mix);
+}
+
+static void
+uaudio_mixer_add_processing(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+ const struct usb2_audio_processing_unit_0 *d0 = iot[id].u.pu;
+ const struct usb2_audio_processing_unit_1 *d1 =
+ (const void *)(d0->baSourceId + d0->bNrInPins);
+ struct uaudio_mixer_node mix;
+ uint16_t ptype;
+
+ bzero(&mix, sizeof(mix));
+
+ ptype = UGETW(d0->wProcessType);
+
+ DPRINTFN(3, "wProcessType=%d bUnitId=%d "
+ "bNrInPins=%d\n", ptype, d0->bUnitId, d0->bNrInPins);
+
+ if (d1->bControlSize == 0) {
+ return;
+ }
+ if (d1->bmControls[0] & UA_PROC_ENABLE_MASK) {
+ mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE_WORD(XX_ENABLE_CONTROL, 0);
+ uaudio_mixer_determine_class(&iot[id], &mix);
+ mix.type = MIX_ON_OFF;
+ uaudio_mixer_add_ctl(sc, &mix);
+ }
+ switch (ptype) {
+ case UPDOWNMIX_PROCESS:
+ uaudio_mixer_add_processing_updown(sc, iot, id);
+ break;
+
+ case DOLBY_PROLOGIC_PROCESS:
+ case P3D_STEREO_EXTENDER_PROCESS:
+ case REVERBATION_PROCESS:
+ case CHORUS_PROCESS:
+ case DYN_RANGE_COMP_PROCESS:
+ default:
+ DPRINTF("unit %d, type=%d is not implemented\n",
+ d0->bUnitId, ptype);
+ break;
+ }
+}
+
+static void
+uaudio_mixer_add_extension(struct uaudio_softc *sc,
+ const struct uaudio_terminal_node *iot, int id)
+{
+ const struct usb2_audio_extension_unit_0 *d0 = iot[id].u.eu;
+ const struct usb2_audio_extension_unit_1 *d1 =
+ (const void *)(d0->baSourceId + d0->bNrInPins);
+ struct uaudio_mixer_node mix;
+
+ DPRINTFN(3, "bUnitId=%d bNrInPins=%d\n",
+ d0->bUnitId, d0->bNrInPins);
+
+ if (sc->sc_uq_au_no_xu) {
+ return;
+ }
+ if (d1->bControlSize == 0) {
+ return;
+ }
+ if (d1->bmControls[0] & UA_EXT_ENABLE_MASK) {
+
+ bzero(&mix, sizeof(mix));
+
+ mix.wIndex = MAKE_WORD(d0->bUnitId, sc->sc_mixer_iface_no);
+ mix.nchan = 1;
+ mix.wValue[0] = MAKE_WORD(UA_EXT_ENABLE, 0);
+ uaudio_mixer_determine_class(&iot[id], &mix);
+ mix.type = MIX_ON_OFF;
+
+ uaudio_mixer_add_ctl(sc, &mix);
+ }
+}
+
+static const void *
+uaudio_mixer_verify_desc(const void *arg, uint32_t len)
+{
+ const struct usb2_audio_mixer_unit_1 *d1;
+ const struct usb2_audio_extension_unit_1 *e1;
+ const struct usb2_audio_processing_unit_1 *u1;
+
+ union {
+ const struct usb2_descriptor *desc;
+ const struct usb2_audio_input_terminal *it;
+ const struct usb2_audio_output_terminal *ot;
+ const struct usb2_audio_mixer_unit_0 *mu;
+ const struct usb2_audio_selector_unit *su;
+ const struct usb2_audio_feature_unit *fu;
+ const struct usb2_audio_processing_unit_0 *pu;
+ const struct usb2_audio_extension_unit_0 *eu;
+ } u;
+
+ u.desc = arg;
+
+ if (u.desc == NULL) {
+ goto error;
+ }
+ if (u.desc->bDescriptorType != UDESC_CS_INTERFACE) {
+ goto error;
+ }
+ switch (u.desc->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ len += sizeof(*u.it);
+ break;
+
+ case UDESCSUB_AC_OUTPUT:
+ len += sizeof(*u.ot);
+ break;
+
+ case UDESCSUB_AC_MIXER:
+ len += sizeof(*u.mu);
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ len += u.mu->bNrInPins;
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ d1 = (const void *)(u.mu->baSourceId + u.mu->bNrInPins);
+
+ len += sizeof(*d1);
+ break;
+
+ case UDESCSUB_AC_SELECTOR:
+ len += sizeof(*u.su);
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ len += u.su->bNrInPins;
+ break;
+
+ case UDESCSUB_AC_FEATURE:
+ len += (sizeof(*u.fu) + 1);
+ break;
+
+ case UDESCSUB_AC_PROCESSING:
+ len += sizeof(*u.pu);
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ len += u.pu->bNrInPins;
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ u1 = (const void *)(u.pu->baSourceId + u.pu->bNrInPins);
+
+ len += sizeof(*u1);
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ len += u1->bControlSize;
+
+ break;
+
+ case UDESCSUB_AC_EXTENSION:
+ len += sizeof(*u.eu);
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ len += u.eu->bNrInPins;
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ e1 = (const void *)(u.eu->baSourceId + u.eu->bNrInPins);
+
+ len += sizeof(*e1);
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ len += e1->bControlSize;
+ break;
+
+ default:
+ goto error;
+ }
+
+ if (u.desc->bLength < len) {
+ goto error;
+ }
+ return (u.desc);
+
+error:
+ if (u.desc) {
+ DPRINTF("invalid descriptor, type=%d, "
+ "sub_type=%d, len=%d of %d bytes\n",
+ u.desc->bDescriptorType,
+ u.desc->bDescriptorSubtype,
+ u.desc->bLength, len);
+ }
+ return (NULL);
+}
+
+#if USB_DEBUG
+static void
+uaudio_mixer_dump_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
+{
+ static const char *channel_names[16] = {
+ "LEFT", "RIGHT", "CENTER", "LFE",
+ "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER",
+ "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP",
+ "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15",
+ };
+ uint16_t cc;
+ uint8_t i;
+ const struct usb2_audio_cluster cl = uaudio_mixer_get_cluster(id, iot);
+
+ cc = UGETW(cl.wChannelConfig);
+
+ DPRINTF("cluster: bNrChannels=%u iChannelNames=%u wChannelConfig="
+ "0x%04x:\n", cl.iChannelNames, cl.bNrChannels, cc);
+
+ for (i = 0; cc; i++) {
+ if (cc & 1) {
+ DPRINTF(" - %s\n", channel_names[i]);
+ }
+ cc >>= 1;
+ }
+}
+
+#endif
+
+static struct usb2_audio_cluster
+uaudio_mixer_get_cluster(uint8_t id, const struct uaudio_terminal_node *iot)
+{
+ struct usb2_audio_cluster r;
+ const struct usb2_descriptor *dp;
+ uint8_t i;
+
+ for (i = 0; i < UAUDIO_RECURSE_LIMIT; i++) { /* avoid infinite loops */
+ dp = iot[id].u.desc;
+ if (dp == NULL) {
+ goto error;
+ }
+ switch (dp->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ r.bNrChannels = iot[id].u.it->bNrChannels;
+ r.wChannelConfig[0] = iot[id].u.it->wChannelConfig[0];
+ r.wChannelConfig[1] = iot[id].u.it->wChannelConfig[1];
+ r.iChannelNames = iot[id].u.it->iChannelNames;
+ goto done;
+
+ case UDESCSUB_AC_OUTPUT:
+ id = iot[id].u.ot->bSourceId;
+ break;
+
+ case UDESCSUB_AC_MIXER:
+ r = *(const struct usb2_audio_cluster *)
+ &iot[id].u.mu->baSourceId[iot[id].u.mu->
+ bNrInPins];
+ goto done;
+
+ case UDESCSUB_AC_SELECTOR:
+ if (iot[id].u.su->bNrInPins > 0) {
+ /* XXX This is not really right */
+ id = iot[id].u.su->baSourceId[0];
+ }
+ break;
+
+ case UDESCSUB_AC_FEATURE:
+ id = iot[id].u.fu->bSourceId;
+ break;
+
+ case UDESCSUB_AC_PROCESSING:
+ r = *((const struct usb2_audio_cluster *)
+ &iot[id].u.pu->baSourceId[iot[id].u.pu->
+ bNrInPins]);
+ goto done;
+
+ case UDESCSUB_AC_EXTENSION:
+ r = *((const struct usb2_audio_cluster *)
+ &iot[id].u.eu->baSourceId[iot[id].u.eu->
+ bNrInPins]);
+ goto done;
+
+ default:
+ goto error;
+ }
+ }
+error:
+ DPRINTF("bad data\n");
+ bzero(&r, sizeof(r));
+done:
+ return (r);
+}
+
+#if USB_DEBUG
+
+struct uaudio_tt_to_string {
+ uint16_t terminal_type;
+ const char *desc;
+};
+
+static const struct uaudio_tt_to_string uaudio_tt_to_string[] = {
+
+ /* USB terminal types */
+ {UAT_UNDEFINED, "UAT_UNDEFINED"},
+ {UAT_STREAM, "UAT_STREAM"},
+ {UAT_VENDOR, "UAT_VENDOR"},
+
+ /* input terminal types */
+ {UATI_UNDEFINED, "UATI_UNDEFINED"},
+ {UATI_MICROPHONE, "UATI_MICROPHONE"},
+ {UATI_DESKMICROPHONE, "UATI_DESKMICROPHONE"},
+ {UATI_PERSONALMICROPHONE, "UATI_PERSONALMICROPHONE"},
+ {UATI_OMNIMICROPHONE, "UATI_OMNIMICROPHONE"},
+ {UATI_MICROPHONEARRAY, "UATI_MICROPHONEARRAY"},
+ {UATI_PROCMICROPHONEARR, "UATI_PROCMICROPHONEARR"},
+
+ /* output terminal types */
+ {UATO_UNDEFINED, "UATO_UNDEFINED"},
+ {UATO_SPEAKER, "UATO_SPEAKER"},
+ {UATO_HEADPHONES, "UATO_HEADPHONES"},
+ {UATO_DISPLAYAUDIO, "UATO_DISPLAYAUDIO"},
+ {UATO_DESKTOPSPEAKER, "UATO_DESKTOPSPEAKER"},
+ {UATO_ROOMSPEAKER, "UATO_ROOMSPEAKER"},
+ {UATO_COMMSPEAKER, "UATO_COMMSPEAKER"},
+ {UATO_SUBWOOFER, "UATO_SUBWOOFER"},
+
+ /* bidir terminal types */
+ {UATB_UNDEFINED, "UATB_UNDEFINED"},
+ {UATB_HANDSET, "UATB_HANDSET"},
+ {UATB_HEADSET, "UATB_HEADSET"},
+ {UATB_SPEAKERPHONE, "UATB_SPEAKERPHONE"},
+ {UATB_SPEAKERPHONEESUP, "UATB_SPEAKERPHONEESUP"},
+ {UATB_SPEAKERPHONEECANC, "UATB_SPEAKERPHONEECANC"},
+
+ /* telephony terminal types */
+ {UATT_UNDEFINED, "UATT_UNDEFINED"},
+ {UATT_PHONELINE, "UATT_PHONELINE"},
+ {UATT_TELEPHONE, "UATT_TELEPHONE"},
+ {UATT_DOWNLINEPHONE, "UATT_DOWNLINEPHONE"},
+
+ /* external terminal types */
+ {UATE_UNDEFINED, "UATE_UNDEFINED"},
+ {UATE_ANALOGCONN, "UATE_ANALOGCONN"},
+ {UATE_LINECONN, "UATE_LINECONN"},
+ {UATE_LEGACYCONN, "UATE_LEGACYCONN"},
+ {UATE_DIGITALAUIFC, "UATE_DIGITALAUIFC"},
+ {UATE_SPDIF, "UATE_SPDIF"},
+ {UATE_1394DA, "UATE_1394DA"},
+ {UATE_1394DV, "UATE_1394DV"},
+
+ /* embedded function terminal types */
+ {UATF_UNDEFINED, "UATF_UNDEFINED"},
+ {UATF_CALIBNOISE, "UATF_CALIBNOISE"},
+ {UATF_EQUNOISE, "UATF_EQUNOISE"},
+ {UATF_CDPLAYER, "UATF_CDPLAYER"},
+ {UATF_DAT, "UATF_DAT"},
+ {UATF_DCC, "UATF_DCC"},
+ {UATF_MINIDISK, "UATF_MINIDISK"},
+ {UATF_ANALOGTAPE, "UATF_ANALOGTAPE"},
+ {UATF_PHONOGRAPH, "UATF_PHONOGRAPH"},
+ {UATF_VCRAUDIO, "UATF_VCRAUDIO"},
+ {UATF_VIDEODISCAUDIO, "UATF_VIDEODISCAUDIO"},
+ {UATF_DVDAUDIO, "UATF_DVDAUDIO"},
+ {UATF_TVTUNERAUDIO, "UATF_TVTUNERAUDIO"},
+ {UATF_SATELLITE, "UATF_SATELLITE"},
+ {UATF_CABLETUNER, "UATF_CABLETUNER"},
+ {UATF_DSS, "UATF_DSS"},
+ {UATF_RADIORECV, "UATF_RADIORECV"},
+ {UATF_RADIOXMIT, "UATF_RADIOXMIT"},
+ {UATF_MULTITRACK, "UATF_MULTITRACK"},
+ {UATF_SYNTHESIZER, "UATF_SYNTHESIZER"},
+
+ /* unknown */
+ {0x0000, "UNKNOWN"},
+};
+
+static const char *
+uaudio_mixer_get_terminal_name(uint16_t terminal_type)
+{
+ const struct uaudio_tt_to_string *uat = uaudio_tt_to_string;
+
+ while (uat->terminal_type) {
+ if (uat->terminal_type == terminal_type) {
+ break;
+ }
+ uat++;
+ }
+ if (uat->terminal_type == 0) {
+ DPRINTF("unknown terminal type (0x%04x)", terminal_type);
+ }
+ return (uat->desc);
+}
+
+#endif
+
+static uint16_t
+uaudio_mixer_determine_class(const struct uaudio_terminal_node *iot,
+ struct uaudio_mixer_node *mix)
+{
+ uint16_t terminal_type = 0x0000;
+ const struct uaudio_terminal_node *input[2];
+ const struct uaudio_terminal_node *output[2];
+
+ input[0] = uaudio_mixer_get_input(iot, 0);
+ input[1] = uaudio_mixer_get_input(iot, 1);
+
+ output[0] = uaudio_mixer_get_output(iot, 0);
+ output[1] = uaudio_mixer_get_output(iot, 1);
+
+ /*
+ * check if there is only
+ * one output terminal:
+ */
+ if (output[0] && (!output[1])) {
+ terminal_type = UGETW(output[0]->u.ot->wTerminalType);
+ }
+ /*
+ * If the only output terminal is USB,
+ * the class is UAC_RECORD.
+ */
+ if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) {
+
+ mix->class = UAC_RECORD;
+ if (input[0] && (!input[1])) {
+ terminal_type = UGETW(input[0]->u.it->wTerminalType);
+ } else {
+ terminal_type = 0;
+ }
+ goto done;
+ }
+ /*
+ * if the unit is connected to just
+ * one input terminal, the
+ * class is UAC_INPUT:
+ */
+ if (input[0] && (!input[1])) {
+ mix->class = UAC_INPUT;
+ terminal_type = UGETW(input[0]->u.it->wTerminalType);
+ goto done;
+ }
+ /*
+ * Otherwise, the class is UAC_OUTPUT.
+ */
+ mix->class = UAC_OUTPUT;
+done:
+ return (terminal_type);
+}
+
+struct uaudio_tt_to_feature {
+ uint16_t terminal_type;
+ uint16_t feature;
+};
+
+static const struct uaudio_tt_to_feature uaudio_tt_to_feature[] = {
+
+ {UAT_STREAM, SOUND_MIXER_PCM},
+
+ {UATI_MICROPHONE, SOUND_MIXER_MIC},
+ {UATI_DESKMICROPHONE, SOUND_MIXER_MIC},
+ {UATI_PERSONALMICROPHONE, SOUND_MIXER_MIC},
+ {UATI_OMNIMICROPHONE, SOUND_MIXER_MIC},
+ {UATI_MICROPHONEARRAY, SOUND_MIXER_MIC},
+ {UATI_PROCMICROPHONEARR, SOUND_MIXER_MIC},
+
+ {UATO_SPEAKER, SOUND_MIXER_SPEAKER},
+ {UATO_DESKTOPSPEAKER, SOUND_MIXER_SPEAKER},
+ {UATO_ROOMSPEAKER, SOUND_MIXER_SPEAKER},
+ {UATO_COMMSPEAKER, SOUND_MIXER_SPEAKER},
+
+ {UATE_ANALOGCONN, SOUND_MIXER_LINE},
+ {UATE_LINECONN, SOUND_MIXER_LINE},
+ {UATE_LEGACYCONN, SOUND_MIXER_LINE},
+
+ {UATE_DIGITALAUIFC, SOUND_MIXER_ALTPCM},
+ {UATE_SPDIF, SOUND_MIXER_ALTPCM},
+ {UATE_1394DA, SOUND_MIXER_ALTPCM},
+ {UATE_1394DV, SOUND_MIXER_ALTPCM},
+
+ {UATF_CDPLAYER, SOUND_MIXER_CD},
+
+ {UATF_SYNTHESIZER, SOUND_MIXER_SYNTH},
+
+ {UATF_VIDEODISCAUDIO, SOUND_MIXER_VIDEO},
+ {UATF_DVDAUDIO, SOUND_MIXER_VIDEO},
+ {UATF_TVTUNERAUDIO, SOUND_MIXER_VIDEO},
+
+ /* telephony terminal types */
+ {UATT_UNDEFINED, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */
+ {UATT_PHONELINE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */
+ {UATT_TELEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */
+ {UATT_DOWNLINEPHONE, SOUND_MIXER_PHONEIN}, /* SOUND_MIXER_PHONEOUT */
+
+ {UATF_RADIORECV, SOUND_MIXER_RADIO},
+ {UATF_RADIOXMIT, SOUND_MIXER_RADIO},
+
+ {UAT_UNDEFINED, SOUND_MIXER_VOLUME},
+ {UAT_VENDOR, SOUND_MIXER_VOLUME},
+ {UATI_UNDEFINED, SOUND_MIXER_VOLUME},
+
+ /* output terminal types */
+ {UATO_UNDEFINED, SOUND_MIXER_VOLUME},
+ {UATO_DISPLAYAUDIO, SOUND_MIXER_VOLUME},
+ {UATO_SUBWOOFER, SOUND_MIXER_VOLUME},
+ {UATO_HEADPHONES, SOUND_MIXER_VOLUME},
+
+ /* bidir terminal types */
+ {UATB_UNDEFINED, SOUND_MIXER_VOLUME},
+ {UATB_HANDSET, SOUND_MIXER_VOLUME},
+ {UATB_HEADSET, SOUND_MIXER_VOLUME},
+ {UATB_SPEAKERPHONE, SOUND_MIXER_VOLUME},
+ {UATB_SPEAKERPHONEESUP, SOUND_MIXER_VOLUME},
+ {UATB_SPEAKERPHONEECANC, SOUND_MIXER_VOLUME},
+
+ /* external terminal types */
+ {UATE_UNDEFINED, SOUND_MIXER_VOLUME},
+
+ /* embedded function terminal types */
+ {UATF_UNDEFINED, SOUND_MIXER_VOLUME},
+ {UATF_CALIBNOISE, SOUND_MIXER_VOLUME},
+ {UATF_EQUNOISE, SOUND_MIXER_VOLUME},
+ {UATF_DAT, SOUND_MIXER_VOLUME},
+ {UATF_DCC, SOUND_MIXER_VOLUME},
+ {UATF_MINIDISK, SOUND_MIXER_VOLUME},
+ {UATF_ANALOGTAPE, SOUND_MIXER_VOLUME},
+ {UATF_PHONOGRAPH, SOUND_MIXER_VOLUME},
+ {UATF_VCRAUDIO, SOUND_MIXER_VOLUME},
+ {UATF_SATELLITE, SOUND_MIXER_VOLUME},
+ {UATF_CABLETUNER, SOUND_MIXER_VOLUME},
+ {UATF_DSS, SOUND_MIXER_VOLUME},
+ {UATF_MULTITRACK, SOUND_MIXER_VOLUME},
+ {0xffff, SOUND_MIXER_VOLUME},
+
+ /* default */
+ {0x0000, SOUND_MIXER_VOLUME},
+};
+
+static uint16_t
+uaudio_mixer_feature_name(const struct uaudio_terminal_node *iot,
+ struct uaudio_mixer_node *mix)
+{
+ const struct uaudio_tt_to_feature *uat = uaudio_tt_to_feature;
+ uint16_t terminal_type = uaudio_mixer_determine_class(iot, mix);
+
+ if ((mix->class == UAC_RECORD) && (terminal_type == 0)) {
+ return (SOUND_MIXER_IMIX);
+ }
+ while (uat->terminal_type) {
+ if (uat->terminal_type == terminal_type) {
+ break;
+ }
+ uat++;
+ }
+
+ DPRINTF("terminal_type=%s (0x%04x) -> %d\n",
+ uaudio_mixer_get_terminal_name(terminal_type),
+ terminal_type, uat->feature);
+
+ return (uat->feature);
+}
+
+const static struct uaudio_terminal_node *
+uaudio_mixer_get_input(const struct uaudio_terminal_node *iot, uint8_t index)
+{
+ struct uaudio_terminal_node *root = iot->root;
+ uint8_t n;
+
+ n = iot->usr.id_max;
+ do {
+ if (iot->usr.bit_input[n / 8] & (1 << (n % 8))) {
+ if (!index--) {
+ return (root + n);
+ }
+ }
+ } while (n--);
+
+ return (NULL);
+}
+
+const static struct uaudio_terminal_node *
+uaudio_mixer_get_output(const struct uaudio_terminal_node *iot, uint8_t index)
+{
+ struct uaudio_terminal_node *root = iot->root;
+ uint8_t n;
+
+ n = iot->usr.id_max;
+ do {
+ if (iot->usr.bit_output[n / 8] & (1 << (n % 8))) {
+ if (!index--) {
+ return (root + n);
+ }
+ }
+ } while (n--);
+
+ return (NULL);
+}
+
+static void
+uaudio_mixer_find_inputs_sub(struct uaudio_terminal_node *root,
+ const uint8_t *p_id, uint8_t n_id,
+ struct uaudio_search_result *info)
+{
+ struct uaudio_terminal_node *iot;
+ uint8_t n;
+ uint8_t i;
+
+ if (info->recurse_level >= UAUDIO_RECURSE_LIMIT) {
+ return;
+ }
+ info->recurse_level++;
+
+ for (n = 0; n < n_id; n++) {
+
+ i = p_id[n];
+
+ if (info->bit_visited[i / 8] & (1 << (i % 8))) {
+ /* don't go into a circle */
+ DPRINTF("avoided going into a circle at id=%d!\n", i);
+ continue;
+ } else {
+ info->bit_visited[i / 8] |= (1 << (i % 8));
+ }
+
+ iot = (root + i);
+
+ if (iot->u.desc == NULL) {
+ continue;
+ }
+ switch (iot->u.desc->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ info->bit_input[i / 8] |= (1 << (i % 8));
+ break;
+
+ case UDESCSUB_AC_FEATURE:
+ uaudio_mixer_find_inputs_sub
+ (root, &iot->u.fu->bSourceId, 1, info);
+ break;
+
+ case UDESCSUB_AC_OUTPUT:
+ uaudio_mixer_find_inputs_sub
+ (root, &iot->u.ot->bSourceId, 1, info);
+ break;
+
+ case UDESCSUB_AC_MIXER:
+ uaudio_mixer_find_inputs_sub
+ (root, iot->u.mu->baSourceId,
+ iot->u.mu->bNrInPins, info);
+ break;
+
+ case UDESCSUB_AC_SELECTOR:
+ uaudio_mixer_find_inputs_sub
+ (root, iot->u.su->baSourceId,
+ iot->u.su->bNrInPins, info);
+ break;
+
+ case UDESCSUB_AC_PROCESSING:
+ uaudio_mixer_find_inputs_sub
+ (root, iot->u.pu->baSourceId,
+ iot->u.pu->bNrInPins, info);
+ break;
+
+ case UDESCSUB_AC_EXTENSION:
+ uaudio_mixer_find_inputs_sub
+ (root, iot->u.eu->baSourceId,
+ iot->u.eu->bNrInPins, info);
+ break;
+
+ case UDESCSUB_AC_HEADER:
+ default:
+ break;
+ }
+ }
+ info->recurse_level--;
+}
+
+static void
+uaudio_mixer_find_outputs_sub(struct uaudio_terminal_node *root, uint8_t id,
+ uint8_t n_id, struct uaudio_search_result *info)
+{
+ struct uaudio_terminal_node *iot = (root + id);
+ uint8_t j;
+
+ j = n_id;
+ do {
+ if ((j != id) && ((root + j)->u.desc) &&
+ ((root + j)->u.desc->bDescriptorSubtype == UDESCSUB_AC_OUTPUT)) {
+
+ /*
+ * "j" (output) <--- virtual wire <--- "id" (input)
+ *
+ * if "j" has "id" on the input, then "id" have "j" on
+ * the output, because they are connected:
+ */
+ if ((root + j)->usr.bit_input[id / 8] & (1 << (id % 8))) {
+ iot->usr.bit_output[j / 8] |= (1 << (j % 8));
+ }
+ }
+ } while (j--);
+}
+
+static void
+uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb2_device *udev,
+ void *desc)
+{
+ const struct usb2_audio_control_descriptor *acdp;
+ struct usb2_config_descriptor *cd = usb2_get_config_descriptor(udev);
+ const struct usb2_descriptor *dp;
+ const struct usb2_audio_unit *au;
+ struct uaudio_terminal_node *iot = NULL;
+ uint16_t wTotalLen;
+ uint8_t ID_max = 0; /* inclusive */
+ uint8_t i;
+
+ desc = usb2_desc_foreach(cd, desc);
+
+ if (desc == NULL) {
+ DPRINTF("no Audio Control header\n");
+ goto done;
+ }
+ acdp = desc;
+
+ if ((acdp->bLength < sizeof(*acdp)) ||
+ (acdp->bDescriptorType != UDESC_CS_INTERFACE) ||
+ (acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER)) {
+ DPRINTF("invalid Audio Control header\n");
+ goto done;
+ }
+ /* "wTotalLen" is allowed to be corrupt */
+ wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength;
+
+ /* get USB audio revision */
+ sc->sc_audio_rev = UGETW(acdp->bcdADC);
+
+ DPRINTFN(3, "found AC header, vers=%03x, len=%d\n",
+ sc->sc_audio_rev, wTotalLen);
+
+ if (sc->sc_audio_rev != UAUDIO_VERSION) {
+
+ if (sc->sc_uq_bad_adc) {
+
+ } else {
+ DPRINTF("invalid audio version\n");
+ goto done;
+ }
+ }
+ iot = malloc(sizeof(struct uaudio_terminal_node) * 256, M_TEMP,
+ M_WAITOK | M_ZERO);
+
+ if (iot == NULL) {
+ DPRINTF("no memory!\n");
+ goto done;
+ }
+ while ((desc = usb2_desc_foreach(cd, desc))) {
+
+ dp = desc;
+
+ if (dp->bLength > wTotalLen) {
+ break;
+ } else {
+ wTotalLen -= dp->bLength;
+ }
+
+ au = uaudio_mixer_verify_desc(dp, 0);
+
+ if (au) {
+ iot[au->bUnitId].u.desc = (const void *)au;
+ if (au->bUnitId > ID_max) {
+ ID_max = au->bUnitId;
+ }
+ }
+ }
+
+ DPRINTF("Maximum ID=%d\n", ID_max);
+
+ /*
+ * determine sourcing inputs for
+ * all nodes in the tree:
+ */
+ i = ID_max;
+ do {
+ uaudio_mixer_find_inputs_sub(iot, &i, 1, &((iot + i)->usr));
+ } while (i--);
+
+ /*
+ * determine outputs for
+ * all nodes in the tree:
+ */
+ i = ID_max;
+ do {
+ uaudio_mixer_find_outputs_sub(iot, i, ID_max, &((iot + i)->usr));
+ } while (i--);
+
+ /* set "id_max" and "root" */
+
+ i = ID_max;
+ do {
+ (iot + i)->usr.id_max = ID_max;
+ (iot + i)->root = iot;
+ } while (i--);
+
+#if USB_DEBUG
+ i = ID_max;
+ do {
+ uint8_t j;
+
+ if (iot[i].u.desc == NULL) {
+ continue;
+ }
+ DPRINTF("id %d:\n", i);
+
+ switch (iot[i].u.desc->bDescriptorSubtype) {
+ case UDESCSUB_AC_INPUT:
+ DPRINTF(" - AC_INPUT type=%s\n",
+ uaudio_mixer_get_terminal_name
+ (UGETW(iot[i].u.it->wTerminalType)));
+ uaudio_mixer_dump_cluster(i, iot);
+ break;
+
+ case UDESCSUB_AC_OUTPUT:
+ DPRINTF(" - AC_OUTPUT type=%s "
+ "src=%d\n", uaudio_mixer_get_terminal_name
+ (UGETW(iot[i].u.ot->wTerminalType)),
+ iot[i].u.ot->bSourceId);
+ break;
+
+ case UDESCSUB_AC_MIXER:
+ DPRINTF(" - AC_MIXER src:\n");
+ for (j = 0; j < iot[i].u.mu->bNrInPins; j++) {
+ DPRINTF(" - %d\n", iot[i].u.mu->baSourceId[j]);
+ }
+ uaudio_mixer_dump_cluster(i, iot);
+ break;
+
+ case UDESCSUB_AC_SELECTOR:
+ DPRINTF(" - AC_SELECTOR src:\n");
+ for (j = 0; j < iot[i].u.su->bNrInPins; j++) {
+ DPRINTF(" - %d\n", iot[i].u.su->baSourceId[j]);
+ }
+ break;
+
+ case UDESCSUB_AC_FEATURE:
+ DPRINTF(" - AC_FEATURE src=%d\n", iot[i].u.fu->bSourceId);
+ break;
+
+ case UDESCSUB_AC_PROCESSING:
+ DPRINTF(" - AC_PROCESSING src:\n");
+ for (j = 0; j < iot[i].u.pu->bNrInPins; j++) {
+ DPRINTF(" - %d\n", iot[i].u.pu->baSourceId[j]);
+ }
+ uaudio_mixer_dump_cluster(i, iot);
+ break;
+
+ case UDESCSUB_AC_EXTENSION:
+ DPRINTF(" - AC_EXTENSION src:\n");
+ for (j = 0; j < iot[i].u.eu->bNrInPins; j++) {
+ DPRINTF("%d ", iot[i].u.eu->baSourceId[j]);
+ }
+ uaudio_mixer_dump_cluster(i, iot);
+ break;
+
+ default:
+ DPRINTF("unknown audio control (subtype=%d)\n",
+ iot[i].u.desc->bDescriptorSubtype);
+ }
+
+ DPRINTF("Inputs to this ID are:\n");
+
+ j = ID_max;
+ do {
+ if (iot[i].usr.bit_input[j / 8] & (1 << (j % 8))) {
+ DPRINTF(" -- ID=%d\n", j);
+ }
+ } while (j--);
+
+ DPRINTF("Outputs from this ID are:\n");
+
+ j = ID_max;
+ do {
+ if (iot[i].usr.bit_output[j / 8] & (1 << (j % 8))) {
+ DPRINTF(" -- ID=%d\n", j);
+ }
+ } while (j--);
+
+ } while (i--);
+#endif
+
+ /*
+ * scan the config to create a linked
+ * list of "mixer" nodes:
+ */
+
+ i = ID_max;
+ do {
+ dp = iot[i].u.desc;
+
+ if (dp == NULL) {
+ continue;
+ }
+ DPRINTFN(11, "id=%d subtype=%d\n",
+ i, dp->bDescriptorSubtype);
+
+ switch (dp->bDescriptorSubtype) {
+ case UDESCSUB_AC_HEADER:
+ DPRINTF("unexpected AC header\n");
+ break;
+
+ case UDESCSUB_AC_INPUT:
+ uaudio_mixer_add_input(sc, iot, i);
+ break;
+
+ case UDESCSUB_AC_OUTPUT:
+ uaudio_mixer_add_output(sc, iot, i);
+ break;
+
+ case UDESCSUB_AC_MIXER:
+ uaudio_mixer_add_mixer(sc, iot, i);
+ break;
+
+ case UDESCSUB_AC_SELECTOR:
+ uaudio_mixer_add_selector(sc, iot, i);
+ break;
+
+ case UDESCSUB_AC_FEATURE:
+ uaudio_mixer_add_feature(sc, iot, i);
+ break;
+
+ case UDESCSUB_AC_PROCESSING:
+ uaudio_mixer_add_processing(sc, iot, i);
+ break;
+
+ case UDESCSUB_AC_EXTENSION:
+ uaudio_mixer_add_extension(sc, iot, i);
+ break;
+
+ default:
+ DPRINTF("bad AC desc subtype=0x%02x\n",
+ dp->bDescriptorSubtype);
+ break;
+ }
+
+ } while (i--);
+
+done:
+ if (iot) {
+ free(iot, M_TEMP);
+ }
+}
+
+static uint16_t
+uaudio_mixer_get(struct usb2_device *udev, uint8_t what,
+ struct uaudio_mixer_node *mc)
+{
+ struct usb2_device_request req;
+ uint16_t val;
+ uint16_t len = MIX_SIZE(mc->type);
+ uint8_t data[4];
+ usb2_error_t err;
+
+ if (mc->wValue[0] == -1) {
+ return (0);
+ }
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = what;
+ USETW(req.wValue, mc->wValue[0]);
+ USETW(req.wIndex, mc->wIndex);
+ USETW(req.wLength, len);
+
+ err = usb2_do_request(udev, &Giant, &req, data);
+ if (err) {
+ DPRINTF("err=%s\n", usb2_errstr(err));
+ return (0);
+ }
+ if (len < 1) {
+ data[0] = 0;
+ }
+ if (len < 2) {
+ data[1] = 0;
+ }
+ val = (data[0] | (data[1] << 8));
+
+ DPRINTFN(3, "val=%d\n", val);
+
+ return (val);
+}
+
+static void
+uaudio_mixer_write_cfg_callback(struct usb2_xfer *xfer)
+{
+ struct usb2_device_request req;
+ struct uaudio_softc *sc = xfer->priv_sc;
+ struct uaudio_mixer_node *mc = sc->sc_mixer_curr;
+ uint16_t len;
+ uint8_t repeat = 1;
+ uint8_t update;
+ uint8_t chan;
+ uint8_t buf[2];
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ case USB_ST_SETUP:
+tr_setup:
+
+ if (mc == NULL) {
+ mc = sc->sc_mixer_root;
+ sc->sc_mixer_curr = mc;
+ sc->sc_mixer_chan = 0;
+ repeat = 0;
+ }
+ while (mc) {
+ while (sc->sc_mixer_chan < mc->nchan) {
+
+ len = MIX_SIZE(mc->type);
+
+ chan = sc->sc_mixer_chan;
+
+ sc->sc_mixer_chan++;
+
+ update = ((mc->update[chan / 8] & (1 << (chan % 8))) &&
+ (mc->wValue[chan] != -1));
+
+ mc->update[chan / 8] &= ~(1 << (chan % 8));
+
+ if (update) {
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = SET_CUR;
+ USETW(req.wValue, mc->wValue[chan]);
+ USETW(req.wIndex, mc->wIndex);
+ USETW(req.wLength, len);
+
+ if (len > 0) {
+ buf[0] = (mc->wData[chan] & 0xFF);
+ }
+ if (len > 1) {
+ buf[1] = (mc->wData[chan] >> 8) & 0xFF;
+ }
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_copy_in(xfer->frbuffers + 1, 0, buf, len);
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = len;
+ xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+ usb2_start_hardware(xfer);
+ return;
+ }
+ }
+
+ mc = mc->next;
+ sc->sc_mixer_curr = mc;
+ sc->sc_mixer_chan = 0;
+ }
+
+ if (repeat) {
+ goto tr_setup;
+ }
+ break;
+
+ default: /* Error */
+ DPRINTF("error=%s\n", usb2_errstr(xfer->error));
+ if (xfer->error == USB_ERR_CANCELLED) {
+ /* do nothing - we are detaching */
+ break;
+ }
+ goto tr_transferred;
+ }
+}
+
+static usb2_error_t
+uaudio_set_speed(struct usb2_device *udev, uint8_t endpt, uint32_t speed)
+{
+ struct usb2_device_request req;
+ uint8_t data[3];
+
+ DPRINTFN(6, "endpt=%d speed=%u\n", endpt, speed);
+
+ req.bmRequestType = UT_WRITE_CLASS_ENDPOINT;
+ req.bRequest = SET_CUR;
+ USETW2(req.wValue, SAMPLING_FREQ_CONTROL, 0);
+ USETW(req.wIndex, endpt);
+ USETW(req.wLength, 3);
+ data[0] = speed;
+ data[1] = speed >> 8;
+ data[2] = speed >> 16;
+
+ return (usb2_do_request(udev, &Giant, &req, data));
+}
+
+static int
+uaudio_mixer_signext(uint8_t type, int val)
+{
+ if (!MIX_UNSIGNED(type)) {
+ if (MIX_SIZE(type) == 2) {
+ val = (int16_t)val;
+ } else {
+ val = (int8_t)val;
+ }
+ }
+ return (val);
+}
+
+static int
+uaudio_mixer_bsd2value(struct uaudio_mixer_node *mc, int32_t val)
+{
+ if (mc->type == MIX_ON_OFF) {
+ val = (val != 0);
+ } else if (mc->type == MIX_SELECTOR) {
+ if ((val < mc->minval) ||
+ (val > mc->maxval)) {
+ val = mc->minval;
+ }
+ } else {
+ val = (((val + (mc->delta / 2)) * mc->mul) / 255) + mc->minval;
+ }
+
+ DPRINTFN(6, "type=0x%03x val=%d min=%d max=%d val=%d\n",
+ mc->type, val, mc->minval, mc->maxval, val);
+ return (val);
+}
+
+static void
+uaudio_mixer_ctl_set(struct uaudio_softc *sc, struct uaudio_mixer_node *mc,
+ uint8_t chan, int32_t val)
+{
+ val = uaudio_mixer_bsd2value(mc, val);
+
+ mc->update[chan / 8] |= (1 << (chan % 8));
+ mc->wData[chan] = val;
+
+ /* start the transfer, if not already started */
+
+ usb2_transfer_start(sc->sc_mixer_xfer[0]);
+}
+
+static void
+uaudio_mixer_init(struct uaudio_softc *sc)
+{
+ struct uaudio_mixer_node *mc;
+ int32_t i;
+
+ for (mc = sc->sc_mixer_root; mc;
+ mc = mc->next) {
+
+ if (mc->ctl != SOUND_MIXER_NRDEVICES) {
+ /*
+ * Set device mask bits. See
+ * /usr/include/machine/soundcard.h
+ */
+ sc->sc_mix_info |= (1 << mc->ctl);
+ }
+ if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
+ (mc->type == MIX_SELECTOR)) {
+
+ for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
+ if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) {
+ continue;
+ }
+ sc->sc_recsrc_info |= 1 << mc->slctrtype[i - 1];
+ }
+ }
+ }
+}
+
+int
+uaudio_mixer_init_sub(struct uaudio_softc *sc, struct snd_mixer *m)
+{
+ DPRINTF("\n");
+
+ if (usb2_transfer_setup(sc->sc_udev, &sc->sc_mixer_iface_index,
+ sc->sc_mixer_xfer, uaudio_mixer_config, 1, sc,
+ mixer_get_lock(m))) {
+ DPRINTFN(0, "could not allocate USB "
+ "transfer for audio mixer!\n");
+ return (ENOMEM);
+ }
+ if (!(sc->sc_mix_info & SOUND_MASK_VOLUME)) {
+ mix_setparentchild(m, SOUND_MIXER_VOLUME, SOUND_MASK_PCM);
+ mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE);
+ }
+ mix_setdevs(m, sc->sc_mix_info);
+ mix_setrecdevs(m, sc->sc_recsrc_info);
+ return (0);
+}
+
+int
+uaudio_mixer_uninit_sub(struct uaudio_softc *sc)
+{
+ DPRINTF("\n");
+
+ usb2_transfer_unsetup(sc->sc_mixer_xfer, 1);
+
+ return (0);
+}
+
+void
+uaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
+ unsigned left, unsigned right)
+{
+ struct uaudio_mixer_node *mc;
+
+ for (mc = sc->sc_mixer_root; mc;
+ mc = mc->next) {
+
+ if (mc->ctl == type) {
+ if (mc->nchan == 2) {
+ /* set Right */
+ uaudio_mixer_ctl_set(sc, mc, 1, (int)(right * 255) / 100);
+ }
+ /* set Left or Mono */
+ uaudio_mixer_ctl_set(sc, mc, 0, (int)(left * 255) / 100);
+ }
+ }
+}
+
+uint32_t
+uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src)
+{
+ struct uaudio_mixer_node *mc;
+ uint32_t mask;
+ uint32_t temp;
+ int32_t i;
+
+ for (mc = sc->sc_mixer_root; mc;
+ mc = mc->next) {
+
+ if ((mc->ctl == SOUND_MIXER_NRDEVICES) &&
+ (mc->type == MIX_SELECTOR)) {
+
+ /* compute selector mask */
+
+ mask = 0;
+ for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
+ mask |= (1 << mc->slctrtype[i - 1]);
+ }
+
+ temp = mask & src;
+ if (temp == 0) {
+ continue;
+ }
+ /* find the first set bit */
+ temp = (-temp) & temp;
+
+ /* update "src" */
+ src &= ~mask;
+ src |= temp;
+
+ for (i = mc->minval; (i > 0) && (i <= mc->maxval); i++) {
+ if (temp != (1 << mc->slctrtype[i - 1])) {
+ continue;
+ }
+ uaudio_mixer_ctl_set(sc, mc, 0, i);
+ break;
+ }
+ }
+ }
+ return (src);
+}
+
+/*========================================================================*
+ * MIDI support routines
+ *========================================================================*/
+
+static void
+umidi_read_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct umidi_chan *chan = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = chan->xfer[1];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ chan->flags &= ~UMIDI_FLAG_READ_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+static void
+umidi_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct umidi_chan *chan = xfer->priv_sc;
+ struct umidi_sub_chan *sub;
+ uint8_t buf[1];
+ uint8_t cmd_len;
+ uint8_t cn;
+ uint16_t pos;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTF("actlen=%d bytes\n", xfer->actlen);
+
+ if (xfer->actlen == 0) {
+ /* should not happen */
+ goto tr_error;
+ }
+ pos = 0;
+
+ while (xfer->actlen >= 4) {
+
+ usb2_copy_out(xfer->frbuffers, pos, buf, 1);
+
+ cmd_len = umidi_cmd_to_len[buf[0] & 0xF]; /* command length */
+ cn = buf[0] >> 4; /* cable number */
+ sub = &chan->sub[cn];
+
+ if (cmd_len && (cn < chan->max_cable) && sub->read_open) {
+ usb2_fifo_put_data(sub->fifo.fp[USB_FIFO_RX], xfer->frbuffers,
+ pos + 1, cmd_len, 1);
+ } else {
+ /* ignore the command */
+ }
+
+ xfer->actlen -= 4;
+ pos += 4;
+ }
+
+ case USB_ST_SETUP:
+ DPRINTF("start\n");
+
+ if (chan->flags & UMIDI_FLAG_READ_STALL) {
+ usb2_transfer_start(chan->xfer[3]);
+ return;
+ }
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default:
+tr_error:
+
+ DPRINTF("error=%s\n", usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ chan->flags |= UMIDI_FLAG_READ_STALL;
+ usb2_transfer_start(chan->xfer[3]);
+ }
+ return;
+
+ }
+}
+
+static void
+umidi_write_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct umidi_chan *chan = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = chan->xfer[0];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ chan->flags &= ~UMIDI_FLAG_WRITE_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+/*
+ * The following statemachine, that converts MIDI commands to
+ * USB MIDI packets, derives from Linux's usbmidi.c, which
+ * was written by "Clemens Ladisch":
+ *
+ * Returns:
+ * 0: No command
+ * Else: Command is complete
+ */
+static uint8_t
+umidi_convert_to_usb(struct umidi_sub_chan *sub, uint8_t cn, uint8_t b)
+{
+ uint8_t p0 = (cn << 4);
+
+ if (b >= 0xf8) {
+ sub->temp_0[0] = p0 | 0x0f;
+ sub->temp_0[1] = b;
+ sub->temp_0[2] = 0;
+ sub->temp_0[3] = 0;
+ sub->temp_cmd = sub->temp_0;
+ return (1);
+
+ } else if (b >= 0xf0) {
+ switch (b) {
+ case 0xf0: /* system exclusive begin */
+ sub->temp_1[1] = b;
+ sub->state = UMIDI_ST_SYSEX_1;
+ break;
+ case 0xf1: /* MIDI time code */
+ case 0xf3: /* song select */
+ sub->temp_1[1] = b;
+ sub->state = UMIDI_ST_1PARAM;
+ break;
+ case 0xf2: /* song position pointer */
+ sub->temp_1[1] = b;
+ sub->state = UMIDI_ST_2PARAM_1;
+ break;
+ case 0xf4: /* unknown */
+ case 0xf5: /* unknown */
+ sub->state = UMIDI_ST_UNKNOWN;
+ break;
+ case 0xf6: /* tune request */
+ sub->temp_1[0] = p0 | 0x05;
+ sub->temp_1[1] = 0xf6;
+ sub->temp_1[2] = 0;
+ sub->temp_1[3] = 0;
+ sub->temp_cmd = sub->temp_1;
+ sub->state = UMIDI_ST_UNKNOWN;
+ return (1);
+
+ case 0xf7: /* system exclusive end */
+ switch (sub->state) {
+ case UMIDI_ST_SYSEX_0:
+ sub->temp_1[0] = p0 | 0x05;
+ sub->temp_1[1] = 0xf7;
+ sub->temp_1[2] = 0;
+ sub->temp_1[3] = 0;
+ sub->temp_cmd = sub->temp_1;
+ sub->state = UMIDI_ST_UNKNOWN;
+ return (1);
+ case UMIDI_ST_SYSEX_1:
+ sub->temp_1[0] = p0 | 0x06;
+ sub->temp_1[2] = 0xf7;
+ sub->temp_1[3] = 0;
+ sub->temp_cmd = sub->temp_1;
+ sub->state = UMIDI_ST_UNKNOWN;
+ return (1);
+ case UMIDI_ST_SYSEX_2:
+ sub->temp_1[0] = p0 | 0x07;
+ sub->temp_1[3] = 0xf7;
+ sub->temp_cmd = sub->temp_1;
+ sub->state = UMIDI_ST_UNKNOWN;
+ return (1);
+ }
+ sub->state = UMIDI_ST_UNKNOWN;
+ break;
+ }
+ } else if (b >= 0x80) {
+ sub->temp_1[1] = b;
+ if ((b >= 0xc0) && (b <= 0xdf)) {
+ sub->state = UMIDI_ST_1PARAM;
+ } else {
+ sub->state = UMIDI_ST_2PARAM_1;
+ }
+ } else { /* b < 0x80 */
+ switch (sub->state) {
+ case UMIDI_ST_1PARAM:
+ if (sub->temp_1[1] < 0xf0) {
+ p0 |= sub->temp_1[1] >> 4;
+ } else {
+ p0 |= 0x02;
+ sub->state = UMIDI_ST_UNKNOWN;
+ }
+ sub->temp_1[0] = p0;
+ sub->temp_1[2] = b;
+ sub->temp_1[3] = 0;
+ sub->temp_cmd = sub->temp_1;
+ return (1);
+ case UMIDI_ST_2PARAM_1:
+ sub->temp_1[2] = b;
+ sub->state = UMIDI_ST_2PARAM_2;
+ break;
+ case UMIDI_ST_2PARAM_2:
+ if (sub->temp_1[1] < 0xf0) {
+ p0 |= sub->temp_1[1] >> 4;
+ sub->state = UMIDI_ST_2PARAM_1;
+ } else {
+ p0 |= 0x03;
+ sub->state = UMIDI_ST_UNKNOWN;
+ }
+ sub->temp_1[0] = p0;
+ sub->temp_1[3] = b;
+ sub->temp_cmd = sub->temp_1;
+ return (1);
+ case UMIDI_ST_SYSEX_0:
+ sub->temp_1[1] = b;
+ sub->state = UMIDI_ST_SYSEX_1;
+ break;
+ case UMIDI_ST_SYSEX_1:
+ sub->temp_1[2] = b;
+ sub->state = UMIDI_ST_SYSEX_2;
+ break;
+ case UMIDI_ST_SYSEX_2:
+ sub->temp_1[0] = p0 | 0x04;
+ sub->temp_1[3] = b;
+ sub->temp_cmd = sub->temp_1;
+ sub->state = UMIDI_ST_SYSEX_0;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static void
+umidi_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct umidi_chan *chan = xfer->priv_sc;
+ struct umidi_sub_chan *sub;
+ uint32_t actlen;
+ uint16_t total_length;
+ uint8_t buf;
+ uint8_t start_cable;
+ uint8_t tr_any;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF("actlen=%d bytes\n", xfer->actlen);
+
+ case USB_ST_SETUP:
+
+ DPRINTF("start\n");
+
+ if (chan->flags & UMIDI_FLAG_WRITE_STALL) {
+ usb2_transfer_start(chan->xfer[2]);
+ return;
+ }
+ total_length = 0; /* reset */
+
+ start_cable = chan->curr_cable;
+
+ tr_any = 0;
+
+ while (1) {
+
+ /* round robin de-queueing */
+
+ sub = &chan->sub[chan->curr_cable];
+
+ if (sub->write_open) {
+ usb2_fifo_get_data(sub->fifo.fp[USB_FIFO_TX],
+ xfer->frbuffers, total_length,
+ 1, &actlen, 0);
+ } else {
+ actlen = 0;
+ }
+
+ if (actlen) {
+ usb2_copy_out(xfer->frbuffers, total_length, &buf, 1);
+
+ tr_any = 1;
+
+ DPRINTF("byte=0x%02x\n", buf);
+
+ if (umidi_convert_to_usb(sub, chan->curr_cable, buf)) {
+
+ DPRINTF("sub= %02x %02x %02x %02x\n",
+ sub->temp_cmd[0], sub->temp_cmd[1],
+ sub->temp_cmd[2], sub->temp_cmd[3]);
+
+ usb2_copy_in(xfer->frbuffers, total_length,
+ sub->temp_cmd, 4);
+
+ total_length += 4;
+
+ if (total_length >= UMIDI_BULK_SIZE) {
+ break;
+ }
+ } else {
+ continue;
+ }
+ }
+ chan->curr_cable++;
+ if (chan->curr_cable >= chan->max_cable) {
+ chan->curr_cable = 0;
+ }
+ if (chan->curr_cable == start_cable) {
+ if (tr_any == 0) {
+ break;
+ }
+ tr_any = 0;
+ }
+ }
+
+ if (total_length) {
+ xfer->frlengths[0] = total_length;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+
+ DPRINTF("error=%s\n", usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ chan->flags |= UMIDI_FLAG_WRITE_STALL;
+ usb2_transfer_start(chan->xfer[2]);
+ }
+ return;
+
+ }
+}
+
+static struct umidi_sub_chan *
+umidi_sub_by_fifo(struct usb2_fifo *fifo)
+{
+ struct umidi_chan *chan = fifo->priv_sc0;
+ struct umidi_sub_chan *sub;
+ uint32_t n;
+
+ for (n = 0; n < UMIDI_CABLES_MAX; n++) {
+ sub = &chan->sub[n];
+ if ((sub->fifo.fp[USB_FIFO_RX] == fifo) ||
+ (sub->fifo.fp[USB_FIFO_TX] == fifo)) {
+ return (sub);
+ }
+ }
+
+ panic("%s:%d cannot find usb2_fifo!\n",
+ __FILE__, __LINE__);
+
+ return (NULL);
+}
+
+static void
+umidi_start_read(struct usb2_fifo *fifo)
+{
+ struct umidi_chan *chan = fifo->priv_sc0;
+
+ usb2_transfer_start(chan->xfer[1]);
+}
+
+static void
+umidi_stop_read(struct usb2_fifo *fifo)
+{
+ struct umidi_chan *chan = fifo->priv_sc0;
+ struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
+
+ DPRINTF("\n");
+
+ sub->read_open = 0;
+
+ if (--(chan->read_open_refcount) == 0) {
+ /*
+ * XXX don't stop the read transfer here, hence that causes
+ * problems with some MIDI adapters
+ */
+ DPRINTF("(stopping read transfer)\n");
+ }
+}
+
+static void
+umidi_start_write(struct usb2_fifo *fifo)
+{
+ struct umidi_chan *chan = fifo->priv_sc0;
+
+ usb2_transfer_start(chan->xfer[0]);
+}
+
+static void
+umidi_stop_write(struct usb2_fifo *fifo)
+{
+ struct umidi_chan *chan = fifo->priv_sc0;
+ struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
+
+ DPRINTF("\n");
+
+ sub->write_open = 0;
+
+ if (--(chan->write_open_refcount) == 0) {
+ DPRINTF("(stopping write transfer)\n");
+ usb2_transfer_stop(chan->xfer[2]);
+ usb2_transfer_stop(chan->xfer[0]);
+ }
+}
+
+static int
+umidi_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct umidi_chan *chan = fifo->priv_sc0;
+ struct umidi_sub_chan *sub = umidi_sub_by_fifo(fifo);
+
+ if (fflags & FREAD) {
+ if (usb2_fifo_alloc_buffer(fifo, 4, (1024 / 4))) {
+ return (ENOMEM);
+ }
+ mtx_lock(fifo->priv_mtx);
+ chan->read_open_refcount++;
+ sub->read_open = 1;
+ mtx_unlock(fifo->priv_mtx);
+ }
+ if (fflags & FWRITE) {
+ if (usb2_fifo_alloc_buffer(fifo, 32, (1024 / 32))) {
+ return (ENOMEM);
+ }
+ /* clear stall first */
+ mtx_lock(fifo->priv_mtx);
+ chan->flags |= UMIDI_FLAG_WRITE_STALL;
+ chan->write_open_refcount++;
+ sub->write_open = 1;
+
+ /* reset */
+ sub->state = UMIDI_ST_UNKNOWN;
+ mtx_unlock(fifo->priv_mtx);
+ }
+ return (0); /* success */
+}
+
+static void
+umidi_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ if (fflags & FREAD) {
+ usb2_fifo_free_buffer(fifo);
+ }
+ if (fflags & FWRITE) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+
+static int
+umidi_ioctl(struct usb2_fifo *fifo, u_long cmd, void *data,
+ int fflags, struct thread *td)
+{
+ return (ENODEV);
+}
+
+static void
+umidi_init(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+ struct umidi_chan *chan = &sc->sc_midi_chan;
+
+ mtx_init(&chan->mtx, "umidi lock", NULL, MTX_DEF | MTX_RECURSE);
+}
+
+static struct usb2_fifo_methods umidi_fifo_methods = {
+ .f_start_read = &umidi_start_read,
+ .f_start_write = &umidi_start_write,
+ .f_stop_read = &umidi_stop_read,
+ .f_stop_write = &umidi_stop_write,
+ .f_open = &umidi_open,
+ .f_close = &umidi_close,
+ .f_ioctl = &umidi_ioctl,
+ .basename[0] = "umidi",
+};
+
+static int32_t
+umidi_probe(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umidi_chan *chan = &sc->sc_midi_chan;
+ struct umidi_sub_chan *sub;
+ int unit = device_get_unit(dev);
+ int error;
+ uint32_t n;
+
+ if (usb2_set_alt_interface_index(sc->sc_udev, chan->iface_index,
+ chan->iface_alt_index)) {
+ DPRINTF("setting of alternate index failed!\n");
+ goto detach;
+ }
+ usb2_set_parent_iface(sc->sc_udev, chan->iface_index, sc->sc_mixer_iface_index);
+
+ error = usb2_transfer_setup(uaa->device, &chan->iface_index,
+ chan->xfer, umidi_config, UMIDI_N_TRANSFER,
+ chan, &chan->mtx);
+ if (error) {
+ DPRINTF("error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ if ((chan->max_cable > UMIDI_CABLES_MAX) ||
+ (chan->max_cable == 0)) {
+ chan->max_cable = UMIDI_CABLES_MAX;
+ }
+ /* set interface permissions */
+ usb2_set_iface_perm(sc->sc_udev, chan->iface_index,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ for (n = 0; n < chan->max_cable; n++) {
+
+ sub = &chan->sub[n];
+
+ error = usb2_fifo_attach(sc->sc_udev, chan, &chan->mtx,
+ &umidi_fifo_methods, &sub->fifo, unit, n,
+ chan->iface_index);
+ if (error) {
+ goto detach;
+ }
+ }
+
+ mtx_lock(&chan->mtx);
+
+ /* clear stall first */
+ chan->flags |= UMIDI_FLAG_READ_STALL;
+
+ /*
+ * NOTE: at least one device will not work properly unless
+ * the BULK pipe is open all the time.
+ */
+ usb2_transfer_start(chan->xfer[1]);
+
+ mtx_unlock(&chan->mtx);
+
+ return (0); /* success */
+
+detach:
+ return (ENXIO); /* failure */
+}
+
+static int32_t
+umidi_detach(device_t dev)
+{
+ struct uaudio_softc *sc = device_get_softc(dev);
+ struct umidi_chan *chan = &sc->sc_midi_chan;
+ uint32_t n;
+
+ for (n = 0; n < UMIDI_CABLES_MAX; n++) {
+ usb2_fifo_detach(&chan->sub[n].fifo);
+ }
+
+ mtx_lock(&chan->mtx);
+
+ usb2_transfer_stop(chan->xfer[3]);
+ usb2_transfer_stop(chan->xfer[1]);
+
+ mtx_unlock(&chan->mtx);
+
+ usb2_transfer_unsetup(chan->xfer, UMIDI_N_TRANSFER);
+
+ mtx_destroy(&chan->mtx);
+
+ return (0);
+}
+
+DRIVER_MODULE(uaudio, ushub, uaudio_driver, uaudio_devclass, NULL, 0);
+MODULE_DEPEND(uaudio, usb, 1, 1, 1);
+MODULE_DEPEND(uaudio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(uaudio, 1);
diff --git a/sys/dev/usb/sound/uaudio.h b/sys/dev/usb/sound/uaudio.h
new file mode 100644
index 0000000..e763c6d
--- /dev/null
+++ b/sys/dev/usb/sound/uaudio.h
@@ -0,0 +1,63 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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.
+ */
+
+/* prototypes from "uaudio.c" used by "uaudio_pcm.c" */
+
+struct uaudio_chan;
+struct uaudio_softc;
+struct snd_dbuf;
+struct snd_mixer;
+struct pcm_channel;
+
+extern int uaudio_attach_sub(device_t dev, kobj_class_t mixer_class,
+ kobj_class_t chan_class);
+extern int uaudio_detach_sub(device_t dev);
+extern void *uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir);
+extern int uaudio_chan_free(struct uaudio_chan *ch);
+extern int uaudio_chan_set_param_blocksize(struct uaudio_chan *ch,
+ uint32_t blocksize);
+extern int uaudio_chan_set_param_fragments(struct uaudio_chan *ch,
+ uint32_t blocksize, uint32_t blockcount);
+extern int uaudio_chan_set_param_speed(struct uaudio_chan *ch,
+ uint32_t speed);
+extern int uaudio_chan_getptr(struct uaudio_chan *ch);
+extern struct pcmchan_caps *uaudio_chan_getcaps(struct uaudio_chan *ch);
+extern int uaudio_chan_set_param_format(struct uaudio_chan *ch,
+ uint32_t format);
+extern int uaudio_chan_start(struct uaudio_chan *ch);
+extern int uaudio_chan_stop(struct uaudio_chan *ch);
+extern int uaudio_mixer_init_sub(struct uaudio_softc *sc,
+ struct snd_mixer *m);
+extern int uaudio_mixer_uninit_sub(struct uaudio_softc *sc);
+extern void uaudio_mixer_set(struct uaudio_softc *sc, unsigned type,
+ unsigned left, unsigned right);
+extern uint32_t uaudio_mixer_setrecsrc(struct uaudio_softc *sc, uint32_t src);
+
+int uaudio_get_vendor(device_t dev);
+int uaudio_get_product(device_t dev);
+int uaudio_get_release(device_t dev);
diff --git a/sys/dev/usb/sound/uaudio_pcm.c b/sys/dev/usb/sound/uaudio_pcm.c
new file mode 100644
index 0000000..a9d3a48
--- /dev/null
+++ b/sys/dev/usb/sound/uaudio_pcm.c
@@ -0,0 +1,234 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2000-2002 Hiroyuki Aizu <aizu@navi.org>
+ * Copyright (c) 2006 Hans Petter Selasky
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/soundcard.h>
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/chip.h>
+
+#include <dev/usb/sound/uaudio.h>
+
+#include "mixer_if.h"
+
+/************************************************************/
+static void *
+ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
+{
+ return (uaudio_chan_init(devinfo, b, c, dir));
+}
+
+static int
+ua_chan_free(kobj_t obj, void *data)
+{
+ return (uaudio_chan_free(data));
+}
+
+static int
+ua_chan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ /*
+ * At this point, no need to query as we
+ * shouldn't select an unsorted format
+ */
+ return (uaudio_chan_set_param_format(data, format));
+}
+
+static int
+ua_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ return (uaudio_chan_set_param_speed(data, speed));
+}
+
+static int
+ua_chan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ return (uaudio_chan_set_param_blocksize(data, blocksize));
+}
+
+static int
+ua_chan_setfragments(kobj_t obj, void *data, uint32_t blocksize, uint32_t blockcount)
+{
+ return (uaudio_chan_set_param_fragments(data, blocksize, blockcount));
+}
+
+static int
+ua_chan_trigger(kobj_t obj, void *data, int go)
+{
+ if (!PCMTRIG_COMMON(go)) {
+ return (0);
+ }
+ if (go == PCMTRIG_START) {
+ return (uaudio_chan_start(data));
+ } else {
+ return (uaudio_chan_stop(data));
+ }
+}
+
+static int
+ua_chan_getptr(kobj_t obj, void *data)
+{
+ return (uaudio_chan_getptr(data));
+}
+
+static struct pcmchan_caps *
+ua_chan_getcaps(kobj_t obj, void *data)
+{
+ return (uaudio_chan_getcaps(data));
+}
+
+static kobj_method_t ua_chan_methods[] = {
+ KOBJMETHOD(channel_init, ua_chan_init),
+ KOBJMETHOD(channel_free, ua_chan_free),
+ KOBJMETHOD(channel_setformat, ua_chan_setformat),
+ KOBJMETHOD(channel_setspeed, ua_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize),
+ KOBJMETHOD(channel_setfragments, ua_chan_setfragments),
+ KOBJMETHOD(channel_trigger, ua_chan_trigger),
+ KOBJMETHOD(channel_getptr, ua_chan_getptr),
+ KOBJMETHOD(channel_getcaps, ua_chan_getcaps),
+ {0, 0}
+};
+
+CHANNEL_DECLARE(ua_chan);
+
+/************************************************************/
+static int
+ua_mixer_init(struct snd_mixer *m)
+{
+ return (uaudio_mixer_init_sub(mix_getdevinfo(m), m));
+}
+
+static int
+ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right)
+{
+ struct mtx *mtx = mixer_get_lock(m);
+ uint8_t do_unlock;
+
+ if (mtx_owned(mtx)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ mtx_lock(mtx);
+ }
+ uaudio_mixer_set(mix_getdevinfo(m), type, left, right);
+ if (do_unlock) {
+ mtx_unlock(mtx);
+ }
+ return (left | (right << 8));
+}
+
+static int
+ua_mixer_setrecsrc(struct snd_mixer *m, uint32_t src)
+{
+ struct mtx *mtx = mixer_get_lock(m);
+ int retval;
+ uint8_t do_unlock;
+
+ if (mtx_owned(mtx)) {
+ do_unlock = 0;
+ } else {
+ do_unlock = 1;
+ mtx_lock(mtx);
+ }
+ retval = uaudio_mixer_setrecsrc(mix_getdevinfo(m), src);
+ if (do_unlock) {
+ mtx_unlock(mtx);
+ }
+ return (retval);
+}
+
+static int
+ua_mixer_uninit(struct snd_mixer *m)
+{
+ return (uaudio_mixer_uninit_sub(mix_getdevinfo(m)));
+}
+
+static kobj_method_t ua_mixer_methods[] = {
+ KOBJMETHOD(mixer_init, ua_mixer_init),
+ KOBJMETHOD(mixer_uninit, ua_mixer_uninit),
+ KOBJMETHOD(mixer_set, ua_mixer_set),
+ KOBJMETHOD(mixer_setrecsrc, ua_mixer_setrecsrc),
+
+ {0, 0}
+};
+
+MIXER_DECLARE(ua_mixer);
+/************************************************************/
+
+
+static int
+ua_probe(device_t dev)
+{
+ struct sndcard_func *func;
+
+ /* the parent device has already been probed */
+
+ func = device_get_ivars(dev);
+
+ if ((func == NULL) ||
+ (func->func != SCF_PCM)) {
+ return (ENXIO);
+ }
+ device_set_desc(dev, "USB audio");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ua_attach(device_t dev)
+{
+ return (uaudio_attach_sub(dev, &ua_mixer_class, &ua_chan_class));
+}
+
+static int
+ua_detach(device_t dev)
+{
+ return (uaudio_detach_sub(dev));
+}
+
+/************************************************************/
+
+static device_method_t ua_pcm_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ua_probe),
+ DEVMETHOD(device_attach, ua_attach),
+ DEVMETHOD(device_detach, ua_detach),
+
+ {0, 0}
+};
+
+static driver_t ua_pcm_driver = {
+ "pcm",
+ ua_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1);
+MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(ua_pcm, 1);
diff --git a/sys/dev/usb/sound/uaudio_reg.h b/sys/dev/usb/sound/uaudio_reg.h
new file mode 100644
index 0000000..2bf68a1
--- /dev/null
+++ b/sys/dev/usb/sound/uaudio_reg.h
@@ -0,0 +1,406 @@
+/* $NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net) at
+ * Carlstedt Research & Technology.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define UAUDIO_VERSION 0x100
+
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+
+#define UDESCSUB_AC_HEADER 1
+#define UDESCSUB_AC_INPUT 2
+#define UDESCSUB_AC_OUTPUT 3
+#define UDESCSUB_AC_MIXER 4
+#define UDESCSUB_AC_SELECTOR 5
+#define UDESCSUB_AC_FEATURE 6
+#define UDESCSUB_AC_PROCESSING 7
+#define UDESCSUB_AC_EXTENSION 8
+
+/* The first fields are identical to struct usb2_endpoint_descriptor */
+typedef struct {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+ uByte bmAttributes;
+ uWord wMaxPacketSize;
+ uByte bInterval;
+ /*
+ * The following two entries are only used by the Audio Class.
+ * And according to the specs the Audio Class is the only one
+ * allowed to extend the endpoint descriptor.
+ * Who knows what goes on in the minds of the people in the USB
+ * standardization? :-(
+ */
+ uByte bRefresh;
+ uByte bSynchAddress;
+} __packed usb2_endpoint_descriptor_audio_t;
+
+struct usb2_audio_control_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uWord bcdADC;
+ uWord wTotalLength;
+ uByte bInCollection;
+ uByte baInterfaceNr[1];
+} __packed;
+
+struct usb2_audio_streaming_interface_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bTerminalLink;
+ uByte bDelay;
+ uWord wFormatTag;
+} __packed;
+
+struct usb2_audio_streaming_endpoint_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmAttributes;
+#define UA_SED_FREQ_CONTROL 0x01
+#define UA_SED_PITCH_CONTROL 0x02
+#define UA_SED_MAXPACKETSONLY 0x80
+ uByte bLockDelayUnits;
+ uWord wLockDelay;
+} __packed;
+
+struct usb2_audio_streaming_type1_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bFormatType;
+ uByte bNrChannels;
+ uByte bSubFrameSize;
+ uByte bBitResolution;
+ uByte bSamFreqType;
+#define UA_SAMP_CONTNUOUS 0
+ uByte tSamFreq[0];
+#define UA_GETSAMP(p, n) (((p)->tSamFreq[((n)*3)+0]) | \
+ ((p)->tSamFreq[((n)*3)+1] << 8) | \
+ ((p)->tSamFreq[((n)*3)+2] << 16))
+#define UA_SAMP_LO(p) UA_GETSAMP(p, 0)
+#define UA_SAMP_HI(p) UA_GETSAMP(p, 1)
+} __packed;
+
+struct usb2_audio_cluster {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+#define UA_CHANNEL_LEFT 0x0001
+#define UA_CHANNEL_RIGHT 0x0002
+#define UA_CHANNEL_CENTER 0x0004
+#define UA_CHANNEL_LFE 0x0008
+#define UA_CHANNEL_L_SURROUND 0x0010
+#define UA_CHANNEL_R_SURROUND 0x0020
+#define UA_CHANNEL_L_CENTER 0x0040
+#define UA_CHANNEL_R_CENTER 0x0080
+#define UA_CHANNEL_SURROUND 0x0100
+#define UA_CHANNEL_L_SIDE 0x0200
+#define UA_CHANNEL_R_SIDE 0x0400
+#define UA_CHANNEL_TOP 0x0800
+ uByte iChannelNames;
+} __packed;
+
+/* Shared by all units and terminals */
+struct usb2_audio_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+};
+
+/* UDESCSUB_AC_INPUT */
+struct usb2_audio_input_terminal {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bTerminalId;
+ uWord wTerminalType;
+ uByte bAssocTerminal;
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+/* uByte iTerminal; */
+} __packed;
+
+/* UDESCSUB_AC_OUTPUT */
+struct usb2_audio_output_terminal {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bTerminalId;
+ uWord wTerminalType;
+ uByte bAssocTerminal;
+ uByte bSourceId;
+ uByte iTerminal;
+} __packed;
+
+/* UDESCSUB_AC_MIXER */
+struct usb2_audio_mixer_unit_0 {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uByte bNrInPins;
+ uByte baSourceId[0]; /* [bNrInPins] */
+ /* struct usb2_audio_mixer_unit_1 */
+} __packed;
+struct usb2_audio_mixer_unit_1 {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte bmControls[0]; /* [see source code] */
+ /* uByte iMixer; */
+} __packed;
+
+/* UDESCSUB_AC_SELECTOR */
+struct usb2_audio_selector_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uByte bNrInPins;
+ uByte baSourceId[0]; /* [bNrInPins] */
+ /* uByte iSelector; */
+} __packed;
+
+/* UDESCSUB_AC_FEATURE */
+struct usb2_audio_feature_unit {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uByte bSourceId;
+ uByte bControlSize;
+ uByte bmaControls[0]; /* [bControlSize * x] */
+ /* uByte iFeature; */
+} __packed;
+
+/* UDESCSUB_AC_PROCESSING */
+struct usb2_audio_processing_unit_0 {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uWord wProcessType;
+ uByte bNrInPins;
+ uByte baSourceId[0]; /* [bNrInPins] */
+ /* struct usb2_audio_processing_unit_1 */
+} __packed;
+struct usb2_audio_processing_unit_1 {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte bControlSize;
+ uByte bmControls[0]; /* [bControlSize] */
+#define UA_PROC_ENABLE_MASK 1
+} __packed;
+
+struct usb2_audio_processing_unit_updown {
+ uByte iProcessing;
+ uByte bNrModes;
+ uWord waModes[0]; /* [bNrModes] */
+} __packed;
+
+/* UDESCSUB_AC_EXTENSION */
+struct usb2_audio_extension_unit_0 {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bUnitId;
+ uWord wExtensionCode;
+ uByte bNrInPins;
+ uByte baSourceId[0]; /* [bNrInPins] */
+ /* struct usb2_audio_extension_unit_1 */
+} __packed;
+struct usb2_audio_extension_unit_1 {
+ uByte bNrChannels;
+ uWord wChannelConfig;
+ uByte iChannelNames;
+ uByte bControlSize;
+ uByte bmControls[0]; /* [bControlSize] */
+#define UA_EXT_ENABLE_MASK 1
+#define UA_EXT_ENABLE 1
+ /* uByte iExtension; */
+} __packed;
+
+/* USB terminal types */
+#define UAT_UNDEFINED 0x0100
+#define UAT_STREAM 0x0101
+#define UAT_VENDOR 0x01ff
+/* input terminal types */
+#define UATI_UNDEFINED 0x0200
+#define UATI_MICROPHONE 0x0201
+#define UATI_DESKMICROPHONE 0x0202
+#define UATI_PERSONALMICROPHONE 0x0203
+#define UATI_OMNIMICROPHONE 0x0204
+#define UATI_MICROPHONEARRAY 0x0205
+#define UATI_PROCMICROPHONEARR 0x0206
+/* output terminal types */
+#define UATO_UNDEFINED 0x0300
+#define UATO_SPEAKER 0x0301
+#define UATO_HEADPHONES 0x0302
+#define UATO_DISPLAYAUDIO 0x0303
+#define UATO_DESKTOPSPEAKER 0x0304
+#define UATO_ROOMSPEAKER 0x0305
+#define UATO_COMMSPEAKER 0x0306
+#define UATO_SUBWOOFER 0x0307
+/* bidir terminal types */
+#define UATB_UNDEFINED 0x0400
+#define UATB_HANDSET 0x0401
+#define UATB_HEADSET 0x0402
+#define UATB_SPEAKERPHONE 0x0403
+#define UATB_SPEAKERPHONEESUP 0x0404
+#define UATB_SPEAKERPHONEECANC 0x0405
+/* telephony terminal types */
+#define UATT_UNDEFINED 0x0500
+#define UATT_PHONELINE 0x0501
+#define UATT_TELEPHONE 0x0502
+#define UATT_DOWNLINEPHONE 0x0503
+/* external terminal types */
+#define UATE_UNDEFINED 0x0600
+#define UATE_ANALOGCONN 0x0601
+#define UATE_DIGITALAUIFC 0x0602
+#define UATE_LINECONN 0x0603
+#define UATE_LEGACYCONN 0x0604
+#define UATE_SPDIF 0x0605
+#define UATE_1394DA 0x0606
+#define UATE_1394DV 0x0607
+/* embedded function terminal types */
+#define UATF_UNDEFINED 0x0700
+#define UATF_CALIBNOISE 0x0701
+#define UATF_EQUNOISE 0x0702
+#define UATF_CDPLAYER 0x0703
+#define UATF_DAT 0x0704
+#define UATF_DCC 0x0705
+#define UATF_MINIDISK 0x0706
+#define UATF_ANALOGTAPE 0x0707
+#define UATF_PHONOGRAPH 0x0708
+#define UATF_VCRAUDIO 0x0709
+#define UATF_VIDEODISCAUDIO 0x070a
+#define UATF_DVDAUDIO 0x070b
+#define UATF_TVTUNERAUDIO 0x070c
+#define UATF_SATELLITE 0x070d
+#define UATF_CABLETUNER 0x070e
+#define UATF_DSS 0x070f
+#define UATF_RADIORECV 0x0710
+#define UATF_RADIOXMIT 0x0711
+#define UATF_MULTITRACK 0x0712
+#define UATF_SYNTHESIZER 0x0713
+
+
+#define SET_CUR 0x01
+#define GET_CUR 0x81
+#define SET_MIN 0x02
+#define GET_MIN 0x82
+#define SET_MAX 0x03
+#define GET_MAX 0x83
+#define SET_RES 0x04
+#define GET_RES 0x84
+#define SET_MEM 0x05
+#define GET_MEM 0x85
+#define GET_STAT 0xff
+
+#define MUTE_CONTROL 0x01
+#define VOLUME_CONTROL 0x02
+#define BASS_CONTROL 0x03
+#define MID_CONTROL 0x04
+#define TREBLE_CONTROL 0x05
+#define GRAPHIC_EQUALIZER_CONTROL 0x06
+#define AGC_CONTROL 0x07
+#define DELAY_CONTROL 0x08
+#define BASS_BOOST_CONTROL 0x09
+#define LOUDNESS_CONTROL 0x0a
+
+#define FU_MASK(u) (1 << ((u)-1))
+
+#define MASTER_CHAN 0
+
+#define AS_GENERAL 1
+#define FORMAT_TYPE 2
+#define FORMAT_SPECIFIC 3
+
+#define UA_FMT_PCM 1
+#define UA_FMT_PCM8 2
+#define UA_FMT_IEEE_FLOAT 3
+#define UA_FMT_ALAW 4
+#define UA_FMT_MULAW 5
+#define UA_FMT_MPEG 0x1001
+#define UA_FMT_AC3 0x1002
+
+#define SAMPLING_FREQ_CONTROL 0x01
+#define PITCH_CONTROL 0x02
+
+#define FORMAT_TYPE_UNDEFINED 0
+#define FORMAT_TYPE_I 1
+#define FORMAT_TYPE_II 2
+#define FORMAT_TYPE_III 3
+
+#define UA_PROC_MASK(n) (1<< ((n)-1))
+#define PROCESS_UNDEFINED 0
+#define XX_ENABLE_CONTROL 1
+#define UPDOWNMIX_PROCESS 1
+#define UD_ENABLE_CONTROL 1
+#define UD_MODE_SELECT_CONTROL 2
+#define DOLBY_PROLOGIC_PROCESS 2
+#define DP_ENABLE_CONTROL 1
+#define DP_MODE_SELECT_CONTROL 2
+#define P3D_STEREO_EXTENDER_PROCESS 3
+#define P3D_ENABLE_CONTROL 1
+#define P3D_SPACIOUSNESS_CONTROL 2
+#define REVERBATION_PROCESS 4
+#define RV_ENABLE_CONTROL 1
+#define RV_LEVEL_CONTROL 2
+#define RV_TIME_CONTROL 3
+#define RV_FEEDBACK_CONTROL 4
+#define CHORUS_PROCESS 5
+#define CH_ENABLE_CONTROL 1
+#define CH_LEVEL_CONTROL 2
+#define CH_RATE_CONTROL 3
+#define CH_DEPTH_CONTROL 4
+#define DYN_RANGE_COMP_PROCESS 6
+#define DR_ENABLE_CONTROL 1
+#define DR_COMPRESSION_RATE_CONTROL 2
+#define DR_MAXAMPL_CONTROL 3
+#define DR_THRESHOLD_CONTROL 4
+#define DR_ATTACK_TIME_CONTROL 5
+#define DR_RELEASE_TIME_CONTROL 6
diff --git a/sys/dev/usb/storage/ata-usb.c b/sys/dev/usb/storage/ata-usb.c
new file mode 100644
index 0000000..01ce320
--- /dev/null
+++ b/sys/dev/usb/storage/ata-usb.c
@@ -0,0 +1,1102 @@
+/*-
+ * Copyright (c) 2006 - 2008 Søren Schmidt <sos@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Copyright (c) 2006 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.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+
+#include <sys/ata.h>
+#include <sys/bio.h>
+#include <sys/sema.h>
+#include <sys/taskqueue.h>
+#include <vm/uma.h>
+
+#include <dev/ata/ata-all.h>
+#include <ata_if.h>
+
+#define ATAUSB_BULK_SIZE (1<<17)
+
+/* Command Block Wrapper */
+struct bbb_cbw {
+ uint8_t signature[4];
+#define CBWSIGNATURE 0x43425355
+
+ uint8_t tag[4];
+ uint8_t transfer_length[4];
+ uint8_t flags;
+#define CBWFLAGS_OUT 0x00
+#define CBWFLAGS_IN 0x80
+
+ uint8_t lun;
+ uint8_t length;
+#define CBWCDBLENGTH 16
+
+ uint8_t cdb[CBWCDBLENGTH];
+} __packed;
+
+/* Command Status Wrapper */
+struct bbb_csw {
+ uint8_t signature[4];
+#define CSWSIGNATURE 0x53425355
+
+ uint8_t tag[4];
+ uint8_t residue[4];
+ uint8_t status;
+#define CSWSTATUS_GOOD 0x0
+#define CSWSTATUS_FAILED 0x1
+#define CSWSTATUS_PHASE 0x2
+} __packed;
+
+/* USB-ATA 'controller' softc */
+struct atausb2_softc {
+ struct bbb_cbw cbw;
+ struct bbb_csw csw;
+ struct mtx locked_mtx;
+
+ struct ata_channel *locked_ch;
+ struct ata_channel *restart_ch;
+ struct ata_request *ata_request;
+
+#define ATAUSB_T_BBB_RESET1 0
+#define ATAUSB_T_BBB_RESET2 1
+#define ATAUSB_T_BBB_RESET3 2
+#define ATAUSB_T_BBB_COMMAND 3
+#define ATAUSB_T_BBB_DATA_READ 4
+#define ATAUSB_T_BBB_DATA_RD_CS 5
+#define ATAUSB_T_BBB_DATA_WRITE 6
+#define ATAUSB_T_BBB_DATA_WR_CS 7
+#define ATAUSB_T_BBB_STATUS 8
+#define ATAUSB_T_BBB_MAX 9
+
+#define ATAUSB_T_MAX ATAUSB_T_BBB_MAX
+
+ struct usb2_xfer *xfer[ATAUSB_T_MAX];
+ caddr_t ata_data;
+ device_t dev;
+
+ uint32_t timeout;
+ uint32_t ata_donecount;
+ uint32_t ata_bytecount;
+
+ uint8_t last_xfer_no;
+ uint8_t usb2_speed;
+ uint8_t intr_stalled;
+ uint8_t maxlun;
+ uint8_t iface_no;
+ uint8_t status_try;
+};
+
+static const int atausbdebug = 0;
+
+/* prototypes */
+
+static device_probe_t atausb2_probe;
+static device_attach_t atausb2_attach;
+static device_detach_t atausb2_detach;
+
+static usb2_callback_t atausb2_t_bbb_reset1_callback;
+static usb2_callback_t atausb2_t_bbb_reset2_callback;
+static usb2_callback_t atausb2_t_bbb_reset3_callback;
+static usb2_callback_t atausb2_t_bbb_command_callback;
+static usb2_callback_t atausb2_t_bbb_data_read_callback;
+static usb2_callback_t atausb2_t_bbb_data_rd_cs_callback;
+static usb2_callback_t atausb2_t_bbb_data_write_callback;
+static usb2_callback_t atausb2_t_bbb_data_wr_cs_callback;
+static usb2_callback_t atausb2_t_bbb_status_callback;
+static usb2_callback_t atausb2_tr_error;
+
+static void atausb2_cancel_request(struct atausb2_softc *sc);
+static void atausb2_transfer_start(struct atausb2_softc *sc, uint8_t xfer_no);
+static void atausb2_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer, uint8_t next_xfer, uint8_t stall_xfer);
+static int ata_usbchannel_begin_transaction(struct ata_request *request);
+static int ata_usbchannel_end_transaction(struct ata_request *request);
+
+static device_probe_t ata_usbchannel_probe;
+static device_attach_t ata_usbchannel_attach;
+static device_detach_t ata_usbchannel_detach;
+
+static ata_setmode_t ata_usbchannel_setmode;
+static ata_locking_t ata_usbchannel_locking;
+
+/*
+ * USB frontend part
+ */
+
+struct usb2_config atausb2_config[ATAUSB_T_BBB_MAX] = {
+
+ [ATAUSB_T_BBB_RESET1] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &atausb2_t_bbb_reset1_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 500, /* 500 milliseconds */
+ },
+
+ [ATAUSB_T_BBB_RESET2] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &atausb2_t_bbb_reset2_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 50, /* 50 milliseconds */
+ },
+
+ [ATAUSB_T_BBB_RESET3] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &atausb2_t_bbb_reset3_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 50, /* 50 milliseconds */
+ },
+
+ [ATAUSB_T_BBB_COMMAND] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = sizeof(struct bbb_cbw),
+ .mh.flags = {},
+ .mh.callback = &atausb2_t_bbb_command_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [ATAUSB_T_BBB_DATA_READ] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = ATAUSB_BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .mh.callback = &atausb2_t_bbb_data_read_callback,
+ .mh.timeout = 0, /* overwritten later */
+ },
+
+ [ATAUSB_T_BBB_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 = &atausb2_t_bbb_data_rd_cs_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [ATAUSB_T_BBB_DATA_WRITE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = ATAUSB_BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .mh.callback = &atausb2_t_bbb_data_write_callback,
+ .mh.timeout = 0, /* overwritten later */
+ },
+
+ [ATAUSB_T_BBB_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 = &atausb2_t_bbb_data_wr_cs_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [ATAUSB_T_BBB_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 = &atausb2_t_bbb_status_callback,
+ .mh.timeout = 5000, /* ms */
+ },
+};
+
+static devclass_t atausb2_devclass;
+
+static device_method_t atausb2_methods[] = {
+ DEVMETHOD(device_probe, atausb2_probe),
+ DEVMETHOD(device_attach, atausb2_attach),
+ DEVMETHOD(device_detach, atausb2_detach),
+ {0, 0}
+};
+
+static driver_t atausb2_driver = {
+ .name = "atausb",
+ .methods = atausb2_methods,
+ .size = sizeof(struct atausb2_softc),
+};
+
+DRIVER_MODULE(atausb, ushub, atausb2_driver, atausb2_devclass, 0, 0);
+MODULE_DEPEND(atausb, usb, 1, 1, 1);
+MODULE_VERSION(atausb, 1);
+
+static int
+atausb2_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->use_generic == 0) {
+ /* give other drivers a try first */
+ return (ENXIO);
+ }
+ id = usb2_get_interface_descriptor(uaa->iface);
+ if ((!id) || (id->bInterfaceClass != UICLASS_MASS)) {
+ return (ENXIO);
+ }
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_QIC157:
+ case UISUBCLASS_RBC:
+ case UISUBCLASS_SCSI:
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ case UISUBCLASS_UFI:
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_CBI:
+ case UIPROTO_MASS_CBI_I:
+ case UIPROTO_MASS_BBB:
+ case UIPROTO_MASS_BBB_OLD:
+ return (0);
+ default:
+ return (0);
+ }
+ break;
+ default:
+ return (0);
+ }
+}
+
+static int
+atausb2_attach(device_t dev)
+{
+ struct atausb2_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+ const char *proto, *subclass;
+ struct usb2_device_request request;
+ uint16_t i;
+ uint8_t maxlun;
+ uint8_t has_intr;
+ int err;
+
+ device_set_usb2_desc(dev);
+
+ sc->dev = dev;
+ sc->maxlun = 0;
+ sc->locked_ch = NULL;
+ sc->restart_ch = NULL;
+ sc->usb2_speed = usb2_get_speed(uaa->device);
+ mtx_init(&sc->locked_mtx, "ATAUSB lock", NULL, (MTX_DEF | MTX_RECURSE));
+
+ id = usb2_get_interface_descriptor(uaa->iface);
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_BBB:
+ case UIPROTO_MASS_BBB_OLD:
+ proto = "Bulk-Only";
+ break;
+ case UIPROTO_MASS_CBI:
+ proto = "CBI";
+ break;
+ case UIPROTO_MASS_CBI_I:
+ proto = "CBI with CCI";
+ break;
+ default:
+ proto = "Unknown";
+ }
+
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_RBC:
+ subclass = "RBC";
+ break;
+ case UISUBCLASS_QIC157:
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ subclass = "ATAPI";
+ break;
+ case UISUBCLASS_SCSI:
+ subclass = "SCSI";
+ break;
+ case UISUBCLASS_UFI:
+ subclass = "UFI";
+ break;
+ default:
+ subclass = "Unknown";
+ }
+
+ has_intr = (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I);
+ sc->iface_no = id->bInterfaceNumber;
+
+ device_printf(dev, "using %s over %s\n", subclass, proto);
+ if (strcmp(proto, "Bulk-Only") ||
+ (strcmp(subclass, "ATAPI") && strcmp(subclass, "SCSI"))) {
+ goto detach;
+ }
+ err = usb2_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
+ sc->xfer, atausb2_config, ATAUSB_T_BBB_MAX, sc,
+ &sc->locked_mtx);
+
+ /* skip reset first time */
+ sc->last_xfer_no = ATAUSB_T_BBB_COMMAND;
+
+ if (err) {
+ device_printf(sc->dev, "could not setup required "
+ "transfers, %s\n", usb2_errstr(err));
+ goto detach;
+ }
+ /* get number of devices so we can add matching channels */
+ request.bmRequestType = UT_READ_CLASS_INTERFACE;
+ request.bRequest = 0xfe; /* GET_MAX_LUN; */
+ USETW(request.wValue, 0);
+ USETW(request.wIndex, sc->iface_no);
+ USETW(request.wLength, sizeof(maxlun));
+ err = usb2_do_request(uaa->device, &Giant, &request, &maxlun);
+
+ if (err) {
+ if (bootverbose) {
+ device_printf(sc->dev, "get maxlun not supported %s\n",
+ usb2_errstr(err));
+ }
+ } else {
+ sc->maxlun = maxlun;
+ if (bootverbose) {
+ device_printf(sc->dev, "maxlun=%d\n", sc->maxlun);
+ }
+ }
+
+ /* ata channels are children to this USB control device */
+ for (i = 0; i <= sc->maxlun; i++) {
+ if (!device_add_child(sc->dev, "ata",
+ devclass_find_free_unit(ata_devclass, 2))) {
+ device_printf(sc->dev, "failed to attach ata child device\n");
+ goto detach;
+ }
+ }
+ bus_generic_attach(sc->dev);
+
+ return (0);
+
+detach:
+ atausb2_detach(dev);
+ return (ENXIO);
+}
+
+static int
+atausb2_detach(device_t dev)
+{
+ struct atausb2_softc *sc = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ /* teardown our statemachine */
+
+ usb2_transfer_unsetup(sc->xfer, ATAUSB_T_MAX);
+
+ /* detach & delete all children, if any */
+
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++) {
+ device_delete_child(dev, children[i]);
+ }
+ free(children, M_TEMP);
+ }
+ mtx_destroy(&sc->locked_mtx);
+ return (0);
+}
+
+static void
+atausb2_transfer_start(struct atausb2_softc *sc, uint8_t xfer_no)
+{
+ if (atausbdebug) {
+ device_printf(sc->dev, "BBB transfer %d\n", xfer_no);
+ }
+ if (sc->xfer[xfer_no]) {
+ sc->last_xfer_no = xfer_no;
+ usb2_transfer_start(sc->xfer[xfer_no]);
+ } else {
+ atausb2_cancel_request(sc);
+ }
+}
+
+static void
+atausb2_t_bbb_reset1_callback(struct usb2_xfer *xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ atausb2_transfer_start(sc, ATAUSB_T_BBB_RESET2);
+ return;
+
+ case USB_ST_SETUP:
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = 0xff; /* bulk-only reset */
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->nframes = 1;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ atausb2_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+atausb2_t_bbb_reset2_callback(struct usb2_xfer *xfer)
+{
+ atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_RESET3,
+ ATAUSB_T_BBB_DATA_READ);
+}
+
+static void
+atausb2_t_bbb_reset3_callback(struct usb2_xfer *xfer)
+{
+ atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_COMMAND,
+ ATAUSB_T_BBB_DATA_WRITE);
+}
+
+static void
+atausb2_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer,
+ uint8_t next_xfer,
+ uint8_t stall_xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ atausb2_transfer_start(sc, next_xfer);
+ return;
+
+ case USB_ST_SETUP:
+ if (usb2_clear_stall_callback(xfer, sc->xfer[stall_xfer])) {
+ goto tr_transferred;
+ }
+ return;
+
+ default: /* Error */
+ atausb2_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+atausb2_t_bbb_command_callback(struct usb2_xfer *xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+ struct ata_request *request = sc->ata_request;
+ struct ata_channel *ch;
+ uint32_t tag;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ atausb2_transfer_start
+ (sc, ((request->flags & ATA_R_READ) ? ATAUSB_T_BBB_DATA_READ :
+ (request->flags & ATA_R_WRITE) ? ATAUSB_T_BBB_DATA_WRITE :
+ ATAUSB_T_BBB_STATUS));
+ return;
+
+ case USB_ST_SETUP:
+
+ sc->status_try = 0;
+
+ if (request) {
+ ch = device_get_softc(request->parent);
+
+ sc->timeout = (request->timeout * 1000) + 5000;
+
+ tag = UGETDW(sc->cbw.tag) + 1;
+
+ USETDW(sc->cbw.signature, CBWSIGNATURE);
+ USETDW(sc->cbw.tag, tag);
+ USETDW(sc->cbw.transfer_length, request->bytecount);
+ sc->cbw.flags = (request->flags & ATA_R_READ) ? CBWFLAGS_IN : CBWFLAGS_OUT;
+ sc->cbw.lun = ch->unit;
+ sc->cbw.length = 16;
+ bzero(sc->cbw.cdb, 16);
+ bcopy(request->u.atapi.ccb, sc->cbw.cdb, 12); /* XXX SOS */
+
+ usb2_copy_in(xfer->frbuffers, 0, &sc->cbw, sizeof(sc->cbw));
+
+ xfer->frlengths[0] = sizeof(sc->cbw);
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ atausb2_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+atausb2_t_bbb_data_read_callback(struct usb2_xfer *xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ usb2_copy_out(xfer->frbuffers, 0,
+ sc->ata_data, xfer->actlen);
+
+ sc->ata_bytecount -= xfer->actlen;
+ sc->ata_data += xfer->actlen;
+ sc->ata_donecount += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->ata_bytecount = 0;
+ }
+ case USB_ST_SETUP:
+
+ if (atausbdebug > 1) {
+ device_printf(sc->dev, "%s: max_bulk=%d, ata_bytecount=%d\n",
+ __FUNCTION__, max_bulk, sc->ata_bytecount);
+ }
+ if (sc->ata_bytecount == 0) {
+ atausb2_transfer_start(sc, ATAUSB_T_BBB_STATUS);
+ return;
+ }
+ if (max_bulk > sc->ata_bytecount) {
+ max_bulk = sc->ata_bytecount;
+ }
+ xfer->timeout = sc->timeout;
+ xfer->frlengths[0] = max_bulk;
+
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ atausb2_tr_error(xfer);
+ } else {
+ atausb2_transfer_start(sc, ATAUSB_T_BBB_DATA_RD_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+atausb2_t_bbb_data_rd_cs_callback(struct usb2_xfer *xfer)
+{
+ atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_STATUS,
+ ATAUSB_T_BBB_DATA_READ);
+}
+
+static void
+atausb2_t_bbb_data_write_callback(struct usb2_xfer *xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ sc->ata_bytecount -= xfer->actlen;
+ sc->ata_data += xfer->actlen;
+ sc->ata_donecount += xfer->actlen;
+
+ case USB_ST_SETUP:
+
+ if (atausbdebug > 1) {
+ device_printf(sc->dev, "%s: max_bulk=%d, ata_bytecount=%d\n",
+ __FUNCTION__, max_bulk, sc->ata_bytecount);
+ }
+ if (sc->ata_bytecount == 0) {
+ atausb2_transfer_start(sc, ATAUSB_T_BBB_STATUS);
+ return;
+ }
+ if (max_bulk > sc->ata_bytecount) {
+ max_bulk = sc->ata_bytecount;
+ }
+ xfer->timeout = sc->timeout;
+ xfer->frlengths[0] = max_bulk;
+
+ usb2_copy_in(xfer->frbuffers, 0,
+ sc->ata_data, max_bulk);
+
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ atausb2_tr_error(xfer);
+ } else {
+ atausb2_transfer_start(sc, ATAUSB_T_BBB_DATA_WR_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+atausb2_t_bbb_data_wr_cs_callback(struct usb2_xfer *xfer)
+{
+ atausb2_t_bbb_data_clear_stall_callback(xfer, ATAUSB_T_BBB_STATUS,
+ ATAUSB_T_BBB_DATA_WRITE);
+}
+
+static void
+atausb2_t_bbb_status_callback(struct usb2_xfer *xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+ struct ata_request *request = sc->ata_request;
+ uint32_t residue;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < sizeof(sc->csw)) {
+ bzero(&sc->csw, sizeof(sc->csw));
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &sc->csw, xfer->actlen);
+
+ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) {
+ request->donecount = sc->ata_donecount;
+ }
+ residue = UGETDW(sc->csw.residue);
+
+ if (!residue) {
+ residue = (request->bytecount - request->donecount);
+ }
+ if (residue > request->bytecount) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "truncating residue from %d "
+ "to %d bytes\n", residue,
+ request->bytecount);
+ }
+ residue = request->bytecount;
+ }
+ /* check CSW and handle eventual error */
+ if (UGETDW(sc->csw.signature) != CSWSIGNATURE) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "bad CSW signature 0x%08x != 0x%08x\n",
+ UGETDW(sc->csw.signature), CSWSIGNATURE);
+ }
+ goto tr_error;
+ } else if (UGETDW(sc->csw.tag) != UGETDW(sc->cbw.tag)) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "bad CSW tag %d != %d\n",
+ UGETDW(sc->csw.tag), UGETDW(sc->cbw.tag));
+ }
+ goto tr_error;
+ } else if (sc->csw.status > CSWSTATUS_PHASE) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "bad CSW status %d > %d\n",
+ sc->csw.status, CSWSTATUS_PHASE);
+ }
+ goto tr_error;
+ } else if (sc->csw.status == CSWSTATUS_PHASE) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "phase error residue = %d\n", residue);
+ }
+ goto tr_error;
+ } else if (request->donecount > request->bytecount) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "buffer overrun %d > %d\n",
+ request->donecount, request->bytecount);
+ }
+ goto tr_error;
+ } else if (sc->csw.status == CSWSTATUS_FAILED) {
+ if (atausbdebug) {
+ device_printf(sc->dev, "CSWSTATUS_FAILED\n");
+ }
+ request->error = ATA_E_ATAPI_SENSE_MASK;
+ }
+ sc->last_xfer_no = ATAUSB_T_BBB_COMMAND;
+
+ sc->ata_request = NULL;
+
+ /* drop the USB transfer lock while doing the ATA interrupt */
+ mtx_unlock(&sc->locked_mtx);
+
+ ata_interrupt(device_get_softc(request->parent));
+
+ mtx_lock(&sc->locked_mtx);
+ return;
+
+ case USB_ST_SETUP:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default:
+tr_error:
+ if ((xfer->error == USB_ERR_CANCELLED) ||
+ (sc->status_try)) {
+ atausb2_tr_error(xfer);
+ } else {
+ sc->status_try = 1;
+ atausb2_transfer_start(sc, ATAUSB_T_BBB_DATA_RD_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+atausb2_cancel_request(struct atausb2_softc *sc)
+{
+ struct ata_request *request;
+
+ mtx_assert(&sc->locked_mtx, MA_OWNED);
+
+ request = sc->ata_request;
+ sc->ata_request = NULL;
+ sc->last_xfer_no = ATAUSB_T_BBB_RESET1;
+
+ if (request) {
+ request->error = ATA_E_ATAPI_SENSE_MASK;
+
+ mtx_unlock(&sc->locked_mtx);
+
+ ata_interrupt(device_get_softc(request->parent));
+
+ mtx_lock(&sc->locked_mtx);
+ }
+}
+
+static void
+atausb2_tr_error(struct usb2_xfer *xfer)
+{
+ struct atausb2_softc *sc = xfer->priv_sc;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+
+ if (atausbdebug) {
+ device_printf(sc->dev, "transfer failed, %s, in state %d "
+ "-> BULK reset\n", usb2_errstr(xfer->error),
+ sc->last_xfer_no);
+ }
+ }
+ atausb2_cancel_request(sc);
+}
+
+/*
+ * ATA backend part
+ */
+struct atapi_inquiry {
+ uint8_t device_type;
+ uint8_t device_modifier;
+ uint8_t version;
+ uint8_t response_format;
+ uint8_t length;
+ uint8_t reserved[2];
+ uint8_t flags;
+ uint8_t vendor[8];
+ uint8_t product[16];
+ uint8_t revision[4];
+ /* uint8_t crap[60]; */
+} __packed;
+
+static int
+ata_usbchannel_begin_transaction(struct ata_request *request)
+{
+ struct atausb2_softc *sc =
+ device_get_softc(device_get_parent(request->parent));
+ int error;
+
+ if (atausbdebug > 1) {
+ device_printf(request->dev, "begin_transaction %s\n",
+ ata_cmd2str(request));
+ }
+ mtx_lock(&sc->locked_mtx);
+
+ /* sanity, just in case */
+ if (sc->ata_request) {
+ device_printf(request->dev, "begin is busy, "
+ "state = %d\n", sc->last_xfer_no);
+ request->result = EBUSY;
+ error = ATA_OP_FINISHED;
+ goto done;
+ }
+ /*
+ * XXX SOS convert the request into the format used, only BBB for
+ * now
+ */
+
+ /* ATA/ATAPI IDENTIFY needs special treatment */
+ if (!(request->flags & ATA_R_ATAPI)) {
+ if (request->u.ata.command != ATA_ATAPI_IDENTIFY) {
+ device_printf(request->dev, "%s unsupported\n",
+ ata_cmd2str(request));
+ request->result = EIO;
+ error = ATA_OP_FINISHED;
+ goto done;
+ }
+ request->flags |= ATA_R_ATAPI;
+ bzero(request->u.atapi.ccb, 16);
+ request->u.atapi.ccb[0] = ATAPI_INQUIRY;
+ request->u.atapi.ccb[4] = 255; /* sizeof(struct
+ * atapi_inquiry); */
+ request->data += 256; /* arbitrary offset into ata_param */
+ request->bytecount = 255; /* sizeof(struct
+ * atapi_inquiry); */
+ }
+ if (sc->xfer[sc->last_xfer_no]) {
+
+ sc->ata_request = request;
+ sc->ata_bytecount = request->bytecount;
+ sc->ata_data = request->data;
+ sc->ata_donecount = 0;
+
+ usb2_transfer_start(sc->xfer[sc->last_xfer_no]);
+ error = ATA_OP_CONTINUES;
+ } else {
+ request->result = EIO;
+ error = ATA_OP_FINISHED;
+ }
+
+done:
+ mtx_unlock(&sc->locked_mtx);
+ return (error);
+}
+
+static int
+ata_usbchannel_end_transaction(struct ata_request *request)
+{
+ if (atausbdebug > 1) {
+ device_printf(request->dev, "end_transaction %s\n",
+ ata_cmd2str(request));
+ }
+ /*
+ * XXX SOS convert the request from the format used, only BBB for
+ * now
+ */
+
+ /* ATA/ATAPI IDENTIFY needs special treatment */
+ if ((request->flags & ATA_R_ATAPI) &&
+ (request->u.atapi.ccb[0] == ATAPI_INQUIRY)) {
+ struct ata_device *atadev = device_get_softc(request->dev);
+ struct atapi_inquiry *inquiry = (struct atapi_inquiry *)request->data;
+ uint16_t *ptr;
+
+ /* convert inquiry data into simple ata_param like format */
+ atadev->param.config = ATA_PROTO_ATAPI | ATA_PROTO_ATAPI_12;
+ atadev->param.config |= (inquiry->device_type & 0x1f) << 8;
+ bzero(atadev->param.model, sizeof(atadev->param.model));
+ strncpy(atadev->param.model, inquiry->vendor, 8);
+ strcpy(atadev->param.model, " ");
+ strncpy(atadev->param.model, inquiry->product, 16);
+ ptr = (uint16_t *)(atadev->param.model + sizeof(atadev->param.model));
+ while (--ptr >= (uint16_t *)atadev->param.model) {
+ *ptr = ntohs(*ptr);
+ }
+ strncpy(atadev->param.revision, inquiry->revision, 4);
+ ptr = (uint16_t *)(atadev->param.revision + sizeof(atadev->param.revision));
+ while (--ptr >= (uint16_t *)atadev->param.revision) {
+ *ptr = ntohs(*ptr);
+ }
+ request->result = 0;
+ }
+ return (ATA_OP_FINISHED);
+}
+
+static int
+ata_usbchannel_probe(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int count, i;
+ char buffer[32];
+
+ /* take care of green memory */
+ bzero(ch, sizeof(struct ata_channel));
+
+ /* find channel number on this controller */
+ if (!device_get_children(device_get_parent(dev), &children, &count)) {
+ for (i = 0; i < count; i++) {
+ if (children[i] == dev)
+ ch->unit = i;
+ }
+ free(children, M_TEMP);
+ }
+ snprintf(buffer, sizeof(buffer), "USB lun %d", ch->unit);
+ device_set_desc_copy(dev, buffer);
+
+ return (0);
+}
+
+static int
+ata_usbchannel_attach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+
+ /* initialize the softc basics */
+ ch->dev = dev;
+ ch->state = ATA_IDLE;
+ ch->hw.begin_transaction = ata_usbchannel_begin_transaction;
+ ch->hw.end_transaction = ata_usbchannel_end_transaction;
+ ch->hw.status = NULL;
+ ch->hw.command = NULL;
+ bzero(&ch->state_mtx, sizeof(struct mtx));
+ mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF);
+ bzero(&ch->queue_mtx, sizeof(struct mtx));
+ mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF);
+ TAILQ_INIT(&ch->ata_queue);
+
+ /* XXX SOS reset the controller HW, the channel and device(s) */
+ /* ATA_RESET(dev); */
+
+ /* probe and attach device on this channel */
+ ch->devices = ATA_ATAPI_MASTER;
+ if (!ata_delayed_attach) {
+ ata_identify(dev);
+ }
+ return (0);
+}
+
+static int
+ata_usbchannel_detach(device_t dev)
+{
+ struct ata_channel *ch = device_get_softc(dev);
+ device_t *children;
+ int nchildren, i;
+
+ /* detach & delete all children */
+ if (!device_get_children(dev, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ if (children[i])
+ device_delete_child(dev, children[i]);
+ free(children, M_TEMP);
+ }
+ mtx_destroy(&ch->state_mtx);
+ mtx_destroy(&ch->queue_mtx);
+ return (0);
+}
+
+static void
+ata_usbchannel_setmode(device_t parent, device_t dev)
+{
+ struct atausb2_softc *sc = device_get_softc(GRANDPARENT(dev));
+ struct ata_device *atadev = device_get_softc(dev);
+
+ if (sc->usb2_speed == USB_SPEED_HIGH)
+ atadev->mode = ATA_USB2;
+ else
+ atadev->mode = ATA_USB1;
+}
+
+static int
+ata_usbchannel_locking(device_t dev, int flags)
+{
+ struct atausb2_softc *sc = device_get_softc(device_get_parent(dev));
+ struct ata_channel *ch = device_get_softc(dev);
+ int res = -1;
+
+ mtx_lock(&sc->locked_mtx);
+ switch (flags) {
+ case ATA_LF_LOCK:
+ if (sc->locked_ch == NULL)
+ sc->locked_ch = ch;
+ if (sc->locked_ch != ch)
+ sc->restart_ch = ch;
+ break;
+
+ case ATA_LF_UNLOCK:
+ if (sc->locked_ch == ch) {
+ sc->locked_ch = NULL;
+ if (sc->restart_ch) {
+ ch = sc->restart_ch;
+ sc->restart_ch = NULL;
+ mtx_unlock(&sc->locked_mtx);
+ ata_start(ch->dev);
+ return (res);
+ }
+ }
+ break;
+
+ case ATA_LF_WHICH:
+ break;
+ }
+ if (sc->locked_ch) {
+ res = sc->locked_ch->unit;
+ }
+ mtx_unlock(&sc->locked_mtx);
+ return (res);
+}
+
+static device_method_t ata_usbchannel_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, ata_usbchannel_probe),
+ DEVMETHOD(device_attach, ata_usbchannel_attach),
+ DEVMETHOD(device_detach, ata_usbchannel_detach),
+
+ /* ATA methods */
+ DEVMETHOD(ata_setmode, ata_usbchannel_setmode),
+ DEVMETHOD(ata_locking, ata_usbchannel_locking),
+ /* DEVMETHOD(ata_reset, ata_usbchannel_reset), */
+
+ {0, 0}
+};
+
+static driver_t ata_usbchannel_driver = {
+ "ata",
+ ata_usbchannel_methods,
+ sizeof(struct ata_channel),
+};
+
+DRIVER_MODULE(ata, atausb, ata_usbchannel_driver, ata_devclass, 0, 0);
+MODULE_DEPEND(atausb, ata, 1, 1, 1);
diff --git a/sys/dev/usb/storage/rio500_usb.h b/sys/dev/usb/storage/rio500_usb.h
new file mode 100644
index 0000000..5b53e2c
--- /dev/null
+++ b/sys/dev/usb/storage/rio500_usb.h
@@ -0,0 +1,48 @@
+/*-
+ ----------------------------------------------------------------------
+
+ Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted under any licence of your choise which
+ meets the open source licence definiton
+ http://www.opensource.org/opd.html such as the GNU licence or the
+ BSD licence.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License or the BSD license for more details.
+
+ ----------------------------------------------------------------------
+
+ Modified for FreeBSD by Iwasa Kazmi <kzmi@ca2.so-net.ne.jp>
+
+ ---------------------------------------------------------------------- */
+
+/* $FreeBSD$ */
+
+#include <sys/ioccom.h>
+#ifndef USB_VENDOR_DIAMOND
+#define USB_VENDOR_DIAMOND 0x841
+#endif
+#ifndef USB_PRODUCT_DIAMOND_RIO500USB
+#define USB_PRODUCT_DIAMOND_RIO500USB 0x1
+#endif
+
+struct RioCommand
+{
+ uint16_t length;
+ int request;
+ int requesttype;
+ int value;
+ int index;
+ void *buffer;
+ int timeout;
+};
+
+#define RIO_SEND_COMMAND _IOWR('U', 200, struct RioCommand)
+#define RIO_RECV_COMMAND _IOWR('U', 201, struct RioCommand)
+
+#define RIO_DIR_OUT 0x0
+#define RIO_DIR_IN 0x1
diff --git a/sys/dev/usb/storage/umass.c b/sys/dev/usb/storage/umass.c
new file mode 100644
index 0000000..156e677
--- /dev/null
+++ b/sys/dev/usb/storage/umass.c
@@ -0,0 +1,3619 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 1999 MAEKAWA Masahide <bishop@rr.iij4u.or.jp>,
+ * Nick Hibma <n_hibma@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $NetBSD: umass.c,v 1.28 2000/04/02 23:46:53 augustss Exp $
+ */
+
+/* Also already merged from NetBSD:
+ * $NetBSD: umass.c,v 1.67 2001/11/25 19:05:22 augustss Exp $
+ * $NetBSD: umass.c,v 1.90 2002/11/04 19:17:33 pooka Exp $
+ * $NetBSD: umass.c,v 1.108 2003/11/07 17:03:25 wiz Exp $
+ * $NetBSD: umass.c,v 1.109 2003/12/04 13:57:31 keihan Exp $
+ */
+
+/*
+ * Universal Serial Bus Mass Storage Class specs:
+ * http://www.usb.org/developers/devclass_docs/usb_msc_overview_1.2.pdf
+ * http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
+ * http://www.usb.org/developers/devclass_docs/usb_msc_cbi_1.1.pdf
+ * http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf
+ */
+
+/*
+ * Ported to NetBSD by Lennart Augustsson <augustss@NetBSD.org>.
+ * Parts of the code written by Jason R. Thorpe <thorpej@shagadelic.org>.
+ */
+
+/*
+ * The driver handles 3 Wire Protocols
+ * - Command/Bulk/Interrupt (CBI)
+ * - Command/Bulk/Interrupt with Command Completion Interrupt (CBI with CCI)
+ * - Mass Storage Bulk-Only (BBB)
+ * (BBB refers Bulk/Bulk/Bulk for Command/Data/Status phases)
+ *
+ * Over these wire protocols it handles the following command protocols
+ * - SCSI
+ * - UFI (floppy command set)
+ * - 8070i (ATAPI)
+ *
+ * UFI and 8070i (ATAPI) are transformed versions of the SCSI command set. The
+ * sc->sc_transform method is used to convert the commands into the appropriate
+ * format (if at all necessary). For example, UFI requires all commands to be
+ * 12 bytes in length amongst other things.
+ *
+ * The source code below is marked and can be split into a number of pieces
+ * (in this order):
+ *
+ * - probe/attach/detach
+ * - generic transfer routines
+ * - BBB
+ * - CBI
+ * - CBI_I (in addition to functions from CBI)
+ * - CAM (Common Access Method)
+ * - SCSI
+ * - UFI
+ * - 8070i (ATAPI)
+ *
+ * The protocols are implemented using a state machine, for the transfers as
+ * well as for the resets. The state machine is contained in umass_t_*_callback.
+ * The state machine is started through either umass_command_start() or
+ * umass_reset().
+ *
+ * The reason for doing this is a) CAM performs a lot better this way and b) it
+ * avoids using tsleep from interrupt context (for example after a failed
+ * transfer).
+ */
+
+/*
+ * The SCSI related part of this driver has been derived from the
+ * dev/ppbus/vpo.c driver, by Nicolas Souchu (nsouch@freebsd.org).
+ *
+ * The CAM layer uses so called actions which are messages sent to the host
+ * adapter for completion. The actions come in through umass_cam_action. The
+ * appropriate block of routines is called depending on the transport protocol
+ * in use. When the transfer has finished, these routines call
+ * umass_cam_cb again to complete the CAM command.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_da.h>
+
+#include <cam/cam_periph.h>
+
+#if 1
+/* this enables loading of virtual buffers into DMA */
+#define UMASS_USB_FLAGS .ext_buffer=1,
+#else
+#define UMASS_USB_FLAGS
+#endif
+
+#if USB_DEBUG
+#define DIF(m, x) \
+ do { \
+ if (umass_debug & (m)) { x ; } \
+ } while (0)
+
+#define DPRINTF(sc, m, fmt, ...) \
+ do { \
+ if (umass_debug & (m)) { \
+ printf("%s:%s: " fmt, \
+ (sc) ? (const char *)(sc)->sc_name : \
+ (const char *)"umassX", \
+ __FUNCTION__ ,## __VA_ARGS__); \
+ } \
+ } while (0)
+
+#define UDMASS_GEN 0x00010000 /* general */
+#define UDMASS_SCSI 0x00020000 /* scsi */
+#define UDMASS_UFI 0x00040000 /* ufi command set */
+#define UDMASS_ATAPI 0x00080000 /* 8070i command set */
+#define UDMASS_CMD (UDMASS_SCSI|UDMASS_UFI|UDMASS_ATAPI)
+#define UDMASS_USB 0x00100000 /* USB general */
+#define UDMASS_BBB 0x00200000 /* Bulk-Only transfers */
+#define UDMASS_CBI 0x00400000 /* CBI transfers */
+#define UDMASS_WIRE (UDMASS_BBB|UDMASS_CBI)
+#define UDMASS_ALL 0xffff0000 /* all of the above */
+static int umass_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass");
+SYSCTL_INT(_hw_usb2_umass, OID_AUTO, debug, CTLFLAG_RW,
+ &umass_debug, 0, "umass debug level");
+#else
+#define DIF(...) do { } while (0)
+#define DPRINTF(...) do { } while (0)
+#endif
+
+#define UMASS_GONE ((struct umass_softc *)1)
+
+#define UMASS_BULK_SIZE (1 << 17)
+#define UMASS_CBI_DIAGNOSTIC_CMDLEN 12 /* bytes */
+#define UMASS_MAX_CMDLEN MAX(12, CAM_MAX_CDBLEN) /* bytes */
+
+/* USB transfer definitions */
+
+#define UMASS_T_BBB_RESET1 0 /* Bulk-Only */
+#define UMASS_T_BBB_RESET2 1
+#define UMASS_T_BBB_RESET3 2
+#define UMASS_T_BBB_COMMAND 3
+#define UMASS_T_BBB_DATA_READ 4
+#define UMASS_T_BBB_DATA_RD_CS 5
+#define UMASS_T_BBB_DATA_WRITE 6
+#define UMASS_T_BBB_DATA_WR_CS 7
+#define UMASS_T_BBB_STATUS 8
+#define UMASS_T_BBB_MAX 9
+
+#define UMASS_T_CBI_RESET1 0 /* CBI */
+#define UMASS_T_CBI_RESET2 1
+#define UMASS_T_CBI_RESET3 2
+#define UMASS_T_CBI_COMMAND 3
+#define UMASS_T_CBI_DATA_READ 4
+#define UMASS_T_CBI_DATA_RD_CS 5
+#define UMASS_T_CBI_DATA_WRITE 6
+#define UMASS_T_CBI_DATA_WR_CS 7
+#define UMASS_T_CBI_STATUS 8
+#define UMASS_T_CBI_RESET4 9
+#define UMASS_T_CBI_MAX 10
+
+#define UMASS_T_MAX MAX(UMASS_T_CBI_MAX, UMASS_T_BBB_MAX)
+
+/* Generic definitions */
+
+/* Direction for transfer */
+#define DIR_NONE 0
+#define DIR_IN 1
+#define DIR_OUT 2
+
+/* device name */
+#define DEVNAME "umass"
+#define DEVNAME_SIM "umass-sim"
+
+/* Approximate maximum transfer speeds (assumes 33% overhead). */
+#define UMASS_FULL_TRANSFER_SPEED 1000
+#define UMASS_HIGH_TRANSFER_SPEED 40000
+#define UMASS_FLOPPY_TRANSFER_SPEED 20
+
+#define UMASS_TIMEOUT 5000 /* ms */
+
+/* CAM specific definitions */
+
+#define UMASS_SCSIID_MAX 1 /* maximum number of drives expected */
+#define UMASS_SCSIID_HOST UMASS_SCSIID_MAX
+
+/* Bulk-Only features */
+
+#define UR_BBB_RESET 0xff /* Bulk-Only reset */
+#define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */
+
+/* Command Block Wrapper */
+typedef struct {
+ 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 umass_bbb_cbw_t;
+
+#define UMASS_BBB_CBW_SIZE 31
+
+/* Command Status Wrapper */
+typedef struct {
+ uDWord dCSWSignature;
+#define CSWSIGNATURE 0x53425355
+#define CSWSIGNATURE_IMAGINATION_DBX1 0x43425355
+#define CSWSIGNATURE_OLYMPUS_C1 0x55425355
+ uDWord dCSWTag;
+ uDWord dCSWDataResidue;
+ uByte bCSWStatus;
+#define CSWSTATUS_GOOD 0x0
+#define CSWSTATUS_FAILED 0x1
+#define CSWSTATUS_PHASE 0x2
+} __packed umass_bbb_csw_t;
+
+#define UMASS_BBB_CSW_SIZE 13
+
+/* CBI features */
+
+#define UR_CBI_ADSC 0x00
+
+typedef union {
+ struct {
+ uint8_t type;
+#define IDB_TYPE_CCI 0x00
+ uint8_t value;
+#define IDB_VALUE_PASS 0x00
+#define IDB_VALUE_FAIL 0x01
+#define IDB_VALUE_PHASE 0x02
+#define IDB_VALUE_PERSISTENT 0x03
+#define IDB_VALUE_STATUS_MASK 0x03
+ } __packed common;
+
+ struct {
+ uint8_t asc;
+ uint8_t ascq;
+ } __packed ufi;
+} __packed umass_cbi_sbl_t;
+
+struct umass_softc; /* see below */
+
+typedef void (umass_callback_t)(struct umass_softc *sc, union ccb *ccb,
+ uint32_t residue, uint8_t status);
+
+#define STATUS_CMD_OK 0 /* everything ok */
+#define STATUS_CMD_UNKNOWN 1 /* will have to fetch sense */
+#define STATUS_CMD_FAILED 2 /* transfer was ok, command failed */
+#define STATUS_WIRE_FAILED 3 /* couldn't even get command across */
+
+typedef uint8_t (umass_transform_t)(struct umass_softc *sc, uint8_t *cmd_ptr,
+ uint8_t cmd_len);
+
+struct umass_devdescr {
+ uint32_t vid;
+#define VID_WILDCARD 0xffffffff
+#define VID_EOT 0xfffffffe
+ uint32_t pid;
+#define PID_WILDCARD 0xffffffff
+#define PID_EOT 0xfffffffe
+ uint32_t rid;
+#define RID_WILDCARD 0xffffffff
+#define RID_EOT 0xfffffffe
+
+ /* wire and command protocol */
+ uint16_t proto;
+#define UMASS_PROTO_BBB 0x0001 /* USB wire protocol */
+#define UMASS_PROTO_CBI 0x0002
+#define UMASS_PROTO_CBI_I 0x0004
+#define UMASS_PROTO_WIRE 0x00ff /* USB wire protocol mask */
+#define UMASS_PROTO_SCSI 0x0100 /* command protocol */
+#define UMASS_PROTO_ATAPI 0x0200
+#define UMASS_PROTO_UFI 0x0400
+#define UMASS_PROTO_RBC 0x0800
+#define UMASS_PROTO_COMMAND 0xff00 /* command protocol mask */
+
+ /* Device specific quirks */
+ uint16_t quirks;
+#define NO_QUIRKS 0x0000
+ /*
+ * The drive does not support Test Unit Ready. Convert to Start Unit
+ */
+#define NO_TEST_UNIT_READY 0x0001
+ /*
+ * The drive does not reset the Unit Attention state after REQUEST
+ * SENSE has been sent. The INQUIRY command does not reset the UA
+ * either, and so CAM runs in circles trying to retrieve the initial
+ * INQUIRY data.
+ */
+#define RS_NO_CLEAR_UA 0x0002
+ /* The drive does not support START STOP. */
+#define NO_START_STOP 0x0004
+ /* Don't ask for full inquiry data (255b). */
+#define FORCE_SHORT_INQUIRY 0x0008
+ /* Needs to be initialised the Shuttle way */
+#define SHUTTLE_INIT 0x0010
+ /* Drive needs to be switched to alternate iface 1 */
+#define ALT_IFACE_1 0x0020
+ /* Drive does not do 1Mb/s, but just floppy speeds (20kb/s) */
+#define FLOPPY_SPEED 0x0040
+ /* The device can't count and gets the residue of transfers wrong */
+#define IGNORE_RESIDUE 0x0080
+ /* No GetMaxLun call */
+#define NO_GETMAXLUN 0x0100
+ /* The device uses a weird CSWSIGNATURE. */
+#define WRONG_CSWSIG 0x0200
+ /* Device cannot handle INQUIRY so fake a generic response */
+#define NO_INQUIRY 0x0400
+ /* Device cannot handle INQUIRY EVPD, return CHECK CONDITION */
+#define NO_INQUIRY_EVPD 0x0800
+ /* Pad all RBC requests to 12 bytes. */
+#define RBC_PAD_TO_12 0x1000
+ /*
+ * Device reports number of sectors from READ_CAPACITY, not max
+ * sector number.
+ */
+#define READ_CAPACITY_OFFBY1 0x2000
+ /*
+ * Device cannot handle a SCSI synchronize cache command. Normally
+ * this quirk would be handled in the cam layer, but for IDE bridges
+ * we need to associate the quirk with the bridge and not the
+ * underlying disk device. This is handled by faking a success
+ * result.
+ */
+#define NO_SYNCHRONIZE_CACHE 0x4000
+};
+
+static const struct umass_devdescr umass_devdescr[] = {
+ {USB_VENDOR_ASAHIOPTICAL, PID_WILDCARD, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ RS_NO_CLEAR_UA
+ },
+ {USB_VENDOR_ADDON, USB_PRODUCT_ADDON_ATTACHE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_ADDON, USB_PRODUCT_ADDON_A256MB, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_ADDON, USB_PRODUCT_ADDON_DISKPRO512, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_ADDONICS2, USB_PRODUCT_ADDONICS2_CABLE_205, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_AIPTEK, USB_PRODUCT_AIPTEK_POCKETCAM3M, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_UMCR_9361, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_TRANSCEND, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO230, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO330, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_USB2SCSI, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_CASIO, USB_PRODUCT_CASIO_QV_DIGICAM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_CCYU, USB_PRODUCT_CCYU_ED1064, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_CENTURY, USB_PRODUCT_CENTURY_EX35QUAT, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_DESKNOTE, USB_PRODUCT_DESKNOTE_UCR_61S2B, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_DMI, USB_PRODUCT_DMI_CFSM_RW, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_EPSON, USB_PRODUCT_EPSON_STYLUS_875DC, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_EPSON, USB_PRODUCT_EPSON_STYLUS_895, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_FEIYA, USB_PRODUCT_FEIYA_5IN1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_FREECOM, USB_PRODUCT_FREECOM_DVD, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_FUJIPHOTO, USB_PRODUCT_FUJIPHOTO_MASS0100, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ RS_NO_CLEAR_UA
+ },
+ {USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB2IDE_2, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_GENESYS, USB_PRODUCT_GENESYS_GL641USB_2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ {USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FG, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_HAGIWARA, USB_PRODUCT_HAGIWARA_FGSM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_DZ_MV100A, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_HITACHI, USB_PRODUCT_HITACHI_DVDCAM_USB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_HP, USB_PRODUCT_HP_CDW4E, RID_WILDCARD,
+ UMASS_PROTO_ATAPI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_HP, USB_PRODUCT_HP_CDW8200, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_TEST_UNIT_READY | NO_START_STOP
+ },
+ {USB_VENDOR_IMAGINATION, USB_PRODUCT_IMAGINATION_DBX1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ {USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_USBCABLE, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_TEST_UNIT_READY | NO_START_STOP | ALT_IFACE_1
+ },
+ {USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_ATAPI, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_INSYSTEM, USB_PRODUCT_INSYSTEM_STORAGE_V2, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_IODATA, USB_PRODUCT_IODATA_IU_CD2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_IODATA, USB_PRODUCT_IODATA_DVR_UEH8, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_IOMEGA, USB_PRODUCT_IOMEGA_ZIP100, RID_WILDCARD,
+ /*
+ * XXX This is not correct as there are Zip drives that use
+ * ATAPI.
+ */
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_TEST_UNIT_READY
+ },
+ {USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_L3, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S3X, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S4, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_FINECAM_S5, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_LACIE, USB_PRODUCT_LACIE_HD, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_CF_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_LEXAR, USB_PRODUCT_LEXAR_JUMPSHOT, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443SU2, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_LOGITEC, USB_PRODUCT_LOGITEC_LDR_H443U2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MELCO, USB_PRODUCT_MELCO_DUBPXXG, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_DPCM, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_TEST_UNIT_READY | NO_START_STOP
+ },
+ {USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIDB25, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MICROTECH, USB_PRODUCT_MICROTECH_SCSIHD50, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_E223, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MINOLTA, USB_PRODUCT_MINOLTA_F300, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_CDRRW, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MITSUMI, USB_PRODUCT_MITSUMI_FDD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_MOTOROLA2, USB_PRODUCT_MOTOROLA2_E398, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_INQUIRY_EVPD | NO_GETMAXLUN
+ },
+ {USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE | NO_GETMAXLUN | RS_NO_CLEAR_UA
+ },
+ {USB_VENDOR_MSYSTEMS, USB_PRODUCT_MSYSTEMS_DISKONKEY2, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MYSON, USB_PRODUCT_MYSON_HEDEN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_MYSON, USB_PRODUCT_MYSON_STARREADER, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_SYNCHRONIZE_CACHE
+ },
+ {USB_VENDOR_NEODIO, USB_PRODUCT_NEODIO_ND3260, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY
+ },
+ {USB_VENDOR_NETAC, USB_PRODUCT_NETAC_CF_CARD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_NETAC, USB_PRODUCT_NETAC_ONLYDISK, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_NETCHIP, USB_PRODUCT_NETCHIP_CLIK_40, RID_WILDCARD,
+ UMASS_PROTO_ATAPI,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_NIKON, USB_PRODUCT_NIKON_D300, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ WRONG_CSWSIG
+ },
+ {USB_VENDOR_OLYMPUS, USB_PRODUCT_OLYMPUS_C700, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_SDS_HOTFIND_D, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN | NO_SYNCHRONIZE_CACHE
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFMS_RW, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_COMBO, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFSM_READER2, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MDCFE_B_CF_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_MDSM_B_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_READER, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_UCF100, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_INQUIRY | NO_GETMAXLUN
+ },
+ {USB_VENDOR_ONSPEC2, USB_PRODUCT_ONSPEC2_IMAGEMATE_SDDR55, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXL840AN, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB20AN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_KXLCB35AN, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_LS120CAM, RID_WILDCARD,
+ UMASS_PROTO_UFI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_PLEXTOR, USB_PRODUCT_PLEXTOR_40_12_40U, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_TEST_UNIT_READY
+ },
+ {USB_VENDOR_PNY, USB_PRODUCT_PNY_ATTACHE2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE | NO_START_STOP
+ },
+ {USB_VENDOR_SAMSUNG, USB_PRODUCT_SAMSUNG_YP_U2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ SHUTTLE_INIT | NO_GETMAXLUN
+ },
+ {USB_VENDOR_SAMSUNG_TECHWIN, USB_PRODUCT_SAMSUNG_TECHWIN_DIGIMAX_410, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR05A, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ READ_CAPACITY_OFFBY1 | NO_GETMAXLUN
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR09, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ READ_CAPACITY_OFFBY1 | NO_GETMAXLUN
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR12, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ READ_CAPACITY_OFFBY1 | NO_GETMAXLUN
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ2_256, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_128, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDCZ4_256, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_SANDISK, USB_PRODUCT_SANDISK_SDDR31, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ READ_CAPACITY_OFFBY1
+ },
+ {USB_VENDOR_SCANLOGIC, USB_PRODUCT_SCANLOGIC_SL11R, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ NO_TEST_UNIT_READY | NO_START_STOP | SHUTTLE_INIT
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CDRW, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_CF, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBATAPI, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSBCFSM, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_EUSCSI, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_HIFD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_SDDR09, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_SHUTTLE, USB_PRODUCT_SHUTTLE_ZIOMMC, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_I_BEAD100, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ SHUTTLE_INIT
+ },
+ {USB_VENDOR_SIIG, USB_PRODUCT_SIIG_WINTERREADER, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_SKANHEX, USB_PRODUCT_SKANHEX_MD_7425, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_SKANHEX, USB_PRODUCT_SKANHEX_SX_520Z, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_HANDYCAM, 0x0500,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ RBC_PAD_TO_12
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_CLIE_40_MS, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, 0x0500,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ RBC_PAD_TO_12
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, 0x0600,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ RBC_PAD_TO_12
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_DSC, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_HANDYCAM, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_MSC, RID_WILDCARD,
+ UMASS_PROTO_RBC | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_MSC_U03, RID_WILDCARD,
+ UMASS_PROTO_UFI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_NW_MS7, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_MS_PEG_N760C, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_MSACUS1, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_SONY, USB_PRODUCT_SONY_PORTABLE_HDD_V2, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_TAUGA, USB_PRODUCT_TAUGA_CAMERAMATE, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_TEAC, USB_PRODUCT_TEAC_FD05PUB, RID_WILDCARD,
+ UMASS_PROTO_UFI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_TREK, USB_PRODUCT_TREK_MEMKEY, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_TREK, USB_PRODUCT_TREK_THUMBDRIVE_8MB, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_BBB,
+ IGNORE_RESIDUE
+ },
+ {USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_C3310, RID_WILDCARD,
+ UMASS_PROTO_UFI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_MP3, RID_WILDCARD,
+ UMASS_PROTO_RBC,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_TRUMPION, USB_PRODUCT_TRUMPION_T33520, RID_WILDCARD,
+ UMASS_PROTO_SCSI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_MDIV, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_VIA, USB_PRODUCT_VIA_USB2IDEBRIDGE, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_SYNCHRONIZE_CACHE
+ },
+ {USB_VENDOR_VIVITAR, USB_PRODUCT_VIVITAR_35XX, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_COMBO, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_EXTHDD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_WESTERN, USB_PRODUCT_WESTERN_MYBOOK, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY_EVPD
+ },
+ {USB_VENDOR_WINMAXGROUP, USB_PRODUCT_WINMAXGROUP_FLASH64MC, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY
+ },
+ {USB_VENDOR_YANO, USB_PRODUCT_YANO_FW800HD, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ FORCE_SHORT_INQUIRY | NO_START_STOP | IGNORE_RESIDUE
+ },
+ {USB_VENDOR_YANO, USB_PRODUCT_YANO_U640MO, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
+ FORCE_SHORT_INQUIRY
+ },
+ {USB_VENDOR_YEDATA, USB_PRODUCT_YEDATA_FLASHBUSTERU, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_CBI,
+ NO_GETMAXLUN
+ },
+ {USB_VENDOR_ZORAN, USB_PRODUCT_ZORAN_EX20DSC, RID_WILDCARD,
+ UMASS_PROTO_ATAPI | UMASS_PROTO_CBI,
+ NO_QUIRKS
+ },
+ {USB_VENDOR_MEIZU, USB_PRODUCT_MEIZU_M6_SL, RID_WILDCARD,
+ UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+ NO_INQUIRY | NO_SYNCHRONIZE_CACHE
+ },
+ {VID_EOT, PID_EOT, RID_EOT, 0, 0}
+};
+
+struct umass_softc {
+
+ struct scsi_sense cam_scsi_sense;
+ struct scsi_test_unit_ready cam_scsi_test_unit_ready;
+ struct mtx sc_mtx;
+ struct {
+ uint8_t *data_ptr;
+ union ccb *ccb;
+ umass_callback_t *callback;
+
+ uint32_t data_len; /* bytes */
+ uint32_t data_rem; /* bytes */
+ uint32_t data_timeout; /* ms */
+ uint32_t actlen; /* bytes */
+
+ uint8_t cmd_data[UMASS_MAX_CMDLEN];
+ uint8_t cmd_len; /* bytes */
+ uint8_t dir;
+ uint8_t lun;
+ } sc_transfer;
+
+ /* Bulk specific variables for transfers in progress */
+ umass_bbb_cbw_t cbw; /* command block wrapper */
+ umass_bbb_csw_t csw; /* command status wrapper */
+
+ /* CBI specific variables for transfers in progress */
+ umass_cbi_sbl_t sbl; /* status block */
+
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct cam_sim *sc_sim; /* SCSI Interface Module */
+ struct usb2_xfer *sc_xfer[UMASS_T_MAX];
+
+ /*
+ * The command transform function is used to convert the SCSI
+ * commands into their derivatives, like UFI, ATAPI, and friends.
+ */
+ umass_transform_t *sc_transform;
+
+ uint32_t sc_unit;
+
+ uint16_t sc_proto; /* wire and cmd protocol */
+ uint16_t sc_quirks; /* they got it almost right */
+
+ uint8_t sc_name[16];
+ uint8_t sc_iface_no; /* interface number */
+ uint8_t sc_maxlun; /* maximum LUN number, inclusive */
+ uint8_t sc_last_xfer_index;
+ uint8_t sc_status_try;
+};
+
+struct umass_probe_proto {
+ uint16_t quirks;
+ uint16_t proto;
+
+ int32_t error;
+};
+
+/* prototypes */
+
+static device_probe_t umass_probe;
+static device_attach_t umass_attach;
+static device_detach_t umass_detach;
+
+static usb2_callback_t umass_tr_error;
+static usb2_callback_t umass_t_bbb_reset1_callback;
+static usb2_callback_t umass_t_bbb_reset2_callback;
+static usb2_callback_t umass_t_bbb_reset3_callback;
+static usb2_callback_t umass_t_bbb_command_callback;
+static usb2_callback_t umass_t_bbb_data_read_callback;
+static usb2_callback_t umass_t_bbb_data_rd_cs_callback;
+static usb2_callback_t umass_t_bbb_data_write_callback;
+static usb2_callback_t umass_t_bbb_data_wr_cs_callback;
+static usb2_callback_t umass_t_bbb_status_callback;
+static usb2_callback_t umass_t_cbi_reset1_callback;
+static usb2_callback_t umass_t_cbi_reset2_callback;
+static usb2_callback_t umass_t_cbi_reset3_callback;
+static usb2_callback_t umass_t_cbi_reset4_callback;
+static usb2_callback_t umass_t_cbi_command_callback;
+static usb2_callback_t umass_t_cbi_data_read_callback;
+static usb2_callback_t umass_t_cbi_data_rd_cs_callback;
+static usb2_callback_t umass_t_cbi_data_write_callback;
+static usb2_callback_t umass_t_cbi_data_wr_cs_callback;
+static usb2_callback_t umass_t_cbi_status_callback;
+
+static void umass_cancel_ccb(struct umass_softc *);
+static void umass_init_shuttle(struct umass_softc *);
+static void umass_reset(struct umass_softc *);
+static void umass_t_bbb_data_clear_stall_callback(struct usb2_xfer *,
+ uint8_t, uint8_t);
+static void umass_command_start(struct umass_softc *, uint8_t, void *,
+ uint32_t, uint32_t, umass_callback_t *, union ccb *);
+static uint8_t umass_bbb_get_max_lun(struct umass_softc *);
+static void umass_cbi_start_status(struct umass_softc *);
+static void umass_t_cbi_data_clear_stall_callback(struct usb2_xfer *,
+ uint8_t, uint8_t);
+static int umass_cam_attach_sim(struct umass_softc *);
+static void umass_cam_rescan_callback(struct cam_periph *, union ccb *);
+static void umass_cam_rescan(struct umass_softc *);
+static void umass_cam_attach(struct umass_softc *);
+static void umass_cam_detach_sim(struct umass_softc *);
+static void umass_cam_action(struct cam_sim *, union ccb *);
+static void umass_cam_poll(struct cam_sim *);
+static void umass_cam_cb(struct umass_softc *, union ccb *, uint32_t,
+ uint8_t);
+static void umass_cam_sense_cb(struct umass_softc *, union ccb *, uint32_t,
+ uint8_t);
+static void umass_cam_quirk_cb(struct umass_softc *, union ccb *, uint32_t,
+ uint8_t);
+static uint8_t umass_scsi_transform(struct umass_softc *, uint8_t *, uint8_t);
+static uint8_t umass_rbc_transform(struct umass_softc *, uint8_t *, uint8_t);
+static uint8_t umass_ufi_transform(struct umass_softc *, uint8_t *, uint8_t);
+static uint8_t umass_atapi_transform(struct umass_softc *, uint8_t *,
+ uint8_t);
+static uint8_t umass_no_transform(struct umass_softc *, uint8_t *, uint8_t);
+static uint8_t umass_std_transform(struct umass_softc *, union ccb *, uint8_t
+ *, uint8_t);
+
+#if USB_DEBUG
+static void umass_bbb_dump_cbw(struct umass_softc *, umass_bbb_cbw_t *);
+static void umass_bbb_dump_csw(struct umass_softc *, umass_bbb_csw_t *);
+static void umass_cbi_dump_cmd(struct umass_softc *, void *, uint8_t);
+static void umass_dump_buffer(struct umass_softc *, uint8_t *, uint32_t,
+ uint32_t);
+#endif
+
+struct usb2_config umass_bbb_config[UMASS_T_BBB_MAX] = {
+
+ [UMASS_T_BBB_RESET1] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umass_t_bbb_reset1_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 500, /* 500 milliseconds */
+ },
+
+ [UMASS_T_BBB_RESET2] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umass_t_bbb_reset2_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 50, /* 50 milliseconds */
+ },
+
+ [UMASS_T_BBB_RESET3] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umass_t_bbb_reset3_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 50, /* 50 milliseconds */
+ },
+
+ [UMASS_T_BBB_COMMAND] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = sizeof(umass_bbb_cbw_t),
+ .mh.flags = {},
+ .mh.callback = &umass_t_bbb_command_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [UMASS_T_BBB_DATA_READ] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UMASS_BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS},
+ .mh.callback = &umass_t_bbb_data_read_callback,
+ .mh.timeout = 0, /* overwritten later */
+ },
+
+ [UMASS_T_BBB_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 = &umass_t_bbb_data_rd_cs_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [UMASS_T_BBB_DATA_WRITE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UMASS_BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS},
+ .mh.callback = &umass_t_bbb_data_write_callback,
+ .mh.timeout = 0, /* overwritten later */
+ },
+
+ [UMASS_T_BBB_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 = &umass_t_bbb_data_wr_cs_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [UMASS_T_BBB_STATUS] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = sizeof(umass_bbb_csw_t),
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.callback = &umass_t_bbb_status_callback,
+ .mh.timeout = 5000, /* ms */
+ },
+};
+
+struct usb2_config umass_cbi_config[UMASS_T_CBI_MAX] = {
+
+ [UMASS_T_CBI_RESET1] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) +
+ UMASS_CBI_DIAGNOSTIC_CMDLEN),
+ .mh.flags = {},
+ .mh.callback = &umass_t_cbi_reset1_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 500, /* 500 milliseconds */
+ },
+
+ [UMASS_T_CBI_RESET2] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umass_t_cbi_reset2_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 50, /* 50 milliseconds */
+ },
+
+ [UMASS_T_CBI_RESET3] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umass_t_cbi_reset3_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ .mh.interval = 50, /* 50 milliseconds */
+ },
+
+ [UMASS_T_CBI_COMMAND] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = (sizeof(struct usb2_device_request) +
+ UMASS_MAX_CMDLEN),
+ .mh.flags = {},
+ .mh.callback = &umass_t_cbi_command_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [UMASS_T_CBI_DATA_READ] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = UMASS_BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS},
+ .mh.callback = &umass_t_cbi_data_read_callback,
+ .mh.timeout = 0, /* overwritten later */
+ },
+
+ [UMASS_T_CBI_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 = &umass_t_cbi_data_rd_cs_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [UMASS_T_CBI_DATA_WRITE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = UMASS_BULK_SIZE,
+ .mh.flags = {.proxy_buffer = 1,.short_xfer_ok = 1, UMASS_USB_FLAGS},
+ .mh.callback = &umass_t_cbi_data_write_callback,
+ .mh.timeout = 0, /* overwritten later */
+ },
+
+ [UMASS_T_CBI_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 = &umass_t_cbi_data_wr_cs_callback,
+ .mh.timeout = 5000, /* 5 seconds */
+ },
+
+ [UMASS_T_CBI_STATUS] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.flags = {.short_xfer_ok = 1,},
+ .mh.bufsize = sizeof(umass_cbi_sbl_t),
+ .mh.callback = &umass_t_cbi_status_callback,
+ .mh.timeout = 5000, /* ms */
+ },
+
+ [UMASS_T_CBI_RESET4] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &umass_t_cbi_reset4_callback,
+ .mh.timeout = 5000, /* ms */
+ },
+};
+
+/* If device cannot return valid inquiry data, fake it */
+static const uint8_t fake_inq_data[SHORT_INQUIRY_LENGTH] = {
+ 0, /* removable */ 0x80, SCSI_REV_2, SCSI_REV_2,
+ /* additional_length */ 31, 0, 0, 0
+};
+
+#define UFI_COMMAND_LENGTH 12 /* UFI commands are always 12 bytes */
+#define ATAPI_COMMAND_LENGTH 12 /* ATAPI commands are always 12 bytes */
+
+static devclass_t umass_devclass;
+
+static device_method_t umass_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, umass_probe),
+ DEVMETHOD(device_attach, umass_attach),
+ DEVMETHOD(device_detach, umass_detach),
+ {0, 0}
+};
+
+static driver_t umass_driver = {
+ .name = "umass",
+ .methods = umass_methods,
+ .size = sizeof(struct umass_softc),
+};
+
+DRIVER_MODULE(umass, ushub, umass_driver, umass_devclass, NULL, 0);
+MODULE_DEPEND(umass, usb, 1, 1, 1);
+MODULE_DEPEND(umass, cam, 1, 1, 1);
+
+/*
+ * USB device probe/attach/detach
+ */
+
+/*
+ * Match the device we are seeing with the
+ * devices supported.
+ */
+static struct umass_probe_proto
+umass_probe_proto(device_t dev, struct usb2_attach_arg *uaa)
+{
+ const struct umass_devdescr *udd = umass_devdescr;
+ struct usb2_interface_descriptor *id;
+ struct umass_probe_proto ret;
+
+ bzero(&ret, sizeof(ret));
+
+ /*
+ * An entry specifically for Y-E Data devices as they don't fit in
+ * the device description table.
+ */
+ if ((uaa->info.idVendor == USB_VENDOR_YEDATA) &&
+ (uaa->info.idProduct == USB_PRODUCT_YEDATA_FLASHBUSTERU)) {
+
+ /*
+ * Revisions < 1.28 do not handle the interrupt endpoint
+ * very well.
+ */
+ if (uaa->info.bcdDevice < 0x128) {
+ ret.proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI;
+ } else {
+ ret.proto = UMASS_PROTO_UFI | UMASS_PROTO_CBI_I;
+ }
+
+ /*
+ * Revisions < 1.28 do not have the TEST UNIT READY command
+ * Revisions == 1.28 have a broken TEST UNIT READY
+ */
+ if (uaa->info.bcdDevice <= 0x128) {
+ ret.quirks |= NO_TEST_UNIT_READY;
+ }
+ ret.quirks |= RS_NO_CLEAR_UA | FLOPPY_SPEED;
+ ret.error = 0;
+ goto done;
+ }
+ /*
+ * Check the list of supported devices for a match. While looking,
+ * check for wildcarded and fully matched. First match wins.
+ */
+ for (; udd->vid != VID_EOT; udd++) {
+ if ((udd->vid == VID_WILDCARD) &&
+ (udd->pid == PID_WILDCARD) &&
+ (udd->rid == RID_WILDCARD)) {
+ device_printf(dev, "ignoring invalid "
+ "wildcard quirk\n");
+ continue;
+ }
+ if (((udd->vid == uaa->info.idVendor) ||
+ (udd->vid == VID_WILDCARD)) &&
+ ((udd->pid == uaa->info.idProduct) ||
+ (udd->pid == PID_WILDCARD))) {
+ if (udd->rid == RID_WILDCARD) {
+ ret.proto = udd->proto;
+ ret.quirks = udd->quirks;
+ ret.error = 0;
+ goto done;
+ } else if (udd->rid == uaa->info.bcdDevice) {
+ ret.proto = udd->proto;
+ ret.quirks = udd->quirks;
+ ret.error = 0;
+ goto done;
+ } /* else RID does not match */
+ }
+ }
+
+ /* Check for a standards compliant device */
+ id = usb2_get_interface_descriptor(uaa->iface);
+ if ((id == NULL) ||
+ (id->bInterfaceClass != UICLASS_MASS)) {
+ ret.error = ENXIO;
+ goto done;
+ }
+ switch (id->bInterfaceSubClass) {
+ case UISUBCLASS_SCSI:
+ ret.proto |= UMASS_PROTO_SCSI;
+ break;
+ case UISUBCLASS_UFI:
+ ret.proto |= UMASS_PROTO_UFI;
+ break;
+ case UISUBCLASS_RBC:
+ ret.proto |= UMASS_PROTO_RBC;
+ break;
+ case UISUBCLASS_SFF8020I:
+ case UISUBCLASS_SFF8070I:
+ ret.proto |= UMASS_PROTO_ATAPI;
+ break;
+ default:
+ device_printf(dev, "unsupported command "
+ "protocol %d\n", id->bInterfaceSubClass);
+ ret.error = ENXIO;
+ goto done;
+ }
+
+ switch (id->bInterfaceProtocol) {
+ case UIPROTO_MASS_CBI:
+ ret.proto |= UMASS_PROTO_CBI;
+ break;
+ case UIPROTO_MASS_CBI_I:
+ ret.proto |= UMASS_PROTO_CBI_I;
+ break;
+ case UIPROTO_MASS_BBB_OLD:
+ case UIPROTO_MASS_BBB:
+ ret.proto |= UMASS_PROTO_BBB;
+ break;
+ default:
+ device_printf(dev, "unsupported wire "
+ "protocol %d\n", id->bInterfaceProtocol);
+ ret.error = ENXIO;
+ goto done;
+ }
+
+ ret.error = 0;
+done:
+ return (ret);
+}
+
+static int
+umass_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umass_probe_proto temp;
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if (uaa->use_generic == 0) {
+ /* give other drivers a try first */
+ return (ENXIO);
+ }
+ temp = umass_probe_proto(dev, uaa);
+
+ return (temp.error);
+}
+
+static int
+umass_attach(device_t dev)
+{
+ struct umass_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct umass_probe_proto temp = umass_probe_proto(dev, uaa);
+ struct usb2_interface_descriptor *id;
+ int32_t err;
+
+ /*
+ * NOTE: the softc struct is bzero-ed in device_set_driver.
+ * We can safely call umass_detach without specifically
+ * initializing the struct.
+ */
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+ sc->sc_proto = temp.proto;
+ sc->sc_quirks = temp.quirks;
+ sc->sc_unit = device_get_unit(dev);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev),
+ NULL, MTX_DEF | MTX_RECURSE);
+
+ /* get interface index */
+
+ id = usb2_get_interface_descriptor(uaa->iface);
+ if (id == NULL) {
+ device_printf(dev, "failed to get "
+ "interface number\n");
+ goto detach;
+ }
+ sc->sc_iface_no = id->bInterfaceNumber;
+
+#if USB_DEBUG
+ device_printf(dev, " ");
+
+ switch (sc->sc_proto & UMASS_PROTO_COMMAND) {
+ case UMASS_PROTO_SCSI:
+ printf("SCSI");
+ break;
+ case UMASS_PROTO_ATAPI:
+ printf("8070i (ATAPI)");
+ break;
+ case UMASS_PROTO_UFI:
+ printf("UFI");
+ break;
+ case UMASS_PROTO_RBC:
+ printf("RBC");
+ break;
+ default:
+ printf("(unknown 0x%02x)",
+ sc->sc_proto & UMASS_PROTO_COMMAND);
+ break;
+ }
+
+ printf(" over ");
+
+ switch (sc->sc_proto & UMASS_PROTO_WIRE) {
+ case UMASS_PROTO_BBB:
+ printf("Bulk-Only");
+ break;
+ case UMASS_PROTO_CBI: /* uses Comand/Bulk pipes */
+ printf("CBI");
+ break;
+ case UMASS_PROTO_CBI_I: /* uses Comand/Bulk/Interrupt pipes */
+ printf("CBI with CCI");
+ break;
+ default:
+ printf("(unknown 0x%02x)",
+ sc->sc_proto & UMASS_PROTO_WIRE);
+ }
+
+ printf("; quirks = 0x%04x\n", sc->sc_quirks);
+#endif
+
+ if (sc->sc_quirks & ALT_IFACE_1) {
+ err = usb2_set_alt_interface_index
+ (uaa->device, uaa->info.bIfaceIndex, 1);
+
+ if (err) {
+ DPRINTF(sc, UDMASS_USB, "could not switch to "
+ "Alt Interface 1\n");
+ goto detach;
+ }
+ }
+ /* allocate all required USB transfers */
+
+ if (sc->sc_proto & UMASS_PROTO_BBB) {
+
+ err = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, umass_bbb_config,
+ UMASS_T_BBB_MAX, sc, &sc->sc_mtx);
+
+ /* skip reset first time */
+ sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;
+
+ } else if (sc->sc_proto & (UMASS_PROTO_CBI | UMASS_PROTO_CBI_I)) {
+
+ err = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, umass_cbi_config,
+ (sc->sc_proto & UMASS_PROTO_CBI_I) ?
+ UMASS_T_CBI_MAX : (UMASS_T_CBI_MAX - 2), sc,
+ &sc->sc_mtx);
+
+ /* skip reset first time */
+ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
+
+ } else {
+ err = USB_ERR_INVAL;
+ }
+
+ if (err) {
+ device_printf(dev, "could not setup required "
+ "transfers, %s\n", usb2_errstr(err));
+ goto detach;
+ }
+ sc->sc_transform =
+ (sc->sc_proto & UMASS_PROTO_SCSI) ? &umass_scsi_transform :
+ (sc->sc_proto & UMASS_PROTO_UFI) ? &umass_ufi_transform :
+ (sc->sc_proto & UMASS_PROTO_ATAPI) ? &umass_atapi_transform :
+ (sc->sc_proto & UMASS_PROTO_RBC) ? &umass_rbc_transform :
+ &umass_no_transform;
+
+ /* from here onwards the device can be used. */
+
+ if (sc->sc_quirks & SHUTTLE_INIT) {
+ umass_init_shuttle(sc);
+ }
+ /* get the maximum LUN supported by the device */
+
+ if (((sc->sc_proto & UMASS_PROTO_WIRE) == UMASS_PROTO_BBB) &&
+ !(sc->sc_quirks & NO_GETMAXLUN))
+ sc->sc_maxlun = umass_bbb_get_max_lun(sc);
+ else
+ sc->sc_maxlun = 0;
+
+ /* Prepare the SCSI command block */
+ sc->cam_scsi_sense.opcode = REQUEST_SENSE;
+ sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY;
+
+ /*
+ * some devices need a delay after that the configuration value is
+ * set to function properly:
+ */
+ usb2_pause_mtx(&Giant, hz);
+
+ /* register the SIM */
+ err = umass_cam_attach_sim(sc);
+ if (err) {
+ goto detach;
+ }
+ /* scan the SIM */
+ umass_cam_attach(sc);
+
+ DPRINTF(sc, UDMASS_GEN, "Attach finished\n");
+
+ return (0); /* success */
+
+detach:
+ umass_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+umass_detach(device_t dev)
+{
+ struct umass_softc *sc = device_get_softc(dev);
+
+ DPRINTF(sc, UDMASS_USB, "\n");
+
+ /* teardown our statemachine */
+
+ usb2_transfer_unsetup(sc->sc_xfer, UMASS_T_MAX);
+
+#if (__FreeBSD_version >= 700037)
+ mtx_lock(&sc->sc_mtx);
+#endif
+ umass_cam_detach_sim(sc);
+
+#if (__FreeBSD_version >= 700037)
+ mtx_unlock(&sc->sc_mtx);
+#endif
+
+ return (0); /* success */
+}
+
+static void
+umass_init_shuttle(struct umass_softc *sc)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t status[2] = {0, 0};
+
+ /*
+ * The Linux driver does this, but no one can tell us what the
+ * command does.
+ */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = 1; /* XXX unknown command */
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, sizeof(status));
+ err = usb2_do_request(sc->sc_udev, &Giant, &req, &status);
+
+ DPRINTF(sc, UDMASS_GEN, "Shuttle init returned 0x%02x%02x\n",
+ status[0], status[1]);
+}
+
+/*
+ * Generic functions to handle transfers
+ */
+
+static void
+umass_transfer_start(struct umass_softc *sc, uint8_t xfer_index)
+{
+ DPRINTF(sc, UDMASS_GEN, "transfer index = "
+ "%d\n", xfer_index);
+
+ if (sc->sc_xfer[xfer_index]) {
+ sc->sc_last_xfer_index = xfer_index;
+ usb2_transfer_start(sc->sc_xfer[xfer_index]);
+ } else {
+ umass_cancel_ccb(sc);
+ }
+}
+
+static void
+umass_reset(struct umass_softc *sc)
+{
+ DPRINTF(sc, UDMASS_GEN, "resetting device\n");
+
+ /*
+ * stop the last transfer, if not already stopped:
+ */
+ usb2_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]);
+ umass_transfer_start(sc, 0);
+}
+
+static void
+umass_cancel_ccb(struct umass_softc *sc)
+{
+ union ccb *ccb;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ ccb = sc->sc_transfer.ccb;
+ sc->sc_transfer.ccb = NULL;
+ sc->sc_last_xfer_index = 0;
+
+ if (ccb) {
+ (sc->sc_transfer.callback)
+ (sc, ccb, (sc->sc_transfer.data_len -
+ sc->sc_transfer.actlen), STATUS_WIRE_FAILED);
+ }
+}
+
+static void
+umass_tr_error(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+
+ DPRINTF(sc, UDMASS_GEN, "transfer error, %s -> "
+ "reset\n", usb2_errstr(xfer->error));
+ }
+ umass_cancel_ccb(sc);
+}
+
+/*
+ * BBB protocol specific functions
+ */
+
+static void
+umass_t_bbb_reset1_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ umass_transfer_start(sc, UMASS_T_BBB_RESET2);
+ return;
+
+ case USB_ST_SETUP:
+ /*
+ * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class)
+ *
+ * For Reset Recovery the host shall issue in the following order:
+ * a) a Bulk-Only Mass Storage Reset
+ * b) a Clear Feature HALT to the Bulk-In endpoint
+ * c) a Clear Feature HALT to the Bulk-Out endpoint
+ *
+ * This is done in 3 steps, using 3 transfers:
+ * UMASS_T_BBB_RESET1
+ * UMASS_T_BBB_RESET2
+ * UMASS_T_BBB_RESET3
+ */
+
+ DPRINTF(sc, UDMASS_BBB, "BBB reset!\n");
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_BBB_RESET; /* bulk only reset */
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->nframes = 1;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+umass_t_bbb_reset2_callback(struct usb2_xfer *xfer)
+{
+ umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_RESET3,
+ UMASS_T_BBB_DATA_READ);
+}
+
+static void
+umass_t_bbb_reset3_callback(struct usb2_xfer *xfer)
+{
+ umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_COMMAND,
+ UMASS_T_BBB_DATA_WRITE);
+}
+
+static void
+umass_t_bbb_data_clear_stall_callback(struct usb2_xfer *xfer,
+ uint8_t next_xfer,
+ uint8_t stall_xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ umass_transfer_start(sc, next_xfer);
+ return;
+
+ case USB_ST_SETUP:
+ if (usb2_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) {
+ goto tr_transferred;
+ }
+ return;
+
+ default: /* Error */
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+umass_t_bbb_command_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ union ccb *ccb = sc->sc_transfer.ccb;
+ uint32_t tag;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ umass_transfer_start
+ (sc, ((sc->sc_transfer.dir == DIR_IN) ? UMASS_T_BBB_DATA_READ :
+ (sc->sc_transfer.dir == DIR_OUT) ? UMASS_T_BBB_DATA_WRITE :
+ UMASS_T_BBB_STATUS));
+ return;
+
+ case USB_ST_SETUP:
+
+ sc->sc_status_try = 0;
+
+ if (ccb) {
+
+ /*
+ * the initial value is not important,
+ * as long as the values are unique:
+ */
+ tag = UGETDW(sc->cbw.dCBWTag) + 1;
+
+ USETDW(sc->cbw.dCBWSignature, CBWSIGNATURE);
+ USETDW(sc->cbw.dCBWTag, tag);
+
+ /*
+ * dCBWDataTransferLength:
+ * This field indicates the number of bytes of data that the host
+ * intends to transfer on the IN or OUT Bulk endpoint(as indicated by
+ * the Direction bit) during the execution of this command. If this
+ * field is set to 0, the device will expect that no data will be
+ * transferred IN or OUT during this command, regardless of the value
+ * of the Direction bit defined in dCBWFlags.
+ */
+ USETDW(sc->cbw.dCBWDataTransferLength, sc->sc_transfer.data_len);
+
+ /*
+ * dCBWFlags:
+ * The bits of the Flags field are defined as follows:
+ * Bits 0-6 reserved
+ * Bit 7 Direction - this bit shall be ignored if the
+ * dCBWDataTransferLength field is zero.
+ * 0 = data Out from host to device
+ * 1 = data In from device to host
+ */
+ sc->cbw.bCBWFlags = ((sc->sc_transfer.dir == DIR_IN) ?
+ CBWFLAGS_IN : CBWFLAGS_OUT);
+ sc->cbw.bCBWLUN = sc->sc_transfer.lun;
+
+ if (sc->sc_transfer.cmd_len > sizeof(sc->cbw.CBWCDB)) {
+ sc->sc_transfer.cmd_len = sizeof(sc->cbw.CBWCDB);
+ DPRINTF(sc, UDMASS_BBB, "Truncating long command!\n");
+ }
+ sc->cbw.bCDBLength = sc->sc_transfer.cmd_len;
+
+ bcopy(sc->sc_transfer.cmd_data, sc->cbw.CBWCDB,
+ sc->sc_transfer.cmd_len);
+
+ bzero(sc->sc_transfer.cmd_data + sc->sc_transfer.cmd_len,
+ sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len);
+
+ DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw));
+
+ usb2_copy_in(xfer->frbuffers, 0, &sc->cbw, sizeof(sc->cbw));
+
+ xfer->frlengths[0] = sizeof(sc->cbw);
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+umass_t_bbb_data_read_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (!xfer->flags.ext_buffer) {
+ usb2_copy_out(xfer->frbuffers, 0,
+ sc->sc_transfer.data_ptr, xfer->actlen);
+ }
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.data_ptr += xfer->actlen;
+ sc->sc_transfer.actlen += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->sc_transfer.data_rem = 0;
+ }
+ case USB_ST_SETUP:
+ DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n",
+ max_bulk, sc->sc_transfer.data_rem);
+
+ if (sc->sc_transfer.data_rem == 0) {
+ umass_transfer_start(sc, UMASS_T_BBB_STATUS);
+ return;
+ }
+ if (max_bulk > sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ }
+ xfer->timeout = sc->sc_transfer.data_timeout;
+ xfer->frlengths[0] = max_bulk;
+
+ if (xfer->flags.ext_buffer) {
+ usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0);
+ }
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ umass_tr_error(xfer);
+ } else {
+ umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+umass_t_bbb_data_rd_cs_callback(struct usb2_xfer *xfer)
+{
+ umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS,
+ UMASS_T_BBB_DATA_READ);
+}
+
+static void
+umass_t_bbb_data_write_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.data_ptr += xfer->actlen;
+ sc->sc_transfer.actlen += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->sc_transfer.data_rem = 0;
+ }
+ case USB_ST_SETUP:
+ DPRINTF(sc, UDMASS_BBB, "max_bulk=%d, data_rem=%d\n",
+ max_bulk, sc->sc_transfer.data_rem);
+
+ if (sc->sc_transfer.data_rem == 0) {
+ umass_transfer_start(sc, UMASS_T_BBB_STATUS);
+ return;
+ }
+ if (max_bulk > sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ }
+ xfer->timeout = sc->sc_transfer.data_timeout;
+ xfer->frlengths[0] = max_bulk;
+
+ if (xfer->flags.ext_buffer) {
+ usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0);
+ } else {
+ usb2_copy_in(xfer->frbuffers, 0,
+ sc->sc_transfer.data_ptr, max_bulk);
+ }
+
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ umass_tr_error(xfer);
+ } else {
+ umass_transfer_start(sc, UMASS_T_BBB_DATA_WR_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+umass_t_bbb_data_wr_cs_callback(struct usb2_xfer *xfer)
+{
+ umass_t_bbb_data_clear_stall_callback(xfer, UMASS_T_BBB_STATUS,
+ UMASS_T_BBB_DATA_WRITE);
+}
+
+static void
+umass_t_bbb_status_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ union ccb *ccb = sc->sc_transfer.ccb;
+ uint32_t residue;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ /*
+ * Do a full reset if there is something wrong with the CSW:
+ */
+ sc->sc_status_try = 1;
+
+ /* Zero missing parts of the CSW: */
+
+ if (xfer->actlen < sizeof(sc->csw)) {
+ bzero(&sc->csw, sizeof(sc->csw));
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &sc->csw, xfer->actlen);
+
+ DIF(UDMASS_BBB, umass_bbb_dump_csw(sc, &sc->csw));
+
+ residue = UGETDW(sc->csw.dCSWDataResidue);
+
+ if (!residue) {
+ residue = (sc->sc_transfer.data_len -
+ sc->sc_transfer.actlen);
+ }
+ if (residue > sc->sc_transfer.data_len) {
+ DPRINTF(sc, UDMASS_BBB, "truncating residue from %d "
+ "to %d bytes\n", residue, sc->sc_transfer.data_len);
+ residue = sc->sc_transfer.data_len;
+ }
+ /* translate weird command-status signatures: */
+ if (sc->sc_quirks & WRONG_CSWSIG) {
+
+ uint32_t temp = UGETDW(sc->csw.dCSWSignature);
+
+ if ((temp == CSWSIGNATURE_OLYMPUS_C1) ||
+ (temp == CSWSIGNATURE_IMAGINATION_DBX1)) {
+ USETDW(sc->csw.dCSWSignature, CSWSIGNATURE);
+ }
+ }
+ /* check CSW and handle eventual error */
+ if (UGETDW(sc->csw.dCSWSignature) != CSWSIGNATURE) {
+ DPRINTF(sc, UDMASS_BBB, "bad CSW signature 0x%08x != 0x%08x\n",
+ UGETDW(sc->csw.dCSWSignature), CSWSIGNATURE);
+ /*
+ * Invalid CSW: Wrong signature or wrong tag might
+ * indicate that we lost synchronization. Reset the
+ * device.
+ */
+ goto tr_error;
+ } else if (UGETDW(sc->csw.dCSWTag) != UGETDW(sc->cbw.dCBWTag)) {
+ DPRINTF(sc, UDMASS_BBB, "Invalid CSW: tag 0x%08x should be "
+ "0x%08x\n", UGETDW(sc->csw.dCSWTag),
+ UGETDW(sc->cbw.dCBWTag));
+ goto tr_error;
+ } else if (sc->csw.bCSWStatus > CSWSTATUS_PHASE) {
+ DPRINTF(sc, UDMASS_BBB, "Invalid CSW: status %d > %d\n",
+ sc->csw.bCSWStatus, CSWSTATUS_PHASE);
+ goto tr_error;
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_PHASE) {
+ DPRINTF(sc, UDMASS_BBB, "Phase error, residue = "
+ "%d\n", residue);
+ goto tr_error;
+ } else if (sc->sc_transfer.actlen > sc->sc_transfer.data_len) {
+ DPRINTF(sc, UDMASS_BBB, "Buffer overrun %d > %d\n",
+ sc->sc_transfer.actlen, sc->sc_transfer.data_len);
+ goto tr_error;
+ } else if (sc->csw.bCSWStatus == CSWSTATUS_FAILED) {
+ DPRINTF(sc, UDMASS_BBB, "Command failed, residue = "
+ "%d\n", residue);
+
+ sc->sc_transfer.ccb = NULL;
+
+ sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;
+
+ (sc->sc_transfer.callback)
+ (sc, ccb, residue, STATUS_CMD_FAILED);
+ } else {
+ sc->sc_transfer.ccb = NULL;
+
+ sc->sc_last_xfer_index = UMASS_T_BBB_COMMAND;
+
+ (sc->sc_transfer.callback)
+ (sc, ccb, residue, STATUS_CMD_OK);
+ }
+ return;
+
+ case USB_ST_SETUP:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default:
+tr_error:
+ DPRINTF(sc, UDMASS_BBB, "Failed to read CSW: %s, try %d\n",
+ usb2_errstr(xfer->error), sc->sc_status_try);
+
+ if ((xfer->error == USB_ERR_CANCELLED) ||
+ (sc->sc_status_try)) {
+ umass_tr_error(xfer);
+ } else {
+ sc->sc_status_try = 1;
+ umass_transfer_start(sc, UMASS_T_BBB_DATA_RD_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+umass_command_start(struct umass_softc *sc, uint8_t dir,
+ void *data_ptr, uint32_t data_len,
+ uint32_t data_timeout, umass_callback_t *callback,
+ union ccb *ccb)
+{
+ sc->sc_transfer.lun = ccb->ccb_h.target_lun;
+
+ /*
+ * NOTE: assumes that "sc->sc_transfer.cmd_data" and
+ * "sc->sc_transfer.cmd_len" has been properly
+ * initialized.
+ */
+
+ sc->sc_transfer.dir = data_len ? dir : DIR_NONE;
+ sc->sc_transfer.data_ptr = data_ptr;
+ sc->sc_transfer.data_len = data_len;
+ sc->sc_transfer.data_rem = data_len;
+ sc->sc_transfer.data_timeout = (data_timeout + UMASS_TIMEOUT);
+
+ sc->sc_transfer.actlen = 0;
+ sc->sc_transfer.callback = callback;
+ sc->sc_transfer.ccb = ccb;
+
+ if (sc->sc_xfer[sc->sc_last_xfer_index]) {
+ usb2_transfer_start(sc->sc_xfer[sc->sc_last_xfer_index]);
+ } else {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ }
+}
+
+static uint8_t
+umass_bbb_get_max_lun(struct umass_softc *sc)
+{
+ struct usb2_device_request req;
+ usb2_error_t err;
+ uint8_t buf = 0;
+
+ /* The Get Max Lun command is a class-specific request. */
+ req.bmRequestType = UT_READ_CLASS_INTERFACE;
+ req.bRequest = UR_BBB_GET_MAX_LUN;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 1);
+
+ err = usb2_do_request(sc->sc_udev, &Giant, &req, &buf);
+ if (err) {
+ buf = 0;
+
+ /* Device doesn't support Get Max Lun request. */
+ printf("%s: Get Max Lun not supported (%s)\n",
+ sc->sc_name, usb2_errstr(err));
+ }
+ return (buf);
+}
+
+/*
+ * Command/Bulk/Interrupt (CBI) specific functions
+ */
+
+static void
+umass_cbi_start_status(struct umass_softc *sc)
+{
+ if (sc->sc_xfer[UMASS_T_CBI_STATUS]) {
+ umass_transfer_start(sc, UMASS_T_CBI_STATUS);
+ } else {
+ union ccb *ccb = sc->sc_transfer.ccb;
+
+ sc->sc_transfer.ccb = NULL;
+
+ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
+
+ (sc->sc_transfer.callback)
+ (sc, ccb, (sc->sc_transfer.data_len -
+ sc->sc_transfer.actlen), STATUS_CMD_UNKNOWN);
+ }
+}
+
+static void
+umass_t_cbi_reset1_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ struct usb2_device_request req;
+ uint8_t buf[UMASS_CBI_DIAGNOSTIC_CMDLEN];
+
+ uint8_t i;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ umass_transfer_start(sc, UMASS_T_CBI_RESET2);
+ return;
+
+ case USB_ST_SETUP:
+ /*
+ * Command Block Reset Protocol
+ *
+ * First send a reset request to the device. Then clear
+ * any possibly stalled bulk endpoints.
+ *
+ * This is done in 3 steps, using 3 transfers:
+ * UMASS_T_CBI_RESET1
+ * UMASS_T_CBI_RESET2
+ * UMASS_T_CBI_RESET3
+ * UMASS_T_CBI_RESET4 (only if there is an interrupt endpoint)
+ */
+
+ DPRINTF(sc, UDMASS_CBI, "CBI reset!\n");
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_CBI_ADSC;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, UMASS_CBI_DIAGNOSTIC_CMDLEN);
+
+ /*
+ * The 0x1d code is the SEND DIAGNOSTIC command. To
+ * distinguish between the two, the last 10 bytes of the CBL
+ * is filled with 0xff (section 2.2 of the CBI
+ * specification)
+ */
+ buf[0] = 0x1d; /* Command Block Reset */
+ buf[1] = 0x04;
+
+ for (i = 2; i < UMASS_CBI_DIAGNOSTIC_CMDLEN; i++) {
+ buf[i] = 0xff;
+ }
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_copy_in(xfer->frbuffers + 1, 0, buf, sizeof(buf));
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = sizeof(buf);
+ xfer->nframes = 2;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+umass_t_cbi_reset2_callback(struct usb2_xfer *xfer)
+{
+ umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_RESET3,
+ UMASS_T_CBI_DATA_READ);
+}
+
+static void
+umass_t_cbi_reset3_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+
+ umass_t_cbi_data_clear_stall_callback
+ (xfer, (sc->sc_xfer[UMASS_T_CBI_RESET4] &&
+ sc->sc_xfer[UMASS_T_CBI_STATUS]) ?
+ UMASS_T_CBI_RESET4 : UMASS_T_CBI_COMMAND,
+ UMASS_T_CBI_DATA_WRITE);
+}
+
+static void
+umass_t_cbi_reset4_callback(struct usb2_xfer *xfer)
+{
+ umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_COMMAND,
+ UMASS_T_CBI_STATUS);
+}
+
+static void
+umass_t_cbi_data_clear_stall_callback(struct usb2_xfer *xfer,
+ uint8_t next_xfer,
+ uint8_t stall_xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+tr_transferred:
+ if (next_xfer == UMASS_T_CBI_STATUS) {
+ umass_cbi_start_status(sc);
+ } else {
+ umass_transfer_start(sc, next_xfer);
+ }
+ return;
+
+ case USB_ST_SETUP:
+ if (usb2_clear_stall_callback(xfer, sc->sc_xfer[stall_xfer])) {
+ goto tr_transferred; /* should not happen */
+ }
+ return;
+
+ default: /* Error */
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+umass_t_cbi_command_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ union ccb *ccb = sc->sc_transfer.ccb;
+ struct usb2_device_request req;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (sc->sc_transfer.dir == DIR_NONE) {
+ umass_cbi_start_status(sc);
+ } else {
+ umass_transfer_start
+ (sc, (sc->sc_transfer.dir == DIR_IN) ?
+ UMASS_T_CBI_DATA_READ : UMASS_T_CBI_DATA_WRITE);
+ }
+ return;
+
+ case USB_ST_SETUP:
+
+ if (ccb) {
+
+ /*
+ * do a CBI transfer with cmd_len bytes from
+ * cmd_data, possibly a data phase of data_len
+ * bytes from/to the device and finally a status
+ * read phase.
+ */
+
+ req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+ req.bRequest = UR_CBI_ADSC;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = sc->sc_iface_no;
+ req.wIndex[1] = 0;
+ req.wLength[0] = sc->sc_transfer.cmd_len;
+ req.wLength[1] = 0;
+
+ usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
+ usb2_copy_in(xfer->frbuffers + 1, 0, sc->sc_transfer.cmd_data,
+ sc->sc_transfer.cmd_len);
+
+ xfer->frlengths[0] = sizeof(req);
+ xfer->frlengths[1] = sc->sc_transfer.cmd_len;
+ xfer->nframes = xfer->frlengths[1] ? 2 : 1;
+
+ DIF(UDMASS_CBI,
+ umass_cbi_dump_cmd(sc,
+ sc->sc_transfer.cmd_data,
+ sc->sc_transfer.cmd_len));
+
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+static void
+umass_t_cbi_data_read_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ if (!xfer->flags.ext_buffer) {
+ usb2_copy_out(xfer->frbuffers, 0,
+ sc->sc_transfer.data_ptr, xfer->actlen);
+ }
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.data_ptr += xfer->actlen;
+ sc->sc_transfer.actlen += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->sc_transfer.data_rem = 0;
+ }
+ case USB_ST_SETUP:
+ DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n",
+ max_bulk, sc->sc_transfer.data_rem);
+
+ if (sc->sc_transfer.data_rem == 0) {
+ umass_cbi_start_status(sc);
+ return;
+ }
+ if (max_bulk > sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ }
+ xfer->timeout = sc->sc_transfer.data_timeout;
+
+ if (xfer->flags.ext_buffer) {
+ usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0);
+ }
+ xfer->frlengths[0] = max_bulk;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if ((xfer->error == USB_ERR_CANCELLED) ||
+ (sc->sc_transfer.callback != &umass_cam_cb)) {
+ umass_tr_error(xfer);
+ } else {
+ umass_transfer_start(sc, UMASS_T_CBI_DATA_RD_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+umass_t_cbi_data_rd_cs_callback(struct usb2_xfer *xfer)
+{
+ umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS,
+ UMASS_T_CBI_DATA_READ);
+}
+
+static void
+umass_t_cbi_data_write_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.data_ptr += xfer->actlen;
+ sc->sc_transfer.actlen += xfer->actlen;
+
+ if (xfer->actlen < xfer->sumlen) {
+ /* short transfer */
+ sc->sc_transfer.data_rem = 0;
+ }
+ case USB_ST_SETUP:
+ DPRINTF(sc, UDMASS_CBI, "max_bulk=%d, data_rem=%d\n",
+ max_bulk, sc->sc_transfer.data_rem);
+
+ if (sc->sc_transfer.data_rem == 0) {
+ umass_cbi_start_status(sc);
+ return;
+ }
+ if (max_bulk > sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ }
+ xfer->timeout = sc->sc_transfer.data_timeout;
+
+ if (xfer->flags.ext_buffer) {
+ usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0);
+ } else {
+ usb2_copy_in(xfer->frbuffers, 0,
+ sc->sc_transfer.data_ptr, max_bulk);
+ }
+
+ xfer->frlengths[0] = max_bulk;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ if ((xfer->error == USB_ERR_CANCELLED) ||
+ (sc->sc_transfer.callback != &umass_cam_cb)) {
+ umass_tr_error(xfer);
+ } else {
+ umass_transfer_start(sc, UMASS_T_CBI_DATA_WR_CS);
+ }
+ return;
+
+ }
+}
+
+static void
+umass_t_cbi_data_wr_cs_callback(struct usb2_xfer *xfer)
+{
+ umass_t_cbi_data_clear_stall_callback(xfer, UMASS_T_CBI_STATUS,
+ UMASS_T_CBI_DATA_WRITE);
+}
+
+static void
+umass_t_cbi_status_callback(struct usb2_xfer *xfer)
+{
+ struct umass_softc *sc = xfer->priv_sc;
+ union ccb *ccb = sc->sc_transfer.ccb;
+ uint32_t residue;
+ uint8_t status;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ if (xfer->actlen < sizeof(sc->sbl)) {
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, &sc->sbl, sizeof(sc->sbl));
+
+ residue = (sc->sc_transfer.data_len -
+ sc->sc_transfer.actlen);
+
+ /* dissect the information in the buffer */
+
+ if (sc->sc_proto & UMASS_PROTO_UFI) {
+
+ /*
+ * Section 3.4.3.1.3 specifies that the UFI command
+ * protocol returns an ASC and ASCQ in the interrupt
+ * data block.
+ */
+
+ DPRINTF(sc, UDMASS_CBI, "UFI CCI, ASC = 0x%02x, "
+ "ASCQ = 0x%02x\n", sc->sbl.ufi.asc,
+ sc->sbl.ufi.ascq);
+
+ status = (((sc->sbl.ufi.asc == 0) &&
+ (sc->sbl.ufi.ascq == 0)) ?
+ STATUS_CMD_OK : STATUS_CMD_FAILED);
+
+ sc->sc_transfer.ccb = NULL;
+
+ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
+
+ (sc->sc_transfer.callback)
+ (sc, ccb, residue, status);
+
+ return;
+
+ } else {
+
+ /* Command Interrupt Data Block */
+
+ DPRINTF(sc, UDMASS_CBI, "type=0x%02x, value=0x%02x\n",
+ sc->sbl.common.type, sc->sbl.common.value);
+
+ if (sc->sbl.common.type == IDB_TYPE_CCI) {
+
+ status = (sc->sbl.common.value & IDB_VALUE_STATUS_MASK);
+
+ status = ((status == IDB_VALUE_PASS) ? STATUS_CMD_OK :
+ (status == IDB_VALUE_FAIL) ? STATUS_CMD_FAILED :
+ (status == IDB_VALUE_PERSISTENT) ? STATUS_CMD_FAILED :
+ STATUS_WIRE_FAILED);
+
+ sc->sc_transfer.ccb = NULL;
+
+ sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND;
+
+ (sc->sc_transfer.callback)
+ (sc, ccb, residue, status);
+
+ return;
+ }
+ }
+
+ /* fallthrough */
+
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ return;
+
+ default: /* Error */
+ DPRINTF(sc, UDMASS_CBI, "Failed to read CSW: %s\n",
+ usb2_errstr(xfer->error));
+ umass_tr_error(xfer);
+ return;
+
+ }
+}
+
+/*
+ * CAM specific functions (used by SCSI, UFI, 8070i (ATAPI))
+ */
+
+static int
+umass_cam_attach_sim(struct umass_softc *sc)
+{
+ struct cam_devq *devq; /* Per device Queue */
+
+ /*
+ * A HBA is attached to the CAM layer.
+ *
+ * The CAM layer will then after a while start probing for devices on
+ * the bus. The number of SIMs is limited to one.
+ */
+
+ devq = cam_simq_alloc(1 /* maximum openings */ );
+ if (devq == NULL) {
+ return (ENOMEM);
+ }
+ sc->sc_sim = cam_sim_alloc
+ (&umass_cam_action, &umass_cam_poll,
+ DEVNAME_SIM,
+ sc /* priv */ ,
+ sc->sc_unit /* unit number */ ,
+#if (__FreeBSD_version >= 700037)
+ &sc->sc_mtx /* mutex */ ,
+#endif
+ 1 /* maximum device openings */ ,
+ 0 /* maximum tagged device openings */ ,
+ devq);
+
+ if (sc->sc_sim == NULL) {
+ cam_simq_free(devq);
+ return (ENOMEM);
+ }
+
+#if (__FreeBSD_version >= 700037)
+ mtx_lock(&sc->sc_mtx);
+#endif
+
+#if (__FreeBSD_version >= 700048)
+ if (xpt_bus_register(sc->sc_sim, sc->sc_dev, sc->sc_unit) != CAM_SUCCESS) {
+ mtx_unlock(&sc->sc_mtx);
+ return (ENOMEM);
+ }
+#else
+ if (xpt_bus_register(sc->sc_sim, sc->sc_unit) != CAM_SUCCESS) {
+#if (__FreeBSD_version >= 700037)
+ mtx_unlock(&sc->sc_mtx);
+#endif
+ return (ENOMEM);
+ }
+#endif
+
+#if (__FreeBSD_version >= 700037)
+ mtx_unlock(&sc->sc_mtx);
+#endif
+ return (0);
+}
+
+static void
+umass_cam_rescan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+#if USB_DEBUG
+ struct umass_softc *sc = NULL;
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ DPRINTF(sc, UDMASS_SCSI, "%s:%d Rescan failed, 0x%04x\n",
+ periph->periph_name, periph->unit_number,
+ ccb->ccb_h.status);
+ } else {
+ DPRINTF(sc, UDMASS_SCSI, "%s%d: Rescan succeeded\n",
+ periph->periph_name, periph->unit_number);
+ }
+#endif
+
+ xpt_free_path(ccb->ccb_h.path);
+ free(ccb, M_USBDEV);
+}
+
+static void
+umass_cam_rescan(struct umass_softc *sc)
+{
+ struct cam_path *path;
+ union ccb *ccb;
+
+ DPRINTF(sc, UDMASS_SCSI, "scbus%d: scanning for %d:%d:%d\n",
+ cam_sim_path(sc->sc_sim),
+ cam_sim_path(sc->sc_sim),
+ sc->sc_unit, CAM_LUN_WILDCARD);
+
+ ccb = malloc(sizeof(*ccb), M_USBDEV, M_WAITOK | M_ZERO);
+
+ if (ccb == NULL) {
+ return;
+ }
+#if (__FreeBSD_version >= 700037)
+ mtx_lock(&sc->sc_mtx);
+#endif
+
+ if (xpt_create_path(&path, xpt_periph, cam_sim_path(sc->sc_sim),
+ CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)
+ != CAM_REQ_CMP) {
+#if (__FreeBSD_version >= 700037)
+ mtx_unlock(&sc->sc_mtx);
+#endif
+ free(ccb, M_USBDEV);
+ return;
+ }
+ xpt_setup_ccb(&ccb->ccb_h, path, 5 /* priority (low) */ );
+ ccb->ccb_h.func_code = XPT_SCAN_BUS;
+ ccb->ccb_h.cbfcnp = &umass_cam_rescan_callback;
+ ccb->crcn.flags = CAM_FLAG_NONE;
+ xpt_action(ccb);
+
+#if (__FreeBSD_version >= 700037)
+ mtx_unlock(&sc->sc_mtx);
+#endif
+
+ /* The scan is in progress now. */
+}
+
+static void
+umass_cam_attach(struct umass_softc *sc)
+{
+#ifndef USB_DEBUG
+ if (bootverbose)
+#endif
+ printf("%s:%d:%d:%d: Attached to scbus%d\n",
+ sc->sc_name, cam_sim_path(sc->sc_sim),
+ sc->sc_unit, CAM_LUN_WILDCARD,
+ cam_sim_path(sc->sc_sim));
+
+ if (!cold) {
+ /*
+ * Notify CAM of the new device after a short delay. Any
+ * failure is benign, as the user can still do it by hand
+ * (camcontrol rescan <busno>). Only do this if we are not
+ * booting, because CAM does a scan after booting has
+ * completed, when interrupts have been enabled.
+ */
+
+ /* scan the new sim */
+ umass_cam_rescan(sc);
+ }
+}
+
+/* umass_cam_detach
+ * detach from the CAM layer
+ */
+
+static void
+umass_cam_detach_sim(struct umass_softc *sc)
+{
+ if (sc->sc_sim != NULL) {
+ if (xpt_bus_deregister(cam_sim_path(sc->sc_sim))) {
+ /* accessing the softc is not possible after this */
+ sc->sc_sim->softc = UMASS_GONE;
+ cam_sim_free(sc->sc_sim, /* free_devq */ TRUE);
+ } else {
+ panic("%s: CAM layer is busy!\n",
+ sc->sc_name);
+ }
+ sc->sc_sim = NULL;
+ }
+}
+
+/* umass_cam_action
+ * CAM requests for action come through here
+ */
+
+static void
+umass_cam_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct umass_softc *sc = (struct umass_softc *)sim->softc;
+
+ if (sc == UMASS_GONE) {
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+ if (sc) {
+#if (__FreeBSD_version < 700037)
+ mtx_lock(&sc->sc_mtx);
+#endif
+ }
+ /*
+ * Verify, depending on the operation to perform, that we either got
+ * a valid sc, because an existing target was referenced, or
+ * otherwise the SIM is addressed.
+ *
+ * This avoids bombing out at a printf and does give the CAM layer some
+ * sensible feedback on errors.
+ */
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ case XPT_RESET_DEV:
+ case XPT_GET_TRAN_SETTINGS:
+ case XPT_SET_TRAN_SETTINGS:
+ case XPT_CALC_GEOMETRY:
+ /* the opcodes requiring a target. These should never occur. */
+ if (sc == NULL) {
+ DPRINTF(sc, UDMASS_GEN, "%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (target needed)\n",
+ DEVNAME_SIM, cam_sim_path(sc->sc_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code);
+
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ goto done;
+ }
+ break;
+ case XPT_PATH_INQ:
+ case XPT_NOOP:
+ /*
+ * The opcodes sometimes aimed at a target (sc is valid),
+ * sometimes aimed at the SIM (sc is invalid and target is
+ * CAM_TARGET_WILDCARD)
+ */
+ if ((sc == NULL) &&
+ (ccb->ccb_h.target_id != CAM_TARGET_WILDCARD)) {
+ DPRINTF(sc, UDMASS_SCSI, "%s:%d:%d:%d:func_code 0x%04x: "
+ "Invalid target (no wildcard)\n",
+ DEVNAME_SIM, cam_sim_path(sc->sc_sim),
+ ccb->ccb_h.target_id, ccb->ccb_h.target_lun,
+ ccb->ccb_h.func_code);
+
+ ccb->ccb_h.status = CAM_TID_INVALID;
+ xpt_done(ccb);
+ goto done;
+ }
+ break;
+ default:
+ /* XXX Hm, we should check the input parameters */
+ break;
+ }
+
+ /* Perform the requested action */
+ switch (ccb->ccb_h.func_code) {
+ case XPT_SCSI_IO:
+ {
+ uint8_t *cmd;
+ uint8_t dir;
+
+ if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) {
+ cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr);
+ } else {
+ cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes);
+ }
+
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SCSI_IO: "
+ "cmd: 0x%02x, flags: 0x%02x, "
+ "%db cmd/%db data/%db sense\n",
+ cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun, cmd[0],
+ ccb->ccb_h.flags & CAM_DIR_MASK, ccb->csio.cdb_len,
+ ccb->csio.dxfer_len, ccb->csio.sense_len);
+
+ if (sc->sc_transfer.ccb) {
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SCSI_IO: "
+ "I/O in progress, deferring\n",
+ cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun);
+ ccb->ccb_h.status = CAM_SCSI_BUSY;
+ xpt_done(ccb);
+ goto done;
+ }
+ switch (ccb->ccb_h.flags & CAM_DIR_MASK) {
+ case CAM_DIR_IN:
+ dir = DIR_IN;
+ break;
+ case CAM_DIR_OUT:
+ dir = DIR_OUT;
+ DIF(UDMASS_SCSI,
+ umass_dump_buffer(sc, ccb->csio.data_ptr,
+ ccb->csio.dxfer_len, 48));
+ break;
+ default:
+ dir = DIR_NONE;
+ }
+
+ ccb->ccb_h.status = CAM_REQ_INPROG | CAM_SIM_QUEUED;
+
+ /*
+ * sc->sc_transform will convert the command to the
+ * command format needed by the specific command set
+ * and return the converted command in
+ * "sc->sc_transfer.cmd_data"
+ */
+ if (umass_std_transform(sc, ccb, cmd, ccb->csio.cdb_len)) {
+
+ if (sc->sc_transfer.cmd_data[0] == INQUIRY) {
+
+ /*
+ * Handle EVPD inquiry for broken devices first
+ * NO_INQUIRY also implies NO_INQUIRY_EVPD
+ */
+ if ((sc->sc_quirks & (NO_INQUIRY_EVPD | NO_INQUIRY)) &&
+ (sc->sc_transfer.cmd_data[1] & SI_EVPD)) {
+ struct scsi_sense_data *sense;
+
+ sense = &ccb->csio.sense_data;
+ bzero(sense, sizeof(*sense));
+ sense->error_code = SSD_CURRENT_ERROR;
+ sense->flags = SSD_KEY_ILLEGAL_REQUEST;
+ sense->add_sense_code = 0x24;
+ sense->extra_len = 10;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
+ CAM_AUTOSNS_VALID;
+ xpt_done(ccb);
+ goto done;
+ }
+ /*
+ * Return fake inquiry data for
+ * broken devices
+ */
+ if (sc->sc_quirks & NO_INQUIRY) {
+ memcpy(ccb->csio.data_ptr, &fake_inq_data,
+ sizeof(fake_inq_data));
+ ccb->csio.scsi_status = SCSI_STATUS_OK;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ goto done;
+ }
+ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
+ ccb->csio.dxfer_len = SHORT_INQUIRY_LENGTH;
+ }
+ } else if (sc->sc_transfer.cmd_data[0] == SYNCHRONIZE_CACHE) {
+ if (sc->sc_quirks & NO_SYNCHRONIZE_CACHE) {
+ ccb->csio.scsi_status = SCSI_STATUS_OK;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ goto done;
+ }
+ }
+ umass_command_start(sc, dir, ccb->csio.data_ptr,
+ ccb->csio.dxfer_len,
+ ccb->ccb_h.timeout,
+ &umass_cam_cb, ccb);
+ }
+ break;
+ }
+ case XPT_PATH_INQ:
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_PATH_INQ:.\n",
+ sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun);
+
+ /* host specific information */
+ cpi->version_num = 1;
+ cpi->hba_inquiry = 0;
+ cpi->target_sprt = 0;
+ cpi->hba_misc = PIM_NO_6_BYTE;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = UMASS_SCSIID_MAX; /* one target */
+ cpi->initiator_id = UMASS_SCSIID_HOST;
+ strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strlcpy(cpi->hba_vid, "USB SCSI", HBA_IDLEN);
+ strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->bus_id = sc->sc_unit;
+#if (__FreeBSD_version >= 700025)
+ cpi->protocol = PROTO_SCSI;
+ cpi->protocol_version = SCSI_REV_2;
+ cpi->transport = XPORT_USB;
+ cpi->transport_version = 0;
+#endif
+ if (sc == NULL) {
+ cpi->base_transfer_speed = 0;
+ cpi->max_lun = 0;
+ } else {
+ if (sc->sc_quirks & FLOPPY_SPEED) {
+ cpi->base_transfer_speed =
+ UMASS_FLOPPY_TRANSFER_SPEED;
+ } else if (usb2_get_speed(sc->sc_udev) ==
+ USB_SPEED_HIGH) {
+ cpi->base_transfer_speed =
+ UMASS_HIGH_TRANSFER_SPEED;
+ } else {
+ cpi->base_transfer_speed =
+ UMASS_FULL_TRANSFER_SPEED;
+ }
+ cpi->max_lun = sc->sc_maxlun;
+ }
+
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_RESET_DEV:
+ {
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_RESET_DEV:.\n",
+ cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun);
+
+ umass_reset(sc);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ {
+ struct ccb_trans_settings *cts = &ccb->cts;
+
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_GET_TRAN_SETTINGS:.\n",
+ cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun);
+
+#if (__FreeBSD_version >= 700025)
+ cts->protocol = PROTO_SCSI;
+ cts->protocol_version = SCSI_REV_2;
+ cts->transport = XPORT_USB;
+ cts->transport_version = 0;
+ cts->xport_specific.valid = 0;
+#else
+ cts->valid = 0;
+ cts->flags = 0; /* no disconnection, tagging */
+#endif
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_SET_TRAN_SETTINGS:.\n",
+ cam_sim_path(sc->sc_sim), ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun);
+
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ cam_calc_geometry(&ccb->ccg, /* extended */ 1);
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_NOOP:
+ {
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:XPT_NOOP:.\n",
+ sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ DPRINTF(sc, UDMASS_SCSI, "%d:%d:%d:func_code 0x%04x: "
+ "Not implemented\n",
+ sc ? cam_sim_path(sc->sc_sim) : -1, ccb->ccb_h.target_id,
+ ccb->ccb_h.target_lun, ccb->ccb_h.func_code);
+
+ ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+
+done:
+#if (__FreeBSD_version < 700037)
+ if (sc) {
+ mtx_unlock(&sc->sc_mtx);
+ }
+#endif
+ return;
+}
+
+static void
+umass_cam_poll(struct cam_sim *sim)
+{
+ struct umass_softc *sc = (struct umass_softc *)sim->softc;
+
+ if (sc == UMASS_GONE)
+ return;
+
+ DPRINTF(sc, UDMASS_SCSI, "CAM poll\n");
+
+ usb2_do_poll(sc->sc_xfer, UMASS_T_MAX);
+}
+
+
+/* umass_cam_cb
+ * finalise a completed CAM command
+ */
+
+static void
+umass_cam_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue,
+ uint8_t status)
+{
+ ccb->csio.resid = residue;
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ if ((sc->sc_quirks & READ_CAPACITY_OFFBY1) &&
+ (ccb->ccb_h.func_code == XPT_SCSI_IO) &&
+ (ccb->csio.cdb_io.cdb_bytes[0] == READ_CAPACITY)) {
+ struct scsi_read_capacity_data *rcap;
+ uint32_t maxsector;
+
+ rcap = (void *)(ccb->csio.data_ptr);
+ maxsector = scsi_4btoul(rcap->addr) - 1;
+ scsi_ulto4b(maxsector, rcap->addr);
+ }
+ xpt_done(ccb);
+ break;
+
+ case STATUS_CMD_UNKNOWN:
+ case STATUS_CMD_FAILED:
+
+ /* fetch sense data */
+
+ /* the rest of the command was filled in at attach */
+ sc->cam_scsi_sense.length = ccb->csio.sense_len;
+
+ DPRINTF(sc, UDMASS_SCSI, "Fetching %d bytes of "
+ "sense data\n", ccb->csio.sense_len);
+
+ if (umass_std_transform(sc, ccb, &sc->cam_scsi_sense.opcode,
+ sizeof(sc->cam_scsi_sense))) {
+
+ if ((sc->sc_quirks & FORCE_SHORT_INQUIRY) &&
+ (sc->sc_transfer.cmd_data[0] == INQUIRY)) {
+ ccb->csio.sense_len = SHORT_INQUIRY_LENGTH;
+ }
+ umass_command_start(sc, DIR_IN, &ccb->csio.sense_data.error_code,
+ ccb->csio.sense_len, ccb->ccb_h.timeout,
+ &umass_cam_sense_cb, ccb);
+ }
+ break;
+
+ default:
+ /*
+ * the wire protocol failed and will have recovered
+ * (hopefully). We return an error to CAM and let CAM retry
+ * the command if necessary.
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ xpt_done(ccb);
+ break;
+ }
+}
+
+/*
+ * Finalise a completed autosense operation
+ */
+static void
+umass_cam_sense_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue,
+ uint8_t status)
+{
+ uint8_t *cmd;
+ uint8_t key;
+
+ switch (status) {
+ case STATUS_CMD_OK:
+ case STATUS_CMD_UNKNOWN:
+ case STATUS_CMD_FAILED:
+
+ if (ccb->csio.ccb_h.flags & CAM_CDB_POINTER) {
+ cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_ptr);
+ } else {
+ cmd = (uint8_t *)(ccb->csio.cdb_io.cdb_bytes);
+ }
+
+ key = (ccb->csio.sense_data.flags & SSD_KEY);
+
+ /*
+ * Getting sense data always succeeds (apart from wire
+ * failures):
+ */
+ if ((sc->sc_quirks & RS_NO_CLEAR_UA) &&
+ (cmd[0] == INQUIRY) &&
+ (key == SSD_KEY_UNIT_ATTENTION)) {
+ /*
+ * Ignore unit attention errors in the case where
+ * the Unit Attention state is not cleared on
+ * REQUEST SENSE. They will appear again at the next
+ * command.
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else if (key == SSD_KEY_NO_SENSE) {
+ /*
+ * No problem after all (in the case of CBI without
+ * CCI)
+ */
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else if ((sc->sc_quirks & RS_NO_CLEAR_UA) &&
+ (cmd[0] == READ_CAPACITY) &&
+ (key == SSD_KEY_UNIT_ATTENTION)) {
+ /*
+ * Some devices do not clear the unit attention error
+ * on request sense. We insert a test unit ready
+ * command to make sure we clear the unit attention
+ * condition, then allow the retry to proceed as
+ * usual.
+ */
+
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+
+#if 0
+ DELAY(300000);
+#endif
+ DPRINTF(sc, UDMASS_SCSI, "Doing a sneaky"
+ "TEST_UNIT_READY\n");
+
+ /* the rest of the command was filled in at attach */
+
+ if (umass_std_transform(sc, ccb,
+ &sc->cam_scsi_test_unit_ready.opcode,
+ sizeof(sc->cam_scsi_test_unit_ready))) {
+ umass_command_start(sc, DIR_NONE, NULL, 0,
+ ccb->ccb_h.timeout,
+ &umass_cam_quirk_cb, ccb);
+ }
+ break;
+ } else {
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ }
+ xpt_done(ccb);
+ break;
+
+ default:
+ DPRINTF(sc, UDMASS_SCSI, "Autosense failed, "
+ "status %d\n", status);
+ ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
+ xpt_done(ccb);
+ }
+}
+
+/*
+ * This completion code just handles the fact that we sent a test-unit-ready
+ * after having previously failed a READ CAPACITY with CHECK_COND. Even
+ * though this command succeeded, we have to tell CAM to retry.
+ */
+static void
+umass_cam_quirk_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue,
+ uint8_t status)
+{
+ DPRINTF(sc, UDMASS_SCSI, "Test unit ready "
+ "returned status %d\n", status);
+
+ ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR
+ | CAM_AUTOSNS_VALID;
+ ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
+ xpt_done(ccb);
+}
+
+/*
+ * SCSI specific functions
+ */
+
+static uint8_t
+umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
+ uint8_t cmd_len)
+{
+ if ((cmd_len == 0) ||
+ (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
+ DPRINTF(sc, UDMASS_SCSI, "Invalid command "
+ "length: %d bytes\n", cmd_len);
+ return (0); /* failure */
+ }
+ sc->sc_transfer.cmd_len = cmd_len;
+
+ switch (cmd_ptr[0]) {
+ case TEST_UNIT_READY:
+ if (sc->sc_quirks & NO_TEST_UNIT_READY) {
+ DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY "
+ "to START_UNIT\n");
+ bzero(sc->sc_transfer.cmd_data, cmd_len);
+ sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
+ sc->sc_transfer.cmd_data[4] = SSS_START;
+ return (1);
+ }
+ break;
+
+ case INQUIRY:
+ /*
+ * some drives wedge when asked for full inquiry
+ * information.
+ */
+ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
+ bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len);
+ sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH;
+ return (1);
+ }
+ break;
+ }
+
+ bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len);
+ return (1);
+}
+
+static uint8_t
+umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len)
+{
+ if ((cmd_len == 0) ||
+ (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
+ DPRINTF(sc, UDMASS_SCSI, "Invalid command "
+ "length: %d bytes\n", cmd_len);
+ return (0); /* failure */
+ }
+ switch (cmd_ptr[0]) {
+ /* these commands are defined in RBC: */
+ case READ_10:
+ case READ_CAPACITY:
+ case START_STOP_UNIT:
+ case SYNCHRONIZE_CACHE:
+ case WRITE_10:
+ case 0x2f: /* VERIFY_10 is absent from
+ * scsi_all.h??? */
+ case INQUIRY:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case TEST_UNIT_READY:
+ case WRITE_BUFFER:
+ /*
+ * The following commands are not listed in my copy of the
+ * RBC specs. CAM however seems to want those, and at least
+ * the Sony DSC device appears to support those as well
+ */
+ case REQUEST_SENSE:
+ case PREVENT_ALLOW:
+
+ bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len);
+
+ if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) {
+ bzero(sc->sc_transfer.cmd_data + cmd_len, 12 - cmd_len);
+ cmd_len = 12;
+ }
+ sc->sc_transfer.cmd_len = cmd_len;
+ return (1); /* sucess */
+
+ /* All other commands are not legal in RBC */
+ default:
+ DPRINTF(sc, UDMASS_SCSI, "Unsupported RBC "
+ "command 0x%02x\n", cmd_ptr[0]);
+ return (0); /* failure */
+ }
+}
+
+static uint8_t
+umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
+ uint8_t cmd_len)
+{
+ if ((cmd_len == 0) ||
+ (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
+ DPRINTF(sc, UDMASS_SCSI, "Invalid command "
+ "length: %d bytes\n", cmd_len);
+ return (0); /* failure */
+ }
+ /* An UFI command is always 12 bytes in length */
+ sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH;
+
+ /* Zero the command data */
+ bzero(sc->sc_transfer.cmd_data, UFI_COMMAND_LENGTH);
+
+ switch (cmd_ptr[0]) {
+ /*
+ * Commands of which the format has been verified. They
+ * should work. Copy the command into the (zeroed out)
+ * destination buffer.
+ */
+ case TEST_UNIT_READY:
+ if (sc->sc_quirks & NO_TEST_UNIT_READY) {
+ /*
+ * Some devices do not support this command. Start
+ * Stop Unit should give the same results
+ */
+ DPRINTF(sc, UDMASS_UFI, "Converted TEST_UNIT_READY "
+ "to START_UNIT\n");
+
+ sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
+ sc->sc_transfer.cmd_data[4] = SSS_START;
+ return (1);
+ }
+ break;
+
+ case REZERO_UNIT:
+ case REQUEST_SENSE:
+ case FORMAT_UNIT:
+ case INQUIRY:
+ case START_STOP_UNIT:
+ case SEND_DIAGNOSTIC:
+ case PREVENT_ALLOW:
+ case READ_CAPACITY:
+ case READ_10:
+ case WRITE_10:
+ case POSITION_TO_ELEMENT: /* SEEK_10 */
+ case WRITE_AND_VERIFY:
+ case VERIFY:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case READ_12:
+ case WRITE_12:
+ case READ_FORMAT_CAPACITIES:
+ break;
+
+ /*
+ * SYNCHRONIZE_CACHE isn't supported by UFI, nor should it be
+ * required for UFI devices, so it is appropriate to fake
+ * success.
+ */
+ case SYNCHRONIZE_CACHE:
+ return (2);
+
+ default:
+ DPRINTF(sc, UDMASS_SCSI, "Unsupported UFI "
+ "command 0x%02x\n", cmd_ptr[0]);
+ return (0); /* failure */
+ }
+
+ bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len);
+ return (1); /* success */
+}
+
+/*
+ * 8070i (ATAPI) specific functions
+ */
+static uint8_t
+umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr,
+ uint8_t cmd_len)
+{
+ if ((cmd_len == 0) ||
+ (cmd_len > sizeof(sc->sc_transfer.cmd_data))) {
+ DPRINTF(sc, UDMASS_SCSI, "Invalid command "
+ "length: %d bytes\n", cmd_len);
+ return (0); /* failure */
+ }
+ /* An ATAPI command is always 12 bytes in length. */
+ sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH;
+
+ /* Zero the command data */
+ bzero(sc->sc_transfer.cmd_data, ATAPI_COMMAND_LENGTH);
+
+ switch (cmd_ptr[0]) {
+ /*
+ * Commands of which the format has been verified. They
+ * should work. Copy the command into the destination
+ * buffer.
+ */
+ case INQUIRY:
+ /*
+ * some drives wedge when asked for full inquiry
+ * information.
+ */
+ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) {
+ bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len);
+
+ sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH;
+ return (1);
+ }
+ break;
+
+ case TEST_UNIT_READY:
+ if (sc->sc_quirks & NO_TEST_UNIT_READY) {
+ DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY "
+ "to START_UNIT\n");
+ sc->sc_transfer.cmd_data[0] = START_STOP_UNIT;
+ sc->sc_transfer.cmd_data[4] = SSS_START;
+ return (1);
+ }
+ break;
+
+ case REZERO_UNIT:
+ case REQUEST_SENSE:
+ case START_STOP_UNIT:
+ case SEND_DIAGNOSTIC:
+ case PREVENT_ALLOW:
+ case READ_CAPACITY:
+ case READ_10:
+ case WRITE_10:
+ case POSITION_TO_ELEMENT: /* SEEK_10 */
+ case SYNCHRONIZE_CACHE:
+ case MODE_SELECT_10:
+ case MODE_SENSE_10:
+ case READ_BUFFER:
+ case 0x42: /* READ_SUBCHANNEL */
+ case 0x43: /* READ_TOC */
+ case 0x44: /* READ_HEADER */
+ case 0x47: /* PLAY_MSF (Play Minute/Second/Frame) */
+ case 0x48: /* PLAY_TRACK */
+ case 0x49: /* PLAY_TRACK_REL */
+ case 0x4b: /* PAUSE */
+ case 0x51: /* READ_DISK_INFO */
+ case 0x52: /* READ_TRACK_INFO */
+ case 0x54: /* SEND_OPC */
+ case 0x59: /* READ_MASTER_CUE */
+ case 0x5b: /* CLOSE_TR_SESSION */
+ case 0x5c: /* READ_BUFFER_CAP */
+ case 0x5d: /* SEND_CUE_SHEET */
+ case 0xa1: /* BLANK */
+ case 0xa5: /* PLAY_12 */
+ case 0xa6: /* EXCHANGE_MEDIUM */
+ case 0xad: /* READ_DVD_STRUCTURE */
+ case 0xbb: /* SET_CD_SPEED */
+ case 0xe5: /* READ_TRACK_INFO_PHILIPS */
+ break;;
+
+ case READ_12:
+ case WRITE_12:
+ default:
+ DPRINTF(sc, UDMASS_SCSI, "Unsupported ATAPI "
+ "command 0x%02x - trying anyway\n",
+ cmd_ptr[0]);
+ break;;
+ }
+
+ bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len);
+ return (1); /* success */
+}
+
+static uint8_t
+umass_no_transform(struct umass_softc *sc, uint8_t *cmd,
+ uint8_t cmdlen)
+{
+ return (0); /* failure */
+}
+
+static uint8_t
+umass_std_transform(struct umass_softc *sc, union ccb *ccb,
+ uint8_t *cmd, uint8_t cmdlen)
+{
+ uint8_t retval;
+
+ retval = (sc->sc_transform) (sc, cmd, cmdlen);
+
+ if (retval == 2) {
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ return (0);
+ } else if (retval == 0) {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ return (0);
+ }
+ /* Command should be executed */
+ return (1);
+}
+
+#if USB_DEBUG
+static void
+umass_bbb_dump_cbw(struct umass_softc *sc, umass_bbb_cbw_t *cbw)
+{
+ uint8_t *c = cbw->CBWCDB;
+
+ uint32_t dlen = UGETDW(cbw->dCBWDataTransferLength);
+ uint32_t tag = UGETDW(cbw->dCBWTag);
+
+ uint8_t clen = cbw->bCDBLength;
+ uint8_t flags = cbw->bCBWFlags;
+ uint8_t lun = cbw->bCBWLUN;
+
+ DPRINTF(sc, UDMASS_BBB, "CBW %d: cmd = %db "
+ "(0x%02x%02x%02x%02x%02x%02x%s), "
+ "data = %db, lun = %d, dir = %s\n",
+ tag, clen,
+ c[0], c[1], c[2], c[3], c[4], c[5], (clen > 6 ? "..." : ""),
+ dlen, lun, (flags == CBWFLAGS_IN ? "in" :
+ (flags == CBWFLAGS_OUT ? "out" : "<invalid>")));
+}
+
+static void
+umass_bbb_dump_csw(struct umass_softc *sc, umass_bbb_csw_t *csw)
+{
+ uint32_t sig = UGETDW(csw->dCSWSignature);
+ uint32_t tag = UGETDW(csw->dCSWTag);
+ uint32_t res = UGETDW(csw->dCSWDataResidue);
+ uint8_t status = csw->bCSWStatus;
+
+ DPRINTF(sc, UDMASS_BBB, "CSW %d: sig = 0x%08x (%s), tag = 0x%08x, "
+ "res = %d, status = 0x%02x (%s)\n",
+ tag, sig, (sig == CSWSIGNATURE ? "valid" : "invalid"),
+ tag, res,
+ status, (status == CSWSTATUS_GOOD ? "good" :
+ (status == CSWSTATUS_FAILED ? "failed" :
+ (status == CSWSTATUS_PHASE ? "phase" : "<invalid>"))));
+}
+
+static void
+umass_cbi_dump_cmd(struct umass_softc *sc, void *cmd, uint8_t cmdlen)
+{
+ uint8_t *c = cmd;
+ uint8_t dir = sc->sc_transfer.dir;
+
+ DPRINTF(sc, UDMASS_BBB, "cmd = %db "
+ "(0x%02x%02x%02x%02x%02x%02x%s), "
+ "data = %db, dir = %s\n",
+ cmdlen,
+ c[0], c[1], c[2], c[3], c[4], c[5], (cmdlen > 6 ? "..." : ""),
+ sc->sc_transfer.data_len,
+ (dir == DIR_IN ? "in" :
+ (dir == DIR_OUT ? "out" :
+ (dir == DIR_NONE ? "no data phase" : "<invalid>"))));
+}
+
+static void
+umass_dump_buffer(struct umass_softc *sc, uint8_t *buffer, uint32_t buflen,
+ uint32_t printlen)
+{
+ uint32_t i, j;
+ char s1[40];
+ char s2[40];
+ char s3[5];
+
+ s1[0] = '\0';
+ s3[0] = '\0';
+
+ sprintf(s2, " buffer=%p, buflen=%d", buffer, buflen);
+ for (i = 0; (i < buflen) && (i < printlen); i++) {
+ j = i % 16;
+ if (j == 0 && i != 0) {
+ DPRINTF(sc, UDMASS_GEN, "0x %s%s\n",
+ s1, s2);
+ s2[0] = '\0';
+ }
+ sprintf(&s1[j * 2], "%02x", buffer[i] & 0xff);
+ }
+ if (buflen > printlen)
+ sprintf(s3, " ...");
+ DPRINTF(sc, UDMASS_GEN, "0x %s%s%s\n",
+ s1, s2, s3);
+}
+
+#endif
diff --git a/sys/dev/usb/storage/urio.c b/sys/dev/usb/storage/urio.c
new file mode 100644
index 0000000..82e16d9
--- /dev/null
+++ b/sys/dev/usb/storage/urio.c
@@ -0,0 +1,479 @@
+/*-
+ * Copyright (c) 2000 Iwasa Kazmi
+ * 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 code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+/*
+ * 2000/3/24 added NetBSD/OpenBSD support (from Alex Nemirovsky)
+ * 2000/3/07 use two bulk-pipe handles for read and write (Dirk)
+ * 2000/3/06 change major number(143), and copyright header
+ * some fix for 4.0 (Dirk)
+ * 2000/3/05 codes for FreeBSD 4.x - CURRENT (Thanks to Dirk-Willem van Gulik)
+ * 2000/3/01 remove retry code from urioioctl()
+ * change method of bulk transfer (no interrupt)
+ * 2000/2/28 small fixes for new rio_usb.h
+ * 2000/2/24 first version.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/storage/rio500_usb.h>
+
+#define USB_DEBUG_VAR urio_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_generic.h>
+
+#if USB_DEBUG
+static int urio_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, urio, CTLFLAG_RW, 0, "USB urio");
+SYSCTL_INT(_hw_usb2_urio, OID_AUTO, debug, CTLFLAG_RW,
+ &urio_debug, 0, "urio debug level");
+#endif
+
+#define URIO_T_WR 0
+#define URIO_T_RD 1
+#define URIO_T_WR_CS 2
+#define URIO_T_RD_CS 3
+#define URIO_T_MAX 4
+
+#define URIO_BSIZE (1<<12) /* bytes */
+#define URIO_IFQ_MAXLEN 2 /* units */
+
+struct urio_softc {
+ struct usb2_fifo_sc sc_fifo;
+ struct mtx sc_mtx;
+
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[URIO_T_MAX];
+
+ uint8_t sc_flags;
+#define URIO_FLAG_READ_STALL 0x01 /* read transfer stalled */
+#define URIO_FLAG_WRITE_STALL 0x02 /* write transfer stalled */
+
+ uint8_t sc_name[16];
+};
+
+/* prototypes */
+
+static device_probe_t urio_probe;
+static device_attach_t urio_attach;
+static device_detach_t urio_detach;
+
+static usb2_callback_t urio_write_callback;
+static usb2_callback_t urio_write_clear_stall_callback;
+static usb2_callback_t urio_read_callback;
+static usb2_callback_t urio_read_clear_stall_callback;
+
+static usb2_fifo_close_t urio_close;
+static usb2_fifo_cmd_t urio_start_read;
+static usb2_fifo_cmd_t urio_start_write;
+static usb2_fifo_cmd_t urio_stop_read;
+static usb2_fifo_cmd_t urio_stop_write;
+static usb2_fifo_ioctl_t urio_ioctl;
+static usb2_fifo_open_t urio_open;
+
+static struct usb2_fifo_methods urio_fifo_methods = {
+ .f_close = &urio_close,
+ .f_ioctl = &urio_ioctl,
+ .f_open = &urio_open,
+ .f_start_read = &urio_start_read,
+ .f_start_write = &urio_start_write,
+ .f_stop_read = &urio_stop_read,
+ .f_stop_write = &urio_stop_write,
+ .basename[0] = "urio",
+};
+
+static const struct usb2_config urio_config[URIO_T_MAX] = {
+ [URIO_T_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = URIO_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,.proxy_buffer = 1,},
+ .mh.callback = &urio_write_callback,
+ },
+
+ [URIO_T_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = URIO_BSIZE,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1,},
+ .mh.callback = &urio_read_callback,
+ },
+
+ [URIO_T_WR_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &urio_write_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+
+ [URIO_T_RD_CS] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .mh.bufsize = sizeof(struct usb2_device_request),
+ .mh.flags = {},
+ .mh.callback = &urio_read_clear_stall_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .mh.interval = 50, /* 50ms */
+ },
+};
+
+static devclass_t urio_devclass;
+
+static device_method_t urio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, urio_probe),
+ DEVMETHOD(device_attach, urio_attach),
+ DEVMETHOD(device_detach, urio_detach),
+ {0, 0}
+};
+
+static driver_t urio_driver = {
+ .name = "urio",
+ .methods = urio_methods,
+ .size = sizeof(struct urio_softc),
+};
+
+DRIVER_MODULE(urio, ushub, urio_driver, urio_devclass, NULL, 0);
+MODULE_DEPEND(urio, usb, 1, 1, 1);
+
+static int
+urio_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST) {
+ return (ENXIO);
+ }
+ if ((((uaa->info.idVendor == USB_VENDOR_DIAMOND) &&
+ (uaa->info.idProduct == USB_PRODUCT_DIAMOND_RIO500USB)) ||
+ ((uaa->info.idVendor == USB_VENDOR_DIAMOND2) &&
+ ((uaa->info.idProduct == USB_PRODUCT_DIAMOND2_RIO600USB) ||
+ (uaa->info.idProduct == USB_PRODUCT_DIAMOND2_RIO800USB)))))
+ return (0);
+ else
+ return (ENXIO);
+}
+
+static int
+urio_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct urio_softc *sc = device_get_softc(dev);
+ int error;
+
+ device_set_usb2_desc(dev);
+
+ sc->sc_udev = uaa->device;
+
+ mtx_init(&sc->sc_mtx, "urio lock", NULL, MTX_DEF | MTX_RECURSE);
+
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ error = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer,
+ urio_config, URIO_T_MAX, sc, &sc->sc_mtx);
+
+ if (error) {
+ DPRINTF("error=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ /* set interface permissions */
+ usb2_set_iface_perm(uaa->device, uaa->info.bIfaceIndex,
+ UID_ROOT, GID_OPERATOR, 0644);
+
+ error = usb2_fifo_attach(uaa->device, sc, &sc->sc_mtx,
+ &urio_fifo_methods, &sc->sc_fifo,
+ device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex);
+ if (error) {
+ goto detach;
+ }
+ return (0); /* success */
+
+detach:
+ urio_detach(dev);
+ return (ENOMEM); /* failure */
+}
+
+static void
+urio_write_callback(struct usb2_xfer *xfer)
+{
+ struct urio_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_TX];
+ uint32_t actlen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+ if (sc->sc_flags & URIO_FLAG_WRITE_STALL) {
+ usb2_transfer_start(sc->sc_xfer[URIO_T_WR_CS]);
+ return;
+ }
+ if (usb2_fifo_get_data(f, xfer->frbuffers, 0,
+ xfer->max_data_length, &actlen, 0)) {
+
+ xfer->frlengths[0] = actlen;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ sc->sc_flags |= URIO_FLAG_WRITE_STALL;
+ usb2_transfer_start(sc->sc_xfer[URIO_T_WR_CS]);
+ }
+ return;
+ }
+}
+
+static void
+urio_write_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct urio_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[URIO_T_WR];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~URIO_FLAG_WRITE_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+static void
+urio_read_callback(struct usb2_xfer *xfer)
+{
+ struct urio_softc *sc = xfer->priv_sc;
+ struct usb2_fifo *f = sc->sc_fifo.fp[USB_FIFO_RX];
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_fifo_put_data(f, xfer->frbuffers, 0,
+ xfer->actlen, 1);
+
+ case USB_ST_SETUP:
+ if (sc->sc_flags & URIO_FLAG_READ_STALL) {
+ usb2_transfer_start(sc->sc_xfer[URIO_T_RD_CS]);
+ return;
+ }
+ if (usb2_fifo_put_bytes_max(f) != 0) {
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ sc->sc_flags |= URIO_FLAG_READ_STALL;
+ usb2_transfer_start(sc->sc_xfer[URIO_T_RD_CS]);
+ }
+ return;
+ }
+}
+
+static void
+urio_read_clear_stall_callback(struct usb2_xfer *xfer)
+{
+ struct urio_softc *sc = xfer->priv_sc;
+ struct usb2_xfer *xfer_other = sc->sc_xfer[URIO_T_RD];
+
+ if (usb2_clear_stall_callback(xfer, xfer_other)) {
+ DPRINTF("stall cleared\n");
+ sc->sc_flags &= ~URIO_FLAG_READ_STALL;
+ usb2_transfer_start(xfer_other);
+ }
+}
+
+static void
+urio_start_read(struct usb2_fifo *fifo)
+{
+ struct urio_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[URIO_T_RD]);
+}
+
+static void
+urio_stop_read(struct usb2_fifo *fifo)
+{
+ struct urio_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[URIO_T_RD_CS]);
+ usb2_transfer_stop(sc->sc_xfer[URIO_T_RD]);
+}
+
+static void
+urio_start_write(struct usb2_fifo *fifo)
+{
+ struct urio_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_start(sc->sc_xfer[URIO_T_WR]);
+}
+
+static void
+urio_stop_write(struct usb2_fifo *fifo)
+{
+ struct urio_softc *sc = fifo->priv_sc0;
+
+ usb2_transfer_stop(sc->sc_xfer[URIO_T_WR_CS]);
+ usb2_transfer_stop(sc->sc_xfer[URIO_T_WR]);
+}
+
+static int
+urio_open(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ struct urio_softc *sc = fifo->priv_sc0;
+
+ if ((fflags & (FWRITE | FREAD)) != (FWRITE | FREAD)) {
+ return (EACCES);
+ }
+ if (fflags & FREAD) {
+ /* clear stall first */
+ mtx_lock(&sc->sc_mtx);
+ sc->sc_flags |= URIO_FLAG_READ_STALL;
+ mtx_unlock(&sc->sc_mtx);
+
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[URIO_T_RD]->max_data_length,
+ URIO_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+ if (fflags & FWRITE) {
+ /* clear stall first */
+ sc->sc_flags |= URIO_FLAG_WRITE_STALL;
+
+ if (usb2_fifo_alloc_buffer(fifo,
+ sc->sc_xfer[URIO_T_WR]->max_data_length,
+ URIO_IFQ_MAXLEN)) {
+ return (ENOMEM);
+ }
+ }
+ return (0); /* success */
+}
+
+static void
+urio_close(struct usb2_fifo *fifo, int fflags, struct thread *td)
+{
+ if (fflags & (FREAD | FWRITE)) {
+ usb2_fifo_free_buffer(fifo);
+ }
+}
+
+static int
+urio_ioctl(struct usb2_fifo *fifo, u_long cmd, void *addr,
+ int fflags, struct thread *td)
+{
+ struct usb2_ctl_request ur;
+ struct RioCommand *rio_cmd;
+ int error;
+
+ switch (cmd) {
+ case RIO_RECV_COMMAND:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ goto done;
+ }
+ bzero(&ur, sizeof(ur));
+ rio_cmd = addr;
+ ur.ucr_request.bmRequestType =
+ rio_cmd->requesttype | UT_READ_VENDOR_DEVICE;
+ break;
+
+ case RIO_SEND_COMMAND:
+ if (!(fflags & FWRITE)) {
+ error = EPERM;
+ goto done;
+ }
+ bzero(&ur, sizeof(ur));
+ rio_cmd = addr;
+ ur.ucr_request.bmRequestType =
+ rio_cmd->requesttype | UT_WRITE_VENDOR_DEVICE;
+ break;
+
+ default:
+ error = EINVAL;
+ goto done;
+ }
+
+ DPRINTFN(2, "Sending command\n");
+
+ /* Send rio control message */
+ ur.ucr_request.bRequest = rio_cmd->request;
+ USETW(ur.ucr_request.wValue, rio_cmd->value);
+ USETW(ur.ucr_request.wIndex, rio_cmd->index);
+ USETW(ur.ucr_request.wLength, rio_cmd->length);
+ ur.ucr_data = rio_cmd->buffer;
+
+ /* reuse generic USB code */
+ error = ugen_do_request(fifo, &ur);
+
+done:
+ return (error);
+}
+
+static int
+urio_detach(device_t dev)
+{
+ struct urio_softc *sc = device_get_softc(dev);
+
+ DPRINTF("\n");
+
+ usb2_fifo_detach(&sc->sc_fifo);
+
+ usb2_transfer_unsetup(sc->sc_xfer, URIO_T_MAX);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
diff --git a/sys/dev/usb/storage/ustorage_fs.c b/sys/dev/usb/storage/ustorage_fs.c
new file mode 100644
index 0000000..2eec249
--- /dev/null
+++ b/sys/dev/usb/storage/ustorage_fs.c
@@ -0,0 +1,1897 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (C) 2003-2005 Alan Stern
+ * 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.
+ * 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 names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * NOTE: Much of the SCSI statemachine handling code derives from the
+ * Linux USB gadget stack.
+ */
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR ustorage_fs_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+
+#if USB_DEBUG
+static int ustorage_fs_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ustorage_fs, CTLFLAG_RW, 0, "USB ustorage_fs");
+SYSCTL_INT(_hw_usb2_ustorage_fs, OID_AUTO, debug, CTLFLAG_RW,
+ &ustorage_fs_debug, 0, "ustorage_fs debug level");
+#endif
+
+/* Define some limits */
+
+#define USTORAGE_FS_BULK_SIZE (1 << 17)
+#define USTORAGE_FS_MAX_LUN 8
+#define USTORAGE_FS_RELEASE 0x0101
+#define USTORAGE_FS_RAM_SECT (1 << 13)
+
+static uint8_t *ustorage_fs_ramdisk;
+
+/* USB transfer definitions */
+
+#define USTORAGE_FS_T_BBB_COMMAND 0
+#define USTORAGE_FS_T_BBB_DATA_DUMP 1
+#define USTORAGE_FS_T_BBB_DATA_READ 2
+#define USTORAGE_FS_T_BBB_DATA_WRITE 3
+#define USTORAGE_FS_T_BBB_STATUS 4
+#define USTORAGE_FS_T_BBB_MAX 5
+
+/* USB data stage direction */
+
+#define DIR_NONE 0
+#define DIR_READ 1
+#define DIR_WRITE 2
+
+/* USB interface specific control request */
+
+#define UR_BBB_RESET 0xff /* Bulk-Only reset */
+#define UR_BBB_GET_MAX_LUN 0xfe /* Get maximum lun */
+
+/* Command Block Wrapper */
+typedef struct {
+ 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 ustorage_fs_bbb_cbw_t;
+
+#define USTORAGE_FS_BBB_CBW_SIZE 31
+
+/* Command Status Wrapper */
+typedef struct {
+ uDWord dCSWSignature;
+#define CSWSIGNATURE 0x53425355
+ uDWord dCSWTag;
+ uDWord dCSWDataResidue;
+ uByte bCSWStatus;
+#define CSWSTATUS_GOOD 0x0
+#define CSWSTATUS_FAILED 0x1
+#define CSWSTATUS_PHASE 0x2
+} __packed ustorage_fs_bbb_csw_t;
+
+#define USTORAGE_FS_BBB_CSW_SIZE 13
+
+struct ustorage_fs_lun {
+
+ void *memory_image;
+
+ uint32_t num_sectors;
+ uint32_t sense_data;
+ uint32_t sense_data_info;
+ uint32_t unit_attention_data;
+
+ uint8_t read_only:1;
+ uint8_t prevent_medium_removal:1;
+ uint8_t info_valid:1;
+ uint8_t removable:1;
+};
+
+struct ustorage_fs_softc {
+
+ ustorage_fs_bbb_cbw_t sc_cbw; /* Command Wrapper Block */
+ ustorage_fs_bbb_csw_t sc_csw; /* Command Status Block */
+
+ struct mtx sc_mtx;
+
+ struct ustorage_fs_lun sc_lun[USTORAGE_FS_MAX_LUN];
+
+ struct {
+ uint8_t *data_ptr;
+ struct ustorage_fs_lun *currlun;
+
+ uint32_t data_rem; /* bytes, as reported by the command
+ * block wrapper */
+ uint32_t offset; /* bytes */
+
+ uint8_t cbw_dir;
+ uint8_t cmd_dir;
+ uint8_t lun;
+ uint8_t cmd_data[CBWCDBLENGTH];
+ uint8_t cmd_len;
+ uint8_t data_short:1;
+ uint8_t data_error:1;
+ } sc_transfer;
+
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct usb2_xfer *sc_xfer[USTORAGE_FS_T_BBB_MAX];
+
+ uint32_t sc_unit;
+
+ uint8_t sc_name[16];
+ uint8_t sc_iface_no; /* interface number */
+ uint8_t sc_last_lun;
+ uint8_t sc_last_xfer_index;
+ uint8_t sc_qdata[1024];
+};
+
+/* prototypes */
+
+static device_probe_t ustorage_fs_probe;
+static device_attach_t ustorage_fs_attach;
+static device_detach_t ustorage_fs_detach;
+static device_suspend_t ustorage_fs_suspend;
+static device_resume_t ustorage_fs_resume;
+static device_shutdown_t ustorage_fs_shutdown;
+static usb_handle_request_t ustorage_fs_handle_request;
+
+static usb2_callback_t ustorage_fs_t_bbb_command_callback;
+static usb2_callback_t ustorage_fs_t_bbb_data_dump_callback;
+static usb2_callback_t ustorage_fs_t_bbb_data_read_callback;
+static usb2_callback_t ustorage_fs_t_bbb_data_write_callback;
+static usb2_callback_t ustorage_fs_t_bbb_status_callback;
+
+static void ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index);
+static void ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc);
+
+static uint8_t ustorage_fs_verify(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_inquiry(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_request_sense(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_read_capacity(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_mode_sense(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_start_stop(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_mode_select(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask);
+static uint8_t ustorage_fs_read(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_write(struct ustorage_fs_softc *sc);
+static uint8_t ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t cmd_size, uint16_t mask, uint8_t needs_medium);
+static uint8_t ustorage_fs_do_cmd(struct ustorage_fs_softc *sc);
+
+static device_method_t ustorage_fs_methods[] = {
+ /* USB interface */
+ DEVMETHOD(usb_handle_request, ustorage_fs_handle_request),
+
+ /* Device interface */
+ DEVMETHOD(device_probe, ustorage_fs_probe),
+ DEVMETHOD(device_attach, ustorage_fs_attach),
+ DEVMETHOD(device_detach, ustorage_fs_detach),
+ DEVMETHOD(device_suspend, ustorage_fs_suspend),
+ DEVMETHOD(device_resume, ustorage_fs_resume),
+ DEVMETHOD(device_shutdown, ustorage_fs_shutdown),
+
+ {0, 0}
+};
+
+static driver_t ustorage_fs_driver = {
+ .name = "ustorage_fs",
+ .methods = ustorage_fs_methods,
+ .size = sizeof(struct ustorage_fs_softc),
+};
+
+static devclass_t ustorage_fs_devclass;
+
+DRIVER_MODULE(ustorage_fs, ushub, ustorage_fs_driver, ustorage_fs_devclass, NULL, 0);
+MODULE_VERSION(ustorage_fs, 0);
+MODULE_DEPEND(ustorage_fs, usb, 1, 1, 1);
+
+struct usb2_config ustorage_fs_bbb_config[USTORAGE_FS_T_BBB_MAX] = {
+
+ [USTORAGE_FS_T_BBB_COMMAND] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .md.bufsize = sizeof(ustorage_fs_bbb_cbw_t),
+ .md.flags = {.ext_buffer = 1,},
+ .md.callback = &ustorage_fs_t_bbb_command_callback,
+ },
+
+ [USTORAGE_FS_T_BBB_DATA_DUMP] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .md.bufsize = 0, /* use wMaxPacketSize */
+ .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .md.callback = &ustorage_fs_t_bbb_data_dump_callback,
+ },
+
+ [USTORAGE_FS_T_BBB_DATA_READ] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .md.bufsize = USTORAGE_FS_BULK_SIZE,
+ .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1},
+ .md.callback = &ustorage_fs_t_bbb_data_read_callback,
+ },
+
+ [USTORAGE_FS_T_BBB_DATA_WRITE] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .md.bufsize = USTORAGE_FS_BULK_SIZE,
+ .md.flags = {.proxy_buffer = 1,.short_xfer_ok = 1,.ext_buffer = 1},
+ .md.callback = &ustorage_fs_t_bbb_data_write_callback,
+ },
+
+ [USTORAGE_FS_T_BBB_STATUS] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .md.bufsize = sizeof(ustorage_fs_bbb_csw_t),
+ .md.flags = {.short_xfer_ok = 1,.ext_buffer = 1,},
+ .md.callback = &ustorage_fs_t_bbb_status_callback,
+ },
+};
+
+/*
+ * USB device probe/attach/detach
+ */
+
+static int
+ustorage_fs_probe(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+
+ if (uaa->usb2_mode != USB_MODE_DEVICE) {
+ return (ENXIO);
+ }
+ if (uaa->use_generic == 0) {
+ /* give other drivers a try first */
+ return (ENXIO);
+ }
+ /* Check for a standards compliant device */
+ id = usb2_get_interface_descriptor(uaa->iface);
+ if ((id == NULL) ||
+ (id->bInterfaceClass != UICLASS_MASS) ||
+ (id->bInterfaceSubClass != UISUBCLASS_SCSI) ||
+ (id->bInterfaceProtocol != UIPROTO_MASS_BBB)) {
+ return (ENXIO);
+ }
+ return (0);
+}
+
+static int
+ustorage_fs_attach(device_t dev)
+{
+ struct ustorage_fs_softc *sc = device_get_softc(dev);
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct usb2_interface_descriptor *id;
+ int err;
+
+ /*
+ * NOTE: the softc struct is bzero-ed in device_set_driver.
+ * We can safely call ustorage_fs_detach without specifically
+ * initializing the struct.
+ */
+
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+ sc->sc_unit = device_get_unit(dev);
+
+ if (sc->sc_unit == 0) {
+ if (ustorage_fs_ramdisk == NULL) {
+ /*
+ * allocate a memory image for our ramdisk until
+ * further
+ */
+ ustorage_fs_ramdisk =
+ malloc(USTORAGE_FS_RAM_SECT << 9, M_USB, M_ZERO | M_WAITOK);
+ if (ustorage_fs_ramdisk == NULL) {
+ return (ENOMEM);
+ }
+ }
+ sc->sc_lun[0].memory_image = ustorage_fs_ramdisk;
+ sc->sc_lun[0].num_sectors = USTORAGE_FS_RAM_SECT;
+ sc->sc_lun[0].removable = 1;
+ }
+ snprintf(sc->sc_name, sizeof(sc->sc_name),
+ "%s", device_get_nameunit(dev));
+
+ device_set_usb2_desc(dev);
+
+ mtx_init(&sc->sc_mtx, "USTORAGE_FS lock",
+ NULL, (MTX_DEF | MTX_RECURSE));
+
+ /* get interface index */
+
+ id = usb2_get_interface_descriptor(uaa->iface);
+ if (id == NULL) {
+ device_printf(dev, "failed to get "
+ "interface number\n");
+ goto detach;
+ }
+ sc->sc_iface_no = id->bInterfaceNumber;
+
+ err = usb2_transfer_setup(uaa->device,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, ustorage_fs_bbb_config,
+ USTORAGE_FS_T_BBB_MAX, sc, &sc->sc_mtx);
+ if (err) {
+ device_printf(dev, "could not setup required "
+ "transfers, %s\n", usb2_errstr(err));
+ goto detach;
+ }
+ /* start Mass Storage State Machine */
+
+ mtx_lock(&sc->sc_mtx);
+ ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND);
+ mtx_unlock(&sc->sc_mtx);
+
+ return (0); /* success */
+
+detach:
+ ustorage_fs_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static int
+ustorage_fs_detach(device_t dev)
+{
+ struct ustorage_fs_softc *sc = device_get_softc(dev);
+
+ /* teardown our statemachine */
+
+ usb2_transfer_unsetup(sc->sc_xfer, USTORAGE_FS_T_BBB_MAX);
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0); /* success */
+}
+
+static int
+ustorage_fs_suspend(device_t dev)
+{
+ device_printf(dev, "suspending\n");
+ return (0); /* success */
+}
+
+static int
+ustorage_fs_resume(device_t dev)
+{
+ device_printf(dev, "resuming\n");
+ return (0); /* success */
+}
+
+static int
+ustorage_fs_shutdown(device_t dev)
+{
+ return (0); /* success */
+}
+
+/*
+ * Generic functions to handle transfers
+ */
+
+static void
+ustorage_fs_transfer_start(struct ustorage_fs_softc *sc, uint8_t xfer_index)
+{
+ if (sc->sc_xfer[xfer_index]) {
+ sc->sc_last_xfer_index = xfer_index;
+ usb2_transfer_start(sc->sc_xfer[xfer_index]);
+ }
+}
+
+static void
+ustorage_fs_transfer_stop(struct ustorage_fs_softc *sc)
+{
+ usb2_transfer_stop(sc->sc_xfer[sc->sc_last_xfer_index]);
+ mtx_unlock(&sc->sc_mtx);
+ usb2_transfer_drain(sc->sc_xfer[sc->sc_last_xfer_index]);
+ mtx_lock(&sc->sc_mtx);
+}
+
+static int
+ustorage_fs_handle_request(device_t dev,
+ const void *preq, void **pptr, uint16_t *plen,
+ uint16_t offset, uint8_t is_complete)
+{
+ struct ustorage_fs_softc *sc = device_get_softc(dev);
+ const struct usb2_device_request *req = preq;
+
+ if (!is_complete) {
+ if (req->bRequest == UR_BBB_RESET) {
+ *plen = 0;
+ mtx_lock(&sc->sc_mtx);
+ ustorage_fs_transfer_stop(sc);
+ sc->sc_transfer.data_error = 1;
+ ustorage_fs_transfer_start(sc,
+ USTORAGE_FS_T_BBB_COMMAND);
+ mtx_unlock(&sc->sc_mtx);
+ return (0);
+ } else if (req->bRequest == UR_BBB_GET_MAX_LUN) {
+ if (offset == 0) {
+ *plen = 1;
+ *pptr = &sc->sc_last_lun;
+ } else {
+ *plen = 0;
+ }
+ return (0);
+ }
+ }
+ return (ENXIO); /* use builtin handler */
+}
+
+static void
+ustorage_fs_t_bbb_command_callback(struct usb2_xfer *xfer)
+{
+ struct ustorage_fs_softc *sc = xfer->priv_sc;
+ uint32_t tag;
+ uint8_t error = 0;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ tag = UGETDW(sc->sc_cbw.dCBWSignature);
+
+ if (tag != CBWSIGNATURE) {
+ /* do nothing */
+ DPRINTF("invalid signature 0x%08x\n", tag);
+ break;
+ }
+ tag = UGETDW(sc->sc_cbw.dCBWTag);
+
+ /* echo back tag */
+ USETDW(sc->sc_csw.dCSWTag, tag);
+
+ /* reset status */
+ sc->sc_csw.bCSWStatus = 0;
+
+ /* reset data offset, data length and data remainder */
+ sc->sc_transfer.offset = 0;
+ sc->sc_transfer.data_rem =
+ UGETDW(sc->sc_cbw.dCBWDataTransferLength);
+
+ /* reset data flags */
+ sc->sc_transfer.data_short = 0;
+
+ /* extract LUN */
+ sc->sc_transfer.lun = sc->sc_cbw.bCBWLUN;
+
+ if (sc->sc_transfer.data_rem == 0) {
+ sc->sc_transfer.cbw_dir = DIR_NONE;
+ } else {
+ if (sc->sc_cbw.bCBWFlags & CBWFLAGS_IN) {
+ sc->sc_transfer.cbw_dir = DIR_WRITE;
+ } else {
+ sc->sc_transfer.cbw_dir = DIR_READ;
+ }
+ }
+
+ sc->sc_transfer.cmd_len = sc->sc_cbw.bCDBLength;
+ if ((sc->sc_transfer.cmd_len > sizeof(sc->sc_cbw.CBWCDB)) ||
+ (sc->sc_transfer.cmd_len == 0)) {
+ /* just halt - this is invalid */
+ DPRINTF("invalid command length %d bytes\n",
+ sc->sc_transfer.cmd_len);
+ break;
+ }
+ bcopy(sc->sc_cbw.CBWCDB, sc->sc_transfer.cmd_data,
+ sc->sc_transfer.cmd_len);
+
+ bzero(sc->sc_cbw.CBWCDB + sc->sc_transfer.cmd_len,
+ sizeof(sc->sc_cbw.CBWCDB) - sc->sc_transfer.cmd_len);
+
+ error = ustorage_fs_do_cmd(sc);
+ if (error) {
+ /* got an error */
+ DPRINTF("command failed\n");
+ break;
+ }
+ if ((sc->sc_transfer.data_rem > 0) &&
+ (sc->sc_transfer.cbw_dir != sc->sc_transfer.cmd_dir)) {
+ /* contradicting data transfer direction */
+ error = 1;
+ DPRINTF("data direction mismatch\n");
+ break;
+ }
+ switch (sc->sc_transfer.cbw_dir) {
+ case DIR_READ:
+ ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_READ);
+ break;
+ case DIR_WRITE:
+ ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_DATA_WRITE);
+ break;
+ default:
+ ustorage_fs_transfer_start(sc,
+ USTORAGE_FS_T_BBB_STATUS);
+ break;
+ }
+ break;
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (sc->sc_transfer.data_error) {
+ sc->sc_transfer.data_error = 0;
+ xfer->flags.stall_pipe = 1;
+ DPRINTF("stall pipe\n");
+ } else {
+ xfer->flags.stall_pipe = 0;
+ }
+
+ xfer->frlengths[0] = sizeof(sc->sc_cbw);
+ usb2_set_frame_data(xfer, &sc->sc_cbw, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ DPRINTF("error\n");
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ /* If the pipe is already stalled, don't do another stall */
+ if (!xfer->pipe->is_stalled) {
+ sc->sc_transfer.data_error = 1;
+ }
+ /* try again */
+ goto tr_setup;
+ }
+ if (error) {
+ if (sc->sc_csw.bCSWStatus == 0) {
+ /* set some default error code */
+ sc->sc_csw.bCSWStatus = CSWSTATUS_FAILED;
+ }
+ if (sc->sc_transfer.cbw_dir == DIR_READ) {
+ /* dump all data */
+ ustorage_fs_transfer_start(sc,
+ USTORAGE_FS_T_BBB_DATA_DUMP);
+ return;
+ }
+ if (sc->sc_transfer.cbw_dir == DIR_WRITE) {
+ /* need to stall before status */
+ sc->sc_transfer.data_error = 1;
+ }
+ ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_STATUS);
+ }
+}
+
+static void
+ustorage_fs_t_bbb_data_dump_callback(struct usb2_xfer *xfer)
+{
+ struct ustorage_fs_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.offset += xfer->actlen;
+
+ if ((xfer->actlen != xfer->sumlen) ||
+ (sc->sc_transfer.data_rem == 0)) {
+ /* short transfer or end of data */
+ ustorage_fs_transfer_start(sc,
+ USTORAGE_FS_T_BBB_STATUS);
+ break;
+ }
+ /* Fallthrough */
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (max_bulk > sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ }
+ if (sc->sc_transfer.data_error) {
+ sc->sc_transfer.data_error = 0;
+ xfer->flags.stall_pipe = 1;
+ } else {
+ xfer->flags.stall_pipe = 0;
+ }
+ xfer->frlengths[0] = max_bulk;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ /*
+ * If the pipe is already stalled, don't do another stall:
+ */
+ if (!xfer->pipe->is_stalled) {
+ sc->sc_transfer.data_error = 1;
+ }
+ /* try again */
+ goto tr_setup;
+ }
+}
+
+static void
+ustorage_fs_t_bbb_data_read_callback(struct usb2_xfer *xfer)
+{
+ struct ustorage_fs_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.data_ptr += xfer->actlen;
+ sc->sc_transfer.offset += xfer->actlen;
+
+ if ((xfer->actlen != xfer->sumlen) ||
+ (sc->sc_transfer.data_rem == 0)) {
+ /* short transfer or end of data */
+ ustorage_fs_transfer_start(sc,
+ USTORAGE_FS_T_BBB_STATUS);
+ break;
+ }
+ /* Fallthrough */
+
+ case USB_ST_SETUP:
+tr_setup:
+ if (max_bulk > sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ }
+ if (sc->sc_transfer.data_error) {
+ sc->sc_transfer.data_error = 0;
+ xfer->flags.stall_pipe = 1;
+ } else {
+ xfer->flags.stall_pipe = 0;
+ }
+
+ xfer->frlengths[0] = max_bulk;
+ usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ /* If the pipe is already stalled, don't do another stall */
+ if (!xfer->pipe->is_stalled) {
+ sc->sc_transfer.data_error = 1;
+ }
+ /* try again */
+ goto tr_setup;
+ }
+}
+
+static void
+ustorage_fs_t_bbb_data_write_callback(struct usb2_xfer *xfer)
+{
+ struct ustorage_fs_softc *sc = xfer->priv_sc;
+ uint32_t max_bulk = xfer->max_data_length;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ sc->sc_transfer.data_rem -= xfer->actlen;
+ sc->sc_transfer.data_ptr += xfer->actlen;
+ sc->sc_transfer.offset += xfer->actlen;
+
+ if ((xfer->actlen != xfer->sumlen) ||
+ (sc->sc_transfer.data_rem == 0)) {
+ /* short transfer or end of data */
+ ustorage_fs_transfer_start(sc,
+ USTORAGE_FS_T_BBB_STATUS);
+ break;
+ }
+ case USB_ST_SETUP:
+tr_setup:
+ if (max_bulk >= sc->sc_transfer.data_rem) {
+ max_bulk = sc->sc_transfer.data_rem;
+ if (sc->sc_transfer.data_short) {
+ xfer->flags.force_short_xfer = 1;
+ } else {
+ xfer->flags.force_short_xfer = 0;
+ }
+ } else {
+ xfer->flags.force_short_xfer = 0;
+ }
+
+ if (sc->sc_transfer.data_error) {
+ sc->sc_transfer.data_error = 0;
+ xfer->flags.stall_pipe = 1;
+ } else {
+ xfer->flags.stall_pipe = 0;
+ }
+
+ xfer->frlengths[0] = max_bulk;
+ usb2_set_frame_data(xfer, sc->sc_transfer.data_ptr, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ /*
+ * If the pipe is already stalled, don't do another
+ * stall
+ */
+ if (!xfer->pipe->is_stalled) {
+ sc->sc_transfer.data_error = 1;
+ }
+ /* try again */
+ goto tr_setup;
+ }
+}
+
+static void
+ustorage_fs_t_bbb_status_callback(struct usb2_xfer *xfer)
+{
+ struct ustorage_fs_softc *sc = xfer->priv_sc;
+
+ DPRINTF("\n");
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ ustorage_fs_transfer_start(sc, USTORAGE_FS_T_BBB_COMMAND);
+ break;
+
+ case USB_ST_SETUP:
+tr_setup:
+ USETDW(sc->sc_csw.dCSWSignature, CSWSIGNATURE);
+ USETDW(sc->sc_csw.dCSWDataResidue, sc->sc_transfer.data_rem);
+
+ if (sc->sc_transfer.data_error) {
+ sc->sc_transfer.data_error = 0;
+ xfer->flags.stall_pipe = 1;
+ } else {
+ xfer->flags.stall_pipe = 0;
+ }
+
+ xfer->frlengths[0] = sizeof(sc->sc_csw);
+ usb2_set_frame_data(xfer, &sc->sc_csw, 0);
+ usb2_start_hardware(xfer);
+ break;
+
+ default:
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ /* If the pipe is already stalled, don't do another stall */
+ if (!xfer->pipe->is_stalled) {
+ sc->sc_transfer.data_error = 1;
+ }
+ /* try again */
+ goto tr_setup;
+ }
+}
+
+/* SCSI commands that we recognize */
+#define SC_FORMAT_UNIT 0x04
+#define SC_INQUIRY 0x12
+#define SC_MODE_SELECT_6 0x15
+#define SC_MODE_SELECT_10 0x55
+#define SC_MODE_SENSE_6 0x1a
+#define SC_MODE_SENSE_10 0x5a
+#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SC_READ_6 0x08
+#define SC_READ_10 0x28
+#define SC_READ_12 0xa8
+#define SC_READ_CAPACITY 0x25
+#define SC_READ_FORMAT_CAPACITIES 0x23
+#define SC_RELEASE 0x17
+#define SC_REQUEST_SENSE 0x03
+#define SC_RESERVE 0x16
+#define SC_SEND_DIAGNOSTIC 0x1d
+#define SC_START_STOP_UNIT 0x1b
+#define SC_SYNCHRONIZE_CACHE 0x35
+#define SC_TEST_UNIT_READY 0x00
+#define SC_VERIFY 0x2f
+#define SC_WRITE_6 0x0a
+#define SC_WRITE_10 0x2a
+#define SC_WRITE_12 0xaa
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE 0
+#define SS_COMMUNICATION_FAILURE 0x040800
+#define SS_INVALID_COMMAND 0x052000
+#define SS_INVALID_FIELD_IN_CDB 0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
+#define SS_MEDIUM_NOT_PRESENT 0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
+#define SS_RESET_OCCURRED 0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
+#define SS_UNRECOVERED_READ_ERROR 0x031100
+#define SS_WRITE_ERROR 0x030c02
+#define SS_WRITE_PROTECTED 0x072700
+
+#define SK(x) ((uint8_t) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((uint8_t) ((x) >> 8))
+#define ASCQ(x) ((uint8_t) (x))
+
+/* Routines for unaligned data access */
+
+static uint16_t
+get_be16(uint8_t *buf)
+{
+ return ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
+}
+
+static uint32_t
+get_be32(uint8_t *buf)
+{
+ return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) |
+ ((uint32_t)buf[2] << 8) | ((uint32_t)buf[3]);
+}
+
+static void
+put_be16(uint8_t *buf, uint16_t val)
+{
+ buf[0] = val >> 8;
+ buf[1] = val;
+}
+
+static void
+put_be32(uint8_t *buf, uint32_t val)
+{
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val & 0xff;
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_verify
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_verify(struct ustorage_fs_softc *sc)
+{
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint32_t lba;
+ uint32_t vlen;
+ uint64_t file_offset;
+ uint64_t amount_left;
+
+ /*
+ * Get the starting Logical Block Address
+ */
+ lba = get_be32(&sc->sc_transfer.cmd_data[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the cache)
+ * but we don't implement it.
+ */
+ if ((sc->sc_transfer.cmd_data[1] & ~0x10) != 0) {
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ vlen = get_be16(&sc->sc_transfer.cmd_data[7]);
+ if (vlen == 0) {
+ goto done;
+ }
+ /* No default reply */
+
+ /* Prepare to carry out the file verify */
+ amount_left = vlen;
+ amount_left <<= 9;
+ file_offset = lba;
+ file_offset <<= 9;
+
+ /* Range check */
+ vlen += lba;
+
+ if ((vlen < lba) ||
+ (vlen > currlun->num_sectors) ||
+ (lba >= currlun->num_sectors)) {
+ currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return (1);
+ }
+ /* XXX TODO: verify that data is readable */
+done:
+ return (ustorage_fs_min_len(sc, 0, 0 - 1));
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_inquiry
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_inquiry(struct ustorage_fs_softc *sc)
+{
+ uint8_t *buf = sc->sc_transfer.data_ptr;
+ static const char vendor_id[] = "FreeBSD ";
+ static const char product_id[] = "File-Stor Gadget";
+
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+
+ if (!sc->sc_transfer.currlun) {
+ /* Unsupported LUNs are okay */
+ memset(buf, 0, 36);
+ buf[0] = 0x7f;
+ /* Unsupported, no device - type */
+ return (ustorage_fs_min_len(sc, 36, 0 - 1));
+ }
+ memset(buf, 0, 8);
+ /* Non - removable, direct - access device */
+ if (currlun->removable)
+ buf[1] = 0x80;
+ buf[2] = 2;
+ /* ANSI SCSI level 2 */
+ buf[3] = 2;
+ /* SCSI - 2 INQUIRY data format */
+ buf[4] = 31;
+ /* Additional length */
+ /* No special options */
+ /*
+ * NOTE: We are writing an extra zero here, that is not
+ * transferred to the peer:
+ */
+ snprintf(buf + 8, 28 + 1, "%-8s%-16s%04x", vendor_id, product_id,
+ USTORAGE_FS_RELEASE);
+ return (ustorage_fs_min_len(sc, 36, 0 - 1));
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_request_sense
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_request_sense(struct ustorage_fs_softc *sc)
+{
+ uint8_t *buf = sc->sc_transfer.data_ptr;
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint32_t sd;
+ uint32_t sdinfo;
+ uint8_t valid;
+
+ /*
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ *
+ * If a REQUEST SENSE command is received from an initiator
+ * with a pending unit attention condition (before the target
+ * generates the contingent allegiance condition), then the
+ * target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * FSG normally uses option a); enable this code to use option b).
+ */
+#if 0
+ if (currlun && currlun->unit_attention_data != SS_NO_SENSE) {
+ currlun->sense_data = currlun->unit_attention_data;
+ currlun->unit_attention_data = SS_NO_SENSE;
+ }
+#endif
+
+ if (!currlun) {
+ /* Unsupported LUNs are okay */
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = currlun->sense_data;
+ sdinfo = currlun->sense_data_info;
+ valid = currlun->info_valid << 7;
+ currlun->sense_data = SS_NO_SENSE;
+ currlun->sense_data_info = 0;
+ currlun->info_valid = 0;
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70;
+ /* Valid, current error */
+ buf[2] = SK(sd);
+ put_be32(&buf[3], sdinfo);
+ /* Sense information */
+ buf[7] = 18 - 8;
+ /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+ return (ustorage_fs_min_len(sc, 18, 0 - 1));
+}
+
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_read_capacity
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_read_capacity(struct ustorage_fs_softc *sc)
+{
+ uint8_t *buf = sc->sc_transfer.data_ptr;
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint32_t lba = get_be32(&sc->sc_transfer.cmd_data[2]);
+ uint8_t pmi = sc->sc_transfer.cmd_data[8];
+
+ /* Check the PMI and LBA fields */
+ if ((pmi > 1) || ((pmi == 0) && (lba != 0))) {
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ put_be32(&buf[0], currlun->num_sectors - 1);
+ /* Max logical block */
+ put_be32(&buf[4], 512);
+ /* Block length */
+ return (ustorage_fs_min_len(sc, 8, 0 - 1));
+}
+
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_mode_sense
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_mode_sense(struct ustorage_fs_softc *sc)
+{
+ uint8_t *buf = sc->sc_transfer.data_ptr;
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint8_t *buf0;
+ uint16_t len;
+ uint16_t limit;
+ uint8_t mscmnd = sc->sc_transfer.cmd_data[0];
+ uint8_t pc;
+ uint8_t page_code;
+ uint8_t changeable_values;
+ uint8_t all_pages;
+
+ buf0 = buf;
+
+ if ((sc->sc_transfer.cmd_data[1] & ~0x08) != 0) {
+ /* Mask away DBD */
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ pc = sc->sc_transfer.cmd_data[2] >> 6;
+ page_code = sc->sc_transfer.cmd_data[2] & 0x3f;
+ if (pc == 3) {
+ currlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ return (1);
+ }
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+
+ /*
+ * Write the mode parameter header. Fixed values are: default
+ * medium type, no cache control (DPOFUA), and no block descriptors.
+ * The only variable value is the WriteProtect bit. We will fill in
+ * the mode data length later.
+ */
+ memset(buf, 0, 8);
+ if (mscmnd == SC_MODE_SENSE_6) {
+ buf[2] = (currlun->read_only ? 0x80 : 0x00);
+ /* WP, DPOFUA */
+ buf += 4;
+ limit = 255;
+ } else {
+ /* SC_MODE_SENSE_10 */
+ buf[3] = (currlun->read_only ? 0x80 : 0x00);
+ /* WP, DPOFUA */
+ buf += 8;
+ limit = 65535;
+ /* Should really be mod_data.buflen */
+ }
+
+ /* No block descriptors */
+
+ /*
+ * The mode pages, in numerical order.
+ */
+ if ((page_code == 0x08) || all_pages) {
+ buf[0] = 0x08;
+ /* Page code */
+ buf[1] = 10;
+ /* Page length */
+ memset(buf + 2, 0, 10);
+ /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04;
+ /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_be16(&buf[4], 0xffff);
+ /* Don 't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_be16(&buf[8], 0xffff);
+ /* Maximum prefetch */
+ put_be16(&buf[10], 0xffff);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+ /*
+ * Check that a valid page was requested and the mode data length
+ * isn't too long.
+ */
+ len = buf - buf0;
+ if (len > limit) {
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ /* Store the mode data length */
+ if (mscmnd == SC_MODE_SENSE_6)
+ buf0[0] = len - 1;
+ else
+ put_be16(buf0, len - 2);
+ return (ustorage_fs_min_len(sc, len, 0 - 1));
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_start_stop
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_start_stop(struct ustorage_fs_softc *sc)
+{
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint8_t loej;
+ uint8_t start;
+ uint8_t immed;
+
+ if (!currlun->removable) {
+ currlun->sense_data = SS_INVALID_COMMAND;
+ return (1);
+ }
+ immed = sc->sc_transfer.cmd_data[1] & 0x01;
+ loej = sc->sc_transfer.cmd_data[4] & 0x02;
+ start = sc->sc_transfer.cmd_data[4] & 0x01;
+
+ if (immed || loej || start) {
+ /* compile fix */
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_prevent_allow
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_prevent_allow(struct ustorage_fs_softc *sc)
+{
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint8_t prevent;
+
+ if (!currlun->removable) {
+ currlun->sense_data = SS_INVALID_COMMAND;
+ return (1);
+ }
+ prevent = sc->sc_transfer.cmd_data[4] & 0x01;
+ if ((sc->sc_transfer.cmd_data[4] & ~0x01) != 0) {
+ /* Mask away Prevent */
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ if (currlun->prevent_medium_removal && !prevent) {
+ //fsync_sub(currlun);
+ }
+ currlun->prevent_medium_removal = prevent;
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_read_format_capacities
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_read_format_capacities(struct ustorage_fs_softc *sc)
+{
+ uint8_t *buf = sc->sc_transfer.data_ptr;
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8;
+ /* Only the Current / Maximum Capacity Descriptor */
+ buf += 4;
+
+ put_be32(&buf[0], currlun->num_sectors);
+ /* Number of blocks */
+ put_be32(&buf[4], 512);
+ /* Block length */
+ buf[4] = 0x02;
+ /* Current capacity */
+ return (ustorage_fs_min_len(sc, 12, 0 - 1));
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_mode_select
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_mode_select(struct ustorage_fs_softc *sc)
+{
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+
+ /* We don't support MODE SELECT */
+ currlun->sense_data = SS_INVALID_COMMAND;
+ return (1);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_synchronize_cache
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_synchronize_cache(struct ustorage_fs_softc *sc)
+{
+#if 0
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint8_t rc;
+
+ /*
+ * We ignore the requested LBA and write out all dirty data buffers.
+ */
+ rc = 0;
+ if (rc) {
+ currlun->sense_data = SS_WRITE_ERROR;
+ }
+#endif
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_read - read data from disk
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_read(struct ustorage_fs_softc *sc)
+{
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint64_t file_offset;
+ uint32_t lba;
+ uint32_t len;
+
+ /*
+ * Get the starting Logical Block Address and check that it's not
+ * too big
+ */
+ if (sc->sc_transfer.cmd_data[0] == SC_READ_6) {
+ lba = (sc->sc_transfer.cmd_data[1] << 16) |
+ get_be16(&sc->sc_transfer.cmd_data[2]);
+ } else {
+ lba = get_be32(&sc->sc_transfer.cmd_data[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = don't read from the
+ * cache), but we don't implement them.
+ */
+ if ((sc->sc_transfer.cmd_data[1] & ~0x18) != 0) {
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ }
+ len = sc->sc_transfer.data_rem >> 9;
+ len += lba;
+
+ if ((len < lba) ||
+ (len > currlun->num_sectors) ||
+ (lba >= currlun->num_sectors)) {
+ currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return (1);
+ }
+ file_offset = lba;
+ file_offset <<= 9;
+
+ sc->sc_transfer.data_ptr =
+ USB_ADD_BYTES(currlun->memory_image, (uint32_t)file_offset);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_write - write data to disk
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_write(struct ustorage_fs_softc *sc)
+{
+ struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun;
+ uint64_t file_offset;
+ uint32_t lba;
+ uint32_t len;
+
+ if (currlun->read_only) {
+ currlun->sense_data = SS_WRITE_PROTECTED;
+ return (1);
+ }
+ /* XXX clear SYNC */
+
+ /*
+ * Get the starting Logical Block Address and check that it's not
+ * too big.
+ */
+ if (sc->sc_transfer.cmd_data[0] == SC_WRITE_6)
+ lba = (sc->sc_transfer.cmd_data[1] << 16) |
+ get_be16(&sc->sc_transfer.cmd_data[2]);
+ else {
+ lba = get_be32(&sc->sc_transfer.cmd_data[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = write directly to the
+ * medium). We don't implement DPO; we implement FUA by
+ * performing synchronous output.
+ */
+ if ((sc->sc_transfer.cmd_data[1] & ~0x18) != 0) {
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return (1);
+ }
+ if (sc->sc_transfer.cmd_data[1] & 0x08) {
+ /* FUA */
+ /* XXX set SYNC flag here */
+ }
+ }
+
+ len = sc->sc_transfer.data_rem >> 9;
+ len += lba;
+
+ if ((len < lba) ||
+ (len > currlun->num_sectors) ||
+ (lba >= currlun->num_sectors)) {
+ currlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return (1);
+ }
+ file_offset = lba;
+ file_offset <<= 9;
+
+ sc->sc_transfer.data_ptr =
+ USB_ADD_BYTES(currlun->memory_image, (uint32_t)file_offset);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_min_len
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_min_len(struct ustorage_fs_softc *sc, uint32_t len, uint32_t mask)
+{
+ if (len != sc->sc_transfer.data_rem) {
+
+ if (sc->sc_transfer.cbw_dir == DIR_READ) {
+ /*
+ * there must be something wrong about this SCSI
+ * command
+ */
+ sc->sc_csw.bCSWStatus = CSWSTATUS_PHASE;
+ return (1);
+ }
+ /* compute the minimum length */
+
+ if (sc->sc_transfer.data_rem > len) {
+ /* data ends prematurely */
+ sc->sc_transfer.data_rem = len;
+ sc->sc_transfer.data_short = 1;
+ }
+ /* check length alignment */
+
+ if (sc->sc_transfer.data_rem & ~mask) {
+ /* data ends prematurely */
+ sc->sc_transfer.data_rem &= mask;
+ sc->sc_transfer.data_short = 1;
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_check_cmd - check command routine
+ *
+ * Check whether the command is properly formed and whether its data
+ * size and direction agree with the values we already have.
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_check_cmd(struct ustorage_fs_softc *sc, uint8_t min_cmd_size,
+ uint16_t mask, uint8_t needs_medium)
+{
+ struct ustorage_fs_lun *currlun;
+ uint8_t lun = (sc->sc_transfer.cmd_data[1] >> 5);
+ uint8_t i;
+
+ /* Verify the length of the command itself */
+ if (min_cmd_size > sc->sc_transfer.cmd_len) {
+ DPRINTF("%u > %u\n",
+ min_cmd_size, sc->sc_transfer.cmd_len);
+ sc->sc_csw.bCSWStatus = CSWSTATUS_PHASE;
+ return (1);
+ }
+ /* Mask away the LUN */
+ sc->sc_transfer.cmd_data[1] &= 0x1f;
+
+ /* Check if LUN is correct */
+ if (lun != sc->sc_transfer.lun) {
+
+ }
+ /* Check the LUN */
+ if (sc->sc_transfer.lun <= sc->sc_last_lun) {
+ sc->sc_transfer.currlun = currlun =
+ sc->sc_lun + sc->sc_transfer.lun;
+ if (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE) {
+ currlun->sense_data = SS_NO_SENSE;
+ currlun->sense_data_info = 0;
+ currlun->info_valid = 0;
+ }
+ /*
+ * If a unit attention condition exists, only INQUIRY
+ * and REQUEST SENSE commands are allowed. Anything
+ * else must fail!
+ */
+ if ((currlun->unit_attention_data != SS_NO_SENSE) &&
+ (sc->sc_transfer.cmd_data[0] != SC_INQUIRY) &&
+ (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE)) {
+ currlun->sense_data = currlun->unit_attention_data;
+ currlun->unit_attention_data = SS_NO_SENSE;
+ return (1);
+ }
+ } else {
+ sc->sc_transfer.currlun = currlun = NULL;
+
+ /*
+ * INQUIRY and REQUEST SENSE commands are explicitly allowed
+ * to use unsupported LUNs; all others may not.
+ */
+ if ((sc->sc_transfer.cmd_data[0] != SC_INQUIRY) &&
+ (sc->sc_transfer.cmd_data[0] != SC_REQUEST_SENSE)) {
+ return (1);
+ }
+ }
+
+ /*
+ * Check that only command bytes listed in the mask are
+ * non-zero.
+ */
+ for (i = 0; i != min_cmd_size; i++) {
+ if (sc->sc_transfer.cmd_data[i] && !(mask & (1 << i))) {
+ if (currlun) {
+ currlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ }
+ return (1);
+ }
+ }
+
+ /*
+ * If the medium isn't mounted and the command needs to access
+ * it, return an error.
+ */
+ if (currlun && (!currlun->memory_image) && needs_medium) {
+ currlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+ return (1);
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * ustorage_fs_do_cmd - do command
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static uint8_t
+ustorage_fs_do_cmd(struct ustorage_fs_softc *sc)
+{
+ uint8_t error = 1;
+ uint8_t i;
+
+ /* set default data transfer pointer */
+ sc->sc_transfer.data_ptr = sc->sc_qdata;
+
+ DPRINTF("cmd_data[0]=0x%02x, data_rem=0x%08x\n",
+ sc->sc_transfer.cmd_data[0], sc->sc_transfer.data_rem);
+
+ switch (sc->sc_transfer.cmd_data[0]) {
+ case SC_INQUIRY:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (1 << 4) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_inquiry(sc);
+
+ break;
+
+ case SC_MODE_SELECT_6:
+ sc->sc_transfer.cmd_dir = DIR_READ;
+ error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (1 << 1) | (1 << 4) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_mode_select(sc);
+
+ break;
+
+ case SC_MODE_SELECT_10:
+ sc->sc_transfer.cmd_dir = DIR_READ;
+ error = ustorage_fs_min_len(sc,
+ get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (1 << 1) | (3 << 7) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_mode_select(sc);
+
+ break;
+
+ case SC_MODE_SENSE_6:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (1 << 1) | (1 << 2) | (1 << 4) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_mode_sense(sc);
+
+ break;
+
+ case SC_MODE_SENSE_10:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc,
+ get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (1 << 1) | (1 << 2) | (3 << 7) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_mode_sense(sc);
+
+ break;
+
+ case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
+ error = ustorage_fs_min_len(sc, 0, 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (1 << 4) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_prevent_allow(sc);
+
+ break;
+
+ case SC_READ_6:
+ i = sc->sc_transfer.cmd_data[4];
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc,
+ ((i == 0) ? 256 : i) << 9, 0 - (1 << 9));
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (7 << 1) | (1 << 4) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_read(sc);
+
+ break;
+
+ case SC_READ_10:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc,
+ get_be16(&sc->sc_transfer.cmd_data[7]) << 9, 0 - (1 << 9));
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (1 << 1) | (0xf << 2) | (3 << 7) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_read(sc);
+
+ break;
+
+ case SC_READ_12:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc,
+ get_be32(&sc->sc_transfer.cmd_data[6]) << 9, 0 - (1 << 9));
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 12,
+ (1 << 1) | (0xf << 2) | (0xf << 6) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_read(sc);
+
+ break;
+
+ case SC_READ_CAPACITY:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_check_cmd(sc, 10,
+ (0xf << 2) | (1 << 8) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_read_capacity(sc);
+
+ break;
+
+ case SC_READ_FORMAT_CAPACITIES:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc,
+ get_be16(&sc->sc_transfer.cmd_data[7]), 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (3 << 7) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_read_format_capacities(sc);
+
+ break;
+
+ case SC_REQUEST_SENSE:
+ sc->sc_transfer.cmd_dir = DIR_WRITE;
+ error = ustorage_fs_min_len(sc, sc->sc_transfer.cmd_data[4], 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (1 << 4) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_request_sense(sc);
+
+ break;
+
+ case SC_START_STOP_UNIT:
+ error = ustorage_fs_min_len(sc, 0, 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (1 << 1) | (1 << 4) | 1, 0);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_start_stop(sc);
+
+ break;
+
+ case SC_SYNCHRONIZE_CACHE:
+ error = ustorage_fs_min_len(sc, 0, 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (0xf << 2) | (3 << 7) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_synchronize_cache(sc);
+
+ break;
+
+ case SC_TEST_UNIT_READY:
+ error = ustorage_fs_min_len(sc, 0, 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ 0 | 1, 1);
+ break;
+
+ /*
+ * Although optional, this command is used by MS-Windows.
+ * We support a minimal version: BytChk must be 0.
+ */
+ case SC_VERIFY:
+ error = ustorage_fs_min_len(sc, 0, 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (1 << 1) | (0xf << 2) | (3 << 7) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_verify(sc);
+
+ break;
+
+ case SC_WRITE_6:
+ i = sc->sc_transfer.cmd_data[4];
+ sc->sc_transfer.cmd_dir = DIR_READ;
+ error = ustorage_fs_min_len(sc,
+ ((i == 0) ? 256 : i) << 9, 0 - (1 << 9));
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 6,
+ (7 << 1) | (1 << 4) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_write(sc);
+
+ break;
+
+ case SC_WRITE_10:
+ sc->sc_transfer.cmd_dir = DIR_READ;
+ error = ustorage_fs_min_len(sc,
+ get_be16(&sc->sc_transfer.cmd_data[7]) << 9, 0 - (1 << 9));
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 10,
+ (1 << 1) | (0xf << 2) | (3 << 7) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_write(sc);
+
+ break;
+
+ case SC_WRITE_12:
+ sc->sc_transfer.cmd_dir = DIR_READ;
+ error = ustorage_fs_min_len(sc,
+ get_be32(&sc->sc_transfer.cmd_data[6]) << 9, 0 - (1 << 9));
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, 12,
+ (1 << 1) | (0xf << 2) | (0xf << 6) | 1, 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_write(sc);
+
+ break;
+
+ /*
+ * Some mandatory commands that we recognize but don't
+ * implement. They don't mean much in this setting.
+ * It's left as an exercise for anyone interested to
+ * implement RESERVE and RELEASE in terms of Posix
+ * locks.
+ */
+ case SC_FORMAT_UNIT:
+ case SC_RELEASE:
+ case SC_RESERVE:
+ case SC_SEND_DIAGNOSTIC:
+ /* Fallthrough */
+
+ default:
+ error = ustorage_fs_min_len(sc, 0, 0 - 1);
+ if (error) {
+ break;
+ }
+ error = ustorage_fs_check_cmd(sc, sc->sc_transfer.cmd_len,
+ 0xff, 0);
+ if (error) {
+ break;
+ }
+ sc->sc_transfer.currlun->sense_data =
+ SS_INVALID_COMMAND;
+ error = 1;
+
+ break;
+ }
+ return (error);
+}
diff --git a/sys/dev/usb/template/usb_template.c b/sys/dev/usb/template/usb_template.c
new file mode 100644
index 0000000..31c853a
--- /dev/null
+++ b/sys/dev/usb/template/usb_template.c
@@ -0,0 +1,1312 @@
+/* $FreeBSD$ */
+/*-
+ * 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.
+ */
+
+/*
+ * This file contains sub-routines to build up USB descriptors from
+ * USB templates.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_dynamic.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#include <dev/usb/template/usb_template.h>
+
+MODULE_DEPEND(usb_template, usb, 1, 1, 1);
+MODULE_VERSION(usb_template, 1);
+
+/* function prototypes */
+
+static void usb2_make_raw_desc(struct usb2_temp_setup *, const uint8_t *);
+static void usb2_make_endpoint_desc(struct usb2_temp_setup *,
+ const struct usb2_temp_endpoint_desc *);
+static void usb2_make_interface_desc(struct usb2_temp_setup *,
+ const struct usb2_temp_interface_desc *);
+static void usb2_make_config_desc(struct usb2_temp_setup *,
+ const struct usb2_temp_config_desc *);
+static void usb2_make_device_desc(struct usb2_temp_setup *,
+ const struct usb2_temp_device_desc *);
+static uint8_t usb2_hw_ep_match(const struct usb2_hw_ep_profile *, uint8_t,
+ uint8_t);
+static uint8_t usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *,
+ struct usb2_hw_ep_scratch_sub *, uint8_t);
+static uint8_t usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *, uint8_t,
+ uint8_t);
+static usb2_error_t usb2_hw_ep_resolve(struct usb2_device *,
+ struct usb2_descriptor *);
+static const struct usb2_temp_device_desc *usb2_temp_get_tdd(struct usb2_device *);
+static void *usb2_temp_get_device_desc(struct usb2_device *);
+static void *usb2_temp_get_qualifier_desc(struct usb2_device *);
+static void *usb2_temp_get_config_desc(struct usb2_device *, uint16_t *,
+ uint8_t);
+static const void *usb2_temp_get_string_desc(struct usb2_device *, uint16_t,
+ uint8_t);
+static const void *usb2_temp_get_vendor_desc(struct usb2_device *,
+ const struct usb2_device_request *);
+static const void *usb2_temp_get_hub_desc(struct usb2_device *);
+static void usb2_temp_get_desc(struct usb2_device *,
+ struct usb2_device_request *, const void **, uint16_t *);
+static usb2_error_t usb2_temp_setup(struct usb2_device *,
+ const struct usb2_temp_device_desc *);
+static void usb2_temp_unsetup(struct usb2_device *);
+static usb2_error_t usb2_temp_setup_by_index(struct usb2_device *,
+ uint16_t index);
+static void usb2_temp_init(void *);
+
+/*------------------------------------------------------------------------*
+ * usb2_make_raw_desc
+ *
+ * This function will insert a raw USB descriptor into the generated
+ * USB configuration.
+ *------------------------------------------------------------------------*/
+static void
+usb2_make_raw_desc(struct usb2_temp_setup *temp,
+ const uint8_t *raw)
+{
+ void *dst;
+ uint8_t len;
+
+ /*
+ * The first byte of any USB descriptor gives the length.
+ */
+ if (raw) {
+ len = raw[0];
+ if (temp->buf) {
+ dst = USB_ADD_BYTES(temp->buf, temp->size);
+ bcopy(raw, dst, len);
+
+ /* check if we have got a CDC union descriptor */
+
+ if ((raw[0] >= sizeof(struct usb2_cdc_union_descriptor)) &&
+ (raw[1] == UDESC_CS_INTERFACE) &&
+ (raw[2] == UDESCSUB_CDC_UNION)) {
+ struct usb2_cdc_union_descriptor *ud = (void *)dst;
+
+ /* update the interface numbers */
+
+ ud->bMasterInterface +=
+ temp->bInterfaceNumber;
+ ud->bSlaveInterface[0] +=
+ temp->bInterfaceNumber;
+ }
+ }
+ temp->size += len;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_make_endpoint_desc
+ *
+ * This function will generate an USB endpoint descriptor from the
+ * given USB template endpoint descriptor, which will be inserted into
+ * the USB configuration.
+ *------------------------------------------------------------------------*/
+static void
+usb2_make_endpoint_desc(struct usb2_temp_setup *temp,
+ const struct usb2_temp_endpoint_desc *ted)
+{
+ struct usb2_endpoint_descriptor *ed;
+ const void **rd;
+ uint16_t old_size;
+ uint16_t mps;
+ uint8_t ea = 0; /* Endpoint Address */
+ uint8_t et = 0; /* Endpiont Type */
+
+ /* Reserve memory */
+ old_size = temp->size;
+ temp->size += sizeof(*ed);
+
+ /* Scan all Raw Descriptors first */
+
+ rd = ted->ppRawDesc;
+ if (rd) {
+ while (*rd) {
+ usb2_make_raw_desc(temp, *rd);
+ rd++;
+ }
+ }
+ if (ted->pPacketSize == NULL) {
+ /* not initialized */
+ temp->err = USB_ERR_INVAL;
+ return;
+ }
+ mps = ted->pPacketSize->mps[temp->usb2_speed];
+ if (mps == 0) {
+ /* not initialized */
+ temp->err = USB_ERR_INVAL;
+ return;
+ } else if (mps == UE_ZERO_MPS) {
+ /* escape for Zero Max Packet Size */
+ mps = 0;
+ }
+ ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT));
+ et = (ted->bmAttributes & UE_XFERTYPE);
+
+ /*
+ * Fill out the real USB endpoint descriptor
+ * in case there is a buffer present:
+ */
+ if (temp->buf) {
+ ed = USB_ADD_BYTES(temp->buf, old_size);
+ ed->bLength = sizeof(*ed);
+ ed->bDescriptorType = UDESC_ENDPOINT;
+ ed->bEndpointAddress = ea;
+ ed->bmAttributes = ted->bmAttributes;
+ USETW(ed->wMaxPacketSize, mps);
+
+ /* setup bInterval parameter */
+
+ if (ted->pIntervals &&
+ ted->pIntervals->bInterval[temp->usb2_speed]) {
+ ed->bInterval =
+ ted->pIntervals->bInterval[temp->usb2_speed];
+ } else {
+ switch (et) {
+ case UE_BULK:
+ case UE_CONTROL:
+ ed->bInterval = 0; /* not used */
+ break;
+ case UE_INTERRUPT:
+ switch (temp->usb2_speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ ed->bInterval = 1; /* 1 ms */
+ break;
+ default:
+ ed->bInterval = 8; /* 8*125 us */
+ break;
+ }
+ break;
+ default: /* UE_ISOCHRONOUS */
+ switch (temp->usb2_speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ ed->bInterval = 1; /* 1 ms */
+ break;
+ default:
+ ed->bInterval = 1; /* 125 us */
+ break;
+ }
+ break;
+ }
+ }
+ }
+ temp->bNumEndpoints++;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_make_interface_desc
+ *
+ * This function will generate an USB interface descriptor from the
+ * given USB template interface descriptor, which will be inserted
+ * into the USB configuration.
+ *------------------------------------------------------------------------*/
+static void
+usb2_make_interface_desc(struct usb2_temp_setup *temp,
+ const struct usb2_temp_interface_desc *tid)
+{
+ struct usb2_interface_descriptor *id;
+ const struct usb2_temp_endpoint_desc **ted;
+ const void **rd;
+ uint16_t old_size;
+
+ /* Reserve memory */
+
+ old_size = temp->size;
+ temp->size += sizeof(*id);
+
+ /* Update interface and alternate interface numbers */
+
+ if (tid->isAltInterface == 0) {
+ temp->bAlternateSetting = 0;
+ temp->bInterfaceNumber++;
+ } else {
+ temp->bAlternateSetting++;
+ }
+
+ /* Scan all Raw Descriptors first */
+
+ rd = tid->ppRawDesc;
+
+ if (rd) {
+ while (*rd) {
+ usb2_make_raw_desc(temp, *rd);
+ rd++;
+ }
+ }
+ /* Reset some counters */
+
+ temp->bNumEndpoints = 0;
+
+ /* Scan all Endpoint Descriptors second */
+
+ ted = tid->ppEndpoints;
+ if (ted) {
+ while (*ted) {
+ usb2_make_endpoint_desc(temp, *ted);
+ ted++;
+ }
+ }
+ /*
+ * Fill out the real USB interface descriptor
+ * in case there is a buffer present:
+ */
+ if (temp->buf) {
+ id = USB_ADD_BYTES(temp->buf, old_size);
+ id->bLength = sizeof(*id);
+ id->bDescriptorType = UDESC_INTERFACE;
+ id->bInterfaceNumber = temp->bInterfaceNumber;
+ id->bAlternateSetting = temp->bAlternateSetting;
+ id->bNumEndpoints = temp->bNumEndpoints;
+ id->bInterfaceClass = tid->bInterfaceClass;
+ id->bInterfaceSubClass = tid->bInterfaceSubClass;
+ id->bInterfaceProtocol = tid->bInterfaceProtocol;
+ id->iInterface = tid->iInterface;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_make_config_desc
+ *
+ * This function will generate an USB config descriptor from the given
+ * USB template config descriptor, which will be inserted into the USB
+ * configuration.
+ *------------------------------------------------------------------------*/
+static void
+usb2_make_config_desc(struct usb2_temp_setup *temp,
+ const struct usb2_temp_config_desc *tcd)
+{
+ struct usb2_config_descriptor *cd;
+ const struct usb2_temp_interface_desc **tid;
+ uint16_t old_size;
+
+ /* Reserve memory */
+
+ old_size = temp->size;
+ temp->size += sizeof(*cd);
+
+ /* Reset some counters */
+
+ temp->bInterfaceNumber = 0 - 1;
+ temp->bAlternateSetting = 0;
+
+ /* Scan all the USB interfaces */
+
+ tid = tcd->ppIfaceDesc;
+ if (tid) {
+ while (*tid) {
+ usb2_make_interface_desc(temp, *tid);
+ tid++;
+ }
+ }
+ /*
+ * Fill out the real USB config descriptor
+ * in case there is a buffer present:
+ */
+ if (temp->buf) {
+ cd = USB_ADD_BYTES(temp->buf, old_size);
+
+ /* compute total size */
+ old_size = temp->size - old_size;
+
+ cd->bLength = sizeof(*cd);
+ cd->bDescriptorType = UDESC_CONFIG;
+ USETW(cd->wTotalLength, old_size);
+ cd->bNumInterface = temp->bInterfaceNumber + 1;
+ cd->bConfigurationValue = temp->bConfigurationValue;
+ cd->iConfiguration = tcd->iConfiguration;
+ cd->bmAttributes = tcd->bmAttributes;
+ cd->bMaxPower = tcd->bMaxPower;
+ cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED);
+
+ if (temp->self_powered) {
+ cd->bmAttributes |= UC_SELF_POWERED;
+ } else {
+ cd->bmAttributes &= ~UC_SELF_POWERED;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_make_device_desc
+ *
+ * This function will generate an USB device descriptor from the
+ * given USB template device descriptor.
+ *------------------------------------------------------------------------*/
+static void
+usb2_make_device_desc(struct usb2_temp_setup *temp,
+ const struct usb2_temp_device_desc *tdd)
+{
+ struct usb2_temp_data *utd;
+ const struct usb2_temp_config_desc **tcd;
+ uint16_t old_size;
+
+ /* Reserve memory */
+
+ old_size = temp->size;
+ temp->size += sizeof(*utd);
+
+ /* Scan all the USB configs */
+
+ temp->bConfigurationValue = 1;
+ tcd = tdd->ppConfigDesc;
+ if (tcd) {
+ while (*tcd) {
+ usb2_make_config_desc(temp, *tcd);
+ temp->bConfigurationValue++;
+ tcd++;
+ }
+ }
+ /*
+ * Fill out the real USB device descriptor
+ * in case there is a buffer present:
+ */
+
+ if (temp->buf) {
+ utd = USB_ADD_BYTES(temp->buf, old_size);
+
+ /* Store a pointer to our template device descriptor */
+ utd->tdd = tdd;
+
+ /* Fill out USB device descriptor */
+ utd->udd.bLength = sizeof(utd->udd);
+ utd->udd.bDescriptorType = UDESC_DEVICE;
+ utd->udd.bDeviceClass = tdd->bDeviceClass;
+ utd->udd.bDeviceSubClass = tdd->bDeviceSubClass;
+ utd->udd.bDeviceProtocol = tdd->bDeviceProtocol;
+ USETW(utd->udd.idVendor, tdd->idVendor);
+ USETW(utd->udd.idProduct, tdd->idProduct);
+ USETW(utd->udd.bcdDevice, tdd->bcdDevice);
+ utd->udd.iManufacturer = tdd->iManufacturer;
+ utd->udd.iProduct = tdd->iProduct;
+ utd->udd.iSerialNumber = tdd->iSerialNumber;
+ utd->udd.bNumConfigurations = temp->bConfigurationValue - 1;
+
+ /*
+ * Fill out the USB device qualifier. Pretend that we
+ * don't support any other speeds by setting
+ * "bNumConfigurations" equal to zero. That saves us
+ * generating an extra set of configuration
+ * descriptors.
+ */
+ utd->udq.bLength = sizeof(utd->udq);
+ utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER;
+ utd->udq.bDeviceClass = tdd->bDeviceClass;
+ utd->udq.bDeviceSubClass = tdd->bDeviceSubClass;
+ utd->udq.bDeviceProtocol = tdd->bDeviceProtocol;
+ utd->udq.bNumConfigurations = 0;
+ USETW(utd->udq.bcdUSB, 0x0200);
+ utd->udq.bMaxPacketSize0 = 0;
+
+ switch (temp->usb2_speed) {
+ case USB_SPEED_LOW:
+ USETW(utd->udd.bcdUSB, 0x0110);
+ utd->udd.bMaxPacketSize = 8;
+ break;
+ case USB_SPEED_FULL:
+ USETW(utd->udd.bcdUSB, 0x0110);
+ utd->udd.bMaxPacketSize = 32;
+ break;
+ case USB_SPEED_HIGH:
+ USETW(utd->udd.bcdUSB, 0x0200);
+ utd->udd.bMaxPacketSize = 64;
+ break;
+ case USB_SPEED_VARIABLE:
+ USETW(utd->udd.bcdUSB, 0x0250);
+ utd->udd.bMaxPacketSize = 255; /* 512 bytes */
+ break;
+ default:
+ temp->err = USB_ERR_INVAL;
+ break;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_hw_ep_match
+ *
+ * Return values:
+ * 0: The endpoint profile does not match the criterias
+ * Else: The endpoint profile matches the criterias
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_hw_ep_match(const struct usb2_hw_ep_profile *pf,
+ uint8_t ep_type, uint8_t ep_dir_in)
+{
+ if (ep_type == UE_CONTROL) {
+ /* special */
+ return (pf->support_control);
+ }
+ if ((pf->support_in && ep_dir_in) ||
+ (pf->support_out && !ep_dir_in)) {
+ if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) ||
+ (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) ||
+ (pf->support_bulk && (ep_type == UE_BULK))) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_hw_ep_find_match
+ *
+ * This function is used to find the best matching endpoint profile
+ * for and endpoint belonging to an USB descriptor.
+ *
+ * Return values:
+ * 0: Success. Got a match.
+ * Else: Failure. No match.
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *ues,
+ struct usb2_hw_ep_scratch_sub *ep, uint8_t is_simplex)
+{
+ const struct usb2_hw_ep_profile *pf;
+ uint16_t distance;
+ uint16_t temp;
+ uint16_t max_frame_size;
+ uint8_t n;
+ uint8_t best_n;
+ uint8_t dir_in;
+ uint8_t dir_out;
+
+ distance = 0xFFFF;
+ best_n = 0;
+
+ if ((!ep->needs_in) && (!ep->needs_out)) {
+ return (0); /* we are done */
+ }
+ if (ep->needs_ep_type == UE_CONTROL) {
+ dir_in = 1;
+ dir_out = 1;
+ } else {
+ if (ep->needs_in) {
+ dir_in = 1;
+ dir_out = 0;
+ } else {
+ dir_in = 0;
+ dir_out = 1;
+ }
+ }
+
+ for (n = 1; n != (USB_EP_MAX / 2); n++) {
+
+ /* get HW endpoint profile */
+ (ues->methods->get_hw_ep_profile) (ues->udev, &pf, n);
+ if (pf == NULL) {
+ /* end of profiles */
+ break;
+ }
+ /* check if IN-endpoint is reserved */
+ if (dir_in || pf->is_simplex) {
+ if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) {
+ /* mismatch */
+ continue;
+ }
+ }
+ /* check if OUT-endpoint is reserved */
+ if (dir_out || pf->is_simplex) {
+ if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) {
+ /* mismatch */
+ continue;
+ }
+ }
+ /* check simplex */
+ if (pf->is_simplex == is_simplex) {
+ /* mismatch */
+ continue;
+ }
+ /* check if HW endpoint matches */
+ if (!usb2_hw_ep_match(pf, ep->needs_ep_type, dir_in)) {
+ /* mismatch */
+ continue;
+ }
+ /* get maximum frame size */
+ if (dir_in)
+ max_frame_size = pf->max_in_frame_size;
+ else
+ max_frame_size = pf->max_out_frame_size;
+
+ /* check if we have a matching profile */
+ if (max_frame_size >= ep->max_frame_size) {
+ temp = (max_frame_size - ep->max_frame_size);
+ if (distance > temp) {
+ distance = temp;
+ best_n = n;
+ ep->pf = pf;
+ }
+ }
+ }
+
+ /* see if we got a match */
+ if (best_n != 0) {
+ /* get the correct profile */
+ pf = ep->pf;
+
+ /* reserve IN-endpoint */
+ if (dir_in) {
+ ues->bmInAlloc[best_n / 8] |=
+ (1 << (best_n % 8));
+ ep->hw_endpoint_in = best_n | UE_DIR_IN;
+ ep->needs_in = 0;
+ }
+ /* reserve OUT-endpoint */
+ if (dir_out) {
+ ues->bmOutAlloc[best_n / 8] |=
+ (1 << (best_n % 8));
+ ep->hw_endpoint_out = best_n | UE_DIR_OUT;
+ ep->needs_out = 0;
+ }
+ return (0); /* got a match */
+ }
+ return (1); /* failure */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_hw_ep_get_needs
+ *
+ * This function will figure out the type and number of endpoints
+ * which are needed for an USB configuration.
+ *
+ * Return values:
+ * 0: Success.
+ * Else: Failure.
+ *------------------------------------------------------------------------*/
+static uint8_t
+usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *ues,
+ uint8_t ep_type, uint8_t is_complete)
+{
+ const struct usb2_hw_ep_profile *pf;
+ struct usb2_hw_ep_scratch_sub *ep_iface;
+ struct usb2_hw_ep_scratch_sub *ep_curr;
+ struct usb2_hw_ep_scratch_sub *ep_max;
+ struct usb2_hw_ep_scratch_sub *ep_end;
+ struct usb2_descriptor *desc;
+ struct usb2_interface_descriptor *id;
+ struct usb2_endpoint_descriptor *ed;
+ uint16_t wMaxPacketSize;
+ uint16_t temp;
+ uint8_t speed;
+ uint8_t ep_no;
+
+ ep_iface = ues->ep_max;
+ ep_curr = ues->ep_max;
+ ep_end = ues->ep + USB_EP_MAX;
+ ep_max = ues->ep_max;
+ desc = NULL;
+ speed = usb2_get_speed(ues->udev);
+
+repeat:
+
+ while ((desc = usb2_desc_foreach(ues->cd, desc))) {
+
+ if ((desc->bDescriptorType == UDESC_INTERFACE) &&
+ (desc->bLength >= sizeof(*id))) {
+
+ id = (void *)desc;
+
+ if (id->bAlternateSetting == 0) {
+ /* going forward */
+ ep_iface = ep_max;
+ } else {
+ /* reset */
+ ep_curr = ep_iface;
+ }
+ }
+ if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
+ (desc->bLength >= sizeof(*ed))) {
+
+ ed = (void *)desc;
+
+ goto handle_endpoint_desc;
+ }
+ }
+ ues->ep_max = ep_max;
+ return (0);
+
+handle_endpoint_desc:
+ temp = (ed->bmAttributes & UE_XFERTYPE);
+
+ if (temp == ep_type) {
+
+ if (ep_curr == ep_end) {
+ /* too many endpoints */
+ return (1); /* failure */
+ }
+ wMaxPacketSize = UGETW(ed->wMaxPacketSize);
+ if ((wMaxPacketSize & 0xF800) &&
+ (speed == USB_SPEED_HIGH)) {
+ /* handle packet multiplier */
+ temp = (wMaxPacketSize >> 11) & 3;
+ wMaxPacketSize &= 0x7FF;
+ if (temp == 1) {
+ wMaxPacketSize *= 2;
+ } else {
+ wMaxPacketSize *= 3;
+ }
+ }
+ /*
+ * Check if we have a fixed endpoint number, else the
+ * endpoint number is allocated dynamically:
+ */
+ ep_no = (ed->bEndpointAddress & UE_ADDR);
+ if (ep_no != 0) {
+
+ /* get HW endpoint profile */
+ (ues->methods->get_hw_ep_profile)
+ (ues->udev, &pf, ep_no);
+ if (pf == NULL) {
+ /* HW profile does not exist - failure */
+ DPRINTFN(0, "Endpoint profile %u "
+ "does not exist\n", ep_no);
+ return (1);
+ }
+ /* reserve fixed endpoint number */
+ if (ep_type == UE_CONTROL) {
+ ues->bmInAlloc[ep_no / 8] |=
+ (1 << (ep_no % 8));
+ ues->bmOutAlloc[ep_no / 8] |=
+ (1 << (ep_no % 8));
+ if ((pf->max_in_frame_size < wMaxPacketSize) ||
+ (pf->max_out_frame_size < wMaxPacketSize)) {
+ DPRINTFN(0, "Endpoint profile %u "
+ "has too small buffer!\n", ep_no);
+ return (1);
+ }
+ } else if (ed->bEndpointAddress & UE_DIR_IN) {
+ ues->bmInAlloc[ep_no / 8] |=
+ (1 << (ep_no % 8));
+ if (pf->max_in_frame_size < wMaxPacketSize) {
+ DPRINTFN(0, "Endpoint profile %u "
+ "has too small buffer!\n", ep_no);
+ return (1);
+ }
+ } else {
+ ues->bmOutAlloc[ep_no / 8] |=
+ (1 << (ep_no % 8));
+ if (pf->max_out_frame_size < wMaxPacketSize) {
+ DPRINTFN(0, "Endpoint profile %u "
+ "has too small buffer!\n", ep_no);
+ return (1);
+ }
+ }
+ } else if (is_complete) {
+
+ /* check if we have enough buffer space */
+ if (wMaxPacketSize >
+ ep_curr->max_frame_size) {
+ return (1); /* failure */
+ }
+ if (ed->bEndpointAddress & UE_DIR_IN) {
+ ed->bEndpointAddress =
+ ep_curr->hw_endpoint_in;
+ } else {
+ ed->bEndpointAddress =
+ ep_curr->hw_endpoint_out;
+ }
+
+ } else {
+
+ /* compute the maximum frame size */
+ if (ep_curr->max_frame_size < wMaxPacketSize) {
+ ep_curr->max_frame_size = wMaxPacketSize;
+ }
+ if (temp == UE_CONTROL) {
+ ep_curr->needs_in = 1;
+ ep_curr->needs_out = 1;
+ } else {
+ if (ed->bEndpointAddress & UE_DIR_IN) {
+ ep_curr->needs_in = 1;
+ } else {
+ ep_curr->needs_out = 1;
+ }
+ }
+ ep_curr->needs_ep_type = ep_type;
+ }
+
+ ep_curr++;
+ if (ep_max < ep_curr) {
+ ep_max = ep_curr;
+ }
+ }
+ goto repeat;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_hw_ep_resolve
+ *
+ * This function will try to resolve endpoint requirements by the
+ * given endpoint profiles that the USB hardware reports.
+ *
+ * Return values:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_hw_ep_resolve(struct usb2_device *udev,
+ struct usb2_descriptor *desc)
+{
+ struct usb2_hw_ep_scratch *ues;
+ struct usb2_hw_ep_scratch_sub *ep;
+ const struct usb2_hw_ep_profile *pf;
+ struct usb2_bus_methods *methods;
+ struct usb2_device_descriptor *dd;
+ uint16_t mps;
+
+ if (desc == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ /* get bus methods */
+ methods = udev->bus->methods;
+
+ if (methods->get_hw_ep_profile == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ if (desc->bDescriptorType == UDESC_DEVICE) {
+
+ if (desc->bLength < sizeof(*dd)) {
+ return (USB_ERR_INVAL);
+ }
+ dd = (void *)desc;
+
+ /* get HW control endpoint 0 profile */
+ (methods->get_hw_ep_profile) (udev, &pf, 0);
+ if (pf == NULL) {
+ return (USB_ERR_INVAL);
+ }
+ if (!usb2_hw_ep_match(pf, UE_CONTROL, 0)) {
+ DPRINTFN(0, "Endpoint 0 does not "
+ "support control\n");
+ return (USB_ERR_INVAL);
+ }
+ mps = dd->bMaxPacketSize;
+
+ if (udev->speed == USB_SPEED_FULL) {
+ /*
+ * We can optionally choose another packet size !
+ */
+ while (1) {
+ /* check if "mps" is ok */
+ if (pf->max_in_frame_size >= mps) {
+ break;
+ }
+ /* reduce maximum packet size */
+ mps /= 2;
+
+ /* check if "mps" is too small */
+ if (mps < 8) {
+ return (USB_ERR_INVAL);
+ }
+ }
+
+ dd->bMaxPacketSize = mps;
+
+ } else {
+ /* We only have one choice */
+ if (mps == 255) {
+ mps = 512;
+ }
+ /* Check if we support the specified wMaxPacketSize */
+ if (pf->max_in_frame_size < mps) {
+ return (USB_ERR_INVAL);
+ }
+ }
+ return (0); /* success */
+ }
+ if (desc->bDescriptorType != UDESC_CONFIG) {
+ return (USB_ERR_INVAL);
+ }
+ if (desc->bLength < sizeof(*(ues->cd))) {
+ return (USB_ERR_INVAL);
+ }
+ ues = udev->bus->scratch[0].hw_ep_scratch;
+
+ bzero(ues, sizeof(*ues));
+
+ ues->ep_max = ues->ep;
+ ues->cd = (void *)desc;
+ ues->methods = methods;
+ ues->udev = udev;
+
+ /* Get all the endpoints we need */
+
+ if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) ||
+ usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 0) ||
+ usb2_hw_ep_get_needs(ues, UE_CONTROL, 0) ||
+ usb2_hw_ep_get_needs(ues, UE_BULK, 0)) {
+ DPRINTFN(0, "Could not get needs\n");
+ return (USB_ERR_INVAL);
+ }
+ for (ep = ues->ep; ep != ues->ep_max; ep++) {
+
+ while (ep->needs_in || ep->needs_out) {
+
+ /*
+ * First try to use a simplex endpoint.
+ * Then try to use a duplex endpoint.
+ */
+ if (usb2_hw_ep_find_match(ues, ep, 1) &&
+ usb2_hw_ep_find_match(ues, ep, 0)) {
+ DPRINTFN(0, "Could not find match\n");
+ return (USB_ERR_INVAL);
+ }
+ }
+ }
+
+ ues->ep_max = ues->ep;
+
+ /* Update all endpoint addresses */
+
+ if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) ||
+ usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 1) ||
+ usb2_hw_ep_get_needs(ues, UE_CONTROL, 1) ||
+ usb2_hw_ep_get_needs(ues, UE_BULK, 1)) {
+ DPRINTFN(0, "Could not update endpoint address\n");
+ return (USB_ERR_INVAL);
+ }
+ return (0); /* success */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_tdd
+ *
+ * Returns:
+ * NULL: No USB template device descriptor found.
+ * Else: Pointer to the USB template device descriptor.
+ *------------------------------------------------------------------------*/
+static const struct usb2_temp_device_desc *
+usb2_temp_get_tdd(struct usb2_device *udev)
+{
+ if (udev->usb2_template_ptr == NULL) {
+ return (NULL);
+ }
+ return (udev->usb2_template_ptr->tdd);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_device_desc
+ *
+ * Returns:
+ * NULL: No USB device descriptor found.
+ * Else: Pointer to USB device descriptor.
+ *------------------------------------------------------------------------*/
+static void *
+usb2_temp_get_device_desc(struct usb2_device *udev)
+{
+ struct usb2_device_descriptor *dd;
+
+ if (udev->usb2_template_ptr == NULL) {
+ return (NULL);
+ }
+ dd = &udev->usb2_template_ptr->udd;
+ if (dd->bDescriptorType != UDESC_DEVICE) {
+ /* sanity check failed */
+ return (NULL);
+ }
+ return (dd);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_qualifier_desc
+ *
+ * Returns:
+ * NULL: No USB device_qualifier descriptor found.
+ * Else: Pointer to USB device_qualifier descriptor.
+ *------------------------------------------------------------------------*/
+static void *
+usb2_temp_get_qualifier_desc(struct usb2_device *udev)
+{
+ struct usb2_device_qualifier *dq;
+
+ if (udev->usb2_template_ptr == NULL) {
+ return (NULL);
+ }
+ dq = &udev->usb2_template_ptr->udq;
+ if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) {
+ /* sanity check failed */
+ return (NULL);
+ }
+ return (dq);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_config_desc
+ *
+ * Returns:
+ * NULL: No USB config descriptor found.
+ * Else: Pointer to USB config descriptor having index "index".
+ *------------------------------------------------------------------------*/
+static void *
+usb2_temp_get_config_desc(struct usb2_device *udev,
+ uint16_t *pLength, uint8_t index)
+{
+ struct usb2_device_descriptor *dd;
+ struct usb2_config_descriptor *cd;
+ uint16_t temp;
+
+ if (udev->usb2_template_ptr == NULL) {
+ return (NULL);
+ }
+ dd = &udev->usb2_template_ptr->udd;
+ cd = (void *)(udev->usb2_template_ptr + 1);
+
+ if (index >= dd->bNumConfigurations) {
+ /* out of range */
+ return (NULL);
+ }
+ while (index--) {
+ if (cd->bDescriptorType != UDESC_CONFIG) {
+ /* sanity check failed */
+ return (NULL);
+ }
+ temp = UGETW(cd->wTotalLength);
+ cd = USB_ADD_BYTES(cd, temp);
+ }
+
+ if (pLength) {
+ *pLength = UGETW(cd->wTotalLength);
+ }
+ return (cd);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_vendor_desc
+ *
+ * Returns:
+ * NULL: No vendor descriptor found.
+ * Else: Pointer to a vendor descriptor.
+ *------------------------------------------------------------------------*/
+static const void *
+usb2_temp_get_vendor_desc(struct usb2_device *udev,
+ const struct usb2_device_request *req)
+{
+ const struct usb2_temp_device_desc *tdd;
+
+ tdd = usb2_temp_get_tdd(udev);
+ if (tdd == NULL) {
+ return (NULL);
+ }
+ if (tdd->getVendorDesc == NULL) {
+ return (NULL);
+ }
+ return ((tdd->getVendorDesc) (req));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_string_desc
+ *
+ * Returns:
+ * NULL: No string descriptor found.
+ * Else: Pointer to a string descriptor.
+ *------------------------------------------------------------------------*/
+static const void *
+usb2_temp_get_string_desc(struct usb2_device *udev,
+ uint16_t lang_id, uint8_t string_index)
+{
+ const struct usb2_temp_device_desc *tdd;
+
+ tdd = usb2_temp_get_tdd(udev);
+ if (tdd == NULL) {
+ return (NULL);
+ }
+ if (tdd->getStringDesc == NULL) {
+ return (NULL);
+ }
+ return ((tdd->getStringDesc) (lang_id, string_index));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_hub_desc
+ *
+ * Returns:
+ * NULL: No USB HUB descriptor found.
+ * Else: Pointer to a USB HUB descriptor.
+ *------------------------------------------------------------------------*/
+static const void *
+usb2_temp_get_hub_desc(struct usb2_device *udev)
+{
+ return (NULL); /* needs to be implemented */
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_get_desc
+ *
+ * This function is a demultiplexer for local USB device side control
+ * endpoint requests.
+ *------------------------------------------------------------------------*/
+static void
+usb2_temp_get_desc(struct usb2_device *udev, struct usb2_device_request *req,
+ const void **pPtr, uint16_t *pLength)
+{
+ const uint8_t *buf;
+ uint16_t len;
+
+ buf = NULL;
+ len = 0;
+
+ switch (req->bmRequestType) {
+ case UT_READ_DEVICE:
+ switch (req->bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_descriptor;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UT_READ_CLASS_DEVICE:
+ switch (req->bRequest) {
+ case UR_GET_DESCRIPTOR:
+ goto tr_handle_get_class_descriptor;
+ default:
+ goto tr_stalled;
+ }
+ break;
+ case UT_READ_VENDOR_DEVICE:
+ case UT_READ_VENDOR_OTHER:
+ buf = usb2_temp_get_vendor_desc(udev, req);
+ goto tr_valid;
+ default:
+ goto tr_stalled;
+ }
+
+tr_handle_get_descriptor:
+ switch (req->wValue[1]) {
+ case UDESC_DEVICE:
+ if (req->wValue[0]) {
+ goto tr_stalled;
+ }
+ buf = usb2_temp_get_device_desc(udev);
+ goto tr_valid;
+ case UDESC_DEVICE_QUALIFIER:
+ if (udev->speed != USB_SPEED_HIGH) {
+ goto tr_stalled;
+ }
+ if (req->wValue[0]) {
+ goto tr_stalled;
+ }
+ buf = usb2_temp_get_qualifier_desc(udev);
+ goto tr_valid;
+ case UDESC_OTHER_SPEED_CONFIGURATION:
+ if (udev->speed != USB_SPEED_HIGH) {
+ goto tr_stalled;
+ }
+ case UDESC_CONFIG:
+ buf = usb2_temp_get_config_desc(udev,
+ &len, req->wValue[0]);
+ goto tr_valid;
+ case UDESC_STRING:
+ buf = usb2_temp_get_string_desc(udev,
+ UGETW(req->wIndex), req->wValue[0]);
+ goto tr_valid;
+ default:
+ goto tr_stalled;
+ }
+ goto tr_stalled;
+
+tr_handle_get_class_descriptor:
+ if (req->wValue[0]) {
+ goto tr_stalled;
+ }
+ buf = usb2_temp_get_hub_desc(udev);
+ goto tr_valid;
+
+tr_valid:
+ if (buf == NULL) {
+ goto tr_stalled;
+ }
+ if (len == 0) {
+ len = buf[0];
+ }
+ *pPtr = buf;
+ *pLength = len;
+ return;
+
+tr_stalled:
+ *pPtr = NULL;
+ *pLength = 0;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_setup
+ *
+ * This function generates USB descriptors according to the given USB
+ * template device descriptor. It will also try to figure out the best
+ * matching endpoint addresses using the hardware endpoint profiles.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_temp_setup(struct usb2_device *udev,
+ const struct usb2_temp_device_desc *tdd)
+{
+ struct usb2_temp_setup *uts;
+ void *buf;
+ uint8_t n;
+
+ if (tdd == NULL) {
+ /* be NULL safe */
+ return (0);
+ }
+ uts = udev->bus->scratch[0].temp_setup;
+
+ bzero(uts, sizeof(*uts));
+
+ uts->usb2_speed = udev->speed;
+ uts->self_powered = udev->flags.self_powered;
+
+ /* first pass */
+
+ usb2_make_device_desc(uts, tdd);
+
+ if (uts->err) {
+ /* some error happened */
+ return (uts->err);
+ }
+ /* sanity check */
+ if (uts->size == 0) {
+ return (USB_ERR_INVAL);
+ }
+ /* allocate zeroed memory */
+ uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO);
+ if (uts->buf == NULL) {
+ /* could not allocate memory */
+ return (USB_ERR_NOMEM);
+ }
+ /* second pass */
+
+ uts->size = 0;
+
+ usb2_make_device_desc(uts, tdd);
+
+ /*
+ * Store a pointer to our descriptors:
+ */
+ udev->usb2_template_ptr = uts->buf;
+
+ if (uts->err) {
+ /* some error happened during second pass */
+ goto error;
+ }
+ /*
+ * Resolve all endpoint addresses !
+ */
+ buf = usb2_temp_get_device_desc(udev);
+ uts->err = usb2_hw_ep_resolve(udev, buf);
+ if (uts->err) {
+ DPRINTFN(0, "Could not resolve endpoints for "
+ "Device Descriptor, error = %s\n",
+ usb2_errstr(uts->err));
+ goto error;
+ }
+ for (n = 0;; n++) {
+
+ buf = usb2_temp_get_config_desc(udev, NULL, n);
+ if (buf == NULL) {
+ break;
+ }
+ uts->err = usb2_hw_ep_resolve(udev, buf);
+ if (uts->err) {
+ DPRINTFN(0, "Could not resolve endpoints for "
+ "Config Descriptor %u, error = %s\n", n,
+ usb2_errstr(uts->err));
+ goto error;
+ }
+ }
+ return (uts->err);
+
+error:
+ usb2_temp_unsetup(udev);
+ return (uts->err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_temp_unsetup
+ *
+ * This function frees any memory associated with the currently
+ * setup template, if any.
+ *------------------------------------------------------------------------*/
+static void
+usb2_temp_unsetup(struct usb2_device *udev)
+{
+ if (udev->usb2_template_ptr) {
+
+ free(udev->usb2_template_ptr, M_USB);
+
+ udev->usb2_template_ptr = NULL;
+ }
+}
+
+static usb2_error_t
+usb2_temp_setup_by_index(struct usb2_device *udev, uint16_t index)
+{
+ usb2_error_t err;
+
+ switch (index) {
+ case 0:
+ err = usb2_temp_setup(udev, &usb2_template_msc);
+ break;
+ case 1:
+ err = usb2_temp_setup(udev, &usb2_template_cdce);
+ break;
+ case 2:
+ err = usb2_temp_setup(udev, &usb2_template_mtp);
+ break;
+ default:
+ return (USB_ERR_INVAL);
+ }
+
+ return (err);
+}
+
+static void
+usb2_temp_init(void *arg)
+{
+ /* register our functions */
+ usb2_temp_get_desc_p = &usb2_temp_get_desc;
+ usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index;
+ usb2_temp_unsetup_p = &usb2_temp_unsetup;
+}
+
+SYSINIT(usb2_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb2_temp_init, NULL);
+SYSUNINIT(usb2_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb2_temp_unload, NULL);
diff --git a/sys/dev/usb/template/usb_template.h b/sys/dev/usb/template/usb_template.h
new file mode 100644
index 0000000..361de3a
--- /dev/null
+++ b/sys/dev/usb/template/usb_template.h
@@ -0,0 +1,102 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2007 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* USB templates are used to build up real USB descriptors */
+
+#ifndef _USB_TEMPLATE_H_
+#define _USB_TEMPLATE_H_
+
+typedef const void *(usb2_temp_get_string_desc_t)(uint16_t lang_id, uint8_t string_index);
+typedef const void *(usb2_temp_get_vendor_desc_t)(const struct usb2_device_request *req);
+
+struct usb2_temp_packet_size {
+ uint16_t mps[USB_SPEED_MAX];
+};
+
+struct usb2_temp_interval {
+ uint8_t bInterval[USB_SPEED_MAX];
+};
+
+struct usb2_temp_endpoint_desc {
+ const void **ppRawDesc;
+ const struct usb2_temp_packet_size *pPacketSize;
+ const struct usb2_temp_interval *pIntervals;
+ /*
+ * If (bEndpointAddress & UE_ADDR) is non-zero the endpoint number
+ * is pre-selected for this endpoint descriptor. Else an endpoint
+ * number is automatically chosen.
+ */
+ uint8_t bEndpointAddress; /* UE_DIR_IN or UE_DIR_OUT */
+ uint8_t bmAttributes;
+};
+
+struct usb2_temp_interface_desc {
+ const void **ppRawDesc;
+ const struct usb2_temp_endpoint_desc **ppEndpoints;
+ uint8_t bInterfaceClass;
+ uint8_t bInterfaceSubClass;
+ uint8_t bInterfaceProtocol;
+ uint8_t iInterface;
+ uint8_t isAltInterface;
+};
+
+struct usb2_temp_config_desc {
+ const struct usb2_temp_interface_desc **ppIfaceDesc;
+ uint8_t bmAttributes;
+ uint8_t bMaxPower;
+ uint8_t iConfiguration;
+};
+
+struct usb2_temp_device_desc {
+ usb2_temp_get_string_desc_t *getStringDesc;
+ usb2_temp_get_vendor_desc_t *getVendorDesc;
+ const struct usb2_temp_config_desc **ppConfigDesc;
+ uint16_t idVendor;
+ uint16_t idProduct;
+ uint16_t bcdDevice;
+ uint8_t bDeviceClass;
+ uint8_t bDeviceSubClass;
+ uint8_t bDeviceProtocol;
+ uint8_t iManufacturer;
+ uint8_t iProduct;
+ uint8_t iSerialNumber;
+};
+
+struct usb2_temp_data {
+ const struct usb2_temp_device_desc *tdd;
+ struct usb2_device_descriptor udd; /* device descriptor */
+ struct usb2_device_qualifier udq; /* device qualifier */
+};
+
+/* prototypes */
+
+extern const struct usb2_temp_device_desc usb2_template_cdce;
+extern const struct usb2_temp_device_desc usb2_template_msc; /* Mass Storage Class */
+extern const struct usb2_temp_device_desc usb2_template_mtp; /* Message Transfer
+ * Protocol */
+
+#endif /* _USB_TEMPLATE_H_ */
diff --git a/sys/dev/usb/template/usb_template_cdce.c b/sys/dev/usb/template/usb_template_cdce.c
new file mode 100644
index 0000000..c215c1d
--- /dev/null
+++ b/sys/dev/usb/template/usb_template_cdce.c
@@ -0,0 +1,292 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2007 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the USB templates for a CDC USB ethernet device.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_cdc.h>
+#include <dev/usb/usb_mfunc.h>
+
+#include <dev/usb/usb_core.h>
+
+#include <dev/usb/template/usb_template.h>
+
+enum {
+ STRING_LANG_INDEX,
+ STRING_MAC_INDEX,
+ STRING_ETH_CONTROL_INDEX,
+ STRING_ETH_DATA_INDEX,
+ STRING_ETH_CONFIG_INDEX,
+ STRING_ETH_VENDOR_INDEX,
+ STRING_ETH_PRODUCT_INDEX,
+ STRING_ETH_SERIAL_INDEX,
+ STRING_ETH_MAX,
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_MAC \
+ '2', 0, 'A', 0, '2', 0, '3', 0, \
+ '4', 0, '5', 0, '6', 0, '7', 0, \
+ '8', 0, '9', 0, 'A', 0, 'B', 0,
+
+#define STRING_ETH_CONTROL \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'E', 0, 't', 0, 'h', 0, 'e', 0, \
+ 'r', 0, 'n', 0, 'e', 0, 't', 0, \
+ ' ', 0, 'C', 0, 'o', 0, 'm', 0, \
+ 'm', 0, ' ', 0, 'i', 0, 'n', 0, \
+ 't', 0, 'e', 0, 'r', 0, 'f', 0, \
+ 'a', 0, 'c', 0, 'e', 0,
+
+#define STRING_ETH_DATA \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'E', 0, 't', 0, 'h', 0, 'e', 0, \
+ 'r', 0, 'n', 0, 'e', 0, 't', 0, \
+ ' ', 0, 'D', 0, 'a', 0, 't', 0, \
+ 'a', 0, ' ', 0, 'i', 0, 'n', 0, \
+ 't', 0, 'e', 0, 'r', 0, 'f', 0, \
+ 'a', 0, 'c', 0, 'e', 0,
+
+#define STRING_ETH_CONFIG \
+ 'D', 0, 'e', 0, 'f', 0, 'a', 0, \
+ 'u', 0, 'l', 0, 't', 0, ' ', 0, \
+ 'c', 0, 'o', 0, 'n', 0, 'f', 0, \
+ 'i', 0, 'g', 0,
+
+#define STRING_ETH_VENDOR \
+ 'F', 0, 'r', 0, 'e', 0, 'e', 0, \
+ 'B', 0, 'S', 0, 'D', 0, ' ', 0, \
+ 'f', 0, 'o', 0, 'u', 0, 'n', 0, \
+ 'd', 0, 'a', 0, 't', 0, 'i', 0, \
+ 'o', 0, 'n', 0,
+
+#define STRING_ETH_PRODUCT \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'E', 0, 't', 0, 'h', 0, 'e', 0, \
+ 'r', 0, 'n', 0, 'e', 0, 't', 0, \
+ ' ', 0, 'A', 0, 'd', 0, 'a', 0, \
+ 'p', 0, 't', 0, 'e', 0, 'r', 0,
+
+#define STRING_ETH_SERIAL \
+ 'D', 0, 'e', 0, 'c', 0, 'e', 0, \
+ 'm', 0, 'b', 0, 'e', 0, 'r', 0, \
+ ' ', 0, '2', 0, '0', 0, '0', 0, \
+ '7', 0,
+
+/* make the real string descriptors */
+
+USB_MAKE_STRING_DESC(STRING_LANG, string_lang);
+USB_MAKE_STRING_DESC(STRING_MAC, string_mac);
+USB_MAKE_STRING_DESC(STRING_ETH_CONTROL, string_eth_control);
+USB_MAKE_STRING_DESC(STRING_ETH_DATA, string_eth_data);
+USB_MAKE_STRING_DESC(STRING_ETH_CONFIG, string_eth_config);
+USB_MAKE_STRING_DESC(STRING_ETH_VENDOR, string_eth_vendor);
+USB_MAKE_STRING_DESC(STRING_ETH_PRODUCT, string_eth_product);
+USB_MAKE_STRING_DESC(STRING_ETH_SERIAL, string_eth_serial);
+
+/* prototypes */
+
+static usb2_temp_get_string_desc_t eth_get_string_desc;
+
+static const struct usb2_cdc_union_descriptor eth_union_desc = {
+ .bLength = sizeof(eth_union_desc),
+ .bDescriptorType = UDESC_CS_INTERFACE,
+ .bDescriptorSubtype = UDESCSUB_CDC_UNION,
+ .bMasterInterface = 0, /* this is automatically updated */
+ .bSlaveInterface[0] = 1, /* this is automatically updated */
+};
+
+static const struct usb2_cdc_header_descriptor eth_header_desc = {
+ .bLength = sizeof(eth_header_desc),
+ .bDescriptorType = UDESC_CS_INTERFACE,
+ .bDescriptorSubtype = UDESCSUB_CDC_HEADER,
+ .bcdCDC[0] = 0x10,
+ .bcdCDC[1] = 0x01,
+};
+
+static const struct usb2_cdc_ethernet_descriptor eth_enf_desc = {
+ .bLength = sizeof(eth_enf_desc),
+ .bDescriptorType = UDESC_CS_INTERFACE,
+ .bDescriptorSubtype = UDESCSUB_CDC_ENF,
+ .iMacAddress = STRING_MAC_INDEX,
+ .bmEthernetStatistics = {0, 0, 0, 0},
+ .wMaxSegmentSize = {0xEA, 0x05},/* 1514 bytes */
+ .wNumberMCFilters = {0, 0},
+ .bNumberPowerFilters = 0,
+};
+
+static const void *eth_control_if_desc[] = {
+ &eth_union_desc,
+ &eth_header_desc,
+ &eth_enf_desc,
+ NULL,
+};
+
+static const struct usb2_temp_packet_size bulk_mps = {
+ .mps[USB_SPEED_FULL] = 64,
+ .mps[USB_SPEED_HIGH] = 512,
+};
+
+static const struct usb2_temp_packet_size intr_mps = {
+ .mps[USB_SPEED_FULL] = 8,
+ .mps[USB_SPEED_HIGH] = 8,
+};
+
+static const struct usb2_temp_endpoint_desc bulk_in_ep = {
+ .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_IN_EP_0
+ .bEndpointAddress = USB_HIP_IN_EP_0,
+#else
+ .bEndpointAddress = UE_DIR_IN,
+#endif
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb2_temp_endpoint_desc bulk_out_ep = {
+ .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_OUT_EP_0
+ .bEndpointAddress = USB_HIP_OUT_EP_0,
+#else
+ .bEndpointAddress = UE_DIR_OUT,
+#endif
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb2_temp_endpoint_desc intr_in_ep = {
+ .pPacketSize = &intr_mps,
+ .bEndpointAddress = UE_DIR_IN,
+ .bmAttributes = UE_INTERRUPT,
+};
+
+static const struct usb2_temp_endpoint_desc *eth_intr_endpoints[] = {
+ &intr_in_ep,
+ NULL,
+};
+
+static const struct usb2_temp_interface_desc eth_control_interface = {
+ .ppEndpoints = eth_intr_endpoints,
+ .ppRawDesc = eth_control_if_desc,
+ .bInterfaceClass = UICLASS_CDC,
+ .bInterfaceSubClass = UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL,
+ .bInterfaceProtocol = 0,
+ .iInterface = STRING_ETH_CONTROL_INDEX,
+};
+
+static const struct usb2_temp_endpoint_desc *eth_data_endpoints[] = {
+ &bulk_in_ep,
+ &bulk_out_ep,
+ NULL,
+};
+
+static const struct usb2_temp_interface_desc eth_data_null_interface = {
+ .ppEndpoints = NULL, /* no endpoints */
+ .bInterfaceClass = UICLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .iInterface = STRING_ETH_DATA_INDEX,
+};
+
+static const struct usb2_temp_interface_desc eth_data_interface = {
+ .ppEndpoints = eth_data_endpoints,
+ .bInterfaceClass = UICLASS_CDC_DATA,
+ .bInterfaceSubClass = UISUBCLASS_DATA,
+ .bInterfaceProtocol = 0,
+ .iInterface = STRING_ETH_DATA_INDEX,
+ .isAltInterface = 1, /* this is an alternate setting */
+};
+
+static const struct usb2_temp_interface_desc *eth_interfaces[] = {
+ &eth_control_interface,
+ &eth_data_null_interface,
+ &eth_data_interface,
+ NULL,
+};
+
+static const struct usb2_temp_config_desc eth_config_desc = {
+ .ppIfaceDesc = eth_interfaces,
+ .bmAttributes = UC_BUS_POWERED,
+ .bMaxPower = 25, /* 50 mA */
+ .iConfiguration = STRING_ETH_CONFIG_INDEX,
+};
+
+static const struct usb2_temp_config_desc *eth_configs[] = {
+ &eth_config_desc,
+ NULL,
+};
+
+const struct usb2_temp_device_desc usb2_template_cdce = {
+ .getStringDesc = &eth_get_string_desc,
+ .ppConfigDesc = eth_configs,
+ .idVendor = 0x0001,
+ .idProduct = 0x0001,
+ .bcdDevice = 0x0100,
+ .bDeviceClass = UDCLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .iManufacturer = STRING_ETH_VENDOR_INDEX,
+ .iProduct = STRING_ETH_PRODUCT_INDEX,
+ .iSerialNumber = STRING_ETH_SERIAL_INDEX,
+};
+
+/*------------------------------------------------------------------------*
+ * eth_get_string_desc
+ *
+ * Return values:
+ * NULL: Failure. No such string.
+ * Else: Success. Pointer to string descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+eth_get_string_desc(uint16_t lang_id, uint8_t string_index)
+{
+ static const void *ptr[STRING_ETH_MAX] = {
+ [STRING_LANG_INDEX] = &string_lang,
+ [STRING_MAC_INDEX] = &string_mac,
+ [STRING_ETH_CONTROL_INDEX] = &string_eth_control,
+ [STRING_ETH_DATA_INDEX] = &string_eth_data,
+ [STRING_ETH_CONFIG_INDEX] = &string_eth_config,
+ [STRING_ETH_VENDOR_INDEX] = &string_eth_vendor,
+ [STRING_ETH_PRODUCT_INDEX] = &string_eth_product,
+ [STRING_ETH_SERIAL_INDEX] = &string_eth_serial,
+ };
+
+ if (string_index == 0) {
+ return (&string_lang);
+ }
+ if (lang_id != 0x0409) {
+ return (NULL);
+ }
+ if (string_index < STRING_ETH_MAX) {
+ return (ptr[string_index]);
+ }
+ return (NULL);
+}
diff --git a/sys/dev/usb/template/usb_template_msc.c b/sys/dev/usb/template/usb_template_msc.c
new file mode 100644
index 0000000..a3d88b0
--- /dev/null
+++ b/sys/dev/usb/template/usb_template_msc.c
@@ -0,0 +1,199 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the USB templates for an USB Mass Storage Device.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+
+#include <dev/usb/usb_core.h>
+
+#include <dev/usb/template/usb_template.h>
+
+enum {
+ STRING_LANG_INDEX,
+ STRING_MSC_DATA_INDEX,
+ STRING_MSC_CONFIG_INDEX,
+ STRING_MSC_VENDOR_INDEX,
+ STRING_MSC_PRODUCT_INDEX,
+ STRING_MSC_SERIAL_INDEX,
+ STRING_MSC_MAX,
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_MSC_DATA \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'M', 0, 'a', 0, 's', 0, 's', 0, \
+ ' ', 0, 'S', 0, 't', 0, 'o', 0, \
+ 'r', 0, 'a', 0, 'g', 0, 'e', 0, \
+ ' ', 0, 'I', 0, 'n', 0, 't', 0, \
+ 'e', 0, 'r', 0, 'f', 0, 'a', 0, \
+ 'c', 0, 'e', 0,
+
+#define STRING_MSC_CONFIG \
+ 'D', 0, 'e', 0, 'f', 0, 'a', 0, \
+ 'u', 0, 'l', 0, 't', 0, ' ', 0, \
+ 'c', 0, 'o', 0, 'n', 0, 'f', 0, \
+ 'i', 0, 'g', 0,
+
+#define STRING_MSC_VENDOR \
+ 'F', 0, 'r', 0, 'e', 0, 'e', 0, \
+ 'B', 0, 'S', 0, 'D', 0, ' ', 0, \
+ 'f', 0, 'o', 0, 'u', 0, 'n', 0, \
+ 'd', 0, 'a', 0, 't', 0, 'i', 0, \
+ 'o', 0, 'n', 0,
+
+#define STRING_MSC_PRODUCT \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'M', 0, 'e', 0, 'm', 0, 'o', 0, \
+ 'r', 0, 'y', 0, ' ', 0, 'S', 0, \
+ 't', 0, 'i', 0, 'c', 0, 'k', 0
+
+#define STRING_MSC_SERIAL \
+ 'M', 0, 'a', 0, 'r', 0, 'c', 0, \
+ 'h', 0, ' ', 0, '2', 0, '0', 0, \
+ '0', 0, '8', 0,
+
+/* make the real string descriptors */
+
+USB_MAKE_STRING_DESC(STRING_LANG, string_lang);
+USB_MAKE_STRING_DESC(STRING_MSC_DATA, string_msc_data);
+USB_MAKE_STRING_DESC(STRING_MSC_CONFIG, string_msc_config);
+USB_MAKE_STRING_DESC(STRING_MSC_VENDOR, string_msc_vendor);
+USB_MAKE_STRING_DESC(STRING_MSC_PRODUCT, string_msc_product);
+USB_MAKE_STRING_DESC(STRING_MSC_SERIAL, string_msc_serial);
+
+/* prototypes */
+
+static usb2_temp_get_string_desc_t msc_get_string_desc;
+
+static const struct usb2_temp_packet_size bulk_mps = {
+ .mps[USB_SPEED_FULL] = 64,
+ .mps[USB_SPEED_HIGH] = 512,
+};
+
+static const struct usb2_temp_endpoint_desc bulk_in_ep = {
+ .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_IN_EP_0
+ .bEndpointAddress = USB_HIP_IN_EP_0,
+#else
+ .bEndpointAddress = UE_DIR_IN,
+#endif
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb2_temp_endpoint_desc bulk_out_ep = {
+ .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_OUT_EP_0
+ .bEndpointAddress = USB_HIP_OUT_EP_0,
+#else
+ .bEndpointAddress = UE_DIR_OUT,
+#endif
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb2_temp_endpoint_desc *msc_data_endpoints[] = {
+ &bulk_in_ep,
+ &bulk_out_ep,
+ NULL,
+};
+
+static const struct usb2_temp_interface_desc msc_data_interface = {
+ .ppEndpoints = msc_data_endpoints,
+ .bInterfaceClass = UICLASS_MASS,
+ .bInterfaceSubClass = UISUBCLASS_SCSI,
+ .bInterfaceProtocol = UIPROTO_MASS_BBB,
+ .iInterface = STRING_MSC_DATA_INDEX,
+};
+
+static const struct usb2_temp_interface_desc *msc_interfaces[] = {
+ &msc_data_interface,
+ NULL,
+};
+
+static const struct usb2_temp_config_desc msc_config_desc = {
+ .ppIfaceDesc = msc_interfaces,
+ .bmAttributes = UC_BUS_POWERED,
+ .bMaxPower = 25, /* 50 mA */
+ .iConfiguration = STRING_MSC_CONFIG_INDEX,
+};
+
+static const struct usb2_temp_config_desc *msc_configs[] = {
+ &msc_config_desc,
+ NULL,
+};
+
+const struct usb2_temp_device_desc usb2_template_msc = {
+ .getStringDesc = &msc_get_string_desc,
+ .ppConfigDesc = msc_configs,
+ .idVendor = 0x0001,
+ .idProduct = 0x0001,
+ .bcdDevice = 0x0100,
+ .bDeviceClass = UDCLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .iManufacturer = STRING_MSC_VENDOR_INDEX,
+ .iProduct = STRING_MSC_PRODUCT_INDEX,
+ .iSerialNumber = STRING_MSC_SERIAL_INDEX,
+};
+
+/*------------------------------------------------------------------------*
+ * msc_get_string_desc
+ *
+ * Return values:
+ * NULL: Failure. No such string.
+ * Else: Success. Pointer to string descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+msc_get_string_desc(uint16_t lang_id, uint8_t string_index)
+{
+ static const void *ptr[STRING_MSC_MAX] = {
+ [STRING_LANG_INDEX] = &string_lang,
+ [STRING_MSC_DATA_INDEX] = &string_msc_data,
+ [STRING_MSC_CONFIG_INDEX] = &string_msc_config,
+ [STRING_MSC_VENDOR_INDEX] = &string_msc_vendor,
+ [STRING_MSC_PRODUCT_INDEX] = &string_msc_product,
+ [STRING_MSC_SERIAL_INDEX] = &string_msc_serial,
+ };
+
+ if (string_index == 0) {
+ return (&string_lang);
+ }
+ if (lang_id != 0x0409) {
+ return (NULL);
+ }
+ if (string_index < STRING_MSC_MAX) {
+ return (ptr[string_index]);
+ }
+ return (NULL);
+}
diff --git a/sys/dev/usb/template/usb_template_mtp.c b/sys/dev/usb/template/usb_template_mtp.c
new file mode 100644
index 0000000..271c202
--- /dev/null
+++ b/sys/dev/usb/template/usb_template_mtp.c
@@ -0,0 +1,262 @@
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky <hselasky@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the USB templates for an USB Message Transfer
+ * Protocol device.
+ *
+ * NOTE: It is common practice that MTP devices use some dummy
+ * descriptor cludges to be automatically detected by the host
+ * operating system. These descriptors are documented in the LibMTP
+ * library at sourceforge.net. The alternative is to supply the host
+ * operating system the VID and PID of your device.
+ */
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+
+#include <dev/usb/usb_core.h>
+
+#include <dev/usb/template/usb_template.h>
+
+#define MTP_BREQUEST 0x08
+
+enum {
+ STRING_LANG_INDEX,
+ STRING_MTP_DATA_INDEX,
+ STRING_MTP_CONFIG_INDEX,
+ STRING_MTP_VENDOR_INDEX,
+ STRING_MTP_PRODUCT_INDEX,
+ STRING_MTP_SERIAL_INDEX,
+ STRING_MTP_MAX,
+};
+
+#define STRING_LANG \
+ 0x09, 0x04, /* American English */
+
+#define STRING_MTP_DATA \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'M', 0, 'T', 0, 'P', 0, \
+ ' ', 0, 'I', 0, 'n', 0, 't', 0, \
+ 'e', 0, 'r', 0, 'f', 0, 'a', 0, \
+ 'c', 0, 'e', 0,
+
+#define STRING_MTP_CONFIG \
+ 'D', 0, 'e', 0, 'f', 0, 'a', 0, \
+ 'u', 0, 'l', 0, 't', 0, ' ', 0, \
+ 'c', 0, 'o', 0, 'n', 0, 'f', 0, \
+ 'i', 0, 'g', 0,
+
+#define STRING_MTP_VENDOR \
+ 'F', 0, 'r', 0, 'e', 0, 'e', 0, \
+ 'B', 0, 'S', 0, 'D', 0, ' ', 0, \
+ 'f', 0, 'o', 0, 'u', 0, 'n', 0, \
+ 'd', 0, 'a', 0, 't', 0, 'i', 0, \
+ 'o', 0, 'n', 0,
+
+#define STRING_MTP_PRODUCT \
+ 'U', 0, 'S', 0, 'B', 0, ' ', 0, \
+ 'M', 0, 'T', 0, 'P', 0,
+
+#define STRING_MTP_SERIAL \
+ 'J', 0, 'u', 0, 'n', 0, 'e', 0, \
+ ' ', 0, '2', 0, '0', 0, '0', 0, \
+ '8', 0,
+
+/* make the real string descriptors */
+
+USB_MAKE_STRING_DESC(STRING_LANG, string_lang);
+USB_MAKE_STRING_DESC(STRING_MTP_DATA, string_mtp_data);
+USB_MAKE_STRING_DESC(STRING_MTP_CONFIG, string_mtp_config);
+USB_MAKE_STRING_DESC(STRING_MTP_VENDOR, string_mtp_vendor);
+USB_MAKE_STRING_DESC(STRING_MTP_PRODUCT, string_mtp_product);
+USB_MAKE_STRING_DESC(STRING_MTP_SERIAL, string_mtp_serial);
+
+/* prototypes */
+
+static usb2_temp_get_string_desc_t mtp_get_string_desc;
+static usb2_temp_get_vendor_desc_t mtp_get_vendor_desc;
+
+static const struct usb2_temp_packet_size bulk_mps = {
+ .mps[USB_SPEED_FULL] = 64,
+ .mps[USB_SPEED_HIGH] = 512,
+};
+
+static const struct usb2_temp_packet_size intr_mps = {
+ .mps[USB_SPEED_FULL] = 64,
+ .mps[USB_SPEED_HIGH] = 64,
+};
+
+static const struct usb2_temp_endpoint_desc bulk_out_ep = {
+ .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_OUT_EP_0
+ .bEndpointAddress = USB_HIP_OUT_EP_0,
+#else
+ .bEndpointAddress = UE_DIR_OUT,
+#endif
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb2_temp_endpoint_desc intr_in_ep = {
+ .pPacketSize = &intr_mps,
+ .bEndpointAddress = UE_DIR_IN,
+ .bmAttributes = UE_INTERRUPT,
+};
+
+static const struct usb2_temp_endpoint_desc bulk_in_ep = {
+ .pPacketSize = &bulk_mps,
+#ifdef USB_HIP_IN_EP_0
+ .bEndpointAddress = USB_HIP_IN_EP_0,
+#else
+ .bEndpointAddress = UE_DIR_IN,
+#endif
+ .bmAttributes = UE_BULK,
+};
+
+static const struct usb2_temp_endpoint_desc *mtp_data_endpoints[] = {
+ &bulk_in_ep,
+ &bulk_out_ep,
+ &intr_in_ep,
+ NULL,
+};
+
+static const struct usb2_temp_interface_desc mtp_data_interface = {
+ .ppEndpoints = mtp_data_endpoints,
+ .bInterfaceClass = UICLASS_IMAGE,
+ .bInterfaceSubClass = UISUBCLASS_SIC, /* Still Image Class */
+ .bInterfaceProtocol = 1, /* PIMA 15740 */
+ .iInterface = STRING_MTP_DATA_INDEX,
+};
+
+static const struct usb2_temp_interface_desc *mtp_interfaces[] = {
+ &mtp_data_interface,
+ NULL,
+};
+
+static const struct usb2_temp_config_desc mtp_config_desc = {
+ .ppIfaceDesc = mtp_interfaces,
+ .bmAttributes = UC_BUS_POWERED,
+ .bMaxPower = 25, /* 50 mA */
+ .iConfiguration = STRING_MTP_CONFIG_INDEX,
+};
+
+static const struct usb2_temp_config_desc *mtp_configs[] = {
+ &mtp_config_desc,
+ NULL,
+};
+
+const struct usb2_temp_device_desc usb2_template_mtp = {
+ .getStringDesc = &mtp_get_string_desc,
+ .getVendorDesc = &mtp_get_vendor_desc,
+ .ppConfigDesc = mtp_configs,
+ .idVendor = 0x0001,
+ .idProduct = 0x0001,
+ .bcdDevice = 0x0100,
+ .bDeviceClass = 0,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
+ .iManufacturer = STRING_MTP_VENDOR_INDEX,
+ .iProduct = STRING_MTP_PRODUCT_INDEX,
+ .iSerialNumber = STRING_MTP_SERIAL_INDEX,
+};
+
+/*------------------------------------------------------------------------*
+ * mtp_get_vendor_desc
+ *
+ * Return values:
+ * NULL: Failure. No such vendor descriptor.
+ * Else: Success. Pointer to vendor descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+mtp_get_vendor_desc(const struct usb2_device_request *req)
+{
+ static const uint8_t dummy_desc[0x28] = {
+ 0x28, 0, 0, 0, 0, 1, 4, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 0x4D, 0x54, 0x50, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+
+ if ((req->bmRequestType == UT_READ_VENDOR_DEVICE) &&
+ (req->bRequest == MTP_BREQUEST) && (req->wValue[0] == 0) &&
+ (req->wValue[1] == 0) && (req->wIndex[1] == 0) &&
+ ((req->wIndex[0] == 4) || (req->wIndex[0] == 5))) {
+ /*
+ * By returning this descriptor LibMTP will
+ * automatically pickup our device.
+ */
+ return (dummy_desc);
+ }
+ return (NULL);
+}
+
+/*------------------------------------------------------------------------*
+ * mtp_get_string_desc
+ *
+ * Return values:
+ * NULL: Failure. No such string.
+ * Else: Success. Pointer to string descriptor is returned.
+ *------------------------------------------------------------------------*/
+static const void *
+mtp_get_string_desc(uint16_t lang_id, uint8_t string_index)
+{
+ static const void *ptr[STRING_MTP_MAX] = {
+ [STRING_LANG_INDEX] = &string_lang,
+ [STRING_MTP_DATA_INDEX] = &string_mtp_data,
+ [STRING_MTP_CONFIG_INDEX] = &string_mtp_config,
+ [STRING_MTP_VENDOR_INDEX] = &string_mtp_vendor,
+ [STRING_MTP_PRODUCT_INDEX] = &string_mtp_product,
+ [STRING_MTP_SERIAL_INDEX] = &string_mtp_serial,
+ };
+
+ static const uint8_t dummy_desc[0x12] = {
+ 0x12, 0x03, 0x4D, 0x00, 0x53, 0x00, 0x46, 0x00,
+ 0x54, 0x00, 0x31, 0x00, 0x30, 0x00, 0x30, 0x00,
+ MTP_BREQUEST, 0x00,
+ };
+
+ if (string_index == 0xEE) {
+ /*
+ * By returning this string LibMTP will automatically
+ * pickup our device.
+ */
+ return (dummy_desc);
+ }
+ if (string_index == 0) {
+ return (&string_lang);
+ }
+ if (lang_id != 0x0409) {
+ return (NULL);
+ }
+ if (string_index < STRING_MTP_MAX) {
+ return (ptr[string_index]);
+ }
+ return (NULL);
+}
diff --git a/sys/dev/usb/ufm_ioctl.h b/sys/dev/usb/ufm_ioctl.h
new file mode 100644
index 0000000..921b3d4
--- /dev/null
+++ b/sys/dev/usb/ufm_ioctl.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2001 M. Warner Losh
+ * 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 code is based on ugen.c and ulpt.c developed by Lennart Augustsson.
+ * This code includes software developed by the NetBSD Foundation, Inc. and
+ * its contributors.
+ */
+
+/* $FreeBSD$ */
+
+#include <sys/ioccom.h>
+
+#define FM_SET_FREQ _IOWR('U', 200, int)
+#define FM_GET_FREQ _IOWR('U', 201, int)
+#define FM_START _IOWR('U', 202, int)
+#define FM_STOP _IOWR('U', 203, int)
+#define FM_GET_STAT _IOWR('U', 204, int)
diff --git a/sys/dev/usb/usb.h b/sys/dev/usb/usb.h
new file mode 100644
index 0000000..104ee36
--- /dev/null
+++ b/sys/dev/usb/usb.h
@@ -0,0 +1,619 @@
+/* $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.
+ */
+
+/*
+ * This file contains standard definitions for the following USB
+ * protocol versions:
+ *
+ * USB v1.0
+ * USB v1.1
+ * USB v2.0
+ * USB v3.0
+ */
+
+#ifndef _USB2_STANDARD_H_
+#define _USB2_STANDARD_H_
+
+#include <dev/usb/usb_endian.h>
+
+/*
+ * Minimum time a device needs to be powered down to go through a
+ * power cycle. These values are not in the USB specification.
+ */
+#define USB_POWER_DOWN_TIME 200 /* ms */
+#define USB_PORT_POWER_DOWN_TIME 100 /* ms */
+
+/* Definition of software USB power modes */
+#define USB_POWER_MODE_OFF 0 /* turn off device */
+#define USB_POWER_MODE_ON 1 /* always on */
+#define USB_POWER_MODE_SAVE 2 /* automatic suspend and resume */
+#define USB_POWER_MODE_SUSPEND 3 /* force suspend */
+#define USB_POWER_MODE_RESUME 4 /* force resume */
+
+#if 0
+/* These are the values from the USB specification. */
+#define USB_PORT_RESET_DELAY 10 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */
+#define USB_PORT_RESET_RECOVERY 10 /* ms */
+#define USB_PORT_POWERUP_DELAY 100 /* ms */
+#define USB_PORT_RESUME_DELAY 20 /* ms */
+#define USB_SET_ADDRESS_SETTLE 2 /* ms */
+#define USB_RESUME_DELAY (20*5) /* ms */
+#define USB_RESUME_WAIT 10 /* ms */
+#define USB_RESUME_RECOVERY 10 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 0 /* ms */
+#else
+/* Allow for marginal and non-conforming devices. */
+#define USB_PORT_RESET_DELAY 50 /* ms */
+#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */
+#define USB_PORT_RESET_RECOVERY 250 /* ms */
+#define USB_PORT_POWERUP_DELAY 300 /* ms */
+#define USB_PORT_RESUME_DELAY (20*2) /* ms */
+#define USB_SET_ADDRESS_SETTLE 10 /* ms */
+#define USB_RESUME_DELAY (50*5) /* ms */
+#define USB_RESUME_WAIT 50 /* ms */
+#define USB_RESUME_RECOVERY 50 /* ms */
+#define USB_EXTRA_POWER_UP_TIME 20 /* ms */
+#endif
+
+#define USB_MIN_POWER 100 /* mA */
+#define USB_MAX_POWER 500 /* mA */
+
+#define USB_BUS_RESET_DELAY 100 /* ms */
+
+/*
+ * USB record layout in memory:
+ *
+ * - USB config 0
+ * - USB interfaces
+ * - USB alternative interfaces
+ * - USB pipes
+ *
+ * - USB config 1
+ * - USB interfaces
+ * - USB alternative interfaces
+ * - USB pipes
+ */
+
+/* Declaration of USB records */
+
+struct usb2_device_request {
+ uByte bmRequestType;
+ uByte bRequest;
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+} __packed;
+
+#define UT_WRITE 0x00
+#define UT_READ 0x80
+#define UT_STANDARD 0x00
+#define UT_CLASS 0x20
+#define UT_VENDOR 0x40
+#define UT_DEVICE 0x00
+#define UT_INTERFACE 0x01
+#define UT_ENDPOINT 0x02
+#define UT_OTHER 0x03
+
+#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE)
+#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE)
+#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT)
+#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE)
+#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE)
+#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT)
+#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE)
+#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE)
+#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER)
+#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT)
+#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE)
+#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE)
+#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER)
+#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT)
+#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE)
+#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE)
+#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER)
+#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT)
+#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE)
+#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE)
+#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER)
+#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT)
+
+/* Requests */
+#define UR_GET_STATUS 0x00
+#define UR_CLEAR_FEATURE 0x01
+#define UR_SET_FEATURE 0x03
+#define UR_SET_ADDRESS 0x05
+#define UR_GET_DESCRIPTOR 0x06
+#define UDESC_DEVICE 0x01
+#define UDESC_CONFIG 0x02
+#define UDESC_STRING 0x03
+#define USB_LANGUAGE_TABLE 0x00 /* language ID string index */
+#define UDESC_INTERFACE 0x04
+#define UDESC_ENDPOINT 0x05
+#define UDESC_DEVICE_QUALIFIER 0x06
+#define UDESC_OTHER_SPEED_CONFIGURATION 0x07
+#define UDESC_INTERFACE_POWER 0x08
+#define UDESC_OTG 0x09
+#define UDESC_DEBUG 0x0A
+#define UDESC_IFACE_ASSOC 0x0B /* interface association */
+#define UDESC_BOS 0x0F /* binary object store */
+#define UDESC_DEVICE_CAPABILITY 0x10
+#define UDESC_CS_DEVICE 0x21 /* class specific */
+#define UDESC_CS_CONFIG 0x22
+#define UDESC_CS_STRING 0x23
+#define UDESC_CS_INTERFACE 0x24
+#define UDESC_CS_ENDPOINT 0x25
+#define UDESC_HUB 0x29
+#define UDESC_ENDPOINT_SS_COMP 0x30 /* super speed */
+#define UR_SET_DESCRIPTOR 0x07
+#define UR_GET_CONFIG 0x08
+#define UR_SET_CONFIG 0x09
+#define UR_GET_INTERFACE 0x0a
+#define UR_SET_INTERFACE 0x0b
+#define UR_SYNCH_FRAME 0x0c
+#define UR_SET_SEL 0x30
+#define UR_ISOCH_DELAY 0x31
+
+/* HUB specific request */
+#define UR_GET_BUS_STATE 0x02
+#define UR_CLEAR_TT_BUFFER 0x08
+#define UR_RESET_TT 0x09
+#define UR_GET_TT_STATE 0x0a
+#define UR_STOP_TT 0x0b
+#define UR_SET_HUB_DEPTH 0x0c
+#define UR_GET_PORT_ERR_COUNT 0x0d
+
+/* Feature numbers */
+#define UF_ENDPOINT_HALT 0
+#define UF_DEVICE_REMOTE_WAKEUP 1
+#define UF_TEST_MODE 2
+#define UF_U1_ENABLE 0x30
+#define UF_U2_ENABLE 0x31
+#define UF_LTM_ENABLE 0x32
+
+/* HUB specific features */
+#define UHF_C_HUB_LOCAL_POWER 0
+#define UHF_C_HUB_OVER_CURRENT 1
+#define UHF_PORT_CONNECTION 0
+#define UHF_PORT_ENABLE 1
+#define UHF_PORT_SUSPEND 2
+#define UHF_PORT_OVER_CURRENT 3
+#define UHF_PORT_RESET 4
+#define UHF_PORT_LINK_STATE 5
+#define UHF_PORT_POWER 8
+#define UHF_PORT_LOW_SPEED 9
+#define UHF_C_PORT_CONNECTION 16
+#define UHF_C_PORT_ENABLE 17
+#define UHF_C_PORT_SUSPEND 18
+#define UHF_C_PORT_OVER_CURRENT 19
+#define UHF_C_PORT_RESET 20
+#define UHF_PORT_TEST 21
+#define UHF_PORT_INDICATOR 22
+
+/* SuperSpeed HUB specific features */
+#define UHF_PORT_U1_TIMEOUT 23
+#define UHF_PORT_U2_TIMEOUT 24
+#define UHF_C_PORT_LINK_STATE 25
+#define UHF_C_PORT_CONFIG_ERROR 26
+#define UHF_PORT_REMOTE_WAKE_MASK 27
+#define UHF_BH_PORT_RESET 28
+#define UHF_C_BH_PORT_RESET 29
+#define UHF_FORCE_LINKPM_ACCEPT 30
+
+struct usb2_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+} __packed;
+
+struct usb2_device_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+#define UD_USB_2_0 0x0200
+#define UD_USB_3_0 0x0300
+#define UD_IS_USB2(d) ((d)->bcdUSB[1] == 0x02)
+#define UD_IS_USB3(d) ((d)->bcdUSB[1] == 0x03)
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize;
+ /* The fields below are not part of the initial descriptor. */
+ uWord idVendor;
+ uWord idProduct;
+ uWord bcdDevice;
+ uByte iManufacturer;
+ uByte iProduct;
+ uByte iSerialNumber;
+ uByte bNumConfigurations;
+} __packed;
+
+/* Binary Device Object Store (BOS) */
+struct usb2_bos_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumDeviceCaps;
+} __packed;
+
+/* Binary Device Object Store Capability */
+struct usb2_bos_cap_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+#define USB_DEVCAP_RESERVED 0x00
+#define USB_DEVCAP_WUSB 0x01
+#define USB_DEVCAP_USB2EXT 0x02
+#define USB_DEVCAP_SUPER_SPEED 0x03
+#define USB_DEVCAP_CONTAINER_ID 0x04
+ /* data ... */
+} __packed;
+
+struct usb2_devcap_usb2ext_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bmAttributes;
+#define USB_V2EXT_LPM 0x02
+} __packed;
+
+struct usb2_devcap_ss_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bmAttributes;
+ uWord wSpeedsSupported;
+ uByte bFunctionaltySupport;
+ uByte bU1DevExitLat;
+ uByte bU2DevExitLat;
+} __packed;
+
+struct usb2_devcap_container_id_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDevCapabilityType;
+ uByte bReserved;
+ uByte ContainerID;
+} __packed;
+
+/* Device class codes */
+#define UDCLASS_IN_INTERFACE 0x00
+#define UDCLASS_COMM 0x02
+#define UDCLASS_HUB 0x09
+#define UDSUBCLASS_HUB 0x00
+#define UDPROTO_FSHUB 0x00
+#define UDPROTO_HSHUBSTT 0x01
+#define UDPROTO_HSHUBMTT 0x02
+#define UDCLASS_DIAGNOSTIC 0xdc
+#define UDCLASS_WIRELESS 0xe0
+#define UDSUBCLASS_RF 0x01
+#define UDPROTO_BLUETOOTH 0x01
+#define UDCLASS_VENDOR 0xff
+
+struct usb2_config_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord wTotalLength;
+ uByte bNumInterface;
+ uByte bConfigurationValue;
+#define USB_UNCONFIG_NO 0
+ uByte iConfiguration;
+ uByte bmAttributes;
+#define UC_BUS_POWERED 0x80
+#define UC_SELF_POWERED 0x40
+#define UC_REMOTE_WAKEUP 0x20
+ uByte bMaxPower; /* max current in 2 mA units */
+#define UC_POWER_FACTOR 2
+} __packed;
+
+struct usb2_interface_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bInterfaceNumber;
+ uByte bAlternateSetting;
+ uByte bNumEndpoints;
+ uByte bInterfaceClass;
+ uByte bInterfaceSubClass;
+ uByte bInterfaceProtocol;
+ uByte iInterface;
+} __packed;
+
+struct usb2_interface_assoc_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bFirstInterface;
+ uByte bInterfaceCount;
+ uByte bFunctionClass;
+ uByte bFunctionSubClass;
+ uByte bFunctionProtocol;
+ uByte iFunction;
+} __packed;
+
+/* Interface class codes */
+#define UICLASS_UNSPEC 0x00
+#define UICLASS_AUDIO 0x01 /* audio */
+#define UISUBCLASS_AUDIOCONTROL 1
+#define UISUBCLASS_AUDIOSTREAM 2
+#define UISUBCLASS_MIDISTREAM 3
+
+#define UICLASS_CDC 0x02 /* communication */
+#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1
+#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2
+#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3
+#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4
+#define UISUBCLASS_CAPI_CONTROLMODEL 5
+#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6
+#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7
+#define UISUBCLASS_WIRELESS_HANDSET_CM 8
+#define UISUBCLASS_DEVICE_MGMT 9
+#define UISUBCLASS_MOBILE_DIRECT_LINE_MODEL 10
+#define UISUBCLASS_OBEX 11
+#define UISUBCLASS_ETHERNET_EMULATION_MODEL 12
+
+#define UIPROTO_CDC_AT 1
+#define UIPROTO_CDC_ETH_512X4 0x76 /* FreeBSD specific */
+
+#define UICLASS_HID 0x03
+#define UISUBCLASS_BOOT 1
+#define UIPROTO_BOOT_KEYBOARD 1
+#define UIPROTO_MOUSE 2
+
+#define UICLASS_PHYSICAL 0x05
+#define UICLASS_IMAGE 0x06
+#define UISUBCLASS_SIC 1 /* still image class */
+#define UICLASS_PRINTER 0x07
+#define UISUBCLASS_PRINTER 1
+#define UIPROTO_PRINTER_UNI 1
+#define UIPROTO_PRINTER_BI 2
+#define UIPROTO_PRINTER_1284 3
+
+#define UICLASS_MASS 0x08
+#define UISUBCLASS_RBC 1
+#define UISUBCLASS_SFF8020I 2
+#define UISUBCLASS_QIC157 3
+#define UISUBCLASS_UFI 4
+#define UISUBCLASS_SFF8070I 5
+#define UISUBCLASS_SCSI 6
+#define UIPROTO_MASS_CBI_I 0
+#define UIPROTO_MASS_CBI 1
+#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */
+#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */
+
+#define UICLASS_HUB 0x09
+#define UISUBCLASS_HUB 0
+#define UIPROTO_FSHUB 0
+#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */
+#define UIPROTO_HSHUBMTT 1
+
+#define UICLASS_CDC_DATA 0x0a
+#define UISUBCLASS_DATA 0
+#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */
+#define UIPROTO_DATA_HDLC 0x31 /* HDLC */
+#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */
+#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */
+#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */
+#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */
+#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */
+#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */
+#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */
+#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */
+#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */
+#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc. */
+#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */
+
+#define UICLASS_SMARTCARD 0x0b
+#define UICLASS_FIRM_UPD 0x0c
+#define UICLASS_SECURITY 0x0d
+#define UICLASS_DIAGNOSTIC 0xdc
+#define UICLASS_WIRELESS 0xe0
+#define UISUBCLASS_RF 0x01
+#define UIPROTO_BLUETOOTH 0x01
+
+#define UICLASS_APPL_SPEC 0xfe
+#define UISUBCLASS_FIRMWARE_DOWNLOAD 1
+#define UISUBCLASS_IRDA 2
+#define UIPROTO_IRDA 0
+
+#define UICLASS_VENDOR 0xff
+#define UISUBCLASS_XBOX360_CONTROLLER 0x5d
+#define UIPROTO_XBOX360_GAMEPAD 0x01
+
+struct usb2_endpoint_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bEndpointAddress;
+#define UE_GET_DIR(a) ((a) & 0x80)
+#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7))
+#define UE_DIR_IN 0x80
+#define UE_DIR_OUT 0x00
+#define UE_DIR_ANY 0xff /* for internal use only! */
+#define UE_ADDR 0x0f
+#define UE_ADDR_ANY 0xff /* for internal use only! */
+#define UE_GET_ADDR(a) ((a) & UE_ADDR)
+ uByte bmAttributes;
+#define UE_XFERTYPE 0x03
+#define UE_CONTROL 0x00
+#define UE_ISOCHRONOUS 0x01
+#define UE_BULK 0x02
+#define UE_INTERRUPT 0x03
+#define UE_BULK_INTR 0xfe /* for internal use only! */
+#define UE_TYPE_ANY 0xff /* for internal use only! */
+#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE)
+#define UE_ISO_TYPE 0x0c
+#define UE_ISO_ASYNC 0x04
+#define UE_ISO_ADAPT 0x08
+#define UE_ISO_SYNC 0x0c
+#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE)
+ uWord wMaxPacketSize;
+#define UE_ZERO_MPS 0xFFFF /* for internal use only */
+ uByte bInterval;
+} __packed;
+
+struct usb2_endpoint_ss_comp_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bMaxBurst;
+ uByte bmAttributes;
+ uWord wBytesPerInterval;
+} __packed;
+
+struct usb2_string_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bString[126];
+ uByte bUnused;
+} __packed;
+
+#define USB_MAKE_STRING_DESC(m,name) \
+struct name { \
+ uByte bLength; \
+ uByte bDescriptorType; \
+ uByte bData[sizeof((uint8_t []){m})]; \
+} __packed; \
+static const struct name name = { \
+ .bLength = sizeof(struct name), \
+ .bDescriptorType = UDESC_STRING, \
+ .bData = { m }, \
+}
+
+struct usb2_hub_descriptor {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+#define UHD_PWR 0x0003
+#define UHD_PWR_GANGED 0x0000
+#define UHD_PWR_INDIVIDUAL 0x0001
+#define UHD_PWR_NO_SWITCH 0x0002
+#define UHD_COMPOUND 0x0004
+#define UHD_OC 0x0018
+#define UHD_OC_GLOBAL 0x0000
+#define UHD_OC_INDIVIDUAL 0x0008
+#define UHD_OC_NONE 0x0010
+#define UHD_TT_THINK 0x0060
+#define UHD_TT_THINK_8 0x0000
+#define UHD_TT_THINK_16 0x0020
+#define UHD_TT_THINK_24 0x0040
+#define UHD_TT_THINK_32 0x0060
+#define UHD_PORT_IND 0x0080
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+#define UHD_PWRON_FACTOR 2
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[32]; /* max 255 ports */
+#define UHD_NOT_REMOV(desc, i) \
+ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1)
+ uByte PortPowerCtrlMask[1]; /* deprecated */
+} __packed;
+
+struct usb2_hub_ss_descriptor {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts; /* max 15 */
+ uWord wHubCharacteristics;
+ uByte bPwrOn2PwrGood; /* delay in 2 ms units */
+ uByte bHubContrCurrent;
+ uByte bHubHdrDecLat;
+ uWord wHubDelay;
+ uByte DeviceRemovable[2]; /* max 15 ports */
+} __packed;
+
+/* minimum HUB descriptor (8-ports maximum) */
+struct usb2_hub_descriptor_min {
+ uByte bDescLength;
+ uByte bDescriptorType;
+ uByte bNbrPorts;
+ uWord wHubCharacteristics;
+ uByte bPwrOn2PwrGood;
+ uByte bHubContrCurrent;
+ uByte DeviceRemovable[1];
+ uByte PortPowerCtrlMask[1];
+} __packed;
+
+struct usb2_device_qualifier {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdUSB;
+ uByte bDeviceClass;
+ uByte bDeviceSubClass;
+ uByte bDeviceProtocol;
+ uByte bMaxPacketSize0;
+ uByte bNumConfigurations;
+ uByte bReserved;
+} __packed;
+
+struct usb2_otg_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bmAttributes;
+#define UOTG_SRP 0x01
+#define UOTG_HNP 0x02
+} __packed;
+
+/* OTG feature selectors */
+#define UOTG_B_HNP_ENABLE 3
+#define UOTG_A_HNP_SUPPORT 4
+#define UOTG_A_ALT_HNP_SUPPORT 5
+
+struct usb2_status {
+ uWord wStatus;
+/* Device status flags */
+#define UDS_SELF_POWERED 0x0001
+#define UDS_REMOTE_WAKEUP 0x0002
+/* Endpoint status flags */
+#define UES_HALT 0x0001
+} __packed;
+
+struct usb2_hub_status {
+ uWord wHubStatus;
+#define UHS_LOCAL_POWER 0x0001
+#define UHS_OVER_CURRENT 0x0002
+ uWord wHubChange;
+} __packed;
+
+struct usb2_port_status {
+ uWord wPortStatus;
+#define UPS_CURRENT_CONNECT_STATUS 0x0001
+#define UPS_PORT_ENABLED 0x0002
+#define UPS_SUSPEND 0x0004
+#define UPS_OVERCURRENT_INDICATOR 0x0008
+#define UPS_RESET 0x0010
+#define UPS_PORT_POWER 0x0100
+#define UPS_LOW_SPEED 0x0200
+#define UPS_HIGH_SPEED 0x0400
+#define UPS_PORT_TEST 0x0800
+#define UPS_PORT_INDICATOR 0x1000
+#define UPS_PORT_MODE_DEVICE 0x8000 /* currently FreeBSD specific */
+ uWord wPortChange;
+#define UPS_C_CONNECT_STATUS 0x0001
+#define UPS_C_PORT_ENABLED 0x0002
+#define UPS_C_SUSPEND 0x0004
+#define UPS_C_OVERCURRENT_INDICATOR 0x0008
+#define UPS_C_PORT_RESET 0x0010
+} __packed;
+
+#endif /* _USB2_STANDARD_H_ */
diff --git a/sys/dev/usb/usb_bus.h b/sys/dev/usb/usb_bus.h
new file mode 100644
index 0000000..59287c4
--- /dev/null
+++ b/sys/dev/usb/usb_bus.h
@@ -0,0 +1,104 @@
+/* $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_BUS_H_
+#define _USB2_BUS_H_
+
+/*
+ * The following structure defines the USB explore message sent to the
+ * USB explore process.
+ */
+
+struct usb2_bus_msg {
+ struct usb2_proc_msg hdr;
+ struct usb2_bus *bus;
+};
+
+/*
+ * The following structure defines the USB statistics structure.
+ */
+struct usb2_bus_stat {
+ uint32_t uds_requests[4];
+};
+
+/*
+ * The following structure defines an USB BUS. There is one USB BUS
+ * for every Host or Device controller.
+ */
+struct usb2_bus {
+ struct usb2_bus_stat stats_err;
+ struct usb2_bus_stat stats_ok;
+ struct usb2_process explore_proc;
+ struct usb2_process roothub_proc;
+ struct root_hold_token *bus_roothold;
+ /*
+ * There are two callback processes. One for Giant locked
+ * callbacks. One for non-Giant locked callbacks. This should
+ * avoid congestion and reduce response time in most cases.
+ */
+ struct usb2_process giant_callback_proc;
+ struct usb2_process non_giant_callback_proc;
+ struct usb2_bus_msg explore_msg[2];
+ struct usb2_bus_msg detach_msg[2];
+ struct usb2_bus_msg attach_msg[2];
+ struct usb2_bus_msg roothub_msg[2];
+ /*
+ * This mutex protects the USB hardware:
+ */
+ struct mtx bus_mtx;
+ struct usb2_perm perm;
+ struct usb2_xfer_queue intr_q;
+ struct usb2_callout power_wdog; /* power management */
+
+ device_t parent;
+ device_t bdev; /* filled by HC driver */
+
+ struct usb2_dma_parent_tag dma_parent_tag[1];
+ struct usb2_dma_tag dma_tags[USB_BUS_DMA_TAG_MAX];
+
+ struct usb2_bus_methods *methods; /* filled by HC driver */
+ struct usb2_device **devices;
+
+ uint32_t hw_power_state; /* see USB_HW_POWER_XXX */
+ uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX];
+ uint32_t transfer_count[4];
+ uint16_t isoc_time_last; /* in milliseconds */
+
+ uint8_t alloc_failed; /* Set if memory allocation failed. */
+ uint8_t driver_added_refcount; /* Current driver generation count */
+ uint8_t usbrev; /* USB revision. See "USB_REV_XXX". */
+
+ uint8_t devices_max; /* maximum number of USB devices */
+ uint8_t do_probe; /* set if USB BUS should be re-probed */
+
+ union {
+ struct usb2_hw_ep_scratch hw_ep_scratch[1];
+ struct usb2_temp_setup temp_setup[1];
+ uint8_t data[128];
+ } scratch[1];
+};
+
+#endif /* _USB2_BUS_H_ */
diff --git a/sys/dev/usb/usb_busdma.c b/sys/dev/usb/usb_busdma.c
new file mode 100644
index 0000000..809c3bf
--- /dev/null
+++ b/sys/dev/usb/usb_busdma.c
@@ -0,0 +1,1426 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+static void usb2_dma_tag_create(struct usb2_dma_tag *, uint32_t, uint32_t);
+static void usb2_dma_tag_destroy(struct usb2_dma_tag *);
+
+#ifdef __FreeBSD__
+static void usb2_dma_lock_cb(void *, bus_dma_lock_op_t);
+static int32_t usb2_m_copy_in_cb(void *, void *, uint32_t);
+static void usb2_pc_alloc_mem_cb(void *, bus_dma_segment_t *, int, int);
+static void usb2_pc_load_mem_cb(void *, bus_dma_segment_t *, int, int);
+static void usb2_pc_common_mem_cb(void *, bus_dma_segment_t *, int, int,
+ uint8_t);
+#endif
+
+#ifdef __NetBSD__
+static int32_t usb2_m_copy_in_cb(void *, caddr_t, uint32_t);
+static void usb2_pc_common_mem_cb(struct usb2_page_cache *,
+ bus_dma_segment_t *, int, int, uint8_t);
+#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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+
+#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 */
+}
+
+/*------------------------------------------------------------------------*
+ * 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 */ BUS_DMA_KEEP_PG_OFFSET,
+ /* lockfn */ &usb2_dma_lock_cb,
+ /* lockarg */ NULL,
+ &tag)) {
+ tag = NULL;
+ }
+ udt->tag = tag;
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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--;
+#if (USB_DEBUG != 0)
+ if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) {
+ /*
+ * This check verifies that the physical address is correct:
+ */
+ DPRINTFN(0, "Page offset was not preserved!\n");
+ error = 1;
+ goto done;
+ }
+#endif
+ 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ /*
+ * We have to unload the previous loaded DMA
+ * pages before trying to load a new one!
+ */
+ bus_dmamap_unload(pc->tag, pc->map);
+
+ /*
+ * 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 {
+
+ /*
+ * We have to unload the previous loaded DMA
+ * pages before trying to load a new one!
+ */
+ bus_dmamap_unload(pc->tag, pc->map);
+
+ /*
+ * 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)
+{
+ if (pc->page_offset_end == pc->page_offset_buf) {
+ /* nothing has been loaded into this page cache! */
+ return;
+ }
+ bus_dmamap_sync(pc->tag, pc->map,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pc_cpu_flush - flush CPU cache
+ *------------------------------------------------------------------------*/
+void
+usb2_pc_cpu_flush(struct usb2_page_cache *pc)
+{
+ if (pc->page_offset_end == pc->page_offset_buf) {
+ /* nothing has been loaded into this page cache! */
+ return;
+ }
+ bus_dmamap_sync(pc->tag, pc->map,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+#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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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--;
+#if (USB_DEBUG != 0)
+ if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) {
+ /*
+ * This check verifies that the physical address is correct:
+ */
+ DPRINTFN(0, "Page offset was not preserved!\n");
+ error = 1;
+ goto done;
+ }
+#endif
+ 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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) {
+
+ /*
+ * We have to unload the previous loaded DMA
+ * pages before trying to load a new one!
+ */
+ bus_dmamap_unload(pc->tag, pc->map);
+
+ /* 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;
+
+ if (len == 0) {
+ /* nothing has been loaded into this page cache */
+ return;
+ }
+ bus_dmamap_sync(pc->tag, pc->map, 0, len,
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ if (len == 0) {
+ /* nothing has been loaded into this page cache */
+ return;
+ }
+ bus_dmamap_sync(pc->tag, pc->map, 0, len,
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+#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++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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->xroot;
+
+ mtx_assert(info->xfer_mtx, MA_OWNED);
+
+ if (xfer->error) {
+ /* some error happened */
+ USB_BUS_LOCK(info->bus);
+ usb2_transfer_done(xfer, 0);
+ USB_BUS_UNLOCK(info->bus);
+ 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) {
+ USB_BUS_LOCK(info->bus);
+ usb2_transfer_done(xfer, USB_ERR_DMA_LOAD_FAILED);
+ USB_BUS_UNLOCK(info->bus);
+ 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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->xfer_mtx, MA_OWNED);
+
+ /* copy error */
+ info->dma_error = udpt->dma_error;
+
+ /* enter workloop again */
+ usb2_command_wrapper(&info->dma_q,
+ info->dma_q.curr);
+}
+
+/*------------------------------------------------------------------------*
+ * 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->isread) {
+ usb2_pc_cpu_invalidate(pc);
+ } else {
+ usb2_pc_cpu_flush(pc);
+ }
+ pc++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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->isread) {
+ usb2_pc_cpu_invalidate(pc);
+ }
+ pc++;
+ }
+}
diff --git a/sys/dev/usb/usb_busdma.h b/sys/dev/usb/usb_busdma.h
new file mode 100644
index 0000000..3c1600b
--- /dev/null
+++ b/sys/dev/usb/usb_busdma.h
@@ -0,0 +1,183 @@
+/* $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/usb/usb_cdc.h b/sys/dev/usb/usb_cdc.h
new file mode 100644
index 0000000..d1e3dcb
--- /dev/null
+++ b/sys/dev/usb/usb_cdc.h
@@ -0,0 +1,191 @@
+/* $NetBSD: usbcdc.h,v 1.9 2004/10/23 13:24:24 augustss Exp $ */
+/* $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.
+ */
+
+#ifndef _USB_CDC_H_
+#define _USB_CDC_H_
+
+#define UDESCSUB_CDC_HEADER 0
+#define UDESCSUB_CDC_CM 1 /* Call Management */
+#define UDESCSUB_CDC_ACM 2 /* Abstract Control Model */
+#define UDESCSUB_CDC_DLM 3 /* Direct Line Management */
+#define UDESCSUB_CDC_TRF 4 /* Telephone Ringer */
+#define UDESCSUB_CDC_TCLSR 5 /* Telephone Call */
+#define UDESCSUB_CDC_UNION 6
+#define UDESCSUB_CDC_CS 7 /* Country Selection */
+#define UDESCSUB_CDC_TOM 8 /* Telephone Operational Modes */
+#define UDESCSUB_CDC_USBT 9 /* USB Terminal */
+#define UDESCSUB_CDC_NCT 10
+#define UDESCSUB_CDC_PUF 11
+#define UDESCSUB_CDC_EUF 12
+#define UDESCSUB_CDC_MCMF 13
+#define UDESCSUB_CDC_CCMF 14
+#define UDESCSUB_CDC_ENF 15
+#define UDESCSUB_CDC_ANF 16
+
+struct usb2_cdc_header_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uWord bcdCDC;
+} __packed;
+
+struct usb2_cdc_cm_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmCapabilities;
+#define USB_CDC_CM_DOES_CM 0x01
+#define USB_CDC_CM_OVER_DATA 0x02
+ uByte bDataInterface;
+} __packed;
+
+struct usb2_cdc_acm_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bmCapabilities;
+#define USB_CDC_ACM_HAS_FEATURE 0x01
+#define USB_CDC_ACM_HAS_LINE 0x02
+#define USB_CDC_ACM_HAS_BREAK 0x04
+#define USB_CDC_ACM_HAS_NETWORK_CONN 0x08
+} __packed;
+
+struct usb2_cdc_union_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte bMasterInterface;
+ uByte bSlaveInterface[1];
+} __packed;
+
+struct usb2_cdc_ethernet_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uByte bDescriptorSubtype;
+ uByte iMacAddress;
+ uDWord bmEthernetStatistics;
+ uWord wMaxSegmentSize;
+ uWord wNumberMCFilters;
+ uByte bNumberPowerFilters;
+} __packed;
+
+#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
+#define UCDC_GET_ENCAPSULATED_RESPONSE 0x01
+#define UCDC_SET_COMM_FEATURE 0x02
+#define UCDC_GET_COMM_FEATURE 0x03
+#define UCDC_ABSTRACT_STATE 0x01
+#define UCDC_COUNTRY_SETTING 0x02
+#define UCDC_CLEAR_COMM_FEATURE 0x04
+#define UCDC_SET_LINE_CODING 0x20
+#define UCDC_GET_LINE_CODING 0x21
+#define UCDC_SET_CONTROL_LINE_STATE 0x22
+#define UCDC_LINE_DTR 0x0001
+#define UCDC_LINE_RTS 0x0002
+#define UCDC_SEND_BREAK 0x23
+#define UCDC_BREAK_ON 0xffff
+#define UCDC_BREAK_OFF 0x0000
+
+struct usb2_cdc_abstract_state {
+ uWord wState;
+#define UCDC_IDLE_SETTING 0x0001
+#define UCDC_DATA_MULTIPLEXED 0x0002
+} __packed;
+
+#define UCDC_ABSTRACT_STATE_LENGTH 2
+
+struct usb2_cdc_line_state {
+ uDWord dwDTERate;
+ uByte bCharFormat;
+#define UCDC_STOP_BIT_1 0
+#define UCDC_STOP_BIT_1_5 1
+#define UCDC_STOP_BIT_2 2
+ uByte bParityType;
+#define UCDC_PARITY_NONE 0
+#define UCDC_PARITY_ODD 1
+#define UCDC_PARITY_EVEN 2
+#define UCDC_PARITY_MARK 3
+#define UCDC_PARITY_SPACE 4
+ uByte bDataBits;
+} __packed;
+
+#define UCDC_LINE_STATE_LENGTH 7
+
+struct usb2_cdc_notification {
+ uByte bmRequestType;
+#define UCDC_NOTIFICATION 0xa1
+ uByte bNotification;
+#define UCDC_N_NETWORK_CONNECTION 0x00
+#define UCDC_N_RESPONSE_AVAILABLE 0x01
+#define UCDC_N_AUX_JACK_HOOK_STATE 0x08
+#define UCDC_N_RING_DETECT 0x09
+#define UCDC_N_SERIAL_STATE 0x20
+#define UCDC_N_CALL_STATE_CHANGED 0x28
+#define UCDC_N_LINE_STATE_CHANGED 0x29
+#define UCDC_N_CONNECTION_SPEED_CHANGE 0x2a
+ uWord wValue;
+ uWord wIndex;
+ uWord wLength;
+ uByte data[16];
+} __packed;
+
+#define UCDC_NOTIFICATION_LENGTH 8
+
+/*
+ * Bits set in the SERIAL STATE notifcation (first byte of data)
+ */
+
+#define UCDC_N_SERIAL_OVERRUN 0x40
+#define UCDC_N_SERIAL_PARITY 0x20
+#define UCDC_N_SERIAL_FRAMING 0x10
+#define UCDC_N_SERIAL_RI 0x08
+#define UCDC_N_SERIAL_BREAK 0x04
+#define UCDC_N_SERIAL_DSR 0x02
+#define UCDC_N_SERIAL_DCD 0x01
+
+/* Serial state bit masks */
+#define UCDC_MDM_RXCARRIER 0x01
+#define UCDC_MDM_TXCARRIER 0x02
+#define UCDC_MDM_BREAK 0x04
+#define UCDC_MDM_RING 0x08
+#define UCDC_MDM_FRAMING_ERR 0x10
+#define UCDC_MDM_PARITY_ERR 0x20
+#define UCDC_MDM_OVERRUN_ERR 0x40
+
+#endif /* _USB_CDC_H_ */
diff --git a/sys/dev/usb/usb_compat_linux.c b/sys/dev/usb/usb_compat_linux.c
new file mode 100644
index 0000000..a544a98
--- /dev/null
+++ b/sys/dev/usb/usb_compat_linux.c
@@ -0,0 +1,1653 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_compat_linux.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_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 *);
+static int usb_start_wait_urb(struct urb *, uint32_t, uint16_t *);
+static const struct usb_device_id *usb_linux_lookup_id(
+ const struct usb_device_id *, struct usb2_attach_arg *);
+static struct usb_driver *usb_linux_get_usb_driver(struct usb_linux_softc *);
+static struct usb_device *usb_linux_create_usb_device(struct usb2_device *,
+ device_t);
+static void usb_linux_cleanup_interface(struct usb_device *,
+ struct usb_interface *);
+static void usb_linux_complete(struct usb2_xfer *);
+static int usb_unlink_urb_sub(struct urb *, uint8_t);
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ 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)
+{
+ ; /* indent fix */
+ switch (usb2_get_speed(dev->bsd_udev)) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ return (USB_MAX_FULL_SPEED_ISOC_FRAMES);
+ default:
+ return (USB_MAX_HIGH_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);
+ }
+}
+
+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();
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_buffer_free
+ *------------------------------------------------------------------------*/
+void
+usb_buffer_free(struct usb_device *dev, uint32_t size,
+ void *addr, uint8_t dma_addr)
+{
+ free(addr, M_USBDEV);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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));
+}
+
+/*------------------------------------------------------------------------*
+ * usb_kill_urb
+ *------------------------------------------------------------------------*/
+void
+usb_kill_urb(struct urb *urb)
+{
+ if (usb_unlink_urb_sub(urb, 1)) {
+ /* ignore */
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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/usb/usb_compat_linux.h b/sys/dev/usb/usb_compat_linux.h
new file mode 100644
index 0000000..8ebb7e3
--- /dev/null
+++ b/sys/dev/usb/usb_compat_linux.h
@@ -0,0 +1,472 @@
+/* $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/usb/usb_controller.h b/sys/dev/usb/usb_controller.h
new file mode 100644
index 0000000..80633d9
--- /dev/null
+++ b/sys/dev/usb/usb_controller.h
@@ -0,0 +1,199 @@
+/* $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_CONTROLLER_H_
+#define _USB2_CONTROLLER_H_
+
+/* defines */
+
+#define USB_BUS_DMA_TAG_MAX 8
+
+/* structure prototypes */
+
+struct usb2_bus;
+struct usb2_page;
+struct usb2_pipe;
+struct usb2_page_cache;
+struct usb2_setup_params;
+struct usb2_hw_ep_profile;
+struct usb2_fs_isoc_schedule;
+struct usb2_config_descriptor;
+struct usb2_endpoint_descriptor;
+
+/* typedefs */
+
+typedef void (usb2_bus_mem_sub_cb_t)(struct usb2_bus *bus, struct usb2_page_cache *pc, struct usb2_page *pg, uint32_t size, uint32_t align);
+typedef void (usb2_bus_mem_cb_t)(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *scb);
+
+/*
+ * The following structure is used to define all the USB BUS
+ * callbacks.
+ */
+struct usb2_bus_methods {
+
+ /* USB Device and Host mode - Mandatory */
+
+ void (*pipe_init) (struct usb2_device *udev, struct usb2_endpoint_descriptor *edesc, struct usb2_pipe *pipe);
+ void (*do_poll) (struct usb2_bus *);
+ void (*xfer_setup) (struct usb2_setup_params *parm);
+ void (*xfer_unsetup) (struct usb2_xfer *xfer);
+ void (*get_dma_delay) (struct usb2_bus *, uint32_t *pdelay);
+ void (*device_suspend) (struct usb2_device *udev);
+ void (*device_resume) (struct usb2_device *udev);
+ void (*set_hw_power) (struct usb2_bus *bus);
+ /*
+ * The following flag is set if one or more control transfers are
+ * active:
+ */
+#define USB_HW_POWER_CONTROL 0x01
+ /*
+ * The following flag is set if one or more bulk transfers are
+ * active:
+ */
+#define USB_HW_POWER_BULK 0x02
+ /*
+ * The following flag is set if one or more interrupt transfers are
+ * active:
+ */
+#define USB_HW_POWER_INTERRUPT 0x04
+ /*
+ * The following flag is set if one or more isochronous transfers
+ * are active:
+ */
+#define USB_HW_POWER_ISOC 0x08
+ /*
+ * The following flag is set if one or more non-root-HUB devices
+ * are present on the given USB bus:
+ */
+#define USB_HW_POWER_NON_ROOT_HUB 0x10
+
+ /* USB Device mode only - Mandatory */
+
+ void (*get_hw_ep_profile) (struct usb2_device *udev, const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr);
+ void (*set_stall) (struct usb2_device *udev, struct usb2_xfer *xfer, struct usb2_pipe *pipe);
+ void (*clear_stall) (struct usb2_device *udev, struct usb2_pipe *pipe);
+
+ /* USB Device and Host mode - Optional */
+
+ void (*roothub_exec) (struct usb2_bus *);
+};
+
+/*
+ * The following structure is used to define all the USB pipe
+ * callbacks.
+ */
+struct usb2_pipe_methods {
+
+ /* Mandatory USB Device and Host mode callbacks: */
+
+ void (*open) (struct usb2_xfer *xfer);
+ void (*close) (struct usb2_xfer *xfer);
+
+ void (*enter) (struct usb2_xfer *xfer);
+ void (*start) (struct usb2_xfer *xfer);
+
+ /* Optional */
+
+ void *info;
+
+ /* Flags */
+
+ uint8_t enter_is_cancelable:1;
+ uint8_t start_is_cancelable:1;
+};
+
+/*
+ * The following structure keeps information about what a hardware USB
+ * endpoint supports.
+ */
+struct usb2_hw_ep_profile {
+ uint16_t max_in_frame_size; /* IN-token direction */
+ uint16_t max_out_frame_size; /* OUT-token direction */
+ uint8_t is_simplex:1;
+ uint8_t support_multi_buffer:1;
+ uint8_t support_bulk:1;
+ uint8_t support_control:1;
+ uint8_t support_interrupt:1;
+ uint8_t support_isochronous:1;
+ uint8_t support_in:1; /* IN-token is supported */
+ uint8_t support_out:1; /* OUT-token is supported */
+};
+
+/*
+ * The following structure is used when trying to allocate hardware
+ * endpoints for an USB configuration in USB device side mode.
+ */
+struct usb2_hw_ep_scratch_sub {
+ const struct usb2_hw_ep_profile *pf;
+ uint16_t max_frame_size;
+ uint8_t hw_endpoint_out;
+ uint8_t hw_endpoint_in;
+ uint8_t needs_ep_type;
+ uint8_t needs_in:1;
+ uint8_t needs_out:1;
+};
+
+/*
+ * The following structure is used when trying to allocate hardware
+ * endpoints for an USB configuration in USB device side mode.
+ */
+struct usb2_hw_ep_scratch {
+ struct usb2_hw_ep_scratch_sub ep[USB_EP_MAX];
+ struct usb2_hw_ep_scratch_sub *ep_max;
+ struct usb2_config_descriptor *cd;
+ struct usb2_device *udev;
+ struct usb2_bus_methods *methods;
+ uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16];
+ uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16];
+};
+
+/*
+ * The following structure is used when generating USB descriptors
+ * from USB templates.
+ */
+struct usb2_temp_setup {
+ void *buf;
+ uint32_t size;
+ uint8_t usb2_speed;
+ uint8_t self_powered;
+ uint8_t bNumEndpoints;
+ uint8_t bInterfaceNumber;
+ uint8_t bAlternateSetting;
+ uint8_t bConfigurationValue;
+ usb2_error_t err;
+};
+
+/* prototypes */
+
+void usb2_bus_mem_flush_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb);
+uint8_t usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat, usb2_bus_mem_cb_t *cb);
+void usb2_bus_mem_free_all(struct usb2_bus *bus, usb2_bus_mem_cb_t *cb);
+void usb2_bus_roothub_exec(struct usb2_bus *bus);
+uint16_t usb2_isoc_time_expand(struct usb2_bus *bus, uint16_t isoc_time_curr);
+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);
+uint8_t usb2_fs_isoc_schedule_alloc(struct usb2_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len);
+
+#endif /* _USB2_CONTROLLER_H_ */
diff --git a/sys/dev/usb/usb_core.c b/sys/dev/usb/usb_core.c
new file mode 100644
index 0000000..a4191ad
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb_core.h>
+#include <dev/usb/usb_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(usb, 1);
diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h
new file mode 100644
index 0000000..cc55316
--- /dev/null
+++ b/sys/dev/usb/usb_core.h
@@ -0,0 +1,467 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb_revision.h>
+
+#include "usb_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_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)
+
+#define USB_BUS_LOCK(_b) mtx_lock(&(_b)->bus_mtx)
+#define USB_BUS_UNLOCK(_b) mtx_unlock(&(_b)->bus_mtx)
+#define USB_BUS_LOCK_ASSERT(_b, _t) mtx_assert(&(_b)->bus_mtx, _t)
+#define USB_XFER_LOCK(_x) mtx_lock((_x)->xroot->xfer_mtx)
+#define USB_XFER_UNLOCK(_x) mtx_unlock((_x)->xroot->xfer_mtx)
+#define USB_XFER_LOCK_ASSERT(_x, _t) mtx_assert((_x)->xroot->xfer_mtx, _t)
+/* 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_xfer_root *xroot; /* 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; /* bus index */
+ uint8_t dev_index; /* device index */
+ uint8_t iface_index; /* interface index */
+ uint8_t fifo_index; /* FIFO index */
+ uint8_t is_read; /* set if location has read access */
+ uint8_t is_write; /* set if location has write access */
+ uint8_t is_uref; /* set if USB refcount decr. needed */
+ uint8_t is_usbfs; /* set if USB-FS is active */
+};
+
+/* 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_mode(struct usb2_device *udev);
+uint8_t usb2_get_speed(struct usb2_device *udev);
+uint32_t usb2_get_isoc_fps(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);
+uint8_t usb2_transfer_pending(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);
+void usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode);
+
+#endif /* _USB2_CORE_H_ */
diff --git a/sys/dev/usb/usb_debug.c b/sys/dev/usb/usb_debug.c
new file mode 100644
index 0000000..b7eeea7
--- /dev/null
+++ b/sys/dev/usb/usb_debug.c
@@ -0,0 +1,152 @@
+/* $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/usb/usb.h>
+#include <dev/usb/usb_defs.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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");
+}
+
+/*------------------------------------------------------------------------*
+ * 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");
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dump_xfer
+ *
+ * This function dumps information about an USB transfer.
+ *------------------------------------------------------------------------*/
+void
+usb2_dump_xfer(struct usb2_xfer *xfer)
+{
+ struct usb2_device *udev;
+ printf("usb2_dump_xfer: xfer=%p\n", xfer);
+ if (xfer == NULL) {
+ return;
+ }
+ if (xfer->pipe == NULL) {
+ printf("xfer %p: pipe=NULL\n",
+ xfer);
+ return;
+ }
+ udev = xfer->xroot->udev;
+ printf("xfer %p: udev=%p vid=0x%04x pid=0x%04x addr=%d "
+ "pipe=%p ep=0x%02x attr=0x%02x\n",
+ xfer, udev,
+ UGETW(udev->ddesc.idVendor),
+ UGETW(udev->ddesc.idProduct),
+ udev->address, xfer->pipe,
+ xfer->pipe->edesc->bEndpointAddress,
+ xfer->pipe->edesc->bmAttributes);
+}
diff --git a/sys/dev/usb/usb_debug.h b/sys/dev/usb/usb_debug.h
new file mode 100644
index 0000000..92dcbd5
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb_defs.h b/sys/dev/usb/usb_defs.h
new file mode 100644
index 0000000..64caf39
--- /dev/null
+++ b/sys/dev/usb/usb_defs.h
@@ -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.
+ */
+
+#ifndef _USB2_DEFS_H_
+#define _USB2_DEFS_H_
+
+/* Definition of some USB constants */
+
+#define USB_BUS_MAX 256 /* units */
+#define USB_DEV_MAX 128 /* units */
+#define USB_IFACE_MAX 32 /* units */
+#define USB_EP_MAX (2*16) /* hardcoded */
+#define USB_FIFO_MAX (4 * USB_EP_MAX)
+
+#define USB_ROOT_HUB_ADDR 1 /* index */
+
+#define USB_MIN_DEVICES 2 /* unused + root HUB */
+
+#define USB_MAX_DEVICES USB_DEV_MAX /* including virtual root HUB and
+ * address zero */
+#define USB_MAX_ENDPOINTS USB_EP_MAX /* 2 directions on 16 endpoints */
+
+#define USB_UNCONFIG_INDEX 0xFF /* internal use only */
+#define USB_IFACE_INDEX_ANY 0xFF /* internal use only */
+
+#define USB_START_ADDR 0 /* default USB device BUS address
+ * after USB bus reset */
+
+#define USB_CONTROL_ENDPOINT 0 /* default control endpoint */
+
+#define USB_FRAMES_PER_SECOND_FS 1000 /* full speed */
+#define USB_FRAMES_PER_SECOND_HS 8000 /* high speed */
+
+#define USB_FS_BYTES_PER_HS_UFRAME 188 /* bytes */
+#define USB_HS_MICRO_FRAMES_MAX 8 /* units */
+
+/* sanity checks */
+
+#if (USB_FIFO_MAX < USB_EP_MAX)
+#error "There cannot be less FIFOs than USB endpoints."
+#endif
+#if (USB_FIFO_MAX & 1)
+#error "Number of FIFOs must be odd."
+#endif
+#if (USB_EP_MAX < (2*16))
+#error "Number of hardware USB endpoints cannot be less than 32."
+#endif
+#if (USB_MAX_DEVICES < USB_MIN_DEVICES)
+#error "Minimum number of devices is greater than maximum number of devices."
+#endif
+#if (USB_ROOT_HUB_ADDR >= USB_MIN_DEVICES)
+#error "The root hub address must be less than USB_MIN_DEVICES."
+#endif
+#endif /* _USB2_DEFS_H_ */
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c
new file mode 100644
index 0000000..f8e2c96
--- /dev/null
+++ b/sys/dev/usb/usb_dev.c
@@ -0,0 +1,2814 @@
+/* $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/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR usb2_fifo_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_generic.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_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 **);
+static uint32_t usb2_path_convert(const char *);
+static int usb2_check_access(int, struct usb2_perm *);
+static int usb2_fifo_open(struct usb2_fifo *, struct file *,
+ struct thread *, int);
+static void usb2_fifo_close(struct usb2_fifo *, struct thread *, int);
+static void usb2_dev_init(void *);
+static void usb2_dev_init_post(void *);
+static void usb2_dev_uninit(void *);
+static int usb2_fifo_uiomove(struct usb2_fifo *, void *, int,
+ struct uio *);
+static void usb2_fifo_check_methods(struct usb2_fifo_methods *);
+static void usb2_clone(void *, USB_UCRED char *, int, struct cdev **);
+static struct usb2_fifo *usb2_fifo_alloc(void);
+static struct usb2_pipe *usb2_dev_get_pipe(struct usb2_device *, uint8_t,
+ uint8_t, uint8_t);
+
+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);
+
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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 dev_ep_index;
+
+ if (fp) {
+ /* check if we need uref */
+ ploc->is_uref = devloc ? 0 : 1;
+ /* get devloc - already verified */
+ devloc = USB_P2U(fp->f_data);
+ /* get file flags */
+ fflags = fp->f_flag;
+ } else {
+ /* 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->fifo_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;
+ }
+ /* 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;
+ ploc->is_usbfs = 0;
+ /* NOTE: variable overloading: */
+ dev_ep_index = ploc->fifo_index;
+ } else {
+ /* initialise "is_usbfs" flag */
+ ploc->is_usbfs = 0;
+ dev_ep_index = 255; /* dummy */
+
+ /* check for write */
+ if (fflags & FWRITE) {
+ ppf = ploc->udev->fifo;
+ f = ppf[ploc->fifo_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;
+ }
+ /* check if USB-FS is active */
+ if (f->fs_ep_max != 0) {
+ ploc->is_usbfs = 1;
+ }
+ /*
+ * Get real endpoint index associated with
+ * this FIFO:
+ */
+ dev_ep_index = f->dev_ep_index;
+ } else {
+ ploc->txfifo = NULL;
+ ploc->is_write = 0; /* no ref */
+ }
+
+ /* check for read */
+ if (fflags & FREAD) {
+ ppf = ploc->udev->fifo;
+ f = ppf[ploc->fifo_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;
+ }
+ /* check if USB-FS is active */
+ if (f->fs_ep_max != 0) {
+ ploc->is_usbfs = 1;
+ }
+ /*
+ * Get real endpoint index associated with
+ * this FIFO:
+ */
+ dev_ep_index = f->dev_ep_index;
+ } else {
+ ploc->rxfifo = NULL;
+ ploc->is_read = 0; /* no ref */
+ }
+ }
+
+ /* check if we require an interface */
+ ploc->iface = usb2_get_iface(ploc->udev, ploc->iface_index);
+ if (dev_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;
+ }
+ }
+ /* when everything is OK we increment the refcounts */
+ if (ploc->is_write) {
+ DPRINTFN(2, "ref write\n");
+ ploc->txfifo->refcount++;
+ }
+ if (ploc->is_read) {
+ DPRINTFN(2, "ref read\n");
+ ploc->rxfifo->refcount++;
+ }
+ if (ploc->is_uref) {
+ DPRINTFN(2, "ref udev - needed\n");
+ ploc->udev->refcount++;
+ }
+ 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_uref_location
+ *
+ * This function is used to upgrade an USB reference to include the
+ * USB device reference on a USB location.
+ *
+ * Return values:
+ * 0: Success, refcount incremented on the given USB device.
+ * Else: Failure.
+ *------------------------------------------------------------------------*/
+static usb2_error_t
+usb2_uref_location(struct usb2_location *ploc)
+{
+ /*
+ * Check if we already got an USB reference on this location:
+ */
+ if (ploc->is_uref) {
+ return (0); /* success */
+ }
+ mtx_lock(&usb2_ref_lock);
+ if (ploc->bus != devclass_get_softc(usb2_devclass_ptr, ploc->bus_index)) {
+ DPRINTFN(2, "bus changed at %u\n", ploc->bus_index);
+ goto error;
+ }
+ if (ploc->udev != ploc->bus->devices[ploc->dev_index]) {
+ DPRINTFN(2, "device changed at %u\n", ploc->dev_index);
+ goto error;
+ }
+ if (ploc->udev->refcount == USB_DEV_REF_MAX) {
+ DPRINTFN(2, "no dev ref\n");
+ goto error;
+ }
+ DPRINTFN(2, "ref udev\n");
+ ploc->udev->refcount++;
+ mtx_unlock(&usb2_ref_lock);
+
+ /* set "uref" */
+ ploc->is_uref = 1;
+
+ /*
+ * 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);
+}
+
+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;
+
+ /* NOTE: variable overloading: */
+ uint8_t dev_ep_index = ploc->fifo_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;
+ 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;
+ 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);
+}
+
+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;
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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");
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ if (ep_index != 0) {
+ /*
+ * Non-control endpoints are always
+ * associated with an interface:
+ */
+ iface = usb2_get_iface(udev, iface_index);
+ if (iface == NULL) {
+ return (EINVAL);
+ }
+ if (iface->idesc == NULL) {
+ return (EINVAL);
+ }
+ } else {
+ iface = NULL;
+ }
+ /* scan down the permissions tree */
+ if ((iface != NULL) &&
+ (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);
+ }
+ /*
+ * NOTE: Variable overloading. "usb2_fifo_create" will update
+ * the FIFO index. Right here we can assume that the
+ * "fifo_index" is the same like the endpoint number without
+ * direction mask, if the "fifo_index" is less than 16.
+ */
+ err = usb2_check_thread_perm(loc.udev, td, fflags,
+ loc.iface_index, loc.fifo_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;
+}
+
+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);
+}
+
+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 0666, because we
+ * check the permissions again in the open routine against the
+ * real USB permissions which are not 0666. Else USB access
+ * will be limited to one user and one group.
+ */
+ usb2_dev = make_dev(&usb2_devsw, 0, UID_ROOT, GID_OPERATOR,
+ 0666, 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");
+ }
+}
+
+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);
+}
+
+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 /* need uref */ );;
+
+ /* 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 (ENOIOCTL);
+ }
+ 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;
+ struct usb2_fifo *f;
+ int fflags;
+ int err;
+
+ err = usb2_ref_device(fp, &loc, 1 /* no uref */ );;
+ if (err) {
+ return (ENXIO);
+ }
+ fflags = fp->f_flag;
+
+ DPRINTFN(2, "fflags=%u, cmd=0x%lx\n", fflags, cmd);
+
+ f = NULL; /* set default value */
+ err = ENOIOCTL; /* set default value */
+
+ if (fflags & FWRITE) {
+ f = loc.txfifo;
+ err = usb2_ioctl_f_sub(f, cmd, addr, td);
+ }
+ if (fflags & FREAD) {
+ f = loc.rxfifo;
+ err = usb2_ioctl_f_sub(f, cmd, addr, td);
+ }
+ if (err == ENOIOCTL) {
+ err = (f->methods->f_ioctl) (f, cmd, addr, fflags, td);
+ if (err == ENOIOCTL) {
+ if (usb2_uref_location(&loc)) {
+ err = ENXIO;
+ goto done;
+ }
+ err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags, td);
+ }
+ }
+ if (err == ENOIOCTL) {
+ err = ENOTTY;
+ }
+done:
+ 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;
+
+ revents = usb2_ref_device(fp, &loc, 1 /* no uref */ );;
+ if (revents) {
+ return (POLLHUP);
+ }
+ fflags = fp->f_flag;
+
+ /* Figure out who needs service */
+
+ if ((events & (POLLOUT | POLLWRNORM)) &&
+ (fflags & FWRITE)) {
+
+ f = loc.txfifo;
+
+ mtx_lock(f->priv_mtx);
+
+ if (!loc.is_usbfs) {
+ 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 (!loc.is_usbfs) {
+ 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);
+
+ if (!loc.is_usbfs) {
+ /* 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);
+
+ /* check for permanent read error */
+ if (f->flag_iserror) {
+ err = EIO;
+ goto done;
+ }
+ /* check if USB-FS interface is active */
+ if (loc.is_usbfs) {
+ /*
+ * The queue is used for events that should be
+ * retrieved using the "USB_FS_COMPLETE" ioctl.
+ */
+ err = EINVAL;
+ goto done;
+ }
+ while (uio->uio_resid > 0) {
+
+ USB_IF_DEQUEUE(&f->used_q, m);
+
+ 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;
+ }
+ if (f->methods->f_filter_read) {
+ /*
+ * Sometimes it is convenient to process data at the
+ * expense of a userland process instead of a kernel
+ * process.
+ */
+ (f->methods->f_filter_read) (f, m);
+ }
+ 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);
+
+ /* check for permanent write error */
+ if (f->flag_iserror) {
+ err = EIO;
+ goto done;
+ }
+ /* check if USB-FS interface is active */
+ if (loc.is_usbfs) {
+ /*
+ * The queue is used for events that should be
+ * retrieved using the "USB_FS_COMPLETE" ioctl.
+ */
+ err = EINVAL;
+ goto done;
+ }
+ if (f->queue_data == NULL) {
+ /* start write transfer, if not already started */
+ (f->methods->f_start_write) (f);
+ }
+ /* we allow writing zero length data */
+ do {
+ USB_IF_DEQUEUE(&f->free_q, m);
+
+ 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;
+ }
+ 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;
+ }
+ if (f->methods->f_filter_write) {
+ /*
+ * Sometimes it is convenient to process data at the
+ * expense of a userland process instead of a kernel
+ * process.
+ */
+ (f->methods->f_filter_write) (f, m);
+ }
+ 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);
+ }
+}
+
+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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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 (ENOIOCTL);
+}
+
+static void
+usb2_fifo_dummy_cmd(struct usb2_fifo *fifo)
+{
+ fifo->flag_flushing = 0; /* not flushing */
+}
+
+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_ioctl_post == NULL)
+ pm->f_ioctl_post = &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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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_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_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));
+}
+
+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);
+}
+
+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;
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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_POLL(&f->used_q, m);
+
+ if (m) {
+ *plen = m->cur_data_len;
+ *pptr = m->cur_data_ptr;
+
+ return (1);
+ }
+ return (0);
+}
+
+void
+usb2_fifo_get_data_error(struct usb2_fifo *f)
+{
+ f->flag_iserror = 1;
+ usb2_fifo_wakeup(f);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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/usb/usb_dev.h b/sys/dev/usb/usb_dev.h
new file mode 100644
index 0000000..6203572
--- /dev/null
+++ b/sys/dev/usb/usb_dev.h
@@ -0,0 +1,168 @@
+/* $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;
+struct usb2_mbuf;
+
+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);
+typedef void (usb2_fifo_filter_t)(struct usb2_fifo *fifo, struct usb2_mbuf *m);
+
+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" and "usb2_fifo_filter_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;
+ /*
+ * NOTE: The post-ioctl callback is called after the USB reference
+ * gets locked in the IOCTL handler:
+ */
+ usb2_fifo_ioctl_t *f_ioctl_post;
+ 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;
+ usb2_fifo_filter_t *f_filter_read;
+ usb2_fifo_filter_t *f_filter_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_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_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/usb/usb_device.c b/sys/dev/usb/usb_device.c
new file mode 100644
index 0000000..439ad2b
--- /dev/null
+++ b/sys/dev/usb/usb_device.c
@@ -0,0 +1,2192 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include "usbdevs.h"
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_msctest.h>
+#include <dev/usb/usb_generic.h>
+
+#include <dev/usb/quirk/usb_quirk.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+/* function prototypes */
+
+static void usb2_fill_pipe_data(struct usb2_device *, uint8_t,
+ struct usb2_endpoint_descriptor *, struct usb2_pipe *);
+static void usb2_free_pipe_data(struct usb2_device *, uint8_t, uint8_t);
+static void usb2_free_iface_data(struct usb2_device *);
+static void usb2_detach_device_sub(struct usb2_device *, device_t *,
+ uint8_t);
+static uint8_t usb2_probe_and_attach_sub(struct usb2_device *,
+ struct usb2_attach_arg *);
+static void usb2_init_attach_arg(struct usb2_device *,
+ struct usb2_attach_arg *);
+static void usb2_suspend_resume_sub(struct usb2_device *, device_t,
+ uint8_t);
+static void usb2_clear_stall_proc(struct usb2_proc_msg *_pm);
+static void usb2_check_strings(struct usb2_device *);
+static usb2_error_t usb2_fill_iface_data(struct usb2_device *, uint8_t,
+ uint8_t);
+static void usb2_notify_addq(const char *type, struct usb2_device *);
+static void usb2_fifo_free_wrap(struct usb2_device *, uint8_t, uint8_t);
+
+/* 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) {
+ USB_BUS_LOCK(udev->bus);
+ (udev->bus->methods->clear_stall) (udev, pipe);
+ USB_BUS_UNLOCK(udev->bus);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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 generic FIFOs for this interface, except control
+ * endpoint FIFOs:
+ */
+ 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);
+ }
+ USB_BUS_LOCK(udev->bus);
+
+ /* 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 */
+ USB_BUS_UNLOCK(udev->bus);
+ 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);
+ }
+ USB_BUS_UNLOCK(udev->bus);
+ 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");
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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");
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_suspend_resume
+ *
+ * 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);
+
+ USB_BUS_LOCK(udev->bus);
+ /* filter the suspend events */
+ if (udev->flags.suspended == do_suspend) {
+ USB_BUS_UNLOCK(udev->bus);
+ /* nothing to do */
+ return (0);
+ }
+ udev->flags.suspended = do_suspend;
+ USB_BUS_UNLOCK(udev->bus);
+
+ /* 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 */
+ USB_BUS_UNLOCK(udev->bus);
+ mtx_lock(udev->default_mtx);
+
+ /* Start clear stall callback */
+ usb2_transfer_start(udev->default_xfer[1]);
+
+ /* Change lock */
+ mtx_unlock(udev->default_mtx);
+ USB_BUS_LOCK(udev->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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.
+ *
+ * Device index zero is not used and device index 1 should
+ * always be the root hub.
+ */
+ for (device_index = USB_ROOT_HUB_ADDR;
+ (device_index != bus->devices_max) &&
+ (bus->devices[device_index] != NULL);
+ device_index++) /* nop */;
+
+ if (device_index == bus->devices_max) {
+ 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;
+ /*
+ * We need to force the power mode to "on" because there are plenty
+ * of USB devices out there that do not work very well with
+ * automatic suspend and resume!
+ */
+ udev->power_mode = USB_POWER_MODE_ON;
+ udev->pwr_save.last_xfer_time = ticks;
+
+ /* 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;
+
+ /* speed combination should be checked by the parent HUB */
+
+ hub = udev->parent_hub;
+
+ /* 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_MS_TO_TICKS(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);
+ /* XXX try to re-enumerate the device */
+ err = usb2_req_re_enumerate(udev, &Giant);
+ if (err) {
+ 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;
+ uint8_t set_config_failed = 0;
+
+ /*
+ * 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) {
+ if (udev->ddesc.bNumConfigurations != 0) {
+ if (!set_config_failed) {
+ set_config_failed = 1;
+ /* XXX try to re-enumerate the device */
+ err = usb2_req_re_enumerate(
+ udev, &Giant);
+ if (err == 0)
+ goto repeat_set_config;
+ }
+ DPRINTFN(0, "Failure selecting "
+ "configuration index %u: %s, port %u, "
+ "addr %u (ignored)\n",
+ config_index, usb2_errstr(err), udev->port_no,
+ udev->address);
+ }
+ /*
+ * Some USB devices do not have any
+ * configurations. Ignore any set config
+ * failures!
+ */
+ err = 0;
+ } 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) == 0) {
+ DPRINTFN(0, "Found possible auto-install "
+ "disk (trying next config)\n");
+ config_index++;
+ goto repeat_set_config;
+ }
+ }
+ } else if (usb2_test_huawei_autoinst_p(udev, &uaa) == 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;
+
+ printf("ugen%u.%u: <%s> at %s (disconnected)\n",
+ device_get_unit(bus->bdev),
+ udev->device_index, udev->manufacturer,
+ device_get_nameunit(bus->bdev));
+
+ /*
+ * 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);
+
+ /* template unsetup, if any */
+ (usb2_temp_unsetup_p) (udev);
+
+ /*
+ * Make sure that our clear-stall messages are not queued
+ * anywhere:
+ */
+ USB_BUS_LOCK(udev->bus);
+ usb2_proc_mwait(&udev->bus->non_giant_callback_proc,
+ &udev->cs_msg[0], &udev->cs_msg[1]);
+ USB_BUS_UNLOCK(udev->bus);
+
+ 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+}
+
+#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 "usbdevs.h"
+#include "usbdevs_data.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);
+ }
+}
+
+/*
+ * Returns:
+ * See: USB_MODE_XXX
+ */
+uint8_t
+usb2_get_mode(struct usb2_device *udev)
+{
+ return (udev->flags.usb2_mode);
+}
+
+/*
+ * Returns:
+ * See: USB_SPEED_XXX
+ */
+uint8_t
+usb2_get_speed(struct usb2_device *udev)
+{
+ return (udev->speed);
+}
+
+uint32_t
+usb2_get_isoc_fps(struct usb2_device *udev)
+{
+ ; /* indent fix */
+ switch (udev->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ return (1000);
+ default:
+ return (8000);
+ }
+}
+
+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);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_fifo_free_wrap
+ *
+ * This function will free the FIFOs.
+ *
+ * Flag values, if "iface_index" is equal to "USB_IFACE_INDEX_ANY".
+ * 0: Free all FIFOs except generic control endpoints.
+ * 1: Free all FIFOs.
+ *
+ * Flag values, if "iface_index" is not equal to "USB_IFACE_INDEX_ANY".
+ * Not used.
+ *------------------------------------------------------------------------*/
+static void
+usb2_fifo_free_wrap(struct usb2_device *udev,
+ uint8_t iface_index, uint8_t flag)
+{
+ struct usb2_fifo *f;
+ 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;
+ }
+ /* Check if the interface index matches */
+ if (iface_index == f->iface_index) {
+ if (f->methods != &usb2_ugen_methods) {
+ /*
+ * Don't free any non-generic FIFOs in
+ * this case.
+ */
+ continue;
+ }
+ if ((f->dev_ep_index == 0) &&
+ (f->fs_xfer == NULL)) {
+ /* no need to free this FIFO */
+ continue;
+ }
+ } else if (iface_index == USB_IFACE_INDEX_ANY) {
+ if ((f->methods == &usb2_ugen_methods) &&
+ (f->dev_ep_index == 0) && (flag == 0) &&
+ (f->fs_xfer == NULL)) {
+ /* no need to free this FIFO */
+ continue;
+ }
+ } else {
+ /* no need to free this FIFO */
+ continue;
+ }
+ /* free this FIFO */
+ usb2_fifo_free(f);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_peer_can_wakeup
+ *
+ * Return values:
+ * 0: Peer cannot do resume signalling.
+ * Else: Peer can do resume signalling.
+ *------------------------------------------------------------------------*/
+uint8_t
+usb2_peer_can_wakeup(struct usb2_device *udev)
+{
+ const struct usb2_config_descriptor *cdp;
+
+ cdp = udev->cdesc;
+ if ((cdp != NULL) && (udev->flags.usb2_mode == USB_MODE_HOST)) {
+ return (cdp->bmAttributes & UC_REMOTE_WAKEUP);
+ }
+ return (0); /* not supported */
+}
diff --git a/sys/dev/usb/usb_device.h b/sys/dev/usb/usb_device.h
new file mode 100644
index 0000000..11686e2
--- /dev/null
+++ b/sys/dev/usb/usb_device.h
@@ -0,0 +1,187 @@
+/* $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 is used for power-save purposes. The data
+ * in this structure is protected by the USB BUS lock.
+ */
+struct usb2_power_save {
+ int last_xfer_time; /* copy of "ticks" */
+ uint32_t type_refs[4]; /* transfer reference count */
+ uint32_t read_refs; /* data read references */
+ uint32_t write_refs; /* data write references */
+ uint8_t suspended; /* set if USB device is suspended */
+};
+
+/*
+ * 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_power_save pwr_save;/* power save data */
+
+ 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);
+uint8_t usb2_peer_can_wakeup(struct usb2_device *udev);
+
+#endif /* _USB2_DEVICE_H_ */
diff --git a/sys/dev/usb/usb_dynamic.c b/sys/dev/usb/usb_dynamic.c
new file mode 100644
index 0000000..6983f64
--- /dev/null
+++ b/sys/dev/usb/usb_dynamic.c
@@ -0,0 +1,155 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_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_test_huawei_autoinst_t usb2_test_huawei_autoinst_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_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_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;
+}
+
+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;
+ }
+}
+
+static uint8_t
+usb2_test_huawei_autoinst_w(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa)
+{
+ return (USB_ERR_INVAL);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+void
+usb2_test_huawei_unload(void *arg)
+{
+ /* reset function pointers */
+
+ usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w;
+
+ /* wait for CPU to exit the loaded functions, if any */
+
+ /* XXX this is a tradeoff */
+
+ pause("WAIT", 16*hz);
+}
diff --git a/sys/dev/usb/usb_dynamic.h b/sys/dev/usb/usb_dynamic.h
new file mode 100644
index 0000000..2c45d09
--- /dev/null
+++ b/sys/dev/usb/usb_dynamic.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.
+ */
+
+#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 usb2_error_t (usb2_test_huawei_autoinst_t)(struct usb2_device *udev,
+ struct usb2_attach_arg *uaa);
+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_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p;
+extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p;
+extern devclass_t usb2_devclass_ptr;
+
+/* function prototypes */
+
+void usb2_test_huawei_unload(void *);
+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/usb/usb_endian.h b/sys/dev/usb/usb_endian.h
new file mode 100644
index 0000000..2f49008
--- /dev/null
+++ b/sys/dev/usb/usb_endian.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_ENDIAN_H_
+#define _USB2_ENDIAN_H_
+
+#include <sys/stdint.h>
+#include <sys/endian.h>
+
+/*
+ * Declare the basic USB record types. USB records have an alignment
+ * of 1 byte and are always packed.
+ */
+typedef uint8_t uByte;
+typedef uint8_t uWord[2];
+typedef uint8_t uDWord[4];
+typedef uint8_t uQWord[8];
+
+/*
+ * Define a set of macros that can get and set data independent of
+ * CPU endianness and CPU alignment requirements:
+ */
+#define UGETB(w) \
+ ((w)[0])
+
+#define UGETW(w) \
+ ((w)[0] | \
+ ((w)[1] << 8))
+
+#define UGETDW(w) \
+ ((w)[0] | \
+ ((w)[1] << 8) | \
+ ((w)[2] << 16) | \
+ ((w)[3] << 24))
+
+#define UGETQW(w) \
+ ((w)[0] | \
+ ((w)[1] << 8) | \
+ ((w)[2] << 16) | \
+ ((w)[3] << 24) | \
+ (((uint64_t)((w)[4])) << 32) | \
+ (((uint64_t)((w)[5])) << 40) | \
+ (((uint64_t)((w)[6])) << 48) | \
+ (((uint64_t)((w)[7])) << 56))
+
+#define USETB(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+} while (0)
+
+#define USETW(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+ (w)[1] = (uint8_t)((v) >> 8); \
+} while (0)
+
+#define USETDW(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+ (w)[1] = (uint8_t)((v) >> 8); \
+ (w)[2] = (uint8_t)((v) >> 16); \
+ (w)[3] = (uint8_t)((v) >> 24); \
+} while (0)
+
+#define USETQW(w,v) do { \
+ (w)[0] = (uint8_t)(v); \
+ (w)[1] = (uint8_t)((v) >> 8); \
+ (w)[2] = (uint8_t)((v) >> 16); \
+ (w)[3] = (uint8_t)((v) >> 24); \
+ (w)[4] = (uint8_t)((v) >> 32); \
+ (w)[5] = (uint8_t)((v) >> 40); \
+ (w)[6] = (uint8_t)((v) >> 48); \
+ (w)[7] = (uint8_t)((v) >> 56); \
+} while (0)
+
+#define USETW2(w,b1,b0) do { \
+ (w)[0] = (uint8_t)(b0); \
+ (w)[1] = (uint8_t)(b1); \
+} while (0)
+
+#define USETW4(w,b3,b2,b1,b0) do { \
+ (w)[0] = (uint8_t)(b0); \
+ (w)[1] = (uint8_t)(b1); \
+ (w)[2] = (uint8_t)(b2); \
+ (w)[3] = (uint8_t)(b3); \
+} while (0)
+
+#define USETW8(w,b7,b6,b5,b4,b3,b2,b1,b0) do { \
+ (w)[0] = (uint8_t)(b0); \
+ (w)[1] = (uint8_t)(b1); \
+ (w)[2] = (uint8_t)(b2); \
+ (w)[3] = (uint8_t)(b3); \
+ (w)[4] = (uint8_t)(b4); \
+ (w)[5] = (uint8_t)(b5); \
+ (w)[6] = (uint8_t)(b6); \
+ (w)[7] = (uint8_t)(b7); \
+} while (0)
+
+#endif /* _USB2_ENDIAN_H_ */
diff --git a/sys/dev/usb/usb_error.c b/sys/dev/usb/usb_error.c
new file mode 100644
index 0000000..daece10
--- /dev/null
+++ b/sys/dev/usb/usb_error.c
@@ -0,0 +1,73 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#include <dev/usb/usb_core.h>
+
+static const char* usb_errstr_table[USB_ERR_MAX] = {
+ [USB_ERR_NORMAL_COMPLETION] = "USB_ERR_NORMAL_COMPLETION",
+ [USB_ERR_PENDING_REQUESTS] = "USB_ERR_PENDING_REQUESTS",
+ [USB_ERR_NOT_STARTED] = "USB_ERR_NOT_STARTED",
+ [USB_ERR_INVAL] = "USB_ERR_INVAL",
+ [USB_ERR_NOMEM] = "USB_ERR_NOMEM",
+ [USB_ERR_CANCELLED] = "USB_ERR_CANCELLED",
+ [USB_ERR_BAD_ADDRESS] = "USB_ERR_BAD_ADDRESS",
+ [USB_ERR_BAD_BUFSIZE] = "USB_ERR_BAD_BUFSIZE",
+ [USB_ERR_BAD_FLAG] = "USB_ERR_BAD_FLAG",
+ [USB_ERR_NO_CALLBACK] = "USB_ERR_NO_CALLBACK",
+ [USB_ERR_IN_USE] = "USB_ERR_IN_USE",
+ [USB_ERR_NO_ADDR] = "USB_ERR_NO_ADDR",
+ [USB_ERR_NO_PIPE] = "USB_ERR_NO_PIPE",
+ [USB_ERR_ZERO_NFRAMES] = "USB_ERR_ZERO_NFRAMES",
+ [USB_ERR_ZERO_MAXP] = "USB_ERR_ZERO_MAXP",
+ [USB_ERR_SET_ADDR_FAILED] = "USB_ERR_SET_ADDR_FAILED",
+ [USB_ERR_NO_POWER] = "USB_ERR_NO_POWER",
+ [USB_ERR_TOO_DEEP] = "USB_ERR_TOO_DEEP",
+ [USB_ERR_IOERROR] = "USB_ERR_IOERROR",
+ [USB_ERR_NOT_CONFIGURED] = "USB_ERR_NOT_CONFIGURED",
+ [USB_ERR_TIMEOUT] = "USB_ERR_TIMEOUT",
+ [USB_ERR_SHORT_XFER] = "USB_ERR_SHORT_XFER",
+ [USB_ERR_STALLED] = "USB_ERR_STALLED",
+ [USB_ERR_INTERRUPTED] = "USB_ERR_INTERRUPTED",
+ [USB_ERR_DMA_LOAD_FAILED] = "USB_ERR_DMA_LOAD_FAILED",
+ [USB_ERR_BAD_CONTEXT] = "USB_ERR_BAD_CONTEXT",
+ [USB_ERR_NO_ROOT_HUB] = "USB_ERR_NO_ROOT_HUB",
+ [USB_ERR_NO_INTR_THREAD] = "USB_ERR_NO_INTR_THREAD",
+ [USB_ERR_NOT_LOCKED] = "USB_ERR_NOT_LOCKED",
+};
+
+/*------------------------------------------------------------------------*
+ * 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_errstr_table[err] : "USB_ERR_UNKNOWN");
+}
diff --git a/sys/dev/usb/usb_error.h b/sys/dev/usb/usb_error.h
new file mode 100644
index 0000000..91f0245
--- /dev/null
+++ b/sys/dev/usb/usb_error.h
@@ -0,0 +1,63 @@
+/* $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_ERROR_H_
+#define _USB2_ERROR_H_
+
+enum { /* keep in sync with usb_errstr_table */
+ USB_ERR_NORMAL_COMPLETION = 0,
+ USB_ERR_PENDING_REQUESTS, /* 1 */
+ USB_ERR_NOT_STARTED, /* 2 */
+ USB_ERR_INVAL, /* 3 */
+ USB_ERR_NOMEM, /* 4 */
+ USB_ERR_CANCELLED, /* 5 */
+ USB_ERR_BAD_ADDRESS, /* 6 */
+ USB_ERR_BAD_BUFSIZE, /* 7 */
+ USB_ERR_BAD_FLAG, /* 8 */
+ USB_ERR_NO_CALLBACK, /* 9 */
+ USB_ERR_IN_USE, /* 10 */
+ USB_ERR_NO_ADDR, /* 11 */
+ USB_ERR_NO_PIPE, /* 12 */
+ USB_ERR_ZERO_NFRAMES, /* 13 */
+ USB_ERR_ZERO_MAXP, /* 14 */
+ USB_ERR_SET_ADDR_FAILED, /* 15 */
+ USB_ERR_NO_POWER, /* 16 */
+ USB_ERR_TOO_DEEP, /* 17 */
+ USB_ERR_IOERROR, /* 18 */
+ USB_ERR_NOT_CONFIGURED, /* 19 */
+ USB_ERR_TIMEOUT, /* 20 */
+ USB_ERR_SHORT_XFER, /* 21 */
+ USB_ERR_STALLED, /* 22 */
+ USB_ERR_INTERRUPTED, /* 23 */
+ USB_ERR_DMA_LOAD_FAILED, /* 24 */
+ USB_ERR_BAD_CONTEXT, /* 25 */
+ USB_ERR_NO_ROOT_HUB, /* 26 */
+ USB_ERR_NO_INTR_THREAD, /* 27 */
+ USB_ERR_NOT_LOCKED, /* 28 */
+ USB_ERR_MAX
+};
+
+#endif /* _USB2_ERROR_H_ */
diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c
new file mode 100644
index 0000000..15ca909
--- /dev/null
+++ b/sys/dev/usb/usb_generic.c
@@ -0,0 +1,2195 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR ugen_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_mbuf.h>
+#include <dev/usb/usb_dev.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_generic.h>
+#include <dev/usb/usb_transfer.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_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_ioctl_t ugen_ioctl_post;
+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 *,
+ const struct usb2_config *, uint8_t);
+static int ugen_open_pipe_write(struct usb2_fifo *);
+static int ugen_open_pipe_read(struct usb2_fifo *);
+static int ugen_set_config(struct usb2_fifo *, uint8_t);
+static int ugen_set_interface(struct usb2_fifo *, uint8_t, uint8_t);
+static int ugen_get_cdesc(struct usb2_fifo *, struct usb2_gen_descriptor *);
+static int ugen_get_sdesc(struct usb2_fifo *, struct usb2_gen_descriptor *);
+static int ugen_get_iface_driver(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd);
+static int usb2_gen_fill_deviceinfo(struct usb2_fifo *,
+ struct usb2_device_info *);
+static int ugen_re_enumerate(struct usb2_fifo *);
+static int ugen_iface_ioctl(struct usb2_fifo *, u_long, void *, int);
+static uint8_t ugen_fs_get_complete(struct usb2_fifo *, uint8_t *);
+static int ugen_fs_uninit(struct usb2_fifo *f);
+
+/* structures */
+
+struct usb2_fifo_methods usb2_ugen_methods = {
+ .f_open = &ugen_open,
+ .f_close = &ugen_close,
+ .f_ioctl = &ugen_ioctl,
+ .f_ioctl_post = &ugen_ioctl_post,
+ .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);
+ switch (usb2_get_speed(f->udev)) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ f->nframes = UGEN_HW_FRAMES;
+ f->bufsize = UGEN_BULK_FS_BUFFER_SIZE;
+ break;
+ default:
+ f->nframes = UGEN_HW_FRAMES * 8;
+ f->bufsize = UGEN_BULK_HS_BUFFER_SIZE;
+ break;
+ }
+
+ 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 */
+ DPRINTFN(6, "no FIFOs\n");
+ }
+}
+
+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);
+ }
+ 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);
+ }
+ 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]);
+}
+
+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]);
+}
+
+static void
+ugen_stop_io(struct usb2_fifo *f)
+{
+ /* stop transfers */
+ usb2_transfer_stop(f->xfer[0]);
+ usb2_transfer_stop(f->xfer[1]);
+}
+
+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;
+ }
+}
+
+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;
+ }
+}
+
+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);
+ }
+}
+
+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);
+ }
+}
+
+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;
+ }
+}
+
+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;
+ }
+}
+
+static int
+ugen_set_config(struct usb2_fifo *f, uint8_t index)
+{
+ DPRINTFN(2, "index %u\n", index);
+
+ 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);
+ }
+ /* make sure all FIFO's are gone */
+ /* else there can be a deadlock */
+ if (ugen_fs_uninit(f)) {
+ /* ignore any errors */
+ DPRINTFN(6, "no FIFOs\n");
+ }
+ /* 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->udev->flags.usb2_mode != USB_MODE_HOST) {
+ /* not possible in device side mode */
+ return (ENOTTY);
+ }
+ /* make sure all FIFO's are gone */
+ /* else there can be a deadlock */
+ if (ugen_fs_uninit(f)) {
+ /* ignore any errors */
+ DPRINTFN(6, "no FIFOs\n");
+ }
+ /* 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 (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 (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);
+}
+
+/*------------------------------------------------------------------------*
+ * ugen_get_iface_driver
+ *
+ * This function generates an USB interface description for userland.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+static int
+ugen_get_iface_driver(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd)
+{
+ struct usb2_device *udev = f->udev;
+ struct usb2_interface *iface;
+ const char *ptr;
+ const char *desc;
+ unsigned int len;
+ unsigned int maxlen;
+ char buf[128];
+ int error;
+
+ DPRINTFN(6, "\n");
+
+ if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) {
+ /* userland pointer should not be zero */
+ return (EINVAL);
+ }
+
+ iface = usb2_get_iface(udev, ugd->ugd_iface_index);
+ if ((iface == NULL) || (iface->idesc == NULL)) {
+ /* invalid interface index */
+ return (EINVAL);
+ }
+
+ /* read out device nameunit string, if any */
+ if ((iface->subdev != NULL) &&
+ device_is_attached(iface->subdev) &&
+ (ptr = device_get_nameunit(iface->subdev)) &&
+ (desc = device_get_desc(iface->subdev))) {
+
+ /* print description */
+ snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc);
+
+ /* range checks */
+ maxlen = ugd->ugd_maxlen - 1;
+ len = strlen(buf);
+ if (len > maxlen)
+ len = maxlen;
+
+ /* update actual length, including terminating zero */
+ ugd->ugd_actlen = len + 1;
+
+ /* copy out interface description */
+ error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen);
+ } else {
+ /* zero length string is default */
+ error = copyout("", ugd->ugd_data, 1);
+ }
+ return (error);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ 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 (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;
+
+ /*
+ * This request can be useful for testing USB drivers:
+ */
+ error = priv_check(curthread, PRIV_DRIVER);
+ if (error) {
+ return (error);
+ }
+ /* get the device unconfigured */
+ error = ugen_set_config(f, USB_UNCONFIG_INDEX);
+ if (error) {
+ return (error);
+ }
+ /* do a bus-reset */
+ mtx_lock(f->priv_mtx);
+ error = usb2_req_re_enumerate(udev, f->priv_mtx);
+ mtx_unlock(f->priv_mtx);
+
+ if (error) {
+ return (ENXIO);
+ }
+ /* restore configuration to index 0 */
+ error = ugen_set_config(f, 0);
+ if (error) {
+ return (error);
+ }
+ return (0);
+}
+
+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;
+ 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);
+}
+
+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_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags,
+ struct thread *td)
+{
+ 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_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;
+
+ DPRINTFN(6, "cmd=0x%08lx\n", cmd);
+
+ 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_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;
+ } 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 = ENOIOCTL;
+ break;
+ }
+
+ DPRINTFN(6, "error=%d\n", error);
+
+ 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;
+ uint8_t old_mode;
+
+ if ((udev == NULL) ||
+ (udev->parent_hub == NULL)) {
+ return (EINVAL);
+ }
+ err = priv_check(curthread, PRIV_ROOT);
+ if (err)
+ return (err);
+
+ /* get old power mode */
+ old_mode = udev->power_mode;
+
+ /* if no change, then just return */
+ if (old_mode == mode)
+ return (0);
+
+ switch (mode) {
+ case USB_POWER_MODE_OFF:
+ /* get the device unconfigured */
+ err = ugen_set_config(f, USB_UNCONFIG_INDEX);
+ if (err) {
+ DPRINTFN(0, "Could not unconfigure "
+ "device (ignored)\n");
+ }
+
+ /* 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:
+ case USB_POWER_MODE_SAVE:
+ break;
+
+ case USB_POWER_MODE_RESUME:
+ err = usb2_req_clear_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ mode = USB_POWER_MODE_SAVE;
+ break;
+
+ case USB_POWER_MODE_SUSPEND:
+ err = usb2_req_set_port_feature(udev->parent_hub,
+ NULL, udev->port_no, UHF_PORT_SUSPEND);
+ mode = USB_POWER_MODE_SAVE;
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ if (err)
+ return (ENXIO); /* I/O failure */
+
+ /* if we are powered off we need to re-enumerate first */
+ if (old_mode == USB_POWER_MODE_OFF) {
+ err = ugen_re_enumerate(f);
+ if (err)
+ return (err);
+ }
+
+ /* set new power mode */
+ usb2_set_power_mode(udev, 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 = ENOIOCTL;
+ break;
+ }
+ return (error);
+}
+
+static int
+ugen_ioctl_post(struct usb2_fifo *f, u_long cmd, void *addr, int fflags,
+ struct thread *td)
+{
+ 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;
+ struct usb2_fs_init *pinit;
+ struct usb2_fs_uninit *puninit;
+ 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;
+
+ DPRINTFN(6, "cmd=0x%08lx\n", cmd);
+
+ 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_GET_IFACE_DRIVER:
+ error = ugen_get_iface_driver(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_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;
+
+ 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->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;
+
+ default:
+ mtx_lock(f->priv_mtx);
+ error = ugen_iface_ioctl(f, cmd, addr, fflags);
+ mtx_unlock(f->priv_mtx);
+ break;
+ }
+ 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;
+ }
+}
diff --git a/sys/dev/usb/usb_generic.h b/sys/dev/usb/usb_generic.h
new file mode 100644
index 0000000..3a4e7c9
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb_handle_request.c b/sys/dev/usb/usb_handle_request.c
new file mode 100644
index 0000000..05a739a
--- /dev/null
+++ b/sys/dev/usb/usb_handle_request.c
@@ -0,0 +1,756 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_dynamic.h>
+#include <dev/usb/usb_hub.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+/* enum */
+
+enum {
+ ST_DATA,
+ ST_POST_STATUS,
+};
+
+/* function prototypes */
+
+static uint8_t usb2_handle_get_stall(struct usb2_device *, uint8_t);
+static usb2_error_t usb2_handle_remote_wakeup(struct usb2_xfer *, uint8_t);
+static usb2_error_t usb2_handle_request(struct usb2_xfer *);
+static usb2_error_t usb2_handle_set_config(struct usb2_xfer *, uint8_t);
+static usb2_error_t usb2_handle_set_stall(struct usb2_xfer *, uint8_t,
+ uint8_t);
+static usb2_error_t usb2_handle_iface_request(struct usb2_xfer *, void **,
+ uint16_t *, struct usb2_device_request, uint16_t,
+ uint8_t);
+
+/*------------------------------------------------------------------------*
+ * 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->xroot->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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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)
+{
+ struct usb2_device *udev = xfer->xroot->udev;
+ usb2_error_t err = 0;
+
+ /*
+ * We need to protect against other threads doing probe and
+ * attach:
+ */
+ USB_XFER_UNLOCK(xfer);
+ mtx_lock(&Giant); /* XXX */
+ sx_xlock(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(udev, conf_no)) {
+ DPRINTF("set config %d failed\n", conf_no);
+ err = USB_ERR_STALLED;
+ goto done;
+ }
+ if (usb2_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
+ DPRINTF("probe and attach failed\n");
+ err = USB_ERR_STALLED;
+ goto done;
+ }
+done:
+ mtx_unlock(&Giant); /* XXX */
+ sx_unlock(udev->default_sx + 1);
+ USB_XFER_LOCK(xfer);
+ 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->xroot->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:
+ */
+ USB_XFER_UNLOCK(xfer);
+ 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 = USB_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 = USB_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);
+ USB_XFER_LOCK(xfer);
+ return (0);
+
+tr_stalled:
+ mtx_unlock(&Giant);
+ sx_unlock(udev->default_sx + 1);
+ USB_XFER_LOCK(xfer);
+ 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)
+{
+ struct usb2_device *udev = xfer->xroot->udev;
+ usb2_error_t err;
+
+ USB_XFER_UNLOCK(xfer);
+ err = usb2_set_endpoint_stall(udev,
+ usb2_get_pipe_by_addr(udev, ep), do_stall);
+ USB_XFER_LOCK(xfer);
+ 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);
+ }
+ USB_BUS_LOCK(udev->bus);
+ halted = pipe->is_stalled;
+ USB_BUS_UNLOCK(udev->bus);
+
+ 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->xroot->udev;
+ bus = udev->bus;
+
+ USB_BUS_LOCK(bus);
+
+ if (is_on) {
+ udev->flags.remote_wakeup = 1;
+ } else {
+ udev->flags.remote_wakeup = 0;
+ }
+
+ USB_BUS_UNLOCK(bus);
+
+ /* In case we are out of sync, update the power state. */
+
+ usb2_bus_power_update(udev->bus);
+
+ 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->xroot->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;
+
+ USB_BUS_LOCK(udev->bus);
+ if (udev->flags.remote_wakeup) {
+ wValue |= UDS_REMOTE_WAKEUP;
+ }
+ if (udev->flags.self_powered) {
+ wValue |= UDS_SELF_POWERED;
+ }
+ USB_BUS_UNLOCK(udev->bus);
+
+ 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/usb/usb_handle_request.h b/sys/dev/usb/usb_handle_request.h
new file mode 100644
index 0000000..6cc0503
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb_hid.c b/sys/dev/usb/usb_hid.c
new file mode 100644
index 0000000..2adf208
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_parse.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_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/usb/usb_hid.h b/sys/dev/usb/usb_hid.h
new file mode 100644
index 0000000..3fb7368
--- /dev/null
+++ b/sys/dev/usb/usb_hid.h
@@ -0,0 +1,95 @@
+/* $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/usb/usb_hub.c b/sys/dev/usb/usb_hub.c
new file mode 100644
index 0000000..a0b4e851
--- /dev/null
+++ b/sys/dev/usb/usb_hub.c
@@ -0,0 +1,1842 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+
+#define USB_DEBUG_VAR uhub_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_hub.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_dynamic.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.h>
+
+#define UHUB_INTR_INTERVAL 250 /* ms */
+#define UHUB_N_TRANSFER 1
+
+#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
+
+static int usb2_power_timeout = 30; /* seconds */
+
+SYSCTL_INT(_hw_usb2, OID_AUTO, power_timeout, CTLFLAG_RW,
+ &usb2_power_timeout, 0, "USB power timeout");
+
+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[UHUB_N_TRANSFER]; /* interrupt xfer */
+ uint8_t sc_flags;
+#define UHUB_FLAG_DID_EXPLORE 0x01
+ 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 device_suspend_t uhub_suspend;
+static device_resume_t uhub_resume;
+
+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 void usb2_dev_resume_peer(struct usb2_device *udev);
+static void usb2_dev_suspend_peer(struct usb2_device *udev);
+
+static const struct usb2_config uhub_config[UHUB_N_TRANSFER] = {
+
+ [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,
+ },
+};
+
+/*
+ * 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, uhub_suspend),
+ DEVMETHOD(device_resume, uhub_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_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:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /*
+ * Do a clear-stall. The "stall_pipe" flag
+ * will get cleared before next callback by
+ * the USB stack.
+ */
+ xfer->flags.stall_pipe = 1;
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ }
+ break;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+
+ if (sc->sc_st.port_status & UPS_SUSPEND) {
+ DPRINTF("Port %d was still "
+ "suspended, clearing.\n", portno);
+ err = usb2_req_clear_port_feature(sc->sc_udev,
+ &Giant, portno, UHF_PORT_SUSPEND);
+ }
+ /* USB Host Mode */
+
+ /* wait for maximum device power up time */
+
+ usb2_pause_mtx(&Giant,
+ USB_MS_TO_TICKS(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
+ */
+ switch (udev->speed) {
+ case USB_SPEED_HIGH:
+ if (sc->sc_st.port_status & UPS_HIGH_SPEED)
+ speed = USB_SPEED_HIGH;
+ else if (sc->sc_st.port_status & UPS_LOW_SPEED)
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+ break;
+ case USB_SPEED_FULL:
+ if (sc->sc_st.port_status & UPS_LOW_SPEED)
+ speed = USB_SPEED_LOW;
+ else
+ speed = USB_SPEED_FULL;
+ break;
+ case USB_SPEED_LOW:
+ speed = USB_SPEED_LOW;
+ break;
+ default:
+ /* same speed like parent */
+ speed = udev->speed;
+ break;
+ }
+ /*
+ * Figure out the device mode
+ *
+ * NOTE: This part is currently FreeBSD specific.
+ */
+ if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)
+ usb2_mode = USB_MODE_DEVICE;
+ else
+ usb2_mode = 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) {
+ DPRINTF("clearing suspend failed.\n");
+ goto done;
+ }
+ /* get fresh status */
+
+ err = uhub_read_port_status(sc, portno);
+ if (err) {
+ DPRINTF("reading port status failed.\n");
+ goto done;
+ }
+ /* get current state */
+
+ if (sc->sc_st.port_status & UPS_SUSPEND) {
+ is_suspend = 1;
+ } else {
+ is_suspend = 0;
+ }
+
+ DPRINTF("suspended=%u\n", is_suspend);
+
+ /* do the suspend or resume */
+
+ if (child) {
+ /*
+ * This code handle two cases: 1) Host Mode - we can only
+ * receive resume here 2) Device Mode - we can receive
+ * suspend and resume here
+ */
+ if (is_suspend == 0)
+ usb2_dev_resume_peer(child);
+ else if (child->flags.usb2_mode == USB_MODE_DEVICE)
+ usb2_dev_suspend_peer(child);
+ }
+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);
+ }
+ if (udev->pwr_save.suspended) {
+ /* need to wait until the child signals resume */
+ DPRINTF("Device is suspended!\n");
+ return (0);
+ }
+ 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_OVERCURRENT_INDICATOR) {
+ DPRINTF("Overcurrent on port %u.\n", portno);
+ err = usb2_req_clear_port_feature(
+ udev, &Giant, portno, UHF_C_PORT_OVER_CURRENT);
+ if (err) {
+ /* most likely the HUB is gone */
+ break;
+ }
+ }
+ if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) {
+ /*
+ * Fake a connect status change so that the
+ * status gets checked initially!
+ */
+ sc->sc_st.port_change |=
+ UPS_C_CONNECT_STATUS;
+ }
+ 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;
+ }
+
+ /* initial status checked */
+ sc->sc_flags |= UHUB_FLAG_DID_EXPLORE;
+
+ /* return success */
+ 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;
+
+ 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, UHUB_N_TRANSFER, 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_MS_TO_TICKS(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, USB_MS_TO_TICKS(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 */
+
+ USB_XFER_LOCK(sc->sc_xfer[0]);
+ usb2_transfer_start(sc->sc_xfer[0]);
+ USB_XFER_UNLOCK(sc->sc_xfer[0]);
+
+ /* Enable automatic power save on all USB HUBs */
+
+ usb2_set_power_mode(udev, USB_POWER_MODE_SAVE);
+
+ return (0);
+
+error:
+ usb2_transfer_unsetup(sc->sc_xfer, UHUB_N_TRANSFER);
+
+ 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, UHUB_N_TRANSFER);
+
+ free(hub, M_USBDEV);
+ sc->sc_udev->hub = NULL;
+ return (0);
+}
+
+static int
+uhub_suspend(device_t dev)
+{
+ DPRINTF("\n");
+ /* Sub-devices are not suspended here! */
+ return (0);
+}
+
+static int
+uhub_resume(device_t dev)
+{
+ DPRINTF("\n");
+ /* Sub-devices are not resumed here! */
+ return (0);
+}
+
+static void
+uhub_driver_added(device_t dev, driver_t *driver)
+{
+ usb2_needs_explore_all();
+}
+
+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;
+}
+
+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;
+ uint8_t speed;
+
+ USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
+
+ speed = usb2_get_speed(udev);
+
+ switch (speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ if (speed == 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;
+ break;
+ default:
+ 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;
+ break;
+ }
+ 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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++;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ USB_BUS_LOCK_ASSERT(bus, 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+ USB_BUS_LOCK(bus);
+ if (do_probe) {
+ bus->do_probe = 1;
+ }
+ if (usb2_proc_msignal(&bus->explore_proc,
+ &bus->explore_msg[0], &bus->explore_msg[1])) {
+ /* ignore */
+ }
+ USB_BUS_UNLOCK(bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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--;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_power_update
+ *
+ * This function will ensure that all USB devices on the given bus are
+ * properly suspended or resumed according to the device transfer
+ * state.
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_power_update(struct usb2_bus *bus)
+{
+ usb2_needs_explore(bus, 0 /* no probe */ );
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_transfer_power_ref
+ *
+ * This function will modify the power save reference counts and
+ * wakeup the USB device associated with the given USB transfer, if
+ * needed.
+ *------------------------------------------------------------------------*/
+void
+usb2_transfer_power_ref(struct usb2_xfer *xfer, int val)
+{
+ static const uint32_t power_mask[4] = {
+ [UE_CONTROL] = USB_HW_POWER_CONTROL,
+ [UE_BULK] = USB_HW_POWER_BULK,
+ [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT,
+ [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC,
+ };
+ struct usb2_device *udev;
+ uint8_t needs_explore;
+ uint8_t needs_hw_power;
+ uint8_t xfer_type;
+
+ udev = xfer->xroot->udev;
+
+ if (udev->device_index == USB_ROOT_HUB_ADDR) {
+ /* no power save for root HUB */
+ return;
+ }
+ USB_BUS_LOCK(udev->bus);
+
+ xfer_type = xfer->pipe->edesc->bmAttributes & UE_XFERTYPE;
+
+ udev->pwr_save.last_xfer_time = ticks;
+ udev->pwr_save.type_refs[xfer_type] += val;
+
+ if (xfer->flags_int.control_xfr) {
+ udev->pwr_save.read_refs += val;
+ if (xfer->flags_int.usb2_mode == USB_MODE_HOST) {
+ /*
+ * it is not allowed to suspend during a control
+ * transfer
+ */
+ udev->pwr_save.write_refs += val;
+ }
+ } else if (USB_GET_DATA_ISREAD(xfer)) {
+ udev->pwr_save.read_refs += val;
+ } else {
+ udev->pwr_save.write_refs += val;
+ }
+
+ if (udev->pwr_save.suspended)
+ needs_explore =
+ (udev->pwr_save.write_refs != 0) ||
+ ((udev->pwr_save.read_refs != 0) &&
+ (usb2_peer_can_wakeup(udev) == 0));
+ else
+ needs_explore = 0;
+
+ if (!(udev->bus->hw_power_state & power_mask[xfer_type])) {
+ DPRINTF("Adding type %u to power state\n", xfer_type);
+ udev->bus->hw_power_state |= power_mask[xfer_type];
+ needs_hw_power = 1;
+ } else {
+ needs_hw_power = 0;
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ if (needs_explore) {
+ DPRINTF("update\n");
+ usb2_bus_power_update(udev->bus);
+ } else if (needs_hw_power) {
+ DPRINTF("needs power\n");
+ if (udev->bus->methods->set_hw_power != NULL) {
+ (udev->bus->methods->set_hw_power) (udev->bus);
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_bus_powerd
+ *
+ * This function implements the USB power daemon and is called
+ * regularly from the USB explore thread.
+ *------------------------------------------------------------------------*/
+void
+usb2_bus_powerd(struct usb2_bus *bus)
+{
+ struct usb2_device *udev;
+ unsigned int temp;
+ unsigned int limit;
+ unsigned int mintime;
+ uint32_t type_refs[5];
+ uint8_t x;
+ uint8_t rem_wakeup;
+
+ limit = usb2_power_timeout;
+ if (limit == 0)
+ limit = hz;
+ else if (limit > 255)
+ limit = 255 * hz;
+ else
+ limit = limit * hz;
+
+ DPRINTF("bus=%p\n", bus);
+
+ USB_BUS_LOCK(bus);
+
+ /*
+ * The root HUB device is never suspended
+ * and we simply skip it.
+ */
+ for (x = USB_ROOT_HUB_ADDR + 1;
+ x != bus->devices_max; x++) {
+
+ udev = bus->devices[x];
+ if (udev == NULL)
+ continue;
+
+ rem_wakeup = usb2_peer_can_wakeup(udev);
+
+ temp = ticks - udev->pwr_save.last_xfer_time;
+
+ if ((udev->power_mode == USB_POWER_MODE_ON) ||
+ (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
+ (udev->pwr_save.write_refs != 0) ||
+ ((udev->pwr_save.read_refs != 0) &&
+ (rem_wakeup == 0))) {
+
+ /* check if we are suspended */
+ if (udev->pwr_save.suspended != 0) {
+ USB_BUS_UNLOCK(bus);
+ usb2_dev_resume_peer(udev);
+ USB_BUS_LOCK(bus);
+ }
+ } else if (temp >= limit) {
+
+ /* check if we are not suspended */
+ if (udev->pwr_save.suspended == 0) {
+ USB_BUS_UNLOCK(bus);
+ usb2_dev_suspend_peer(udev);
+ USB_BUS_LOCK(bus);
+ }
+ }
+ }
+
+ /* reset counters */
+
+ mintime = 0 - 1;
+ type_refs[0] = 0;
+ type_refs[1] = 0;
+ type_refs[2] = 0;
+ type_refs[3] = 0;
+ type_refs[4] = 0;
+
+ /* Re-loop all the devices to get the actual state */
+
+ for (x = USB_ROOT_HUB_ADDR + 1;
+ x != bus->devices_max; x++) {
+
+ udev = bus->devices[x];
+ if (udev == NULL)
+ continue;
+
+ /* we found a non-Root-Hub USB device */
+ type_refs[4] += 1;
+
+ /* "last_xfer_time" can be updated by a resume */
+ temp = ticks - udev->pwr_save.last_xfer_time;
+
+ /*
+ * Compute minimum time since last transfer for the complete
+ * bus:
+ */
+ if (temp < mintime)
+ mintime = temp;
+
+ if (udev->pwr_save.suspended == 0) {
+ type_refs[0] += udev->pwr_save.type_refs[0];
+ type_refs[1] += udev->pwr_save.type_refs[1];
+ type_refs[2] += udev->pwr_save.type_refs[2];
+ type_refs[3] += udev->pwr_save.type_refs[3];
+ }
+ }
+
+ if (mintime >= (1 * hz)) {
+ /* recompute power masks */
+ DPRINTF("Recomputing power masks\n");
+ bus->hw_power_state = 0;
+ if (type_refs[UE_CONTROL] != 0)
+ bus->hw_power_state |= USB_HW_POWER_CONTROL;
+ if (type_refs[UE_BULK] != 0)
+ bus->hw_power_state |= USB_HW_POWER_BULK;
+ if (type_refs[UE_INTERRUPT] != 0)
+ bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
+ if (type_refs[UE_ISOCHRONOUS] != 0)
+ bus->hw_power_state |= USB_HW_POWER_ISOC;
+ if (type_refs[4] != 0)
+ bus->hw_power_state |= USB_HW_POWER_NON_ROOT_HUB;
+ }
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power != NULL) {
+ /* always update hardware power! */
+ (bus->methods->set_hw_power) (bus);
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dev_resume_peer
+ *
+ * This function will resume an USB peer and do the required USB
+ * signalling to get an USB device out of the suspended state.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dev_resume_peer(struct usb2_device *udev)
+{
+ struct usb2_bus *bus;
+ int err;
+
+ /* be NULL safe */
+ if (udev == NULL)
+ return;
+
+ /* check if already resumed */
+ if (udev->pwr_save.suspended == 0)
+ return;
+
+ /* we need a parent HUB to do resume */
+ if (udev->parent_hub == NULL)
+ return;
+
+ DPRINTF("udev=%p\n", udev);
+
+ if ((udev->flags.usb2_mode == USB_MODE_DEVICE) &&
+ (udev->flags.remote_wakeup == 0)) {
+ /*
+ * If the host did not set the remote wakeup feature, we can
+ * not wake it up either!
+ */
+ DPRINTF("remote wakeup is not set!\n");
+ return;
+ }
+ /* get bus pointer */
+ bus = udev->bus;
+
+ /* resume parent hub first */
+ usb2_dev_resume_peer(udev->parent_hub);
+
+ /* resume current port (Valid in Host and Device Mode) */
+ err = usb2_req_clear_port_feature(udev->parent_hub,
+ &Giant, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Resuming port failed!\n");
+ return;
+ }
+ /* resume settle time */
+ usb2_pause_mtx(&Giant, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY));
+
+ if (bus->methods->device_resume != NULL) {
+ /* resume USB device on the USB controller */
+ (bus->methods->device_resume) (udev);
+ }
+ USB_BUS_LOCK(bus);
+ /* set that this device is now resumed */
+ udev->pwr_save.suspended = 0;
+ /* make sure that we don't go into suspend right away */
+ udev->pwr_save.last_xfer_time = ticks;
+
+ /* make sure the needed power masks are on */
+ if (udev->pwr_save.type_refs[UE_CONTROL] != 0)
+ bus->hw_power_state |= USB_HW_POWER_CONTROL;
+ if (udev->pwr_save.type_refs[UE_BULK] != 0)
+ bus->hw_power_state |= USB_HW_POWER_BULK;
+ if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0)
+ bus->hw_power_state |= USB_HW_POWER_INTERRUPT;
+ if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0)
+ bus->hw_power_state |= USB_HW_POWER_ISOC;
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power != NULL) {
+ /* always update hardware power! */
+ (bus->methods->set_hw_power) (bus);
+ }
+ sx_xlock(udev->default_sx + 1);
+ /* notify all sub-devices about resume */
+ err = usb2_suspend_resume(udev, 0);
+ sx_unlock(udev->default_sx + 1);
+
+ /* check if peer has wakeup capability */
+ if (usb2_peer_can_wakeup(udev)) {
+ /* clear remote wakeup */
+ err = usb2_req_clear_device_feature(udev,
+ &Giant, UF_DEVICE_REMOTE_WAKEUP);
+ if (err) {
+ DPRINTFN(0, "Clearing device "
+ "remote wakeup failed: %s!\n",
+ usb2_errstr(err));
+ }
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_dev_suspend_peer
+ *
+ * This function will suspend an USB peer and do the required USB
+ * signalling to get an USB device into the suspended state.
+ *------------------------------------------------------------------------*/
+static void
+usb2_dev_suspend_peer(struct usb2_device *udev)
+{
+ struct usb2_device *hub;
+ struct usb2_device *child;
+ uint32_t temp;
+ int err;
+ uint8_t x;
+ uint8_t nports;
+ uint8_t suspend_parent;
+
+repeat:
+ /* be NULL safe */
+ if (udev == NULL)
+ return;
+
+ /* check if already suspended */
+ if (udev->pwr_save.suspended)
+ return;
+
+ /* we need a parent HUB to do suspend */
+ if (udev->parent_hub == NULL)
+ return;
+
+ DPRINTF("udev=%p\n", udev);
+
+ /* check if all devices on the parent hub are suspended */
+ hub = udev->parent_hub;
+ if (hub != NULL) {
+ nports = hub->hub->nports;
+ suspend_parent = 1;
+
+ for (x = 0; x != nports; x++) {
+
+ child = usb2_bus_port_get_device(hub->bus,
+ hub->hub->ports + x);
+
+ if (child == NULL)
+ continue;
+
+ if (child->pwr_save.suspended)
+ continue;
+
+ if (child == udev)
+ continue;
+
+ /* another device on the HUB is not suspended */
+ suspend_parent = 0;
+
+ break;
+ }
+ } else {
+ suspend_parent = 0;
+ }
+
+ sx_xlock(udev->default_sx + 1);
+ /* notify all sub-devices about suspend */
+ err = usb2_suspend_resume(udev, 1);
+ sx_unlock(udev->default_sx + 1);
+
+ if (usb2_peer_can_wakeup(udev)) {
+ /* allow device to do remote wakeup */
+ err = usb2_req_set_device_feature(udev,
+ &Giant, UF_DEVICE_REMOTE_WAKEUP);
+ if (err) {
+ DPRINTFN(0, "Setting device "
+ "remote wakeup failed!\n");
+ }
+ }
+ USB_BUS_LOCK(udev->bus);
+ /*
+ * Set that this device is suspended. This variable must be set
+ * before calling USB controller suspend callbacks.
+ */
+ udev->pwr_save.suspended = 1;
+ USB_BUS_UNLOCK(udev->bus);
+
+ if (udev->bus->methods->device_suspend != NULL) {
+
+ /* suspend device on the USB controller */
+ (udev->bus->methods->device_suspend) (udev);
+
+ /* do DMA delay */
+ temp = usb2_get_dma_delay(udev->bus);
+ usb2_pause_mtx(&Giant, USB_MS_TO_TICKS(temp));
+
+ }
+ /* suspend current port */
+ err = usb2_req_set_port_feature(udev->parent_hub,
+ &Giant, udev->port_no, UHF_PORT_SUSPEND);
+ if (err) {
+ DPRINTFN(0, "Suspending port failed\n");
+ return;
+ }
+ if (suspend_parent) {
+ udev = udev->parent_hub;
+ goto repeat;
+ }
+ return;
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_set_power_mode
+ *
+ * This function will set the power mode, see USB_POWER_MODE_XXX for a
+ * USB device.
+ *------------------------------------------------------------------------*/
+void
+usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode)
+{
+ /* filter input argument */
+ if ((power_mode != USB_POWER_MODE_ON) &&
+ (power_mode != USB_POWER_MODE_OFF)) {
+ power_mode = USB_POWER_MODE_SAVE;
+ }
+ udev->power_mode = power_mode; /* update copy of power mode */
+
+ usb2_bus_power_update(udev->bus);
+
+ return;
+}
diff --git a/sys/dev/usb/usb_hub.h b/sys/dev/usb/usb_hub.h
new file mode 100644
index 0000000..87d85b3
--- /dev/null
+++ b/sys/dev/usb/usb_hub.h
@@ -0,0 +1,80 @@
+/* $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);
+void usb2_bus_power_update(struct usb2_bus *bus);
+void usb2_bus_powerd(struct usb2_bus *bus);
+
+#endif /* _USB2_HUB_H_ */
diff --git a/sys/dev/usb/usb_if.m b/sys/dev/usb/usb_if.m
new file mode 100644
index 0000000..f6d67d4
--- /dev/null
+++ b/sys/dev/usb/usb_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 usb;
+
+# 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/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h
new file mode 100644
index 0000000..4b91643
--- /dev/null
+++ b/sys/dev/usb/usb_ioctl.h
@@ -0,0 +1,292 @@
+/* $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_IOCTL_H_
+#define _USB2_IOCTL_H_
+
+#include <sys/ioccom.h>
+
+/* Building "kdump" depends on these includes */
+
+#include <dev/usb/usb_endian.h>
+#include <dev/usb/usb.h>
+
+#define USB_DEVICE_NAME "usb"
+#define USB_GENERIC_NAME "ugen"
+
+struct usb2_read_dir {
+ void *urd_data;
+ uint32_t urd_startentry;
+ uint32_t urd_maxlen;
+};
+
+struct usb2_ctl_request {
+ void *ucr_data;
+ uint16_t ucr_flags;
+#define USB_USE_POLLING 0x0001 /* internal flag */
+#define USB_SHORT_XFER_OK 0x0004 /* allow short reads */
+#define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */
+#define USB_USER_DATA_PTR 0x0020 /* internal flag */
+ uint16_t ucr_actlen; /* actual length transferred */
+ uint8_t ucr_addr; /* zero - currently not used */
+ struct usb2_device_request ucr_request;
+};
+
+struct usb2_alt_interface {
+ uint8_t uai_interface_index;
+ uint8_t uai_alt_index;
+};
+
+struct usb2_gen_descriptor {
+ void *ugd_data;
+ uint16_t ugd_lang_id;
+ uint16_t ugd_maxlen;
+ uint16_t ugd_actlen;
+ uint16_t ugd_offset;
+ uint8_t ugd_config_index;
+ uint8_t ugd_string_index;
+ uint8_t ugd_iface_index;
+ uint8_t ugd_altif_index;
+ uint8_t ugd_endpt_index;
+ uint8_t ugd_report_type;
+ uint8_t reserved[8];
+};
+
+struct usb2_device_info {
+ uint16_t udi_productNo;
+ uint16_t udi_vendorNo;
+ uint16_t udi_releaseNo;
+ uint16_t udi_power; /* power consumption in mA, 0 if
+ * selfpowered */
+ uint8_t udi_bus;
+ uint8_t udi_addr; /* device address */
+ uint8_t udi_index; /* device index */
+ uint8_t udi_class;
+ uint8_t udi_subclass;
+ uint8_t udi_protocol;
+ uint8_t udi_config_no; /* current config number */
+ uint8_t udi_config_index; /* current config index */
+ uint8_t udi_speed; /* see "USB_SPEED_XXX" */
+ uint8_t udi_mode; /* see "USB_MODE_XXX" */
+ uint8_t udi_nports;
+ uint8_t udi_hubaddr; /* parent HUB address */
+ uint8_t udi_hubindex; /* parent HUB device index */
+ uint8_t udi_hubport; /* parent HUB port */
+ uint8_t udi_power_mode; /* see "USB_POWER_MODE_XXX" */
+ uint8_t udi_suspended; /* set if device is suspended */
+ uint8_t udi_reserved[16]; /* leave space for the future */
+ char udi_product[128];
+ char udi_vendor[128];
+ char udi_serial[64];
+ char udi_release[8];
+};
+
+struct usb2_device_stats {
+ uint32_t uds_requests_ok[4]; /* Indexed by transfer type UE_XXX */
+ uint32_t uds_requests_fail[4]; /* Indexed by transfer type UE_XXX */
+};
+
+struct usb2_fs_start {
+ uint8_t ep_index;
+};
+
+struct usb2_fs_stop {
+ uint8_t ep_index;
+};
+
+struct usb2_fs_complete {
+ uint8_t ep_index;
+};
+
+/* This structure is used for all endpoint types */
+struct usb2_fs_endpoint {
+ /*
+ * NOTE: isochronous USB transfer only use one buffer, but can have
+ * multiple frame lengths !
+ */
+ void **ppBuffer; /* pointer to userland buffers */
+ uint32_t *pLength; /* pointer to frame lengths, updated
+ * to actual length */
+ uint32_t nFrames; /* number of frames */
+ uint32_t aFrames; /* actual number of frames */
+ uint16_t flags;
+ /* a single short frame will terminate */
+#define USB_FS_FLAG_SINGLE_SHORT_OK 0x0001
+ /* multiple short frames are allowed */
+#define USB_FS_FLAG_MULTI_SHORT_OK 0x0002
+ /* all frame(s) transmitted are short terminated */
+#define USB_FS_FLAG_FORCE_SHORT 0x0004
+ /* will do a clear-stall before xfer */
+#define USB_FS_FLAG_CLEAR_STALL 0x0008
+ uint16_t timeout; /* in milliseconds */
+ /* isocronous completion time in milliseconds - used for echo cancel */
+ uint16_t isoc_time_complete;
+ /* timeout value for no timeout */
+#define USB_FS_TIMEOUT_NONE 0
+ uint8_t status; /* see USB_ERR_XXX */
+};
+
+struct usb2_fs_init {
+ /* userland pointer to endpoints structure */
+ struct usb2_fs_endpoint *pEndpoints;
+ /* maximum number of endpoints */
+ uint8_t ep_index_max;
+};
+
+struct usb2_fs_uninit {
+ uint8_t dummy; /* zero */
+};
+
+struct usb2_fs_open {
+#define USB_FS_MAX_BUFSIZE (1 << 18)
+ uint32_t max_bufsize;
+#define USB_FS_MAX_FRAMES (1 << 12)
+ uint32_t max_frames;
+ uint16_t max_packet_length; /* read only */
+ uint8_t dev_index; /* currently unused */
+ uint8_t ep_index;
+ uint8_t ep_no; /* bEndpointNumber */
+};
+
+struct usb2_fs_close {
+ uint8_t ep_index;
+};
+
+struct usb2_fs_clear_stall_sync {
+ uint8_t ep_index;
+};
+
+struct usb2_dev_perm {
+ /* Access information */
+ uint32_t user_id;
+ uint32_t group_id;
+ uint16_t mode;
+
+ /* Device location */
+ uint16_t bus_index;
+ uint16_t dev_index;
+ uint16_t iface_index;
+};
+
+struct usb2_gen_quirk {
+ uint16_t index; /* Quirk Index */
+ uint16_t vid; /* Vendor ID */
+ uint16_t pid; /* Product ID */
+ uint16_t bcdDeviceLow; /* Low Device Revision */
+ uint16_t bcdDeviceHigh; /* High Device Revision */
+ uint16_t reserved[2];
+ /*
+ * String version of quirk including terminating zero. See UQ_XXX in
+ * "usb_quirk.h".
+ */
+ char quirkname[64 - 14];
+};
+
+/* USB controller */
+#define USB_REQUEST _IOWR('U', 1, struct usb2_ctl_request)
+#define USB_SETDEBUG _IOW ('U', 2, int)
+#define USB_DISCOVER _IO ('U', 3)
+#define USB_DEVICEINFO _IOWR('U', 4, struct usb2_device_info)
+#define USB_DEVICESTATS _IOR ('U', 5, struct usb2_device_stats)
+#define USB_DEVICEENUMERATE _IOW ('U', 6, int)
+
+/* Generic HID device */
+#define USB_GET_REPORT_DESC _IOWR('U', 21, struct usb2_gen_descriptor)
+#define USB_SET_IMMED _IOW ('U', 22, int)
+#define USB_GET_REPORT _IOWR('U', 23, struct usb2_gen_descriptor)
+#define USB_SET_REPORT _IOW ('U', 24, struct usb2_gen_descriptor)
+#define USB_GET_REPORT_ID _IOR ('U', 25, int)
+
+/* Generic USB device */
+#define USB_GET_CONFIG _IOR ('U', 100, int)
+#define USB_SET_CONFIG _IOW ('U', 101, int)
+#define USB_GET_ALTINTERFACE _IOWR('U', 102, struct usb2_alt_interface)
+#define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb2_alt_interface)
+#define USB_GET_DEVICE_DESC _IOR ('U', 105, struct usb2_device_descriptor)
+#define USB_GET_CONFIG_DESC _IOR ('U', 106, struct usb2_config_descriptor)
+#define USB_GET_RX_INTERFACE_DESC _IOR ('U', 107, struct usb2_interface_descriptor)
+#define USB_GET_RX_ENDPOINT_DESC _IOR ('U', 108, struct usb2_endpoint_descriptor)
+#define USB_GET_FULL_DESC _IOWR('U', 109, struct usb2_gen_descriptor)
+#define USB_GET_STRING_DESC _IOWR('U', 110, struct usb2_gen_descriptor)
+#define USB_DO_REQUEST _IOWR('U', 111, struct usb2_ctl_request)
+#define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb2_device_info)
+#define USB_SET_RX_SHORT_XFER _IOW ('U', 113, int)
+#define USB_SET_RX_TIMEOUT _IOW ('U', 114, int)
+#define USB_GET_RX_FRAME_SIZE _IOR ('U', 115, int)
+#define USB_GET_RX_BUFFER_SIZE _IOR ('U', 117, int)
+#define USB_SET_RX_BUFFER_SIZE _IOW ('U', 118, int)
+#define USB_SET_RX_STALL_FLAG _IOW ('U', 119, int)
+#define USB_SET_TX_STALL_FLAG _IOW ('U', 120, int)
+#define USB_GET_IFACE_DRIVER _IOWR('U', 121, struct usb2_gen_descriptor)
+#define USB_CLAIM_INTERFACE _IOW ('U', 122, int)
+#define USB_RELEASE_INTERFACE _IOW ('U', 123, int)
+#define USB_IFACE_DRIVER_ACTIVE _IOW ('U', 124, int)
+#define USB_IFACE_DRIVER_DETACH _IOW ('U', 125, int)
+#define USB_GET_PLUGTIME _IOR ('U', 126, uint32_t)
+#define USB_READ_DIR _IOW ('U', 127, struct usb2_read_dir)
+#define USB_SET_ROOT_PERM _IOW ('U', 128, struct usb2_dev_perm)
+#define USB_SET_BUS_PERM _IOW ('U', 129, struct usb2_dev_perm)
+#define USB_SET_DEVICE_PERM _IOW ('U', 130, struct usb2_dev_perm)
+#define USB_SET_IFACE_PERM _IOW ('U', 131, struct usb2_dev_perm)
+#define USB_GET_ROOT_PERM _IOWR('U', 132, struct usb2_dev_perm)
+#define USB_GET_BUS_PERM _IOWR('U', 133, struct usb2_dev_perm)
+#define USB_GET_DEVICE_PERM _IOWR('U', 134, struct usb2_dev_perm)
+#define USB_GET_IFACE_PERM _IOWR('U', 135, struct usb2_dev_perm)
+#define USB_SET_TX_FORCE_SHORT _IOW ('U', 136, int)
+#define USB_SET_TX_TIMEOUT _IOW ('U', 137, int)
+#define USB_GET_TX_FRAME_SIZE _IOR ('U', 138, int)
+#define USB_GET_TX_BUFFER_SIZE _IOR ('U', 139, int)
+#define USB_SET_TX_BUFFER_SIZE _IOW ('U', 140, int)
+#define USB_GET_TX_INTERFACE_DESC _IOR ('U', 141, struct usb2_interface_descriptor)
+#define USB_GET_TX_ENDPOINT_DESC _IOR ('U', 142, struct usb2_endpoint_descriptor)
+#define USB_SET_PORT_ENABLE _IOW ('U', 143, int)
+#define USB_SET_PORT_DISABLE _IOW ('U', 144, int)
+#define USB_SET_POWER_MODE _IOW ('U', 145, int)
+#define USB_GET_POWER_MODE _IOR ('U', 146, int)
+
+/* Modem device */
+#define USB_GET_CM_OVER_DATA _IOR ('U', 180, int)
+#define USB_SET_CM_OVER_DATA _IOW ('U', 181, int)
+
+/* USB file system interface */
+#define USB_FS_START _IOW ('U', 192, struct usb2_fs_start)
+#define USB_FS_STOP _IOW ('U', 193, struct usb2_fs_stop)
+#define USB_FS_COMPLETE _IOR ('U', 194, struct usb2_fs_complete)
+#define USB_FS_INIT _IOW ('U', 195, struct usb2_fs_init)
+#define USB_FS_UNINIT _IOW ('U', 196, struct usb2_fs_uninit)
+#define USB_FS_OPEN _IOWR('U', 197, struct usb2_fs_open)
+#define USB_FS_CLOSE _IOW ('U', 198, struct usb2_fs_close)
+#define USB_FS_CLEAR_STALL_SYNC _IOW ('U', 199, struct usb2_fs_clear_stall_sync)
+
+/* USB quirk system interface */
+#define USB_DEV_QUIRK_GET _IOWR('Q', 0, struct usb2_gen_quirk)
+#define USB_QUIRK_NAME_GET _IOWR('Q', 1, struct usb2_gen_quirk)
+#define USB_DEV_QUIRK_ADD _IOW ('Q', 2, struct usb2_gen_quirk)
+#define USB_DEV_QUIRK_REMOVE _IOW ('Q', 3, struct usb2_gen_quirk)
+
+#endif /* _USB2_IOCTL_H_ */
diff --git a/sys/dev/usb/usb_lookup.c b/sys/dev/usb/usb_lookup.c
new file mode 100644
index 0000000..a8fa271
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb_core.h>
+#include <dev/usb/usb_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/usb/usb_lookup.h b/sys/dev/usb/usb_lookup.h
new file mode 100644
index 0000000..e447292
--- /dev/null
+++ b/sys/dev/usb/usb_lookup.h
@@ -0,0 +1,122 @@
+/* $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/usb/usb_mbuf.c b/sys/dev/usb/usb_mbuf.c
new file mode 100644
index 0000000..3ae6ee6
--- /dev/null
+++ b/sys/dev/usb/usb_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/usb/usb_core.h>
+#include <dev/usb/usb_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/usb/usb_mbuf.h b/sys/dev/usb/usb_mbuf.h
new file mode 100644
index 0000000..109340c
--- /dev/null
+++ b/sys/dev/usb/usb_mbuf.h
@@ -0,0 +1,102 @@
+/* $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;
+ uint8_t last_packet:1;
+ uint8_t unused:7;
+};
+
+/*
+ * 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/usb/usb_mfunc.h b/sys/dev/usb/usb_mfunc.h
new file mode 100644
index 0000000..0fad203
--- /dev/null
+++ b/sys/dev/usb/usb_mfunc.h
@@ -0,0 +1,78 @@
+/* $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 macro functions. */
+
+#ifndef _USB2_MFUNC_H_
+#define _USB2_MFUNC_H_
+
+#define USB_LOG2(n) ( \
+((x) <= (1<<0x00)) ? 0x00 : \
+((x) <= (1<<0x01)) ? 0x01 : \
+((x) <= (1<<0x02)) ? 0x02 : \
+((x) <= (1<<0x03)) ? 0x03 : \
+((x) <= (1<<0x04)) ? 0x04 : \
+((x) <= (1<<0x05)) ? 0x05 : \
+((x) <= (1<<0x06)) ? 0x06 : \
+((x) <= (1<<0x07)) ? 0x07 : \
+((x) <= (1<<0x08)) ? 0x08 : \
+((x) <= (1<<0x09)) ? 0x09 : \
+((x) <= (1<<0x0A)) ? 0x0A : \
+((x) <= (1<<0x0B)) ? 0x0B : \
+((x) <= (1<<0x0C)) ? 0x0C : \
+((x) <= (1<<0x0D)) ? 0x0D : \
+((x) <= (1<<0x0E)) ? 0x0E : \
+((x) <= (1<<0x0F)) ? 0x0F : \
+((x) <= (1<<0x10)) ? 0x10 : \
+((x) <= (1<<0x11)) ? 0x11 : \
+((x) <= (1<<0x12)) ? 0x12 : \
+((x) <= (1<<0x13)) ? 0x13 : \
+((x) <= (1<<0x14)) ? 0x14 : \
+((x) <= (1<<0x15)) ? 0x15 : \
+((x) <= (1<<0x16)) ? 0x16 : \
+((x) <= (1<<0x17)) ? 0x17 : \
+((x) <= (1<<0x18)) ? 0x18 : \
+((x) <= (1<<0x19)) ? 0x19 : \
+((x) <= (1<<0x1A)) ? 0x1A : \
+((x) <= (1<<0x1B)) ? 0x1B : \
+((x) <= (1<<0x1C)) ? 0x1C : \
+((x) <= (1<<0x1D)) ? 0x1D : \
+((x) <= (1<<0x1E)) ? 0x1E : \
+0x1F)
+
+
+/* helper for converting pointers to integers */
+#define USB_P2U(ptr) \
+ (((const uint8_t *)(ptr)) - ((const uint8_t *)0))
+
+/* helper for computing offsets */
+#define USB_ADD_BYTES(ptr,size) \
+ ((void *)(USB_P2U(ptr) + (size)))
+
+/* debug macro */
+#define USB_ASSERT KASSERT
+
+#endif /* _USB2_MFUNC_H_ */
diff --git a/sys/dev/usb/usb_msctest.c b/sys/dev/usb/usb_msctest.c
new file mode 100644
index 0000000..35b71ec
--- /dev/null
+++ b/sys/dev/usb/usb_msctest.c
@@ -0,0 +1,578 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_msctest.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_lookup.h>
+
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.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,},
+ .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);
+}
+
+static void
+bbb_transfer_start(struct bbb_transfer *sc, uint8_t xfer_index)
+{
+ sc->state = xfer_index;
+ usb2_transfer_start(sc->xfer[xfer_index]);
+}
+
+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;
+ }
+ }
+}
+
+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;
+ sc->cbw.bCDBLength = sc->cmd_len;
+ 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;
+ }
+}
+
+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;
+ }
+}
+
+static void
+bbb_data_rd_cs_callback(struct usb2_xfer *xfer)
+{
+ bbb_data_clear_stall_callback(xfer, ST_STATUS,
+ ST_DATA_RD);
+}
+
+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);
+}
+
+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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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,
+ uint8_t do_eject)
+{
+ 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 */
+ sc->cbw.CBWCDB[1] = 0;
+ sc->cbw.CBWCDB[2] = 0;
+ sc->cbw.CBWCDB[3] = 0;
+ sc->cbw.CBWCDB[4] = 0x24; /* length */
+ sc->cbw.CBWCDB[5] = 0;
+ err = bbb_command_start(sc, DIR_IN, 0,
+ sc->buffer, 0x24, 6, USB_MS_HZ);
+
+ if ((sc->actlen != 0) && (err == 0)) {
+ sid_type = sc->buffer[0] & 0x1F;
+ if (sid_type == 0x05) {
+ /* CD-ROM */
+ if (do_eject) {
+ /* 0: opcode: SCSI START/STOP */
+ sc->cbw.CBWCDB[0] = 0x1b;
+ /* 1: byte2: Not immediate */
+ sc->cbw.CBWCDB[1] = 0x00;
+ /* 2..3: reserved */
+ sc->cbw.CBWCDB[2] = 0x00;
+ sc->cbw.CBWCDB[3] = 0x00;
+ /* 4: Load/Eject command */
+ sc->cbw.CBWCDB[4] = 0x02;
+ /* 5: control */
+ sc->cbw.CBWCDB[5] = 0x00;
+ err = bbb_command_start(sc, DIR_OUT, 0,
+ NULL, 0, 6, USB_MS_HZ);
+
+ DPRINTFN(0, "Eject CD command "
+ "status: %s\n", usb2_errstr(err));
+ }
+ err = 0;
+ goto done;
+ }
+ } else if ((err != 2) && --timeout) {
+ usb2_pause_mtx(&sc->mtx, 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);
+}
diff --git a/sys/dev/usb/usb_msctest.h b/sys/dev/usb/usb_msctest.h
new file mode 100644
index 0000000..5bf64d0
--- /dev/null
+++ b/sys/dev/usb/usb_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, uint8_t do_eject);
+
+#endif /* _USB2_MSCTEST_H_ */
diff --git a/sys/dev/usb/usb_parse.c b/sys/dev/usb/usb_parse.c
new file mode 100644
index 0000000..381f546
--- /dev/null
+++ b/sys/dev/usb/usb_parse.c
@@ -0,0 +1,225 @@
+/* $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/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_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)
+{
+ uint8_t *desc_next;
+ uint8_t *start;
+ uint8_t *end;
+ uint8_t *desc;
+
+ /* be NULL safe */
+ if (cd == NULL)
+ return (NULL);
+
+ /* We assume that the "wTotalLength" has been checked. */
+ start = (uint8_t *)cd;
+ end = start + UGETW(cd->wTotalLength);
+ desc = (uint8_t *)_desc;
+
+ /* Get start of next USB descriptor. */
+ if (desc == NULL)
+ desc = start;
+ else
+ desc = desc + desc[0];
+
+ /* Check that the next USB descriptor is within the range. */
+ if ((desc < start) || (desc >= end))
+ return (NULL); /* out of range, or EOD */
+
+ /* Check that the second next USB descriptor is within range. */
+ desc_next = desc + desc[0];
+ if ((desc_next < start) || (desc_next > end))
+ return (NULL); /* out of range */
+
+ /* Check minimum descriptor length. */
+ if (desc[0] < 3)
+ return (NULL); /* too short descriptor */
+
+ /* Return start of next descriptor. */
+ return ((struct usb2_descriptor *)desc);
+}
+
+/*------------------------------------------------------------------------*
+ * 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/usb/usb_parse.h b/sys/dev/usb/usb_parse.h
new file mode 100644
index 0000000..a9e6509
--- /dev/null
+++ b/sys/dev/usb/usb_parse.h
@@ -0,0 +1,41 @@
+/* $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/usb/usb_pci.h b/sys/dev/usb/usb_pci.h
new file mode 100644
index 0000000..9297c29
--- /dev/null
+++ b/sys/dev/usb/usb_pci.h
@@ -0,0 +1,39 @@
+/* $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_PCI_H_
+#define _USB2_PCI_H_
+
+/*
+ * We don't want the following files included everywhere, that's why
+ * they are in a separate file.
+ */
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <sys/rman.h>
+
+#endif /* _USB2_PCI_H_ */
diff --git a/sys/dev/usb/usb_process.c b/sys/dev/usb/usb_process.c
new file mode 100644
index 0000000..0354284
--- /dev/null
+++ b/sys/dev/usb/usb_process.c
@@ -0,0 +1,426 @@
+/* $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/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_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);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_create
+ *
+ * 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
+ *------------------------------------------------------------------------*/
+int
+usb2_proc_create(struct usb2_process *up, struct mtx *p_mtx,
+ const char *pmesg, 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, pmesg)) {
+ DPRINTFN(0, "Unable to create USB process.");
+ up->up_ptr = NULL;
+ goto error;
+ }
+ return (0);
+
+error:
+ usb2_proc_free(up);
+ return (ENOMEM);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_proc_free
+ *
+ * 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_free(struct usb2_process *up)
+{
+ /* check if not initialised */
+ if (up->up_mtx == NULL)
+ 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ /* check if gone, return dummy value */
+ if (up->up_gone)
+ return (_pm0);
+
+ 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)
+{
+ if (up->up_gone)
+ return (1);
+
+ mtx_assert(up->up_mtx, MA_OWNED);
+ return (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;
+
+ /* check if gone */
+ if (up->up_gone)
+ return;
+
+ 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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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)
+{
+ /* check if not initialised */
+ if (up->up_mtx == NULL)
+ return;
+ /* handle special case with Giant */
+ 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);
+}
diff --git a/sys/dev/usb/usb_process.h b/sys/dev/usb/usb_process.h
new file mode 100644
index 0000000..756b929
--- /dev/null
+++ b/sys/dev/usb/usb_process.h
@@ -0,0 +1,88 @@
+/* $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_is_gone(struct usb2_process *up);
+int usb2_proc_create(struct usb2_process *up, struct mtx *p_mtx,
+ const char *pmesg, uint8_t prio);
+void usb2_proc_drain(struct usb2_process *up);
+void usb2_proc_mwait(struct usb2_process *up, void *pm0, void *pm1);
+void usb2_proc_free(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/usb/usb_request.c b/sys/dev/usb/usb_request.c
new file mode 100644
index 0000000..3d19442
--- /dev/null
+++ b/sys/dev/usb/usb_request.c
@@ -0,0 +1,1486 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include <dev/usb/usbhid.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_dynamic.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.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->xroot->udev->default_cv);
+ break;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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_device *udev;
+ struct usb2_pipe *pipe;
+ struct usb2_pipe *pipe_end;
+ struct usb2_pipe *pipe_first;
+ uint8_t to = USB_EP_MAX;
+
+ udev = xfer->xroot->udev;
+
+ USB_BUS_LOCK(udev->bus);
+
+ /* round robin pipe clear stall */
+
+ pipe = udev->pipe_curr;
+ pipe_end = udev->pipes + USB_EP_MAX;
+ pipe_first = 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;
+ USB_BUS_UNLOCK(udev->bus);
+
+ usb2_start_hardware(xfer);
+
+ USB_BUS_LOCK(udev->bus);
+ break;
+ }
+ pipe++;
+ if (--to)
+ goto tr_setup;
+ break;
+
+ default:
+ if (xfer->error == USB_ERR_CANCELLED) {
+ break;
+ }
+ goto tr_setup;
+ }
+
+ /* store current pipe */
+ udev->pipe_curr = pipe;
+ USB_BUS_UNLOCK(udev->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+ USB_XFER_LOCK(xfer);
+
+ 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) {
+ USB_XFER_UNLOCK(xfer);
+ err = usb2_copy_in_user(xfer->frbuffers + 1,
+ 0, data, temp);
+ USB_XFER_LOCK(xfer);
+ 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->xroot->xfer_mtx,
+ USB_MS_TO_TICKS(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(udev->default_cv,
+ xfer->xroot->xfer_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) {
+ USB_XFER_UNLOCK(xfer);
+ err = usb2_copy_out_user(xfer->frbuffers + 1,
+ 0, data, temp);
+ USB_XFER_LOCK(xfer);
+ 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);
+ }
+ USB_XFER_UNLOCK(xfer);
+
+done:
+ sx_xunlock(udev->default_sx);
+
+ if (mtx) {
+ mtx_lock(mtx);
+ }
+ return ((usb2_error_t)err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_do_request_proc - factored out code
+ *
+ * This function is factored out code. It does basically the same like
+ * usb2_do_request_flags, except it will check the status of the
+ * passed process argument before doing the USB request. If the
+ * process is draining the USB_ERR_IOERROR code will be returned. It
+ * is assumed that the mutex associated with the process is locked
+ * when calling this function.
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_do_request_proc(struct usb2_device *udev, struct usb2_process *pproc,
+ struct usb2_device_request *req, void *data, uint32_t flags,
+ uint16_t *actlen, uint32_t timeout)
+{
+ usb2_error_t err;
+ uint16_t len;
+
+ /* get request data length */
+ len = UGETW(req->wLength);
+
+ /* check if the device is being detached */
+ if (usb2_proc_is_gone(pproc)) {
+ err = USB_ERR_IOERROR;
+ goto done;
+ }
+
+ /* forward the USB request */
+ err = usb2_do_request_flags(udev, pproc->up_mtx,
+ req, data, flags, actlen, timeout);
+
+done:
+ /* on failure we zero the data */
+ /* on short packet we zero the unused data */
+ if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) {
+ if (err)
+ memset(data, 0, len);
+ else if (actlen && *actlen != len)
+ memset(((uint8_t *)data) + *actlen, 0, len - *actlen);
+ }
+ return (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, USB_MS_TO_TICKS(pr_poll_delay));
+ n += pr_poll_delay;
+#else
+ /* wait for the device to recover from reset */
+ usb2_pause_mtx(mtx, USB_MS_TO_TICKS(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, USB_MS_TO_TICKS(pr_recovery_delay));
+#else
+ /* wait for the device to recover from reset */
+ usb2_pause_mtx(mtx, USB_MS_TO_TICKS(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_flags(udev, mtx, &req,
+ desc, 0, NULL, 1000);
+
+ if (err) {
+ if (!retries) {
+ goto done;
+ }
+ retries--;
+
+ usb2_pause_mtx(mtx, hz / 5);
+
+ 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);
+ }
+ if (string_index == 0) {
+ /* this is the language table */
+ buf[0] = 0;
+ return (USB_ERR_INVAL);
+ }
+ if (udev->flags.no_strings) {
+ buf[0] = 0;
+ return (USB_ERR_STALLED);
+ }
+ err = usb2_req_get_string_desc
+ (udev, mtx, buf, len, udev->langid, string_index);
+ if (err) {
+ buf[0] = 0;
+ return (err);
+ }
+ temp = (uint8_t *)buf;
+
+ if (temp[0] < 2) {
+ /* string length is too short */
+ buf[0] = 0;
+ 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 {
+ /* silently skip bad character */
+ continue;
+ }
+
+ /*
+ * Filter by default - we don't allow greater and less than
+ * signs because they might confuse the dmesg printouts!
+ */
+ if ((*s == '<') || (*s == '>') || (!isprint(*s))) {
+ /* silently skip bad character */
+ continue;
+ }
+ s++;
+ }
+ *s = 0; /* zero terminate resulting string */
+ 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
+ *
+ * NOTE: After this function returns the hardware is in the
+ * unconfigured state! The application is responsible for setting a
+ * new configuration.
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx)
+{
+ struct usb2_device *parent_hub;
+ usb2_error_t err;
+ uint8_t old_addr;
+ uint8_t do_retry = 1;
+
+ if (udev->flags.usb2_mode != USB_MODE_HOST) {
+ return (USB_ERR_INVAL);
+ }
+ old_addr = udev->address;
+ parent_hub = udev->parent_hub;
+ if (parent_hub == NULL) {
+ return (USB_ERR_INVAL);
+ }
+retry:
+ 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;
+
+ /* reset "bMaxPacketSize" */
+ udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
+
+ /*
+ * 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! (ignored)\n",
+ old_addr);
+ }
+ /* restore device address */
+ udev->address = old_addr;
+
+ /* allow device time to set new address */
+ usb2_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
+
+ /* get the device descriptor */
+ err = usb2_req_get_desc(udev, mtx, &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;
+ }
+ /* get the full device descriptor */
+ err = usb2_req_get_device_desc(udev, mtx, &udev->ddesc);
+ if (err) {
+ DPRINTFN(0, "addr=%d, getting device "
+ "descriptor failed!\n", old_addr);
+ goto done;
+ }
+done:
+ if (err && do_retry) {
+ /* give the USB firmware some time to load */
+ usb2_pause_mtx(mtx, hz / 2);
+ /* no more retries after this retry */
+ do_retry = 0;
+ /* try again */
+ goto retry;
+ }
+ /* restore address */
+ udev->address = old_addr;
+ return (err);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_req_clear_device_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_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_device_feature
+ *
+ * Returns:
+ * 0: Success
+ * Else: Failure
+ *------------------------------------------------------------------------*/
+usb2_error_t
+usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx,
+ uint16_t sel)
+{
+ struct usb2_device_request req;
+
+ req.bmRequestType = UT_WRITE_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));
+}
diff --git a/sys/dev/usb/usb_request.h b/sys/dev/usb/usb_request.h
new file mode 100644
index 0000000..8a360b7
--- /dev/null
+++ b/sys/dev/usb/usb_request.h
@@ -0,0 +1,103 @@
+/* $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_
+
+struct usb2_process;
+
+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_do_request_proc(struct usb2_device *udev, struct usb2_process *pproc,
+ 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);
+usb2_error_t usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel);
+usb2_error_t usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel);
+
+#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/usb/usb_revision.h b/sys/dev/usb/usb_revision.h
new file mode 100644
index 0000000..06d1b21
--- /dev/null
+++ b/sys/dev/usb/usb_revision.h
@@ -0,0 +1,65 @@
+/* $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_REVISION_H_
+#define _USB2_REVISION_H_
+
+/*
+ * The "USB_SPEED" macro defines all the supported USB speeds.
+ */
+enum {
+ USB_SPEED_VARIABLE,
+ USB_SPEED_LOW,
+ USB_SPEED_FULL,
+ USB_SPEED_HIGH,
+ USB_SPEED_SUPER,
+ USB_SPEED_MAX
+};
+
+/*
+ * The "USB_REV" macro defines all the supported USB revisions.
+ */
+enum {
+ USB_REV_UNKNOWN,
+ USB_REV_PRE_1_0,
+ USB_REV_1_0,
+ USB_REV_1_1,
+ USB_REV_2_0,
+ USB_REV_2_5,
+ USB_REV_3_0,
+ USB_REV_MAX
+};
+
+/*
+ * The "USB_MODE" macro defines all the supported USB modes.
+ */
+enum {
+ USB_MODE_HOST,
+ USB_MODE_DEVICE,
+ USB_MODE_MAX
+};
+
+#endif /* _USB2_REVISION_H_ */
diff --git a/sys/dev/usb/usb_sw_transfer.c b/sys/dev/usb/usb_sw_transfer.c
new file mode 100644
index 0000000..984c233
--- /dev/null
+++ b/sys/dev/usb/usb_sw_transfer.c
@@ -0,0 +1,170 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_sw_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_debug.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_bus.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;
+ }
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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);
+}
diff --git a/sys/dev/usb/usb_sw_transfer.h b/sys/dev/usb/usb_sw_transfer.h
new file mode 100644
index 0000000..d2da0eb
--- /dev/null
+++ b/sys/dev/usb/usb_sw_transfer.h
@@ -0,0 +1,62 @@
+/* $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/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
new file mode 100644
index 0000000..df544a7
--- /dev/null
+++ b/sys/dev/usb/usb_transfer.c
@@ -0,0 +1,2826 @@
+/* $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/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_defs.h>
+
+#define USB_DEBUG_VAR usb2_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_transfer.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_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}},
+ [USB_SPEED_SUPER] = {.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}},
+ [USB_SPEED_SUPER] = {.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}},
+ [USB_SPEED_SUPER] = {.fixed = {1024, 1024, 1024, 1024}},
+ },
+
+ [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}},
+ [USB_SPEED_SUPER] = {.range = {0, 1024}},
+ },
+};
+
+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 *);
+static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *, uint8_t);
+static void usb2_control_transfer_init(struct usb2_xfer *);
+static uint8_t usb2_start_hardware_sub(struct usb2_xfer *);
+static void usb2_callback_proc(struct usb2_proc_msg *);
+static void usb2_callback_ss_done_defer(struct usb2_xfer *);
+static void usb2_callback_wrapper(struct usb2_xfer_queue *);
+static void usb2_dma_delay_done_cb(void *);
+static void usb2_transfer_start_cb(void *);
+static uint8_t usb2_callback_wrapper_sub(struct usb2_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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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
+ *------------------------------------------------------------------------*/
+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->xroot->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->xroot->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;
+ }
+ switch (parm->speed) {
+ case USB_SPEED_LOW:
+ case USB_SPEED_FULL:
+ frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER;
+ break;
+ default:
+ frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER;
+ break;
+ }
+
+ 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;
+
+ switch (parm->speed) {
+ case USB_SPEED_SUPER:
+ case USB_SPEED_VARIABLE:
+ /* 125us -> 1ms */
+ if (xfer->interval < 4)
+ xfer->interval = 1;
+ else if (xfer->interval > 16)
+ xfer->interval = (1<<(16-4));
+ else
+ xfer->interval =
+ (1 << (xfer->interval-4));
+ break;
+ case USB_SPEED_HIGH:
+ /* 125us -> 1ms */
+ xfer->interval /= 8;
+ break;
+ default:
+ break;
+ }
+ if (xfer->interval == 0) {
+ /*
+ * One millisecond is the smallest
+ * interval we support:
+ */
+ 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->xroot->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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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 *xfer_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 (xfer_mtx == NULL) {
+ DPRINTFN(6, "using global lock\n");
+ xfer_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->xfer_mtx = xfer_mtx;
+
+ usb2_dma_tag_setup(&info->dma_parent_tag,
+ parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag,
+ xfer_mtx, &usb2_bdma_done_event, info, 32, parm.dma_tag_max);
+
+ info->bus = udev->bus;
+ info->udev = udev;
+
+ 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].xroot = info;
+ info->done_m[1].hdr.pm_callback = &usb2_callback_proc;
+ info->done_m[1].xroot = info;
+
+ if (xfer_mtx == &Giant)
+ info->done_p =
+ &udev->bus->giant_callback_proc;
+ else
+ info->done_p =
+ &udev->bus->non_giant_callback_proc;
+ }
+ /* 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->address = udev->address;
+ xfer->priv_sc = priv_sc;
+ xfer->xroot = info;
+ info->setup_refcount++;
+
+ usb2_callout_init_mtx(&xfer->timeout_handle,
+ &udev->bus->bus_mtx, 0);
+ } 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
+ * the bus mutex before returning !
+ */
+ USB_BUS_LOCK(info->bus);
+
+ /* 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;
+
+ USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED);
+
+ /* wait for any outstanding DMA operations */
+
+ if (needs_delay) {
+ temp = usb2_get_dma_delay(info->bus);
+ usb2_pause_mtx(&info->bus->bus_mtx,
+ USB_MS_TO_TICKS(temp));
+ }
+
+ /* make sure that our done messages are not queued anywhere */
+ usb2_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]);
+
+ USB_BUS_UNLOCK(info->bus);
+
+ /* 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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) {
+ USB_XFER_LOCK(xfer);
+ USB_BUS_LOCK(xfer->xroot->bus);
+
+ /*
+ * 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 "xfer_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;
+
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ USB_XFER_UNLOCK(xfer);
+
+ 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->xroot) {
+ info = xfer->xroot;
+
+ USB_BUS_LOCK(info->bus);
+
+ 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 {
+ USB_BUS_UNLOCK(info->bus);
+ }
+ }
+ }
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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) {
+ USB_BUS_LOCK(xfer->xroot->bus);
+
+ usb2_dump_pipe(xfer->pipe);
+
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ }
+#endif
+
+ USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_NOTOWNED);
+
+ /* Only open the USB transfer once! */
+ if (!xfer->flags_int.open) {
+ xfer->flags_int.open = 1;
+
+ DPRINTF("open\n");
+
+ USB_BUS_LOCK(xfer->xroot->bus);
+ (xfer->pipe->methods->open) (xfer);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ }
+ /* set "transferring" flag */
+ xfer->flags_int.transferring = 1;
+
+ /* increment power reference */
+ usb2_transfer_power_ref(xfer, 1);
+
+ /*
+ * Check if the transfer is waiting on a queue, most
+ * frequently the "done_q":
+ */
+ if (xfer->wait_queue) {
+ USB_BUS_LOCK(xfer->xroot->bus);
+ usb2_transfer_dequeue(xfer);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ }
+ /* 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);
+ USB_BUS_LOCK(xfer->xroot->bus);
+ xfer->flags_int.can_cancel_immed = 1;
+ /* start the transfer */
+ usb2_command_wrapper(&xfer->pipe->pipe_q, xfer);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ return;
+ }
+ USB_BUS_LOCK(xfer->xroot->bus);
+ usb2_transfer_done(xfer, USB_ERR_INVAL);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ 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 */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ usb2_transfer_done(xfer, USB_ERR_INVAL);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ 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)) {
+ USB_BUS_LOCK(xfer->xroot->bus);
+ usb2_transfer_done(xfer, USB_ERR_STALLED);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ 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->xroot->dma_q, xfer);
+ return;
+ }
+ /*
+ * Enter the USB transfer into the Host Controller or
+ * Device Controller schedule:
+ */
+ usb2_pipe_enter(xfer);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pipe_enter - factored out code
+ *------------------------------------------------------------------------*/
+void
+usb2_pipe_enter(struct usb2_xfer *xfer)
+{
+ struct usb2_pipe *pipe;
+
+ USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+
+ USB_BUS_LOCK(xfer->xroot->bus);
+
+ 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);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ return;
+ }
+ } else {
+ xfer->flags_int.can_cancel_immed = 0;
+ }
+
+ /* start the transfer */
+ usb2_command_wrapper(&pipe->pipe_q, xfer);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+ USB_XFER_LOCK_ASSERT(xfer, 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;
+ }
+ USB_BUS_LOCK(xfer->xroot->bus);
+ /* call the USB transfer callback */
+ usb2_callback_ss_done_defer(xfer);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+ USB_XFER_LOCK_ASSERT(xfer, 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 */
+
+ USB_BUS_LOCK(xfer->xroot->bus);
+ 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);
+ }
+ }
+
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ if (xfer == NULL) {
+ /* transfer is gone */
+ return (0);
+ }
+ USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+
+ if (xfer->flags_int.transferring) {
+ /* trivial case */
+ return (1);
+ }
+ USB_BUS_LOCK(xfer->xroot->bus);
+ if (xfer->wait_queue) {
+ /* we are waiting on a queue somewhere */
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ return (1);
+ }
+ info = xfer->xroot;
+ pq = &info->done_q;
+
+ if (pq->curr == xfer) {
+ /* we are currently scheduled for callback */
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ return (1);
+ }
+ /* we are not pending */
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ 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->xroot->xfer_mtx != &Giant) {
+ USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED);
+ }
+ USB_XFER_LOCK(xfer);
+
+ 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->xroot->cv_drain, xfer->xroot->xfer_mtx);
+ }
+ USB_XFER_UNLOCK(xfer);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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->xroot;
+
+ /* Change locking order */
+ USB_BUS_UNLOCK(info->bus);
+
+ /*
+ * We exploit the fact that the mutex is the same for all
+ * callbacks that will be called from this thread:
+ */
+ mtx_lock(info->xfer_mtx);
+ USB_BUS_LOCK(info->bus);
+
+ /* Continue where we lost track */
+ usb2_command_wrapper(&info->done_q,
+ info->done_q.curr);
+
+ mtx_unlock(info->xfer_mtx);
+}
+
+/*------------------------------------------------------------------------*
+ * 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->xroot;
+ struct usb2_xfer_queue *pq = &info->done_q;
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ 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->xroot;
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+ if (!mtx_owned(xfer->xroot->xfer_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;
+
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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 */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ goto done;
+ }
+ } else {
+
+ if (usb2_callback_wrapper_sub(xfer)) {
+ /* the callback has been deferred */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ goto done;
+ }
+ /* decrement power reference */
+ usb2_transfer_power_ref(xfer, -1);
+
+ 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 */
+ USB_BUS_LOCK(xfer->xroot->bus);
+
+ /*
+ * Check if we got started after that we got cancelled, but
+ * before we managed to do the callback.
+ */
+ 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;
+ }
+
+done:
+ /*
+ * Check if we are draining.
+ */
+ 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->xroot->cv_drain);
+ }
+
+ /* do the next callback, if any */
+ usb2_command_wrapper(&info->done_q,
+ info->done_q.curr);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ DPRINTFN(3, "Completed %p\n", xfer);
+
+ /* queue callback for execution, again */
+ usb2_transfer_done(xfer, 0);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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->xroot->xfer_mtx)) {
+ /*
+ * If the private USB lock is not locked, then we assume
+ * that the BUS-DMA load stage has been passed:
+ */
+ pq = &xfer->xroot->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->xroot->bus->stats_err.uds_requests
+ [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++;
+ } else {
+ xfer->xroot->bus->stats_ok.uds_requests
+ [xfer->pipe->edesc->bmAttributes & UE_XFERTYPE]++;
+ }
+
+ /* call the USB transfer callback */
+ usb2_callback_ss_done_defer(xfer);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+ USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+
+ /* avoid any races by locking the USB mutex */
+ USB_BUS_LOCK(xfer->xroot->bus);
+
+ xfer->flags.stall_pipe = 1;
+
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+ }
+ USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+
+ /* avoid any races by locking the USB mutex */
+ USB_BUS_LOCK(xfer->xroot->bus);
+
+ xfer->flags.stall_pipe = 0;
+
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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;
+
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, 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;
+
+ info = xfer->xroot;
+ udev = info->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]->xroot;
+ if (usb2_proc_msignal(
+ &info->bus->non_giant_callback_proc,
+ &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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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)
+{
+ USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+
+ /* defer delay */
+ usb2_callout_reset(&xfer->timeout_handle,
+ USB_MS_TO_TICKS(ms), cb, xfer);
+}
+
+/*------------------------------------------------------------------------*
+ * 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");
+ USB_BUS_LOCK(xfer->xroot->bus);
+ (xfer->pipe->methods->close) (xfer);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ /* 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->xroot->bus);
+
+ DPRINTFN(3, "DMA delay, %u ms, "
+ "on %p\n", temp, xfer);
+
+ if (temp != 0) {
+ USB_BUS_LOCK(xfer->xroot->bus);
+ usb2_transfer_timeout_ms(xfer,
+ &usb2_dma_delay_done_cb, temp);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ 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:
+ */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ 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;
+ }
+ }
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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) {
+ USB_XFER_LOCK(xfer);
+ 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);
+ }
+ }
+ USB_XFER_UNLOCK(xfer);
+ } 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;
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+
+ USB_BUS_LOCK(udev->bus);
+ pipe->toggle_next = 0;
+ USB_BUS_UNLOCK(udev->bus);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+ }
+ USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED);
+ USB_XFER_LOCK_ASSERT(xfer2, 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->xroot->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);
+ }
+ }
+ mtx_unlock(mtx);
+}
+
+
+/*------------------------------------------------------------------------*
+ * 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 *xroot;
+ 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) {
+ xroot = xfer->xroot;
+ udev = xroot->udev;
+
+ /*
+ * Poll hardware - signal that we are polling by
+ * locking the private mutex:
+ */
+ USB_XFER_LOCK(xfer);
+ (udev->bus->methods->do_poll) (udev->bus);
+ USB_XFER_UNLOCK(xfer);
+
+ /* poll clear stall start */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ pm = &udev->cs_msg[0].hdr;
+ (pm->pm_callback) (pm);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+
+ if (udev->default_xfer[1]) {
+
+ /* poll timeout */
+ usb2_callout_poll(udev->default_xfer[1]);
+
+ /* poll clear stall done thread */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ pm = &udev->default_xfer[1]->
+ xroot->done_m[0].hdr;
+ (pm->pm_callback) (pm);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ }
+ /* poll timeout */
+ usb2_callout_poll(xfer);
+
+ /* poll done thread */
+ USB_BUS_LOCK(xfer->xroot->bus);
+ pm = &xroot->done_m[0].hdr;
+ (pm->pm_callback) (pm);
+ USB_BUS_UNLOCK(xfer->xroot->bus);
+ }
+ }
+}
+
+#else
+
+void
+usb2_do_poll(struct usb2_xfer **ppxfer, uint16_t max)
+{
+ /* polling not supported */
+}
+
+#endif
diff --git a/sys/dev/usb/usb_transfer.h b/sys/dev/usb/usb_transfer.h
new file mode 100644
index 0000000..34124c5
--- /dev/null
+++ b/sys/dev/usb/usb_transfer.h
@@ -0,0 +1,129 @@
+/* $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 *xroot;
+};
+
+/*
+ * 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; /* pointer to callback process */
+ void *memory_base;
+ struct mtx *xfer_mtx; /* cannot be changed during operation */
+ 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; /* pointer to USB bus (cached) */
+ struct usb2_device *udev; /* pointer to USB device */
+
+ 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_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);
+uint32_t usb2_get_dma_delay(struct usb2_bus *bus);
+void usb2_transfer_power_ref(struct usb2_xfer *xfer, int val);
+
+#endif /* _USB2_TRANSFER_H_ */
diff --git a/sys/dev/usb/usb_util.c b/sys/dev/usb/usb_util.c
new file mode 100644
index 0000000..541cd55
--- /dev/null
+++ b/sys/dev/usb/usb_util.c
@@ -0,0 +1,346 @@
+/* $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/usb/usb_defs.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+#include <dev/usb/usb.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_util.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_device.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+
+#include <dev/usb/usb_controller.h>
+#include <dev/usb/usb_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 happen 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));
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_pause_mtx - factored out code
+ *
+ * This function will delay the code by the passed number of system
+ * ticks. The passed mutex "mtx" will be dropped while waiting, if
+ * "mtx" is not NULL.
+ *------------------------------------------------------------------------*/
+void
+usb2_pause_mtx(struct mtx *mtx, int _ticks)
+{
+ if (mtx != NULL)
+ mtx_unlock(mtx);
+
+ if (cold) {
+ /* convert to milliseconds */
+ _ticks = (_ticks * 1000) / hz;
+ /* convert to microseconds, rounded up */
+ _ticks = (_ticks + 1) * 1000;
+ DELAY(_ticks);
+
+ } else {
+
+ /*
+ * Add one to the number of ticks so that we don't return
+ * too early!
+ */
+ _ticks++;
+
+ if (pause("USBWAIT", _ticks)) {
+ /* ignore */
+ }
+ }
+ if (mtx != NULL)
+ mtx_lock(mtx);
+}
+
+/*------------------------------------------------------------------------*
+ * 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 */
+ }
+}
+
+/*------------------------------------------------------------------------*
+ * 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 */
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_destroy - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_destroy(struct cv *cv)
+{
+ cv_destroy(cv);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * 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);
+}
+
+/*------------------------------------------------------------------------*
+ * usb2_cv_broadcast - wrapper function
+ *------------------------------------------------------------------------*/
+void
+usb2_cv_broadcast(struct cv *cv)
+{
+ wakeup(cv);
+}
+
+/*------------------------------------------------------------------------*
+ * 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/usb/usb_util.h b/sys/dev/usb/usb_util.h
new file mode 100644
index 0000000..e081c31
--- /dev/null
+++ b/sys/dev/usb/usb_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, int _ticks);
+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/usb/usbdevs b/sys/dev/usb/usbdevs
new file mode 100644
index 0000000..0a3d85e
--- /dev/null
+++ b/sys/dev/usb/usbdevs
@@ -0,0 +1,2527 @@
+$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 MEIZU 0x0492 Meizu Electronics
+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 AIPTEK2 0x04fc AIPTEK International
+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 CHICONY2 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 YISO 0x0eab Yiso Wireless Co.
+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 C-motech
+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 STELERA 0x1a8d Stelera Wireless
+vendor DRESDENELEKTRONIK 0x1cf1 dresden elektronik
+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 AIPTEK2 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 TRANSCEND 0x6387 Transcend JetFlash Drive
+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 F8T012 0x0121 F8T012xx1 Bluetooth USB Adapter
+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
+/* Also sold as 'Ativa 802.11g wireless card' */
+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 CHICONY2 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 CDMA Technologies USB modem
+product CMOTECH CNU550 0x5543 CDMA 2000 1xRTT/1xEVDO USB modem
+product CMOTECH CGU628 0x6006 CGU-628
+product CMOTECH CDMA_MODEM1 0x6280 CDMA Technologies USB modem
+product CMOTECH DISK 0xf000 disk mode
+
+/* 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
+
+/* dresden elektronik products */
+product DRESDENELEKTRONIK SENSORTERMINALBOARD 0x0001 SensorTerminalBoard
+
+/* 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 DX3800 0x0818 CX3700/CX3800/DX38x0 MFP scanner
+product EPSON 4800 0x0819 CX4700/CX4800/DX48x0 MFP scanner
+product EPSON 4200 0x0820 CX4100/CX4200/DX4200 MFP scanner
+product EPSON 5000 0x082b CX4900/CX5000/DX50x0 MFP scanner
+product EPSON 6000 0x082e CX5900/CX6000/DX60x0 MFP scanner
+product EPSON DX4000 0x082f DX4000 MFP scanner
+product EPSON DX7400 0x0838 CX7300/CX7400/DX7400 MFP scanner
+product EPSON DX8400 0x0839 CX8300/CX8400/DX8400 MFP scanner
+product EPSON SX100 0x0841 SX100/NX100 MFP scanner
+product EPSON NX300 0x0848 NX300 MFP scanner
+product EPSON SX200 0x0849 SX200/SX205 MFP scanner
+product EPSON SX400 0x084a SX400/NX400/TX400 MFP 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 MAXSTREAM 0xee18 Maxstream PKG-U
+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 4470C 0x0805 Scanjet 4470C
+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 OJ4215 0x3d11 OfficeJet 4215
+product HP HN210E 0x811c Ethernet HN210E
+product HP2 C500 0x6002 PhotoSmart C500
+product HP HS2300 0x1e1d hs2300 HSDPA (aka MC8775)
+
+/* 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 E220 0x1003 Huawei HSDPA 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
+product ITUNERNET USBLCD4X20 0xc001 USB-LCD 4x20
+
+/* 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
+
+/* Meizu Electronics */
+product MEIZU M6_SL 0x0140 MiniPlayer M6 (SL)
+
+/* 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
+product MELCO SG54HG 0x00f4 WLI-U2-SG54HG
+
+/* 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
+
+/* 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 COMFORT3000 0x00d1 Comfort Optical Mouse 3000 (Model 1043)
+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
+product MYSON STARREADER 0x9920 USB flash card adapter
+
+/* 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 NETGEAR WG111V2 0x6a00 WG111V2
+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 MC950D 0x4400 Novatel MC950D HSUPA
+product NOVATEL ZEROCD 0x5010 Novatel ZeroCD
+product NOVATEL ZEROCD2 0x5030 Novatel ZeroCD
+product NOVATEL U760 0x6000 Novatel U760
+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 SDS_HOTFIND_D 0x0400 SDS-infrared.com Hotfind-D Infrared Camera
+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 GTICON322 0xd033 GlobeTrotter Icon322 storage
+product OPTION GTMAX36 0x6701 GlobeTrotter Max 3.6 Modem
+product OPTION GTMAXHSUPA 0x7001 GlobeTrotter HSUPA
+
+/* 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
+product QUALCOMMINC ZTE_STOR 0x2000 USB ZTE Storage
+product QUALCOMMINC AC8700 0xfffe CDMA 1xEVDO USB 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
+product REALTEK RTL8187 0x8187 RTL8187 Wireless Adapter
+
+/* 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 AC875 0x6820 Sierra Wireless AirCard 875
+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 AC885U 0x6880 Sierra Wireless AirCard 885U
+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
+product SONY RF_RECEIVER 0x01db Sony RF mouse/kbd Receiver VGP-WRC1
+
+/* 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
+
+/* Stelera Wireless products */
+product STELERA ZEROCD 0x1000 Zerocd Installer
+product STELERA C105 0x1002 Stelera/Bandrish C105 USB
+
+/* 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
+
+/* Yiso Wireless Co. products */
+product YISO C893 0xc893 CDMA 2000 1xEVDO PC Card
+
+/* 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
diff --git a/sys/dev/usb/usbhid.h b/sys/dev/usb/usbhid.h
new file mode 100644
index 0000000..db6d370
--- /dev/null
+++ b/sys/dev/usb/usbhid.h
@@ -0,0 +1,175 @@
+/* $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_HID_H_
+#define _USB2_HID_H_
+
+#include <dev/usb/usb_endian.h>
+
+#define UR_GET_HID_DESCRIPTOR 0x06
+#define UDESC_HID 0x21
+#define UDESC_REPORT 0x22
+#define UDESC_PHYSICAL 0x23
+#define UR_SET_HID_DESCRIPTOR 0x07
+#define UR_GET_REPORT 0x01
+#define UR_SET_REPORT 0x09
+#define UR_GET_IDLE 0x02
+#define UR_SET_IDLE 0x0a
+#define UR_GET_PROTOCOL 0x03
+#define UR_SET_PROTOCOL 0x0b
+
+struct usb2_hid_descriptor {
+ uByte bLength;
+ uByte bDescriptorType;
+ uWord bcdHID;
+ uByte bCountryCode;
+ uByte bNumDescriptors;
+ struct {
+ uByte bDescriptorType;
+ uWord wDescriptorLength;
+ } descrs[1];
+} __packed;
+
+#define USB_HID_DESCRIPTOR_SIZE(n) (9+((n)*3))
+
+/* Usage pages */
+#define HUP_UNDEFINED 0x0000
+#define HUP_GENERIC_DESKTOP 0x0001
+#define HUP_SIMULATION 0x0002
+#define HUP_VR_CONTROLS 0x0003
+#define HUP_SPORTS_CONTROLS 0x0004
+#define HUP_GAMING_CONTROLS 0x0005
+#define HUP_KEYBOARD 0x0007
+#define HUP_LEDS 0x0008
+#define HUP_BUTTON 0x0009
+#define HUP_ORDINALS 0x000a
+#define HUP_TELEPHONY 0x000b
+#define HUP_CONSUMER 0x000c
+#define HUP_DIGITIZERS 0x000d
+#define HUP_PHYSICAL_IFACE 0x000e
+#define HUP_UNICODE 0x0010
+#define HUP_ALPHANUM_DISPLAY 0x0014
+#define HUP_MONITOR 0x0080
+#define HUP_MONITOR_ENUM_VAL 0x0081
+#define HUP_VESA_VC 0x0082
+#define HUP_VESA_CMD 0x0083
+#define HUP_POWER 0x0084
+#define HUP_BATTERY_SYSTEM 0x0085
+#define HUP_BARCODE_SCANNER 0x008b
+#define HUP_SCALE 0x008c
+#define HUP_CAMERA_CONTROL 0x0090
+#define HUP_ARCADE 0x0091
+#define HUP_MICROSOFT 0xff00
+
+/* Usages, generic desktop */
+#define HUG_POINTER 0x0001
+#define HUG_MOUSE 0x0002
+#define HUG_JOYSTICK 0x0004
+#define HUG_GAME_PAD 0x0005
+#define HUG_KEYBOARD 0x0006
+#define HUG_KEYPAD 0x0007
+#define HUG_X 0x0030
+#define HUG_Y 0x0031
+#define HUG_Z 0x0032
+#define HUG_RX 0x0033
+#define HUG_RY 0x0034
+#define HUG_RZ 0x0035
+#define HUG_SLIDER 0x0036
+#define HUG_DIAL 0x0037
+#define HUG_WHEEL 0x0038
+#define HUG_HAT_SWITCH 0x0039
+#define HUG_COUNTED_BUFFER 0x003a
+#define HUG_BYTE_COUNT 0x003b
+#define HUG_MOTION_WAKEUP 0x003c
+#define HUG_VX 0x0040
+#define HUG_VY 0x0041
+#define HUG_VZ 0x0042
+#define HUG_VBRX 0x0043
+#define HUG_VBRY 0x0044
+#define HUG_VBRZ 0x0045
+#define HUG_VNO 0x0046
+#define HUG_TWHEEL 0x0048 /* M$ Wireless Intellimouse Wheel */
+#define HUG_SYSTEM_CONTROL 0x0080
+#define HUG_SYSTEM_POWER_DOWN 0x0081
+#define HUG_SYSTEM_SLEEP 0x0082
+#define HUG_SYSTEM_WAKEUP 0x0083
+#define HUG_SYSTEM_CONTEXT_MENU 0x0084
+#define HUG_SYSTEM_MAIN_MENU 0x0085
+#define HUG_SYSTEM_APP_MENU 0x0086
+#define HUG_SYSTEM_MENU_HELP 0x0087
+#define HUG_SYSTEM_MENU_EXIT 0x0088
+#define HUG_SYSTEM_MENU_SELECT 0x0089
+#define HUG_SYSTEM_MENU_RIGHT 0x008a
+#define HUG_SYSTEM_MENU_LEFT 0x008b
+#define HUG_SYSTEM_MENU_UP 0x008c
+#define HUG_SYSTEM_MENU_DOWN 0x008d
+
+/* Usages Digitizers */
+#define HUD_UNDEFINED 0x0000
+#define HUD_TIP_PRESSURE 0x0030
+#define HUD_BARREL_PRESSURE 0x0031
+#define HUD_IN_RANGE 0x0032
+#define HUD_TOUCH 0x0033
+#define HUD_UNTOUCH 0x0034
+#define HUD_TAP 0x0035
+#define HUD_QUALITY 0x0036
+#define HUD_DATA_VALID 0x0037
+#define HUD_TRANSDUCER_INDEX 0x0038
+#define HUD_TABLET_FKEYS 0x0039
+#define HUD_PROGRAM_CHANGE_KEYS 0x003a
+#define HUD_BATTERY_STRENGTH 0x003b
+#define HUD_INVERT 0x003c
+#define HUD_X_TILT 0x003d
+#define HUD_Y_TILT 0x003e
+#define HUD_AZIMUTH 0x003f
+#define HUD_ALTITUDE 0x0040
+#define HUD_TWIST 0x0041
+#define HUD_TIP_SWITCH 0x0042
+#define HUD_SEC_TIP_SWITCH 0x0043
+#define HUD_BARREL_SWITCH 0x0044
+#define HUD_ERASER 0x0045
+#define HUD_TABLET_PICK 0x0046
+
+#define HID_USAGE2(p,u) (((p) << 16) | (u))
+
+#define UHID_INPUT_REPORT 0x01
+#define UHID_OUTPUT_REPORT 0x02
+#define UHID_FEATURE_REPORT 0x03
+
+/* Bits in the input/output/feature items */
+#define HIO_CONST 0x001
+#define HIO_VARIABLE 0x002
+#define HIO_RELATIVE 0x004
+#define HIO_WRAP 0x008
+#define HIO_NONLINEAR 0x010
+#define HIO_NOPREF 0x020
+#define HIO_NULLSTATE 0x040
+#define HIO_VOLATILE 0x080
+#define HIO_BUFBYTES 0x100
+
+#endif /* _USB2_HID_H_ */
diff --git a/sys/dev/usb/wlan/if_rum.c b/sys/dev/usb/wlan/if_rum.c
new file mode 100644
index 0000000..9b903f0
--- /dev/null
+++ b/sys/dev/usb/wlan/if_rum.c
@@ -0,0 +1,2435 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005-2007 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ * Copyright (c) 2007-2008 Hans Petter Selasky <hselasky@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2501USB/RT2601USB chipset driver
+ * http://www.ralinktech.com.tw/
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR rum_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/wlan/usb_wlan.h>
+#include <dev/usb/wlan/if_rumreg.h>
+#include <dev/usb/wlan/if_rumvar.h>
+#include <dev/usb/wlan/if_rumfw.h>
+
+#if USB_DEBUG
+static int rum_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum");
+SYSCTL_INT(_hw_usb2_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0,
+ "Debug level");
+#endif
+
+#define rum_do_request(sc,req,data) \
+ usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000)
+
+static const struct usb2_device_id rum_devs[] = {
+ { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM) },
+ { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2) },
+ { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3) },
+ { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4) },
+ { USB_VP(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700) },
+ { USB_VP(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO) },
+ { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1) },
+ { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2) },
+ { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A) },
+ { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3) },
+ { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC) },
+ { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR) },
+ { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2) },
+ { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL) },
+ { USB_VP(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX) },
+ { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F) },
+ { USB_VP(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573) },
+ { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1) },
+ { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340) },
+ { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111) },
+ { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110) },
+ { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS) },
+ { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS) },
+ { USB_VP(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573) },
+ { USB_VP(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573) },
+ { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB) },
+ { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP) },
+ { USB_VP(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G) },
+ { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP) },
+ { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4) },
+ { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573) },
+ { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP) },
+ { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2) },
+ { USB_VP(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM) },
+ { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573) },
+ { USB_VP(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2) },
+ { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573) },
+ { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2) },
+ { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671) },
+ { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2) },
+ { USB_VP(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172) },
+ { USB_VP(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573) },
+ { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573) },
+};
+
+MODULE_DEPEND(rum, wlan, 1, 1, 1);
+MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1);
+MODULE_DEPEND(rum, usb, 1, 1, 1);
+
+static device_probe_t rum_match;
+static device_attach_t rum_attach;
+static device_detach_t rum_detach;
+
+static usb2_callback_t rum_bulk_read_callback;
+static usb2_callback_t rum_bulk_write_callback;
+
+static usb2_proc_callback_t rum_attach_post;
+static usb2_proc_callback_t rum_task;
+static usb2_proc_callback_t rum_scantask;
+static usb2_proc_callback_t rum_promisctask;
+static usb2_proc_callback_t rum_amrr_task;
+static usb2_proc_callback_t rum_init_task;
+static usb2_proc_callback_t rum_stop_task;
+
+static struct ieee80211vap *rum_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rum_vap_delete(struct ieee80211vap *);
+static void rum_tx_free(struct rum_tx_data *, int);
+static void rum_setup_tx_list(struct rum_softc *);
+static void rum_unsetup_tx_list(struct rum_softc *);
+static int rum_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void rum_setup_tx_desc(struct rum_softc *,
+ struct rum_tx_desc *, uint32_t, uint16_t, int,
+ int);
+static int rum_tx_mgt(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int rum_tx_raw(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *,
+ const struct ieee80211_bpf_params *);
+static int rum_tx_data(struct rum_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static void rum_start(struct ifnet *);
+static int rum_ioctl(struct ifnet *, u_long, caddr_t);
+static void rum_eeprom_read(struct rum_softc *, uint16_t, void *,
+ int);
+static uint32_t rum_read(struct rum_softc *, uint16_t);
+static void rum_read_multi(struct rum_softc *, uint16_t, void *,
+ int);
+static void rum_write(struct rum_softc *, uint16_t, uint32_t);
+static void rum_write_multi(struct rum_softc *, uint16_t, void *,
+ size_t);
+static void rum_bbp_write(struct rum_softc *, uint8_t, uint8_t);
+static uint8_t rum_bbp_read(struct rum_softc *, uint8_t);
+static void rum_rf_write(struct rum_softc *, uint8_t, uint32_t);
+static void rum_select_antenna(struct rum_softc *);
+static void rum_enable_mrr(struct rum_softc *);
+static void rum_set_txpreamble(struct rum_softc *);
+static void rum_set_basicrates(struct rum_softc *);
+static void rum_select_band(struct rum_softc *,
+ struct ieee80211_channel *);
+static void rum_set_chan(struct rum_softc *,
+ struct ieee80211_channel *);
+static void rum_enable_tsf_sync(struct rum_softc *);
+static void rum_update_slot(struct ifnet *);
+static void rum_set_bssid(struct rum_softc *, const uint8_t *);
+static void rum_set_macaddr(struct rum_softc *, const uint8_t *);
+static const char *rum_get_rf(int);
+static void rum_read_eeprom(struct rum_softc *);
+static int rum_bbp_init(struct rum_softc *);
+static void rum_init(void *);
+static int rum_load_microcode(struct rum_softc *, const u_char *,
+ size_t);
+static int rum_prepare_beacon(struct rum_softc *,
+ struct ieee80211vap *);
+static int rum_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void rum_newassoc(struct ieee80211_node *, int);
+static void rum_scan_start(struct ieee80211com *);
+static void rum_scan_end(struct ieee80211com *);
+static void rum_set_channel(struct ieee80211com *);
+static int rum_get_rssi(struct rum_softc *, uint8_t);
+static void rum_amrr_start(struct rum_softc *,
+ struct ieee80211_node *);
+static void rum_amrr_timeout(void *);
+static int rum_pause(struct rum_softc *, int);
+static void rum_queue_command(struct rum_softc *,
+ usb2_proc_callback_t *, struct usb2_proc_msg *,
+ struct usb2_proc_msg *);
+
+static const struct {
+ uint32_t reg;
+ uint32_t val;
+} rum_def_mac[] = {
+ { RT2573_TXRX_CSR0, 0x025fb032 },
+ { RT2573_TXRX_CSR1, 0x9eaa9eaf },
+ { RT2573_TXRX_CSR2, 0x8a8b8c8d },
+ { RT2573_TXRX_CSR3, 0x00858687 },
+ { RT2573_TXRX_CSR7, 0x2e31353b },
+ { RT2573_TXRX_CSR8, 0x2a2a2a2c },
+ { RT2573_TXRX_CSR15, 0x0000000f },
+ { RT2573_MAC_CSR6, 0x00000fff },
+ { RT2573_MAC_CSR8, 0x016c030a },
+ { RT2573_MAC_CSR10, 0x00000718 },
+ { RT2573_MAC_CSR12, 0x00000004 },
+ { RT2573_MAC_CSR13, 0x00007f00 },
+ { RT2573_SEC_CSR0, 0x00000000 },
+ { RT2573_SEC_CSR1, 0x00000000 },
+ { RT2573_SEC_CSR5, 0x00000000 },
+ { RT2573_PHY_CSR1, 0x000023b0 },
+ { RT2573_PHY_CSR5, 0x00040a06 },
+ { RT2573_PHY_CSR6, 0x00080606 },
+ { RT2573_PHY_CSR7, 0x00000408 },
+ { RT2573_AIFSN_CSR, 0x00002273 },
+ { RT2573_CWMIN_CSR, 0x00002344 },
+ { RT2573_CWMAX_CSR, 0x000034aa }
+};
+
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} rum_def_bbp[] = {
+ { 3, 0x80 },
+ { 15, 0x30 },
+ { 17, 0x20 },
+ { 21, 0xc8 },
+ { 22, 0x38 },
+ { 23, 0x06 },
+ { 24, 0xfe },
+ { 25, 0x0a },
+ { 26, 0x0d },
+ { 32, 0x0b },
+ { 34, 0x12 },
+ { 37, 0x07 },
+ { 39, 0xf8 },
+ { 41, 0x60 },
+ { 53, 0x10 },
+ { 54, 0x18 },
+ { 60, 0x10 },
+ { 61, 0x04 },
+ { 62, 0x04 },
+ { 75, 0xfe },
+ { 86, 0xfe },
+ { 88, 0xfe },
+ { 90, 0x0f },
+ { 99, 0x00 },
+ { 102, 0x16 },
+ { 107, 0x04 }
+};
+
+static const struct rfprog {
+ uint8_t chan;
+ uint32_t r1, r2, r3, r4;
+} rum_rf5226[] = {
+ { 1, 0x00b03, 0x001e1, 0x1a014, 0x30282 },
+ { 2, 0x00b03, 0x001e1, 0x1a014, 0x30287 },
+ { 3, 0x00b03, 0x001e2, 0x1a014, 0x30282 },
+ { 4, 0x00b03, 0x001e2, 0x1a014, 0x30287 },
+ { 5, 0x00b03, 0x001e3, 0x1a014, 0x30282 },
+ { 6, 0x00b03, 0x001e3, 0x1a014, 0x30287 },
+ { 7, 0x00b03, 0x001e4, 0x1a014, 0x30282 },
+ { 8, 0x00b03, 0x001e4, 0x1a014, 0x30287 },
+ { 9, 0x00b03, 0x001e5, 0x1a014, 0x30282 },
+ { 10, 0x00b03, 0x001e5, 0x1a014, 0x30287 },
+ { 11, 0x00b03, 0x001e6, 0x1a014, 0x30282 },
+ { 12, 0x00b03, 0x001e6, 0x1a014, 0x30287 },
+ { 13, 0x00b03, 0x001e7, 0x1a014, 0x30282 },
+ { 14, 0x00b03, 0x001e8, 0x1a014, 0x30284 },
+
+ { 34, 0x00b03, 0x20266, 0x36014, 0x30282 },
+ { 38, 0x00b03, 0x20267, 0x36014, 0x30284 },
+ { 42, 0x00b03, 0x20268, 0x36014, 0x30286 },
+ { 46, 0x00b03, 0x20269, 0x36014, 0x30288 },
+
+ { 36, 0x00b03, 0x00266, 0x26014, 0x30288 },
+ { 40, 0x00b03, 0x00268, 0x26014, 0x30280 },
+ { 44, 0x00b03, 0x00269, 0x26014, 0x30282 },
+ { 48, 0x00b03, 0x0026a, 0x26014, 0x30284 },
+ { 52, 0x00b03, 0x0026b, 0x26014, 0x30286 },
+ { 56, 0x00b03, 0x0026c, 0x26014, 0x30288 },
+ { 60, 0x00b03, 0x0026e, 0x26014, 0x30280 },
+ { 64, 0x00b03, 0x0026f, 0x26014, 0x30282 },
+
+ { 100, 0x00b03, 0x0028a, 0x2e014, 0x30280 },
+ { 104, 0x00b03, 0x0028b, 0x2e014, 0x30282 },
+ { 108, 0x00b03, 0x0028c, 0x2e014, 0x30284 },
+ { 112, 0x00b03, 0x0028d, 0x2e014, 0x30286 },
+ { 116, 0x00b03, 0x0028e, 0x2e014, 0x30288 },
+ { 120, 0x00b03, 0x002a0, 0x2e014, 0x30280 },
+ { 124, 0x00b03, 0x002a1, 0x2e014, 0x30282 },
+ { 128, 0x00b03, 0x002a2, 0x2e014, 0x30284 },
+ { 132, 0x00b03, 0x002a3, 0x2e014, 0x30286 },
+ { 136, 0x00b03, 0x002a4, 0x2e014, 0x30288 },
+ { 140, 0x00b03, 0x002a6, 0x2e014, 0x30280 },
+
+ { 149, 0x00b03, 0x002a8, 0x2e014, 0x30287 },
+ { 153, 0x00b03, 0x002a9, 0x2e014, 0x30289 },
+ { 157, 0x00b03, 0x002ab, 0x2e014, 0x30281 },
+ { 161, 0x00b03, 0x002ac, 0x2e014, 0x30283 },
+ { 165, 0x00b03, 0x002ad, 0x2e014, 0x30285 }
+}, rum_rf5225[] = {
+ { 1, 0x00b33, 0x011e1, 0x1a014, 0x30282 },
+ { 2, 0x00b33, 0x011e1, 0x1a014, 0x30287 },
+ { 3, 0x00b33, 0x011e2, 0x1a014, 0x30282 },
+ { 4, 0x00b33, 0x011e2, 0x1a014, 0x30287 },
+ { 5, 0x00b33, 0x011e3, 0x1a014, 0x30282 },
+ { 6, 0x00b33, 0x011e3, 0x1a014, 0x30287 },
+ { 7, 0x00b33, 0x011e4, 0x1a014, 0x30282 },
+ { 8, 0x00b33, 0x011e4, 0x1a014, 0x30287 },
+ { 9, 0x00b33, 0x011e5, 0x1a014, 0x30282 },
+ { 10, 0x00b33, 0x011e5, 0x1a014, 0x30287 },
+ { 11, 0x00b33, 0x011e6, 0x1a014, 0x30282 },
+ { 12, 0x00b33, 0x011e6, 0x1a014, 0x30287 },
+ { 13, 0x00b33, 0x011e7, 0x1a014, 0x30282 },
+ { 14, 0x00b33, 0x011e8, 0x1a014, 0x30284 },
+
+ { 34, 0x00b33, 0x01266, 0x26014, 0x30282 },
+ { 38, 0x00b33, 0x01267, 0x26014, 0x30284 },
+ { 42, 0x00b33, 0x01268, 0x26014, 0x30286 },
+ { 46, 0x00b33, 0x01269, 0x26014, 0x30288 },
+
+ { 36, 0x00b33, 0x01266, 0x26014, 0x30288 },
+ { 40, 0x00b33, 0x01268, 0x26014, 0x30280 },
+ { 44, 0x00b33, 0x01269, 0x26014, 0x30282 },
+ { 48, 0x00b33, 0x0126a, 0x26014, 0x30284 },
+ { 52, 0x00b33, 0x0126b, 0x26014, 0x30286 },
+ { 56, 0x00b33, 0x0126c, 0x26014, 0x30288 },
+ { 60, 0x00b33, 0x0126e, 0x26014, 0x30280 },
+ { 64, 0x00b33, 0x0126f, 0x26014, 0x30282 },
+
+ { 100, 0x00b33, 0x0128a, 0x2e014, 0x30280 },
+ { 104, 0x00b33, 0x0128b, 0x2e014, 0x30282 },
+ { 108, 0x00b33, 0x0128c, 0x2e014, 0x30284 },
+ { 112, 0x00b33, 0x0128d, 0x2e014, 0x30286 },
+ { 116, 0x00b33, 0x0128e, 0x2e014, 0x30288 },
+ { 120, 0x00b33, 0x012a0, 0x2e014, 0x30280 },
+ { 124, 0x00b33, 0x012a1, 0x2e014, 0x30282 },
+ { 128, 0x00b33, 0x012a2, 0x2e014, 0x30284 },
+ { 132, 0x00b33, 0x012a3, 0x2e014, 0x30286 },
+ { 136, 0x00b33, 0x012a4, 0x2e014, 0x30288 },
+ { 140, 0x00b33, 0x012a6, 0x2e014, 0x30280 },
+
+ { 149, 0x00b33, 0x012a8, 0x2e014, 0x30287 },
+ { 153, 0x00b33, 0x012a9, 0x2e014, 0x30289 },
+ { 157, 0x00b33, 0x012ab, 0x2e014, 0x30281 },
+ { 161, 0x00b33, 0x012ac, 0x2e014, 0x30283 },
+ { 165, 0x00b33, 0x012ad, 0x2e014, 0x30285 }
+};
+
+static const struct usb2_config rum_config[RUM_N_TRANSFER] = {
+ [RUM_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = rum_bulk_write_callback,
+ .mh.timeout = 5000, /* ms */
+ },
+ [RUM_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = rum_bulk_read_callback,
+ },
+};
+
+static int
+rum_match(device_t self)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa));
+}
+
+static int
+rum_attach(device_t self)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(self);
+ struct rum_softc *sc = device_get_softc(self);
+ uint8_t iface_index;
+ int error;
+
+ device_set_usb2_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(self),
+ MTX_NETWORK_LOCK, MTX_DEF);
+
+ iface_index = RT2573_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device, &iface_index,
+ sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(self, "could not allocate USB transfers, "
+ "err=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx,
+ device_get_nameunit(self), USB_PRI_MED);
+ if (error) {
+ device_printf(self, "could not setup config thread!\n");
+ goto detach;
+ }
+
+ /* fork rest of the attach code */
+ RUM_LOCK(sc);
+ rum_queue_command(sc, rum_attach_post,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ RUM_UNLOCK(sc);
+ return (0);
+
+detach:
+ rum_detach(self);
+ return (ENXIO); /* failure */
+}
+
+static void
+rum_attach_post(struct usb2_proc_msg *pm)
+{
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp;
+ struct ieee80211com *ic;
+ unsigned int ntries;
+ int error;
+ uint32_t tmp;
+ uint8_t bands;
+
+ /* retrieve RT2573 rev. no */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if ((tmp = rum_read(sc, RT2573_MAC_CSR0)) != 0)
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for chip to settle\n");
+ return;
+ }
+
+ /* retrieve MAC address and various other things from EEPROM */
+ rum_read_eeprom(sc);
+
+ device_printf(sc->sc_dev, "MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
+ tmp, rum_get_rf(sc->rf_rev));
+
+ error = rum_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode));
+ if (error != 0) {
+ RUM_UNLOCK(sc);
+ device_printf(sc->sc_dev, "could not load 8051 microcode\n");
+ return;
+ }
+ RUM_UNLOCK(sc);
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ RUM_LOCK(sc);
+ return;
+ }
+ ic = ifp->if_l2com;
+
+ ifp->if_softc = sc;
+ if_initname(ifp, "rum", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = rum_init;
+ ifp->if_ioctl = rum_ioctl;
+ ifp->if_start = rum_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid);
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_5226)
+ setbit(&bands, IEEE80211_MODE_11A);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_newassoc = rum_newassoc;
+ ic->ic_raw_xmit = rum_raw_xmit;
+ ic->ic_node_alloc = rum_node_alloc;
+ ic->ic_scan_start = rum_scan_start;
+ ic->ic_scan_end = rum_scan_end;
+ ic->ic_set_channel = rum_set_channel;
+
+ ic->ic_vap_create = rum_vap_create;
+ ic->ic_vap_delete = rum_vap_delete;
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
+
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT);
+
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ RUM_LOCK(sc);
+}
+
+static int
+rum_detach(device_t self)
+{
+ struct rum_softc *sc = device_get_softc(self);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic;
+
+ /* wait for any post attach or other command to complete */
+ usb2_proc_drain(&sc->sc_tq);
+
+ /* stop all USB transfers */
+ usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER);
+ usb2_proc_free(&sc->sc_tq);
+
+ /* free TX list, if any */
+ RUM_LOCK(sc);
+ rum_unsetup_tx_list(sc);
+ RUM_UNLOCK(sc);
+
+ if (ifp) {
+ ic = ifp->if_l2com;
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+ if_free(ifp);
+ }
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+rum_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+ struct rum_vap *rvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ rvp = (struct rum_vap *) malloc(sizeof(struct rum_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (rvp == NULL)
+ return NULL;
+ vap = &rvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ rvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = rum_newstate;
+
+ rvp->sc = sc;
+ usb2_callout_init_mtx(&rvp->amrr_ch, &sc->sc_mtx, 0);
+ ieee80211_amrr_init(&rvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+rum_vap_delete(struct ieee80211vap *vap)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ usb2_callout_drain(&rvp->amrr_ch);
+ ieee80211_amrr_cleanup(&rvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(rvp, M_80211_VAP);
+}
+
+static void
+rum_tx_free(struct rum_tx_data *data, int txerr)
+{
+ struct rum_softc *sc = data->sc;
+
+ if (data->m != NULL) {
+ if (data->m->m_flags & M_TXCB)
+ ieee80211_process_callback(data->ni, data->m,
+ txerr ? ETIMEDOUT : 0);
+ m_freem(data->m);
+ data->m = NULL;
+
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+}
+
+static void
+rum_setup_tx_list(struct rum_softc *sc)
+{
+ struct rum_tx_data *data;
+ int i;
+
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+}
+
+static void
+rum_unsetup_tx_list(struct rum_softc *sc)
+{
+ struct rum_tx_data *data;
+ int i;
+
+ /* make sure any subsequent use of the queues will fail */
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ /* free up all node references and mbufs */
+ for (i = 0; i < RUM_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static void
+rum_task(struct usb2_proc_msg *pm)
+{
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rum_vap *rvp = RUM_VAP(vap);
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_state ostate;
+ struct ieee80211_node *ni;
+ uint32_t tmp;
+
+ ostate = vap->iv_state;
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ tmp = rum_read(sc, RT2573_TXRX_CSR9);
+ rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ ni = vap->iv_bss;
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ rum_update_slot(ic->ic_ifp);
+ rum_enable_mrr(sc);
+ rum_set_txpreamble(sc);
+ rum_set_basicrates(sc);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ rum_set_bssid(sc, sc->sc_bssid);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS)
+ rum_prepare_beacon(sc, vap);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
+ rum_enable_tsf_sync(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ rum_amrr_start(sc, ni);
+ break;
+ default:
+ break;
+ }
+
+ RUM_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ rvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+ RUM_LOCK(sc);
+}
+
+static int
+rum_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ DPRINTF("%s -> %s\n",
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ RUM_LOCK(sc);
+ usb2_callout_stop(&rvp->amrr_ch);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+ RUM_UNLOCK(sc);
+
+ if (nstate == IEEE80211_S_INIT) {
+ rvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
+ RUM_LOCK(sc);
+ rum_queue_command(sc, rum_task, &sc->sc_task[0].hdr,
+ &sc->sc_task[1].hdr);
+ RUM_UNLOCK(sc);
+ return EINPROGRESS;
+ }
+}
+
+static void
+rum_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct rum_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ struct rum_tx_data *data;
+ struct mbuf *m;
+ unsigned int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen);
+
+ /* free resources */
+ data = xfer->priv_fifo;
+ rum_tx_free(data, 0);
+ xfer->priv_fifo = NULL;
+
+ ifp->if_opackets++;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->tx_q);
+ if (data) {
+ STAILQ_REMOVE_HEAD(&sc->tx_q, next);
+ m = data->m;
+
+ if (m->m_pkthdr.len > (MCLBYTES + RT2573_TX_DESC_SIZE)) {
+ DPRINTFN(0, "data overflow, %u bytes\n",
+ m->m_pkthdr.len);
+ m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE);
+ }
+ usb2_copy_in(xfer->frbuffers, 0, &data->desc,
+ RT2573_TX_DESC_SIZE);
+ usb2_m_copy_in(xfer->frbuffers, RT2573_TX_DESC_SIZE, m,
+ 0, m->m_pkthdr.len);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = data->rate;
+ tap->wt_chan_freq = htole16(c->ic_freq);
+ tap->wt_chan_flags = htole16(c->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m);
+ }
+
+ /* align end on a 4-bytes boundary */
+ len = (RT2573_TX_DESC_SIZE + m->m_pkthdr.len + 3) & ~3;
+ if ((len % 64) == 0)
+ len += 4;
+
+ DPRINTFN(11, "sending frame len=%u xferlen=%u\n",
+ m->m_pkthdr.len, len);
+
+ xfer->frlengths[0] = len;
+ xfer->priv_fifo = data;
+
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+ data = xfer->priv_fifo;
+ if (data != NULL) {
+ rum_tx_free(data, xfer->error);
+ xfer->priv_fifo = NULL;
+ }
+
+ if (xfer->error == USB_ERR_STALLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ if (xfer->error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+ break;
+ }
+}
+
+static void
+rum_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct rum_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ uint32_t flags;
+ uint8_t rssi = 0;
+ unsigned int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen);
+
+ len = xfer->actlen;
+ if (len < RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN) {
+ DPRINTF("%s: xfer too short %d\n",
+ device_get_nameunit(sc->sc_dev), len);
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+
+ len -= RT2573_RX_DESC_SIZE;
+ usb2_copy_out(xfer->frbuffers, 0, &sc->sc_rx_desc,
+ RT2573_RX_DESC_SIZE);
+
+ rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi);
+ flags = le32toh(sc->sc_rx_desc.flags);
+ if (flags & RT2573_RX_CRC_ERROR) {
+ /*
+ * This should not happen since we did not
+ * request to receive those frames when we
+ * filled RUM_TXRX_CSR2:
+ */
+ DPRINTFN(5, "PHY or CRC error\n");
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ DPRINTF("could not allocate mbuf\n");
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE,
+ mtod(m, uint8_t *), len);
+
+ /* finalize mbuf */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff;
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
+ tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate,
+ (flags & RT2573_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antenna = sc->rx_ant;
+ tap->wr_antsignal = rssi;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+
+ /*
+ * At the end of a USB callback it is always safe to unlock
+ * the private mutex of a device! That is why we do the
+ * "ieee80211_input" here, and not some lines up!
+ */
+ if (m) {
+ RUM_UNLOCK(sc);
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi,
+ RT2573_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi,
+ RT2573_NOISE_FLOOR, 0);
+ RUM_LOCK(sc);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static uint8_t
+rum_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+
+static void
+rum_setup_tx_desc(struct rum_softc *sc, struct rum_tx_desc *desc,
+ uint32_t flags, uint16_t xflags, int len, int rate)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t plcp_length;
+ int remainder;
+
+ desc->flags = htole32(flags);
+ desc->flags |= htole32(RT2573_TX_VALID);
+ desc->flags |= htole32(len << 16);
+
+ desc->xflags = htole16(xflags);
+
+ desc->wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) |
+ RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10));
+
+ /* setup PLCP fields */
+ desc->plcp_signal = rum_plcp_signal(rate);
+ desc->plcp_service = 4;
+
+ len += IEEE80211_CRC_LEN;
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
+ desc->flags |= htole32(RT2573_TX_OFDM);
+
+ plcp_length = len & 0xfff;
+ desc->plcp_length_hi = plcp_length >> 6;
+ desc->plcp_length_lo = plcp_length & 0x3f;
+ } else {
+ plcp_length = (16 * len + rate - 1) / rate;
+ if (rate == 22) {
+ remainder = (16 * len) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= RT2573_PLCP_LENGEXT;
+ }
+ desc->plcp_length_hi = plcp_length >> 8;
+ desc->plcp_length_lo = plcp_length & 0xff;
+
+ if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->plcp_signal |= 0x08;
+ }
+}
+
+#define RUM_TX_TIMEOUT 5000
+
+static int
+rum_sendprot(struct rum_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct rum_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RT2573_TX_MORE_FRAG;
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RT2573_TX_NEED_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ data->rate = protrate;
+ rum_setup_tx_desc(sc, &data->desc, flags, 0, mprot->m_pkthdr.len, protrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static int
+rum_tx_mgt(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RT2573_TX_NEED_ACK;
+
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
+ flags |= RT2573_TX_TIMESTAMP;
+ }
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = tp->mgmtrate;
+
+ rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, tp->mgmtrate);
+
+ DPRINTFN(10, "sending mgt frame len=%d rate=%d\n",
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, tp->mgmtrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static int
+rum_tx_raw(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct rum_tx_data *data;
+ uint32_t flags;
+ int rate, error;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
+ /* XXX validate */
+ if (rate == 0) {
+ m_freem(m0);
+ return EINVAL;
+ }
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RT2573_TX_NEED_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = rum_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ /* XXX need to setup descriptor ourself */
+ rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending raw frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static int
+rum_tx_data(struct rum_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct rum_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+ int error, rate;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else
+ rate = ni->ni_txrate;
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = rum_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
+ }
+ }
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RT2573_TX_NEED_ACK;
+ flags |= RT2573_TX_MORE_FRAG;
+
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+ }
+
+ rum_setup_tx_desc(sc, &data->desc, flags, 0, m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending frame len=%d rate=%d\n",
+ m0->m_pkthdr.len + (int)RT2573_TX_DESC_SIZE, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[RUM_BULK_WR]);
+
+ return 0;
+}
+
+static void
+rum_start(struct ifnet *ifp)
+{
+ struct rum_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ RUM_LOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ RUM_UNLOCK(sc);
+ return;
+ }
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_nfree == 0) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (rum_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+ }
+ RUM_UNLOCK(sc);
+}
+
+static int
+rum_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct rum_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ RUM_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ rum_queue_command(sc, rum_init_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ startall = 1;
+ } else
+ rum_queue_command(sc, rum_promisctask,
+ &sc->sc_promisctask[0].hdr,
+ &sc->sc_promisctask[1].hdr);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ rum_queue_command(sc, rum_stop_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ }
+ }
+ RUM_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+static void
+rum_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_EEPROM;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ error = rum_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static uint32_t
+rum_read(struct rum_softc *sc, uint16_t reg)
+{
+ uint32_t val;
+
+ rum_read_multi(sc, reg, &val, sizeof val);
+
+ return le32toh(val);
+}
+
+static void
+rum_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, int len)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RT2573_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = rum_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not multi read MAC register: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static void
+rum_write(struct rum_softc *sc, uint16_t reg, uint32_t val)
+{
+ uint32_t tmp = htole32(val);
+
+ rum_write_multi(sc, reg, &tmp, sizeof tmp);
+}
+
+static void
+rum_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, size_t len)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2573_WRITE_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = rum_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not multi write MAC register: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static void
+rum_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY))
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to BBP\n");
+ return;
+ }
+
+ tmp = RT2573_BBP_BUSY | (reg & 0x7f) << 8 | val;
+ rum_write(sc, RT2573_PHY_CSR3, tmp);
+}
+
+static uint8_t
+rum_bbp_read(struct rum_softc *sc, uint8_t reg)
+{
+ uint32_t val;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR3) & RT2573_BBP_BUSY))
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+ }
+
+ val = RT2573_BBP_BUSY | RT2573_BBP_READ | reg << 8;
+ rum_write(sc, RT2573_PHY_CSR3, val);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ val = rum_read(sc, RT2573_PHY_CSR3);
+ if (!(val & RT2573_BBP_BUSY))
+ return val & 0xff;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+}
+
+static void
+rum_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(rum_read(sc, RT2573_PHY_CSR4) & RT2573_RF_BUSY))
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to RF\n");
+ return;
+ }
+
+ tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | (val & 0xfffff) << 2 |
+ (reg & 3);
+ rum_write(sc, RT2573_PHY_CSR4, tmp);
+
+ /* remember last written value in sc */
+ sc->rf_regs[reg] = val;
+
+ DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 3, val & 0xfffff);
+}
+
+static void
+rum_select_antenna(struct rum_softc *sc)
+{
+ uint8_t bbp4, bbp77;
+ uint32_t tmp;
+
+ bbp4 = rum_bbp_read(sc, 4);
+ bbp77 = rum_bbp_read(sc, 77);
+
+ /* TBD */
+
+ /* make sure Rx is disabled before switching antenna */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+ rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
+
+ rum_bbp_write(sc, 4, bbp4);
+ rum_bbp_write(sc, 77, bbp77);
+
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+}
+
+/*
+ * Enable multi-rate retries for frames sent at OFDM rates.
+ * In 802.11b/g mode, allow fallback to CCK rates.
+ */
+static void
+rum_enable_mrr(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t tmp;
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR4);
+
+ tmp &= ~RT2573_MRR_CCK_FALLBACK;
+ if (!IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan))
+ tmp |= RT2573_MRR_CCK_FALLBACK;
+ tmp |= RT2573_MRR_ENABLED;
+
+ rum_write(sc, RT2573_TXRX_CSR4, tmp);
+}
+
+static void
+rum_set_txpreamble(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t tmp;
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR4);
+
+ tmp &= ~RT2573_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= RT2573_SHORT_PREAMBLE;
+
+ rum_write(sc, RT2573_TXRX_CSR4, tmp);
+}
+
+static void
+rum_set_basicrates(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ /* update basic rate set */
+ if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ /* 11b basic rates: 1, 2Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0x3);
+ } else if (IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan)) {
+ /* 11a basic rates: 6, 12, 24Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0x150);
+ } else {
+ /* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
+ rum_write(sc, RT2573_TXRX_CSR5, 0xf);
+ }
+}
+
+/*
+ * Reprogram MAC/BBP to switch to a new band. Values taken from the reference
+ * driver.
+ */
+static void
+rum_select_band(struct rum_softc *sc, struct ieee80211_channel *c)
+{
+ uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104;
+ uint32_t tmp;
+
+ /* update all BBP registers that depend on the band */
+ bbp17 = 0x20; bbp96 = 0x48; bbp104 = 0x2c;
+ bbp35 = 0x50; bbp97 = 0x48; bbp98 = 0x48;
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ bbp17 += 0x08; bbp96 += 0x10; bbp104 += 0x0c;
+ bbp35 += 0x10; bbp97 += 0x10; bbp98 += 0x10;
+ }
+ if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+ (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+ bbp17 += 0x10; bbp96 += 0x10; bbp104 += 0x10;
+ }
+
+ sc->bbp17 = bbp17;
+ rum_bbp_write(sc, 17, bbp17);
+ rum_bbp_write(sc, 96, bbp96);
+ rum_bbp_write(sc, 104, bbp104);
+
+ if ((IEEE80211_IS_CHAN_2GHZ(c) && sc->ext_2ghz_lna) ||
+ (IEEE80211_IS_CHAN_5GHZ(c) && sc->ext_5ghz_lna)) {
+ rum_bbp_write(sc, 75, 0x80);
+ rum_bbp_write(sc, 86, 0x80);
+ rum_bbp_write(sc, 88, 0x80);
+ }
+
+ rum_bbp_write(sc, 35, bbp35);
+ rum_bbp_write(sc, 97, bbp97);
+ rum_bbp_write(sc, 98, bbp98);
+
+ tmp = rum_read(sc, RT2573_PHY_CSR0);
+ tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ);
+ if (IEEE80211_IS_CHAN_2GHZ(c))
+ tmp |= RT2573_PA_PE_2GHZ;
+ else
+ tmp |= RT2573_PA_PE_5GHZ;
+ rum_write(sc, RT2573_PHY_CSR0, tmp);
+}
+
+static void
+rum_set_chan(struct rum_softc *sc, struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ const struct rfprog *rfprog;
+ uint8_t bbp3, bbp94 = RT2573_BBPR94_DEFAULT;
+ int8_t power;
+ int i, chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return;
+
+ /* select the appropriate RF settings based on what EEPROM says */
+ rfprog = (sc->rf_rev == RT2573_RF_5225 ||
+ sc->rf_rev == RT2573_RF_2527) ? rum_rf5225 : rum_rf5226;
+
+ /* find the settings for this channel (we know it exists) */
+ for (i = 0; rfprog[i].chan != chan; i++);
+
+ power = sc->txpow[i];
+ if (power < 0) {
+ bbp94 += power;
+ power = 0;
+ } else if (power > 31) {
+ bbp94 += power - 31;
+ power = 31;
+ }
+
+ /*
+ * If we are switching from the 2GHz band to the 5GHz band or
+ * vice-versa, BBP registers need to be reprogrammed.
+ */
+ if (c->ic_flags != ic->ic_curchan->ic_flags) {
+ rum_select_band(sc, c);
+ rum_select_antenna(sc);
+ }
+ ic->ic_curchan = c;
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7 | 1);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_rf_write(sc, RT2573_RF1, rfprog[i].r1);
+ rum_rf_write(sc, RT2573_RF2, rfprog[i].r2);
+ rum_rf_write(sc, RT2573_RF3, rfprog[i].r3 | power << 7);
+ rum_rf_write(sc, RT2573_RF4, rfprog[i].r4 | sc->rffreq << 10);
+
+ rum_pause(sc, hz / 100);
+
+ /* enable smart mode for MIMO-capable RFs */
+ bbp3 = rum_bbp_read(sc, 3);
+
+ bbp3 &= ~RT2573_SMART_MODE;
+ if (sc->rf_rev == RT2573_RF_5225 || sc->rf_rev == RT2573_RF_2527)
+ bbp3 |= RT2573_SMART_MODE;
+
+ rum_bbp_write(sc, 3, bbp3);
+
+ if (bbp94 != RT2573_BBPR94_DEFAULT)
+ rum_bbp_write(sc, 94, bbp94);
+}
+
+/*
+ * Enable TSF synchronization and tell h/w to start sending beacons for IBSS
+ * and HostAP operating modes.
+ */
+static void
+rum_enable_tsf_sync(struct rum_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint32_t tmp;
+
+ if (vap->iv_opmode != IEEE80211_M_STA) {
+ /*
+ * Change default 16ms TBTT adjustment to 8ms.
+ * Must be done before enabling beacon generation.
+ */
+ rum_write(sc, RT2573_TXRX_CSR10, 1 << 12 | 8);
+ }
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR9) & 0xff000000;
+
+ /* set beacon interval (in 1/16ms unit) */
+ tmp |= vap->iv_bss->ni_intval * 16;
+
+ tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT;
+ if (vap->iv_opmode == IEEE80211_M_STA)
+ tmp |= RT2573_TSF_MODE(1);
+ else
+ tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON;
+
+ rum_write(sc, RT2573_TXRX_CSR9, tmp);
+}
+
+static void
+rum_update_slot(struct ifnet *ifp)
+{
+ struct rum_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint8_t slottime;
+ uint32_t tmp;
+
+ slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+
+ tmp = rum_read(sc, RT2573_MAC_CSR9);
+ tmp = (tmp & ~0xff) | slottime;
+ rum_write(sc, RT2573_MAC_CSR9, tmp);
+
+ DPRINTF("setting slot time to %uus\n", slottime);
+}
+
+static void
+rum_set_bssid(struct rum_softc *sc, const uint8_t *bssid)
+{
+ uint32_t tmp;
+
+ tmp = bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24;
+ rum_write(sc, RT2573_MAC_CSR4, tmp);
+
+ tmp = bssid[4] | bssid[5] << 8 | RT2573_ONE_BSSID << 16;
+ rum_write(sc, RT2573_MAC_CSR5, tmp);
+}
+
+static void
+rum_set_macaddr(struct rum_softc *sc, const uint8_t *addr)
+{
+ uint32_t tmp;
+
+ tmp = addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24;
+ rum_write(sc, RT2573_MAC_CSR2, tmp);
+
+ tmp = addr[4] | addr[5] << 8 | 0xff << 16;
+ rum_write(sc, RT2573_MAC_CSR3, tmp);
+}
+
+static void
+rum_promisctask(struct usb2_proc_msg *pm)
+{
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ uint32_t tmp;
+
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+
+ tmp &= ~RT2573_DROP_NOT_TO_ME;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RT2573_DROP_NOT_TO_ME;
+
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+
+ DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving");
+}
+
+static const char *
+rum_get_rf(int rev)
+{
+ switch (rev) {
+ case RT2573_RF_2527: return "RT2527 (MIMO XR)";
+ case RT2573_RF_2528: return "RT2528";
+ case RT2573_RF_5225: return "RT5225 (MIMO XR)";
+ case RT2573_RF_5226: return "RT5226";
+ default: return "unknown";
+ }
+}
+
+static void
+rum_read_eeprom(struct rum_softc *sc)
+{
+ uint16_t val;
+#ifdef RUM_DEBUG
+ int i;
+#endif
+
+ /* read MAC address */
+ rum_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_bssid, 6);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_ANTENNA, &val, 2);
+ val = le16toh(val);
+ sc->rf_rev = (val >> 11) & 0x1f;
+ sc->hw_radio = (val >> 10) & 0x1;
+ sc->rx_ant = (val >> 4) & 0x3;
+ sc->tx_ant = (val >> 2) & 0x3;
+ sc->nb_ant = val & 0x3;
+
+ DPRINTF("RF revision=%d\n", sc->rf_rev);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_CONFIG2, &val, 2);
+ val = le16toh(val);
+ sc->ext_5ghz_lna = (val >> 6) & 0x1;
+ sc->ext_2ghz_lna = (val >> 4) & 0x1;
+
+ DPRINTF("External 2GHz LNA=%d\nExternal 5GHz LNA=%d\n",
+ sc->ext_2ghz_lna, sc->ext_5ghz_lna);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */
+
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_2ghz_corr < -10 || sc->rssi_2ghz_corr > 10)
+ sc->rssi_2ghz_corr = 0;
+
+ rum_eeprom_read(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */
+
+ /* Only [-10, 10] is valid */
+ if (sc->rssi_5ghz_corr < -10 || sc->rssi_5ghz_corr > 10)
+ sc->rssi_5ghz_corr = 0;
+
+ if (sc->ext_2ghz_lna)
+ sc->rssi_2ghz_corr -= 14;
+ if (sc->ext_5ghz_lna)
+ sc->rssi_5ghz_corr -= 14;
+
+ DPRINTF("RSSI 2GHz corr=%d\nRSSI 5GHz corr=%d\n",
+ sc->rssi_2ghz_corr, sc->rssi_5ghz_corr);
+
+ rum_eeprom_read(sc, RT2573_EEPROM_FREQ_OFFSET, &val, 2);
+ val = le16toh(val);
+ if ((val & 0xff) != 0xff)
+ sc->rffreq = val & 0xff;
+
+ DPRINTF("RF freq=%d\n", sc->rffreq);
+
+ /* read Tx power for all a/b/g channels */
+ rum_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->txpow, 14);
+ /* XXX default Tx power for 802.11a channels */
+ memset(sc->txpow + 14, 24, sizeof (sc->txpow) - 14);
+#ifdef RUM_DEBUG
+ for (i = 0; i < 14; i++)
+ DPRINTF("Channel=%d Tx power=%d\n", i + 1, sc->txpow[i]);
+#endif
+
+ /* read default values for BBP registers */
+ rum_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);
+#ifdef RUM_DEBUG
+ for (i = 0; i < 14; i++) {
+ if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff)
+ continue;
+ DPRINTF("BBP R%d=%02x\n", sc->bbp_prom[i].reg,
+ sc->bbp_prom[i].val);
+ }
+#endif
+}
+
+static int
+rum_bbp_init(struct rum_softc *sc)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ int i, ntries;
+
+ /* wait for BBP to be ready */
+ for (ntries = 0; ntries < 100; ntries++) {
+ const uint8_t val = rum_bbp_read(sc, 0);
+ if (val != 0 && val != 0xff)
+ break;
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+ return EIO;
+ }
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < N(rum_def_bbp); i++)
+ rum_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val);
+
+ /* write vendor-specific BBP values (from EEPROM) */
+ for (i = 0; i < 16; i++) {
+ if (sc->bbp_prom[i].reg == 0 || sc->bbp_prom[i].reg == 0xff)
+ continue;
+ rum_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+ }
+
+ return 0;
+#undef N
+}
+
+static void
+rum_init_task(struct usb2_proc_msg *pm)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t tmp;
+ usb2_error_t error;
+ int i, ntries;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+
+ rum_stop_task(pm);
+
+ /* initialize MAC registers to default values */
+ for (i = 0; i < N(rum_def_mac); i++)
+ rum_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val);
+
+ /* set host ready */
+ rum_write(sc, RT2573_MAC_CSR1, 3);
+ rum_write(sc, RT2573_MAC_CSR1, 0);
+
+ /* wait for BBP/RF to wakeup */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (rum_read(sc, RT2573_MAC_CSR12) & 8)
+ break;
+ rum_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */
+ if (rum_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
+ goto fail;
+ }
+
+ if ((error = rum_bbp_init(sc)) != 0)
+ goto fail;
+
+ /* select default channel */
+ rum_select_band(sc, ic->ic_curchan);
+ rum_select_antenna(sc);
+ rum_set_chan(sc, ic->ic_curchan);
+
+ /* clear STA registers */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ rum_set_macaddr(sc, ic->ic_myaddr);
+
+ /* initialize ASIC */
+ rum_write(sc, RT2573_MAC_CSR1, 4);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ rum_setup_tx_list(sc);
+
+ /* update Rx filter */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0) & 0xffff;
+
+ tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR |
+ RT2573_DROP_ACKCTS;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ tmp |= RT2573_DROP_TODS;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RT2573_DROP_NOT_TO_ME;
+ }
+ rum_write(sc, RT2573_TXRX_CSR0, tmp);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ usb2_transfer_set_stall(sc->sc_xfer[RUM_BULK_WR]);
+ usb2_transfer_start(sc->sc_xfer[RUM_BULK_RD]);
+ return;
+
+fail: rum_stop_task(pm);
+#undef N
+}
+
+static void
+rum_init(void *priv)
+{
+ struct rum_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RUM_LOCK(sc);
+ rum_queue_command(sc, rum_init_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ RUM_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+rum_stop_task(struct usb2_proc_msg *pm)
+{
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ uint32_t tmp;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ RUM_UNLOCK(sc);
+
+ /*
+ * Drain the USB transfers, if not already drained:
+ */
+ usb2_transfer_drain(sc->sc_xfer[RUM_BULK_WR]);
+ usb2_transfer_drain(sc->sc_xfer[RUM_BULK_RD]);
+
+ RUM_LOCK(sc);
+
+ rum_unsetup_tx_list(sc);
+
+ /* disable Rx */
+ tmp = rum_read(sc, RT2573_TXRX_CSR0);
+ rum_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
+
+ /* reset ASIC */
+ rum_write(sc, RT2573_MAC_CSR1, 3);
+ rum_write(sc, RT2573_MAC_CSR1, 0);
+}
+
+static int
+rum_load_microcode(struct rum_softc *sc, const u_char *ucode, size_t size)
+{
+ struct usb2_device_request req;
+ uint16_t reg = RT2573_MCU_CODE_BASE;
+ usb2_error_t error;
+
+ /* copy firmware image into NIC */
+ for (; size >= 4; reg += 4, ucode += 4, size -= 4)
+ rum_write(sc, reg, UGETDW(ucode));
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RT2573_MCU_CNTL;
+ USETW(req.wValue, RT2573_MCU_RUN);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, 0);
+
+ error = rum_do_request(sc, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not run firmware: %s\n",
+ usb2_errstr(error));
+ }
+ return error;
+}
+
+static int
+rum_prepare_beacon(struct rum_softc *sc, struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+ const struct ieee80211_txparam *tp;
+ struct rum_tx_desc desc;
+ struct mbuf *m0;
+
+ m0 = ieee80211_beacon_alloc(vap->iv_bss, &RUM_VAP(vap)->bo);
+ if (m0 == NULL) {
+ return ENOBUFS;
+ }
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ rum_setup_tx_desc(sc, &desc, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ,
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ /* copy the first 24 bytes of Tx descriptor into NIC memory */
+ rum_write_multi(sc, RT2573_HW_BEACON_BASE0, (uint8_t *)&desc, 24);
+
+ /* copy beacon header and payload into NIC memory */
+ rum_write_multi(sc, RT2573_HW_BEACON_BASE0 + 24, mtod(m0, uint8_t *),
+ m0->m_pkthdr.len);
+
+ m_freem(m0);
+
+ return 0;
+}
+
+static int
+rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ifnet *ifp = ni->ni_ic->ic_ifp;
+ struct rum_softc *sc = ifp->if_softc;
+
+ RUM_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ RUM_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->tx_nfree == 0) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ RUM_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return EIO;
+ }
+
+ ifp->if_opackets++;
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if (rum_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if (rum_tx_raw(sc, m, ni, params) != 0)
+ goto bad;
+ }
+ RUM_UNLOCK(sc);
+
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ RUM_UNLOCK(sc);
+ ieee80211_free_node(ni);
+ return EIO;
+}
+
+static void
+rum_amrr_start(struct rum_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct rum_vap *rvp = RUM_VAP(vap);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR5) */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ieee80211_amrr_node_init(&rvp->amrr, &RUM_NODE(ni)->amn, ni);
+
+ usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp);
+}
+
+static void
+rum_amrr_timeout(void *arg)
+{
+ struct rum_vap *rvp = arg;
+ struct rum_softc *sc = rvp->sc;
+
+ rum_queue_command(sc, rum_amrr_task,
+ &rvp->amrr_task[0].hdr, &rvp->amrr_task[1].hdr);
+}
+
+static void
+rum_amrr_task(struct usb2_proc_msg *pm)
+{
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct rum_vap *rvp = RUM_VAP(vap);
+ struct ieee80211_node *ni = vap->iv_bss;
+ int ok, fail;
+
+ /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */
+ rum_read_multi(sc, RT2573_STA_CSR0, sc->sta, sizeof(sc->sta));
+
+ ok = (le32toh(sc->sta[4]) >> 16) + /* TX ok w/o retry */
+ (le32toh(sc->sta[5]) & 0xffff); /* TX ok w/ retry */
+ fail = (le32toh(sc->sta[5]) >> 16); /* TX retry-fail count */
+
+ ieee80211_amrr_tx_update(&RUM_NODE(ni)->amn,
+ ok+fail, ok, (le32toh(sc->sta[5]) & 0xffff) + fail);
+ (void) ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn);
+
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
+
+ usb2_callout_reset(&rvp->amrr_ch, hz, rum_amrr_timeout, rvp);
+}
+
+/* ARGUSED */
+static struct ieee80211_node *
+rum_node_alloc(struct ieee80211vap *vap __unused,
+ const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
+{
+ struct rum_node *rn;
+
+ rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return rn != NULL ? &rn->ni : NULL;
+}
+
+static void
+rum_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni);
+}
+
+static void
+rum_scan_start(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ RUM_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SCAN_START;
+ rum_queue_command(sc, rum_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ RUM_UNLOCK(sc);
+
+}
+
+static void
+rum_scan_end(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ RUM_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SCAN_END;
+ rum_queue_command(sc, rum_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ RUM_UNLOCK(sc);
+
+}
+
+static void
+rum_set_channel(struct ieee80211com *ic)
+{
+ struct rum_softc *sc = ic->ic_ifp->if_softc;
+
+ RUM_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = RUM_SET_CHANNEL;
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+ rum_queue_command(sc, rum_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ RUM_UNLOCK(sc);
+}
+
+static void
+rum_scantask(struct usb2_proc_msg *pm)
+{
+ struct rum_task *task = (struct rum_task *)pm;
+ struct rum_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t tmp;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+
+ switch (sc->sc_scan_action) {
+ case RUM_SCAN_START:
+ /* abort TSF synchronization */
+ tmp = rum_read(sc, RT2573_TXRX_CSR9);
+ rum_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
+ rum_set_bssid(sc, ifp->if_broadcastaddr);
+ break;
+
+ case RUM_SET_CHANNEL:
+ rum_set_chan(sc, ic->ic_curchan);
+ break;
+
+ default: /* RUM_SCAN_END */
+ rum_enable_tsf_sync(sc);
+ rum_set_bssid(sc, sc->sc_bssid);
+ break;
+ }
+}
+
+static int
+rum_get_rssi(struct rum_softc *sc, uint8_t raw)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ int lna, agc, rssi;
+
+ lna = (raw >> 5) & 0x3;
+ agc = raw & 0x1f;
+
+ if (lna == 0) {
+ /*
+ * No RSSI mapping
+ *
+ * NB: Since RSSI is relative to noise floor, -1 is
+ * adequate for caller to know error happened.
+ */
+ return -1;
+ }
+
+ rssi = (2 * agc) - RT2573_NOISE_FLOOR;
+
+ if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
+ rssi += sc->rssi_2ghz_corr;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 74;
+ else if (lna == 3)
+ rssi -= 90;
+ } else {
+ rssi += sc->rssi_5ghz_corr;
+
+ if (!sc->ext_5ghz_lna && lna != 1)
+ rssi += 4;
+
+ if (lna == 1)
+ rssi -= 64;
+ else if (lna == 2)
+ rssi -= 86;
+ else if (lna == 3)
+ rssi -= 100;
+ }
+ return rssi;
+}
+
+static int
+rum_pause(struct rum_softc *sc, int timeout)
+{
+ if (usb2_proc_is_gone(&sc->sc_tq))
+ return (1);
+
+ usb2_pause_mtx(&sc->sc_mtx, timeout);
+ return (0);
+}
+
+static void
+rum_queue_command(struct rum_softc *sc, usb2_proc_callback_t *fn,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct rum_task *task;
+
+ RUM_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (usb2_proc_is_gone(&sc->sc_tq)) {
+ DPRINTF("proc is gone\n");
+ return; /* nothing to do */
+ }
+ /*
+ * NOTE: The task cannot get executed before we drop the
+ * "sc_mtx" mutex. It is safe to update fields in the message
+ * structure after that the message got queued.
+ */
+ task = (struct rum_task *)
+ usb2_proc_msignal(&sc->sc_tq, t0, t1);
+
+ /* Setup callback and softc pointers */
+ task->hdr.pm_callback = fn;
+ task->sc = sc;
+
+ /*
+ * Init and stop must be synchronous!
+ */
+ if ((fn == rum_init_task) || (fn == rum_stop_task))
+ usb2_proc_mwait(&sc->sc_tq, t0, t1);
+}
+
+static device_method_t rum_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, rum_match),
+ DEVMETHOD(device_attach, rum_attach),
+ DEVMETHOD(device_detach, rum_detach),
+
+ { 0, 0 }
+};
+
+static driver_t rum_driver = {
+ .name = "rum",
+ .methods = rum_methods,
+ .size = sizeof(struct rum_softc),
+};
+
+static devclass_t rum_devclass;
+
+DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0);
diff --git a/sys/dev/usb/wlan/if_rumfw.h b/sys/dev/usb/wlan/if_rumfw.h
new file mode 100644
index 0000000..0f08674
--- /dev/null
+++ b/sys/dev/usb/wlan/if_rumfw.h
@@ -0,0 +1,213 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005-2006, Ralink Technology, Corp.
+ * Paul Lin <paul_lin@ralinktech.com.tw>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This file contains the loadable 8051 microcode for the Ralink RT2573
+ * chipset.
+ */
+
+static const uint8_t rt2573_ucode[] = {
+ 0x02, 0x13, 0x25, 0x12, 0x10, 0xd9, 0x02, 0x12, 0x58, 0x02, 0x13,
+ 0x58, 0x02, 0x13, 0x5a, 0xc0, 0xd0, 0x75, 0xd0, 0x18, 0x12, 0x13,
+ 0x5c, 0xd0, 0xd0, 0x22, 0x02, 0x14, 0x5c, 0x02, 0x14, 0xe7, 0xed,
+ 0x4c, 0x70, 0x44, 0x90, 0x01, 0xa8, 0x74, 0x80, 0xf0, 0xef, 0x30,
+ 0xe5, 0x07, 0xe4, 0x90, 0x00, 0x0f, 0xf0, 0x80, 0x2c, 0xe5, 0x40,
+ 0x24, 0xc0, 0x60, 0x13, 0x24, 0xc0, 0x60, 0x16, 0x24, 0xc0, 0x60,
+ 0x19, 0x24, 0xc0, 0x70, 0x1a, 0xe4, 0x90, 0x00, 0x0b, 0xf0, 0x80,
+ 0x13, 0xe4, 0x90, 0x00, 0x13, 0xf0, 0x80, 0x0c, 0xe4, 0x90, 0x00,
+ 0x1b, 0xf0, 0x80, 0x05, 0xe4, 0x90, 0x00, 0x23, 0xf0, 0xe4, 0x90,
+ 0x01, 0xa8, 0xf0, 0xd3, 0x22, 0x90, 0x02, 0x02, 0xed, 0xf0, 0x90,
+ 0x02, 0x01, 0xef, 0xf0, 0xd3, 0x22, 0xef, 0x24, 0xc0, 0x60, 0x1f,
+ 0x24, 0xc0, 0x60, 0x2e, 0x24, 0xc0, 0x60, 0x3d, 0x24, 0xc0, 0x70,
+ 0x53, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90,
+ 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00, 0x08, 0x80, 0x37, 0x90, 0x00,
+ 0x13, 0xe0, 0x30, 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x11, 0xe0,
+ 0xfe, 0x90, 0x00, 0x10, 0x80, 0x24, 0x90, 0x00, 0x1b, 0xe0, 0x30,
+ 0xe1, 0x02, 0xc3, 0x22, 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00,
+ 0x18, 0x80, 0x11, 0x90, 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x02, 0xc3,
+ 0x22, 0x90, 0x00, 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd,
+ 0xee, 0xf5, 0x37, 0xed, 0xf5, 0x38, 0xd3, 0x22, 0x30, 0x09, 0x20,
+ 0x20, 0x04, 0x0b, 0x90, 0x02, 0x08, 0xe0, 0x54, 0x0f, 0x70, 0x03,
+ 0x02, 0x12, 0x57, 0xc2, 0x09, 0x90, 0x02, 0x00, 0xe0, 0x44, 0x04,
+ 0xf0, 0x74, 0x04, 0x12, 0x0c, 0x3a, 0xc2, 0x04, 0xc2, 0x07, 0x90,
+ 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03,
+ 0x26, 0xe0, 0x20, 0xe2, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x08,
+ 0xe0, 0x70, 0x1b, 0x20, 0x07, 0x03, 0x02, 0x12, 0x57, 0x90, 0x03,
+ 0x12, 0xe0, 0x64, 0x22, 0x60, 0x03, 0x02, 0x12, 0x57, 0xd2, 0x09,
+ 0xc2, 0x07, 0x74, 0x02, 0x12, 0x0c, 0x3a, 0x22, 0x90, 0x02, 0x03,
+ 0xe0, 0x30, 0xe4, 0x47, 0x20, 0x06, 0x44, 0xe5, 0x3c, 0x60, 0x34,
+ 0xe5, 0x40, 0x24, 0xc0, 0x60, 0x14, 0x24, 0xc0, 0x60, 0x18, 0x24,
+ 0xc0, 0x60, 0x1c, 0x24, 0xc0, 0x70, 0x22, 0x90, 0x00, 0x0b, 0xe0,
+ 0x30, 0xe1, 0x1b, 0x22, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x13,
+ 0x22, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x0b, 0x22, 0x90, 0x00,
+ 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x02, 0x03,
+ 0x74, 0x01, 0xf0, 0x00, 0xe0, 0x54, 0xc0, 0xf5, 0x40, 0xe5, 0x40,
+ 0x24, 0xc0, 0x60, 0x20, 0x24, 0xc0, 0x60, 0x30, 0x24, 0xc0, 0x60,
+ 0x40, 0x24, 0xc0, 0x70, 0x56, 0x90, 0x00, 0x0b, 0xe0, 0x30, 0xe1,
+ 0x03, 0x02, 0x12, 0x57, 0x90, 0x00, 0x09, 0xe0, 0xfe, 0x90, 0x00,
+ 0x08, 0x80, 0x3a, 0x90, 0x00, 0x13, 0xe0, 0x30, 0xe1, 0x03, 0x02,
+ 0x12, 0x57, 0x90, 0x00, 0x11, 0xe0, 0xfe, 0x90, 0x00, 0x10, 0x80,
+ 0x26, 0x90, 0x00, 0x1b, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57,
+ 0x90, 0x00, 0x19, 0xe0, 0xfe, 0x90, 0x00, 0x18, 0x80, 0x12, 0x90,
+ 0x00, 0x23, 0xe0, 0x30, 0xe1, 0x03, 0x02, 0x12, 0x57, 0x90, 0x00,
+ 0x21, 0xe0, 0xfe, 0x90, 0x00, 0x20, 0xe0, 0xfd, 0xee, 0xf5, 0x37,
+ 0xed, 0xf5, 0x38, 0x90, 0x03, 0x27, 0x74, 0x82, 0xf0, 0x90, 0x02,
+ 0x01, 0xe5, 0x40, 0xf0, 0x90, 0x02, 0x06, 0xe0, 0xf5, 0x3c, 0xc3,
+ 0xe5, 0x38, 0x95, 0x3a, 0xe5, 0x37, 0x95, 0x39, 0x50, 0x21, 0xe5,
+ 0x40, 0x44, 0x05, 0xff, 0xe5, 0x37, 0xa2, 0xe7, 0x13, 0xfc, 0xe5,
+ 0x38, 0x13, 0xfd, 0x12, 0x10, 0x20, 0xe5, 0x3c, 0x30, 0xe2, 0x04,
+ 0xd2, 0x06, 0x80, 0x02, 0xc2, 0x06, 0x53, 0x3c, 0x01, 0x22, 0x30,
+ 0x0b, 0x07, 0xe4, 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02,
+ 0x02, 0x74, 0x20, 0xf0, 0xe5, 0x40, 0x44, 0x01, 0x90, 0x02, 0x01,
+ 0xf0, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80, 0xf6,
+ 0x90, 0x03, 0x27, 0x74, 0x02, 0xf0, 0xaf, 0x40, 0x12, 0x10, 0x74,
+ 0x40, 0xa5, 0x00, 0x80, 0xf6, 0x22, 0x90, 0x7f, 0xf8, 0xe0, 0xb4,
+ 0x02, 0x03, 0x12, 0x16, 0x38, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0,
+ 0x03, 0x00, 0x80, 0xf6, 0x90, 0x03, 0x26, 0xe0, 0x20, 0xe1, 0x07,
+ 0xe5, 0x3b, 0x70, 0x03, 0x02, 0x13, 0x24, 0xe5, 0x3b, 0x70, 0x15,
+ 0x90, 0x03, 0x24, 0xe0, 0x75, 0xf0, 0x40, 0xa4, 0xf5, 0x36, 0x85,
+ 0xf0, 0x35, 0x75, 0x24, 0x83, 0x75, 0x3b, 0x01, 0x80, 0x03, 0x75,
+ 0x24, 0x03, 0xd3, 0xe5, 0x36, 0x95, 0x3a, 0xe5, 0x35, 0x95, 0x39,
+ 0x40, 0x36, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80,
+ 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0,
+ 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x30, 0x0b, 0x07, 0xe4,
+ 0x90, 0x02, 0x02, 0xf0, 0x80, 0x06, 0x90, 0x02, 0x02, 0x74, 0x20,
+ 0xf0, 0x90, 0x02, 0x01, 0x74, 0x21, 0xf0, 0x75, 0x24, 0x03, 0x80,
+ 0x3d, 0xe5, 0x35, 0xa2, 0xe7, 0x13, 0xfe, 0xe5, 0x36, 0x13, 0xfd,
+ 0xac, 0x06, 0x90, 0x02, 0x01, 0xe0, 0x30, 0xe0, 0x03, 0x00, 0x80,
+ 0xf6, 0x90, 0x03, 0x27, 0xe5, 0x24, 0xf0, 0x90, 0x00, 0x0f, 0xe0,
+ 0x30, 0xe1, 0x04, 0x30, 0x0e, 0xf6, 0x22, 0x7f, 0x25, 0x12, 0x10,
+ 0x20, 0xe5, 0x36, 0xb5, 0x3a, 0x08, 0xe5, 0x35, 0xb5, 0x39, 0x03,
+ 0x00, 0x80, 0x04, 0xe4, 0xf5, 0x3b, 0x22, 0xc3, 0xe5, 0x36, 0x95,
+ 0x3a, 0xf5, 0x36, 0xe5, 0x35, 0x95, 0x39, 0xf5, 0x35, 0x02, 0x12,
+ 0x96, 0x22, 0x75, 0xa8, 0x0f, 0x90, 0x03, 0x06, 0x74, 0x01, 0xf0,
+ 0x90, 0x03, 0x07, 0xf0, 0x90, 0x03, 0x08, 0x04, 0xf0, 0x90, 0x03,
+ 0x09, 0x74, 0x6c, 0xf0, 0x90, 0x03, 0x0a, 0x74, 0xff, 0xf0, 0x90,
+ 0x03, 0x02, 0x74, 0x1f, 0xf0, 0x90, 0x03, 0x00, 0x74, 0x04, 0xf0,
+ 0x90, 0x03, 0x25, 0x74, 0x31, 0xf0, 0xd2, 0xaf, 0x22, 0x00, 0x22,
+ 0x00, 0x22, 0x90, 0x03, 0x05, 0xe0, 0x30, 0xe0, 0x0b, 0xe0, 0x44,
+ 0x01, 0xf0, 0x30, 0x09, 0x02, 0xd2, 0x04, 0xc2, 0x07, 0x22, 0x8d,
+ 0x24, 0xa9, 0x07, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5,
+ 0x26, 0xa3, 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff,
+ 0xa3, 0xe0, 0xfd, 0xe9, 0x30, 0xe5, 0x14, 0x54, 0xc0, 0x60, 0x05,
+ 0x43, 0x05, 0x03, 0x80, 0x03, 0x53, 0x05, 0xfc, 0xef, 0x54, 0x3f,
+ 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x07, 0x3f, 0x53, 0x05, 0xf0,
+ 0xe5, 0x24, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53,
+ 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28,
+ 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x8f, 0x24, 0xa9,
+ 0x05, 0x90, 0x7f, 0xfc, 0xe0, 0x75, 0x25, 0x00, 0xf5, 0x26, 0xa3,
+ 0xe0, 0x75, 0x27, 0x00, 0xf5, 0x28, 0xa3, 0xe0, 0xff, 0xa3, 0xe0,
+ 0xfd, 0xe5, 0x24, 0x30, 0xe5, 0x0b, 0x43, 0x05, 0x0f, 0xef, 0x54,
+ 0x3f, 0x44, 0x40, 0xff, 0x80, 0x06, 0x53, 0x05, 0xf0, 0x53, 0x07,
+ 0x3f, 0xe9, 0x30, 0xe0, 0x05, 0x43, 0x05, 0x10, 0x80, 0x03, 0x53,
+ 0x05, 0xef, 0x90, 0x7f, 0xfc, 0xe5, 0x26, 0xf0, 0xa3, 0xe5, 0x28,
+ 0xf0, 0xa3, 0xef, 0xf0, 0xa3, 0xed, 0xf0, 0x22, 0x90, 0x7f, 0xfc,
+ 0xe0, 0xf9, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfb,
+ 0xef, 0x30, 0xe5, 0x0b, 0x43, 0x03, 0x0f, 0xec, 0x54, 0x3f, 0x44,
+ 0x40, 0xfc, 0x80, 0x06, 0x53, 0x03, 0xf0, 0x53, 0x04, 0x3f, 0xed,
+ 0x30, 0xe0, 0x07, 0xef, 0x54, 0xc0, 0x60, 0x07, 0x80, 0x0a, 0xef,
+ 0x54, 0xc0, 0x60, 0x05, 0x43, 0x03, 0x10, 0x80, 0x03, 0x53, 0x03,
+ 0xef, 0x90, 0x7f, 0xfc, 0xe9, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xec,
+ 0xf0, 0xa3, 0xeb, 0xf0, 0x22, 0xe5, 0x4b, 0xfd, 0x54, 0x1f, 0x90,
+ 0x7f, 0xf8, 0xf0, 0xe5, 0x4a, 0xf5, 0x09, 0x90, 0x30, 0x38, 0xe0,
+ 0x90, 0x7f, 0xfc, 0xf0, 0x90, 0x30, 0x39, 0xe0, 0x90, 0x7f, 0xfd,
+ 0xf0, 0x90, 0x30, 0x3a, 0xe0, 0x90, 0x7f, 0xfe, 0xf0, 0x90, 0x30,
+ 0x3b, 0xe0, 0x90, 0x7f, 0xff, 0xf0, 0xed, 0x30, 0xe5, 0x0c, 0x54,
+ 0xc0, 0x60, 0x0d, 0x90, 0x7f, 0xf0, 0xe5, 0x47, 0xf0, 0x80, 0x05,
+ 0xe4, 0x90, 0x7f, 0xf0, 0xf0, 0x90, 0x7f, 0xf8, 0xe0, 0x14, 0x60,
+ 0x08, 0x24, 0xfe, 0x60, 0x0d, 0x24, 0x03, 0x80, 0x12, 0xaf, 0x05,
+ 0xad, 0x09, 0x12, 0x13, 0xc5, 0x80, 0x10, 0xaf, 0x05, 0xad, 0x09,
+ 0x12, 0x14, 0x12, 0x80, 0x07, 0xaf, 0x05, 0xad, 0x09, 0x12, 0x13,
+ 0x6f, 0x90, 0x7f, 0xfc, 0xe0, 0x90, 0x30, 0x38, 0xf0, 0x90, 0x7f,
+ 0xfd, 0xe0, 0x90, 0x30, 0x39, 0xf0, 0x90, 0x7f, 0xfe, 0xe0, 0x90,
+ 0x30, 0x3a, 0xf0, 0x90, 0x7f, 0xff, 0xe0, 0x90, 0x30, 0x3b, 0xf0,
+ 0x22, 0xe5, 0x4b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x15, 0x71, 0xf5,
+ 0x4b, 0xe5, 0x44, 0x45, 0x43, 0x70, 0x03, 0x02, 0x15, 0xa0, 0x12,
+ 0x0c, 0x14, 0x12, 0x0b, 0x86, 0x50, 0xfb, 0x90, 0x00, 0x00, 0xe0,
+ 0xf5, 0x25, 0x12, 0x15, 0xb4, 0xc2, 0x92, 0xe4, 0xf5, 0x24, 0xe5,
+ 0x24, 0xc3, 0x95, 0x25, 0x50, 0x49, 0x7e, 0x00, 0x7f, 0x4c, 0x74,
+ 0x40, 0x25, 0x24, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xad, 0x82, 0xfc,
+ 0x75, 0x2b, 0x02, 0x7b, 0x10, 0x12, 0x07, 0x1e, 0xc2, 0x93, 0x12,
+ 0x15, 0xa1, 0x7d, 0xa0, 0x12, 0x15, 0xd0, 0xe5, 0x24, 0x54, 0x0f,
+ 0x24, 0x4c, 0xf8, 0xe6, 0xfd, 0xaf, 0x4b, 0xae, 0x4a, 0x12, 0x15,
+ 0xd8, 0x05, 0x4b, 0xe5, 0x4b, 0x70, 0x02, 0x05, 0x4a, 0x12, 0x0a,
+ 0x5f, 0x05, 0x24, 0xe5, 0x24, 0x54, 0x0f, 0x70, 0xd5, 0xd2, 0x93,
+ 0x80, 0xb0, 0xc3, 0xe5, 0x44, 0x95, 0x25, 0xf5, 0x44, 0xe5, 0x43,
+ 0x94, 0x00, 0xf5, 0x43, 0x02, 0x14, 0xf2, 0x12, 0x15, 0xb4, 0xc2,
+ 0x93, 0xc2, 0x92, 0x12, 0x15, 0xa1, 0x7d, 0x80, 0x12, 0x15, 0xd0,
+ 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55,
+ 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x7d, 0x30, 0xaf, 0x4b,
+ 0xae, 0x4a, 0x12, 0x15, 0xd8, 0x12, 0x0a, 0x5f, 0xd2, 0x93, 0x22,
+ 0x7d, 0xaa, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x7d, 0x55,
+ 0x7f, 0xaa, 0x7e, 0x2a, 0x12, 0x15, 0xd8, 0x22, 0xad, 0x47, 0x7f,
+ 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0x7d, 0xff, 0x7f, 0x35, 0x7e,
+ 0x30, 0x12, 0x15, 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x7e, 0x30, 0x12,
+ 0x15, 0xd8, 0x22, 0x74, 0x55, 0xff, 0xfe, 0x12, 0x15, 0xd8, 0x22,
+ 0x8f, 0x82, 0x8e, 0x83, 0xed, 0xf0, 0x22, 0xe4, 0xfc, 0x90, 0x7f,
+ 0xf0, 0xe0, 0xaf, 0x09, 0x14, 0x60, 0x14, 0x14, 0x60, 0x15, 0x14,
+ 0x60, 0x16, 0x14, 0x60, 0x17, 0x14, 0x60, 0x18, 0x24, 0x05, 0x70,
+ 0x16, 0xe4, 0xfc, 0x80, 0x12, 0x7c, 0x01, 0x80, 0x0e, 0x7c, 0x03,
+ 0x80, 0x0a, 0x7c, 0x07, 0x80, 0x06, 0x7c, 0x0f, 0x80, 0x02, 0x7c,
+ 0x1f, 0xec, 0x6f, 0xf4, 0x54, 0x1f, 0xfc, 0x90, 0x30, 0x34, 0xe0,
+ 0x54, 0xe0, 0x4c, 0xfd, 0xa3, 0xe0, 0xfc, 0x43, 0x04, 0x1f, 0x7f,
+ 0x34, 0x7e, 0x30, 0x12, 0x15, 0xd8, 0xad, 0x04, 0x0f, 0x12, 0x15,
+ 0xd8, 0xe4, 0xfd, 0x7f, 0x37, 0x02, 0x15, 0xd8, 0x02, 0x15, 0xdf,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07,
+ 0x29, 0xe9
+};
diff --git a/sys/dev/usb/wlan/if_rumreg.h b/sys/dev/usb/wlan/if_rumreg.h
new file mode 100644
index 0000000..75a51bc
--- /dev/null
+++ b/sys/dev/usb/wlan/if_rumreg.h
@@ -0,0 +1,235 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RT2573_NOISE_FLOOR -95
+
+#define RT2573_TX_DESC_SIZE (sizeof (struct rum_tx_desc))
+#define RT2573_RX_DESC_SIZE (sizeof (struct rum_rx_desc))
+
+#define RT2573_CONFIG_NO 1
+#define RT2573_IFACE_INDEX 0
+
+#define RT2573_MCU_CNTL 0x01
+#define RT2573_WRITE_MAC 0x02
+#define RT2573_READ_MAC 0x03
+#define RT2573_WRITE_MULTI_MAC 0x06
+#define RT2573_READ_MULTI_MAC 0x07
+#define RT2573_READ_EEPROM 0x09
+#define RT2573_WRITE_LED 0x0a
+
+/*
+ * Control and status registers.
+ */
+#define RT2573_AIFSN_CSR 0x0400
+#define RT2573_CWMIN_CSR 0x0404
+#define RT2573_CWMAX_CSR 0x0408
+#define RT2573_MCU_CODE_BASE 0x0800
+#define RT2573_HW_BEACON_BASE0 0x2400
+#define RT2573_MAC_CSR0 0x3000
+#define RT2573_MAC_CSR1 0x3004
+#define RT2573_MAC_CSR2 0x3008
+#define RT2573_MAC_CSR3 0x300c
+#define RT2573_MAC_CSR4 0x3010
+#define RT2573_MAC_CSR5 0x3014
+#define RT2573_MAC_CSR6 0x3018
+#define RT2573_MAC_CSR7 0x301c
+#define RT2573_MAC_CSR8 0x3020
+#define RT2573_MAC_CSR9 0x3024
+#define RT2573_MAC_CSR10 0x3028
+#define RT2573_MAC_CSR11 0x302c
+#define RT2573_MAC_CSR12 0x3030
+#define RT2573_MAC_CSR13 0x3034
+#define RT2573_MAC_CSR14 0x3038
+#define RT2573_MAC_CSR15 0x303c
+#define RT2573_TXRX_CSR0 0x3040
+#define RT2573_TXRX_CSR1 0x3044
+#define RT2573_TXRX_CSR2 0x3048
+#define RT2573_TXRX_CSR3 0x304c
+#define RT2573_TXRX_CSR4 0x3050
+#define RT2573_TXRX_CSR5 0x3054
+#define RT2573_TXRX_CSR6 0x3058
+#define RT2573_TXRX_CSR7 0x305c
+#define RT2573_TXRX_CSR8 0x3060
+#define RT2573_TXRX_CSR9 0x3064
+#define RT2573_TXRX_CSR10 0x3068
+#define RT2573_TXRX_CSR11 0x306c
+#define RT2573_TXRX_CSR12 0x3070
+#define RT2573_TXRX_CSR13 0x3074
+#define RT2573_TXRX_CSR14 0x3078
+#define RT2573_TXRX_CSR15 0x307c
+#define RT2573_PHY_CSR0 0x3080
+#define RT2573_PHY_CSR1 0x3084
+#define RT2573_PHY_CSR2 0x3088
+#define RT2573_PHY_CSR3 0x308c
+#define RT2573_PHY_CSR4 0x3090
+#define RT2573_PHY_CSR5 0x3094
+#define RT2573_PHY_CSR6 0x3098
+#define RT2573_PHY_CSR7 0x309c
+#define RT2573_SEC_CSR0 0x30a0
+#define RT2573_SEC_CSR1 0x30a4
+#define RT2573_SEC_CSR2 0x30a8
+#define RT2573_SEC_CSR3 0x30ac
+#define RT2573_SEC_CSR4 0x30b0
+#define RT2573_SEC_CSR5 0x30b4
+#define RT2573_STA_CSR0 0x30c0
+#define RT2573_STA_CSR1 0x30c4
+#define RT2573_STA_CSR2 0x30c8
+#define RT2573_STA_CSR3 0x30cc
+#define RT2573_STA_CSR4 0x30d0
+#define RT2573_STA_CSR5 0x30d4
+
+
+/* possible flags for register RT2573_MAC_CSR1 */
+#define RT2573_RESET_ASIC (1 << 0)
+#define RT2573_RESET_BBP (1 << 1)
+#define RT2573_HOST_READY (1 << 2)
+
+/* possible flags for register MAC_CSR5 */
+#define RT2573_ONE_BSSID 3
+
+/* possible flags for register TXRX_CSR0 */
+/* Tx filter flags are in the low 16 bits */
+#define RT2573_AUTO_TX_SEQ (1 << 15)
+/* Rx filter flags are in the high 16 bits */
+#define RT2573_DISABLE_RX (1 << 16)
+#define RT2573_DROP_CRC_ERROR (1 << 17)
+#define RT2573_DROP_PHY_ERROR (1 << 18)
+#define RT2573_DROP_CTL (1 << 19)
+#define RT2573_DROP_NOT_TO_ME (1 << 20)
+#define RT2573_DROP_TODS (1 << 21)
+#define RT2573_DROP_VER_ERROR (1 << 22)
+#define RT2573_DROP_MULTICAST (1 << 23)
+#define RT2573_DROP_BROADCAST (1 << 24)
+#define RT2573_DROP_ACKCTS (1 << 25)
+
+/* possible flags for register TXRX_CSR4 */
+#define RT2573_SHORT_PREAMBLE (1 << 18)
+#define RT2573_MRR_ENABLED (1 << 19)
+#define RT2573_MRR_CCK_FALLBACK (1 << 22)
+
+/* possible flags for register TXRX_CSR9 */
+#define RT2573_TSF_TICKING (1 << 16)
+#define RT2573_TSF_MODE(x) (((x) & 0x3) << 17)
+/* TBTT stands for Target Beacon Transmission Time */
+#define RT2573_ENABLE_TBTT (1 << 19)
+#define RT2573_GENERATE_BEACON (1 << 20)
+
+/* possible flags for register PHY_CSR0 */
+#define RT2573_PA_PE_2GHZ (1 << 16)
+#define RT2573_PA_PE_5GHZ (1 << 17)
+
+/* possible flags for register PHY_CSR3 */
+#define RT2573_BBP_READ (1 << 15)
+#define RT2573_BBP_BUSY (1 << 16)
+/* possible flags for register PHY_CSR4 */
+#define RT2573_RF_20BIT (20 << 24)
+#define RT2573_RF_BUSY (1 << 31)
+
+/* LED values */
+#define RT2573_LED_RADIO (1 << 8)
+#define RT2573_LED_G (1 << 9)
+#define RT2573_LED_A (1 << 10)
+#define RT2573_LED_ON 0x1e1e
+#define RT2573_LED_OFF 0x0
+
+#define RT2573_MCU_RUN (1 << 3)
+
+#define RT2573_SMART_MODE (1 << 0)
+
+#define RT2573_BBPR94_DEFAULT 6
+
+#define RT2573_BBP_WRITE (1 << 15)
+
+/* dual-band RF */
+#define RT2573_RF_5226 1
+#define RT2573_RF_5225 3
+/* single-band RF */
+#define RT2573_RF_2528 2
+#define RT2573_RF_2527 4
+
+#define RT2573_BBP_VERSION 0
+
+struct rum_tx_desc {
+ uint32_t flags;
+#define RT2573_TX_BURST (1 << 0)
+#define RT2573_TX_VALID (1 << 1)
+#define RT2573_TX_MORE_FRAG (1 << 2)
+#define RT2573_TX_NEED_ACK (1 << 3)
+#define RT2573_TX_TIMESTAMP (1 << 4)
+#define RT2573_TX_OFDM (1 << 5)
+#define RT2573_TX_IFS_SIFS (1 << 6)
+#define RT2573_TX_LONG_RETRY (1 << 7)
+
+ uint16_t wme;
+#define RT2573_QID(v) (v)
+#define RT2573_AIFSN(v) ((v) << 4)
+#define RT2573_LOGCWMIN(v) ((v) << 8)
+#define RT2573_LOGCWMAX(v) ((v) << 12)
+
+ uint16_t xflags;
+#define RT2573_TX_HWSEQ (1 << 12)
+
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RT2573_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+
+ uint32_t iv;
+ uint32_t eiv;
+
+ uint8_t offset;
+ uint8_t qid;
+ uint8_t txpower;
+#define RT2573_DEFAULT_TXPOWER 0
+
+ uint8_t reserved;
+} __packed;
+
+struct rum_rx_desc {
+ uint32_t flags;
+#define RT2573_RX_BUSY (1 << 0)
+#define RT2573_RX_DROP (1 << 1)
+#define RT2573_RX_CRC_ERROR (1 << 6)
+#define RT2573_RX_OFDM (1 << 7)
+
+ uint8_t rate;
+ uint8_t rssi;
+ uint8_t reserved1;
+ uint8_t offset;
+ uint32_t iv;
+ uint32_t eiv;
+ uint32_t reserved2[2];
+} __packed;
+
+#define RT2573_RF1 0
+#define RT2573_RF2 2
+#define RT2573_RF3 1
+#define RT2573_RF4 3
+
+#define RT2573_EEPROM_MACBBP 0x0000
+#define RT2573_EEPROM_ADDRESS 0x0004
+#define RT2573_EEPROM_ANTENNA 0x0020
+#define RT2573_EEPROM_CONFIG2 0x0022
+#define RT2573_EEPROM_BBP_BASE 0x0026
+#define RT2573_EEPROM_TXPOWER 0x0046
+#define RT2573_EEPROM_FREQ_OFFSET 0x005e
+#define RT2573_EEPROM_RSSI_2GHZ_OFFSET 0x009a
+#define RT2573_EEPROM_RSSI_5GHZ_OFFSET 0x009c
diff --git a/sys/dev/usb/wlan/if_rumvar.h b/sys/dev/usb/wlan/if_rumvar.h
new file mode 100644
index 0000000..1b58dc4
--- /dev/null
+++ b/sys/dev/usb/wlan/if_rumvar.h
@@ -0,0 +1,156 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006 Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RUM_TX_LIST_COUNT 8
+
+struct rum_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+};
+
+#define RT2573_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct rum_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+};
+
+#define RT2573_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct rum_softc;
+
+struct rum_task {
+ struct usb2_proc_msg hdr;
+ struct rum_softc *sc;
+};
+
+struct rum_tx_data {
+ STAILQ_ENTRY(rum_tx_data) next;
+ struct rum_softc *sc;
+ struct rum_tx_desc desc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ int rate;
+};
+typedef STAILQ_HEAD(, rum_tx_data) rum_txdhead;
+
+struct rum_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define RUM_NODE(ni) ((struct rum_node *)(ni))
+
+struct rum_vap {
+ struct ieee80211vap vap;
+ struct rum_softc *sc;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct usb2_callout amrr_ch;
+ struct rum_task amrr_task[2];
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define RUM_VAP(vap) ((struct rum_vap *)(vap))
+
+enum {
+ RUM_BULK_WR,
+ RUM_BULK_RD,
+ RUM_N_TRANSFER = 2,
+};
+
+struct rum_softc {
+ struct ifnet *sc_ifp;
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct usb2_process sc_tq;
+
+ const struct ieee80211_rate_table *sc_rates;
+ struct usb2_xfer *sc_xfer[RUM_N_TRANSFER];
+
+ uint8_t rf_rev;
+ uint8_t rffreq;
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ struct rum_task sc_synctask[2];
+ struct rum_task sc_task[2];
+ struct rum_task sc_promisctask[2];
+ struct rum_task sc_scantask[2];
+ int sc_scan_action;
+#define RUM_SCAN_START 0
+#define RUM_SCAN_END 1
+#define RUM_SET_CHANNEL 2
+
+ struct rum_tx_data tx_data[RUM_TX_LIST_COUNT];
+ rum_txdhead tx_q;
+ rum_txdhead tx_free;
+ int tx_nfree;
+ struct rum_rx_desc sc_rx_desc;
+
+ struct mtx sc_mtx;
+
+ uint32_t sta[6];
+ uint32_t rf_regs[4];
+ uint8_t txpow[44];
+ uint8_t sc_bssid[6];
+
+ struct {
+ uint8_t val;
+ uint8_t reg;
+ } __packed bbp_prom[16];
+
+ int hw_radio;
+ int rx_ant;
+ int tx_ant;
+ int nb_ant;
+ int ext_2ghz_lna;
+ int ext_5ghz_lna;
+ int rssi_2ghz_corr;
+ int rssi_5ghz_corr;
+ uint8_t bbp17;
+
+ struct rum_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+
+ struct rum_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#define RUM_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RUM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RUM_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
diff --git a/sys/dev/usb/wlan/if_ural.c b/sys/dev/usb/wlan/if_ural.c
new file mode 100644
index 0000000..aebffaa
--- /dev/null
+++ b/sys/dev/usb/wlan/if_ural.c
@@ -0,0 +1,2364 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Copyright (c) 2006, 2008
+ * Hans Petter Selasky <hselasky@freebsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*-
+ * Ralink Technology RT2500USB chipset driver
+ * http://www.ralinktech.com/
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#define USB_DEBUG_VAR ural_debug
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/wlan/usb_wlan.h>
+#include <dev/usb/wlan/if_uralreg.h>
+#include <dev/usb/wlan/if_uralvar.h>
+
+#if USB_DEBUG
+static int ural_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural");
+SYSCTL_INT(_hw_usb2_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0,
+ "Debug level");
+#endif
+
+#define ural_do_request(sc,req,data) \
+ usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000)
+
+#define URAL_RSSI(rssi) \
+ ((rssi) > (RAL_NOISE_FLOOR + RAL_RSSI_CORR) ? \
+ ((rssi) - (RAL_NOISE_FLOOR + RAL_RSSI_CORR)) : 0)
+
+/* various supported device vendors/products */
+static const struct usb2_device_id ural_devs[] = {
+ { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL167G) },
+ { USB_VP(USB_VENDOR_ASUS, USB_PRODUCT_RALINK_RT2570) },
+ { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050) },
+ { USB_VP(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7051) },
+ { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_HU200TS) },
+ { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54G) },
+ { USB_VP(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GP) },
+ { USB_VP(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU) },
+ { USB_VP(USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG122) },
+ { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GN54G) },
+ { USB_VP(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWBKG) },
+ { USB_VP(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254) },
+ { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54) },
+ { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54AI) },
+ { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54YB) },
+ { USB_VP(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_NINWIFI) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_2) },
+ { USB_VP(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2570_3) },
+ { USB_VP(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_NV902) },
+ { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570) },
+ { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2) },
+ { USB_VP(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3) },
+ { USB_VP(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G) },
+ { USB_VP(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG) },
+ { USB_VP(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R) },
+ { USB_VP(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2570) },
+ { USB_VP(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_RT2570) },
+ { USB_VP(USB_VENDOR_ZINWELL, USB_PRODUCT_ZINWELL_RT2570) },
+};
+
+static usb2_callback_t ural_bulk_read_callback;
+static usb2_callback_t ural_bulk_write_callback;
+
+static usb2_proc_callback_t ural_attach_post;
+static usb2_proc_callback_t ural_task;
+static usb2_proc_callback_t ural_scantask;
+static usb2_proc_callback_t ural_promisctask;
+static usb2_proc_callback_t ural_amrr_task;
+static usb2_proc_callback_t ural_init_task;
+static usb2_proc_callback_t ural_stop_task;
+
+static struct ieee80211vap *ural_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ural_vap_delete(struct ieee80211vap *);
+static void ural_tx_free(struct ural_tx_data *, int);
+static void ural_setup_tx_list(struct ural_softc *);
+static void ural_unsetup_tx_list(struct ural_softc *);
+static int ural_newstate(struct ieee80211vap *,
+ enum ieee80211_state, int);
+static void ural_setup_tx_desc(struct ural_softc *,
+ struct ural_tx_desc *, uint32_t, int, int);
+static int ural_tx_bcn(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_tx_mgt(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int ural_tx_data(struct ural_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static void ural_start(struct ifnet *);
+static int ural_ioctl(struct ifnet *, u_long, caddr_t);
+static void ural_set_testmode(struct ural_softc *);
+static void ural_eeprom_read(struct ural_softc *, uint16_t, void *,
+ int);
+static uint16_t ural_read(struct ural_softc *, uint16_t);
+static void ural_read_multi(struct ural_softc *, uint16_t, void *,
+ int);
+static void ural_write(struct ural_softc *, uint16_t, uint16_t);
+static void ural_write_multi(struct ural_softc *, uint16_t, void *,
+ int) __unused;
+static void ural_bbp_write(struct ural_softc *, uint8_t, uint8_t);
+static uint8_t ural_bbp_read(struct ural_softc *, uint8_t);
+static void ural_rf_write(struct ural_softc *, uint8_t, uint32_t);
+static struct ieee80211_node *ural_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void ural_newassoc(struct ieee80211_node *, int);
+static void ural_scan_start(struct ieee80211com *);
+static void ural_scan_end(struct ieee80211com *);
+static void ural_set_channel(struct ieee80211com *);
+static void ural_set_chan(struct ural_softc *,
+ struct ieee80211_channel *);
+static void ural_disable_rf_tune(struct ural_softc *);
+static void ural_enable_tsf_sync(struct ural_softc *);
+static void ural_update_slot(struct ifnet *);
+static void ural_set_txpreamble(struct ural_softc *);
+static void ural_set_basicrates(struct ural_softc *,
+ const struct ieee80211_channel *);
+static void ural_set_bssid(struct ural_softc *, const uint8_t *);
+static void ural_set_macaddr(struct ural_softc *, uint8_t *);
+static const char *ural_get_rf(int);
+static void ural_read_eeprom(struct ural_softc *);
+static int ural_bbp_init(struct ural_softc *);
+static void ural_set_txantenna(struct ural_softc *, int);
+static void ural_set_rxantenna(struct ural_softc *, int);
+static void ural_init(void *);
+static int ural_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static void ural_amrr_start(struct ural_softc *,
+ struct ieee80211_node *);
+static void ural_amrr_timeout(void *);
+static int ural_pause(struct ural_softc *sc, int timeout);
+static void ural_queue_command(struct ural_softc *,
+ usb2_proc_callback_t *, struct usb2_proc_msg *,
+ struct usb2_proc_msg *);
+
+/*
+ * Default values for MAC registers; values taken from the reference driver.
+ */
+static const struct {
+ uint16_t reg;
+ uint16_t val;
+} ural_def_mac[] = {
+ { RAL_TXRX_CSR5, 0x8c8d },
+ { RAL_TXRX_CSR6, 0x8b8a },
+ { RAL_TXRX_CSR7, 0x8687 },
+ { RAL_TXRX_CSR8, 0x0085 },
+ { RAL_MAC_CSR13, 0x1111 },
+ { RAL_MAC_CSR14, 0x1e11 },
+ { RAL_TXRX_CSR21, 0xe78f },
+ { RAL_MAC_CSR9, 0xff1d },
+ { RAL_MAC_CSR11, 0x0002 },
+ { RAL_MAC_CSR22, 0x0053 },
+ { RAL_MAC_CSR15, 0x0000 },
+ { RAL_MAC_CSR8, RAL_FRAME_SIZE },
+ { RAL_TXRX_CSR19, 0x0000 },
+ { RAL_TXRX_CSR18, 0x005a },
+ { RAL_PHY_CSR2, 0x0000 },
+ { RAL_TXRX_CSR0, 0x1ec0 },
+ { RAL_PHY_CSR4, 0x000f }
+};
+
+/*
+ * Default values for BBP registers; values taken from the reference driver.
+ */
+static const struct {
+ uint8_t reg;
+ uint8_t val;
+} ural_def_bbp[] = {
+ { 3, 0x02 },
+ { 4, 0x19 },
+ { 14, 0x1c },
+ { 15, 0x30 },
+ { 16, 0xac },
+ { 17, 0x48 },
+ { 18, 0x18 },
+ { 19, 0xff },
+ { 20, 0x1e },
+ { 21, 0x08 },
+ { 22, 0x08 },
+ { 23, 0x08 },
+ { 24, 0x80 },
+ { 25, 0x50 },
+ { 26, 0x08 },
+ { 27, 0x23 },
+ { 30, 0x10 },
+ { 31, 0x2b },
+ { 32, 0xb9 },
+ { 34, 0x12 },
+ { 35, 0x50 },
+ { 39, 0xc4 },
+ { 40, 0x02 },
+ { 41, 0x60 },
+ { 53, 0x10 },
+ { 54, 0x18 },
+ { 56, 0x08 },
+ { 57, 0x10 },
+ { 58, 0x08 },
+ { 61, 0x60 },
+ { 62, 0x10 },
+ { 75, 0xff }
+};
+
+/*
+ * Default values for RF register R2 indexed by channel numbers.
+ */
+static const uint32_t ural_rf2522_r2[] = {
+ 0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814,
+ 0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e
+};
+
+static const uint32_t ural_rf2523_r2[] = {
+ 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
+ 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
+};
+
+static const uint32_t ural_rf2524_r2[] = {
+ 0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
+ 0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
+};
+
+static const uint32_t ural_rf2525_r2[] = {
+ 0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d,
+ 0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346
+};
+
+static const uint32_t ural_rf2525_hi_r2[] = {
+ 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345,
+ 0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e
+};
+
+static const uint32_t ural_rf2525e_r2[] = {
+ 0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463,
+ 0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b
+};
+
+static const uint32_t ural_rf2526_hi_r2[] = {
+ 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d,
+ 0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241
+};
+
+static const uint32_t ural_rf2526_r2[] = {
+ 0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229,
+ 0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d
+};
+
+/*
+ * For dual-band RF, RF registers R1 and R4 also depend on channel number;
+ * values taken from the reference driver.
+ */
+static const struct {
+ uint8_t chan;
+ uint32_t r1;
+ uint32_t r2;
+ uint32_t r4;
+} ural_rf5222[] = {
+ { 1, 0x08808, 0x0044d, 0x00282 },
+ { 2, 0x08808, 0x0044e, 0x00282 },
+ { 3, 0x08808, 0x0044f, 0x00282 },
+ { 4, 0x08808, 0x00460, 0x00282 },
+ { 5, 0x08808, 0x00461, 0x00282 },
+ { 6, 0x08808, 0x00462, 0x00282 },
+ { 7, 0x08808, 0x00463, 0x00282 },
+ { 8, 0x08808, 0x00464, 0x00282 },
+ { 9, 0x08808, 0x00465, 0x00282 },
+ { 10, 0x08808, 0x00466, 0x00282 },
+ { 11, 0x08808, 0x00467, 0x00282 },
+ { 12, 0x08808, 0x00468, 0x00282 },
+ { 13, 0x08808, 0x00469, 0x00282 },
+ { 14, 0x08808, 0x0046b, 0x00286 },
+
+ { 36, 0x08804, 0x06225, 0x00287 },
+ { 40, 0x08804, 0x06226, 0x00287 },
+ { 44, 0x08804, 0x06227, 0x00287 },
+ { 48, 0x08804, 0x06228, 0x00287 },
+ { 52, 0x08804, 0x06229, 0x00287 },
+ { 56, 0x08804, 0x0622a, 0x00287 },
+ { 60, 0x08804, 0x0622b, 0x00287 },
+ { 64, 0x08804, 0x0622c, 0x00287 },
+
+ { 100, 0x08804, 0x02200, 0x00283 },
+ { 104, 0x08804, 0x02201, 0x00283 },
+ { 108, 0x08804, 0x02202, 0x00283 },
+ { 112, 0x08804, 0x02203, 0x00283 },
+ { 116, 0x08804, 0x02204, 0x00283 },
+ { 120, 0x08804, 0x02205, 0x00283 },
+ { 124, 0x08804, 0x02206, 0x00283 },
+ { 128, 0x08804, 0x02207, 0x00283 },
+ { 132, 0x08804, 0x02208, 0x00283 },
+ { 136, 0x08804, 0x02209, 0x00283 },
+ { 140, 0x08804, 0x0220a, 0x00283 },
+
+ { 149, 0x08808, 0x02429, 0x00281 },
+ { 153, 0x08808, 0x0242b, 0x00281 },
+ { 157, 0x08808, 0x0242d, 0x00281 },
+ { 161, 0x08808, 0x0242f, 0x00281 }
+};
+
+static const struct usb2_config ural_config[URAL_N_TRANSFER] = {
+ [URAL_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE + 4),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = ural_bulk_write_callback,
+ .mh.timeout = 5000, /* ms */
+ },
+ [URAL_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = (RAL_FRAME_SIZE + RAL_RX_DESC_SIZE),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = ural_bulk_read_callback,
+ },
+};
+
+static device_probe_t ural_match;
+static device_attach_t ural_attach;
+static device_detach_t ural_detach;
+
+static device_method_t ural_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ural_match),
+ DEVMETHOD(device_attach, ural_attach),
+ DEVMETHOD(device_detach, ural_detach),
+
+ { 0, 0 }
+};
+
+static driver_t ural_driver = {
+ .name = "ural",
+ .methods = ural_methods,
+ .size = sizeof(struct ural_softc),
+};
+
+static devclass_t ural_devclass;
+
+DRIVER_MODULE(ural, ushub, ural_driver, ural_devclass, NULL, 0);
+MODULE_DEPEND(ural, usb, 1, 1, 1);
+MODULE_DEPEND(ural, wlan, 1, 1, 1);
+MODULE_DEPEND(ural, wlan_amrr, 1, 1, 1);
+
+static int
+ural_match(device_t self)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(self);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != 0)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != RAL_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(ural_devs, sizeof(ural_devs), uaa));
+}
+
+static int
+ural_attach(device_t self)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(self);
+ struct ural_softc *sc = device_get_softc(self);
+ int error;
+ uint8_t iface_index;
+
+ device_set_usb2_desc(self);
+ sc->sc_udev = uaa->device;
+ sc->sc_dev = self;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(self),
+ MTX_NETWORK_LOCK, MTX_DEF);
+
+ iface_index = RAL_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device,
+ &iface_index, sc->sc_xfer, ural_config,
+ URAL_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(self, "could not allocate USB transfers, "
+ "err=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx,
+ device_get_nameunit(self), USB_PRI_MED);
+ if (error) {
+ device_printf(self, "could not setup config thread!\n");
+ goto detach;
+ }
+
+ /* fork rest of the attach code */
+ RAL_LOCK(sc);
+ ural_queue_command(sc, ural_attach_post,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ RAL_UNLOCK(sc);
+ return (0);
+
+detach:
+ ural_detach(self);
+ return (ENXIO); /* failure */
+}
+
+static void
+ural_attach_post(struct usb2_proc_msg *pm)
+{
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp;
+ struct ieee80211com *ic;
+ uint8_t bands;
+
+ /* retrieve RT2570 rev. no */
+ sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);
+
+ /* retrieve MAC address and various other things from EEPROM */
+ ural_read_eeprom(sc);
+ RAL_UNLOCK(sc);
+
+ device_printf(sc->sc_dev, "MAC/BBP RT2570 (rev 0x%02x), RF %s\n",
+ sc->asic_rev, ural_get_rf(sc->rf_rev));
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ RAL_LOCK(sc);
+ return;
+ }
+ ic = ifp->if_l2com;
+
+ ifp->if_softc = sc;
+ if_initname(ifp, "ural", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = ural_init;
+ ifp->if_ioctl = ural_ioctl;
+ ifp->if_start = ural_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid);
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode supported */
+ | IEEE80211_C_IBSS /* IBSS mode supported */
+ | IEEE80211_C_MONITOR /* monitor mode supported */
+ | IEEE80211_C_HOSTAP /* HostAp mode supported */
+ | IEEE80211_C_TXPMGT /* tx power management */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* bg scanning supported */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ if (sc->rf_rev == RAL_RF_5222)
+ setbit(&bands, IEEE80211_MODE_11A);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_newassoc = ural_newassoc;
+ ic->ic_raw_xmit = ural_raw_xmit;
+ ic->ic_node_alloc = ural_node_alloc;
+ ic->ic_scan_start = ural_scan_start;
+ ic->ic_scan_end = ural_scan_end;
+ ic->ic_set_channel = ural_set_channel;
+
+ ic->ic_vap_create = ural_vap_create;
+ ic->ic_vap_delete = ural_vap_delete;
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + sizeof(sc->sc_txtap));
+
+ sc->sc_rxtap_len = sizeof sc->sc_rxtap;
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(RAL_RX_RADIOTAP_PRESENT);
+
+ sc->sc_txtap_len = sizeof sc->sc_txtap;
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(RAL_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ RAL_LOCK(sc);
+}
+
+static int
+ural_detach(device_t self)
+{
+ struct ural_softc *sc = device_get_softc(self);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic;
+
+ /* wait for any post attach or other command to complete */
+ usb2_proc_drain(&sc->sc_tq);
+
+ /* stop all USB transfers */
+ usb2_transfer_unsetup(sc->sc_xfer, URAL_N_TRANSFER);
+ usb2_proc_free(&sc->sc_tq);
+
+ /* free TX list, if any */
+ RAL_LOCK(sc);
+ ural_unsetup_tx_list(sc);
+ RAL_UNLOCK(sc);
+
+ if (ifp) {
+ ic = ifp->if_l2com;
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+ if_free(ifp);
+ }
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+ural_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+ struct ural_vap *uvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return NULL;
+ uvp = (struct ural_vap *) malloc(sizeof(struct ural_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (uvp == NULL)
+ return NULL;
+ vap = &uvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ uvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = ural_newstate;
+
+ uvp->sc = sc;
+ usb2_callout_init_mtx(&uvp->amrr_ch, &sc->sc_mtx, 0);
+ ieee80211_amrr_init(&uvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return vap;
+}
+
+static void
+ural_vap_delete(struct ieee80211vap *vap)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ usb2_callout_drain(&uvp->amrr_ch);
+ ieee80211_amrr_cleanup(&uvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(uvp, M_80211_VAP);
+}
+
+static void
+ural_tx_free(struct ural_tx_data *data, int txerr)
+{
+ struct ural_softc *sc = data->sc;
+
+ if (data->m != NULL) {
+ if (data->m->m_flags & M_TXCB)
+ ieee80211_process_callback(data->ni, data->m,
+ txerr ? ETIMEDOUT : 0);
+ m_freem(data->m);
+ data->m = NULL;
+
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+}
+
+static void
+ural_setup_tx_list(struct ural_softc *sc)
+{
+ struct ural_tx_data *data;
+ int i;
+
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+}
+
+static void
+ural_unsetup_tx_list(struct ural_softc *sc)
+{
+ struct ural_tx_data *data;
+ int i;
+
+ /* make sure any subsequent use of the queues will fail */
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ /* free up all node references and mbufs */
+ for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+static void
+ural_task(struct usb2_proc_msg *pm)
+{
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ural_vap *uvp = URAL_VAP(vap);
+ const struct ieee80211_txparam *tp;
+ enum ieee80211_state ostate;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ ostate = vap->iv_state;
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN) {
+ /* abort TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+
+ /* force tx led to stop blinking */
+ ural_write(sc, RAL_MAC_CSR20, 0);
+ }
+ break;
+
+ case IEEE80211_S_RUN:
+ ni = vap->iv_bss;
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR) {
+ ural_update_slot(ic->ic_ifp);
+ ural_set_txpreamble(sc);
+ ural_set_basicrates(sc, ic->ic_bsschan);
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ ural_set_bssid(sc, sc->sc_bssid);
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP ||
+ vap->iv_opmode == IEEE80211_M_IBSS) {
+ m = ieee80211_beacon_alloc(ni, &uvp->bo);
+ if (m == NULL) {
+ device_printf(sc->sc_dev,
+ "could not allocate beacon\n");
+ return;
+ }
+
+ if (ural_tx_bcn(sc, m, ni) != 0) {
+ device_printf(sc->sc_dev,
+ "could not send beacon\n");
+ return;
+ }
+ }
+
+ /* make tx led blink on tx (controlled by ASIC) */
+ ural_write(sc, RAL_MAC_CSR20, 1);
+
+ if (vap->iv_opmode != IEEE80211_M_MONITOR)
+ ural_enable_tsf_sync(sc);
+
+ /* enable automatic rate adaptation */
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+ if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE)
+ ural_amrr_start(sc, ni);
+
+ break;
+
+ default:
+ break;
+ }
+
+ RAL_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ uvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+ RAL_LOCK(sc);
+}
+
+static void
+ural_scantask(struct usb2_proc_msg *pm)
+{
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ switch (sc->sc_scan_action) {
+ case URAL_SCAN_START:
+ /* abort TSF synchronization */
+ DPRINTF("starting scan\n");
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+ ural_set_bssid(sc, ifp->if_broadcastaddr);
+ break;
+
+ case URAL_SET_CHANNEL:
+ ural_set_chan(sc, ic->ic_curchan);
+ break;
+
+ default: /* URAL_SCAN_END */
+ DPRINTF("stopping scan\n");
+ ural_enable_tsf_sync(sc);
+ ural_set_bssid(sc, sc->sc_bssid);
+ break;
+ }
+}
+
+static int
+ural_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ DPRINTF("%s -> %s\n",
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ RAL_LOCK(sc);
+ usb2_callout_stop(&uvp->amrr_ch);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+ RAL_UNLOCK(sc);
+
+ if (nstate == IEEE80211_S_INIT) {
+ uvp->newstate(vap, nstate, arg);
+ return 0;
+ } else {
+ RAL_LOCK(sc);
+ ural_queue_command(sc, ural_task, &sc->sc_task[0].hdr,
+ &sc->sc_task[1].hdr);
+ RAL_UNLOCK(sc);
+ return EINPROGRESS;
+ }
+}
+
+
+static void
+ural_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct ural_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ struct ural_tx_data *data;
+ struct mbuf *m;
+ unsigned int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTFN(11, "transfer complete, %d bytes\n", xfer->actlen);
+
+ /* free resources */
+ data = xfer->priv_fifo;
+ ural_tx_free(data, 0);
+ xfer->priv_fifo = NULL;
+
+ ifp->if_opackets++;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->tx_q);
+ if (data) {
+ STAILQ_REMOVE_HEAD(&sc->tx_q, next);
+ m = data->m;
+
+ if (m->m_pkthdr.len > (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE)) {
+ DPRINTFN(0, "data overflow, %u bytes\n",
+ m->m_pkthdr.len);
+ m->m_pkthdr.len = (RAL_FRAME_SIZE + RAL_TX_DESC_SIZE);
+ }
+ usb2_copy_in(xfer->frbuffers, 0, &data->desc,
+ RAL_TX_DESC_SIZE);
+ usb2_m_copy_in(xfer->frbuffers, RAL_TX_DESC_SIZE, m, 0,
+ m->m_pkthdr.len);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct ural_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = data->rate;
+ tap->wt_chan_freq = htole16(c->ic_freq);
+ tap->wt_chan_flags = htole16(c->ic_flags);
+ tap->wt_antenna = sc->tx_ant;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m);
+ }
+
+ /* xfer length needs to be a multiple of two! */
+ len = (RAL_TX_DESC_SIZE + m->m_pkthdr.len + 1) & ~1;
+ if ((len % 64) == 0)
+ len += 2;
+
+ DPRINTFN(11, "sending frame len=%u xferlen=%u\n",
+ m->m_pkthdr.len, len);
+
+ xfer->frlengths[0] = len;
+ xfer->priv_fifo = data;
+
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTFN(11, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+ data = xfer->priv_fifo;
+ if (data != NULL) {
+ ural_tx_free(data, xfer->error);
+ xfer->priv_fifo = NULL;
+ }
+
+ if (xfer->error == USB_ERR_STALLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ if (xfer->error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+ break;
+ }
+}
+
+static void
+ural_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct ural_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_node *ni;
+ struct mbuf *m = NULL;
+ uint32_t flags;
+ uint8_t rssi = 0;
+ unsigned int len;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+
+ DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen);
+
+ len = xfer->actlen;
+ if (len < RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN) {
+ DPRINTF("%s: xfer too short %d\n",
+ device_get_nameunit(sc->sc_dev), len);
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+
+ len -= RAL_RX_DESC_SIZE;
+ /* rx descriptor is located at the end */
+ usb2_copy_out(xfer->frbuffers, len, &sc->sc_rx_desc,
+ RAL_RX_DESC_SIZE);
+
+ rssi = URAL_RSSI(sc->sc_rx_desc.rssi);
+ flags = le32toh(sc->sc_rx_desc.flags);
+ if (flags & (RAL_RX_PHY_ERROR | RAL_RX_CRC_ERROR)) {
+ /*
+ * This should not happen since we did not
+ * request to receive those frames when we
+ * filled RAL_TXRX_CSR2:
+ */
+ DPRINTFN(5, "PHY or CRC error\n");
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ if (m == NULL) {
+ DPRINTF("could not allocate mbuf\n");
+ ifp->if_ierrors++;
+ goto tr_setup;
+ }
+ usb2_copy_out(xfer->frbuffers, 0, mtod(m, uint8_t *), len);
+
+ /* finalize mbuf */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff;
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct ural_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
+ tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate,
+ (flags & RAL_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
+ tap->wr_antenna = sc->rx_ant;
+ tap->wr_antsignal = rssi;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+ /* Strip trailing 802.11 MAC FCS. */
+ m_adj(m, -IEEE80211_CRC_LEN);
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+
+ /*
+ * At the end of a USB callback it is always safe to unlock
+ * the private mutex of a device! That is why we do the
+ * "ieee80211_input" here, and not some lines up!
+ */
+ if (m) {
+ RAL_UNLOCK(sc);
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void) ieee80211_input(ni, m, rssi,
+ RAL_NOISE_FLOOR, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void) ieee80211_input_all(ic, m, rssi,
+ RAL_NOISE_FLOOR, 0);
+ RAL_LOCK(sc);
+ }
+ return;
+
+ default: /* Error */
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ return;
+ }
+}
+
+static uint8_t
+ural_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12: return 0xb;
+ case 18: return 0xf;
+ case 24: return 0xa;
+ case 36: return 0xe;
+ case 48: return 0x9;
+ case 72: return 0xd;
+ case 96: return 0x8;
+ case 108: return 0xc;
+
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2: return 0x0;
+ case 4: return 0x1;
+ case 11: return 0x2;
+ case 22: return 0x3;
+ }
+ return 0xff; /* XXX unsupported/unknown rate */
+}
+
+static void
+ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
+ uint32_t flags, int len, int rate)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t plcp_length;
+ int remainder;
+
+ desc->flags = htole32(flags);
+ desc->flags |= htole32(RAL_TX_NEWSEQ);
+ desc->flags |= htole32(len << 16);
+
+ desc->wme = htole16(RAL_AIFSN(2) | RAL_LOGCWMIN(3) | RAL_LOGCWMAX(5));
+ desc->wme |= htole16(RAL_IVOFFSET(sizeof (struct ieee80211_frame)));
+
+ /* setup PLCP fields */
+ desc->plcp_signal = ural_plcp_signal(rate);
+ desc->plcp_service = 4;
+
+ len += IEEE80211_CRC_LEN;
+ if (ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM) {
+ desc->flags |= htole32(RAL_TX_OFDM);
+
+ plcp_length = len & 0xfff;
+ desc->plcp_length_hi = plcp_length >> 6;
+ desc->plcp_length_lo = plcp_length & 0x3f;
+ } else {
+ plcp_length = (16 * len + rate - 1) / rate;
+ if (rate == 22) {
+ remainder = (16 * len) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= RAL_PLCP_LENGEXT;
+ }
+ desc->plcp_length_hi = plcp_length >> 8;
+ desc->plcp_length_lo = plcp_length & 0xff;
+
+ if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->plcp_signal |= 0x08;
+ }
+
+ desc->iv = 0;
+ desc->eiv = 0;
+}
+
+#define RAL_TX_TIMEOUT 5000
+
+static int
+ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ const struct ieee80211_txparam *tp;
+ struct ural_tx_data *data;
+
+ if (sc->tx_nfree == 0) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ m_freem(m0);
+ ieee80211_free_node(ni);
+ return EIO;
+ }
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = tp->mgmtrate;
+
+ ural_setup_tx_desc(sc, &data->desc,
+ RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP, m0->m_pkthdr.len,
+ tp->mgmtrate);
+
+ DPRINTFN(10, "sending beacon frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return (0);
+}
+
+static int
+ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_txparam *tp;
+ struct ural_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ uint32_t flags;
+ uint16_t dur;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = tp->mgmtrate;
+
+ flags = 0;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RAL_TX_ACK;
+
+ dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+
+ /* tell hardware to add timestamp for probe responses */
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT &&
+ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+ flags |= RAL_TX_TIMESTAMP;
+ }
+
+ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, tp->mgmtrate);
+
+ DPRINTFN(10, "sending mgt frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, tp->mgmtrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_sendprot(struct ural_softc *sc,
+ const struct mbuf *m, struct ieee80211_node *ni, int prot, int rate)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ const struct ieee80211_frame *wh;
+ struct ural_tx_data *data;
+ struct mbuf *mprot;
+ int protrate, ackrate, pktlen, flags, isshort;
+ uint16_t dur;
+
+ KASSERT(prot == IEEE80211_PROT_RTSCTS || prot == IEEE80211_PROT_CTSONLY,
+ ("protection %d", prot));
+
+ wh = mtod(m, const struct ieee80211_frame *);
+ pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
+ ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
+
+ isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+ dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
+ + ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags = RAL_TX_RETRY(7);
+ if (prot == IEEE80211_PROT_RTSCTS) {
+ /* NB: CTS is the same size as an ACK */
+ dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
+ flags |= RAL_TX_ACK;
+ mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
+ } else {
+ mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+ }
+ if (mprot == NULL) {
+ /* XXX stat + msg */
+ return ENOBUFS;
+ }
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = mprot;
+ data->ni = ieee80211_ref_node(ni);
+ data->rate = protrate;
+ ural_setup_tx_desc(sc, &data->desc, flags, mprot->m_pkthdr.len, protrate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_tx_raw(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ural_tx_data *data;
+ uint32_t flags;
+ int error;
+ int rate;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+ KASSERT(params != NULL, ("no raw xmit params"));
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
+ /* XXX validate */
+ if (rate == 0) {
+ m_freem(m0);
+ return EINVAL;
+ }
+ flags = 0;
+ if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
+ flags |= RAL_TX_ACK;
+ if (params->ibp_flags & (IEEE80211_BPF_RTS|IEEE80211_BPF_CTS)) {
+ error = ural_sendprot(sc, m0, ni,
+ params->ibp_flags & IEEE80211_BPF_RTS ?
+ IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
+ rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ /* XXX need to setup descriptor ourself */
+ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending raw frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static int
+ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ural_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ uint32_t flags = 0;
+ uint16_t dur;
+ int error, rate;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ rate = tp->mcastrate;
+ else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
+ rate = tp->ucastrate;
+ else
+ rate = ni->ni_txrate;
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return ENOBUFS;
+ }
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ int prot = IEEE80211_PROT_NONE;
+ if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
+ prot = IEEE80211_PROT_RTSCTS;
+ else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+ ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
+ prot = ic->ic_protmode;
+ if (prot != IEEE80211_PROT_NONE) {
+ error = ural_sendprot(sc, m0, ni, prot, rate);
+ if (error) {
+ m_freem(m0);
+ return error;
+ }
+ flags |= RAL_TX_IFS_SIFS;
+ }
+ }
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+
+ data->m = m0;
+ data->ni = ni;
+ data->rate = rate;
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= RAL_TX_ACK;
+ flags |= RAL_TX_RETRY(7);
+
+ dur = ieee80211_ack_duration(sc->sc_rates, rate,
+ ic->ic_flags & IEEE80211_F_SHPREAMBLE);
+ *(uint16_t *)wh->i_dur = htole16(dur);
+ }
+
+ ural_setup_tx_desc(sc, &data->desc, flags, m0->m_pkthdr.len, rate);
+
+ DPRINTFN(10, "sending data frame len=%u rate=%u\n",
+ m0->m_pkthdr.len, rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[URAL_BULK_WR]);
+
+ return 0;
+}
+
+static void
+ural_start(struct ifnet *ifp)
+{
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ RAL_LOCK(sc);
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ RAL_UNLOCK(sc);
+ return;
+ }
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_nfree == 0) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ continue;
+ }
+ if (ural_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+ }
+ RAL_UNLOCK(sc);
+}
+
+static int
+ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ RAL_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
+ ural_queue_command(sc, ural_init_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ startall = 1;
+ } else
+ ural_queue_command(sc, ural_promisctask,
+ &sc->sc_promisctask[0].hdr,
+ &sc->sc_promisctask[1].hdr);
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ ural_queue_command(sc, ural_stop_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ }
+ }
+ RAL_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ default:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ }
+ return error;
+}
+
+static void
+ural_set_testmode(struct ural_softc *sc)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_VENDOR_REQUEST;
+ USETW(req.wValue, 4);
+ USETW(req.wIndex, 1);
+ USETW(req.wLength, 0);
+
+ error = ural_do_request(sc, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not set test mode: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static void
+ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_EEPROM;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, addr);
+ USETW(req.wLength, len);
+
+ error = ural_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static uint16_t
+ural_read(struct ural_softc *sc, uint16_t reg)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+ uint16_t val;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, sizeof (uint16_t));
+
+ error = ural_do_request(sc, &req, &val);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usb2_errstr(error));
+ return 0;
+ }
+
+ return le16toh(val);
+}
+
+static void
+ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = RAL_READ_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = ural_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read MAC register: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static void
+ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_WRITE_MAC;
+ USETW(req.wValue, val);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, 0);
+
+ error = ural_do_request(sc, &req, NULL);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static void
+ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = RAL_WRITE_MULTI_MAC;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, reg);
+ USETW(req.wLength, len);
+
+ error = ural_do_request(sc, &req, buf);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not write MAC register: %s\n",
+ usb2_errstr(error));
+ }
+}
+
+static void
+ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val)
+{
+ uint16_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to BBP\n");
+ return;
+ }
+
+ tmp = reg << 8 | val;
+ ural_write(sc, RAL_PHY_CSR7, tmp);
+}
+
+static uint8_t
+ural_bbp_read(struct ural_softc *sc, uint8_t reg)
+{
+ uint16_t val;
+ int ntries;
+
+ val = RAL_BBP_WRITE | reg << 8;
+ ural_write(sc, RAL_PHY_CSR7, val);
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not read BBP\n");
+ return 0;
+ }
+
+ return ural_read(sc, RAL_PHY_CSR7) & 0xff;
+}
+
+static void
+ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
+{
+ uint32_t tmp;
+ int ntries;
+
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "could not write to RF\n");
+ return;
+ }
+
+ tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3);
+ ural_write(sc, RAL_PHY_CSR9, tmp & 0xffff);
+ ural_write(sc, RAL_PHY_CSR10, tmp >> 16);
+
+ /* remember last written value in sc */
+ sc->rf_regs[reg] = val;
+
+ DPRINTFN(15, "RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff);
+}
+
+/* ARGUSED */
+static struct ieee80211_node *
+ural_node_alloc(struct ieee80211vap *vap __unused,
+ const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
+{
+ struct ural_node *un;
+
+ un = malloc(sizeof(struct ural_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return un != NULL ? &un->ni : NULL;
+}
+
+static void
+ural_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&URAL_VAP(vap)->amrr, &URAL_NODE(ni)->amn, ni);
+}
+
+static void
+ural_scan_start(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ RAL_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = URAL_SCAN_START;
+ ural_queue_command(sc, ural_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ RAL_UNLOCK(sc);
+
+}
+
+static void
+ural_scan_end(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ RAL_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = URAL_SCAN_END;
+ ural_queue_command(sc, ural_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ RAL_UNLOCK(sc);
+
+}
+
+static void
+ural_set_channel(struct ieee80211com *ic)
+{
+ struct ural_softc *sc = ic->ic_ifp->if_softc;
+
+ RAL_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = URAL_SET_CHANNEL;
+ ural_queue_command(sc, ural_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+
+ sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
+ RAL_UNLOCK(sc);
+}
+
+static void
+ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint8_t power, tmp;
+ int i, chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY)
+ return;
+
+ if (IEEE80211_IS_CHAN_2GHZ(c))
+ power = min(sc->txpow[chan - 1], 31);
+ else
+ power = 31;
+
+ /* adjust txpower using ifconfig settings */
+ power -= (100 - ic->ic_txpowlimit) / 8;
+
+ DPRINTFN(2, "setting channel to %u, txpower to %u\n", chan, power);
+
+ switch (sc->rf_rev) {
+ case RAL_RF_2522:
+ ural_rf_write(sc, RAL_RF1, 0x00814);
+ ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ break;
+
+ case RAL_RF_2523:
+ ural_rf_write(sc, RAL_RF1, 0x08804);
+ ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2524:
+ ural_rf_write(sc, RAL_RF1, 0x0c808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2525:
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
+ break;
+
+ case RAL_RF_2525E:
+ ural_rf_write(sc, RAL_RF1, 0x08808);
+ ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282);
+ break;
+
+ case RAL_RF_2526:
+ ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+ ural_rf_write(sc, RAL_RF1, 0x08804);
+
+ ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
+ ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
+ break;
+
+ /* dual-band RF */
+ case RAL_RF_5222:
+ for (i = 0; ural_rf5222[i].chan != chan; i++);
+
+ ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1);
+ ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2);
+ ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
+ ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4);
+ break;
+ }
+
+ if (ic->ic_opmode != IEEE80211_M_MONITOR &&
+ (ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ /* set Japan filter bit for channel 14 */
+ tmp = ural_bbp_read(sc, 70);
+
+ tmp &= ~RAL_JAPAN_FILTER;
+ if (chan == 14)
+ tmp |= RAL_JAPAN_FILTER;
+
+ ural_bbp_write(sc, 70, tmp);
+
+ /* clear CRC errors */
+ ural_read(sc, RAL_STA_CSR0);
+
+ ural_pause(sc, hz / 100);
+ ural_disable_rf_tune(sc);
+ }
+
+ /* XXX doesn't belong here */
+ /* update basic rate set */
+ ural_set_basicrates(sc, c);
+}
+
+/*
+ * Disable RF auto-tuning.
+ */
+static void
+ural_disable_rf_tune(struct ural_softc *sc)
+{
+ uint32_t tmp;
+
+ if (sc->rf_rev != RAL_RF_2523) {
+ tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE;
+ ural_rf_write(sc, RAL_RF1, tmp);
+ }
+
+ tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
+ ural_rf_write(sc, RAL_RF3, tmp);
+
+ DPRINTFN(2, "disabling RF autotune\n");
+}
+
+/*
+ * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF
+ * synchronization.
+ */
+static void
+ural_enable_tsf_sync(struct ural_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ uint16_t logcwmin, preload, tmp;
+
+ /* first, disable TSF synchronization */
+ ural_write(sc, RAL_TXRX_CSR19, 0);
+
+ tmp = (16 * vap->iv_bss->ni_intval) << 4;
+ ural_write(sc, RAL_TXRX_CSR18, tmp);
+
+ logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0;
+ preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6;
+ tmp = logcwmin << 12 | preload;
+ ural_write(sc, RAL_TXRX_CSR20, tmp);
+
+ /* finally, enable TSF synchronization */
+ tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN;
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ tmp |= RAL_ENABLE_TSF_SYNC(1);
+ else
+ tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR;
+ ural_write(sc, RAL_TXRX_CSR19, tmp);
+
+ DPRINTF("enabling TSF synchronization\n");
+}
+
+#define RAL_RXTX_TURNAROUND 5 /* us */
+static void
+ural_update_slot(struct ifnet *ifp)
+{
+ struct ural_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t slottime, sifs, eifs;
+
+ slottime = (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
+
+ /*
+ * These settings may sound a bit inconsistent but this is what the
+ * reference driver does.
+ */
+ if (ic->ic_curmode == IEEE80211_MODE_11B) {
+ sifs = 16 - RAL_RXTX_TURNAROUND;
+ eifs = 364;
+ } else {
+ sifs = 10 - RAL_RXTX_TURNAROUND;
+ eifs = 64;
+ }
+
+ ural_write(sc, RAL_MAC_CSR10, slottime);
+ ural_write(sc, RAL_MAC_CSR11, sifs);
+ ural_write(sc, RAL_MAC_CSR12, eifs);
+}
+
+static void
+ural_set_txpreamble(struct ural_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t tmp;
+
+ tmp = ural_read(sc, RAL_TXRX_CSR10);
+
+ tmp &= ~RAL_SHORT_PREAMBLE;
+ if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ tmp |= RAL_SHORT_PREAMBLE;
+
+ ural_write(sc, RAL_TXRX_CSR10, tmp);
+}
+
+static void
+ural_set_basicrates(struct ural_softc *sc, const struct ieee80211_channel *c)
+{
+ /* XXX wrong, take from rate set */
+ /* update basic rate set */
+ if (IEEE80211_IS_CHAN_5GHZ(c)) {
+ /* 11a basic rates: 6, 12, 24Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x150);
+ } else if (IEEE80211_IS_CHAN_ANYG(c)) {
+ /* 11g basic rates: 1, 2, 5.5, 11, 6, 12, 24Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+ } else {
+ /* 11b basic rates: 1, 2Mbps */
+ ural_write(sc, RAL_TXRX_CSR11, 0x3);
+ }
+}
+
+static void
+ural_set_bssid(struct ural_softc *sc, const uint8_t *bssid)
+{
+ uint16_t tmp;
+
+ tmp = bssid[0] | bssid[1] << 8;
+ ural_write(sc, RAL_MAC_CSR5, tmp);
+
+ tmp = bssid[2] | bssid[3] << 8;
+ ural_write(sc, RAL_MAC_CSR6, tmp);
+
+ tmp = bssid[4] | bssid[5] << 8;
+ ural_write(sc, RAL_MAC_CSR7, tmp);
+
+ DPRINTF("setting BSSID to %6D\n", bssid, ":");
+}
+
+static void
+ural_set_macaddr(struct ural_softc *sc, uint8_t *addr)
+{
+ uint16_t tmp;
+
+ tmp = addr[0] | addr[1] << 8;
+ ural_write(sc, RAL_MAC_CSR2, tmp);
+
+ tmp = addr[2] | addr[3] << 8;
+ ural_write(sc, RAL_MAC_CSR3, tmp);
+
+ tmp = addr[4] | addr[5] << 8;
+ ural_write(sc, RAL_MAC_CSR4, tmp);
+
+ DPRINTF("setting MAC address to %6D\n", addr, ":");
+}
+
+static void
+ural_promisctask(struct usb2_proc_msg *pm)
+{
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ uint32_t tmp;
+
+ tmp = ural_read(sc, RAL_TXRX_CSR2);
+
+ tmp &= ~RAL_DROP_NOT_TO_ME;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RAL_DROP_NOT_TO_ME;
+
+ ural_write(sc, RAL_TXRX_CSR2, tmp);
+
+ DPRINTF("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
+ "entering" : "leaving");
+}
+
+static const char *
+ural_get_rf(int rev)
+{
+ switch (rev) {
+ case RAL_RF_2522: return "RT2522";
+ case RAL_RF_2523: return "RT2523";
+ case RAL_RF_2524: return "RT2524";
+ case RAL_RF_2525: return "RT2525";
+ case RAL_RF_2525E: return "RT2525e";
+ case RAL_RF_2526: return "RT2526";
+ case RAL_RF_5222: return "RT5222";
+ default: return "unknown";
+ }
+}
+
+static void
+ural_read_eeprom(struct ural_softc *sc)
+{
+ uint16_t val;
+
+ ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2);
+ val = le16toh(val);
+ sc->rf_rev = (val >> 11) & 0x7;
+ sc->hw_radio = (val >> 10) & 0x1;
+ sc->led_mode = (val >> 6) & 0x7;
+ sc->rx_ant = (val >> 4) & 0x3;
+ sc->tx_ant = (val >> 2) & 0x3;
+ sc->nb_ant = val & 0x3;
+
+ /* read MAC address */
+ ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, sc->sc_bssid, 6);
+
+ /* read default values for BBP registers */
+ ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);
+
+ /* read Tx power for all b/g channels */
+ ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14);
+}
+
+static int
+ural_bbp_init(struct ural_softc *sc)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ int i, ntries;
+
+ /* wait for BBP to be ready */
+ for (ntries = 0; ntries < 100; ntries++) {
+ if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0)
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev, "timeout waiting for BBP\n");
+ return EIO;
+ }
+
+ /* initialize BBP registers to default values */
+ for (i = 0; i < N(ural_def_bbp); i++)
+ ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val);
+
+#if 0
+ /* initialize BBP registers to values stored in EEPROM */
+ for (i = 0; i < 16; i++) {
+ if (sc->bbp_prom[i].reg == 0xff)
+ continue;
+ ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
+ }
+#endif
+
+ return 0;
+#undef N
+}
+
+static void
+ural_set_txantenna(struct ural_softc *sc, int antenna)
+{
+ uint16_t tmp;
+ uint8_t tx;
+
+ tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK;
+ if (antenna == 1)
+ tx |= RAL_BBP_ANTA;
+ else if (antenna == 2)
+ tx |= RAL_BBP_ANTB;
+ else
+ tx |= RAL_BBP_DIVERSITY;
+
+ /* need to force I/Q flip for RF 2525e, 2526 and 5222 */
+ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 ||
+ sc->rf_rev == RAL_RF_5222)
+ tx |= RAL_BBP_FLIPIQ;
+
+ ural_bbp_write(sc, RAL_BBP_TX, tx);
+
+ /* update values in PHY_CSR5 and PHY_CSR6 */
+ tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7;
+ ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7));
+
+ tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7;
+ ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7));
+}
+
+static void
+ural_set_rxantenna(struct ural_softc *sc, int antenna)
+{
+ uint8_t rx;
+
+ rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK;
+ if (antenna == 1)
+ rx |= RAL_BBP_ANTA;
+ else if (antenna == 2)
+ rx |= RAL_BBP_ANTB;
+ else
+ rx |= RAL_BBP_DIVERSITY;
+
+ /* need to force no I/Q flip for RF 2525e and 2526 */
+ if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526)
+ rx &= ~RAL_BBP_FLIPIQ;
+
+ ural_bbp_write(sc, RAL_BBP_RX, rx);
+}
+
+static void
+ural_init_task(struct usb2_proc_msg *pm)
+{
+#define N(a) (sizeof (a) / sizeof ((a)[0]))
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint16_t tmp;
+ int i, ntries;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ ural_set_testmode(sc);
+ ural_write(sc, 0x308, 0x00f0); /* XXX magic */
+
+ ural_stop_task(pm);
+
+ /* initialize MAC registers to default values */
+ for (i = 0; i < N(ural_def_mac); i++)
+ ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val);
+
+ /* wait for BBP and RF to wake up (this can take a long time!) */
+ for (ntries = 0; ntries < 100; ntries++) {
+ tmp = ural_read(sc, RAL_MAC_CSR17);
+ if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) ==
+ (RAL_BBP_AWAKE | RAL_RF_AWAKE))
+ break;
+ if (ural_pause(sc, hz / 100))
+ break;
+ }
+ if (ntries == 100) {
+ device_printf(sc->sc_dev,
+ "timeout waiting for BBP/RF to wakeup\n");
+ goto fail;
+ }
+
+ /* we're ready! */
+ ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY);
+
+ /* set basic rate set (will be updated later) */
+ ural_write(sc, RAL_TXRX_CSR11, 0x15f);
+
+ if (ural_bbp_init(sc) != 0)
+ goto fail;
+
+ ural_set_chan(sc, ic->ic_curchan);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ural_set_txantenna(sc, sc->tx_ant);
+ ural_set_rxantenna(sc, sc->rx_ant);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ ural_set_macaddr(sc, ic->ic_myaddr);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ ural_setup_tx_list(sc);
+
+ /* kick Rx */
+ tmp = RAL_DROP_PHY | RAL_DROP_CRC;
+ if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+ tmp |= RAL_DROP_CTL | RAL_DROP_BAD_VERSION;
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ tmp |= RAL_DROP_TODS;
+ if (!(ifp->if_flags & IFF_PROMISC))
+ tmp |= RAL_DROP_NOT_TO_ME;
+ }
+ ural_write(sc, RAL_TXRX_CSR2, tmp);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ usb2_transfer_start(sc->sc_xfer[URAL_BULK_RD]);
+ return;
+
+fail: ural_stop_task(pm);
+#undef N
+}
+
+static void
+ural_init(void *priv)
+{
+ struct ural_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ RAL_LOCK(sc);
+ ural_queue_command(sc, ural_init_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ RAL_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+ural_stop_task(struct usb2_proc_msg *pm)
+{
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ /*
+ * Drain all the transfers, if not already drained:
+ */
+ RAL_UNLOCK(sc);
+ usb2_transfer_drain(sc->sc_xfer[URAL_BULK_WR]);
+ usb2_transfer_drain(sc->sc_xfer[URAL_BULK_RD]);
+ RAL_LOCK(sc);
+
+ ural_unsetup_tx_list(sc);
+
+ /* disable Rx */
+ ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX);
+ /* reset ASIC and BBP (but won't reset MAC registers!) */
+ ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP);
+ /* wait a little */
+ ural_pause(sc, hz / 10);
+ ural_write(sc, RAL_MAC_CSR1, 0);
+}
+
+static int
+ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct ural_softc *sc = ifp->if_softc;
+
+ RAL_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return ENETDOWN;
+ }
+ if (sc->tx_nfree == 0) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ RAL_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return EIO;
+ }
+
+ ifp->if_opackets++;
+
+ if (params == NULL) {
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ */
+ if (ural_tx_mgt(sc, m, ni) != 0)
+ goto bad;
+ } else {
+ /*
+ * Caller supplied explicit parameters to use in
+ * sending the frame.
+ */
+ if (ural_tx_raw(sc, m, ni, params) != 0)
+ goto bad;
+ }
+ RAL_UNLOCK(sc);
+ return 0;
+bad:
+ ifp->if_oerrors++;
+ RAL_UNLOCK(sc);
+ ieee80211_free_node(ni);
+ return EIO; /* XXX */
+}
+
+static void
+ural_amrr_start(struct ural_softc *sc, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ural_vap *uvp = URAL_VAP(vap);
+
+ /* clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof sc->sta);
+
+ ieee80211_amrr_node_init(&uvp->amrr, &URAL_NODE(ni)->amn, ni);
+
+ usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp);
+}
+
+static void
+ural_amrr_timeout(void *arg)
+{
+ struct ural_vap *uvp = arg;
+ struct ural_softc *sc = uvp->sc;
+
+ ural_queue_command(sc, ural_amrr_task,
+ &uvp->amrr_task[0].hdr, &uvp->amrr_task[1].hdr);
+}
+
+static void
+ural_amrr_task(struct usb2_proc_msg *pm)
+{
+ struct ural_task *task = (struct ural_task *)pm;
+ struct ural_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ural_vap *uvp = URAL_VAP(vap);
+ struct ieee80211_node *ni = vap->iv_bss;
+ int ok, fail;
+
+ /* read and clear statistic registers (STA_CSR0 to STA_CSR10) */
+ ural_read_multi(sc, RAL_STA_CSR0, sc->sta, sizeof(sc->sta));
+
+ ok = sc->sta[7] + /* TX ok w/o retry */
+ sc->sta[8]; /* TX ok w/ retry */
+ fail = sc->sta[9]; /* TX retry-fail count */
+
+ ieee80211_amrr_tx_update(&URAL_NODE(ni)->amn,
+ ok+fail, ok, sc->sta[8] + fail);
+ (void) ieee80211_amrr_choose(ni, &URAL_NODE(ni)->amn);
+
+ ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */
+
+ usb2_callout_reset(&uvp->amrr_ch, hz, ural_amrr_timeout, uvp);
+}
+
+static int
+ural_pause(struct ural_softc *sc, int timeout)
+{
+ if (usb2_proc_is_gone(&sc->sc_tq))
+ return (1);
+
+ usb2_pause_mtx(&sc->sc_mtx, timeout);
+ return (0);
+}
+
+static void
+ural_queue_command(struct ural_softc *sc, usb2_proc_callback_t *fn,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct ural_task *task;
+
+ RAL_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (usb2_proc_is_gone(&sc->sc_tq)) {
+ DPRINTF("proc is gone\n");
+ return; /* nothing to do */
+ }
+ /*
+ * NOTE: The task cannot get executed before we drop the
+ * "sc_mtx" mutex. It is safe to update fields in the message
+ * structure after that the message got queued.
+ */
+ task = (struct ural_task *)
+ usb2_proc_msignal(&sc->sc_tq, t0, t1);
+
+ /* Setup callback and softc pointers */
+ task->hdr.pm_callback = fn;
+ task->sc = sc;
+
+ /*
+ * Init and stop must be synchronous!
+ */
+ if ((fn == ural_init_task) || (fn == ural_stop_task))
+ usb2_proc_mwait(&sc->sc_tq, t0, t1);
+}
+
diff --git a/sys/dev/usb/wlan/if_uralreg.h b/sys/dev/usb/wlan/if_uralreg.h
new file mode 100644
index 0000000..042cf5a
--- /dev/null
+++ b/sys/dev/usb/wlan/if_uralreg.h
@@ -0,0 +1,211 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005, 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RAL_NOISE_FLOOR -95
+#define RAL_RSSI_CORR 120
+
+#define RAL_RX_DESC_SIZE (sizeof (struct ural_rx_desc))
+#define RAL_TX_DESC_SIZE (sizeof (struct ural_tx_desc))
+#define RAL_FRAME_SIZE 0x780 /* NOTE: using 0x980 does not work */
+
+#define RAL_CONFIG_NO 1
+#define RAL_IFACE_INDEX 0
+
+#define RAL_VENDOR_REQUEST 0x01
+#define RAL_WRITE_MAC 0x02
+#define RAL_READ_MAC 0x03
+#define RAL_WRITE_MULTI_MAC 0x06
+#define RAL_READ_MULTI_MAC 0x07
+#define RAL_READ_EEPROM 0x09
+
+/*
+ * MAC registers.
+ */
+#define RAL_MAC_CSR0 0x0400 /* ASIC Version */
+#define RAL_MAC_CSR1 0x0402 /* System control */
+#define RAL_MAC_CSR2 0x0404 /* MAC addr0 */
+#define RAL_MAC_CSR3 0x0406 /* MAC addr1 */
+#define RAL_MAC_CSR4 0x0408 /* MAC addr2 */
+#define RAL_MAC_CSR5 0x040a /* BSSID0 */
+#define RAL_MAC_CSR6 0x040c /* BSSID1 */
+#define RAL_MAC_CSR7 0x040e /* BSSID2 */
+#define RAL_MAC_CSR8 0x0410 /* Max frame length */
+#define RAL_MAC_CSR9 0x0412 /* Timer control */
+#define RAL_MAC_CSR10 0x0414 /* Slot time */
+#define RAL_MAC_CSR11 0x0416 /* IFS */
+#define RAL_MAC_CSR12 0x0418 /* EIFS */
+#define RAL_MAC_CSR13 0x041a /* Power mode0 */
+#define RAL_MAC_CSR14 0x041c /* Power mode1 */
+#define RAL_MAC_CSR15 0x041e /* Power saving transition0 */
+#define RAL_MAC_CSR16 0x0420 /* Power saving transition1 */
+#define RAL_MAC_CSR17 0x0422 /* Power state control */
+#define RAL_MAC_CSR18 0x0424 /* Auto wake-up control */
+#define RAL_MAC_CSR19 0x0426 /* GPIO control */
+#define RAL_MAC_CSR20 0x0428 /* LED control0 */
+#define RAL_MAC_CSR22 0x042c /* XXX not documented */
+
+/*
+ * Tx/Rx Registers.
+ */
+#define RAL_TXRX_CSR0 0x0440 /* Security control */
+#define RAL_TXRX_CSR2 0x0444 /* Rx control */
+#define RAL_TXRX_CSR5 0x044a /* CCK Tx BBP ID0 */
+#define RAL_TXRX_CSR6 0x044c /* CCK Tx BBP ID1 */
+#define RAL_TXRX_CSR7 0x044e /* OFDM Tx BBP ID0 */
+#define RAL_TXRX_CSR8 0x0450 /* OFDM Tx BBP ID1 */
+#define RAL_TXRX_CSR10 0x0454 /* Auto responder control */
+#define RAL_TXRX_CSR11 0x0456 /* Auto responder basic rate */
+#define RAL_TXRX_CSR18 0x0464 /* Beacon interval */
+#define RAL_TXRX_CSR19 0x0466 /* Beacon/sync control */
+#define RAL_TXRX_CSR20 0x0468 /* Beacon alignment */
+#define RAL_TXRX_CSR21 0x046a /* XXX not documented */
+
+/*
+ * Security registers.
+ */
+#define RAL_SEC_CSR0 0x0480 /* Shared key 0, word 0 */
+
+/*
+ * PHY registers.
+ */
+#define RAL_PHY_CSR2 0x04c4 /* Tx MAC configuration */
+#define RAL_PHY_CSR4 0x04c8 /* Interface configuration */
+#define RAL_PHY_CSR5 0x04ca /* BBP Pre-Tx CCK */
+#define RAL_PHY_CSR6 0x04cc /* BBP Pre-Tx OFDM */
+#define RAL_PHY_CSR7 0x04ce /* BBP serial control */
+#define RAL_PHY_CSR8 0x04d0 /* BBP serial status */
+#define RAL_PHY_CSR9 0x04d2 /* RF serial control0 */
+#define RAL_PHY_CSR10 0x04d4 /* RF serial control1 */
+
+/*
+ * Statistics registers.
+ */
+#define RAL_STA_CSR0 0x04e0 /* FCS error */
+
+
+#define RAL_DISABLE_RX (1 << 0)
+#define RAL_DROP_CRC (1 << 1)
+#define RAL_DROP_PHY (1 << 2)
+#define RAL_DROP_CTL (1 << 3)
+#define RAL_DROP_NOT_TO_ME (1 << 4)
+#define RAL_DROP_TODS (1 << 5)
+#define RAL_DROP_BAD_VERSION (1 << 6)
+#define RAL_DROP_MULTICAST (1 << 9)
+#define RAL_DROP_BROADCAST (1 << 10)
+
+#define RAL_SHORT_PREAMBLE (1 << 2)
+
+#define RAL_RESET_ASIC (1 << 0)
+#define RAL_RESET_BBP (1 << 1)
+#define RAL_HOST_READY (1 << 2)
+
+#define RAL_ENABLE_TSF (1 << 0)
+#define RAL_ENABLE_TSF_SYNC(x) (((x) & 0x3) << 1)
+#define RAL_ENABLE_TBCN (1 << 3)
+#define RAL_ENABLE_BEACON_GENERATOR (1 << 4)
+
+#define RAL_RF_AWAKE (3 << 7)
+#define RAL_BBP_AWAKE (3 << 5)
+
+#define RAL_BBP_WRITE (1 << 15)
+#define RAL_BBP_BUSY (1 << 0)
+
+#define RAL_RF1_AUTOTUNE 0x08000
+#define RAL_RF3_AUTOTUNE 0x00040
+
+#define RAL_RF_2522 0x00
+#define RAL_RF_2523 0x01
+#define RAL_RF_2524 0x02
+#define RAL_RF_2525 0x03
+#define RAL_RF_2525E 0x04
+#define RAL_RF_2526 0x05
+/* dual-band RF */
+#define RAL_RF_5222 0x10
+
+#define RAL_BBP_VERSION 0
+#define RAL_BBP_TX 2
+#define RAL_BBP_RX 14
+
+#define RAL_BBP_ANTA 0x00
+#define RAL_BBP_DIVERSITY 0x01
+#define RAL_BBP_ANTB 0x02
+#define RAL_BBP_ANTMASK 0x03
+#define RAL_BBP_FLIPIQ 0x04
+
+#define RAL_JAPAN_FILTER 0x08
+
+struct ural_tx_desc {
+ uint32_t flags;
+#define RAL_TX_RETRY(x) ((x) << 4)
+#define RAL_TX_MORE_FRAG (1 << 8)
+#define RAL_TX_ACK (1 << 9)
+#define RAL_TX_TIMESTAMP (1 << 10)
+#define RAL_TX_OFDM (1 << 11)
+#define RAL_TX_NEWSEQ (1 << 12)
+
+#define RAL_TX_IFS_MASK 0x00006000
+#define RAL_TX_IFS_BACKOFF (0 << 13)
+#define RAL_TX_IFS_SIFS (1 << 13)
+#define RAL_TX_IFS_NEWBACKOFF (2 << 13)
+#define RAL_TX_IFS_NONE (3 << 13)
+
+ uint16_t wme;
+#define RAL_LOGCWMAX(x) (((x) & 0xf) << 12)
+#define RAL_LOGCWMIN(x) (((x) & 0xf) << 8)
+#define RAL_AIFSN(x) (((x) & 0x3) << 6)
+#define RAL_IVOFFSET(x) (((x) & 0x3f))
+
+ uint16_t reserved1;
+ uint8_t plcp_signal;
+ uint8_t plcp_service;
+#define RAL_PLCP_LENGEXT 0x80
+
+ uint8_t plcp_length_lo;
+ uint8_t plcp_length_hi;
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+struct ural_rx_desc {
+ uint32_t flags;
+#define RAL_RX_CRC_ERROR (1 << 5)
+#define RAL_RX_OFDM (1 << 6)
+#define RAL_RX_PHY_ERROR (1 << 7)
+
+ uint8_t rssi;
+ uint8_t rate;
+ uint16_t reserved;
+
+ uint32_t iv;
+ uint32_t eiv;
+} __packed;
+
+#define RAL_RF_LOBUSY (1 << 15)
+#define RAL_RF_BUSY (1 << 31)
+#define RAL_RF_20BIT (20 << 24)
+
+#define RAL_RF1 0
+#define RAL_RF2 2
+#define RAL_RF3 1
+#define RAL_RF4 3
+
+#define RAL_EEPROM_ADDRESS 0x0004
+#define RAL_EEPROM_TXPOWER 0x003c
+#define RAL_EEPROM_CONFIG0 0x0016
+#define RAL_EEPROM_BBP_BASE 0x001c
diff --git a/sys/dev/usb/wlan/if_uralvar.h b/sys/dev/usb/wlan/if_uralvar.h
new file mode 100644
index 0000000..c7e5469
--- /dev/null
+++ b/sys/dev/usb/wlan/if_uralvar.h
@@ -0,0 +1,155 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2005
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RAL_TX_LIST_COUNT 8
+
+#define URAL_SCAN_START 1
+#define URAL_SCAN_END 2
+#define URAL_SET_CHANNEL 3
+
+
+struct ural_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+};
+
+#define RAL_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct ural_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+};
+
+#define RAL_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+struct ural_softc;
+
+struct ural_task {
+ struct usb2_proc_msg hdr;
+ struct ural_softc *sc;
+};
+
+struct ural_tx_data {
+ STAILQ_ENTRY(ural_tx_data) next;
+ struct ural_softc *sc;
+ struct ural_tx_desc desc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ int rate;
+};
+typedef STAILQ_HEAD(, ural_tx_data) ural_txdhead;
+
+struct ural_node {
+ struct ieee80211_node ni;
+ struct ieee80211_amrr_node amn;
+};
+#define URAL_NODE(ni) ((struct ural_node *)(ni))
+
+struct ural_vap {
+ struct ieee80211vap vap;
+ struct ural_softc *sc;
+ struct ieee80211_beacon_offsets bo;
+ struct ieee80211_amrr amrr;
+ struct usb2_callout amrr_ch;
+ struct ural_task amrr_task[2];
+
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+};
+#define URAL_VAP(vap) ((struct ural_vap *)(vap))
+
+enum {
+ URAL_BULK_WR,
+ URAL_BULK_RD,
+ URAL_N_TRANSFER = 2,
+};
+
+struct ural_softc {
+ struct ifnet *sc_ifp;
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct usb2_process sc_tq;
+
+ const struct ieee80211_rate_table *sc_rates;
+
+ uint32_t asic_rev;
+ uint8_t rf_rev;
+
+ struct usb2_xfer *sc_xfer[URAL_N_TRANSFER];
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int sc_scan_action; /* should be an enum */
+ struct ural_task sc_synctask[2];
+ struct ural_task sc_task[2];
+ struct ural_task sc_promisctask[2];
+ struct ural_task sc_scantask[2];
+
+ struct ural_tx_data tx_data[RAL_TX_LIST_COUNT];
+ ural_txdhead tx_q;
+ ural_txdhead tx_free;
+ int tx_nfree;
+ struct ural_rx_desc sc_rx_desc;
+
+ struct mtx sc_mtx;
+
+ uint16_t sta[11];
+ uint32_t rf_regs[4];
+ uint8_t txpow[14];
+ uint8_t sc_bssid[6];
+
+ struct {
+ uint8_t val;
+ uint8_t reg;
+ } __packed bbp_prom[16];
+
+ int led_mode;
+ int hw_radio;
+ int rx_ant;
+ int tx_ant;
+ int nb_ant;
+
+ struct ural_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+
+ struct ural_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define RAL_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
diff --git a/sys/dev/usb/wlan/if_zyd.c b/sys/dev/usb/wlan/if_zyd.c
new file mode 100644
index 0000000..1d8b38f
--- /dev/null
+++ b/sys/dev/usb/wlan/if_zyd.c
@@ -0,0 +1,3121 @@
+/* $OpenBSD: if_zyd.c,v 1.52 2007/02/11 00:08:04 jsg Exp $ */
+/* $NetBSD: if_zyd.c,v 1.7 2007/06/21 04:04:29 kiyohara Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ZyDAS ZD1211/ZD1211B USB WLAN driver.
+ */
+
+#include "usbdevs.h"
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_mfunc.h>
+#include <dev/usb/usb_error.h>
+
+#include <dev/usb/usb_core.h>
+#include <dev/usb/usb_lookup.h>
+#include <dev/usb/usb_process.h>
+#include <dev/usb/usb_debug.h>
+#include <dev/usb/usb_request.h>
+#include <dev/usb/usb_busdma.h>
+#include <dev/usb/usb_util.h>
+
+#include <dev/usb/wlan/usb_wlan.h>
+#include <dev/usb/wlan/if_zydreg.h>
+#include <dev/usb/wlan/if_zydfw.h>
+
+#if USB_DEBUG
+static int zyd_debug = 0;
+
+SYSCTL_NODE(_hw_usb2, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd");
+SYSCTL_INT(_hw_usb2_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0,
+ "zyd debug level");
+
+enum {
+ ZYD_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
+ ZYD_DEBUG_RECV = 0x00000002, /* basic recv operation */
+ ZYD_DEBUG_RESET = 0x00000004, /* reset processing */
+ ZYD_DEBUG_INIT = 0x00000008, /* device init */
+ ZYD_DEBUG_TX_PROC = 0x00000010, /* tx ISR proc */
+ ZYD_DEBUG_RX_PROC = 0x00000020, /* rx ISR proc */
+ ZYD_DEBUG_STATE = 0x00000040, /* 802.11 state transitions */
+ ZYD_DEBUG_STAT = 0x00000080, /* statistic */
+ ZYD_DEBUG_FW = 0x00000100, /* firmware */
+ ZYD_DEBUG_CMD = 0x00000200, /* fw commands */
+ ZYD_DEBUG_ANY = 0xffffffff
+};
+#define DPRINTF(sc, m, fmt, ...) do { \
+ if (zyd_debug & (m)) \
+ printf("%s: " fmt, __func__, ## __VA_ARGS__); \
+} while (0)
+#else
+#define DPRINTF(sc, m, fmt, ...) do { \
+ (void) sc; \
+} while (0)
+#endif
+
+#define zyd_do_request(sc,req,data) \
+ usb2_do_request_proc((sc)->sc_udev, &(sc)->sc_tq, req, data, 0, NULL, 5000)
+
+static device_probe_t zyd_match;
+static device_attach_t zyd_attach;
+static device_detach_t zyd_detach;
+
+static usb2_callback_t zyd_intr_read_callback;
+static usb2_callback_t zyd_intr_write_callback;
+static usb2_callback_t zyd_bulk_read_callback;
+static usb2_callback_t zyd_bulk_write_callback;
+
+static usb2_proc_callback_t zyd_attach_post;
+static usb2_proc_callback_t zyd_task;
+static usb2_proc_callback_t zyd_scantask;
+static usb2_proc_callback_t zyd_multitask;
+static usb2_proc_callback_t zyd_init_task;
+static usb2_proc_callback_t zyd_stop_task;
+
+static struct ieee80211vap *zyd_vap_create(struct ieee80211com *,
+ const char name[IFNAMSIZ], int unit, int opmode,
+ int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static void zyd_vap_delete(struct ieee80211vap *);
+static void zyd_tx_free(struct zyd_tx_data *, int);
+static void zyd_setup_tx_list(struct zyd_softc *);
+static void zyd_unsetup_tx_list(struct zyd_softc *);
+static struct ieee80211_node *zyd_node_alloc(struct ieee80211vap *,
+ const uint8_t mac[IEEE80211_ADDR_LEN]);
+static int zyd_newstate(struct ieee80211vap *, enum ieee80211_state, int);
+static int zyd_cmd(struct zyd_softc *, uint16_t, const void *, int,
+ void *, int, int);
+static int zyd_read16(struct zyd_softc *, uint16_t, uint16_t *);
+static int zyd_read32(struct zyd_softc *, uint16_t, uint32_t *);
+static int zyd_write16(struct zyd_softc *, uint16_t, uint16_t);
+static int zyd_write32(struct zyd_softc *, uint16_t, uint32_t);
+static int zyd_rfwrite(struct zyd_softc *, uint32_t);
+static int zyd_lock_phy(struct zyd_softc *);
+static int zyd_unlock_phy(struct zyd_softc *);
+static int zyd_rf_attach(struct zyd_softc *, uint8_t);
+static const char *zyd_rf_name(uint8_t);
+static int zyd_hw_init(struct zyd_softc *);
+static int zyd_read_pod(struct zyd_softc *);
+static int zyd_read_eeprom(struct zyd_softc *);
+static int zyd_get_macaddr(struct zyd_softc *);
+static int zyd_set_macaddr(struct zyd_softc *, const uint8_t *);
+static int zyd_set_bssid(struct zyd_softc *, const uint8_t *);
+static int zyd_switch_radio(struct zyd_softc *, int);
+static int zyd_set_led(struct zyd_softc *, int, int);
+static void zyd_set_multi(struct zyd_softc *);
+static void zyd_update_mcast(struct ifnet *);
+static int zyd_set_rxfilter(struct zyd_softc *);
+static void zyd_set_chan(struct zyd_softc *, struct ieee80211_channel *);
+static int zyd_set_beacon_interval(struct zyd_softc *, int);
+static void zyd_rx_data(struct usb2_xfer *, int, uint16_t);
+static int zyd_tx_mgt(struct zyd_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static int zyd_tx_data(struct zyd_softc *, struct mbuf *,
+ struct ieee80211_node *);
+static void zyd_start(struct ifnet *);
+static int zyd_raw_xmit(struct ieee80211_node *, struct mbuf *,
+ const struct ieee80211_bpf_params *);
+static int zyd_ioctl(struct ifnet *, u_long, caddr_t);
+static void zyd_init(void *);
+static int zyd_loadfirmware(struct zyd_softc *);
+static void zyd_newassoc(struct ieee80211_node *, int);
+static void zyd_scan_start(struct ieee80211com *);
+static void zyd_scan_end(struct ieee80211com *);
+static void zyd_set_channel(struct ieee80211com *);
+static int zyd_rfmd_init(struct zyd_rf *);
+static int zyd_rfmd_switch_radio(struct zyd_rf *, int);
+static int zyd_rfmd_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2230_init(struct zyd_rf *);
+static int zyd_al2230_switch_radio(struct zyd_rf *, int);
+static int zyd_al2230_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2230_set_channel_b(struct zyd_rf *, uint8_t);
+static int zyd_al2230_init_b(struct zyd_rf *);
+static int zyd_al7230B_init(struct zyd_rf *);
+static int zyd_al7230B_switch_radio(struct zyd_rf *, int);
+static int zyd_al7230B_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_al2210_init(struct zyd_rf *);
+static int zyd_al2210_switch_radio(struct zyd_rf *, int);
+static int zyd_al2210_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_gct_init(struct zyd_rf *);
+static int zyd_gct_switch_radio(struct zyd_rf *, int);
+static int zyd_gct_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_maxim_init(struct zyd_rf *);
+static int zyd_maxim_switch_radio(struct zyd_rf *, int);
+static int zyd_maxim_set_channel(struct zyd_rf *, uint8_t);
+static int zyd_maxim2_init(struct zyd_rf *);
+static int zyd_maxim2_switch_radio(struct zyd_rf *, int);
+static int zyd_maxim2_set_channel(struct zyd_rf *, uint8_t);
+static void zyd_queue_command(struct zyd_softc *, usb2_proc_callback_t *,
+ struct usb2_proc_msg *, struct usb2_proc_msg *);
+
+static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY;
+static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB;
+
+/* various supported device vendors/products */
+#define ZYD_ZD1211 0
+#define ZYD_ZD1211B 1
+
+static const struct usb2_device_id zyd_devs[] = {
+ /* ZYD_ZD1211 */
+ {USB_VPI(USB_VENDOR_3COM2, USB_PRODUCT_3COM2_3CRUSB10075, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WL54, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_WL159G, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_CYBERTAN, USB_PRODUCT_CYBERTAN_TG54USB, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_DRAYTEK, USB_PRODUCT_DRAYTEK_VIGOR550, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GD, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54GZL, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54GZ, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_PLANEX3, USB_PRODUCT_PLANEX3_GWUS54MINI, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG760A, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_SENAO, USB_PRODUCT_SENAO_NUB8301, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_SWEEX, USB_PRODUCT_SWEEX_ZD1211, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_QUICKWLAN, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_1, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_TEKRAM, USB_PRODUCT_TEKRAM_ZD1211_2, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_TWINMOS, USB_PRODUCT_TWINMOS_G240, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_ALL0298V2, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB_A, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UB, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_WISTRONNEWEB, USB_PRODUCT_WISTRONNEWEB_UR055G, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_AG225H, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_ZYAIRG220, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G200V2, ZYD_ZD1211)},
+ {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G202, ZYD_ZD1211)},
+ /* ZYD_ZD1211B */
+ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_SMCWUSBG, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_ZD1211B, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_A9T_WIFI, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050_V4000, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_ZD1211B, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSBF54G, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_FIBERLINE, USB_PRODUCT_FIBERLINE_WL430U, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_KG54L, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_PHILIPS, USB_PRODUCT_PHILIPS_SNU5600, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GW_US54GXS, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_SAGEM, USB_PRODUCT_SAGEM_XG76NA, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_ZD1211B, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_UMEDIA, USB_PRODUCT_UMEDIA_TEW429UBC1, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_USR, USB_PRODUCT_USR_USR5423, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_VTECH, USB_PRODUCT_VTECH_ZD1211B, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_ZD1211B, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_ZYDAS, USB_PRODUCT_ZYDAS_ZD1211B, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_M202, ZYD_ZD1211B)},
+ {USB_VPI(USB_VENDOR_ZYXEL, USB_PRODUCT_ZYXEL_G220V2, ZYD_ZD1211B)},
+};
+
+static const struct usb2_config zyd_config[ZYD_N_TRANSFER] = {
+ [ZYD_BULK_WR] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = ZYD_MAX_TXBUFSZ,
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = zyd_bulk_write_callback,
+ .ep_index = 0,
+ .mh.timeout = 10000, /* 10 seconds */
+ },
+ [ZYD_BULK_RD] = {
+ .type = UE_BULK,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = ZYX_MAX_RXBUFSZ,
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = zyd_bulk_read_callback,
+ .ep_index = 0,
+ },
+ [ZYD_INTR_WR] = {
+ .type = UE_BULK_INTR,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_OUT,
+ .mh.bufsize = sizeof(struct zyd_cmd),
+ .mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+ .mh.callback = zyd_intr_write_callback,
+ .mh.timeout = 1000, /* 1 second */
+ .ep_index = 1,
+ },
+ [ZYD_INTR_RD] = {
+ .type = UE_INTERRUPT,
+ .endpoint = UE_ADDR_ANY,
+ .direction = UE_DIR_IN,
+ .mh.bufsize = sizeof(struct zyd_cmd),
+ .mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+ .mh.callback = zyd_intr_read_callback,
+ },
+};
+#define zyd_read16_m(sc, val, data) do { \
+ error = zyd_read16(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_write16_m(sc, val, data) do { \
+ error = zyd_write16(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_read32_m(sc, val, data) do { \
+ error = zyd_read32(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+#define zyd_write32_m(sc, val, data) do { \
+ error = zyd_write32(sc, val, data); \
+ if (error != 0) \
+ goto fail; \
+} while (0)
+
+static int
+zyd_match(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+
+ if (uaa->usb2_mode != USB_MODE_HOST)
+ return (ENXIO);
+ if (uaa->info.bConfigIndex != ZYD_CONFIG_INDEX)
+ return (ENXIO);
+ if (uaa->info.bIfaceIndex != ZYD_IFACE_INDEX)
+ return (ENXIO);
+
+ return (usb2_lookup_id_by_uaa(zyd_devs, sizeof(zyd_devs), uaa));
+}
+
+static int
+zyd_attach(device_t dev)
+{
+ struct usb2_attach_arg *uaa = device_get_ivars(dev);
+ struct zyd_softc *sc = device_get_softc(dev);
+ int error;
+ uint8_t iface_index;
+
+ if (uaa->info.bcdDevice < 0x4330) {
+ device_printf(dev, "device version mismatch: 0x%X "
+ "(only >= 43.30 supported)\n",
+ uaa->info.bcdDevice);
+ return (EINVAL);
+ }
+
+ device_set_usb2_desc(dev);
+ sc->sc_dev = dev;
+ sc->sc_udev = uaa->device;
+ sc->sc_macrev = USB_GET_DRIVER_INFO(uaa);
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev),
+ MTX_NETWORK_LOCK, MTX_DEF);
+
+ STAILQ_INIT(&sc->sc_rqh);
+
+ iface_index = ZYD_IFACE_INDEX;
+ error = usb2_transfer_setup(uaa->device,
+ &iface_index, sc->sc_xfer, zyd_config,
+ ZYD_N_TRANSFER, sc, &sc->sc_mtx);
+ if (error) {
+ device_printf(dev, "could not allocate USB transfers, "
+ "err=%s\n", usb2_errstr(error));
+ goto detach;
+ }
+ error = usb2_proc_create(&sc->sc_tq, &sc->sc_mtx,
+ device_get_nameunit(dev), USB_PRI_MED);
+ if (error) {
+ device_printf(dev, "could not setup config thread!\n");
+ goto detach;
+ }
+
+ /* fork rest of the attach code */
+ ZYD_LOCK(sc);
+ zyd_queue_command(sc, zyd_attach_post,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ ZYD_UNLOCK(sc);
+ return (0);
+
+detach:
+ zyd_detach(dev);
+ return (ENXIO); /* failure */
+}
+
+static void
+zyd_attach_post(struct usb2_proc_msg *pm)
+{
+ struct zyd_task *task = (struct zyd_task *)pm;
+ struct zyd_softc *sc = task->sc;
+ struct ifnet *ifp;
+ struct ieee80211com *ic;
+ int error;
+ uint8_t bands;
+
+ if ((error = zyd_get_macaddr(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ return;
+ }
+
+ ZYD_UNLOCK(sc);
+
+ ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
+ if (ifp == NULL) {
+ device_printf(sc->sc_dev, "can not if_alloc()\n");
+ ZYD_LOCK(sc);
+ return;
+ }
+ ifp->if_softc = sc;
+ if_initname(ifp, "zyd", device_get_unit(sc->sc_dev));
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = zyd_init;
+ ifp->if_ioctl = zyd_ioctl;
+ ifp->if_start = zyd_start;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ ic = ifp->if_l2com;
+ ic->ic_ifp = ifp;
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA;
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, sc->sc_bssid);
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_STA /* station mode */
+ | IEEE80211_C_MONITOR /* monitor mode */
+ | IEEE80211_C_SHPREAMBLE /* short preamble supported */
+ | IEEE80211_C_SHSLOT /* short slot time supported */
+ | IEEE80211_C_BGSCAN /* capable of bg scanning */
+ | IEEE80211_C_WPA /* 802.11i */
+ ;
+
+ bands = 0;
+ setbit(&bands, IEEE80211_MODE_11B);
+ setbit(&bands, IEEE80211_MODE_11G);
+ ieee80211_init_channels(ic, NULL, &bands);
+
+ ieee80211_ifattach(ic);
+ ic->ic_newassoc = zyd_newassoc;
+ ic->ic_raw_xmit = zyd_raw_xmit;
+ ic->ic_node_alloc = zyd_node_alloc;
+ ic->ic_scan_start = zyd_scan_start;
+ ic->ic_scan_end = zyd_scan_end;
+ ic->ic_set_channel = zyd_set_channel;
+
+ ic->ic_vap_create = zyd_vap_create;
+ ic->ic_vap_delete = zyd_vap_delete;
+ ic->ic_update_mcast = zyd_update_mcast;
+ ic->ic_update_promisc = zyd_update_mcast;
+
+ bpfattach(ifp, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap));
+ sc->sc_rxtap_len = sizeof(sc->sc_rxtap);
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(ZYD_RX_RADIOTAP_PRESENT);
+ sc->sc_txtap_len = sizeof(sc->sc_txtap);
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(ZYD_TX_RADIOTAP_PRESENT);
+
+ if (bootverbose)
+ ieee80211_announce(ic);
+
+ ZYD_LOCK(sc);
+}
+
+static int
+zyd_detach(device_t dev)
+{
+ struct zyd_softc *sc = device_get_softc(dev);
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic;
+
+ /* wait for any post attach or other command to complete */
+ usb2_proc_drain(&sc->sc_tq);
+
+ /* stop all USB transfers */
+ usb2_transfer_unsetup(sc->sc_xfer, ZYD_N_TRANSFER);
+ usb2_proc_free(&sc->sc_tq);
+
+ /* free TX list, if any */
+ zyd_unsetup_tx_list(sc);
+
+ if (ifp) {
+ ic = ifp->if_l2com;
+ bpfdetach(ifp);
+ ieee80211_ifdetach(ic);
+ if_free(ifp);
+ }
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+static struct ieee80211vap *
+zyd_vap_create(struct ieee80211com *ic,
+ const char name[IFNAMSIZ], int unit, int opmode, int flags,
+ const uint8_t bssid[IEEE80211_ADDR_LEN],
+ const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ struct zyd_vap *zvp;
+ struct ieee80211vap *vap;
+
+ if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
+ return (NULL);
+ zvp = (struct zyd_vap *) malloc(sizeof(struct zyd_vap),
+ M_80211_VAP, M_NOWAIT | M_ZERO);
+ if (zvp == NULL)
+ return (NULL);
+ vap = &zvp->vap;
+ /* enable s/w bmiss handling for sta mode */
+ ieee80211_vap_setup(ic, vap, name, unit, opmode,
+ flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
+
+ /* override state transition machine */
+ zvp->newstate = vap->iv_newstate;
+ vap->iv_newstate = zyd_newstate;
+
+ ieee80211_amrr_init(&zvp->amrr, vap,
+ IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
+ IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
+ 1000 /* 1 sec */);
+
+ /* complete setup */
+ ieee80211_vap_attach(vap, ieee80211_media_change,
+ ieee80211_media_status);
+ ic->ic_opmode = opmode;
+ return (vap);
+}
+
+static void
+zyd_vap_delete(struct ieee80211vap *vap)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+
+ ieee80211_amrr_cleanup(&zvp->amrr);
+ ieee80211_vap_detach(vap);
+ free(zvp, M_80211_VAP);
+}
+
+static void
+zyd_tx_free(struct zyd_tx_data *data, int txerr)
+{
+ struct zyd_softc *sc = data->sc;
+
+ if (data->m != NULL) {
+ if (data->m->m_flags & M_TXCB)
+ ieee80211_process_callback(data->ni, data->m,
+ txerr ? ETIMEDOUT : 0);
+ m_freem(data->m);
+ data->m = NULL;
+
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+}
+
+static void
+zyd_setup_tx_list(struct zyd_softc *sc)
+{
+ struct zyd_tx_data *data;
+ int i;
+
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ for (i = 0; i < ZYD_TX_LIST_CNT; i++) {
+ data = &sc->tx_data[i];
+
+ data->sc = sc;
+ STAILQ_INSERT_TAIL(&sc->tx_free, data, next);
+ sc->tx_nfree++;
+ }
+}
+
+static void
+zyd_unsetup_tx_list(struct zyd_softc *sc)
+{
+ struct zyd_tx_data *data;
+ int i;
+
+ /* make sure any subsequent use of the queues will fail */
+ sc->tx_nfree = 0;
+ STAILQ_INIT(&sc->tx_q);
+ STAILQ_INIT(&sc->tx_free);
+
+ /* free up all node references and mbufs */
+ for (i = 0; i < ZYD_TX_LIST_CNT; i++) {
+ data = &sc->tx_data[i];
+
+ if (data->m != NULL) {
+ m_freem(data->m);
+ data->m = NULL;
+ }
+ if (data->ni != NULL) {
+ ieee80211_free_node(data->ni);
+ data->ni = NULL;
+ }
+ }
+}
+
+/* ARGUSED */
+static struct ieee80211_node *
+zyd_node_alloc(struct ieee80211vap *vap __unused,
+ const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
+{
+ struct zyd_node *zn;
+
+ zn = malloc(sizeof(struct zyd_node), M_80211_NODE, M_NOWAIT | M_ZERO);
+ return (zn != NULL) ? (&zn->ni) : (NULL);
+}
+
+static void
+zyd_task(struct usb2_proc_msg *pm)
+{
+ struct zyd_task *task = (struct zyd_task *)pm;
+ struct zyd_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+ int error;
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_AUTH:
+ zyd_set_chan(sc, ic->ic_curchan);
+ break;
+ case IEEE80211_S_RUN:
+ if (vap->iv_opmode == IEEE80211_M_MONITOR)
+ break;
+
+ /* turn link LED on */
+ error = zyd_set_led(sc, ZYD_LED1, 1);
+ if (error != 0)
+ goto fail;
+
+ /* make data LED blink upon Tx */
+ zyd_write32_m(sc, sc->sc_fwbase + ZYD_FW_LINK_STATUS, 1);
+
+ IEEE80211_ADDR_COPY(sc->sc_bssid, ni->ni_bssid);
+ zyd_set_bssid(sc, sc->sc_bssid);
+ break;
+ default:
+ break;
+ }
+fail:
+ ZYD_UNLOCK(sc);
+ IEEE80211_LOCK(ic);
+ zvp->newstate(vap, sc->sc_state, sc->sc_arg);
+ if (vap->iv_newstate_cb != NULL)
+ vap->iv_newstate_cb(vap, sc->sc_state, sc->sc_arg);
+ IEEE80211_UNLOCK(ic);
+ ZYD_LOCK(sc);
+}
+
+static int
+zyd_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+{
+ struct zyd_vap *zvp = ZYD_VAP(vap);
+ struct ieee80211com *ic = vap->iv_ic;
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ DPRINTF(sc, ZYD_DEBUG_STATE, "%s: %s -> %s\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[nstate]);
+
+ ZYD_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+ ZYD_UNLOCK(sc);
+
+ if (nstate == IEEE80211_S_INIT) {
+ zvp->newstate(vap, nstate, arg);
+ return (0);
+ } else {
+ ZYD_LOCK(sc);
+ zyd_queue_command(sc, zyd_task, &sc->sc_task[0].hdr,
+ &sc->sc_task[1].hdr);
+ ZYD_UNLOCK(sc);
+ return (EINPROGRESS);
+ }
+}
+
+/*
+ * Callback handler for interrupt transfer
+ */
+static void
+zyd_intr_read_callback(struct usb2_xfer *xfer)
+{
+ struct zyd_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni;
+ struct zyd_cmd *cmd = &sc->sc_ibuf;
+ int datalen;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_copy_out(xfer->frbuffers, 0, cmd, sizeof(*cmd));
+
+ switch (le16toh(cmd->code)) {
+ case ZYD_NOTIF_RETRYSTATUS:
+ {
+ struct zyd_notif_retry *retry =
+ (struct zyd_notif_retry *)cmd->data;
+
+ DPRINTF(sc, ZYD_DEBUG_TX_PROC,
+ "retry intr: rate=0x%x addr=%s count=%d (0x%x)\n",
+ le16toh(retry->rate), ether_sprintf(retry->macaddr),
+ le16toh(retry->count)&0xff, le16toh(retry->count));
+
+ /*
+ * Find the node to which the packet was sent and
+ * update its retry statistics. In BSS mode, this node
+ * is the AP we're associated to so no lookup is
+ * actually needed.
+ */
+ ni = ieee80211_find_txnode(vap, retry->macaddr);
+ if (ni != NULL) {
+ ieee80211_amrr_tx_complete(&ZYD_NODE(ni)->amn,
+ IEEE80211_AMRR_FAILURE, 1);
+ ieee80211_free_node(ni);
+ }
+ if (le16toh(retry->count) & 0x100)
+ ifp->if_oerrors++; /* too many retries */
+ break;
+ }
+ case ZYD_NOTIF_IORD:
+ {
+ struct zyd_rq *rqp;
+
+ if (le16toh(*(uint16_t *)cmd->data) == ZYD_CR_INTERRUPT)
+ break; /* HMAC interrupt */
+
+ datalen = xfer->actlen - sizeof(cmd->code);
+ datalen -= 2; /* XXX: padding? */
+
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) {
+ int i, cnt;
+
+ if (rqp->olen != datalen)
+ continue;
+ cnt = rqp->olen / sizeof(struct zyd_pair);
+ for (i = 0; i < cnt; i++) {
+ if (*(((const uint16_t *)rqp->idata) + i) !=
+ (((struct zyd_pair *)cmd->data) + i)->reg)
+ break;
+ }
+ if (i != cnt)
+ continue;
+ /* copy answer into caller-supplied buffer */
+ bcopy(cmd->data, rqp->odata, rqp->olen);
+ DPRINTF(sc, ZYD_DEBUG_CMD,
+ "command %p complete, data = %*D \n",
+ rqp, rqp->olen, rqp->odata, ":");
+ wakeup(rqp); /* wakeup caller */
+ break;
+ }
+ if (rqp == NULL) {
+ device_printf(sc->sc_dev,
+ "unexpected IORD notification %*D\n",
+ datalen, cmd->data, ":");
+ }
+ break;
+ }
+ default:
+ device_printf(sc->sc_dev, "unknown notification %x\n",
+ le16toh(cmd->code));
+ }
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_CMD, "error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static void
+zyd_intr_write_callback(struct usb2_xfer *xfer)
+{
+ struct zyd_softc *sc = xfer->priv_sc;
+ struct zyd_rq *rqp;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ rqp = xfer->priv_fifo;
+ DPRINTF(sc, ZYD_DEBUG_CMD, "command %p transferred\n", rqp);
+ if ((rqp->flags & ZYD_CMD_FLAG_READ) == 0)
+ wakeup(rqp); /* wakeup caller */
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ STAILQ_FOREACH(rqp, &sc->sc_rqh, rq) {
+ if (rqp->flags & ZYD_CMD_FLAG_SENT)
+ continue;
+
+ usb2_copy_in(xfer->frbuffers, 0, rqp->cmd, rqp->ilen);
+
+ xfer->frlengths[0] = rqp->ilen;
+ xfer->priv_fifo = rqp;
+ rqp->flags |= ZYD_CMD_FLAG_SENT;
+ usb2_start_hardware(xfer);
+ break;
+ }
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_ANY, "error = %s\n",
+ usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static int
+zyd_cmd(struct zyd_softc *sc, uint16_t code, const void *idata, int ilen,
+ void *odata, int olen, int flags)
+{
+ struct zyd_cmd cmd;
+ struct zyd_rq rq;
+ int error;
+
+ if (ilen > sizeof(cmd.data))
+ return (EINVAL);
+
+ if (usb2_proc_is_gone(&sc->sc_tq))
+ return (ENXIO);
+
+ cmd.code = htole16(code);
+ bcopy(idata, cmd.data, ilen);
+ DPRINTF(sc, ZYD_DEBUG_CMD, "sending cmd %p = %*D\n",
+ &rq, ilen, idata, ":");
+
+ rq.cmd = &cmd;
+ rq.idata = idata;
+ rq.odata = odata;
+ rq.ilen = sizeof(uint16_t) + ilen;
+ rq.olen = olen;
+ rq.flags = flags;
+ STAILQ_INSERT_TAIL(&sc->sc_rqh, &rq, rq);
+ usb2_transfer_start(sc->sc_xfer[ZYD_INTR_RD]);
+ usb2_transfer_start(sc->sc_xfer[ZYD_INTR_WR]);
+
+ /* wait at most one second for command reply */
+ error = mtx_sleep(&rq, &sc->sc_mtx, 0 , "zydcmd", hz);
+ if (error)
+ device_printf(sc->sc_dev, "command timeout\n");
+ STAILQ_REMOVE(&sc->sc_rqh, &rq, zyd_rq, rq);
+ DPRINTF(sc, ZYD_DEBUG_CMD, "finsihed cmd %p, error = %d \n",
+ &rq, error);
+
+ return (error);
+}
+
+static int
+zyd_read16(struct zyd_softc *sc, uint16_t reg, uint16_t *val)
+{
+ struct zyd_pair tmp;
+ int error;
+
+ reg = htole16(reg);
+ error = zyd_cmd(sc, ZYD_CMD_IORD, &reg, sizeof(reg), &tmp, sizeof(tmp),
+ ZYD_CMD_FLAG_READ);
+ if (error == 0)
+ *val = le16toh(tmp.val);
+ return (error);
+}
+
+static int
+zyd_read32(struct zyd_softc *sc, uint16_t reg, uint32_t *val)
+{
+ struct zyd_pair tmp[2];
+ uint16_t regs[2];
+ int error;
+
+ regs[0] = htole16(ZYD_REG32_HI(reg));
+ regs[1] = htole16(ZYD_REG32_LO(reg));
+ error = zyd_cmd(sc, ZYD_CMD_IORD, regs, sizeof(regs), tmp, sizeof(tmp),
+ ZYD_CMD_FLAG_READ);
+ if (error == 0)
+ *val = le16toh(tmp[0].val) << 16 | le16toh(tmp[1].val);
+ return (error);
+}
+
+static int
+zyd_write16(struct zyd_softc *sc, uint16_t reg, uint16_t val)
+{
+ struct zyd_pair pair;
+
+ pair.reg = htole16(reg);
+ pair.val = htole16(val);
+
+ return zyd_cmd(sc, ZYD_CMD_IOWR, &pair, sizeof(pair), NULL, 0, 0);
+}
+
+static int
+zyd_write32(struct zyd_softc *sc, uint16_t reg, uint32_t val)
+{
+ struct zyd_pair pair[2];
+
+ pair[0].reg = htole16(ZYD_REG32_HI(reg));
+ pair[0].val = htole16(val >> 16);
+ pair[1].reg = htole16(ZYD_REG32_LO(reg));
+ pair[1].val = htole16(val & 0xffff);
+
+ return zyd_cmd(sc, ZYD_CMD_IOWR, pair, sizeof(pair), NULL, 0, 0);
+}
+
+static int
+zyd_rfwrite(struct zyd_softc *sc, uint32_t val)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+ struct zyd_rfwrite_cmd req;
+ uint16_t cr203;
+ int error, i;
+
+ zyd_read16_m(sc, ZYD_CR203, &cr203);
+ cr203 &= ~(ZYD_RF_IF_LE | ZYD_RF_CLK | ZYD_RF_DATA);
+
+ req.code = htole16(2);
+ req.width = htole16(rf->width);
+ for (i = 0; i < rf->width; i++) {
+ req.bit[i] = htole16(cr203);
+ if (val & (1 << (rf->width - 1 - i)))
+ req.bit[i] |= htole16(ZYD_RF_DATA);
+ }
+ error = zyd_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + 2 * rf->width, NULL, 0, 0);
+fail:
+ return (error);
+}
+
+static int
+zyd_rfwrite_cr(struct zyd_softc *sc, uint32_t val)
+{
+ int error;
+
+ zyd_write16_m(sc, ZYD_CR244, (val >> 16) & 0xff);
+ zyd_write16_m(sc, ZYD_CR243, (val >> 8) & 0xff);
+ zyd_write16_m(sc, ZYD_CR242, (val >> 0) & 0xff);
+fail:
+ return (error);
+}
+
+static int
+zyd_lock_phy(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_MISC, &tmp);
+ tmp &= ~ZYD_UNLOCK_PHY_REGS;
+ zyd_write32_m(sc, ZYD_MAC_MISC, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_unlock_phy(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_MISC, &tmp);
+ tmp |= ZYD_UNLOCK_PHY_REGS;
+ zyd_write32_m(sc, ZYD_MAC_MISC, tmp);
+fail:
+ return (error);
+}
+
+/*
+ * RFMD RF methods.
+ */
+static int
+zyd_rfmd_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_RFMD_PHY;
+ static const uint32_t rfini[] = ZYD_RFMD_RF;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++) {
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+ }
+
+ /* init RFMD radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_rfmd_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ zyd_write16_m(sc, ZYD_CR10, on ? 0x89 : 0x15);
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x81);
+fail:
+ return (error);
+}
+
+static int
+zyd_rfmd_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_RFMD_CHANTABLE;
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+fail:
+ return (error);
+}
+
+/*
+ * AL2230 RF methods.
+ */
+static int
+zyd_al2230_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY;
+ static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT;
+ static const struct zyd_phy_pair phypll[] = {
+ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f },
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 }
+ };
+ static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1;
+ static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2;
+ static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) {
+ for (i = 0; i < N(phy2230s); i++)
+ zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val);
+ }
+
+ /* init AL2230 radio */
+ for (i = 0; i < N(rfini1); i++) {
+ error = zyd_rfwrite(sc, rfini1[i]);
+ if (error != 0)
+ goto fail;
+ }
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0)
+ error = zyd_rfwrite(sc, 0x000824);
+ else
+ error = zyd_rfwrite(sc, 0x0005a4);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < N(rfini2); i++) {
+ error = zyd_rfwrite(sc, rfini2[i]);
+ if (error != 0)
+ goto fail;
+ }
+
+ for (i = 0; i < N(phypll); i++)
+ zyd_write16_m(sc, phypll[i].reg, phypll[i].val);
+
+ for (i = 0; i < N(rfini3); i++) {
+ error = zyd_rfwrite(sc, rfini3[i]);
+ if (error != 0)
+ goto fail;
+ }
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_fini(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1;
+
+ for (i = 0; i < N(phy); i++)
+ zyd_write16_m(sc, phy[i].reg, phy[i].val);
+
+ if (sc->sc_newphy != 0)
+ zyd_write16_m(sc, ZYD_CR9, 0xe1);
+
+ zyd_write16_m(sc, ZYD_CR203, 0x6);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_init_b(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1;
+ static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2;
+ static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3;
+ static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B;
+ static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1;
+ static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2;
+ static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3;
+ static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE;
+ int i, error;
+
+ for (i = 0; i < N(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0) {
+ for (i = 0; i < N(phy2230s); i++)
+ zyd_write16_m(sc, phy2230s[i].reg, phy2230s[i].val);
+ }
+
+ for (i = 0; i < 3; i++) {
+ error = zyd_rfwrite_cr(sc, zyd_al2230_chtable[0][i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < N(rfini_part1); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part1[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ if (sc->sc_rfrev == ZYD_RF_AL2230S || sc->sc_al2230s != 0)
+ error = zyd_rfwrite(sc, 0x241000);
+ else
+ error = zyd_rfwrite(sc, 0x25a000);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < N(rfini_part2); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part2[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < N(phy2); i++)
+ zyd_write16_m(sc, phy2[i].reg, phy2[i].val);
+
+ for (i = 0; i < N(rfini_part3); i++) {
+ error = zyd_rfwrite_cr(sc, rfini_part3[i]);
+ if (error != 0)
+ return (error);
+ }
+
+ for (i = 0; i < N(phy3); i++)
+ zyd_write16_m(sc, phy3[i].reg, phy3[i].val);
+
+ error = zyd_al2230_fini(rf);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_switch_radio(struct zyd_rf *rf, int on)
+{
+ struct zyd_softc *sc = rf->rf_sc;
+ int error, on251 = (sc->sc_macrev == ZYD_ZD1211) ? 0x3f : 0x7f;
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251, on ? on251 : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_al2230_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = {
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 },
+ };
+ static const struct {
+ uint32_t r1, r2, r3;
+ } rfprog[] = ZYD_AL2230_CHANTABLE;
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r3);
+ if (error != 0)
+ goto fail;
+
+ for (i = 0; i < N(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2230_set_channel_b(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1;
+ static const struct {
+ uint32_t r1, r2, r3;
+ } rfprog[] = ZYD_AL2230_CHANTABLE_B;
+
+ for (i = 0; i < N(phy1); i++)
+ zyd_write16_m(sc, phy1[i].reg, phy1[i].val);
+
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite_cr(sc, rfprog[chan - 1].r3);
+ if (error != 0)
+ goto fail;
+ error = zyd_al2230_fini(rf);
+fail:
+ return (error);
+#undef N
+}
+
+#define ZYD_AL2230_PHY_BANDEDGE6 \
+{ \
+ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \
+ { ZYD_CR47, 0x1e } \
+}
+
+static int
+zyd_al2230_bandedge6(struct zyd_rf *rf, struct ieee80211_channel *c)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ int error = 0, i;
+ struct zyd_softc *sc = rf->rf_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6;
+ int chan = ieee80211_chan2ieee(ic, c);
+
+ if (chan == 1 || chan == 11)
+ r[0].val = 0x12;
+
+ for (i = 0; i < N(r); i++)
+ zyd_write16_m(sc, r[i].reg, r[i].val);
+fail:
+ return (error);
+#undef N
+}
+
+/*
+ * AL7230B RF methods.
+ */
+static int
+zyd_al7230B_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini_1[] = ZYD_AL7230B_PHY_1;
+ static const struct zyd_phy_pair phyini_2[] = ZYD_AL7230B_PHY_2;
+ static const struct zyd_phy_pair phyini_3[] = ZYD_AL7230B_PHY_3;
+ static const uint32_t rfini_1[] = ZYD_AL7230B_RF_1;
+ static const uint32_t rfini_2[] = ZYD_AL7230B_RF_2;
+ int i, error;
+
+ /* for AL7230B, PHY and RF need to be initialized in "phases" */
+
+ /* init RF-dependent PHY registers, part one */
+ for (i = 0; i < N(phyini_1); i++)
+ zyd_write16_m(sc, phyini_1[i].reg, phyini_1[i].val);
+
+ /* init AL7230B radio, part one */
+ for (i = 0; i < N(rfini_1); i++) {
+ if ((error = zyd_rfwrite(sc, rfini_1[i])) != 0)
+ return (error);
+ }
+ /* init RF-dependent PHY registers, part two */
+ for (i = 0; i < N(phyini_2); i++)
+ zyd_write16_m(sc, phyini_2[i].reg, phyini_2[i].val);
+
+ /* init AL7230B radio, part two */
+ for (i = 0; i < N(rfini_2); i++) {
+ if ((error = zyd_rfwrite(sc, rfini_2[i])) != 0)
+ return (error);
+ }
+ /* init RF-dependent PHY registers, part three */
+ for (i = 0; i < N(phyini_3); i++)
+ zyd_write16_m(sc, phyini_3[i].reg, phyini_3[i].val);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al7230B_switch_radio(struct zyd_rf *rf, int on)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+
+ zyd_write16_m(sc, ZYD_CR11, on ? 0x00 : 0x04);
+ zyd_write16_m(sc, ZYD_CR251, on ? 0x3f : 0x2f);
+fail:
+ return (error);
+}
+
+static int
+zyd_al7230B_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_AL7230B_CHANTABLE;
+ static const uint32_t rfsc[] = ZYD_AL7230B_RF_SETCHANNEL;
+ int i, error;
+
+ zyd_write16_m(sc, ZYD_CR240, 0x57);
+ zyd_write16_m(sc, ZYD_CR251, 0x2f);
+
+ for (i = 0; i < N(rfsc); i++) {
+ if ((error = zyd_rfwrite(sc, rfsc[i])) != 0)
+ return (error);
+ }
+
+ zyd_write16_m(sc, ZYD_CR128, 0x14);
+ zyd_write16_m(sc, ZYD_CR129, 0x12);
+ zyd_write16_m(sc, ZYD_CR130, 0x10);
+ zyd_write16_m(sc, ZYD_CR38, 0x38);
+ zyd_write16_m(sc, ZYD_CR136, 0xdf);
+
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, 0x3c9000);
+ if (error != 0)
+ goto fail;
+
+ zyd_write16_m(sc, ZYD_CR251, 0x3f);
+ zyd_write16_m(sc, ZYD_CR203, 0x06);
+ zyd_write16_m(sc, ZYD_CR240, 0x08);
+fail:
+ return (error);
+#undef N
+}
+
+/*
+ * AL2210 RF methods.
+ */
+static int
+zyd_al2210_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_AL2210_PHY;
+ static const uint32_t rfini[] = ZYD_AL2210_RF;
+ uint32_t tmp;
+ int i, error;
+
+ zyd_write32_m(sc, ZYD_CR18, 2);
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ /* init AL2210 radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_write32_m(sc, ZYD_CR18, 3);
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_al2210_switch_radio(struct zyd_rf *rf, int on)
+{
+ /* vendor driver does nothing for this RF chip */
+
+ return (0);
+}
+
+static int
+zyd_al2210_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const uint32_t rfprog[] = ZYD_AL2210_CHANTABLE;
+ uint32_t tmp;
+
+ zyd_write32_m(sc, ZYD_CR18, 2);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+ zyd_read32_m(sc, ZYD_CR_RADIO_PD, &tmp);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp & ~1);
+ zyd_write32_m(sc, ZYD_CR_RADIO_PD, tmp | 1);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x05);
+ zyd_write32_m(sc, ZYD_CR_RFCFG, 0x00);
+ zyd_write16_m(sc, ZYD_CR47, 0x1e);
+
+ /* actually set the channel */
+ error = zyd_rfwrite(sc, rfprog[chan - 1]);
+ if (error != 0)
+ goto fail;
+
+ zyd_write32_m(sc, ZYD_CR18, 3);
+fail:
+ return (error);
+}
+
+/*
+ * GCT RF methods.
+ */
+static int
+zyd_gct_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_GCT_PHY;
+ static const uint32_t rfini[] = ZYD_GCT_RF;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ /* init cgt radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_gct_switch_radio(struct zyd_rf *rf, int on)
+{
+ /* vendor driver does nothing for this RF chip */
+
+ return (0);
+}
+
+static int
+zyd_gct_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+ int error;
+ struct zyd_softc *sc = rf->rf_sc;
+ static const uint32_t rfprog[] = ZYD_GCT_CHANTABLE;
+
+ error = zyd_rfwrite(sc, 0x1c0000);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1]);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, 0x1c0008);
+fail:
+ return (error);
+}
+
+/*
+ * Maxim RF methods.
+ */
+static int
+zyd_maxim_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM_RF;
+ uint16_t tmp;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* init maxim radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_maxim_switch_radio(struct zyd_rf *rf, int on)
+{
+
+ /* vendor driver does nothing for this RF chip */
+ return (0);
+}
+
+static int
+zyd_maxim_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM_RF;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_MAXIM_CHANTABLE;
+ uint16_t tmp;
+ int i, error;
+
+ /*
+ * Do the same as we do when initializing it, except for the channel
+ * values coming from the two channel tables.
+ */
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* first two values taken from the chantables */
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+ /* init maxim radio - skipping the two first values */
+ for (i = 2; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+/*
+ * Maxim2 RF methods.
+ */
+static int
+zyd_maxim2_init(struct zyd_rf *rf)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM2_RF;
+ uint16_t tmp;
+ int i, error;
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* init maxim2 radio */
+ for (i = 0; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_maxim2_switch_radio(struct zyd_rf *rf, int on)
+{
+
+ /* vendor driver does nothing for this RF chip */
+ return (0);
+}
+
+static int
+zyd_maxim2_set_channel(struct zyd_rf *rf, uint8_t chan)
+{
+#define N(a) (sizeof(a) / sizeof((a)[0]))
+ struct zyd_softc *sc = rf->rf_sc;
+ static const struct zyd_phy_pair phyini[] = ZYD_MAXIM2_PHY;
+ static const uint32_t rfini[] = ZYD_MAXIM2_RF;
+ static const struct {
+ uint32_t r1, r2;
+ } rfprog[] = ZYD_MAXIM2_CHANTABLE;
+ uint16_t tmp;
+ int i, error;
+
+ /*
+ * Do the same as we do when initializing it, except for the channel
+ * values coming from the two channel tables.
+ */
+
+ /* init RF-dependent PHY registers */
+ for (i = 0; i < N(phyini); i++)
+ zyd_write16_m(sc, phyini[i].reg, phyini[i].val);
+
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp & ~(1 << 4));
+
+ /* first two values taken from the chantables */
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r1);
+ if (error != 0)
+ goto fail;
+ error = zyd_rfwrite(sc, rfprog[chan - 1].r2);
+ if (error != 0)
+ goto fail;
+
+ /* init maxim2 radio - skipping the two first values */
+ for (i = 2; i < N(rfini); i++) {
+ if ((error = zyd_rfwrite(sc, rfini[i])) != 0)
+ return (error);
+ }
+ zyd_read16_m(sc, ZYD_CR203, &tmp);
+ zyd_write16_m(sc, ZYD_CR203, tmp | (1 << 4));
+fail:
+ return (error);
+#undef N
+}
+
+static int
+zyd_rf_attach(struct zyd_softc *sc, uint8_t type)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+
+ rf->rf_sc = sc;
+
+ switch (type) {
+ case ZYD_RF_RFMD:
+ rf->init = zyd_rfmd_init;
+ rf->switch_radio = zyd_rfmd_switch_radio;
+ rf->set_channel = zyd_rfmd_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL2230:
+ case ZYD_RF_AL2230S:
+ if (sc->sc_macrev == ZYD_ZD1211B) {
+ rf->init = zyd_al2230_init_b;
+ rf->set_channel = zyd_al2230_set_channel_b;
+ } else {
+ rf->init = zyd_al2230_init;
+ rf->set_channel = zyd_al2230_set_channel;
+ }
+ rf->switch_radio = zyd_al2230_switch_radio;
+ rf->bandedge6 = zyd_al2230_bandedge6;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL7230B:
+ rf->init = zyd_al7230B_init;
+ rf->switch_radio = zyd_al7230B_switch_radio;
+ rf->set_channel = zyd_al7230B_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_AL2210:
+ rf->init = zyd_al2210_init;
+ rf->switch_radio = zyd_al2210_switch_radio;
+ rf->set_channel = zyd_al2210_set_channel;
+ rf->width = 24; /* 24-bit RF values */
+ break;
+ case ZYD_RF_GCT:
+ rf->init = zyd_gct_init;
+ rf->switch_radio = zyd_gct_switch_radio;
+ rf->set_channel = zyd_gct_set_channel;
+ rf->width = 21; /* 21-bit RF values */
+ break;
+ case ZYD_RF_MAXIM_NEW:
+ rf->init = zyd_maxim_init;
+ rf->switch_radio = zyd_maxim_switch_radio;
+ rf->set_channel = zyd_maxim_set_channel;
+ rf->width = 18; /* 18-bit RF values */
+ break;
+ case ZYD_RF_MAXIM_NEW2:
+ rf->init = zyd_maxim2_init;
+ rf->switch_radio = zyd_maxim2_switch_radio;
+ rf->set_channel = zyd_maxim2_set_channel;
+ rf->width = 18; /* 18-bit RF values */
+ break;
+ default:
+ device_printf(sc->sc_dev,
+ "sorry, radio \"%s\" is not supported yet\n",
+ zyd_rf_name(type));
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static const char *
+zyd_rf_name(uint8_t type)
+{
+ static const char * const zyd_rfs[] = {
+ "unknown", "unknown", "UW2451", "UCHIP", "AL2230",
+ "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT",
+ "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2",
+ "PHILIPS"
+ };
+
+ return zyd_rfs[(type > 15) ? 0 : type];
+}
+
+static int
+zyd_hw_init(struct zyd_softc *sc)
+{
+ int error;
+ const struct zyd_phy_pair *phyp;
+ struct zyd_rf *rf = &sc->sc_rf;
+ uint16_t val;
+
+ /* specify that the plug and play is finished */
+ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1);
+ zyd_read16_m(sc, ZYD_FIRMWARE_BASE_ADDR, &sc->sc_fwbase);
+ DPRINTF(sc, ZYD_DEBUG_FW, "firmware base address=0x%04x\n",
+ sc->sc_fwbase);
+
+ /* retrieve firmware revision number */
+ zyd_read16_m(sc, sc->sc_fwbase + ZYD_FW_FIRMWARE_REV, &sc->sc_fwrev);
+ zyd_write32_m(sc, ZYD_CR_GPI_EN, 0);
+ zyd_write32_m(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f);
+ /* set mandatory rates - XXX assumes 802.11b/g */
+ zyd_write32_m(sc, ZYD_MAC_MAN_RATE, 0x150f);
+
+ /* disable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0);
+
+ if ((error = zyd_read_pod(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ goto fail;
+ }
+
+ /* PHY init (resetting) */
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ phyp = (sc->sc_macrev == ZYD_ZD1211B) ? zyd_def_phyB : zyd_def_phy;
+ for (; phyp->reg != 0; phyp++)
+ zyd_write16_m(sc, phyp->reg, phyp->val);
+ if (sc->sc_macrev == ZYD_ZD1211 && sc->sc_fix_cr157 != 0) {
+ zyd_read16_m(sc, ZYD_EEPROM_PHY_REG, &val);
+ zyd_write32_m(sc, ZYD_CR157, val >> 8);
+ }
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ /* HMAC init */
+ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000020);
+ zyd_write32_m(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808);
+ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_GHTBL, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_GHTBH, 0x80000000);
+ zyd_write32_m(sc, ZYD_MAC_MISC, 0x000000a4);
+ zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x0000007f);
+ zyd_write32_m(sc, ZYD_MAC_BCNCFG, 0x00f00401);
+ zyd_write32_m(sc, ZYD_MAC_PHY_DELAY2, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_ACK_EXT, 0x00000080);
+ zyd_write32_m(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000);
+ zyd_write32_m(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100);
+ zyd_write32_m(sc, ZYD_CR_RX_PE_DELAY, 0x00000070);
+ zyd_write32_m(sc, ZYD_CR_PS_CTRL, 0x10000000);
+ zyd_write32_m(sc, ZYD_MAC_RTSCTSRATE, 0x02030203);
+ zyd_write32_m(sc, ZYD_MAC_AFTER_PNP, 1);
+ zyd_write32_m(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114);
+ zyd_write32_m(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032);
+ zyd_write32_m(sc, ZYD_MAC_CAM_MODE, 0x3);
+
+ if (sc->sc_macrev == ZYD_ZD1211) {
+ zyd_write32_m(sc, ZYD_MAC_RETRY, 0x00000002);
+ zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640);
+ } else {
+ zyd_write32_m(sc, ZYD_MACB_MAX_RETRY, 0x02020202);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f);
+ zyd_write32_m(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f);
+ zyd_write32_m(sc, ZYD_MACB_AIFS_CTL1, 0x00280028);
+ zyd_write32_m(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C);
+ zyd_write32_m(sc, ZYD_MACB_TXOP, 0x01800824);
+ zyd_write32_m(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff);
+ }
+
+ /* init beacon interval to 100ms */
+ if ((error = zyd_set_beacon_interval(sc, 100)) != 0)
+ goto fail;
+
+ if ((error = zyd_rf_attach(sc, sc->sc_rfrev)) != 0) {
+ device_printf(sc->sc_dev, "could not attach RF, rev 0x%x\n",
+ sc->sc_rfrev);
+ goto fail;
+ }
+
+ /* RF chip init */
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ error = (*rf->init)(rf);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "radio initialization failed, error %d\n", error);
+ goto fail;
+ }
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ if ((error = zyd_read_eeprom(sc)) != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM\n");
+ goto fail;
+ }
+
+fail: return (error);
+}
+
+static int
+zyd_read_pod(struct zyd_softc *sc)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_EEPROM_POD, &tmp);
+ sc->sc_rfrev = tmp & 0x0f;
+ sc->sc_ledtype = (tmp >> 4) & 0x01;
+ sc->sc_al2230s = (tmp >> 7) & 0x01;
+ sc->sc_cckgain = (tmp >> 8) & 0x01;
+ sc->sc_fix_cr157 = (tmp >> 13) & 0x01;
+ sc->sc_parev = (tmp >> 16) & 0x0f;
+ sc->sc_bandedge6 = (tmp >> 21) & 0x01;
+ sc->sc_newphy = (tmp >> 31) & 0x01;
+ sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1;
+fail:
+ return (error);
+}
+
+static int
+zyd_read_eeprom(struct zyd_softc *sc)
+{
+ uint16_t val;
+ int error, i;
+
+ /* read Tx power calibration tables */
+ for (i = 0; i < 7; i++) {
+ zyd_read16_m(sc, ZYD_EEPROM_PWR_CAL + i, &val);
+ sc->sc_pwrcal[i * 2] = val >> 8;
+ sc->sc_pwrcal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_PWR_INT + i, &val);
+ sc->sc_pwrint[i * 2] = val >> 8;
+ sc->sc_pwrint[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_36M_CAL + i, &val);
+ sc->sc_ofdm36_cal[i * 2] = val >> 8;
+ sc->sc_ofdm36_cal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_48M_CAL + i, &val);
+ sc->sc_ofdm48_cal[i * 2] = val >> 8;
+ sc->sc_ofdm48_cal[i * 2 + 1] = val & 0xff;
+ zyd_read16_m(sc, ZYD_EEPROM_54M_CAL + i, &val);
+ sc->sc_ofdm54_cal[i * 2] = val >> 8;
+ sc->sc_ofdm54_cal[i * 2 + 1] = val & 0xff;
+ }
+fail:
+ return (error);
+}
+
+static int
+zyd_get_macaddr(struct zyd_softc *sc)
+{
+ struct usb2_device_request req;
+ usb2_error_t error;
+
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ZYD_READFWDATAREQ;
+ USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, IEEE80211_ADDR_LEN);
+
+ error = zyd_do_request(sc, &req, sc->sc_bssid);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not read EEPROM: %s\n",
+ usb2_errstr(error));
+ }
+
+ return (error);
+}
+
+static int
+zyd_set_macaddr(struct zyd_softc *sc, const uint8_t *addr)
+{
+ int error;
+ uint32_t tmp;
+
+ tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
+ zyd_write32_m(sc, ZYD_MAC_MACADRL, tmp);
+ tmp = addr[5] << 8 | addr[4];
+ zyd_write32_m(sc, ZYD_MAC_MACADRH, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_set_bssid(struct zyd_softc *sc, const uint8_t *addr)
+{
+ int error;
+ uint32_t tmp;
+
+ tmp = addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0];
+ zyd_write32_m(sc, ZYD_MAC_BSSADRL, tmp);
+ tmp = addr[5] << 8 | addr[4];
+ zyd_write32_m(sc, ZYD_MAC_BSSADRH, tmp);
+fail:
+ return (error);
+}
+
+static int
+zyd_switch_radio(struct zyd_softc *sc, int on)
+{
+ struct zyd_rf *rf = &sc->sc_rf;
+ int error;
+
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+ error = (*rf->switch_radio)(rf, on);
+ if (error != 0)
+ goto fail;
+ error = zyd_unlock_phy(sc);
+fail:
+ return (error);
+}
+
+static int
+zyd_set_led(struct zyd_softc *sc, int which, int on)
+{
+ int error;
+ uint32_t tmp;
+
+ zyd_read32_m(sc, ZYD_MAC_TX_PE_CONTROL, &tmp);
+ tmp &= ~which;
+ if (on)
+ tmp |= which;
+ zyd_write32_m(sc, ZYD_MAC_TX_PE_CONTROL, tmp);
+fail:
+ return (error);
+}
+
+static void
+zyd_multitask(struct usb2_proc_msg *pm)
+{
+ struct zyd_task *task = (struct zyd_task *)pm;
+ struct zyd_softc *sc = task->sc;
+
+ zyd_set_multi(sc);
+}
+
+static void
+zyd_set_multi(struct zyd_softc *sc)
+{
+ int error;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifmultiaddr *ifma;
+ uint32_t low, high;
+ uint8_t v;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ low = 0x00000000;
+ high = 0x80000000;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR ||
+ (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) {
+ low = 0xffffffff;
+ high = 0xffffffff;
+ } else {
+ IF_ADDR_LOCK(ifp);
+ TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+ if (ifma->ifma_addr->sa_family != AF_LINK)
+ continue;
+ v = ((uint8_t *)LLADDR((struct sockaddr_dl *)
+ ifma->ifma_addr))[5] >> 2;
+ if (v < 32)
+ low |= 1 << v;
+ else
+ high |= 1 << (v - 32);
+ }
+ IF_ADDR_UNLOCK(ifp);
+ }
+
+ /* reprogram multicast global hash table */
+ zyd_write32_m(sc, ZYD_MAC_GHTBL, low);
+ zyd_write32_m(sc, ZYD_MAC_GHTBH, high);
+fail:
+ if (error != 0)
+ device_printf(sc->sc_dev,
+ "could not set multicast hash table\n");
+}
+
+static void
+zyd_update_mcast(struct ifnet *ifp)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+
+ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+ return;
+
+ ZYD_LOCK(sc);
+ zyd_queue_command(sc, zyd_multitask,
+ &sc->sc_mcasttask[0].hdr, &sc->sc_mcasttask[1].hdr);
+ ZYD_UNLOCK(sc);
+}
+
+static int
+zyd_set_rxfilter(struct zyd_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ uint32_t rxfilter;
+
+ switch (ic->ic_opmode) {
+ case IEEE80211_M_STA:
+ rxfilter = ZYD_FILTER_BSS;
+ break;
+ case IEEE80211_M_IBSS:
+ case IEEE80211_M_HOSTAP:
+ rxfilter = ZYD_FILTER_HOSTAP;
+ break;
+ case IEEE80211_M_MONITOR:
+ rxfilter = ZYD_FILTER_MONITOR;
+ break;
+ default:
+ /* should not get there */
+ return (EINVAL);
+ }
+ return zyd_write32(sc, ZYD_MAC_RXFILTER, rxfilter);
+}
+
+static void
+zyd_set_chan(struct zyd_softc *sc, struct ieee80211_channel *c)
+{
+ int error;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct zyd_rf *rf = &sc->sc_rf;
+ uint32_t tmp;
+ int chan;
+
+ chan = ieee80211_chan2ieee(ic, c);
+ if (chan == 0 || chan == IEEE80211_CHAN_ANY) {
+ /* XXX should NEVER happen */
+ device_printf(sc->sc_dev,
+ "%s: invalid channel %x\n", __func__, chan);
+ return;
+ }
+
+ error = zyd_lock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ error = (*rf->set_channel)(rf, chan);
+ if (error != 0)
+ goto fail;
+
+ /* update Tx power */
+ zyd_write16_m(sc, ZYD_CR31, sc->sc_pwrint[chan - 1]);
+
+ if (sc->sc_macrev == ZYD_ZD1211B) {
+ zyd_write16_m(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR68, sc->sc_pwrcal[chan - 1]);
+ zyd_write16_m(sc, ZYD_CR69, 0x28);
+ zyd_write16_m(sc, ZYD_CR69, 0x2a);
+ }
+ if (sc->sc_cckgain) {
+ /* set CCK baseband gain from EEPROM */
+ if (zyd_read32(sc, ZYD_EEPROM_PHY_REG, &tmp) == 0)
+ zyd_write16_m(sc, ZYD_CR47, tmp & 0xff);
+ }
+ if (sc->sc_bandedge6 && rf->bandedge6 != NULL) {
+ error = (*rf->bandedge6)(rf, c);
+ if (error != 0)
+ goto fail;
+ }
+ zyd_write32_m(sc, ZYD_CR_CONFIG_PHILIPS, 0);
+
+ error = zyd_unlock_phy(sc);
+ if (error != 0)
+ goto fail;
+
+ sc->sc_rxtap.wr_chan_freq = sc->sc_txtap.wt_chan_freq =
+ htole16(c->ic_freq);
+ sc->sc_rxtap.wr_chan_flags = sc->sc_txtap.wt_chan_flags =
+ htole16(c->ic_flags);
+fail:
+ return;
+}
+
+static int
+zyd_set_beacon_interval(struct zyd_softc *sc, int bintval)
+{
+ int error;
+ uint32_t val;
+
+ zyd_read32_m(sc, ZYD_CR_ATIM_WND_PERIOD, &val);
+ sc->sc_atim_wnd = val;
+ zyd_read32_m(sc, ZYD_CR_PRE_TBTT, &val);
+ sc->sc_pre_tbtt = val;
+ sc->sc_bcn_int = bintval;
+
+ if (sc->sc_bcn_int <= 5)
+ sc->sc_bcn_int = 5;
+ if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int)
+ sc->sc_pre_tbtt = sc->sc_bcn_int - 1;
+ if (sc->sc_atim_wnd >= sc->sc_pre_tbtt)
+ sc->sc_atim_wnd = sc->sc_pre_tbtt - 1;
+
+ zyd_write32_m(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd);
+ zyd_write32_m(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt);
+ zyd_write32_m(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int);
+fail:
+ return (error);
+}
+
+static void
+zyd_rx_data(struct usb2_xfer *xfer, int offset, uint16_t len)
+{
+ struct zyd_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct zyd_plcphdr plcp;
+ struct zyd_rx_stat stat;
+ struct mbuf *m;
+ int rlen, rssi;
+
+ if (len < ZYD_MIN_FRAGSZ) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n",
+ device_get_nameunit(sc->sc_dev), len);
+ ifp->if_ierrors++;
+ return;
+ }
+ usb2_copy_out(xfer->frbuffers, offset, &plcp, sizeof(plcp));
+ usb2_copy_out(xfer->frbuffers, offset + len - sizeof(stat),
+ &stat, sizeof(stat));
+
+ if (stat.flags & ZYD_RX_ERROR) {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: RX status indicated error (%x)\n",
+ device_get_nameunit(sc->sc_dev), stat.flags);
+ ifp->if_ierrors++;
+ return;
+ }
+
+ /* compute actual frame length */
+ rlen = len - sizeof(struct zyd_plcphdr) -
+ sizeof(struct zyd_rx_stat) - IEEE80211_CRC_LEN;
+
+ /* allocate a mbuf to store the frame */
+ if (rlen > MCLBYTES) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n",
+ device_get_nameunit(sc->sc_dev), rlen);
+ ifp->if_ierrors++;
+ return;
+ } else if (rlen > MHLEN)
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ else
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n",
+ device_get_nameunit(sc->sc_dev));
+ ifp->if_ierrors++;
+ return;
+ }
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = m->m_len = rlen;
+ usb2_copy_out(xfer->frbuffers, offset + sizeof(plcp),
+ mtod(m, uint8_t *), rlen);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct zyd_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ if (stat.flags & (ZYD_RX_BADCRC16 | ZYD_RX_BADCRC32))
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ /* XXX toss, no way to express errors */
+ if (stat.flags & ZYD_RX_DECRYPTERR)
+ tap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
+ tap->wr_rate = ieee80211_plcp2rate(plcp.signal,
+ (stat.flags & ZYD_RX_OFDM) ?
+ IEEE80211_T_OFDM : IEEE80211_T_CCK);
+ tap->wr_antsignal = stat.rssi + -95;
+ tap->wr_antnoise = -95; /* XXX */
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
+ }
+ rssi = (stat.rssi > 63) ? 127 : 2 * stat.rssi;
+
+ sc->sc_rx_data[sc->sc_rx_count].rssi = rssi;
+ sc->sc_rx_data[sc->sc_rx_count].m = m;
+ sc->sc_rx_count++;
+}
+
+static void
+zyd_bulk_read_callback(struct usb2_xfer *xfer)
+{
+ struct zyd_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_node *ni;
+ struct zyd_rx_desc desc;
+ struct mbuf *m;
+ uint32_t offset;
+ uint8_t rssi;
+ int8_t nf;
+ int i;
+
+ sc->sc_rx_count = 0;
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ usb2_copy_out(xfer->frbuffers, xfer->actlen - sizeof(desc),
+ &desc, sizeof(desc));
+
+ offset = 0;
+ if (UGETW(desc.tag) == ZYD_TAG_MULTIFRAME) {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: received multi-frame transfer\n", __func__);
+
+ for (i = 0; i < ZYD_MAX_RXFRAMECNT; i++) {
+ uint16_t len16 = UGETW(desc.len[i]);
+
+ if (len16 == 0 || len16 > xfer->actlen)
+ break;
+
+ zyd_rx_data(xfer, offset, len16);
+
+ /* next frame is aligned on a 32-bit boundary */
+ len16 = (len16 + 3) & ~3;
+ offset += len16;
+ if (len16 > xfer->actlen)
+ break;
+ xfer->actlen -= len16;
+ }
+ } else {
+ DPRINTF(sc, ZYD_DEBUG_RECV,
+ "%s: received single-frame transfer\n", __func__);
+
+ zyd_rx_data(xfer, 0, xfer->actlen);
+ }
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ xfer->frlengths[0] = xfer->max_data_length;
+ usb2_start_hardware(xfer);
+
+ /*
+ * At the end of a USB callback it is always safe to unlock
+ * the private mutex of a device! That is why we do the
+ * "ieee80211_input" here, and not some lines up!
+ */
+ ZYD_UNLOCK(sc);
+ for (i = 0; i < sc->sc_rx_count; i++) {
+ rssi = sc->sc_rx_data[i].rssi;
+ m = sc->sc_rx_data[i].m;
+ sc->sc_rx_data[i].m = NULL;
+
+ nf = -95; /* XXX */
+
+ ni = ieee80211_find_rxnode(ic,
+ mtod(m, struct ieee80211_frame_min *));
+ if (ni != NULL) {
+ (void)ieee80211_input(ni, m, rssi, nf, 0);
+ ieee80211_free_node(ni);
+ } else
+ (void)ieee80211_input_all(ic, m, rssi, nf, 0);
+ }
+ ZYD_LOCK(sc);
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_ANY, "frame error: %s\n", usb2_errstr(xfer->error));
+
+ if (xfer->error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ break;
+ }
+}
+
+static uint8_t
+zyd_plcp_signal(int rate)
+{
+ switch (rate) {
+ /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
+ case 12:
+ return (0xb);
+ case 18:
+ return (0xf);
+ case 24:
+ return (0xa);
+ case 36:
+ return (0xe);
+ case 48:
+ return (0x9);
+ case 72:
+ return (0xd);
+ case 96:
+ return (0x8);
+ case 108:
+ return (0xc);
+ /* CCK rates (NB: not IEEE std, device-specific) */
+ case 2:
+ return (0x0);
+ case 4:
+ return (0x1);
+ case 11:
+ return (0x2);
+ case 22:
+ return (0x3);
+ }
+ return (0xff); /* XXX unsupported/unknown rate */
+}
+
+static int
+zyd_tx_mgt(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct zyd_tx_desc *desc;
+ struct zyd_tx_data *data;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *k;
+ int rate, totlen;
+ uint16_t pktlen;
+
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+ desc = &data->desc;
+
+ rate = IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? 12 : 2;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ }
+
+ data->ni = ni;
+ data->m = m0;
+ data->rate = rate;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ /* fill Tx descriptor */
+ desc->len = htole16(totlen);
+
+ desc->flags = ZYD_TX_FLAG_BACKOFF;
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* multicast frames are not sent at OFDM rates in 802.11b/g */
+ if (totlen > vap->iv_rtsthreshold) {
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ } else if (ZYD_RATE_IS_OFDM(rate) &&
+ (ic->ic_flags & IEEE80211_F_USEPROT)) {
+ if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF;
+ else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ }
+ } else
+ desc->flags |= ZYD_TX_FLAG_MULTICAST;
+
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
+ desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
+
+ desc->phy = zyd_plcp_signal(rate);
+ if (ZYD_RATE_IS_OFDM(rate)) {
+ desc->phy |= ZYD_TX_PHY_OFDM;
+ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ desc->phy |= ZYD_TX_PHY_5GHZ;
+ } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->phy |= ZYD_TX_PHY_SHPREAMBLE;
+
+ /* actual transmit length (XXX why +10?) */
+ pktlen = ZYD_TX_DESC_SIZE + 10;
+ if (sc->sc_macrev == ZYD_ZD1211)
+ pktlen += totlen;
+ desc->pktlen = htole16(pktlen);
+
+ desc->plcp_length = (16 * totlen + rate - 1) / rate;
+ desc->plcp_service = 0;
+ if (rate == 22) {
+ const int remainder = (16 * totlen) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= ZYD_PLCP_LENGEXT;
+ }
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = rate;
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0);
+ }
+
+ DPRINTF(sc, ZYD_DEBUG_XMIT,
+ "%s: sending mgt frame len=%zu rate=%u\n",
+ device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len,
+ rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]);
+
+ return (0);
+}
+
+static void
+zyd_bulk_write_callback(struct usb2_xfer *xfer)
+{
+ struct zyd_softc *sc = xfer->priv_sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ieee80211_channel *c = ic->ic_curchan;
+ struct zyd_tx_data *data;
+ struct mbuf *m;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ DPRINTF(sc, ZYD_DEBUG_ANY, "transfer complete, %u bytes\n",
+ xfer->actlen);
+
+ /* free resources */
+ data = xfer->priv_fifo;
+ zyd_tx_free(data, 0);
+ xfer->priv_fifo = NULL;
+
+ ifp->if_opackets++;
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+
+ /* FALLTHROUGH */
+ case USB_ST_SETUP:
+tr_setup:
+ data = STAILQ_FIRST(&sc->tx_q);
+ if (data) {
+ STAILQ_REMOVE_HEAD(&sc->tx_q, next);
+ m = data->m;
+
+ if (m->m_pkthdr.len > ZYD_MAX_TXBUFSZ) {
+ DPRINTF(sc, ZYD_DEBUG_ANY, "data overflow, %u bytes\n",
+ m->m_pkthdr.len);
+ m->m_pkthdr.len = ZYD_MAX_TXBUFSZ;
+ }
+ usb2_copy_in(xfer->frbuffers, 0, &data->desc,
+ ZYD_TX_DESC_SIZE);
+ usb2_m_copy_in(xfer->frbuffers, ZYD_TX_DESC_SIZE, m, 0,
+ m->m_pkthdr.len);
+
+ if (bpf_peers_present(ifp->if_bpf)) {
+ struct zyd_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = data->rate;
+ tap->wt_chan_freq = htole16(c->ic_freq);
+ tap->wt_chan_flags = htole16(c->ic_flags);
+
+ bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m);
+ }
+
+ xfer->frlengths[0] = ZYD_TX_DESC_SIZE + m->m_pkthdr.len;
+ xfer->priv_fifo = data;
+ usb2_start_hardware(xfer);
+ }
+ break;
+
+ default: /* Error */
+ DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n",
+ usb2_errstr(xfer->error));
+
+ ifp->if_oerrors++;
+ data = xfer->priv_fifo;
+ xfer->priv_fifo = NULL;
+ if (data != NULL)
+ zyd_tx_free(data, xfer->error);
+
+ if (xfer->error == USB_ERR_STALLED) {
+ /* try to clear stall first */
+ xfer->flags.stall_pipe = 1;
+ goto tr_setup;
+ }
+ if (xfer->error == USB_ERR_TIMEOUT)
+ device_printf(sc->sc_dev, "device timeout\n");
+ break;
+ }
+}
+
+static int
+zyd_tx_data(struct zyd_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ struct ieee80211com *ic = ni->ni_ic;
+ struct zyd_tx_desc *desc;
+ struct zyd_tx_data *data;
+ struct ieee80211_frame *wh;
+ const struct ieee80211_txparam *tp;
+ struct ieee80211_key *k;
+ int rate, totlen;
+ uint16_t pktlen;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ data = STAILQ_FIRST(&sc->tx_free);
+ STAILQ_REMOVE_HEAD(&sc->tx_free, next);
+ sc->tx_nfree--;
+ desc = &data->desc;
+
+ desc->flags = ZYD_TX_FLAG_BACKOFF;
+ tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ rate = tp->mcastrate;
+ desc->flags |= ZYD_TX_FLAG_MULTICAST;
+ } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) {
+ rate = tp->ucastrate;
+ } else {
+ (void) ieee80211_amrr_choose(ni, &ZYD_NODE(ni)->amn);
+ rate = ni->ni_txrate;
+ }
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ k = ieee80211_crypto_encap(ni, m0);
+ if (k == NULL) {
+ m_freem(m0);
+ return (ENOBUFS);
+ }
+ /* packet header may have moved, reset our local pointer */
+ wh = mtod(m0, struct ieee80211_frame *);
+ }
+
+ data->ni = ni;
+ data->m = m0;
+
+ totlen = m0->m_pkthdr.len + IEEE80211_CRC_LEN;
+
+ /* fill Tx descriptor */
+ desc->len = htole16(totlen);
+
+ if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ /* multicast frames are not sent at OFDM rates in 802.11b/g */
+ if (totlen > vap->iv_rtsthreshold) {
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ } else if (ZYD_RATE_IS_OFDM(rate) &&
+ (ic->ic_flags & IEEE80211_F_USEPROT)) {
+ if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+ desc->flags |= ZYD_TX_FLAG_CTS_TO_SELF;
+ else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+ desc->flags |= ZYD_TX_FLAG_RTS;
+ }
+ }
+
+ if ((wh->i_fc[0] &
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
+ (IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_PS_POLL))
+ desc->flags |= ZYD_TX_FLAG_TYPE(ZYD_TX_TYPE_PS_POLL);
+
+ desc->phy = zyd_plcp_signal(rate);
+ if (ZYD_RATE_IS_OFDM(rate)) {
+ desc->phy |= ZYD_TX_PHY_OFDM;
+ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan))
+ desc->phy |= ZYD_TX_PHY_5GHZ;
+ } else if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
+ desc->phy |= ZYD_TX_PHY_SHPREAMBLE;
+
+ /* actual transmit length (XXX why +10?) */
+ pktlen = sizeof(struct zyd_tx_desc) + 10;
+ if (sc->sc_macrev == ZYD_ZD1211)
+ pktlen += totlen;
+ desc->pktlen = htole16(pktlen);
+
+ desc->plcp_length = (16 * totlen + rate - 1) / rate;
+ desc->plcp_service = 0;
+ if (rate == 22) {
+ const int remainder = (16 * totlen) % 22;
+ if (remainder != 0 && remainder < 7)
+ desc->plcp_service |= ZYD_PLCP_LENGEXT;
+ }
+
+ DPRINTF(sc, ZYD_DEBUG_XMIT,
+ "%s: sending data frame len=%zu rate=%u\n",
+ device_get_nameunit(sc->sc_dev), (size_t)m0->m_pkthdr.len,
+ rate);
+
+ STAILQ_INSERT_TAIL(&sc->tx_q, data, next);
+ usb2_transfer_start(sc->sc_xfer[ZYD_BULK_WR]);
+
+ return (0);
+}
+
+static void
+zyd_start(struct ifnet *ifp)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+
+ ZYD_LOCK(sc);
+ for (;;) {
+ IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+ if (sc->tx_nfree == 0) {
+ IFQ_DRV_PREPEND(&ifp->if_snd, m);
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ break;
+ }
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m = ieee80211_encap(ni, m);
+ if (m == NULL) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ continue;
+ }
+ if (zyd_tx_data(sc, m, ni) != 0) {
+ ieee80211_free_node(ni);
+ ifp->if_oerrors++;
+ break;
+ }
+ }
+ ZYD_UNLOCK(sc);
+}
+
+static int
+zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
+ const struct ieee80211_bpf_params *params)
+{
+ struct ieee80211com *ic = ni->ni_ic;
+ struct ifnet *ifp = ic->ic_ifp;
+ struct zyd_softc *sc = ifp->if_softc;
+
+ ZYD_LOCK(sc);
+ /* prevent management frames from being sent if we're not ready */
+ if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
+ ZYD_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return (ENETDOWN);
+ }
+ if (sc->tx_nfree == 0) {
+ ifp->if_drv_flags |= IFF_DRV_OACTIVE;
+ ZYD_UNLOCK(sc);
+ m_freem(m);
+ ieee80211_free_node(ni);
+ return (ENOBUFS); /* XXX */
+ }
+
+ /*
+ * Legacy path; interpret frame contents to decide
+ * precisely how to send the frame.
+ * XXX raw path
+ */
+ if (zyd_tx_mgt(sc, m, ni) != 0) {
+ ZYD_UNLOCK(sc);
+ ifp->if_oerrors++;
+ ieee80211_free_node(ni);
+ return (EIO);
+ }
+ ZYD_UNLOCK(sc);
+ return (0);
+}
+
+static int
+zyd_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct zyd_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int error = 0, startall = 0;
+
+ switch (cmd) {
+ case SIOCSIFFLAGS:
+ ZYD_LOCK(sc);
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ if ((ifp->if_flags ^ sc->sc_if_flags) &
+ (IFF_ALLMULTI | IFF_PROMISC))
+ zyd_set_multi(sc);
+ } else {
+ zyd_queue_command(sc, zyd_init_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ startall = 1;
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ zyd_queue_command(sc, zyd_stop_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ }
+ }
+ sc->sc_if_flags = ifp->if_flags;
+ ZYD_UNLOCK(sc);
+ if (startall)
+ ieee80211_start_all(ic);
+ break;
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
+ break;
+ case SIOCGIFADDR:
+ error = ether_ioctl(ifp, cmd, data);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static void
+zyd_init_task(struct usb2_proc_msg *pm)
+{
+ struct zyd_task *task = (struct zyd_task *)pm;
+ struct zyd_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+ struct usb2_config_descriptor *cd;
+ int error;
+ uint32_t val;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (!(sc->sc_flags & ZYD_FLAG_INITONCE)) {
+ error = zyd_loadfirmware(sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "could not load firmware (error=%d)\n", error);
+ goto fail;
+ }
+
+ /* reset device */
+ cd = usb2_get_config_descriptor(sc->sc_udev);
+ error = usb2_req_set_config(sc->sc_udev, &sc->sc_mtx,
+ cd->bConfigurationValue);
+ if (error)
+ device_printf(sc->sc_dev, "reset failed, continuing\n");
+
+ error = zyd_hw_init(sc);
+ if (error) {
+ device_printf(sc->sc_dev,
+ "hardware initialization failed\n");
+ goto fail;
+ }
+
+ device_printf(sc->sc_dev,
+ "HMAC ZD1211%s, FW %02x.%02x, RF %s S%x, PA%x LED %x "
+ "BE%x NP%x Gain%x F%x\n",
+ (sc->sc_macrev == ZYD_ZD1211) ? "": "B",
+ sc->sc_fwrev >> 8, sc->sc_fwrev & 0xff,
+ zyd_rf_name(sc->sc_rfrev), sc->sc_al2230s, sc->sc_parev,
+ sc->sc_ledtype, sc->sc_bandedge6, sc->sc_newphy,
+ sc->sc_cckgain, sc->sc_fix_cr157);
+
+ /* read regulatory domain (currently unused) */
+ zyd_read32_m(sc, ZYD_EEPROM_SUBID, &val);
+ sc->sc_regdomain = val >> 16;
+ DPRINTF(sc, ZYD_DEBUG_INIT, "regulatory domain %x\n",
+ sc->sc_regdomain);
+
+ /* we'll do software WEP decryption for now */
+ DPRINTF(sc, ZYD_DEBUG_INIT, "%s: setting encryption type\n",
+ __func__);
+ zyd_write32_m(sc, ZYD_MAC_ENCRYPTION_TYPE, ZYD_ENC_SNIFFER);
+
+ sc->sc_flags |= ZYD_FLAG_INITONCE;
+ }
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ zyd_stop_task(pm);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
+ DPRINTF(sc, ZYD_DEBUG_INIT, "setting MAC address to %s\n",
+ ether_sprintf(ic->ic_myaddr));
+ error = zyd_set_macaddr(sc, ic->ic_myaddr);
+ if (error != 0)
+ return;
+
+ /* set basic rates */
+ if (ic->ic_curmode == IEEE80211_MODE_11B)
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x0003);
+ else if (ic->ic_curmode == IEEE80211_MODE_11A)
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0x1500);
+ else /* assumes 802.11b/g */
+ zyd_write32_m(sc, ZYD_MAC_BAS_RATE, 0xff0f);
+
+ /* promiscuous mode */
+ zyd_write32_m(sc, ZYD_MAC_SNIFFER, 0);
+ /* multicast setup */
+ zyd_set_multi(sc);
+ /* set RX filter */
+ error = zyd_set_rxfilter(sc);
+ if (error != 0)
+ goto fail;
+
+ /* switch radio transmitter ON */
+ error = zyd_switch_radio(sc, 1);
+ if (error != 0)
+ goto fail;
+ /* set default BSS channel */
+ zyd_set_chan(sc, ic->ic_curchan);
+
+ /*
+ * Allocate Tx and Rx xfer queues.
+ */
+ zyd_setup_tx_list(sc);
+
+ /* enable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, ZYD_HWINT_MASK);
+
+ ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+ ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ usb2_transfer_start(sc->sc_xfer[ZYD_BULK_RD]);
+ usb2_transfer_start(sc->sc_xfer[ZYD_INTR_RD]);
+
+ return;
+
+fail: zyd_stop_task(pm);
+ return;
+}
+
+static void
+zyd_init(void *priv)
+{
+ struct zyd_softc *sc = priv;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ ZYD_LOCK(sc);
+ zyd_queue_command(sc, zyd_init_task,
+ &sc->sc_synctask[0].hdr,
+ &sc->sc_synctask[1].hdr);
+ ZYD_UNLOCK(sc);
+
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ ieee80211_start_all(ic); /* start all vap's */
+}
+
+static void
+zyd_stop_task(struct usb2_proc_msg *pm)
+{
+ struct zyd_task *task = (struct zyd_task *)pm;
+ struct zyd_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ int error;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ /*
+ * Drain all the transfers, if not already drained:
+ */
+ ZYD_UNLOCK(sc);
+ usb2_transfer_drain(sc->sc_xfer[ZYD_BULK_WR]);
+ usb2_transfer_drain(sc->sc_xfer[ZYD_BULK_RD]);
+ ZYD_LOCK(sc);
+
+ zyd_unsetup_tx_list(sc);
+
+ /* Stop now if the device was never set up */
+ if (!(sc->sc_flags & ZYD_FLAG_INITONCE))
+ return;
+
+ /* switch radio transmitter OFF */
+ error = zyd_switch_radio(sc, 0);
+ if (error != 0)
+ goto fail;
+ /* disable Rx */
+ zyd_write32_m(sc, ZYD_MAC_RXFILTER, 0);
+ /* disable interrupts */
+ zyd_write32_m(sc, ZYD_CR_INTERRUPT, 0);
+
+fail:
+ return;
+}
+
+static int
+zyd_loadfirmware(struct zyd_softc *sc)
+{
+ struct usb2_device_request req;
+ size_t size;
+ u_char *fw;
+ uint8_t stat;
+ uint16_t addr;
+
+ if (sc->sc_flags & ZYD_FLAG_FWLOADED)
+ return (0);
+
+ if (sc->sc_macrev == ZYD_ZD1211) {
+ fw = (u_char *)zd1211_firmware;
+ size = sizeof(zd1211_firmware);
+ } else {
+ fw = (u_char *)zd1211b_firmware;
+ size = sizeof(zd1211b_firmware);
+ }
+
+ req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ req.bRequest = ZYD_DOWNLOADREQ;
+ USETW(req.wIndex, 0);
+
+ addr = ZYD_FIRMWARE_START_ADDR;
+ while (size > 0) {
+ /*
+ * When the transfer size is 4096 bytes, it is not
+ * likely to be able to transfer it.
+ * The cause is port or machine or chip?
+ */
+ const int mlen = min(size, 64);
+
+ DPRINTF(sc, ZYD_DEBUG_FW,
+ "loading firmware block: len=%d, addr=0x%x\n", mlen, addr);
+
+ USETW(req.wValue, addr);
+ USETW(req.wLength, mlen);
+ if (zyd_do_request(sc, &req, fw) != 0)
+ return (EIO);
+
+ addr += mlen / 2;
+ fw += mlen;
+ size -= mlen;
+ }
+
+ /* check whether the upload succeeded */
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ZYD_DOWNLOADSTS;
+ USETW(req.wValue, 0);
+ USETW(req.wIndex, 0);
+ USETW(req.wLength, sizeof(stat));
+ if (zyd_do_request(sc, &req, &stat) != 0)
+ return (EIO);
+
+ sc->sc_flags |= ZYD_FLAG_FWLOADED;
+
+ return (stat & 0x80) ? (EIO) : (0);
+}
+
+static void
+zyd_newassoc(struct ieee80211_node *ni, int isnew)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ ieee80211_amrr_node_init(&ZYD_VAP(vap)->amrr, &ZYD_NODE(ni)->amn, ni);
+}
+
+static void
+zyd_scan_start(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ ZYD_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = ZYD_SCAN_START;
+ zyd_queue_command(sc, zyd_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ ZYD_UNLOCK(sc);
+}
+
+static void
+zyd_scan_end(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ ZYD_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = ZYD_SCAN_END;
+ zyd_queue_command(sc, zyd_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ ZYD_UNLOCK(sc);
+}
+
+static void
+zyd_set_channel(struct ieee80211com *ic)
+{
+ struct zyd_softc *sc = ic->ic_ifp->if_softc;
+
+ ZYD_LOCK(sc);
+ /* do it in a process context */
+ sc->sc_scan_action = ZYD_SET_CHANNEL;
+ zyd_queue_command(sc, zyd_scantask,
+ &sc->sc_scantask[0].hdr, &sc->sc_scantask[1].hdr);
+ ZYD_UNLOCK(sc);
+}
+
+static void
+zyd_scantask(struct usb2_proc_msg *pm)
+{
+ struct zyd_task *task = (struct zyd_task *)pm;
+ struct zyd_softc *sc = task->sc;
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ieee80211com *ic = ifp->if_l2com;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ switch (sc->sc_scan_action) {
+ case ZYD_SCAN_START:
+ /* want broadcast address while scanning */
+ zyd_set_bssid(sc, ifp->if_broadcastaddr);
+ break;
+
+ case ZYD_SET_CHANNEL:
+ zyd_set_chan(sc, ic->ic_curchan);
+ break;
+
+ default: /* ZYD_SCAN_END */
+ /* restore previous bssid */
+ zyd_set_bssid(sc, sc->sc_bssid);
+ break;
+ }
+}
+
+static void
+zyd_queue_command(struct zyd_softc *sc, usb2_proc_callback_t *fn,
+ struct usb2_proc_msg *t0, struct usb2_proc_msg *t1)
+{
+ struct zyd_task *task;
+
+ ZYD_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (usb2_proc_is_gone(&sc->sc_tq)) {
+ DPRINTF(sc, ZYD_DEBUG_STATE, "proc is gone\n");
+ return; /* nothing to do */
+ }
+ /*
+ * NOTE: The task cannot get executed before we drop the
+ * "sc_mtx" mutex. It is safe to update fields in the message
+ * structure after that the message got queued.
+ */
+ task = (struct zyd_task *)
+ usb2_proc_msignal(&sc->sc_tq, t0, t1);
+
+ /* Setup callback and softc pointers */
+ task->hdr.pm_callback = fn;
+ task->sc = sc;
+
+ /*
+ * Init and stop must be synchronous!
+ */
+ if ((fn == zyd_init_task) || (fn == zyd_stop_task))
+ usb2_proc_mwait(&sc->sc_tq, t0, t1);
+}
+
+static device_method_t zyd_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zyd_match),
+ DEVMETHOD(device_attach, zyd_attach),
+ DEVMETHOD(device_detach, zyd_detach),
+
+ { 0, 0 }
+};
+
+static driver_t zyd_driver = {
+ "zyd",
+ zyd_methods,
+ sizeof(struct zyd_softc)
+};
+
+static devclass_t zyd_devclass;
+
+DRIVER_MODULE(zyd, ushub, zyd_driver, zyd_devclass, NULL, 0);
+MODULE_DEPEND(zyd, usb, 1, 1, 1);
+MODULE_DEPEND(zyd, wlan, 1, 1, 1);
+MODULE_DEPEND(zyd, wlan_amrr, 1, 1, 1);
diff --git a/sys/dev/usb/wlan/if_zydfw.h b/sys/dev/usb/wlan/if_zydfw.h
new file mode 100644
index 0000000..46f5c2a
--- /dev/null
+++ b/sys/dev/usb/wlan/if_zydfw.h
@@ -0,0 +1,1144 @@
+/*
+ * Copyright (C) 2001, 2002, 2003,2004 ZyDAS Technology Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that the following conditions are met:
+ * 1. Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistribution 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$ */
+
+uint8_t zd1211_firmware[] = {
+ 0x08, 0x91, 0xFF, 0xED, 0x09, 0x93, 0x1E, 0xEE,
+ 0xD1, 0x94, 0x11, 0xEE, 0x88, 0xD4, 0xD1, 0x96,
+ 0xD1, 0x98, 0x5C, 0x99, 0x5C, 0x99, 0x4C, 0x99,
+ 0x04, 0x9D, 0xD1, 0x98, 0xD1, 0x9A, 0x03, 0xEE,
+ 0xF4, 0x94, 0xD3, 0xD4, 0x41, 0x2A, 0x40, 0x4A,
+ 0x45, 0xBE, 0x88, 0x92, 0x41, 0x24, 0x40, 0x44,
+ 0x53, 0xBE, 0x40, 0xF0, 0x93, 0xEE, 0x41, 0xEE,
+ 0x98, 0x9A, 0xD4, 0xF7, 0x02, 0x00, 0x1F, 0xEC,
+ 0x00, 0x00, 0xB2, 0xF8, 0x4D, 0x00, 0xA1, 0xEC,
+ 0x00, 0x00, 0xA6, 0xF7, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xD8,
+ 0xA0, 0x90, 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8,
+ 0x40, 0xF0, 0xB4, 0xF0, 0xA0, 0x90, 0x98, 0x9A,
+ 0xA0, 0xD8, 0x40, 0xF0, 0x64, 0xEF, 0xA0, 0x90,
+ 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0, 0xF6, 0xF0,
+ 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0,
+ 0xF7, 0xF6, 0xA0, 0x90, 0x98, 0x9A, 0xA0, 0xD8,
+ 0x40, 0xF0, 0xF8, 0xF5, 0xA0, 0x90, 0x98, 0x9A,
+ 0xA0, 0xD8, 0x40, 0xF0, 0xF1, 0xF0, 0xA0, 0x90,
+ 0x98, 0x9A, 0x98, 0x9A, 0xA0, 0xD8, 0x40, 0xF0,
+ 0x97, 0xF7, 0xA0, 0x90, 0x98, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00,
+ 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x42, 0x02,
+ 0xC1, 0x92, 0x03, 0x96, 0x1B, 0xD7, 0x2A, 0x86,
+ 0x1A, 0xD5, 0x2B, 0x86, 0x09, 0xA3, 0x00, 0x80,
+ 0x19, 0xD3, 0x2C, 0x86, 0x00, 0xEE, 0x0A, 0x65,
+ 0xC0, 0x7A, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xFE, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94, 0xC5, 0xD4,
+ 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94, 0x01, 0xD4,
+ 0x42, 0x02, 0xC1, 0x96, 0x0A, 0x65, 0xC0, 0x7A,
+ 0x02, 0x99, 0xC4, 0x92, 0x41, 0xA2, 0xC4, 0xD2,
+ 0xC5, 0x98, 0x1C, 0xD9, 0x2A, 0x86, 0x01, 0x98,
+ 0x1C, 0xD9, 0x2B, 0x86, 0x1B, 0xD7, 0x2C, 0x86,
+ 0x00, 0xEE, 0x09, 0xB3, 0xFE, 0xFF, 0xC2, 0xD2,
+ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x41, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0xE5, 0xEE, 0x11, 0x93, 0xD8, 0xF7, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xAE, 0xEE, 0x40, 0xF1,
+ 0x40, 0x92, 0x19, 0xD3, 0xD8, 0xF7, 0xC5, 0x92,
+ 0x41, 0x92, 0x19, 0xD3, 0x00, 0x83, 0x40, 0x92,
+ 0x19, 0xD3, 0x00, 0x83, 0x0F, 0x9F, 0x95, 0xF8,
+ 0x0F, 0x9F, 0x99, 0xEE, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x99, 0xEE, 0x40, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3,
+ 0x91, 0xEC, 0x40, 0xF0, 0x5F, 0xF2, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0F, 0x9F,
+ 0x99, 0xEE, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92,
+ 0x19, 0xD3, 0x12, 0x95, 0x19, 0xD3, 0x10, 0x95,
+ 0x19, 0xD3, 0x02, 0x80, 0x19, 0xD3, 0x03, 0x82,
+ 0x09, 0x93, 0xC7, 0xF7, 0x19, 0xD3, 0x91, 0xEC,
+ 0x40, 0xF0, 0x5F, 0xF2, 0x40, 0xF0, 0xDE, 0xF3,
+ 0x11, 0x93, 0x04, 0xEC, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xE3, 0xEE, 0x40, 0x92, 0x19, 0xD3,
+ 0x04, 0xEC, 0x40, 0xF0, 0x38, 0xF2, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x11, 0x93, 0x44, 0x96, 0x09, 0xB3, 0xFF, 0xFD,
+ 0x19, 0xD3, 0x44, 0x96, 0x40, 0xF0, 0x90, 0xF7,
+ 0x6E, 0x92, 0x19, 0xD3, 0x05, 0x84, 0x40, 0xF0,
+ 0xC4, 0xEE, 0x4B, 0x62, 0x0A, 0x95, 0x2E, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x2B, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x00, 0xEE, 0xD1, 0xD4, 0x0B, 0x97,
+ 0x2F, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x34, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x39, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x3E, 0xEE, 0xD1, 0xD4, 0x0B, 0x97,
+ 0x43, 0xEE, 0xD1, 0xD6, 0x0A, 0x95, 0x48, 0xEE,
+ 0xD1, 0xD4, 0x0B, 0x97, 0x4D, 0xEE, 0xD1, 0xD6,
+ 0x0A, 0x95, 0x4E, 0xEE, 0xC1, 0xD4, 0x0A, 0x65,
+ 0x00, 0x44, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2,
+ 0xC2, 0xD2, 0x43, 0xF1, 0x09, 0x93, 0x01, 0x3F,
+ 0x19, 0xD3, 0xC0, 0x85, 0x11, 0x93, 0x44, 0x96,
+ 0x09, 0xB3, 0xFF, 0xFC, 0x19, 0xD3, 0x44, 0x96,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x03, 0x00, 0x03, 0x96,
+ 0x41, 0x02, 0x03, 0x99, 0xC4, 0x94, 0x42, 0x04,
+ 0xC1, 0x04, 0xC2, 0x94, 0xC3, 0xD4, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0x13, 0x97,
+ 0x95, 0xEC, 0x1B, 0xD7, 0x02, 0x80, 0x11, 0x93,
+ 0x99, 0xEC, 0x19, 0xD3, 0x7C, 0x96, 0x0B, 0x97,
+ 0xA0, 0x00, 0x1B, 0xD7, 0x6E, 0xEC, 0x0A, 0x65,
+ 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xFF, 0xBF, 0x11, 0xA3, 0x9A, 0xEC, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3,
+ 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x14, 0x99, 0x03, 0x80, 0x0C, 0xB3, 0x00, 0x10,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x97, 0xF0,
+ 0x11, 0x93, 0x9F, 0xEC, 0x41, 0x02, 0x19, 0xD3,
+ 0x9F, 0xEC, 0x11, 0x93, 0xD6, 0xF7, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x84, 0xEF, 0x0A, 0x65,
+ 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0x00, 0x04, 0xC2, 0xD2, 0x0F, 0x9F, 0xB1, 0xF0,
+ 0x11, 0x93, 0x94, 0xEC, 0x02, 0xD2, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xD0, 0xEF, 0x41, 0x92,
+ 0x19, 0xD3, 0x94, 0xEC, 0x19, 0xD3, 0x9F, 0xEC,
+ 0x12, 0x95, 0x02, 0x80, 0x1A, 0xD5, 0x95, 0xEC,
+ 0x13, 0x97, 0x7C, 0x96, 0x1B, 0xD7, 0x99, 0xEC,
+ 0x0A, 0x65, 0x0E, 0x42, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0x00, 0x40, 0x19, 0xD3, 0x9A, 0xEC,
+ 0x09, 0x63, 0x00, 0x40, 0xC2, 0xD2, 0x02, 0x94,
+ 0x1A, 0xD5, 0x7C, 0x96, 0x0C, 0xB3, 0x00, 0x08,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xB0, 0xEF,
+ 0x0C, 0xB3, 0xFF, 0x07, 0x0F, 0x9F, 0xB4, 0xEF,
+ 0x11, 0x93, 0x06, 0x80, 0x09, 0xB3, 0xFF, 0x07,
+ 0x09, 0x03, 0x00, 0xA0, 0x19, 0xD3, 0x97, 0xEC,
+ 0x40, 0x98, 0x0B, 0x97, 0x9C, 0xEC, 0x04, 0x95,
+ 0x03, 0x05, 0x14, 0x03, 0x97, 0xEC, 0x46, 0x02,
+ 0xC1, 0x92, 0xC2, 0xD2, 0x41, 0x08, 0x42, 0x48,
+ 0x02, 0x9E, 0x0F, 0x9F, 0xBB, 0xEF, 0x11, 0x93,
+ 0x97, 0xEC, 0xC1, 0x92, 0xC5, 0xD2, 0x5F, 0xB2,
+ 0x19, 0xD3, 0x9B, 0xEC, 0x0F, 0x9F, 0xD3, 0xEF,
+ 0x13, 0x97, 0x98, 0xEC, 0xC5, 0xD6, 0x11, 0x93,
+ 0x03, 0x80, 0x09, 0xB3, 0x00, 0x08, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xE9, 0xEF, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x10,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x40, 0x98, 0x1C, 0xD9,
+ 0x9B, 0xEC, 0x12, 0x95, 0x9B, 0xEC, 0x40, 0x44,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x86, 0xF0, 0x0A, 0xB3,
+ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x07, 0xF0, 0x0A, 0xB3, 0x07, 0x00, 0x09, 0x05,
+ 0xA9, 0xEC, 0xC2, 0x94, 0x01, 0xD4, 0x09, 0x03,
+ 0xA1, 0xEC, 0xC1, 0x92, 0x19, 0xD3, 0x9B, 0xEC,
+ 0xC5, 0x94, 0x0A, 0xB5, 0x00, 0xFF, 0x01, 0xA5,
+ 0xC5, 0xD4, 0x0F, 0x9F, 0x13, 0xF0, 0x0A, 0x05,
+ 0xFF, 0xFF, 0x0A, 0x03, 0xB1, 0xEC, 0xC1, 0x92,
+ 0x01, 0xD2, 0x1A, 0xD5, 0x9B, 0xEC, 0xC5, 0x96,
+ 0x0B, 0x07, 0xFF, 0xFF, 0xC5, 0xD6, 0x11, 0x93,
+ 0x97, 0xEC, 0xC5, 0x98, 0xC1, 0xD8, 0x11, 0x93,
+ 0x97, 0xEC, 0x09, 0x05, 0x0B, 0x00, 0x03, 0xD4,
+ 0xC2, 0x96, 0x06, 0xD6, 0x7B, 0x95, 0x7A, 0x95,
+ 0x4C, 0x02, 0xC1, 0x92, 0x59, 0x93, 0x59, 0x93,
+ 0x01, 0xA5, 0x01, 0x98, 0x0C, 0xF5, 0x7B, 0x93,
+ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xB3,
+ 0xFF, 0x00, 0x04, 0xD2, 0x5C, 0x93, 0x59, 0x93,
+ 0x04, 0x94, 0x01, 0xA5, 0x03, 0x96, 0xC3, 0xD4,
+ 0x11, 0x93, 0x97, 0xEC, 0x4C, 0x02, 0x05, 0xD2,
+ 0xC1, 0x92, 0x09, 0xB3, 0x00, 0xFF, 0x7C, 0x95,
+ 0x7A, 0x95, 0x02, 0xA3, 0x05, 0x98, 0xC4, 0xD2,
+ 0x12, 0x95, 0x97, 0xEC, 0x45, 0x04, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2,
+ 0x12, 0x95, 0x9B, 0xEC, 0x0A, 0xB3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5B, 0xF0,
+ 0x12, 0x95, 0x97, 0xEC, 0x4A, 0x04, 0x02, 0x99,
+ 0xC4, 0x92, 0x01, 0x98, 0x0C, 0xF3, 0x7B, 0x93,
+ 0x41, 0x02, 0x0F, 0x9F, 0x7C, 0xF0, 0x43, 0x44,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x7D, 0xF0, 0x11, 0x93,
+ 0x97, 0xEC, 0x42, 0x02, 0x0A, 0x05, 0xFF, 0xFF,
+ 0xC1, 0xD4, 0x11, 0x93, 0x97, 0xEC, 0x4A, 0x02,
+ 0x12, 0x95, 0x60, 0x96, 0xC1, 0xD4, 0x12, 0x95,
+ 0x97, 0xEC, 0x4B, 0x04, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0x1F, 0xFF, 0xC2, 0xD2, 0x12, 0x95,
+ 0x97, 0xEC, 0x4B, 0x04, 0x11, 0x93, 0x62, 0x96,
+ 0x41, 0x93, 0x59, 0x93, 0x02, 0x99, 0xC4, 0xA2,
+ 0xC2, 0xD2, 0xC5, 0x92, 0x19, 0xD3, 0x98, 0xEC,
+ 0x0A, 0x95, 0x0C, 0x02, 0x1A, 0xD5, 0x02, 0x80,
+ 0x0F, 0x9F, 0xB1, 0xF0, 0x09, 0x63, 0xFE, 0x7F,
+ 0x01, 0x97, 0xC3, 0x94, 0x0A, 0xA5, 0x00, 0x04,
+ 0xC1, 0xD4, 0x11, 0x93, 0x9F, 0xEC, 0x09, 0xA3,
+ 0x00, 0x01, 0x19, 0xD3, 0x9F, 0xEC, 0x40, 0xF0,
+ 0x39, 0xEF, 0x0F, 0x9F, 0xB1, 0xF0, 0x11, 0x93,
+ 0x94, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xA6, 0xF0, 0x40, 0xF0, 0x39, 0xEF, 0x11, 0x93,
+ 0x95, 0xEC, 0x44, 0xB2, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xB1, 0xF0, 0x48, 0x98, 0x1C, 0xD9,
+ 0x02, 0x80, 0x11, 0x93, 0x91, 0xEC, 0x41, 0x22,
+ 0x0A, 0x95, 0xB1, 0xF0, 0x88, 0xD4, 0x88, 0xDC,
+ 0x91, 0x9A, 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0x04, 0x82, 0x48, 0xB2, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xC8, 0xF0, 0x0A, 0x65, 0xFD, 0x7D,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFF, 0xFE,
+ 0xC2, 0xD2, 0x41, 0x92, 0x19, 0xD3, 0xBF, 0xEC,
+ 0x11, 0x93, 0x04, 0x82, 0x43, 0xB2, 0x12, 0x95,
+ 0x03, 0x82, 0x02, 0xB3, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xEF, 0xF0, 0x0A, 0xB3, 0x00, 0xFF,
+ 0x48, 0xA2, 0x19, 0xD3, 0x03, 0x82, 0x40, 0xF0,
+ 0xEB, 0xF3, 0x11, 0x93, 0xBF, 0xEC, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93,
+ 0x07, 0x82, 0x11, 0x43, 0x03, 0xEC, 0x02, 0x0E,
+ 0x0F, 0x9F, 0xEF, 0xF0, 0x11, 0x93, 0x03, 0x82,
+ 0x09, 0xA3, 0x00, 0x01, 0x19, 0xD3, 0x03, 0x82,
+ 0x40, 0x96, 0x1B, 0xD7, 0xBF, 0xEC, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x11, 0x93, 0x20, 0xBC, 0xC8, 0xD2,
+ 0x40, 0xF0, 0x48, 0xF1, 0x41, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x05, 0x00, 0x05, 0x94,
+ 0x41, 0x02, 0xC1, 0x92, 0x01, 0x97, 0xC3, 0x96,
+ 0xC2, 0xD6, 0x0A, 0x45, 0x00, 0x95, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x45, 0xF1, 0xC1, 0x92, 0x41, 0xB2,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x45, 0xF1,
+ 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x45, 0xF1, 0x41, 0x98, 0x1C, 0xD9,
+ 0xC0, 0xEC, 0x12, 0x95, 0x02, 0x80, 0x01, 0xD4,
+ 0x40, 0xF0, 0x56, 0xF2, 0x0B, 0x67, 0xFD, 0x7D,
+ 0x03, 0x99, 0xC4, 0x92, 0x0C, 0x99, 0x96, 0x03,
+ 0x1C, 0xD9, 0x06, 0x82, 0x41, 0x98, 0x1C, 0xD9,
+ 0x02, 0x82, 0x42, 0x98, 0x1C, 0xD9, 0x05, 0x82,
+ 0x0C, 0x69, 0x80, 0x7F, 0x1C, 0xD9, 0x00, 0xB0,
+ 0x09, 0xA3, 0x00, 0x01, 0xC3, 0xD2, 0x01, 0x94,
+ 0x0A, 0xB3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x43, 0xF1, 0x42, 0xA4, 0x1A, 0xD5,
+ 0x02, 0x80, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x42, 0x20, 0x08, 0x0B, 0x01, 0x00,
+ 0x05, 0x92, 0xC5, 0xD2, 0x60, 0xB2, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x55, 0xF1, 0x40, 0xF0,
+ 0x35, 0xF7, 0xC5, 0x94, 0x0A, 0xB3, 0x10, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x5E, 0xF1,
+ 0x40, 0xF0, 0x23, 0xF6, 0xC5, 0x96, 0x0B, 0xB3,
+ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x67, 0xF1, 0x40, 0xF0, 0x5D, 0xF5, 0xC5, 0x94,
+ 0x0A, 0xB3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xC8, 0xF1, 0x13, 0x97, 0x21, 0xBC,
+ 0x01, 0xD6, 0x0B, 0xB3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x79, 0xF1, 0x40, 0xF0,
+ 0x62, 0xFB, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x82, 0xF1,
+ 0x40, 0xF0, 0x6C, 0xFB, 0x01, 0x96, 0x0B, 0xB3,
+ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xA2, 0xF1, 0x40, 0xF0, 0xB0, 0xFA, 0x41, 0x92,
+ 0x19, 0xD3, 0xD5, 0xF7, 0x11, 0x93, 0x03, 0xEC,
+ 0x09, 0x43, 0x40, 0x00, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x98, 0xF1, 0x40, 0x94, 0x1A, 0xD5, 0xD5, 0xF7,
+ 0x11, 0x93, 0x00, 0xEC, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xAB, 0xF1, 0x40, 0xF0, 0x38, 0xF2,
+ 0x0F, 0x9F, 0xAB, 0xF1, 0x01, 0x96, 0x0B, 0xB3,
+ 0x08, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xAB, 0xF1, 0x40, 0xF0, 0x7C, 0xFB, 0x01, 0x94,
+ 0x0A, 0xB3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0xB4, 0xF1, 0x40, 0xF0, 0x87, 0xFB,
+ 0x11, 0x93, 0x10, 0xEC, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xBF, 0xF1, 0x44, 0x96, 0x1B, 0xD7,
+ 0x0B, 0xBC, 0x0F, 0x9F, 0xC5, 0xF1, 0x41, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xC5, 0xF1, 0x19, 0xD3,
+ 0x0B, 0xBC, 0x40, 0x92, 0x19, 0xD3, 0x10, 0xEC,
+ 0xC5, 0x94, 0x0A, 0xB3, 0x80, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2, 0x13, 0x97,
+ 0x28, 0xBC, 0x01, 0xD6, 0x0B, 0xB3, 0x40, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xDA, 0xF1,
+ 0x40, 0xF0, 0x18, 0xF7, 0x01, 0x94, 0x0A, 0xB3,
+ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xED, 0xF1, 0x40, 0xF0, 0xC4, 0xEE, 0x40, 0xF0,
+ 0x8F, 0xFB, 0x40, 0xF0, 0x1B, 0xF2, 0x40, 0x96,
+ 0x1B, 0xD7, 0x00, 0xEC, 0x41, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x04, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x09, 0xF2,
+ 0x40, 0xF0, 0x9E, 0xFB, 0x09, 0x63, 0x00, 0x44,
+ 0x01, 0x97, 0xC3, 0x94, 0x48, 0xA4, 0xC1, 0xD4,
+ 0x00, 0xEE, 0x40, 0x92, 0x19, 0xD3, 0x12, 0x95,
+ 0x19, 0xD3, 0x10, 0x95, 0x19, 0xD3, 0x02, 0x80,
+ 0x19, 0xD3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xD3,
+ 0xD8, 0xF7, 0x01, 0x94, 0x0A, 0xB3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x12, 0xF2,
+ 0x40, 0xF0, 0xAE, 0xFB, 0x0A, 0x65, 0x00, 0x44,
+ 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2,
+ 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65, 0xEA, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD, 0x0A, 0x65,
+ 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEB, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF,
+ 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3,
+ 0x02, 0x80, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x93, 0x00, 0x09,
+ 0x19, 0xD3, 0x02, 0x80, 0x40, 0xF0, 0x56, 0xF2,
+ 0x40, 0x92, 0x19, 0xD3, 0x94, 0xEC, 0xC8, 0xD2,
+ 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0,
+ 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0x3B, 0xF5, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x85, 0xF2, 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97,
+ 0xC3, 0x92, 0x44, 0xA2, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0x92, 0xF2, 0x40, 0xF0, 0x94, 0xF2, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x92, 0xF2, 0xC8, 0xD2,
+ 0x09, 0x93, 0x91, 0xEC, 0xC8, 0xD2, 0x40, 0xF0,
+ 0x2A, 0xEF, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0xF1, 0xBD, 0x19, 0xD3, 0xB6, 0xEC, 0x11, 0x93,
+ 0xB4, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xAC, 0xF2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xC3, 0x94, 0x0A, 0x07, 0x07, 0x00, 0xC1, 0xD6,
+ 0x0A, 0x05, 0x00, 0xA0, 0x1A, 0xD5, 0x96, 0xEC,
+ 0x11, 0x93, 0xB6, 0xEC, 0x19, 0xD3, 0x01, 0x80,
+ 0x0A, 0x65, 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92,
+ 0x41, 0xA2, 0xC2, 0xD2, 0x40, 0x92, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x13, 0x97, 0xB4, 0xEC, 0x40, 0x46,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x2C, 0xF3, 0x12, 0x95,
+ 0x96, 0xEC, 0x0A, 0x03, 0x07, 0x00, 0xC1, 0x92,
+ 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0x09, 0x05,
+ 0x01, 0x00, 0x48, 0x02, 0xC1, 0x92, 0xC2, 0xD2,
+ 0x11, 0x93, 0x96, 0xEC, 0x4E, 0x02, 0xC1, 0x94,
+ 0xC5, 0xD6, 0xC5, 0x92, 0x11, 0x07, 0x96, 0xEC,
+ 0x0B, 0x03, 0x0F, 0x00, 0xC1, 0x98, 0x46, 0x06,
+ 0x7A, 0x93, 0x79, 0x93, 0x5C, 0x95, 0x5A, 0x95,
+ 0x02, 0xA3, 0xC3, 0xD2, 0x04, 0x95, 0xC5, 0x96,
+ 0x41, 0x06, 0xC5, 0xD6, 0x42, 0x46, 0x02, 0x9E,
+ 0x0F, 0x9F, 0xD5, 0xF2, 0x11, 0x93, 0x96, 0xEC,
+ 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xC1, 0x92,
+ 0xC2, 0xD2, 0x11, 0x93, 0x96, 0xEC, 0xC1, 0x92,
+ 0x09, 0xB5, 0x1F, 0x00, 0x43, 0x44, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x02, 0xF3, 0x40, 0x44, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x03, 0xF3, 0x0A, 0x05, 0xFF, 0xFF,
+ 0x0F, 0x9F, 0x03, 0xF3, 0x43, 0x94, 0x11, 0x93,
+ 0x96, 0xEC, 0x42, 0x02, 0xC1, 0xD4, 0x11, 0x93,
+ 0x96, 0xEC, 0x49, 0x02, 0xC1, 0x92, 0x19, 0xD3,
+ 0xB4, 0xEC, 0x09, 0x05, 0xF2, 0xFF, 0x1A, 0xD5,
+ 0x92, 0xEC, 0x09, 0x43, 0xD0, 0x07, 0x02, 0x9E,
+ 0x0F, 0x9F, 0x2C, 0xF3, 0x11, 0x93, 0xDC, 0xF7,
+ 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93,
+ 0xDB, 0xF7, 0x09, 0xA3, 0x40, 0x00, 0x19, 0xD3,
+ 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80, 0x01, 0x95,
+ 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC, 0x40, 0x96,
+ 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F, 0x92, 0xF3,
+ 0x11, 0x93, 0x92, 0xEC, 0x12, 0x95, 0xB6, 0xEC,
+ 0x02, 0x43, 0x02, 0x8E, 0x0F, 0x9F, 0x7A, 0xF3,
+ 0x02, 0x0E, 0x0F, 0x9F, 0x4D, 0xF3, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x80, 0x00,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x95, 0xC2, 0x94, 0x1A, 0xD5, 0xB5, 0xEC,
+ 0x40, 0x96, 0x1B, 0xD7, 0xB4, 0xEC, 0x0F, 0x9F,
+ 0x92, 0xF3, 0x11, 0x93, 0x03, 0x80, 0x09, 0xB3,
+ 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x5F, 0xF3, 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x5F, 0xF3, 0x40, 0xF0,
+ 0xA6, 0xF3, 0x0F, 0x9F, 0x94, 0xF3, 0x41, 0x92,
+ 0xC8, 0xD2, 0x0A, 0x95, 0x91, 0xEC, 0xC8, 0xD4,
+ 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00, 0x11, 0x93,
+ 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x72, 0xF3, 0x42, 0x96, 0x1B, 0xD7, 0xC0, 0xEC,
+ 0x0F, 0x9F, 0x94, 0xF3, 0x0A, 0x65, 0xFE, 0x7F,
+ 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2,
+ 0x0F, 0x9F, 0x94, 0xF3, 0x12, 0x45, 0x03, 0xEC,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x8C, 0xF3, 0x11, 0x93,
+ 0xDC, 0xF7, 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7,
+ 0x11, 0x93, 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x08,
+ 0x19, 0xD3, 0xDB, 0xF7, 0x1A, 0xD5, 0x92, 0xEC,
+ 0x11, 0x93, 0x92, 0xEC, 0x19, 0x25, 0x92, 0xEC,
+ 0x09, 0x63, 0x00, 0x80, 0x19, 0xD3, 0xF2, 0xBD,
+ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0, 0xA6, 0xF3,
+ 0x40, 0x92, 0xC8, 0xD2, 0x09, 0x93, 0x91, 0xEC,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x42, 0x00,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x11, 0x93, 0xD7, 0xF7, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xB6, 0xF3, 0x0A, 0x65,
+ 0xBC, 0x69, 0x02, 0x97, 0xC3, 0x92, 0x09, 0x83,
+ 0x00, 0x02, 0xC2, 0xD2, 0x11, 0x93, 0x03, 0x80,
+ 0x09, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xC9, 0xF3, 0x11, 0x93, 0xDC, 0xF7,
+ 0x41, 0x02, 0x19, 0xD3, 0xDC, 0xF7, 0x11, 0x93,
+ 0xDB, 0xF7, 0x09, 0xA3, 0x00, 0x20, 0x19, 0xD3,
+ 0xDB, 0xF7, 0x11, 0x93, 0xB5, 0xEC, 0x19, 0xD3,
+ 0x04, 0x80, 0x12, 0x95, 0xB4, 0xEC, 0x1A, 0xD5,
+ 0x05, 0x80, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xC3, 0x96, 0x1B, 0xD7, 0xB5, 0xEC, 0x40, 0x94,
+ 0x1A, 0xD5, 0xB4, 0xEC, 0x19, 0xD3, 0xF2, 0xBD,
+ 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B,
+ 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19, 0xD3,
+ 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xD3,
+ 0x03, 0x82, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x47, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0x01, 0x82, 0xC5, 0xD2, 0x40, 0x94, 0x01, 0xD4,
+ 0x13, 0x97, 0xB8, 0xEC, 0x02, 0xD6, 0x03, 0x95,
+ 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x05, 0x13, 0x97,
+ 0x03, 0xEC, 0x01, 0x27, 0x02, 0x99, 0xC4, 0x92,
+ 0x03, 0x03, 0xC2, 0xD2, 0x14, 0x99, 0xBA, 0xEC,
+ 0x03, 0x09, 0x1C, 0xD9, 0xBA, 0xEC, 0x12, 0x95,
+ 0x04, 0x82, 0x0A, 0xB3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0x29, 0xF5, 0x01, 0x92,
+ 0x03, 0xD2, 0x0A, 0xA3, 0x02, 0x00, 0x19, 0xD3,
+ 0x04, 0x82, 0x02, 0x96, 0x0B, 0x05, 0x01, 0x00,
+ 0x1A, 0xD5, 0xB8, 0xEC, 0xC5, 0x92, 0x43, 0x42,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x37, 0xF4, 0x42, 0x44,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x93,
+ 0xBF, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x37, 0xF4, 0x0C, 0x49, 0xD3, 0x08, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x37, 0xF4, 0x11, 0x63, 0x07, 0x82,
+ 0x11, 0xA3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93,
+ 0x79, 0x93, 0x79, 0x93, 0x03, 0xD2, 0xC5, 0x94,
+ 0x0A, 0xB5, 0xFC, 0xFF, 0x04, 0xD4, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x46, 0xF4,
+ 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x8E,
+ 0x0F, 0x9F, 0x4D, 0xF4, 0xC5, 0x98, 0x0C, 0x03,
+ 0xFF, 0xFF, 0x42, 0x42, 0x02, 0x8E, 0x0F, 0x9F,
+ 0x74, 0xF4, 0x0A, 0x95, 0xBB, 0xEC, 0x42, 0x92,
+ 0x19, 0xD3, 0xB9, 0xEC, 0xC5, 0x96, 0x43, 0x46,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x66, 0xF4, 0x0B, 0x07,
+ 0xFC, 0xFF, 0xC5, 0xD6, 0xD2, 0x98, 0x1C, 0xD9,
+ 0xC8, 0xBC, 0xD2, 0x96, 0x1B, 0xD7, 0xCA, 0xBC,
+ 0x09, 0x03, 0xFF, 0xFF, 0x40, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x52, 0xF4, 0x19, 0xD3, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x72, 0xF4,
+ 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xC2, 0xD2,
+ 0x0F, 0x9F, 0x74, 0xF4, 0x1A, 0xD5, 0x93, 0xEC,
+ 0x03, 0x98, 0x40, 0x48, 0x02, 0x5E, 0x0F, 0x9F,
+ 0xA1, 0xF4, 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42,
+ 0x02, 0x9E, 0x0F, 0x9F, 0x84, 0xF4, 0x04, 0x94,
+ 0x48, 0x44, 0x02, 0x4E, 0x0F, 0x9F, 0x8F, 0xF4,
+ 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xA1, 0xF4,
+ 0x11, 0x93, 0x04, 0x82, 0x41, 0xB2, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xA1, 0xF4, 0x41, 0x96,
+ 0x01, 0xD6, 0x0A, 0x65, 0xBD, 0x43, 0x02, 0x99,
+ 0xC4, 0x92, 0x09, 0xA3, 0x80, 0x00, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0xFA, 0xF4, 0xC5, 0x98, 0x43, 0x48, 0x02, 0x9E,
+ 0x0F, 0x9F, 0xFA, 0xF4, 0x4F, 0x96, 0x0C, 0xB3,
+ 0x01, 0x00, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xAE, 0xF4, 0x47, 0x96, 0x11, 0x93, 0xB7, 0xEC,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4,
+ 0x11, 0x93, 0xB8, 0xEC, 0x41, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0xD6, 0xF4, 0x12, 0x95, 0x00, 0x82,
+ 0x0A, 0x05, 0xFF, 0xAF, 0x05, 0xD4, 0xC8, 0xD6,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x7B, 0xF7, 0x42, 0x00,
+ 0x05, 0x96, 0xC3, 0x94, 0x01, 0xB5, 0x40, 0x44,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xD6, 0xF4, 0x06, 0x98,
+ 0x50, 0x98, 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x98,
+ 0x1C, 0xD9, 0xA2, 0xBC, 0x40, 0x92, 0x03, 0xD2,
+ 0x0F, 0x9F, 0xFF, 0xF4, 0x03, 0x94, 0x40, 0x44,
+ 0x02, 0x5E, 0x0F, 0x9F, 0xE3, 0xF4, 0x0A, 0x65,
+ 0x5E, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x48, 0xA2,
+ 0xC2, 0xD2, 0x0F, 0x9F, 0xFF, 0xF4, 0x11, 0x93,
+ 0xB8, 0xEC, 0x0C, 0x99, 0xBB, 0xEC, 0x04, 0x03,
+ 0x04, 0x96, 0x13, 0x25, 0x03, 0xEC, 0xC1, 0xD4,
+ 0x11, 0x93, 0xBA, 0xEC, 0x19, 0x05, 0xBA, 0xEC,
+ 0x1B, 0xD7, 0x01, 0x82, 0x0A, 0x65, 0xFD, 0x7D,
+ 0x02, 0x99, 0xC4, 0x92, 0x43, 0xA2, 0xC2, 0xD2,
+ 0x41, 0x92, 0x01, 0xD2, 0x03, 0x94, 0x40, 0x44,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5, 0x11, 0x93,
+ 0xB9, 0xEC, 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x0B, 0xF5, 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3,
+ 0xBA, 0xEC, 0x19, 0xD3, 0xBB, 0xEC, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x13, 0xF5,
+ 0x41, 0x98, 0x1C, 0xD9, 0xB7, 0xEC, 0x11, 0x93,
+ 0xBF, 0xEC, 0x41, 0x42, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x24, 0xF5, 0x11, 0x93, 0x00, 0x82, 0x19, 0xD3,
+ 0x02, 0x82, 0x0A, 0x65, 0xFD, 0x7D, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x00, 0x01, 0xC2, 0xD2,
+ 0x40, 0x98, 0x1C, 0xD9, 0xBF, 0xEC, 0x0F, 0x9F,
+ 0x2C, 0xF5, 0x01, 0x92, 0x19, 0xD3, 0xB7, 0xEC,
+ 0x01, 0x94, 0x40, 0x44, 0x02, 0x5E, 0x0F, 0x9F,
+ 0x38, 0xF5, 0x0A, 0x65, 0xEA, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF, 0xC2, 0xD2,
+ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x12, 0x95, 0x03, 0x80,
+ 0x0A, 0xB3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x57, 0xF5, 0x0A, 0xB7, 0x00, 0x08,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0x5A, 0xF5,
+ 0x11, 0x93, 0x03, 0xEC, 0x41, 0x02, 0x09, 0xB3,
+ 0xFE, 0xFF, 0x12, 0x95, 0x07, 0x80, 0x01, 0x45,
+ 0x02, 0x8E, 0x0F, 0x9F, 0x5A, 0xF5, 0x41, 0x92,
+ 0x0F, 0x9F, 0x5B, 0xF5, 0x40, 0x92, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x41, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x09, 0xA3, 0x40, 0x00, 0xC2, 0xD2,
+ 0x13, 0x97, 0x6E, 0xEC, 0x0B, 0x47, 0xA0, 0x00,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x86, 0xF5, 0x09, 0x63,
+ 0x08, 0x43, 0x0A, 0x65, 0xFF, 0x5F, 0x01, 0x99,
+ 0xC4, 0xD4, 0x0A, 0x95, 0x9B, 0xEC, 0xD2, 0x96,
+ 0x1B, 0xD7, 0xFA, 0xBC, 0xD2, 0x96, 0xC4, 0xD6,
+ 0xD2, 0x98, 0x1C, 0xD9, 0xFA, 0xBC, 0xD2, 0x96,
+ 0xC1, 0xD6, 0xC2, 0x94, 0x1A, 0xD5, 0xFA, 0xBC,
+ 0x0F, 0x9F, 0xC4, 0xF5, 0x0C, 0x69, 0xFF, 0x6F,
+ 0x1C, 0xD9, 0xF8, 0xBC, 0x0B, 0x47, 0x10, 0x95,
+ 0x02, 0x5E, 0x0F, 0x9F, 0x9E, 0xF5, 0x0A, 0x95,
+ 0x6F, 0xEC, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99,
+ 0xC4, 0xD6, 0xD2, 0x96, 0x1B, 0xD7, 0xF8, 0xBC,
+ 0x0C, 0x69, 0xEE, 0x6A, 0xC1, 0xD8, 0xC2, 0x94,
+ 0x1A, 0xD5, 0xF8, 0xBC, 0x40, 0x92, 0xC5, 0xD2,
+ 0x11, 0x43, 0xC1, 0xEC, 0x02, 0x0E, 0x0F, 0x9F,
+ 0xC1, 0xF5, 0xC5, 0x94, 0x0A, 0x03, 0x71, 0xEC,
+ 0xC1, 0x94, 0x1A, 0xD5, 0xFA, 0xBC, 0x11, 0x93,
+ 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0xB3, 0xF5, 0x0A, 0x95, 0x6F, 0xEC, 0xC8, 0xD4,
+ 0x40, 0xF0, 0x9C, 0xF7, 0x19, 0xD3, 0xF8, 0xBC,
+ 0x41, 0x00, 0xC5, 0x96, 0x41, 0x06, 0xC5, 0xD6,
+ 0x13, 0x47, 0xC1, 0xEC, 0x02, 0x1E, 0x0F, 0x9F,
+ 0xA5, 0xF5, 0x40, 0x98, 0x1C, 0xD9, 0xFA, 0xBC,
+ 0x40, 0x92, 0x19, 0xD3, 0x6E, 0xEC, 0x19, 0xD3,
+ 0xC1, 0xEC, 0x0A, 0x65, 0x52, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3,
+ 0xBF, 0xFF, 0xC2, 0xD2, 0x41, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x43, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x06, 0x92, 0x01, 0xD2, 0x0A, 0x65,
+ 0xF0, 0x6A, 0x0B, 0x97, 0x6F, 0xEC, 0x02, 0x99,
+ 0xC4, 0x98, 0xD3, 0xD8, 0x02, 0xD6, 0x0A, 0x03,
+ 0x02, 0x00, 0x01, 0x97, 0xC3, 0x98, 0x02, 0x96,
+ 0xC3, 0xD8, 0x01, 0x96, 0xC1, 0xD6, 0x1A, 0xD5,
+ 0x6E, 0xEC, 0xC5, 0x98, 0x14, 0x99, 0x6F, 0xEC,
+ 0xC2, 0xD8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0x92,
+ 0xC8, 0xD2, 0x40, 0xF0, 0xD9, 0xF5, 0x41, 0x00,
+ 0x11, 0x93, 0xC0, 0xEC, 0x40, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x13, 0xF6, 0x42, 0x42, 0x02, 0x5E,
+ 0x0F, 0x9F, 0x10, 0xF6, 0x0A, 0x65, 0xFE, 0x7F,
+ 0x02, 0x97, 0xC3, 0x92, 0x42, 0xA2, 0xC2, 0xD2,
+ 0x40, 0x92, 0x19, 0xD3, 0xC0, 0xEC, 0x0A, 0x65,
+ 0xEB, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0xC0, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xE9, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xBF, 0xFF,
+ 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x63, 0x20, 0x08, 0x0B, 0x01, 0x00, 0x11, 0x93,
+ 0xAF, 0xBC, 0x47, 0xB2, 0x59, 0x95, 0x5A, 0x95,
+ 0x12, 0xA5, 0xBF, 0xBC, 0x0A, 0xB3, 0x01, 0x00,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x35, 0xF6,
+ 0x41, 0x04, 0x05, 0x93, 0x40, 0x96, 0x20, 0xD6,
+ 0x62, 0x97, 0x0F, 0x9F, 0x44, 0xF6, 0x14, 0x99,
+ 0xFC, 0xBC, 0xD1, 0xD8, 0x14, 0x99, 0xFE, 0xBC,
+ 0xD1, 0xD8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xD8,
+ 0x20, 0x98, 0x03, 0x49, 0x02, 0x1E, 0x0F, 0x9F,
+ 0x3B, 0xF6, 0xC5, 0x92, 0x62, 0x42, 0x02, 0x4E,
+ 0x0F, 0x9F, 0x5D, 0xF6, 0x02, 0x8E, 0x0F, 0x9F,
+ 0x57, 0xF6, 0x61, 0x42, 0x02, 0x4E, 0x0F, 0x9F,
+ 0x81, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6, 0x63, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xA4, 0xF6, 0x0F, 0x9F,
+ 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00, 0x0C, 0x99,
+ 0x71, 0xEC, 0x0B, 0x05, 0xFF, 0xFF, 0x40, 0x96,
+ 0x0F, 0x9F, 0x6A, 0xF6, 0xD1, 0x96, 0xD4, 0xD6,
+ 0x20, 0x96, 0x41, 0x06, 0x20, 0xD6, 0x02, 0x47,
+ 0x02, 0x1E, 0x0F, 0x9F, 0x66, 0xF6, 0x1A, 0xD5,
+ 0xC1, 0xEC, 0x0A, 0x65, 0xEB, 0x43, 0x02, 0x99,
+ 0xC4, 0x92, 0x09, 0xA3, 0xC0, 0x00, 0xC2, 0xD2,
+ 0x0A, 0x65, 0xE9, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x0F, 0x9F,
+ 0xAE, 0xF6, 0x0A, 0x03, 0xFE, 0xFF, 0x61, 0x95,
+ 0x40, 0x98, 0x20, 0xD8, 0x02, 0x49, 0x02, 0x0E,
+ 0x0F, 0x9F, 0xAE, 0xF6, 0x0D, 0x03, 0x01, 0x00,
+ 0x21, 0xD2, 0x20, 0x92, 0x05, 0x03, 0x42, 0x02,
+ 0xC8, 0xD2, 0x21, 0x96, 0xC3, 0x92, 0x42, 0x06,
+ 0x21, 0xD6, 0xC8, 0xD2, 0x22, 0xD4, 0x40, 0xF0,
+ 0x01, 0xF1, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08,
+ 0x20, 0xD8, 0x22, 0x94, 0x02, 0x49, 0x02, 0x1E,
+ 0x0F, 0x9F, 0x8D, 0xF6, 0x0F, 0x9F, 0xAE, 0xF6,
+ 0x0D, 0x03, 0x03, 0x00, 0xC8, 0xD2, 0x02, 0x92,
+ 0xC8, 0xD2, 0x01, 0x96, 0xC8, 0xD6, 0x40, 0xF0,
+ 0xB1, 0xF6, 0x43, 0x00, 0x63, 0x00, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x45, 0x20, 0x08, 0x0B,
+ 0x01, 0x00, 0x0D, 0x03, 0x08, 0x00, 0x08, 0x94,
+ 0xC5, 0xD4, 0x09, 0x05, 0x01, 0x00, 0xC2, 0x94,
+ 0x03, 0xD4, 0x42, 0x02, 0xC1, 0x92, 0x01, 0xD2,
+ 0x02, 0x97, 0xC5, 0x94, 0x0A, 0x83, 0xFF, 0xFF,
+ 0x11, 0xB3, 0x2C, 0x93, 0x09, 0xB3, 0xFB, 0xFF,
+ 0x19, 0xD3, 0x2C, 0x93, 0x03, 0x92, 0x40, 0x42,
+ 0x02, 0x4E, 0x0F, 0x9F, 0xE4, 0xF6, 0x01, 0x94,
+ 0xD2, 0x92, 0x19, 0xD3, 0x2C, 0x93, 0x01, 0xD4,
+ 0x02, 0x94, 0x12, 0x95, 0x2C, 0x93, 0x44, 0xA4,
+ 0x1A, 0xD5, 0x2C, 0x93, 0x0A, 0xB5, 0xFB, 0xFF,
+ 0x1A, 0xD5, 0x2C, 0x93, 0x0B, 0x07, 0xFF, 0xFF,
+ 0x40, 0x46, 0x02, 0x5E, 0x0F, 0x9F, 0xCF, 0xF6,
+ 0x09, 0x63, 0xD4, 0x6C, 0x01, 0x95, 0xC2, 0x96,
+ 0xC5, 0x94, 0x02, 0xA7, 0xC1, 0xD6, 0x03, 0x92,
+ 0x54, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0xF4, 0xF6,
+ 0x0A, 0x83, 0xFF, 0xFF, 0x1B, 0xB3, 0x2C, 0x93,
+ 0x45, 0x00, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xD3, 0xF2, 0xBD, 0x40, 0xF0, 0x3B, 0xF5,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x08, 0xF7,
+ 0x40, 0xF0, 0x94, 0xF2, 0x0F, 0x9F, 0x16, 0xF7,
+ 0x40, 0x96, 0xC8, 0xD6, 0x09, 0x93, 0x91, 0xEC,
+ 0xC8, 0xD2, 0x40, 0xF0, 0x2A, 0xEF, 0x0A, 0x65,
+ 0xFE, 0x7F, 0x02, 0x97, 0xC3, 0x92, 0x44, 0xA2,
+ 0xC2, 0xD2, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x0A, 0x65,
+ 0xE8, 0x43, 0x02, 0x97, 0xC3, 0x92, 0x09, 0xA3,
+ 0x40, 0x00, 0xC2, 0xD2, 0x0A, 0x65, 0xEA, 0x43,
+ 0x02, 0x97, 0xC3, 0x92, 0x09, 0xB3, 0xFB, 0xFF,
+ 0xC2, 0xD2, 0x40, 0x92, 0x19, 0xD3, 0x2D, 0xBC,
+ 0x0A, 0x65, 0xD8, 0x43, 0x02, 0x97, 0xC3, 0x92,
+ 0x09, 0xB3, 0xBF, 0xFF, 0xC2, 0xD2, 0x88, 0x98,
+ 0x90, 0x9A, 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00,
+ 0x09, 0x63, 0xEA, 0x43, 0x01, 0x97, 0xC3, 0x94,
+ 0x44, 0xA4, 0xC1, 0xD4, 0x11, 0x93, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x4E, 0x0F, 0x9F, 0x6F, 0xF7,
+ 0x12, 0x95, 0x93, 0xEC, 0x0B, 0x67, 0x36, 0x43,
+ 0xD2, 0x98, 0x1C, 0xD9, 0xC8, 0xBC, 0xD2, 0x98,
+ 0x03, 0x93, 0xC1, 0xD8, 0x11, 0x93, 0xB9, 0xEC,
+ 0x09, 0x03, 0xFF, 0xFF, 0x19, 0xD3, 0xB9, 0xEC,
+ 0x40, 0x42, 0x02, 0x5E, 0x0F, 0x9F, 0x48, 0xF7,
+ 0x19, 0xD3, 0xB8, 0xEC, 0x19, 0xD3, 0xBA, 0xEC,
+ 0x0A, 0x05, 0xFE, 0xFF, 0xCA, 0xD2, 0xCA, 0xD2,
+ 0xC2, 0xD2, 0x0A, 0x65, 0x5E, 0x43, 0x02, 0x97,
+ 0xC3, 0x92, 0x48, 0xA2, 0xC2, 0xD2, 0x0A, 0x65,
+ 0xEA, 0x43, 0x02, 0x99, 0xC4, 0x92, 0x09, 0xB3,
+ 0xFB, 0xFF, 0x0F, 0x9F, 0x78, 0xF7, 0x11, 0x93,
+ 0x03, 0xEC, 0x19, 0xD3, 0x01, 0x82, 0x0A, 0x65,
+ 0xFD, 0x7D, 0x02, 0x97, 0xC3, 0x92, 0x43, 0xA2,
+ 0xC2, 0xD2, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x03, 0x92, 0x04, 0x96,
+ 0x0D, 0x5E, 0x50, 0x46, 0x02, 0x0E, 0x40, 0x92,
+ 0x09, 0xEE, 0x44, 0x46, 0x04, 0x0E, 0x59, 0x93,
+ 0x44, 0x26, 0x04, 0x5E, 0x46, 0xEE, 0x41, 0x93,
+ 0x41, 0x26, 0x43, 0x4E, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x40, 0xF0,
+ 0xB1, 0xFE, 0x88, 0x98, 0x90, 0x9A, 0x88, 0xDA,
+ 0x08, 0x0B, 0x01, 0x00, 0x88, 0x98, 0x90, 0x9A,
+ 0x88, 0xDA, 0x08, 0x0B, 0x01, 0x00, 0x03, 0x94,
+ 0x1A, 0xD5, 0xA3, 0xF7, 0x11, 0x93, 0x00, 0x90,
+ 0x88, 0x98, 0x90, 0x9A, 0x1D, 0x00, 0x1A, 0x00,
+ 0x03, 0x00, 0x03, 0x00, 0x18, 0x00, 0x19, 0x00,
+ 0x1A, 0x00, 0x1B, 0x00, 0x16, 0x00, 0x21, 0x00,
+ 0x12, 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00,
+ 0x19, 0x00, 0x19, 0x00, 0x21, 0x00, 0x2D, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x69,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x5F, 0xF2, 0xCD, 0xF7, 0x00, 0x00, 0x74, 0xF2,
+ 0xCD, 0xF7, 0x00, 0x00, 0xB9, 0xF2, 0xCA, 0xF7,
+ 0xD1, 0xF7, 0x00, 0x00, 0x97, 0xF3, 0xCD, 0xF7,
+ 0x05, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * current zd1211b firmware version.
+ */
+#define ZD1211B_FIRMWARE_VER 4705
+
+uint8_t zd1211b_firmware[] = {
+ 0x08, 0x91, 0xff, 0xed, 0x09, 0x93, 0x1e, 0xee, 0xd1, 0x94, 0x11,
+ 0xee, 0x88, 0xd4, 0xd1, 0x96, 0xd1, 0x98, 0x5c, 0x99, 0x5c, 0x99,
+ 0x4c, 0x99, 0x04, 0x9d, 0xd1, 0x98, 0xd1, 0x9a, 0x03, 0xee, 0xf4,
+ 0x94, 0xd3, 0xd4, 0x41, 0x2a, 0x40, 0x4a, 0x45, 0xbe, 0x88, 0x92,
+ 0x41, 0x24, 0x40, 0x44, 0x53, 0xbe, 0x40, 0xf0, 0x4e, 0xee, 0x41,
+ 0xee, 0x98, 0x9a, 0x72, 0xf7, 0x02, 0x00, 0x1f, 0xec, 0x00, 0x00,
+ 0xb2, 0xf8, 0x4d, 0x00, 0xa1, 0xec, 0x00, 0x00, 0x43, 0xf7, 0x22,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xd8,
+ 0xa0, 0x90, 0x98, 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x5a,
+ 0xf0, 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x0a, 0xef,
+ 0xa0, 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x97, 0xf0, 0xa0,
+ 0x90, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x94, 0xf6, 0xa0, 0x90,
+ 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x95, 0xf5, 0xa0, 0x90, 0x98,
+ 0x9a, 0x98, 0x9a, 0xa0, 0xd8, 0x40, 0xf0, 0x34, 0xf7, 0xa0, 0x90,
+ 0x98, 0x9a, 0x88, 0xda, 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x40,
+ 0xf0, 0x8e, 0xee, 0x40, 0x96, 0x0a, 0x65, 0x00, 0x7d, 0x11, 0x93,
+ 0x76, 0xf7, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x57, 0xee, 0x40,
+ 0xf1, 0x1b, 0xd7, 0x76, 0xf7, 0xc5, 0x92, 0x02, 0x99, 0x41, 0x92,
+ 0xc4, 0xd2, 0x40, 0x92, 0xc4, 0xd2, 0x0f, 0x9f, 0x95, 0xf8, 0x0f,
+ 0x9f, 0x57, 0xee, 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0x19, 0xd3, 0x12, 0x95, 0x19,
+ 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19, 0xd3, 0x03, 0x82,
+ 0x09, 0x93, 0x65, 0xf7, 0x19, 0xd3, 0x91, 0xec, 0x40, 0xf0, 0x07,
+ 0xf2, 0x40, 0xf0, 0x75, 0xf3, 0x11, 0x93, 0x04, 0xec, 0x42, 0x42,
+ 0x02, 0x5e, 0x0f, 0x9f, 0x8c, 0xee, 0x40, 0x92, 0x19, 0xd3, 0x04,
+ 0xec, 0x40, 0xf0, 0xe0, 0xf1, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff,
+ 0xfd, 0x19, 0xd3, 0x44, 0x96, 0x40, 0xf0, 0x2d, 0xf7, 0x40, 0xf0,
+ 0x6d, 0xee, 0x4b, 0x62, 0x0a, 0x95, 0x2e, 0xee, 0xd1, 0xd4, 0x0b,
+ 0x97, 0x2b, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x00, 0xee, 0xd1, 0xd4,
+ 0x0b, 0x97, 0x2f, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x34, 0xee, 0xd1,
+ 0xd4, 0x0b, 0x97, 0x39, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x3e, 0xee,
+ 0xd1, 0xd4, 0x0b, 0x97, 0x43, 0xee, 0xd1, 0xd6, 0x0a, 0x95, 0x2e,
+ 0xee, 0xd1, 0xd4, 0x0b, 0x97, 0x48, 0xee, 0xd1, 0xd6, 0x0a, 0x95,
+ 0x49, 0xee, 0xc1, 0xd4, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97, 0xc3,
+ 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x43, 0xf1, 0x09, 0x93, 0x01, 0x3f,
+ 0x19, 0xd3, 0xc0, 0x85, 0x11, 0x93, 0x44, 0x96, 0x09, 0xb3, 0xff,
+ 0xfc, 0x19, 0xd3, 0x44, 0x96, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x03, 0x00, 0x03, 0x96, 0x41,
+ 0x02, 0x03, 0x99, 0xc4, 0x94, 0x42, 0x04, 0xc1, 0x04, 0xc2, 0x94,
+ 0xc3, 0xd4, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0x13, 0x97, 0x95, 0xec,
+ 0x1b, 0xd7, 0x02, 0x80, 0x11, 0x93, 0x99, 0xec, 0x19, 0xd3, 0x7c,
+ 0x96, 0x0b, 0x97, 0xa0, 0x00, 0x1b, 0xd7, 0x6e, 0xec, 0x0a, 0x65,
+ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xff, 0xbf, 0x11,
+ 0xa3, 0x9a, 0xec, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xe9,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01,
+ 0x00, 0x14, 0x99, 0x03, 0x80, 0x0c, 0xb3, 0x00, 0x10, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x3d, 0xf0, 0x11, 0x93, 0x9f, 0xec, 0x41,
+ 0x02, 0x19, 0xd3, 0x9f, 0xec, 0x11, 0x93, 0x74, 0xf7, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x2a, 0xef, 0x0a, 0x65, 0xfe, 0x7f, 0x02,
+ 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x04, 0xc2, 0xd2, 0x0f, 0x9f,
+ 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x02, 0xd2, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x76, 0xef, 0x41, 0x92, 0x19, 0xd3, 0x94, 0xec,
+ 0x19, 0xd3, 0x9f, 0xec, 0x12, 0x95, 0x02, 0x80, 0x1a, 0xd5, 0x95,
+ 0xec, 0x13, 0x97, 0x7c, 0x96, 0x1b, 0xd7, 0x99, 0xec, 0x0a, 0x65,
+ 0x0e, 0x42, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0x00, 0x40, 0x19,
+ 0xd3, 0x9a, 0xec, 0x09, 0x63, 0x00, 0x40, 0xc2, 0xd2, 0x02, 0x94,
+ 0x1a, 0xd5, 0x7c, 0x96, 0x0c, 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x56, 0xef, 0x0c, 0xb3, 0xff, 0x07, 0x0f, 0x9f,
+ 0x5a, 0xef, 0x11, 0x93, 0x06, 0x80, 0x09, 0xb3, 0xff, 0x07, 0x09,
+ 0x03, 0x00, 0xa0, 0x19, 0xd3, 0x97, 0xec, 0x40, 0x98, 0x0b, 0x97,
+ 0x9c, 0xec, 0x04, 0x95, 0x03, 0x05, 0x14, 0x03, 0x97, 0xec, 0x46,
+ 0x02, 0xc1, 0x92, 0xc2, 0xd2, 0x41, 0x08, 0x42, 0x48, 0x02, 0x9e,
+ 0x0f, 0x9f, 0x61, 0xef, 0x11, 0x93, 0x97, 0xec, 0xc1, 0x92, 0xc5,
+ 0xd2, 0x5f, 0xb2, 0x19, 0xd3, 0x9b, 0xec, 0x0f, 0x9f, 0x79, 0xef,
+ 0x13, 0x97, 0x98, 0xec, 0xc5, 0xd6, 0x11, 0x93, 0x03, 0x80, 0x09,
+ 0xb3, 0x00, 0x08, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x8f, 0xef,
+ 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11,
+ 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x10, 0x19, 0xd3, 0x79, 0xf7,
+ 0x40, 0x98, 0x1c, 0xd9, 0x9b, 0xec, 0x12, 0x95, 0x9b, 0xec, 0x40,
+ 0x44, 0x02, 0x4e, 0x0f, 0x9f, 0x2c, 0xf0, 0x0a, 0xb3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xad, 0xef, 0x0a, 0xb3, 0x07,
+ 0x00, 0x09, 0x05, 0xa9, 0xec, 0xc2, 0x94, 0x01, 0xd4, 0x09, 0x03,
+ 0xa1, 0xec, 0xc1, 0x92, 0x19, 0xd3, 0x9b, 0xec, 0xc5, 0x94, 0x0a,
+ 0xb5, 0x00, 0xff, 0x01, 0xa5, 0xc5, 0xd4, 0x0f, 0x9f, 0xb9, 0xef,
+ 0x0a, 0x05, 0xff, 0xff, 0x0a, 0x03, 0xb1, 0xec, 0xc1, 0x92, 0x01,
+ 0xd2, 0x1a, 0xd5, 0x9b, 0xec, 0xc5, 0x96, 0x0b, 0x07, 0xff, 0xff,
+ 0xc5, 0xd6, 0x11, 0x93, 0x97, 0xec, 0xc5, 0x98, 0xc1, 0xd8, 0x11,
+ 0x93, 0x97, 0xec, 0x09, 0x05, 0x0b, 0x00, 0x03, 0xd4, 0xc2, 0x96,
+ 0x06, 0xd6, 0x7b, 0x95, 0x7a, 0x95, 0x4c, 0x02, 0xc1, 0x92, 0x59,
+ 0x93, 0x59, 0x93, 0x01, 0xa5, 0x01, 0x98, 0x0c, 0xf5, 0x7b, 0x93,
+ 0x09, 0x09, 0x01, 0x00, 0x06, 0x92, 0x09, 0xb3, 0xff, 0x00, 0x04,
+ 0xd2, 0x5c, 0x93, 0x59, 0x93, 0x04, 0x94, 0x01, 0xa5, 0x03, 0x96,
+ 0xc3, 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4c, 0x02, 0x05, 0xd2, 0xc1,
+ 0x92, 0x09, 0xb3, 0x00, 0xff, 0x7c, 0x95, 0x7a, 0x95, 0x02, 0xa3,
+ 0x05, 0x98, 0xc4, 0xd2, 0x12, 0x95, 0x97, 0xec, 0x45, 0x04, 0x02,
+ 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x00, 0x01, 0xc2, 0xd2, 0x12, 0x95,
+ 0x9b, 0xec, 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x01, 0xf0, 0x12, 0x95, 0x97, 0xec, 0x4a, 0x04, 0x02, 0x99,
+ 0xc4, 0x92, 0x01, 0x98, 0x0c, 0xf3, 0x7b, 0x93, 0x41, 0x02, 0x0f,
+ 0x9f, 0x22, 0xf0, 0x43, 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0x23, 0xf0,
+ 0x11, 0x93, 0x97, 0xec, 0x42, 0x02, 0x0a, 0x05, 0xff, 0xff, 0xc1,
+ 0xd4, 0x11, 0x93, 0x97, 0xec, 0x4a, 0x02, 0x12, 0x95, 0x60, 0x96,
+ 0xc1, 0xd4, 0x12, 0x95, 0x97, 0xec, 0x4b, 0x04, 0x02, 0x97, 0xc3,
+ 0x92, 0x09, 0xb3, 0x1f, 0xff, 0xc2, 0xd2, 0x12, 0x95, 0x97, 0xec,
+ 0x4b, 0x04, 0x11, 0x93, 0x62, 0x96, 0x41, 0x93, 0x59, 0x93, 0x02,
+ 0x99, 0xc4, 0xa2, 0xc2, 0xd2, 0xc5, 0x92, 0x19, 0xd3, 0x98, 0xec,
+ 0x0a, 0x95, 0x0c, 0x02, 0x1a, 0xd5, 0x02, 0x80, 0x0f, 0x9f, 0x57,
+ 0xf0, 0x09, 0x63, 0xfe, 0x7f, 0x01, 0x97, 0xc3, 0x94, 0x0a, 0xa5,
+ 0x00, 0x04, 0xc1, 0xd4, 0x11, 0x93, 0x9f, 0xec, 0x09, 0xa3, 0x00,
+ 0x01, 0x19, 0xd3, 0x9f, 0xec, 0x40, 0xf0, 0xdf, 0xee, 0x0f, 0x9f,
+ 0x57, 0xf0, 0x11, 0x93, 0x94, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f,
+ 0x9f, 0x4c, 0xf0, 0x40, 0xf0, 0xdf, 0xee, 0x11, 0x93, 0x95, 0xec,
+ 0x44, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x57, 0xf0, 0x48,
+ 0x98, 0x1c, 0xd9, 0x02, 0x80, 0x11, 0x93, 0x91, 0xec, 0x41, 0x22,
+ 0x0a, 0x95, 0x57, 0xf0, 0x88, 0xd4, 0x88, 0xdc, 0x91, 0x9a, 0x47,
+ 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00,
+ 0x11, 0x93, 0x04, 0x82, 0x48, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x6e, 0xf0, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92,
+ 0x09, 0xb3, 0xff, 0xfe, 0xc2, 0xd2, 0x41, 0x92, 0x19, 0xd3, 0xbf,
+ 0xec, 0x11, 0x93, 0x04, 0x82, 0x43, 0xb2, 0x12, 0x95, 0x03, 0x82,
+ 0x02, 0xb3, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf0, 0x0a,
+ 0xb3, 0x00, 0xff, 0x48, 0xa2, 0x19, 0xd3, 0x03, 0x82, 0x40, 0xf0,
+ 0x82, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f,
+ 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x07, 0x82, 0x11, 0x43, 0x03, 0xec,
+ 0x02, 0x0e, 0x0f, 0x9f, 0x95, 0xf0, 0x11, 0x93, 0x03, 0x82, 0x09,
+ 0xa3, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82, 0x40, 0x96, 0x1b, 0xd7,
+ 0xbf, 0xec, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x11, 0x93, 0x20, 0xbc, 0xc8, 0xd2, 0x40, 0xf0, 0xe9, 0xf0,
+ 0x41, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08,
+ 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x05, 0x00, 0x05, 0x94, 0x41, 0x02,
+ 0xc1, 0x92, 0x01, 0x97, 0xc3, 0x96, 0xc2, 0xd6, 0x0a, 0x45, 0x00,
+ 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0xc1, 0x92, 0x41, 0xb2,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xe6, 0xf0, 0x11, 0x93, 0xc0,
+ 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe6, 0xf0, 0x41, 0x98,
+ 0x1c, 0xd9, 0xc0, 0xec, 0x12, 0x95, 0x02, 0x80, 0x01, 0xd4, 0x40,
+ 0xf0, 0xfe, 0xf1, 0x0b, 0x67, 0xfd, 0x7d, 0x03, 0x99, 0xc4, 0x92,
+ 0x0c, 0x99, 0x96, 0x03, 0x1c, 0xd9, 0x06, 0x82, 0x41, 0x98, 0x1c,
+ 0xd9, 0x02, 0x82, 0x42, 0x98, 0x1c, 0xd9, 0x05, 0x82, 0x0c, 0x69,
+ 0x80, 0x7f, 0x1c, 0xd9, 0x00, 0xb0, 0x09, 0xa3, 0x00, 0x01, 0xc3,
+ 0xd2, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0xe4, 0xf0, 0x42, 0xa4, 0x1a, 0xd5, 0x02, 0x80, 0x42,
+ 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x42, 0x20, 0x08, 0x0b,
+ 0x01, 0x00, 0x05, 0x92, 0xc5, 0xd2, 0x60, 0xb2, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0xf6, 0xf0, 0x40, 0xf0, 0xd2, 0xf6, 0xc5, 0x94,
+ 0x0a, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xff,
+ 0xf0, 0x40, 0xf0, 0xc0, 0xf5, 0xc5, 0x96, 0x0b, 0xb3, 0x40, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x08, 0xf1, 0x40, 0xf0, 0xfa,
+ 0xf4, 0xc5, 0x94, 0x0a, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0x70, 0xf1, 0x13, 0x97, 0x21, 0xbc, 0x01, 0xd6, 0x0b,
+ 0xb3, 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1a, 0xf1,
+ 0x40, 0xf0, 0x62, 0xfb, 0x01, 0x94, 0x0a, 0xb3, 0x04, 0x00, 0x40,
+ 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf1, 0x40, 0xf0, 0x6c, 0xfb,
+ 0x01, 0x96, 0x0b, 0xb3, 0x01, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x4c, 0xf1, 0x40, 0xf0, 0xb0, 0xfa, 0x41, 0x92, 0x19, 0xd3,
+ 0x73, 0xf7, 0x11, 0x93, 0x03, 0xec, 0x09, 0x43, 0x40, 0x00, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x39, 0xf1, 0x40, 0x94, 0x1a, 0xd5, 0x73, 0xf7,
+ 0x11, 0x93, 0x00, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55,
+ 0xf1, 0x11, 0x93, 0xc1, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x55, 0xf1, 0x40, 0xf0, 0xe0, 0xf1, 0x41, 0x96, 0x1b, 0xd7, 0xc1,
+ 0xec, 0x0f, 0x9f, 0x55, 0xf1, 0x01, 0x94, 0x0a, 0xb3, 0x08, 0x00,
+ 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x55, 0xf1, 0x40, 0xf0, 0x7c,
+ 0xfb, 0x01, 0x96, 0x0b, 0xb3, 0x10, 0x00, 0x40, 0x42, 0x02, 0x4e,
+ 0x0f, 0x9f, 0x5e, 0xf1, 0x40, 0xf0, 0x87, 0xfb, 0x11, 0x93, 0x10,
+ 0xec, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x67, 0xf1, 0x44, 0x92,
+ 0x0f, 0x9f, 0x6b, 0xf1, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x6d,
+ 0xf1, 0x19, 0xd3, 0x0b, 0xbc, 0x40, 0x94, 0x1a, 0xd5, 0x10, 0xec,
+ 0xc5, 0x96, 0x0b, 0xb3, 0x80, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0xba, 0xf1, 0x11, 0x93, 0x28, 0xbc, 0x01, 0xd2, 0x09, 0xb3,
+ 0x40, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x82, 0xf1, 0x40,
+ 0xf0, 0xb5, 0xf6, 0x01, 0x94, 0x0a, 0xb3, 0x02, 0x00, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0x95, 0xf1, 0x40, 0xf0, 0x6d, 0xee, 0x40,
+ 0xf0, 0x8f, 0xfb, 0x40, 0xf0, 0xc3, 0xf1, 0x40, 0x96, 0x1b, 0xd7,
+ 0x00, 0xec, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94, 0x0a,
+ 0xb3, 0x04, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xb1, 0xf1,
+ 0x40, 0xf0, 0x9e, 0xfb, 0x09, 0x63, 0x00, 0x44, 0x01, 0x97, 0xc3,
+ 0x94, 0x48, 0xa4, 0xc1, 0xd4, 0x00, 0xee, 0x40, 0x92, 0x19, 0xd3,
+ 0x12, 0x95, 0x19, 0xd3, 0x10, 0x95, 0x19, 0xd3, 0x02, 0x80, 0x19,
+ 0xd3, 0x03, 0x82, 0x41, 0x92, 0x19, 0xd3, 0x76, 0xf7, 0x01, 0x94,
+ 0x0a, 0xb3, 0x08, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xba,
+ 0xf1, 0x40, 0xf0, 0xae, 0xfb, 0x0a, 0x65, 0x00, 0x44, 0x02, 0x97,
+ 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63, 0x00, 0x40,
+ 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3,
+ 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x0a, 0x65, 0xeb,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09,
+ 0x63, 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x0a, 0x65, 0xe8, 0x43,
+ 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2, 0xd2, 0x0a,
+ 0x65, 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff,
+ 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09,
+ 0xb3, 0xfb, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x02,
+ 0x80, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00,
+ 0x09, 0x93, 0x00, 0x09, 0x19, 0xd3, 0x02, 0x80, 0x40, 0xf0, 0xfe,
+ 0xf1, 0x40, 0x92, 0x19, 0xd3, 0x94, 0xec, 0xc8, 0xd2, 0x09, 0x93,
+ 0x91, 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0,
+ 0xd8, 0xf4, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x2d, 0xf2, 0x0a,
+ 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2,
+ 0x0f, 0x9f, 0x3a, 0xf2, 0x40, 0xf0, 0x3c, 0xf2, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x3a, 0xf2, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec,
+ 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xf1, 0xbd,
+ 0x19, 0xd3, 0xb6, 0xec, 0x11, 0x93, 0xb4, 0xec, 0x40, 0x42, 0x02,
+ 0x5e, 0x0f, 0x9f, 0x54, 0xf2, 0x09, 0x63, 0x00, 0x80, 0x01, 0x97,
+ 0xc3, 0x94, 0x0a, 0x07, 0x07, 0x00, 0xc1, 0xd6, 0x0a, 0x05, 0x00,
+ 0xa0, 0x1a, 0xd5, 0x96, 0xec, 0x11, 0x93, 0xb6, 0xec, 0x19, 0xd3,
+ 0x01, 0x80, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x41,
+ 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x41, 0x20, 0x08, 0x0b, 0x01, 0x00, 0x13, 0x97, 0xb4, 0xec, 0x40,
+ 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xc3, 0xf2, 0x12, 0x95, 0x96, 0xec,
+ 0x0a, 0x03, 0x07, 0x00, 0xc1, 0x92, 0xc2, 0xd2, 0x11, 0x93, 0x96,
+ 0xec, 0x09, 0x05, 0x01, 0x00, 0x48, 0x02, 0xc1, 0x92, 0xc2, 0xd2,
+ 0x11, 0x93, 0x96, 0xec, 0x4e, 0x02, 0xc1, 0x94, 0xc5, 0xd6, 0xc5,
+ 0x92, 0x11, 0x07, 0x96, 0xec, 0x0b, 0x03, 0x0f, 0x00, 0xc1, 0x98,
+ 0x46, 0x06, 0x7a, 0x93, 0x79, 0x93, 0x5c, 0x95, 0x5a, 0x95, 0x02,
+ 0xa3, 0xc3, 0xd2, 0x04, 0x95, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6,
+ 0x42, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0x7d, 0xf2, 0x11, 0x93, 0x96,
+ 0xec, 0x09, 0x05, 0x05, 0x00, 0x41, 0x02, 0xc1, 0x92, 0xc2, 0xd2,
+ 0x11, 0x93, 0x96, 0xec, 0xc1, 0x92, 0x09, 0xb5, 0x1f, 0x00, 0x43,
+ 0x44, 0x02, 0x8e, 0x0f, 0x9f, 0xaa, 0xf2, 0x40, 0x44, 0x02, 0x4e,
+ 0x0f, 0x9f, 0xab, 0xf2, 0x0a, 0x05, 0xff, 0xff, 0x0f, 0x9f, 0xab,
+ 0xf2, 0x43, 0x94, 0x11, 0x93, 0x96, 0xec, 0x42, 0x02, 0xc1, 0xd4,
+ 0x13, 0x97, 0x96, 0xec, 0x03, 0x93, 0xd1, 0x94, 0x7a, 0x95, 0x7a,
+ 0x95, 0xc1, 0x92, 0x59, 0x93, 0x59, 0x93, 0x01, 0x05, 0x49, 0x06,
+ 0xc3, 0x92, 0x7f, 0xb2, 0x01, 0x05, 0x1a, 0xd5, 0xb4, 0xec, 0x0a,
+ 0x05, 0xf2, 0xff, 0x1a, 0xd5, 0x92, 0xec, 0x11, 0x93, 0x92, 0xec,
+ 0x12, 0x95, 0xb6, 0xec, 0x02, 0x43, 0x02, 0x8e, 0x0f, 0x9f, 0x11,
+ 0xf3, 0x02, 0x0e, 0x0f, 0x9f, 0xe4, 0xf2, 0x11, 0x93, 0x7a, 0xf7,
+ 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09,
+ 0xa3, 0x80, 0x00, 0x19, 0xd3, 0x79, 0xf7, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x95, 0xc2, 0x94, 0x1a, 0xd5, 0xb5, 0xec, 0x40, 0x96, 0x1b,
+ 0xd7, 0xb4, 0xec, 0x0f, 0x9f, 0x29, 0xf3, 0x11, 0x93, 0x03, 0x80,
+ 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xf6,
+ 0xf2, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0xf6, 0xf2, 0x40, 0xf0, 0x3d, 0xf3, 0x0f, 0x9f, 0x2b, 0xf3, 0x41,
+ 0x92, 0xc8, 0xd2, 0x0a, 0x95, 0x91, 0xec, 0xc8, 0xd4, 0x40, 0xf0,
+ 0xd0, 0xee, 0x42, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0x09, 0xf3, 0x42, 0x96, 0x1b, 0xd7, 0xc0, 0xec,
+ 0x0f, 0x9f, 0x2b, 0xf3, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3,
+ 0x92, 0x42, 0xa2, 0xc2, 0xd2, 0x0f, 0x9f, 0x2b, 0xf3, 0x12, 0x45,
+ 0x03, 0xec, 0x02, 0x4e, 0x0f, 0x9f, 0x23, 0xf3, 0x11, 0x93, 0x7a,
+ 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a, 0xf7, 0x11, 0x93, 0x79, 0xf7,
+ 0x09, 0xa3, 0x00, 0x08, 0x19, 0xd3, 0x79, 0xf7, 0x1a, 0xd5, 0x92,
+ 0xec, 0x11, 0x93, 0x92, 0xec, 0x19, 0x25, 0x92, 0xec, 0x09, 0x63,
+ 0x00, 0x80, 0x19, 0xd3, 0xf2, 0xbd, 0x41, 0x00, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0x3d, 0xf3,
+ 0x40, 0x92, 0xc8, 0xd2, 0x09, 0x93, 0x91, 0xec, 0xc8, 0xd2, 0x40,
+ 0xf0, 0xd0, 0xee, 0x42, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda,
+ 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0x75, 0xf7, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0x4d, 0xf3, 0x0a, 0x65, 0xbc, 0x69, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0x83, 0x00, 0x02, 0xc2, 0xd2, 0x11, 0x93, 0x03,
+ 0x80, 0x09, 0xb3, 0x00, 0x40, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x60, 0xf3, 0x11, 0x93, 0x7a, 0xf7, 0x41, 0x02, 0x19, 0xd3, 0x7a,
+ 0xf7, 0x11, 0x93, 0x79, 0xf7, 0x09, 0xa3, 0x00, 0x20, 0x19, 0xd3,
+ 0x79, 0xf7, 0x11, 0x93, 0xb5, 0xec, 0x19, 0xd3, 0x04, 0x80, 0x12,
+ 0x95, 0xb4, 0xec, 0x1a, 0xd5, 0x05, 0x80, 0x09, 0x63, 0x00, 0x80,
+ 0x01, 0x97, 0xc3, 0x96, 0x1b, 0xd7, 0xb5, 0xec, 0x40, 0x94, 0x1a,
+ 0xd5, 0xb4, 0xec, 0x19, 0xd3, 0xf2, 0xbd, 0x88, 0x98, 0x90, 0x9a,
+ 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x93, 0x96, 0x03, 0x19,
+ 0xd3, 0x06, 0x82, 0x09, 0x93, 0x00, 0x01, 0x19, 0xd3, 0x03, 0x82,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x47, 0x20, 0x08, 0x0b, 0x01,
+ 0x00, 0x11, 0x93, 0x01, 0x82, 0xc5, 0xd2, 0x40, 0x94, 0x01, 0xd4,
+ 0x13, 0x97, 0xb8, 0xec, 0x02, 0xd6, 0x03, 0x95, 0x0c, 0x99, 0xbb,
+ 0xec, 0x04, 0x05, 0x13, 0x97, 0x03, 0xec, 0x01, 0x27, 0x02, 0x99,
+ 0xc4, 0x92, 0x03, 0x03, 0xc2, 0xd2, 0x14, 0x99, 0xba, 0xec, 0x03,
+ 0x09, 0x1c, 0xd9, 0xba, 0xec, 0x12, 0x95, 0x04, 0x82, 0x0a, 0xb3,
+ 0x02, 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xc6, 0xf4, 0x01,
+ 0x92, 0x03, 0xd2, 0x0a, 0xa3, 0x02, 0x00, 0x19, 0xd3, 0x04, 0x82,
+ 0x02, 0x96, 0x0b, 0x05, 0x01, 0x00, 0x1a, 0xd5, 0xb8, 0xec, 0xc5,
+ 0x92, 0x43, 0x42, 0x02, 0x9e, 0x0f, 0x9f, 0xce, 0xf3, 0x42, 0x44,
+ 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x93, 0xbf, 0xec, 0x40,
+ 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xce, 0xf3, 0x0c, 0x49, 0xd3, 0x08,
+ 0x02, 0x8e, 0x0f, 0x9f, 0xce, 0xf3, 0x11, 0x63, 0x07, 0x82, 0x11,
+ 0xa3, 0x07, 0x82, 0x71, 0x93, 0x79, 0x93, 0x79, 0x93, 0x79, 0x93,
+ 0x03, 0xd2, 0xc5, 0x94, 0x0a, 0xb5, 0xfc, 0xff, 0x04, 0xd4, 0x03,
+ 0x96, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xdd, 0xf3, 0x11, 0x93,
+ 0xb8, 0xec, 0x41, 0x42, 0x02, 0x8e, 0x0f, 0x9f, 0xe4, 0xf3, 0xc5,
+ 0x98, 0x0c, 0x03, 0xff, 0xff, 0x42, 0x42, 0x02, 0x8e, 0x0f, 0x9f,
+ 0x0b, 0xf4, 0x0a, 0x95, 0xbb, 0xec, 0x42, 0x92, 0x19, 0xd3, 0xb9,
+ 0xec, 0xc5, 0x96, 0x43, 0x46, 0x02, 0x9e, 0x0f, 0x9f, 0xfd, 0xf3,
+ 0x0b, 0x07, 0xfc, 0xff, 0xc5, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xc8,
+ 0xbc, 0xd2, 0x96, 0x1b, 0xd7, 0xca, 0xbc, 0x09, 0x03, 0xff, 0xff,
+ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe9, 0xf3, 0x19, 0xd3, 0xb9,
+ 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x09, 0xf4, 0x0a, 0x05,
+ 0xfe, 0xff, 0xca, 0xd2, 0xc2, 0xd2, 0x0f, 0x9f, 0x0b, 0xf4, 0x1a,
+ 0xd5, 0x93, 0xec, 0x03, 0x98, 0x40, 0x48, 0x02, 0x5e, 0x0f, 0x9f,
+ 0x38, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x9e, 0x0f,
+ 0x9f, 0x1b, 0xf4, 0x04, 0x94, 0x48, 0x44, 0x02, 0x4e, 0x0f, 0x9f,
+ 0x26, 0xf4, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x38, 0xf4, 0x11,
+ 0x93, 0x04, 0x82, 0x41, 0xb2, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f,
+ 0x38, 0xf4, 0x41, 0x96, 0x01, 0xd6, 0x0a, 0x65, 0xbd, 0x43, 0x02,
+ 0x99, 0xc4, 0x92, 0x09, 0xa3, 0x80, 0x00, 0xc2, 0xd2, 0x0a, 0x65,
+ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2,
+ 0xd2, 0x0f, 0x9f, 0x97, 0xf4, 0xc5, 0x98, 0x43, 0x48, 0x02, 0x9e,
+ 0x0f, 0x9f, 0x97, 0xf4, 0x4f, 0x96, 0x0c, 0xb3, 0x01, 0x00, 0x40,
+ 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x45, 0xf4, 0x47, 0x96, 0x11, 0x93,
+ 0xb7, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x73, 0xf4, 0x11,
+ 0x93, 0xb8, 0xec, 0x41, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x73, 0xf4,
+ 0x12, 0x95, 0x00, 0x82, 0x0a, 0x05, 0xff, 0xaf, 0x05, 0xd4, 0xc8,
+ 0xd6, 0xc8, 0xd2, 0x40, 0xf0, 0x18, 0xf7, 0x42, 0x00, 0x05, 0x96,
+ 0xc3, 0x94, 0x01, 0xb5, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x68,
+ 0xf4, 0x11, 0x93, 0xba, 0xec, 0x4d, 0x42, 0x02, 0x8e, 0x0f, 0x9f,
+ 0x73, 0xf4, 0x06, 0x98, 0x50, 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40,
+ 0x98, 0x1c, 0xd9, 0xa2, 0xbc, 0x40, 0x92, 0x03, 0xd2, 0x0f, 0x9f,
+ 0x9c, 0xf4, 0x03, 0x94, 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0x80,
+ 0xf4, 0x0a, 0x65, 0x5e, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x48, 0xa2,
+ 0xc2, 0xd2, 0x0f, 0x9f, 0x9c, 0xf4, 0x11, 0x93, 0xb8, 0xec, 0x0c,
+ 0x99, 0xbb, 0xec, 0x04, 0x03, 0x04, 0x96, 0x13, 0x25, 0x03, 0xec,
+ 0xc1, 0xd4, 0x11, 0x93, 0xba, 0xec, 0x19, 0x05, 0xba, 0xec, 0x1b,
+ 0xd7, 0x01, 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x99, 0xc4, 0x92,
+ 0x43, 0xa2, 0xc2, 0xd2, 0x41, 0x92, 0x01, 0xd2, 0x03, 0x94, 0x40,
+ 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x11, 0x93, 0xb9, 0xec,
+ 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa8, 0xf4, 0x19, 0xd3, 0xb8,
+ 0xec, 0x19, 0xd3, 0xba, 0xec, 0x19, 0xd3, 0xbb, 0xec, 0x03, 0x96,
+ 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xb0, 0xf4, 0x41, 0x98, 0x1c,
+ 0xd9, 0xb7, 0xec, 0x11, 0x93, 0xbf, 0xec, 0x41, 0x42, 0x02, 0x5e,
+ 0x0f, 0x9f, 0xc1, 0xf4, 0x11, 0x93, 0x00, 0x82, 0x19, 0xd3, 0x02,
+ 0x82, 0x0a, 0x65, 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3,
+ 0x00, 0x01, 0xc2, 0xd2, 0x40, 0x98, 0x1c, 0xd9, 0xbf, 0xec, 0x0f,
+ 0x9f, 0xc9, 0xf4, 0x01, 0x92, 0x19, 0xd3, 0xb7, 0xec, 0x01, 0x94,
+ 0x40, 0x44, 0x02, 0x5e, 0x0f, 0x9f, 0xd5, 0xf4, 0x0a, 0x65, 0xea,
+ 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0xc2, 0xd2,
+ 0x47, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x12, 0x95, 0x03, 0x80, 0x0a, 0xb3, 0x00, 0x40, 0x40, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0xf4, 0xf4, 0x0a, 0xb7, 0x00, 0x08, 0x40,
+ 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0xf7, 0xf4, 0x11, 0x93, 0x03, 0xec,
+ 0x41, 0x02, 0x09, 0xb3, 0xfe, 0xff, 0x12, 0x95, 0x07, 0x80, 0x01,
+ 0x45, 0x02, 0x8e, 0x0f, 0x9f, 0xf7, 0xf4, 0x41, 0x92, 0x0f, 0x9f,
+ 0xf8, 0xf4, 0x40, 0x92, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x41,
+ 0x20, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97,
+ 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2, 0xd2, 0x13, 0x97, 0x6e,
+ 0xec, 0x0b, 0x47, 0xa0, 0x00, 0x02, 0x5e, 0x0f, 0x9f, 0x23, 0xf5,
+ 0x09, 0x63, 0x08, 0x43, 0x0a, 0x65, 0xff, 0x5f, 0x01, 0x99, 0xc4,
+ 0xd4, 0x0a, 0x95, 0x9b, 0xec, 0xd2, 0x96, 0x1b, 0xd7, 0xfa, 0xbc,
+ 0xd2, 0x96, 0xc4, 0xd6, 0xd2, 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0xd2,
+ 0x96, 0xc1, 0xd6, 0xc2, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x0f, 0x9f,
+ 0x61, 0xf5, 0x0c, 0x69, 0xff, 0x6f, 0x1c, 0xd9, 0xf8, 0xbc, 0x0b,
+ 0x47, 0x10, 0x95, 0x02, 0x5e, 0x0f, 0x9f, 0x3b, 0xf5, 0x0a, 0x95,
+ 0x6f, 0xec, 0x09, 0x63, 0x06, 0x43, 0x01, 0x99, 0xc4, 0xd6, 0xd2,
+ 0x96, 0x1b, 0xd7, 0xf8, 0xbc, 0x0c, 0x69, 0xee, 0x6a, 0xc1, 0xd8,
+ 0xc2, 0x94, 0x1a, 0xd5, 0xf8, 0xbc, 0x40, 0x92, 0xc5, 0xd2, 0x11,
+ 0x43, 0xc2, 0xec, 0x02, 0x0e, 0x0f, 0x9f, 0x5e, 0xf5, 0xc5, 0x94,
+ 0x0a, 0x03, 0x71, 0xec, 0xc1, 0x94, 0x1a, 0xd5, 0xfa, 0xbc, 0x11,
+ 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x50, 0xf5,
+ 0x0a, 0x95, 0x6f, 0xec, 0xc8, 0xd4, 0x40, 0xf0, 0x39, 0xf7, 0x19,
+ 0xd3, 0xf8, 0xbc, 0x41, 0x00, 0xc5, 0x96, 0x41, 0x06, 0xc5, 0xd6,
+ 0x13, 0x47, 0xc2, 0xec, 0x02, 0x1e, 0x0f, 0x9f, 0x42, 0xf5, 0x40,
+ 0x98, 0x1c, 0xd9, 0xfa, 0xbc, 0x40, 0x92, 0x19, 0xd3, 0x6e, 0xec,
+ 0x19, 0xd3, 0xc2, 0xec, 0x0a, 0x65, 0x52, 0x43, 0x02, 0x97, 0xc3,
+ 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xeb, 0x43, 0x02, 0x99,
+ 0xc4, 0x92, 0x09, 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x41, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x43, 0x20, 0x08, 0x0b, 0x01, 0x00,
+ 0x06, 0x92, 0x01, 0xd2, 0x0a, 0x65, 0xf0, 0x6a, 0x0b, 0x97, 0x6f,
+ 0xec, 0x02, 0x99, 0xc4, 0x98, 0xd3, 0xd8, 0x02, 0xd6, 0x0a, 0x03,
+ 0x02, 0x00, 0x01, 0x97, 0xc3, 0x98, 0x02, 0x96, 0xc3, 0xd8, 0x01,
+ 0x96, 0xc1, 0xd6, 0x1a, 0xd5, 0x6e, 0xec, 0xc5, 0x98, 0x14, 0x99,
+ 0x6f, 0xec, 0xc2, 0xd8, 0x43, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88,
+ 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0x92, 0xc8, 0xd2, 0x40, 0xf0,
+ 0x76, 0xf5, 0x41, 0x00, 0x11, 0x93, 0xc0, 0xec, 0x40, 0x42, 0x02,
+ 0x4e, 0x0f, 0x9f, 0xb0, 0xf5, 0x42, 0x42, 0x02, 0x5e, 0x0f, 0x9f,
+ 0xad, 0xf5, 0x0a, 0x65, 0xfe, 0x7f, 0x02, 0x97, 0xc3, 0x92, 0x42,
+ 0xa2, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0xc0, 0xec, 0x0a, 0x65,
+ 0xeb, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0xc0, 0x00, 0xc2,
+ 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3,
+ 0xbf, 0xff, 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x63,
+ 0x20, 0x08, 0x0b, 0x01, 0x00, 0x11, 0x93, 0xaf, 0xbc, 0x47, 0xb2,
+ 0x59, 0x95, 0x5a, 0x95, 0x12, 0xa5, 0xbf, 0xbc, 0x0a, 0xb3, 0x01,
+ 0x00, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0xd2, 0xf5, 0x41, 0x04,
+ 0x05, 0x93, 0x40, 0x96, 0x20, 0xd6, 0x62, 0x97, 0x0f, 0x9f, 0xe1,
+ 0xf5, 0x14, 0x99, 0xfc, 0xbc, 0xd1, 0xd8, 0x14, 0x99, 0xfe, 0xbc,
+ 0xd1, 0xd8, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x20, 0x98, 0x03,
+ 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0xd8, 0xf5, 0xc5, 0x92, 0x62, 0x42,
+ 0x02, 0x4e, 0x0f, 0x9f, 0xfa, 0xf5, 0x02, 0x8e, 0x0f, 0x9f, 0xf4,
+ 0xf5, 0x61, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x1e, 0xf6, 0x0f, 0x9f,
+ 0x4b, 0xf6, 0x63, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x41, 0xf6, 0x0f,
+ 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x0c, 0x99, 0x71, 0xec,
+ 0x0b, 0x05, 0xff, 0xff, 0x40, 0x96, 0x0f, 0x9f, 0x07, 0xf6, 0xd1,
+ 0x96, 0xd4, 0xd6, 0x20, 0x96, 0x41, 0x06, 0x20, 0xd6, 0x02, 0x47,
+ 0x02, 0x1e, 0x0f, 0x9f, 0x03, 0xf6, 0x1a, 0xd5, 0xc2, 0xec, 0x0a,
+ 0x65, 0xeb, 0x43, 0x02, 0x99, 0xc4, 0x92, 0x09, 0xa3, 0xc0, 0x00,
+ 0xc2, 0xd2, 0x0a, 0x65, 0xe9, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09,
+ 0xb3, 0xbf, 0xff, 0xc2, 0xd2, 0x0f, 0x9f, 0x4b, 0xf6, 0x0a, 0x03,
+ 0xfe, 0xff, 0x61, 0x95, 0x40, 0x98, 0x20, 0xd8, 0x02, 0x49, 0x02,
+ 0x0e, 0x0f, 0x9f, 0x4b, 0xf6, 0x0d, 0x03, 0x01, 0x00, 0x21, 0xd2,
+ 0x20, 0x92, 0x05, 0x03, 0x42, 0x02, 0xc8, 0xd2, 0x21, 0x96, 0xc3,
+ 0x92, 0x42, 0x06, 0x21, 0xd6, 0xc8, 0xd2, 0x22, 0xd4, 0x40, 0xf0,
+ 0xa2, 0xf0, 0x42, 0x00, 0x20, 0x98, 0x42, 0x08, 0x20, 0xd8, 0x22,
+ 0x94, 0x02, 0x49, 0x02, 0x1e, 0x0f, 0x9f, 0x2a, 0xf6, 0x0f, 0x9f,
+ 0x4b, 0xf6, 0x0d, 0x03, 0x03, 0x00, 0xc8, 0xd2, 0x02, 0x92, 0xc8,
+ 0xd2, 0x01, 0x96, 0xc8, 0xd6, 0x40, 0xf0, 0x4e, 0xf6, 0x43, 0x00,
+ 0x63, 0x00, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x45, 0x20, 0x08,
+ 0x0b, 0x01, 0x00, 0x0d, 0x03, 0x08, 0x00, 0x08, 0x94, 0xc5, 0xd4,
+ 0x09, 0x05, 0x01, 0x00, 0xc2, 0x94, 0x03, 0xd4, 0x42, 0x02, 0xc1,
+ 0x92, 0x01, 0xd2, 0x02, 0x97, 0xc5, 0x94, 0x0a, 0x83, 0xff, 0xff,
+ 0x11, 0xb3, 0x2c, 0x93, 0x09, 0xb3, 0xfb, 0xff, 0x19, 0xd3, 0x2c,
+ 0x93, 0x03, 0x92, 0x40, 0x42, 0x02, 0x4e, 0x0f, 0x9f, 0x81, 0xf6,
+ 0x01, 0x94, 0xd2, 0x92, 0x19, 0xd3, 0x2c, 0x93, 0x01, 0xd4, 0x02,
+ 0x94, 0x12, 0x95, 0x2c, 0x93, 0x44, 0xa4, 0x1a, 0xd5, 0x2c, 0x93,
+ 0x0a, 0xb5, 0xfb, 0xff, 0x1a, 0xd5, 0x2c, 0x93, 0x0b, 0x07, 0xff,
+ 0xff, 0x40, 0x46, 0x02, 0x5e, 0x0f, 0x9f, 0x6c, 0xf6, 0x09, 0x63,
+ 0xd4, 0x6c, 0x01, 0x95, 0xc2, 0x96, 0xc5, 0x94, 0x02, 0xa7, 0xc1,
+ 0xd6, 0x03, 0x92, 0x54, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0x91, 0xf6,
+ 0x0a, 0x83, 0xff, 0xff, 0x1b, 0xb3, 0x2c, 0x93, 0x45, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x09, 0x63,
+ 0x00, 0x40, 0x19, 0xd3, 0xf2, 0xbd, 0x40, 0xf0, 0xd8, 0xf4, 0x40,
+ 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xa5, 0xf6, 0x40, 0xf0, 0x3c, 0xf2,
+ 0x0f, 0x9f, 0xb3, 0xf6, 0x40, 0x96, 0xc8, 0xd6, 0x09, 0x93, 0x91,
+ 0xec, 0xc8, 0xd2, 0x40, 0xf0, 0xd0, 0xee, 0x0a, 0x65, 0xfe, 0x7f,
+ 0x02, 0x97, 0xc3, 0x92, 0x44, 0xa2, 0xc2, 0xd2, 0x42, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x0a, 0x65,
+ 0xe8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xa3, 0x40, 0x00, 0xc2,
+ 0xd2, 0x0a, 0x65, 0xea, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3,
+ 0xfb, 0xff, 0xc2, 0xd2, 0x40, 0x92, 0x19, 0xd3, 0x2d, 0xbc, 0x0a,
+ 0x65, 0xd8, 0x43, 0x02, 0x97, 0xc3, 0x92, 0x09, 0xb3, 0xbf, 0xff,
+ 0xc2, 0xd2, 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01,
+ 0x00, 0x09, 0x63, 0xea, 0x43, 0x01, 0x97, 0xc3, 0x94, 0x44, 0xa4,
+ 0xc1, 0xd4, 0x11, 0x93, 0xb9, 0xec, 0x40, 0x42, 0x02, 0x4e, 0x0f,
+ 0x9f, 0x0c, 0xf7, 0x12, 0x95, 0x93, 0xec, 0x0b, 0x67, 0x36, 0x43,
+ 0xd2, 0x98, 0x1c, 0xd9, 0xc8, 0xbc, 0xd2, 0x98, 0x03, 0x93, 0xc1,
+ 0xd8, 0x11, 0x93, 0xb9, 0xec, 0x09, 0x03, 0xff, 0xff, 0x19, 0xd3,
+ 0xb9, 0xec, 0x40, 0x42, 0x02, 0x5e, 0x0f, 0x9f, 0xe5, 0xf6, 0x19,
+ 0xd3, 0xb8, 0xec, 0x19, 0xd3, 0xba, 0xec, 0x0a, 0x05, 0xfe, 0xff,
+ 0xca, 0xd2, 0xca, 0xd2, 0xc2, 0xd2, 0x0a, 0x65, 0x5e, 0x43, 0x02,
+ 0x97, 0xc3, 0x92, 0x48, 0xa2, 0xc2, 0xd2, 0x0a, 0x65, 0xea, 0x43,
+ 0x02, 0x99, 0xc4, 0x92, 0x09, 0xb3, 0xfb, 0xff, 0x0f, 0x9f, 0x15,
+ 0xf7, 0x11, 0x93, 0x03, 0xec, 0x19, 0xd3, 0x01, 0x82, 0x0a, 0x65,
+ 0xfd, 0x7d, 0x02, 0x97, 0xc3, 0x92, 0x43, 0xa2, 0xc2, 0xd2, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x92,
+ 0x04, 0x96, 0x0d, 0x5e, 0x50, 0x46, 0x02, 0x0e, 0x40, 0x92, 0x09,
+ 0xee, 0x44, 0x46, 0x04, 0x0e, 0x59, 0x93, 0x44, 0x26, 0x04, 0x5e,
+ 0x46, 0xee, 0x41, 0x93, 0x41, 0x26, 0x43, 0x4e, 0x88, 0x98, 0x90,
+ 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x40, 0xf0, 0xb1, 0xfe,
+ 0x88, 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x88,
+ 0x98, 0x90, 0x9a, 0x88, 0xda, 0x08, 0x0b, 0x01, 0x00, 0x03, 0x94,
+ 0x1a, 0xd5, 0x40, 0xf7, 0x11, 0x93, 0x00, 0x90, 0x88, 0x98, 0x90,
+ 0x9a, 0x1d, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x03, 0x00, 0x18, 0x00,
+ 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x12,
+ 0x00, 0x09, 0x00, 0x13, 0x00, 0x19, 0x00, 0x19, 0x00, 0x19, 0x00,
+ 0x21, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf2, 0x6b, 0xf7, 0x00, 0x00,
+ 0x1c, 0xf2, 0x6b, 0xf7, 0x00, 0x00, 0x61, 0xf2, 0x68, 0xf7, 0x6f,
+ 0xf7, 0x00, 0x00, 0x2e, 0xf3, 0x6b, 0xf7, 0x25, 0x47, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+};
diff --git a/sys/dev/usb/wlan/if_zydreg.h b/sys/dev/usb/wlan/if_zydreg.h
new file mode 100644
index 0000000..8ef34e3
--- /dev/null
+++ b/sys/dev/usb/wlan/if_zydreg.h
@@ -0,0 +1,1338 @@
+/* $OpenBSD: if_zydreg.h,v 1.19 2006/11/30 19:28:07 damien Exp $ */
+/* $NetBSD: if_zydreg.h,v 1.2 2007/06/16 11:18:45 kiyohara Exp $ */
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 2006 by Damien Bergamini <damien.bergamini@free.fr>
+ * Copyright (c) 2006 by Florian Stoehr <ich@florian-stoehr.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * ZyDAS ZD1211/ZD1211B USB WLAN driver.
+ */
+
+#define ZYD_CR_GPI_EN 0x9418
+#define ZYD_CR_RADIO_PD 0x942c
+#define ZYD_CR_RF2948_PD 0x942c
+#define ZYD_CR_EN_PS_MANUAL_AGC 0x943c
+#define ZYD_CR_CONFIG_PHILIPS 0x9440
+#define ZYD_CR_I2C_WRITE 0x9444
+#define ZYD_CR_SA2400_SER_RP 0x9448
+#define ZYD_CR_RADIO_PE 0x9458
+#define ZYD_CR_RST_BUS_MASTER 0x945c
+#define ZYD_CR_RFCFG 0x9464
+#define ZYD_CR_HSTSCHG 0x946c
+#define ZYD_CR_PHY_ON 0x9474
+#define ZYD_CR_RX_DELAY 0x9478
+#define ZYD_CR_RX_PE_DELAY 0x947c
+#define ZYD_CR_GPIO_1 0x9490
+#define ZYD_CR_GPIO_2 0x9494
+#define ZYD_CR_EnZYD_CRyBufMux 0x94a8
+#define ZYD_CR_PS_CTRL 0x9500
+#define ZYD_CR_ADDA_PWR_DWN 0x9504
+#define ZYD_CR_ADDA_MBIAS_WT 0x9508
+#define ZYD_CR_INTERRUPT 0x9510
+#define ZYD_CR_MAC_PS_STATE 0x950c
+#define ZYD_CR_ATIM_WND_PERIOD 0x951c
+#define ZYD_CR_BCN_INTERVAL 0x9520
+#define ZYD_CR_PRE_TBTT 0x9524
+
+/*
+ * MAC registers.
+ */
+#define ZYD_MAC_MACADRL 0x9610 /* MAC address (low) */
+#define ZYD_MAC_MACADRH 0x9614 /* MAC address (high) */
+#define ZYD_MAC_BSSADRL 0x9618 /* BSS address (low) */
+#define ZYD_MAC_BSSADRH 0x961c /* BSS address (high) */
+#define ZYD_MAC_BCNCFG 0x9620 /* BCN configuration */
+#define ZYD_MAC_GHTBL 0x9624 /* Group hash table (low) */
+#define ZYD_MAC_GHTBH 0x9628 /* Group hash table (high) */
+#define ZYD_MAC_RX_TIMEOUT 0x962c /* Rx timeout value */
+#define ZYD_MAC_BAS_RATE 0x9630 /* Basic rate setting */
+#define ZYD_MAC_MAN_RATE 0x9634 /* Mandatory rate setting */
+#define ZYD_MAC_RTSCTSRATE 0x9638 /* RTS CTS rate */
+#define ZYD_MAC_BACKOFF_PROTECT 0x963c /* Backoff protection */
+#define ZYD_MAC_RX_THRESHOLD 0x9640 /* Rx threshold */
+#define ZYD_MAC_TX_PE_CONTROL 0x9644 /* Tx_PE control */
+#define ZYD_MAC_AFTER_PNP 0x9648 /* After PnP */
+#define ZYD_MAC_RX_PE_DELAY 0x964c /* Rx_pe delay */
+#define ZYD_MAC_RX_ADDR2_L 0x9650 /* RX address2 (low) */
+#define ZYD_MAC_RX_ADDR2_H 0x9654 /* RX address2 (high) */
+#define ZYD_MAC_SIFS_ACK_TIME 0x9658 /* Dynamic SIFS ack time */
+#define ZYD_MAC_PHY_DELAY 0x9660 /* PHY delay */
+#define ZYD_MAC_PHY_DELAY2 0x966c /* PHY delay */
+#define ZYD_MAC_BCNFIFO 0x9670 /* Beacon FIFO I/O port */
+#define ZYD_MAC_SNIFFER 0x9674 /* Sniffer on/off */
+#define ZYD_MAC_ENCRYPTION_TYPE 0x9678 /* Encryption type */
+#define ZYD_MAC_RETRY 0x967c /* Retry time */
+#define ZYD_MAC_MISC 0x9680 /* Misc */
+#define ZYD_MAC_STMACHINESTAT 0x9684 /* State machine status */
+#define ZYD_MAC_TX_UNDERRUN_CNT 0x9688 /* TX underrun counter */
+#define ZYD_MAC_RXFILTER 0x968c /* Send to host settings */
+#define ZYD_MAC_ACK_EXT 0x9690 /* Acknowledge extension */
+#define ZYD_MAC_BCNFIFOST 0x9694 /* BCN FIFO set and status */
+#define ZYD_MAC_DIFS_EIFS_SIFS 0x9698 /* DIFS, EIFS & SIFS settings */
+#define ZYD_MAC_RX_TIMEOUT_CNT 0x969c /* RX timeout count */
+#define ZYD_MAC_RX_TOTAL_FRAME 0x96a0 /* RX total frame count */
+#define ZYD_MAC_RX_CRC32_CNT 0x96a4 /* RX CRC32 frame count */
+#define ZYD_MAC_RX_CRC16_CNT 0x96a8 /* RX CRC16 frame count */
+#define ZYD_MAC_RX_UDEC 0x96ac /* RX unicast decr. error count */
+#define ZYD_MAC_RX_OVERRUN_CNT 0x96b0 /* RX FIFO overrun count */
+#define ZYD_MAC_RX_MDEC 0x96bc /* RX multicast decr. err. cnt. */
+#define ZYD_MAC_NAV_TCR 0x96c4 /* NAV timer count read */
+#define ZYD_MAC_BACKOFF_ST_RD 0x96c8 /* Backoff status read */
+#define ZYD_MAC_DM_RETRY_CNT_RD 0x96cc /* DM retry count read */
+#define ZYD_MAC_RX_ACR 0x96d0 /* RX arbitration count read */
+#define ZYD_MAC_TX_CCR 0x96d4 /* Tx complete count read */
+#define ZYD_MAC_TCB_ADDR 0x96e8 /* Current PCI process TCP addr */
+#define ZYD_MAC_RCB_ADDR 0x96ec /* Next RCB address */
+#define ZYD_MAC_CONT_WIN_LIMIT 0x96f0 /* Contention window limit */
+#define ZYD_MAC_TX_PKT 0x96f4 /* Tx total packet count read */
+#define ZYD_MAC_DL_CTRL 0x96f8 /* Download control */
+#define ZYD_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */
+#define ZYD_MACB_TXPWR_CTL1 0x9b00
+#define ZYD_MACB_TXPWR_CTL2 0x9b04
+#define ZYD_MACB_TXPWR_CTL3 0x9b08
+#define ZYD_MACB_TXPWR_CTL4 0x9b0c
+#define ZYD_MACB_AIFS_CTL1 0x9b10
+#define ZYD_MACB_AIFS_CTL2 0x9b14
+#define ZYD_MACB_TXOP 0x9b20
+#define ZYD_MACB_MAX_RETRY 0x9b28
+
+/*
+ * Miscellanous registers.
+ */
+#define ZYD_FIRMWARE_START_ADDR 0xee00
+#define ZYD_FIRMWARE_BASE_ADDR 0xee1d /* Firmware base address */
+
+/*
+ * EEPROM registers.
+ */
+#define ZYD_EEPROM_START_HEAD 0xf800 /* EEPROM start */
+#define ZYD_EEPROM_SUBID 0xf817
+#define ZYD_EEPROM_POD 0xf819
+#define ZYD_EEPROM_MAC_ADDR_P1 0xf81b /* Part 1 of the MAC address */
+#define ZYD_EEPROM_MAC_ADDR_P2 0xf81d /* Part 2 of the MAC address */
+#define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */
+#define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */
+#define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */
+#define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */
+#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */
+#define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */
+#define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */
+#define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */
+#define ZYD_EEPROM_48M_INT 0xf857 /* Interpolation */
+#define ZYD_EEPROM_54M_CAL 0xf85f /* Calibration */
+#define ZYD_EEPROM_54M_INT 0xf867 /* Interpolation */
+
+/*
+ * Firmware registers offsets (relative to fwbase).
+ */
+#define ZYD_FW_FIRMWARE_REV 0x0000 /* Firmware version */
+#define ZYD_FW_USB_SPEED 0x0001 /* USB speed (!=0 if highspeed) */
+#define ZYD_FW_FIX_TX_RATE 0x0002 /* Fixed TX rate */
+#define ZYD_FW_LINK_STATUS 0x0003
+#define ZYD_FW_SOFT_RESET 0x0004
+#define ZYD_FW_FLASH_CHK 0x0005
+
+/* possible flags for register ZYD_FW_LINK_STATUS */
+#define ZYD_LED1 (1 << 8)
+#define ZYD_LED2 (1 << 9)
+
+/*
+ * RF IDs.
+ */
+#define ZYD_RF_UW2451 0x2 /* not supported yet */
+#define ZYD_RF_UCHIP 0x3 /* not supported yet */
+#define ZYD_RF_AL2230 0x4
+#define ZYD_RF_AL7230B 0x5
+#define ZYD_RF_THETA 0x6 /* not supported yet */
+#define ZYD_RF_AL2210 0x7
+#define ZYD_RF_MAXIM_NEW 0x8
+#define ZYD_RF_GCT 0x9
+#define ZYD_RF_AL2230S 0xa /* not supported yet */
+#define ZYD_RF_RALINK 0xb /* not supported yet */
+#define ZYD_RF_INTERSIL 0xc /* not supported yet */
+#define ZYD_RF_RFMD 0xd
+#define ZYD_RF_MAXIM_NEW2 0xe
+#define ZYD_RF_PHILIPS 0xf /* not supported yet */
+
+/*
+ * PHY registers (8 bits, not documented).
+ */
+#define ZYD_CR0 0x9000
+#define ZYD_CR1 0x9004
+#define ZYD_CR2 0x9008
+#define ZYD_CR3 0x900c
+#define ZYD_CR5 0x9010
+#define ZYD_CR6 0x9014
+#define ZYD_CR7 0x9018
+#define ZYD_CR8 0x901c
+#define ZYD_CR4 0x9020
+#define ZYD_CR9 0x9024
+#define ZYD_CR10 0x9028
+#define ZYD_CR11 0x902c
+#define ZYD_CR12 0x9030
+#define ZYD_CR13 0x9034
+#define ZYD_CR14 0x9038
+#define ZYD_CR15 0x903c
+#define ZYD_CR16 0x9040
+#define ZYD_CR17 0x9044
+#define ZYD_CR18 0x9048
+#define ZYD_CR19 0x904c
+#define ZYD_CR20 0x9050
+#define ZYD_CR21 0x9054
+#define ZYD_CR22 0x9058
+#define ZYD_CR23 0x905c
+#define ZYD_CR24 0x9060
+#define ZYD_CR25 0x9064
+#define ZYD_CR26 0x9068
+#define ZYD_CR27 0x906c
+#define ZYD_CR28 0x9070
+#define ZYD_CR29 0x9074
+#define ZYD_CR30 0x9078
+#define ZYD_CR31 0x907c
+#define ZYD_CR32 0x9080
+#define ZYD_CR33 0x9084
+#define ZYD_CR34 0x9088
+#define ZYD_CR35 0x908c
+#define ZYD_CR36 0x9090
+#define ZYD_CR37 0x9094
+#define ZYD_CR38 0x9098
+#define ZYD_CR39 0x909c
+#define ZYD_CR40 0x90a0
+#define ZYD_CR41 0x90a4
+#define ZYD_CR42 0x90a8
+#define ZYD_CR43 0x90ac
+#define ZYD_CR44 0x90b0
+#define ZYD_CR45 0x90b4
+#define ZYD_CR46 0x90b8
+#define ZYD_CR47 0x90bc
+#define ZYD_CR48 0x90c0
+#define ZYD_CR49 0x90c4
+#define ZYD_CR50 0x90c8
+#define ZYD_CR51 0x90cc
+#define ZYD_CR52 0x90d0
+#define ZYD_CR53 0x90d4
+#define ZYD_CR54 0x90d8
+#define ZYD_CR55 0x90dc
+#define ZYD_CR56 0x90e0
+#define ZYD_CR57 0x90e4
+#define ZYD_CR58 0x90e8
+#define ZYD_CR59 0x90ec
+#define ZYD_CR60 0x90f0
+#define ZYD_CR61 0x90f4
+#define ZYD_CR62 0x90f8
+#define ZYD_CR63 0x90fc
+#define ZYD_CR64 0x9100
+#define ZYD_CR65 0x9104
+#define ZYD_CR66 0x9108
+#define ZYD_CR67 0x910c
+#define ZYD_CR68 0x9110
+#define ZYD_CR69 0x9114
+#define ZYD_CR70 0x9118
+#define ZYD_CR71 0x911c
+#define ZYD_CR72 0x9120
+#define ZYD_CR73 0x9124
+#define ZYD_CR74 0x9128
+#define ZYD_CR75 0x912c
+#define ZYD_CR76 0x9130
+#define ZYD_CR77 0x9134
+#define ZYD_CR78 0x9138
+#define ZYD_CR79 0x913c
+#define ZYD_CR80 0x9140
+#define ZYD_CR81 0x9144
+#define ZYD_CR82 0x9148
+#define ZYD_CR83 0x914c
+#define ZYD_CR84 0x9150
+#define ZYD_CR85 0x9154
+#define ZYD_CR86 0x9158
+#define ZYD_CR87 0x915c
+#define ZYD_CR88 0x9160
+#define ZYD_CR89 0x9164
+#define ZYD_CR90 0x9168
+#define ZYD_CR91 0x916c
+#define ZYD_CR92 0x9170
+#define ZYD_CR93 0x9174
+#define ZYD_CR94 0x9178
+#define ZYD_CR95 0x917c
+#define ZYD_CR96 0x9180
+#define ZYD_CR97 0x9184
+#define ZYD_CR98 0x9188
+#define ZYD_CR99 0x918c
+#define ZYD_CR100 0x9190
+#define ZYD_CR101 0x9194
+#define ZYD_CR102 0x9198
+#define ZYD_CR103 0x919c
+#define ZYD_CR104 0x91a0
+#define ZYD_CR105 0x91a4
+#define ZYD_CR106 0x91a8
+#define ZYD_CR107 0x91ac
+#define ZYD_CR108 0x91b0
+#define ZYD_CR109 0x91b4
+#define ZYD_CR110 0x91b8
+#define ZYD_CR111 0x91bc
+#define ZYD_CR112 0x91c0
+#define ZYD_CR113 0x91c4
+#define ZYD_CR114 0x91c8
+#define ZYD_CR115 0x91cc
+#define ZYD_CR116 0x91d0
+#define ZYD_CR117 0x91d4
+#define ZYD_CR118 0x91d8
+#define ZYD_CR119 0x91dc
+#define ZYD_CR120 0x91e0
+#define ZYD_CR121 0x91e4
+#define ZYD_CR122 0x91e8
+#define ZYD_CR123 0x91ec
+#define ZYD_CR124 0x91f0
+#define ZYD_CR125 0x91f4
+#define ZYD_CR126 0x91f8
+#define ZYD_CR127 0x91fc
+#define ZYD_CR128 0x9200
+#define ZYD_CR129 0x9204
+#define ZYD_CR130 0x9208
+#define ZYD_CR131 0x920c
+#define ZYD_CR132 0x9210
+#define ZYD_CR133 0x9214
+#define ZYD_CR134 0x9218
+#define ZYD_CR135 0x921c
+#define ZYD_CR136 0x9220
+#define ZYD_CR137 0x9224
+#define ZYD_CR138 0x9228
+#define ZYD_CR139 0x922c
+#define ZYD_CR140 0x9230
+#define ZYD_CR141 0x9234
+#define ZYD_CR142 0x9238
+#define ZYD_CR143 0x923c
+#define ZYD_CR144 0x9240
+#define ZYD_CR145 0x9244
+#define ZYD_CR146 0x9248
+#define ZYD_CR147 0x924c
+#define ZYD_CR148 0x9250
+#define ZYD_CR149 0x9254
+#define ZYD_CR150 0x9258
+#define ZYD_CR151 0x925c
+#define ZYD_CR152 0x9260
+#define ZYD_CR153 0x9264
+#define ZYD_CR154 0x9268
+#define ZYD_CR155 0x926c
+#define ZYD_CR156 0x9270
+#define ZYD_CR157 0x9274
+#define ZYD_CR158 0x9278
+#define ZYD_CR159 0x927c
+#define ZYD_CR160 0x9280
+#define ZYD_CR161 0x9284
+#define ZYD_CR162 0x9288
+#define ZYD_CR163 0x928c
+#define ZYD_CR164 0x9290
+#define ZYD_CR165 0x9294
+#define ZYD_CR166 0x9298
+#define ZYD_CR167 0x929c
+#define ZYD_CR168 0x92a0
+#define ZYD_CR169 0x92a4
+#define ZYD_CR170 0x92a8
+#define ZYD_CR171 0x92ac
+#define ZYD_CR172 0x92b0
+#define ZYD_CR173 0x92b4
+#define ZYD_CR174 0x92b8
+#define ZYD_CR175 0x92bc
+#define ZYD_CR176 0x92c0
+#define ZYD_CR177 0x92c4
+#define ZYD_CR178 0x92c8
+#define ZYD_CR179 0x92cc
+#define ZYD_CR180 0x92d0
+#define ZYD_CR181 0x92d4
+#define ZYD_CR182 0x92d8
+#define ZYD_CR183 0x92dc
+#define ZYD_CR184 0x92e0
+#define ZYD_CR185 0x92e4
+#define ZYD_CR186 0x92e8
+#define ZYD_CR187 0x92ec
+#define ZYD_CR188 0x92f0
+#define ZYD_CR189 0x92f4
+#define ZYD_CR190 0x92f8
+#define ZYD_CR191 0x92fc
+#define ZYD_CR192 0x9300
+#define ZYD_CR193 0x9304
+#define ZYD_CR194 0x9308
+#define ZYD_CR195 0x930c
+#define ZYD_CR196 0x9310
+#define ZYD_CR197 0x9314
+#define ZYD_CR198 0x9318
+#define ZYD_CR199 0x931c
+#define ZYD_CR200 0x9320
+#define ZYD_CR201 0x9324
+#define ZYD_CR202 0x9328
+#define ZYD_CR203 0x932c
+#define ZYD_CR204 0x9330
+#define ZYD_CR205 0x9334
+#define ZYD_CR206 0x9338
+#define ZYD_CR207 0x933c
+#define ZYD_CR208 0x9340
+#define ZYD_CR209 0x9344
+#define ZYD_CR210 0x9348
+#define ZYD_CR211 0x934c
+#define ZYD_CR212 0x9350
+#define ZYD_CR213 0x9354
+#define ZYD_CR214 0x9358
+#define ZYD_CR215 0x935c
+#define ZYD_CR216 0x9360
+#define ZYD_CR217 0x9364
+#define ZYD_CR218 0x9368
+#define ZYD_CR219 0x936c
+#define ZYD_CR220 0x9370
+#define ZYD_CR221 0x9374
+#define ZYD_CR222 0x9378
+#define ZYD_CR223 0x937c
+#define ZYD_CR224 0x9380
+#define ZYD_CR225 0x9384
+#define ZYD_CR226 0x9388
+#define ZYD_CR227 0x938c
+#define ZYD_CR228 0x9390
+#define ZYD_CR229 0x9394
+#define ZYD_CR230 0x9398
+#define ZYD_CR231 0x939c
+#define ZYD_CR232 0x93a0
+#define ZYD_CR233 0x93a4
+#define ZYD_CR234 0x93a8
+#define ZYD_CR235 0x93ac
+#define ZYD_CR236 0x93b0
+#define ZYD_CR240 0x93c0
+#define ZYD_CR241 0x93c4
+#define ZYD_CR242 0x93c8
+#define ZYD_CR243 0x93cc
+#define ZYD_CR244 0x93d0
+#define ZYD_CR245 0x93d4
+#define ZYD_CR251 0x93ec
+#define ZYD_CR252 0x93f0
+#define ZYD_CR253 0x93f4
+#define ZYD_CR254 0x93f8
+#define ZYD_CR255 0x93fc
+
+/* copied nearly verbatim from the Linux driver rewrite */
+#define ZYD_DEF_PHY \
+{ \
+ { ZYD_CR0, 0x0a }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \
+ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xa0 }, \
+ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0x7f }, \
+ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \
+ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \
+ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0c }, \
+ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x19 }, \
+ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \
+ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \
+ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \
+ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \
+ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \
+ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \
+ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \
+ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \
+ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \
+ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \
+ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \
+ { ZYD_CR79, 0x68 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \
+ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x00 }, { ZYD_CR84, 0x00 }, \
+ { ZYD_CR85, 0x02 }, { ZYD_CR86, 0x00 }, { ZYD_CR87, 0x00 }, \
+ { ZYD_CR88, 0xff }, { ZYD_CR89, 0xfc }, { ZYD_CR90, 0x00 }, \
+ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x08 }, \
+ { ZYD_CR94, 0x00 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0xff }, \
+ { ZYD_CR97, 0xe7 }, { ZYD_CR98, 0x00 }, { ZYD_CR99, 0x00 }, \
+ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0xae }, { ZYD_CR102, 0x02 }, \
+ { ZYD_CR103, 0x00 }, { ZYD_CR104, 0x03 }, { ZYD_CR105, 0x65 }, \
+ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \
+ { ZYD_CR109, 0xaa }, { ZYD_CR110, 0xaa }, { ZYD_CR111, 0x25 }, \
+ { ZYD_CR112, 0x25 }, { ZYD_CR113, 0x00 }, { ZYD_CR119, 0x1e }, \
+ { ZYD_CR125, 0x90 }, { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, \
+ { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \
+ { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \
+ { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \
+ { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \
+ { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \
+ { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \
+ { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \
+ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x50 }, \
+ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR101, 0x13 }, \
+ { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, \
+ { ZYD_CR105, 0x12 }, { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, \
+ { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \
+ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \
+ { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \
+ { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \
+ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \
+ { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \
+ { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \
+ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \
+ { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \
+ { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \
+ { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \
+ { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \
+ { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \
+ { ZYD_CR203, 0x30 }, { 0, 0} \
+}
+
+#define ZYD_DEF_PHYB \
+{ \
+ { ZYD_CR0, 0x14 }, { ZYD_CR1, 0x06 }, { ZYD_CR2, 0x26 }, \
+ { ZYD_CR3, 0x38 }, { ZYD_CR4, 0x80 }, { ZYD_CR9, 0xe0 }, \
+ { ZYD_CR10, 0x81 }, { ZYD_CR11, 0x00 }, { ZYD_CR12, 0xf0 }, \
+ { ZYD_CR13, 0x8c }, { ZYD_CR14, 0x80 }, { ZYD_CR15, 0x3d }, \
+ { ZYD_CR16, 0x20 }, { ZYD_CR17, 0x1e }, { ZYD_CR18, 0x0a }, \
+ { ZYD_CR19, 0x48 }, { ZYD_CR20, 0x10 }, { ZYD_CR21, 0x0e }, \
+ { ZYD_CR22, 0x23 }, { ZYD_CR23, 0x90 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR25, 0x40 }, { ZYD_CR26, 0x10 }, { ZYD_CR27, 0x10 }, \
+ { ZYD_CR28, 0x7f }, { ZYD_CR29, 0x80 }, { ZYD_CR30, 0x4b }, \
+ { ZYD_CR31, 0x60 }, { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x08 }, \
+ { ZYD_CR34, 0x06 }, { ZYD_CR35, 0x0a }, { ZYD_CR36, 0x00 }, \
+ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \
+ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \
+ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x33 }, { ZYD_CR46, 0xff }, \
+ { ZYD_CR47, 0x1E }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \
+ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \
+ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \
+ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \
+ { ZYD_CR73, 0x32 }, { ZYD_CR74, 0x30 }, { ZYD_CR75, 0x65 }, \
+ { ZYD_CR76, 0x41 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x30 }, \
+ { ZYD_CR79, 0xf0 }, { ZYD_CR80, 0x64 }, { ZYD_CR81, 0x64 }, \
+ { ZYD_CR82, 0x00 }, { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, \
+ { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0c }, { ZYD_CR87, 0x12 }, \
+ { ZYD_CR88, 0x0c }, { ZYD_CR89, 0x00 }, { ZYD_CR90, 0x58 }, \
+ { ZYD_CR91, 0x04 }, { ZYD_CR92, 0x00 }, { ZYD_CR93, 0x00 }, \
+ { ZYD_CR94, 0x01 }, { ZYD_CR95, 0x20 }, { ZYD_CR96, 0x50 }, \
+ { ZYD_CR97, 0x37 }, { ZYD_CR98, 0x35 }, { ZYD_CR99, 0x00 }, \
+ { ZYD_CR100, 0x01 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR103, 0x27 }, { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, \
+ { ZYD_CR106, 0x04 }, { ZYD_CR107, 0x00 }, { ZYD_CR108, 0x0a }, \
+ { ZYD_CR109, 0x27 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, \
+ { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, \
+ { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfc }, \
+ { ZYD_CR118, 0xfa }, { ZYD_CR119, 0x1e }, { ZYD_CR125, 0x90 }, \
+ { ZYD_CR126, 0x00 }, { ZYD_CR127, 0x00 }, { ZYD_CR128, 0x14 }, \
+ { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0c }, \
+ { ZYD_CR136, 0xdf }, { ZYD_CR137, 0xa0 }, { ZYD_CR138, 0xa8 }, \
+ { ZYD_CR139, 0xb4 }, { ZYD_CR140, 0x98 }, { ZYD_CR141, 0x82 }, \
+ { ZYD_CR142, 0x53 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \
+ { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x40 }, { ZYD_CR149, 0x40 }, \
+ { ZYD_CR150, 0x14 }, { ZYD_CR151, 0x18 }, { ZYD_CR159, 0x70 }, \
+ { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \
+ { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \
+ { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \
+ { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \
+ { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \
+ { 0, 0 } \
+}
+
+#define ZYD_RFMD_PHY \
+{ \
+ { ZYD_CR2, 0x1e }, { ZYD_CR9, 0x20 }, { ZYD_CR10, 0x89 }, \
+ { ZYD_CR11, 0x00 }, { ZYD_CR15, 0xd0 }, { ZYD_CR17, 0x68 }, \
+ { ZYD_CR19, 0x4a }, { ZYD_CR20, 0x0c }, { ZYD_CR21, 0x0e }, \
+ { ZYD_CR23, 0x48 }, { ZYD_CR24, 0x14 }, { ZYD_CR26, 0x90 }, \
+ { ZYD_CR27, 0x30 }, { ZYD_CR29, 0x20 }, { ZYD_CR31, 0xb2 }, \
+ { ZYD_CR32, 0x43 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 }, \
+ { ZYD_CR34, 0x0f }, { ZYD_CR35, 0xf0 }, { ZYD_CR41, 0x2a }, \
+ { ZYD_CR46, 0x7f }, { ZYD_CR47, 0x1e }, { ZYD_CR51, 0xc5 }, \
+ { ZYD_CR52, 0xc5 }, { ZYD_CR53, 0xc5 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR82, 0x00 }, \
+ { ZYD_CR83, 0x24 }, { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, \
+ { ZYD_CR86, 0x10 }, { ZYD_CR87, 0x2a }, { ZYD_CR88, 0x10 }, \
+ { ZYD_CR89, 0x24 }, { ZYD_CR90, 0x18 }, { ZYD_CR91, 0x00 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR93, 0x00 }, { ZYD_CR94, 0x01 }, \
+ { ZYD_CR95, 0x00 }, { ZYD_CR96, 0x40 }, { ZYD_CR97, 0x37 }, \
+ { ZYD_CR98, 0x05 }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x00 }, \
+ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR103, 0x27 }, \
+ { ZYD_CR104, 0x18 }, { ZYD_CR105, 0x12 }, { ZYD_CR106, 0x1a }, \
+ { ZYD_CR107, 0x24 }, { ZYD_CR108, 0x0a }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x2f }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x40 }, \
+ { ZYD_CR116, 0x40 }, { ZYD_CR117, 0xf0 }, { ZYD_CR118, 0xf0 }, \
+ { ZYD_CR119, 0x16 }, { ZYD_CR122, 0x00 }, { ZYD_CR127, 0x03 }, \
+ { ZYD_CR131, 0x08 }, { ZYD_CR138, 0x28 }, { ZYD_CR148, 0x44 }, \
+ { ZYD_CR150, 0x10 }, { ZYD_CR169, 0xbb }, { ZYD_CR170, 0xbb } \
+}
+
+#define ZYD_RFMD_RF \
+{ \
+ 0x000007, 0x07dd43, 0x080959, 0x0e6666, 0x116a57, 0x17dd43, \
+ 0x1819f9, 0x1e6666, 0x214554, 0x25e7fa, 0x27fffa, 0x294128, \
+ 0x2c0000, 0x300000, 0x340000, 0x381e0f, 0x6c180f \
+}
+
+#define ZYD_RFMD_CHANTABLE \
+{ \
+ { 0x181979, 0x1e6666 }, \
+ { 0x181989, 0x1e6666 }, \
+ { 0x181999, 0x1e6666 }, \
+ { 0x1819a9, 0x1e6666 }, \
+ { 0x1819b9, 0x1e6666 }, \
+ { 0x1819c9, 0x1e6666 }, \
+ { 0x1819d9, 0x1e6666 }, \
+ { 0x1819e9, 0x1e6666 }, \
+ { 0x1819f9, 0x1e6666 }, \
+ { 0x181a09, 0x1e6666 }, \
+ { 0x181a19, 0x1e6666 }, \
+ { 0x181a29, 0x1e6666 }, \
+ { 0x181a39, 0x1e6666 }, \
+ { 0x181a60, 0x1c0000 } \
+}
+
+#define ZYD_AL2230_PHY \
+{ \
+ { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \
+ { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, \
+ { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, \
+ { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x2b }, \
+ { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, { ZYD_CR10, 0x89 }, \
+ { ZYD_CR17, 0x28 }, { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, \
+ { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, \
+ { ZYD_CR46, 0x96 }, { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, \
+ { ZYD_CR89, 0x04 }, { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, \
+ { ZYD_CR100, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfc }, \
+ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, \
+ { ZYD_CR122, 0xe0 }, { ZYD_CR137, 0x88 }, { ZYD_CR252, 0xff }, \
+ { ZYD_CR253, 0xff }, { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x3f }, \
+ { ZYD_CR138, 0x28 }, { ZYD_CR203, 0x06 } \
+}
+
+#define ZYD_AL2230_PHY_B \
+{ \
+ { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \
+ { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \
+ { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \
+ { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \
+ { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \
+ { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \
+ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \
+ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \
+ { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \
+ { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \
+ { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \
+ { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \
+}
+
+#define ZYD_AL2230_PHY_PART1 \
+{ \
+ { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \
+}
+
+#define ZYD_AL2230_PHY_PART2 \
+{ \
+ { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \
+}
+
+#define ZYD_AL2230_PHY_PART3 \
+{ \
+ { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \
+}
+
+#define ZYD_AL2230S_PHY_INIT \
+{ \
+ { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \
+ { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \
+ { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \
+ { ZYD_CR130, 0x10 } \
+}
+
+#define ZYD_AL2230_PHY_FINI_PART1 \
+{ \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \
+ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \
+}
+
+#define ZYD_AL2230_RF_PART1 \
+{ \
+ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \
+}
+
+#define ZYD_AL2230_RF_PART2 \
+{ \
+ 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \
+ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \
+}
+
+#define ZYD_AL2230_RF_PART3 \
+{ \
+ 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \
+}
+
+#define ZYD_AL2230_RF_B \
+{ \
+ 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \
+ 0x0005a4, 0x0f4dc5, 0x0805b6, 0x0146c7, 0x000688, 0x0403b9, \
+ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \
+}
+
+#define ZYD_AL2230_RF_B_PART1 \
+{ \
+ 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \
+}
+
+#define ZYD_AL2230_RF_B_PART2 \
+{ \
+ 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \
+ 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \
+}
+
+#define ZYD_AL2230_RF_B_PART3 \
+{ \
+ 0xf01b00, 0xf01e00, 0xf01a00 \
+}
+
+#define ZYD_AL2230_CHANTABLE \
+{ \
+ { 0x03f790, 0x033331, 0x00000d }, \
+ { 0x03f790, 0x0b3331, 0x00000d }, \
+ { 0x03e790, 0x033331, 0x00000d }, \
+ { 0x03e790, 0x0b3331, 0x00000d }, \
+ { 0x03f7a0, 0x033331, 0x00000d }, \
+ { 0x03f7a0, 0x0b3331, 0x00000d }, \
+ { 0x03e7a0, 0x033331, 0x00000d }, \
+ { 0x03e7a0, 0x0b3331, 0x00000d }, \
+ { 0x03f7b0, 0x033331, 0x00000d }, \
+ { 0x03f7b0, 0x0b3331, 0x00000d }, \
+ { 0x03e7b0, 0x033331, 0x00000d }, \
+ { 0x03e7b0, 0x0b3331, 0x00000d }, \
+ { 0x03f7c0, 0x033331, 0x00000d }, \
+ { 0x03e7c0, 0x066661, 0x00000d } \
+}
+
+#define ZYD_AL2230_CHANTABLE_B \
+{ \
+ { 0x09efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x09efc0, 0x8cccd0, 0xb00000 }, \
+ { 0x09e7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x09e7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x05efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x05efc0, 0x8cccd0, 0xb00000 }, \
+ { 0x05e7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x05e7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x0defc0, 0x8cccc0, 0xb00000 }, \
+ { 0x0defc0, 0x8cccd0, 0xb00000 }, \
+ { 0x0de7c0, 0x8cccc0, 0xb00000 }, \
+ { 0x0de7c0, 0x8cccd0, 0xb00000 }, \
+ { 0x03efc0, 0x8cccc0, 0xb00000 }, \
+ { 0x03e7c0, 0x866660, 0xb00000 } \
+}
+
+#define ZYD_AL7230B_PHY_1 \
+{ \
+ { ZYD_CR240, 0x57 }, { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, \
+ { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x11 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR44, 0x33 }, { ZYD_CR106, 0x22 }, \
+ { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x27 }, \
+ { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, { ZYD_CR119, 0x0a }, \
+ { ZYD_CR122, 0xfc }, { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x28 }, \
+ { ZYD_CR26, 0x93 }, { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, \
+ { ZYD_CR41, 0x24 }, { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x96 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \
+ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR99, 0x28 }, { ZYD_CR100, 0x02 }, \
+ { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, { ZYD_CR106, 0x22 }, \
+ { ZYD_CR107, 0x3f }, { ZYD_CR109, 0x09 }, { ZYD_CR110, 0x1f }, \
+ { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, { ZYD_CR113, 0x27 }, \
+ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, { ZYD_CR116, 0x3f }, \
+ { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfc }, { ZYD_CR119, 0x10 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR137, 0x88 }, \
+ { ZYD_CR138, 0xa8 }, { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 }, \
+ { ZYD_CR251, 0x2f } \
+}
+
+#define ZYD_AL7230B_PHY_2 \
+{ \
+ { ZYD_CR251, 0x3f }, { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, \
+ { ZYD_CR130, 0x10 }, { ZYD_CR38, 0x38 }, { ZYD_CR136, 0xdf } \
+}
+
+#define ZYD_AL7230B_PHY_3 \
+{ \
+ { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 } \
+}
+
+#define ZYD_AL7230B_RF_1 \
+{ \
+ 0x09ec04, 0x8cccc8, 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, \
+ 0x6cf56a, 0xe04073, 0x193d76, 0x9dd844, 0x500007, 0xd8c010, \
+ 0x3c9000, 0xbfffff, 0x700000, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_RF_2 \
+{ \
+ 0xf15d59, 0xf15d5c, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_RF_SETCHANNEL \
+{ \
+ 0x4ff821, 0xc5fbfc, 0x21ebfe, 0xafd401, 0x6cf56a, 0xe04073, \
+ 0x193d76, 0x9dd844, 0x500007, 0xd8c010, 0x3c9000, 0xf15d58 \
+}
+
+#define ZYD_AL7230B_CHANTABLE \
+{ \
+ { 0x09ec00, 0x8cccc8 }, \
+ { 0x09ec00, 0x8cccd8 }, \
+ { 0x09ec00, 0x8cccc0 }, \
+ { 0x09ec00, 0x8cccd0 }, \
+ { 0x05ec00, 0x8cccc8 }, \
+ { 0x05ec00, 0x8cccd8 }, \
+ { 0x05ec00, 0x8cccc0 }, \
+ { 0x05ec00, 0x8cccd0 }, \
+ { 0x0dec00, 0x8cccc8 }, \
+ { 0x0dec00, 0x8cccd8 }, \
+ { 0x0dec00, 0x8cccc0 }, \
+ { 0x0dec00, 0x8cccd0 }, \
+ { 0x03ec00, 0x8cccc8 }, \
+ { 0x03ec00, 0x866660 } \
+}
+
+#define ZYD_AL2210_PHY \
+{ \
+ { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \
+ { ZYD_CR15, 0xd0 }, { ZYD_CR16, 0x40 }, { ZYD_CR17, 0x58 }, \
+ { ZYD_CR18, 0x04 }, { ZYD_CR23, 0x66 }, { ZYD_CR24, 0x14 }, \
+ { ZYD_CR26, 0x90 }, { ZYD_CR31, 0x80 }, { ZYD_CR34, 0x06 }, \
+ { ZYD_CR35, 0x3e }, { ZYD_CR38, 0x38 }, { ZYD_CR46, 0x90 }, \
+ { ZYD_CR47, 0x1e }, { ZYD_CR64, 0x64 }, { ZYD_CR79, 0xb5 }, \
+ { ZYD_CR80, 0x38 }, { ZYD_CR81, 0x30 }, { ZYD_CR113, 0xc0 }, \
+ { ZYD_CR127, 0x03 } \
+}
+
+#define ZYD_AL2210_RF \
+{ \
+ 0x2396c0, 0x00fcb1, 0x358132, 0x0108b3, 0xc77804, 0x456415, \
+ 0xff2226, 0x806667, 0x7860f8, 0xbb01c9, 0x00000a, 0x00000b \
+}
+
+#define ZYD_AL2210_CHANTABLE \
+{ \
+ 0x0196c0, 0x019710, 0x019760, 0x0197b0, 0x019800, 0x019850, \
+ 0x0198a0, 0x0198f0, 0x019940, 0x019990, 0x0199e0, 0x019a30, \
+ 0x019a80, 0x019b40 \
+}
+
+#define ZYD_GCT_PHY \
+{ \
+ { ZYD_CR47, 0x1e }, { ZYD_CR15, 0xdc }, { ZYD_CR113, 0xc0 }, \
+ { ZYD_CR20, 0x0c }, { ZYD_CR17, 0x65 }, { ZYD_CR34, 0x04 }, \
+ { ZYD_CR35, 0x35 }, { ZYD_CR24, 0x20 }, { ZYD_CR9, 0xe0 }, \
+ { ZYD_CR127, 0x02 }, { ZYD_CR10, 0x91 }, { ZYD_CR23, 0x7f }, \
+ { ZYD_CR27, 0x10 }, { ZYD_CR28, 0x7a }, { ZYD_CR79, 0xb5 }, \
+ { ZYD_CR64, 0x80 }, { ZYD_CR33, 0x28 }, { ZYD_CR38, 0x30 } \
+}
+
+#define ZYD_GCT_RF \
+{ \
+ 0x1f0000, 0x1f0000, 0x1f0200, 0x1f0600, 0x1f8600, 0x1f8600, \
+ 0x002050, 0x1f8000, 0x1f8200, 0x1f8600, 0x1c0000, 0x10c458, \
+ 0x088e92, 0x187b82, 0x0401b4, 0x140816, 0x0c7000, 0x1c0000, \
+ 0x02ccae, 0x128023, 0x0a0000, 0x1a0000, 0x06e380, 0x16cb94, \
+ 0x0e1740, 0x014980, 0x116240, 0x090000, 0x192304, 0x05112f, \
+ 0x0d54a8, 0x0f8000, 0x1c0008, 0x1c0000, 0x1a0000, 0x1c0008, \
+ 0x150000, 0x0c7000, 0x150800, 0x150000 \
+}
+
+#define ZYD_GCT_CHANTABLE \
+{ \
+ 0x1a0000, 0x1a8000, 0x1a4000, 0x1ac000, 0x1a2000, 0x1aa000, \
+ 0x1a6000, 0x1ae000, 0x1a1000, 0x1a9000, 0x1a5000, 0x1ad000, \
+ 0x1a3000, 0x1ab000 \
+}
+
+#define ZYD_MAXIM_PHY \
+{ \
+ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \
+ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \
+ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x13 }, \
+ { ZYD_CR110, 0x27 }, { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR150, 0x0d } \
+}
+
+#define ZYD_MAXIM_RF \
+{ \
+ 0x00ccd4, 0x030a03, 0x000400, 0x000ca1, 0x010072, 0x018645, \
+ 0x004006, 0x0000a7, 0x008258, 0x003fc9, 0x00040a, 0x00000b, \
+ 0x00026c \
+}
+
+#define ZYD_MAXIM_CHANTABLE \
+{ \
+ { 0x0ccd4, 0x30a03 }, \
+ { 0x22224, 0x00a13 }, \
+ { 0x37774, 0x10a13 }, \
+ { 0x0ccd4, 0x30a13 }, \
+ { 0x22224, 0x00a23 }, \
+ { 0x37774, 0x10a23 }, \
+ { 0x0ccd4, 0x30a23 }, \
+ { 0x22224, 0x00a33 }, \
+ { 0x37774, 0x10a33 }, \
+ { 0x0ccd4, 0x30a33 }, \
+ { 0x22224, 0x00a43 }, \
+ { 0x37774, 0x10a43 }, \
+ { 0x0ccd4, 0x30a43 }, \
+ { 0x199a4, 0x20a53 } \
+}
+
+#define ZYD_MAXIM2_PHY \
+{ \
+ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \
+ { ZYD_CR29, 0x00 }, { ZYD_CR26, 0x11 }, { ZYD_CR44, 0x33 }, \
+ { ZYD_CR106, 0x2a }, { ZYD_CR107, 0x1a }, { ZYD_CR109, 0x2b }, \
+ { ZYD_CR110, 0x2b }, { ZYD_CR111, 0x2b }, { ZYD_CR112, 0x2b }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0xfa }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x77 }, { ZYD_CR122, 0xfe }, \
+ { ZYD_CR10, 0x89 }, { ZYD_CR17, 0x20 }, { ZYD_CR26, 0x93 }, \
+ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x40 }, { ZYD_CR41, 0x24 }, \
+ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x90 }, { ZYD_CR79, 0x58 }, \
+ { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR89, 0x18 }, \
+ { ZYD_CR92, 0x0a }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \
+ { ZYD_CR106, 0x20 }, { ZYD_CR107, 0x24 }, { ZYD_CR109, 0x09 }, \
+ { ZYD_CR110, 0x13 }, { ZYD_CR111, 0x13 }, { ZYD_CR112, 0x13 }, \
+ { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x24 }, \
+ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xf4 }, { ZYD_CR118, 0x00 }, \
+ { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x06 }, { ZYD_CR122, 0xfe } \
+}
+
+#define ZYD_MAXIM2_RF \
+{ \
+ 0x33334, 0x10a03, 0x00400, 0x00ca1, 0x10072, 0x18645, 0x04006, \
+ 0x000a7, 0x08258, 0x03fc9, 0x0040a, 0x0000b, 0x0026c \
+}
+
+#define ZYD_MAXIM2_CHANTABLE_F \
+{ \
+ 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, \
+ 0x08884, 0x1ddd4, 0x33334, 0x08884, 0x1ddd4, 0x33334, 0x26664 \
+}
+
+#define ZYD_MAXIM2_CHANTABLE \
+{ \
+ { 0x33334, 0x10a03 }, \
+ { 0x08884, 0x20a13 }, \
+ { 0x1ddd4, 0x30a13 }, \
+ { 0x33334, 0x10a13 }, \
+ { 0x08884, 0x20a23 }, \
+ { 0x1ddd4, 0x30a23 }, \
+ { 0x33334, 0x10a23 }, \
+ { 0x08884, 0x20a33 }, \
+ { 0x1ddd4, 0x30a33 }, \
+ { 0x33334, 0x10a33 }, \
+ { 0x08884, 0x20a43 }, \
+ { 0x1ddd4, 0x30a43 }, \
+ { 0x33334, 0x10a43 }, \
+ { 0x26664, 0x20a53 } \
+}
+
+/*
+ * Control pipe requests.
+ */
+#define ZYD_DOWNLOADREQ 0x30
+#define ZYD_DOWNLOADSTS 0x31
+#define ZYD_READFWDATAREQ 0x32
+
+/* possible values for register ZYD_CR_INTERRUPT */
+#define ZYD_HWINT_MASK 0x004f0000
+
+/* possible values for register ZYD_MAC_MISC */
+#define ZYD_UNLOCK_PHY_REGS 0x80
+
+/* possible values for register ZYD_MAC_ENCRYPTION_TYPE */
+#define ZYD_ENC_SNIFFER 8
+
+/* flags for register ZYD_MAC_RXFILTER */
+#define ZYD_FILTER_ASS_REQ (1 << 0)
+#define ZYD_FILTER_ASS_RSP (1 << 1)
+#define ZYD_FILTER_REASS_REQ (1 << 2)
+#define ZYD_FILTER_REASS_RSP (1 << 3)
+#define ZYD_FILTER_PRB_REQ (1 << 4)
+#define ZYD_FILTER_PRB_RSP (1 << 5)
+#define ZYD_FILTER_BCN (1 << 8)
+#define ZYD_FILTER_ATIM (1 << 9)
+#define ZYD_FILTER_DEASS (1 << 10)
+#define ZYD_FILTER_AUTH (1 << 11)
+#define ZYD_FILTER_DEAUTH (1 << 12)
+#define ZYD_FILTER_PS_POLL (1 << 26)
+#define ZYD_FILTER_RTS (1 << 27)
+#define ZYD_FILTER_CTS (1 << 28)
+#define ZYD_FILTER_ACK (1 << 29)
+#define ZYD_FILTER_CFE (1 << 30)
+#define ZYD_FILTER_CFE_A (1 << 31)
+
+/* helpers for register ZYD_MAC_RXFILTER */
+#define ZYD_FILTER_MONITOR 0xffffffff
+#define ZYD_FILTER_BSS \
+ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \
+ ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \
+ ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \
+ (0x3 << 6) | \
+ ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \
+ ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \
+ (0x7 << 13) | \
+ ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK)
+#define ZYD_FILTER_HOSTAP \
+ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \
+ ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \
+ ZYD_FILTER_DEAUTH | ZYD_FILTER_PS_POLL)
+
+struct zyd_tx_desc {
+ uint8_t phy;
+#define ZYD_TX_PHY_SIGNAL(x) ((x) & 0xf)
+#define ZYD_TX_PHY_OFDM (1 << 4)
+#define ZYD_TX_PHY_SHPREAMBLE (1 << 5) /* CCK */
+#define ZYD_TX_PHY_5GHZ (1 << 5) /* OFDM */
+ uint16_t len;
+ uint8_t flags;
+#define ZYD_TX_FLAG_BACKOFF (1 << 0)
+#define ZYD_TX_FLAG_MULTICAST (1 << 1)
+#define ZYD_TX_FLAG_TYPE(x) (((x) & 0x3) << 2)
+#define ZYD_TX_TYPE_DATA 0
+#define ZYD_TX_TYPE_PS_POLL 1
+#define ZYD_TX_TYPE_MGMT 2
+#define ZYD_TX_TYPE_CTL 3
+#define ZYD_TX_FLAG_WAKEUP (1 << 4)
+#define ZYD_TX_FLAG_RTS (1 << 5)
+#define ZYD_TX_FLAG_ENCRYPT (1 << 6)
+#define ZYD_TX_FLAG_CTS_TO_SELF (1 << 7)
+ uint16_t pktlen;
+ uint16_t plcp_length;
+ uint8_t plcp_service;
+#define ZYD_PLCP_LENGEXT 0x80
+ uint16_t nextlen;
+} __packed;
+
+struct zyd_plcphdr {
+ uint8_t signal;
+ uint8_t reserved[2];
+ uint16_t service; /* unaligned! */
+} __packed;
+
+struct zyd_rx_stat {
+ uint8_t signal_cck;
+ uint8_t rssi;
+ uint8_t signal_ofdm;
+ uint8_t cipher;
+#define ZYD_RX_CIPHER_WEP64 1
+#define ZYD_RX_CIPHER_TKIP 2
+#define ZYD_RX_CIPHER_AES 4
+#define ZYD_RX_CIPHER_WEP128 5
+#define ZYD_RX_CIPHER_WEP256 6
+#define ZYD_RX_CIPHER_WEP \
+ (ZYD_RX_CIPHER_WEP64 | ZYD_RX_CIPHER_WEP128 | ZYD_RX_CIPHER_WEP256)
+ uint8_t flags;
+#define ZYD_RX_OFDM (1 << 0)
+#define ZYD_RX_TIMEOUT (1 << 1)
+#define ZYD_RX_OVERRUN (1 << 2)
+#define ZYD_RX_DECRYPTERR (1 << 3)
+#define ZYD_RX_BADCRC32 (1 << 4)
+#define ZYD_RX_NOT2ME (1 << 5)
+#define ZYD_RX_BADCRC16 (1 << 6)
+#define ZYD_RX_ERROR (1 << 7)
+} __packed;
+
+/* this structure may be unaligned */
+struct zyd_rx_desc {
+#define ZYD_MAX_RXFRAMECNT 3
+ uWord len[ZYD_MAX_RXFRAMECNT];
+ uWord tag;
+#define ZYD_TAG_MULTIFRAME 0x697e
+} __packed;
+
+/* I2C bus alike */
+struct zyd_rfwrite_cmd {
+ uint16_t code;
+ uint16_t width;
+ uint16_t bit[32];
+#define ZYD_RF_IF_LE (1 << 1)
+#define ZYD_RF_CLK (1 << 2)
+#define ZYD_RF_DATA (1 << 3)
+} __packed;
+
+struct zyd_cmd {
+ uint16_t code;
+#define ZYD_CMD_IOWR 0x0021 /* write HMAC or PHY register */
+#define ZYD_CMD_IORD 0x0022 /* read HMAC or PHY register */
+#define ZYD_CMD_RFCFG 0x0023 /* write RF register */
+#define ZYD_NOTIF_IORD 0x9001 /* response for ZYD_CMD_IORD */
+#define ZYD_NOTIF_MACINTR 0x9001 /* interrupt notification */
+#define ZYD_NOTIF_RETRYSTATUS 0xa001 /* Tx retry notification */
+ uint8_t data[64];
+} __packed;
+
+/* structure for command ZYD_CMD_IOWR */
+struct zyd_pair {
+ uint16_t reg;
+/* helpers macros to read/write 32-bit registers */
+#define ZYD_REG32_LO(reg) (reg)
+#define ZYD_REG32_HI(reg) \
+ ((reg) + ((((reg) & 0xf000) == 0x9000) ? 2 : 1))
+ uint16_t val;
+} __packed;
+
+/* structure for notification ZYD_NOTIF_RETRYSTATUS */
+struct zyd_notif_retry {
+ uint16_t rate;
+ uint8_t macaddr[IEEE80211_ADDR_LEN];
+ uint16_t count;
+} __packed;
+
+#define ZYD_CONFIG_INDEX 0
+#define ZYD_IFACE_INDEX 0
+
+#define ZYD_INTR_TIMEOUT 1000
+#define ZYD_TX_TIMEOUT 10000
+
+#define ZYD_MAX_TXBUFSZ \
+ (sizeof(struct zyd_tx_desc) + MCLBYTES)
+#define ZYD_MIN_FRAGSZ \
+ (sizeof(struct zyd_plcphdr) + IEEE80211_MIN_LEN + \
+ sizeof(struct zyd_rx_stat))
+#define ZYD_MIN_RXBUFSZ ZYD_MIN_FRAGSZ
+#define ZYX_MAX_RXBUFSZ \
+ ((sizeof (struct zyd_plcphdr) + IEEE80211_MAX_LEN + \
+ sizeof (struct zyd_rx_stat)) * ZYD_MAX_RXFRAMECNT + \
+ sizeof (struct zyd_rx_desc))
+#define ZYD_TX_DESC_SIZE (sizeof (struct zyd_tx_desc))
+
+#define ZYD_RX_LIST_CNT 1
+#define ZYD_TX_LIST_CNT 5
+#define ZYD_CMD_FLAG_READ (1 << 0)
+#define ZYD_CMD_FLAG_SENT (1 << 1)
+
+/* quickly determine if a given rate is CCK or OFDM */
+#define ZYD_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)
+
+struct zyd_phy_pair {
+ uint16_t reg;
+ uint8_t val;
+};
+
+struct zyd_mac_pair {
+ uint16_t reg;
+ uint32_t val;
+};
+
+struct zyd_task {
+ struct usb2_proc_msg hdr;
+ struct zyd_softc *sc;
+};
+
+struct zyd_tx_data {
+ STAILQ_ENTRY(zyd_tx_data) next;
+ struct zyd_softc *sc;
+ struct zyd_tx_desc desc;
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+ int rate;
+};
+typedef STAILQ_HEAD(, zyd_tx_data) zyd_txdhead;
+
+struct zyd_rx_data {
+ struct mbuf *m;
+ int rssi;
+};
+
+struct zyd_node {
+ struct ieee80211_node ni; /* must be the first */
+ struct ieee80211_amrr_node amn;
+};
+#define ZYD_NODE(ni) ((struct zyd_node *)(ni))
+
+struct zyd_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_antsignal;
+ int8_t wr_antnoise;
+} __packed;
+
+#define ZYD_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct zyd_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed;
+
+#define ZYD_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct zyd_softc; /* forward declaration */
+
+struct zyd_rf {
+ /* RF methods */
+ int (*init)(struct zyd_rf *);
+ int (*switch_radio)(struct zyd_rf *, int);
+ int (*set_channel)(struct zyd_rf *, uint8_t);
+ int (*bandedge6)(struct zyd_rf *,
+ struct ieee80211_channel *);
+ /* RF attributes */
+ struct zyd_softc *rf_sc; /* back-pointer */
+ int width;
+};
+
+struct zyd_rq {
+ struct zyd_cmd *cmd;
+ const uint16_t *idata;
+ struct zyd_pair *odata;
+ int ilen;
+ int olen;
+ int flags;
+ STAILQ_ENTRY(zyd_rq) rq;
+};
+
+struct zyd_vap {
+ struct ieee80211vap vap;
+ int (*newstate)(struct ieee80211vap *,
+ enum ieee80211_state, int);
+ struct ieee80211_amrr amrr;
+};
+#define ZYD_VAP(vap) ((struct zyd_vap *)(vap))
+
+enum {
+ ZYD_BULK_WR,
+ ZYD_BULK_RD,
+ ZYD_INTR_WR,
+ ZYD_INTR_RD,
+ ZYD_N_TRANSFER = 4,
+};
+
+struct zyd_softc {
+ struct ifnet *sc_ifp;
+ device_t sc_dev;
+ struct usb2_device *sc_udev;
+ struct usb2_process sc_tq;
+
+ struct usb2_xfer *sc_xfer[ZYD_N_TRANSFER];
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int sc_flags;
+#define ZYD_FLAG_FWLOADED (1 << 0)
+#define ZYD_FLAG_INITONCE (1 << 1)
+#define ZYD_FLAG_INITDONE (1 << 2)
+ int sc_if_flags;
+
+ struct zyd_task sc_synctask[2];
+ struct zyd_task sc_mcasttask[2];
+ struct zyd_task sc_scantask[2];
+ int sc_scan_action;
+#define ZYD_SCAN_START 0
+#define ZYD_SCAN_END 1
+#define ZYD_SET_CHANNEL 2
+ struct zyd_task sc_task[2];
+
+ struct zyd_rf sc_rf;
+
+ STAILQ_HEAD(, zyd_rq) sc_rtx;
+ STAILQ_HEAD(, zyd_rq) sc_rqh;
+
+ uint8_t sc_bssid[IEEE80211_ADDR_LEN];
+ uint16_t sc_fwbase;
+ uint8_t sc_regdomain;
+ uint8_t sc_macrev;
+ uint16_t sc_fwrev;
+ uint8_t sc_rfrev;
+ uint8_t sc_parev;
+ uint8_t sc_al2230s;
+ uint8_t sc_bandedge6;
+ uint8_t sc_newphy;
+ uint8_t sc_cckgain;
+ uint8_t sc_fix_cr157;
+ uint8_t sc_ledtype;
+ uint8_t sc_txled;
+
+ uint32_t sc_atim_wnd;
+ uint32_t sc_pre_tbtt;
+ uint32_t sc_bcn_int;
+
+ uint8_t sc_pwrcal[14];
+ uint8_t sc_pwrint[14];
+ uint8_t sc_ofdm36_cal[14];
+ uint8_t sc_ofdm48_cal[14];
+ uint8_t sc_ofdm54_cal[14];
+
+ struct mtx sc_mtx;
+ struct cv sc_intr_cv;
+ struct zyd_tx_data tx_data[ZYD_TX_LIST_CNT];
+ zyd_txdhead tx_q;
+ zyd_txdhead tx_free;
+ int tx_nfree;
+ struct zyd_rx_desc sc_rx_desc;
+ struct zyd_rx_data sc_rx_data[ZYD_MAX_RXFRAMECNT];
+ int sc_rx_count;
+
+ struct zyd_cmd sc_ibuf;
+
+ struct zyd_rx_radiotap_header sc_rxtap;
+ int sc_rxtap_len;
+ struct zyd_tx_radiotap_header sc_txtap;
+ int sc_txtap_len;
+};
+
+#define ZYD_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define ZYD_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define ZYD_LOCK_ASSERT(sc, t) mtx_assert(&(sc)->sc_mtx, t)
+
diff --git a/sys/dev/usb/wlan/usb_wlan.h b/sys/dev/usb/wlan/usb_wlan.h
new file mode 100644
index 0000000..9db120e
--- /dev/null
+++ b/sys/dev/usb/wlan/usb_wlan.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_WLAN_H_
+#define _USB2_WLAN_H_
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_clone.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_phy.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#endif /* _USB2_WLAN_H_ */
OpenPOWER on IntegriCloud