summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/dev/usb
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2015-02-02 14:27:13 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2015-02-13 10:34:19 +0100
commit7eeb079d84bc4abe9897be0047fc28a754e46ecd (patch)
tree9b47ac7055ce0cb1e2d86c684a2a7a1cd20c0c4d /freebsd/sys/dev/usb
parentfreebsd-to-rtems.py: Fix revert includes (diff)
downloadrtems-libbsd-7eeb079d84bc4abe9897be0047fc28a754e46ecd.tar.bz2
Update to FreeBSD 9.3
Diffstat (limited to 'freebsd/sys/dev/usb')
-rw-r--r--freebsd/sys/dev/usb/controller/ehci.c112
-rw-r--r--freebsd/sys/dev/usb/controller/ohci.c5
-rw-r--r--freebsd/sys/dev/usb/controller/usb_controller.c67
-rw-r--r--freebsd/sys/dev/usb/controller/xhcireg.h3
-rw-r--r--freebsd/sys/dev/usb/quirk/usb_quirk.c11
-rw-r--r--freebsd/sys/dev/usb/storage/umass.c4
-rw-r--r--freebsd/sys/dev/usb/usb.h3
-rw-r--r--freebsd/sys/dev/usb/usb_bus.h1
-rw-r--r--freebsd/sys/dev/usb/usb_busdma.c27
-rw-r--r--freebsd/sys/dev/usb/usb_controller.h1
-rw-r--r--freebsd/sys/dev/usb/usb_core.h2
-rw-r--r--freebsd/sys/dev/usb/usb_dev.c254
-rw-r--r--freebsd/sys/dev/usb/usb_device.c116
-rw-r--r--freebsd/sys/dev/usb/usb_device.h17
-rw-r--r--freebsd/sys/dev/usb/usb_freebsd.h3
-rw-r--r--freebsd/sys/dev/usb/usb_generic.c84
-rw-r--r--freebsd/sys/dev/usb/usb_hub.c395
-rw-r--r--freebsd/sys/dev/usb/usb_hub.h7
-rw-r--r--freebsd/sys/dev/usb/usb_ioctl.h64
-rw-r--r--freebsd/sys/dev/usb/usb_msctest.c74
-rw-r--r--freebsd/sys/dev/usb/usb_process.c12
-rw-r--r--freebsd/sys/dev/usb/usb_process.h1
-rw-r--r--freebsd/sys/dev/usb/usb_request.c80
-rw-r--r--freebsd/sys/dev/usb/usb_transfer.c59
-rw-r--r--freebsd/sys/dev/usb/usbdi.h3
25 files changed, 1145 insertions, 260 deletions
diff --git a/freebsd/sys/dev/usb/controller/ehci.c b/freebsd/sys/dev/usb/controller/ehci.c
index 456aff61..528e4a02 100644
--- a/freebsd/sys/dev/usb/controller/ehci.c
+++ b/freebsd/sys/dev/usb/controller/ehci.c
@@ -258,7 +258,7 @@ ehci_init_sub(struct ehci_softc *sc)
DPRINTF("HCC uses 64-bit structures\n");
/* MUST clear segment register if 64 bit capable */
- EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
+ EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
}
usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
@@ -1197,9 +1197,16 @@ ehci_non_isoc_done_sub(struct usb_xfer *xfer)
(status & EHCI_QTD_PINGSTATE) ? "[PING]" : "");
}
#endif
-
- return ((status & EHCI_QTD_HALTED) ?
- USB_ERR_STALLED : USB_ERR_NORMAL_COMPLETION);
+ if (status & EHCI_QTD_HALTED) {
+ if ((xfer->xroot->udev->parent_hs_hub != NULL) ||
+ (xfer->xroot->udev->address != 0)) {
+ /* try to separate I/O errors from STALL */
+ if (EHCI_QTD_GET_CERR(status) == 0)
+ return (USB_ERR_IOERROR);
+ }
+ return (USB_ERR_STALLED);
+ }
+ return (USB_ERR_NORMAL_COMPLETION);
}
static void
@@ -1653,12 +1660,17 @@ restart:
}
td->len = 0;
+ /* properly reset reserved fields */
td->qtd_buffer[0] = 0;
- td->qtd_buffer_hi[0] = 0;
-
td->qtd_buffer[1] = 0;
+ td->qtd_buffer[2] = 0;
+ td->qtd_buffer[3] = 0;
+ td->qtd_buffer[4] = 0;
+ td->qtd_buffer_hi[0] = 0;
td->qtd_buffer_hi[1] = 0;
-
+ td->qtd_buffer_hi[2] = 0;
+ td->qtd_buffer_hi[3] = 0;
+ td->qtd_buffer_hi[4] = 0;
} else {
uint8_t x;
@@ -1713,6 +1725,12 @@ restart:
htohc32(temp->sc,
buf_res.physaddr & (~0xFFF));
td->qtd_buffer_hi[x] = 0;
+
+ /* properly reset reserved fields */
+ while (++x < EHCI_QTD_NBUFFERS) {
+ td->qtd_buffer[x] = 0;
+ td->qtd_buffer_hi[x] = 0;
+ }
}
if (td_next) {
@@ -2000,6 +2018,18 @@ ehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last)
qh->qh_qtd.qtd_altnext =
htohc32(temp.sc, EHCI_LINK_TERMINATE);
+ /* properly reset reserved fields */
+ qh->qh_qtd.qtd_buffer[0] = 0;
+ qh->qh_qtd.qtd_buffer[1] = 0;
+ qh->qh_qtd.qtd_buffer[2] = 0;
+ qh->qh_qtd.qtd_buffer[3] = 0;
+ qh->qh_qtd.qtd_buffer[4] = 0;
+ qh->qh_qtd.qtd_buffer_hi[0] = 0;
+ qh->qh_qtd.qtd_buffer_hi[1] = 0;
+ qh->qh_qtd.qtd_buffer_hi[2] = 0;
+ qh->qh_qtd.qtd_buffer_hi[3] = 0;
+ qh->qh_qtd.qtd_buffer_hi[4] = 0;
+
usb_pc_cpu_flush(qh->page_cache);
if (xfer->xroot->udev->flags.self_suspended == 0) {
@@ -2230,10 +2260,26 @@ ehci_device_bulk_enter(struct usb_xfer *xfer)
}
static void
+ehci_doorbell_async(struct ehci_softc *sc)
+{
+ uint32_t temp;
+
+ /*
+ * XXX Performance quirk: Some Host Controllers have a too low
+ * interrupt rate. Issue an IAAD to stimulate the Host
+ * Controller after queueing the BULK transfer.
+ *
+ * XXX Force the host controller to refresh any QH caches.
+ */
+ temp = EOREAD4(sc, EHCI_USBCMD);
+ if (!(temp & EHCI_CMD_IAAD))
+ EOWRITE4(sc, EHCI_USBCMD, temp | EHCI_CMD_IAAD);
+}
+
+static void
ehci_device_bulk_start(struct usb_xfer *xfer)
{
ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
- uint32_t temp;
/* setup TD's and QH */
ehci_setup_standard_chain(xfer, &sc->sc_async_p_last);
@@ -2248,13 +2294,7 @@ ehci_device_bulk_start(struct usb_xfer *xfer)
if (sc->sc_flags & EHCI_SCFLG_IAADBUG)
return;
- /* XXX Performance quirk: Some Host Controllers have a too low
- * interrupt rate. Issue an IAAD to stimulate the Host
- * Controller after queueing the BULK transfer.
- */
- temp = EOREAD4(sc, EHCI_USBCMD);
- if (!(temp & EHCI_CMD_IAAD))
- EOWRITE4(sc, EHCI_USBCMD, temp | EHCI_CMD_IAAD);
+ ehci_doorbell_async(sc);
}
struct usb_pipe_methods ehci_device_bulk_methods =
@@ -3707,10 +3747,6 @@ ehci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
edesc->bEndpointAddress, udev->flags.usb_mode,
sc->sc_addr);
- if (udev->flags.usb_mode != USB_MODE_HOST) {
- /* not supported */
- return;
- }
if (udev->device_index != sc->sc_addr) {
if ((udev->speed != USB_SPEED_HIGH) &&
@@ -3754,7 +3790,7 @@ ehci_get_dma_delay(struct usb_device *udev, uint32_t *pus)
* Wait until the hardware has finished any possible use of
* the transfer descriptor(s) and QH
*/
- *pus = (188); /* microseconds */
+ *pus = (1125); /* microseconds */
}
static void
@@ -3875,6 +3911,41 @@ ehci_set_hw_power(struct usb_bus *bus)
return;
}
+static void
+ehci_start_dma_delay_second(struct usb_xfer *xfer)
+{
+ struct ehci_softc *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTF("\n");
+
+ /* trigger doorbell */
+ ehci_doorbell_async(sc);
+
+ /* give the doorbell 4ms */
+ usbd_transfer_timeout_ms(xfer,
+ (void (*)(void *))&usb_dma_delay_done_cb, 4);
+}
+
+/*
+ * Ring the doorbell twice before freeing any DMA descriptors. Some host
+ * controllers apparently cache the QH descriptors and need a message
+ * that the cache needs to be discarded.
+ */
+static void
+ehci_start_dma_delay(struct usb_xfer *xfer)
+{
+ struct ehci_softc *sc = EHCI_BUS2SC(xfer->xroot->bus);
+
+ DPRINTF("\n");
+
+ /* trigger doorbell */
+ ehci_doorbell_async(sc);
+
+ /* give the doorbell 4ms */
+ usbd_transfer_timeout_ms(xfer,
+ (void (*)(void *))&ehci_start_dma_delay_second, 4);
+}
+
struct usb_bus_methods ehci_bus_methods =
{
.endpoint_init = ehci_ep_init,
@@ -3887,4 +3958,5 @@ struct usb_bus_methods ehci_bus_methods =
.set_hw_power_sleep = ehci_set_hw_power_sleep,
.roothub_exec = ehci_roothub_exec,
.xfer_poll = ehci_do_poll,
+ .start_dma_delay = ehci_start_dma_delay,
};
diff --git a/freebsd/sys/dev/usb/controller/ohci.c b/freebsd/sys/dev/usb/controller/ohci.c
index 42129a8e..05c5e19b 100644
--- a/freebsd/sys/dev/usb/controller/ohci.c
+++ b/freebsd/sys/dev/usb/controller/ohci.c
@@ -2316,6 +2316,7 @@ ohci_roothub_exec(struct usb_device *udev,
}
v = OREAD4(sc, OHCI_RH_PORT_STATUS(index));
DPRINTFN(9, "port status=0x%04x\n", v);
+ v &= ~UPS_PORT_MODE_DEVICE; /* force host mode */
USETW(sc->sc_hub_desc.ps.wPortStatus, v);
USETW(sc->sc_hub_desc.ps.wPortChange, v >> 16);
len = sizeof(sc->sc_hub_desc.ps);
@@ -2552,10 +2553,6 @@ ohci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
edesc->bEndpointAddress, udev->flags.usb_mode,
sc->sc_addr);
- if (udev->flags.usb_mode != USB_MODE_HOST) {
- /* not supported */
- return;
- }
if (udev->device_index != sc->sc_addr) {
switch (edesc->bmAttributes & UE_XFERTYPE) {
case UE_CONTROL:
diff --git a/freebsd/sys/dev/usb/controller/usb_controller.c b/freebsd/sys/dev/usb/controller/usb_controller.c
index 37f79364..c0fda652 100644
--- a/freebsd/sys/dev/usb/controller/usb_controller.c
+++ b/freebsd/sys/dev/usb/controller/usb_controller.c
@@ -288,6 +288,28 @@ usb_resume(device_t dev)
}
/*------------------------------------------------------------------------*
+ * usb_bus_reset_async_locked
+ *------------------------------------------------------------------------*/
+void
+usb_bus_reset_async_locked(struct usb_bus *bus)
+{
+ USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
+
+ DPRINTF("\n");
+
+ if (bus->reset_msg[0].hdr.pm_qentry.tqe_prev != NULL ||
+ bus->reset_msg[1].hdr.pm_qentry.tqe_prev != NULL) {
+ DPRINTF("Reset already pending\n");
+ return;
+ }
+
+ device_printf(bus->parent, "Resetting controller\n");
+
+ usb_proc_msignal(&bus->explore_proc,
+ &bus->reset_msg[0], &bus->reset_msg[1]);
+}
+
+/*------------------------------------------------------------------------*
* usb_shutdown
*------------------------------------------------------------------------*/
static int
@@ -338,7 +360,13 @@ usb_bus_explore(struct usb_proc_msg *pm)
if (bus->no_explore != 0)
return;
- if (udev && udev->hub) {
+ if (udev != NULL) {
+ USB_BUS_UNLOCK(bus);
+ uhub_explore_handle_re_enumerate(udev);
+ USB_BUS_LOCK(bus);
+ }
+
+ if (udev != NULL && udev->hub != NULL) {
if (bus->do_probe) {
bus->do_probe = 0;
@@ -411,7 +439,7 @@ usb_bus_detach(struct usb_proc_msg *pm)
/*------------------------------------------------------------------------*
* usb_bus_suspend
*
- * This function is used to suspend the USB contoller.
+ * This function is used to suspend the USB controller.
*------------------------------------------------------------------------*/
static void
usb_bus_suspend(struct usb_proc_msg *pm)
@@ -421,6 +449,8 @@ usb_bus_suspend(struct usb_proc_msg *pm)
usb_error_t err;
uint8_t do_unlock;
+ DPRINTF("\n");
+
bus = ((struct usb_bus_msg *)pm)->bus;
udev = bus->devices[USB_ROOT_HUB_ADDR];
@@ -466,7 +496,7 @@ usb_bus_suspend(struct usb_proc_msg *pm)
/*------------------------------------------------------------------------*
* usb_bus_resume
*
- * This function is used to resume the USB contoller.
+ * This function is used to resume the USB controller.
*------------------------------------------------------------------------*/
static void
usb_bus_resume(struct usb_proc_msg *pm)
@@ -476,6 +506,8 @@ usb_bus_resume(struct usb_proc_msg *pm)
usb_error_t err;
uint8_t do_unlock;
+ DPRINTF("\n");
+
bus = ((struct usb_bus_msg *)pm)->bus;
udev = bus->devices[USB_ROOT_HUB_ADDR];
@@ -525,9 +557,31 @@ usb_bus_resume(struct usb_proc_msg *pm)
}
/*------------------------------------------------------------------------*
+ * usb_bus_reset
+ *
+ * This function is used to reset the USB controller.
+ *------------------------------------------------------------------------*/
+static void
+usb_bus_reset(struct usb_proc_msg *pm)
+{
+ struct usb_bus *bus;
+
+ DPRINTF("\n");
+
+ bus = ((struct usb_bus_msg *)pm)->bus;
+
+ if (bus->bdev == NULL || bus->no_explore != 0)
+ return;
+
+ /* a suspend and resume will reset the USB controller */
+ usb_bus_suspend(pm);
+ usb_bus_resume(pm);
+}
+
+/*------------------------------------------------------------------------*
* usb_bus_shutdown
*
- * This function is used to shutdown the USB contoller.
+ * This function is used to shutdown the USB controller.
*------------------------------------------------------------------------*/
static void
usb_bus_shutdown(struct usb_proc_msg *pm)
@@ -740,6 +794,11 @@ usb_attach_sub(device_t dev, struct usb_bus *bus)
bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume;
bus->resume_msg[1].bus = bus;
+ bus->reset_msg[0].hdr.pm_callback = &usb_bus_reset;
+ bus->reset_msg[0].bus = bus;
+ bus->reset_msg[1].hdr.pm_callback = &usb_bus_reset;
+ bus->reset_msg[1].bus = bus;
+
bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown;
bus->shutdown_msg[0].bus = bus;
bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown;
diff --git a/freebsd/sys/dev/usb/controller/xhcireg.h b/freebsd/sys/dev/usb/controller/xhcireg.h
index 85d989a6..bd1d635c 100644
--- a/freebsd/sys/dev/usb/controller/xhcireg.h
+++ b/freebsd/sys/dev/usb/controller/xhcireg.h
@@ -166,7 +166,8 @@
#define XHCI_IMOD_IVAL_SET(x) (((x) & 0xFFFF) << 0) /* 250ns unit */
#define XHCI_IMOD_ICNT_GET(x) (((x) >> 16) & 0xFFFF) /* 250ns unit */
#define XHCI_IMOD_ICNT_SET(x) (((x) & 0xFFFF) << 16) /* 250ns unit */
-#define XHCI_IMOD_DEFAULT 0x000003E8U /* 8000 IRQ/second */
+#define XHCI_IMOD_DEFAULT 0x000001F4U /* 8000 IRQs/second */
+#define XHCI_IMOD_DEFAULT_LP 0x000003F8U /* 4000 IRQs/second - LynxPoint */
#define XHCI_ERSTSZ(n) (0x0028 + (0x20 * (n))) /* XHCI event ring segment table size */
#define XHCI_ERSTS_GET(x) ((x) & 0xFFFF)
#define XHCI_ERSTS_SET(x) ((x) & 0xFFFF)
diff --git a/freebsd/sys/dev/usb/quirk/usb_quirk.c b/freebsd/sys/dev/usb/quirk/usb_quirk.c
index 9042c166..2f7ea792 100644
--- a/freebsd/sys/dev/usb/quirk/usb_quirk.c
+++ b/freebsd/sys/dev/usb/quirk/usb_quirk.c
@@ -95,6 +95,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(TELEX, MIC1, 0x009, 0x009, UQ_AU_NO_FRAC),
USB_QUIRK(SILICONPORTALS, YAPPHONE, 0x100, 0x100, UQ_AU_INP_ASYNC),
USB_QUIRK(LOGITECH, UN53B, 0x0000, 0xffff, UQ_NO_STRINGS),
+ USB_QUIRK(REALTEK, RTL8196EU, 0x0000, 0xffff, UQ_CFG_INDEX_1),
USB_QUIRK(ELSA, MODEM1, 0x0000, 0xffff, UQ_CFG_INDEX_1),
USB_QUIRK(PLANEX2, MZKUE150N, 0x0000, 0xffff, UQ_CFG_INDEX_1),
/* Quirks for printer devices */
@@ -114,6 +115,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(ITUNERNET, USBLCD2X20, 0x0000, 0xffff, UQ_HID_IGNORE),
USB_QUIRK(ITUNERNET, USBLCD4X20, 0x0000, 0xffff, UQ_HID_IGNORE),
USB_QUIRK(LIEBERT, POWERSURE_PXT, 0x0000, 0xffff, UQ_HID_IGNORE),
+ USB_QUIRK(LIEBERT2, PSI1000, 0x0000, 0xffff, UQ_HID_IGNORE),
USB_QUIRK(MGE, UPS1, 0x0000, 0xffff, UQ_HID_IGNORE),
USB_QUIRK(MGE, UPS2, 0x0000, 0xffff, UQ_HID_IGNORE),
USB_QUIRK(APPLE, IPHONE, 0x0000, 0xffff, UQ_HID_IGNORE),
@@ -165,6 +167,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY),
USB_QUIRK(ASAHIOPTICAL, OPTIO330, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY),
+ USB_QUIRK(ATP, EUSB, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(BELKIN, USB2SCSI, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(CASIO, QV_DIGICAM, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI,
@@ -235,6 +238,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(JMICRON, JM20337, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI,
UQ_MSC_NO_SYNC_CACHE),
+ USB_QUIRK(KINGSTON, HYPERX3_0, 0x0000, 0xffff, UQ_MSC_NO_INQUIRY),
USB_QUIRK(KYOCERA, FINECAM_L3, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY),
USB_QUIRK(KYOCERA, FINECAM_S3X, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI,
@@ -287,6 +291,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_IGNORE_RESIDUE),
USB_QUIRK(NETCHIP, CLIK_40, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_ATAPI,
UQ_MSC_NO_INQUIRY),
+ USB_QUIRK(NETCHIP, POCKETBOOK, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(NIKON, D300, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(OLYMPUS, C1, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
@@ -333,6 +338,9 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(SANDISK, SDDR12, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_READ_CAP_OFFBY1,
UQ_MSC_NO_GETMAXLUN),
+ USB_QUIRK(SANDISK, SDCZ2_128, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
+ UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_IGNORE_RESIDUE,
+ UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(SANDISK, SDCZ2_256, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_IGNORE_RESIDUE),
USB_QUIRK(SANDISK, SDCZ4_128, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
@@ -398,6 +406,8 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
USB_QUIRK(STMICRO, ST72682, 0x0000, 0xffff, UQ_MSC_NO_PREVENT_ALLOW),
USB_QUIRK(SUPERTOP, IDE, 0x0000, 0xffff, UQ_MSC_IGNORE_RESIDUE,
UQ_MSC_NO_SYNC_CACHE),
+ USB_QUIRK(SUPERTOP, FLASHDRIVE, 0x0000, 0xffff, UQ_MSC_NO_TEST_UNIT_READY,
+ UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(TAUGA, CAMERAMATE, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI),
USB_QUIRK(TEAC, FD05PUB, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI,
UQ_MSC_FORCE_PROTO_UFI),
@@ -427,6 +437,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = {
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY_EVPD,
UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(WESTERN, MYPASSWORD, 0x0000, 0xffff, UQ_MSC_FORCE_SHORT_INQ),
+ USB_QUIRK(WESTERN, MYPASSPORT, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE),
USB_QUIRK(WINMAXGROUP, FLASH64MC, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY),
USB_QUIRK(YANO, FW800HD, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB,
diff --git a/freebsd/sys/dev/usb/storage/umass.c b/freebsd/sys/dev/usb/storage/umass.c
index 76a1fcb6..39d32698 100644
--- a/freebsd/sys/dev/usb/storage/umass.c
+++ b/freebsd/sys/dev/usb/storage/umass.c
@@ -1326,10 +1326,12 @@ umass_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
}
sc->cbw.bCDBLength = sc->sc_transfer.cmd_len;
+ /* copy SCSI command data */
memcpy(sc->cbw.CBWCDB, sc->sc_transfer.cmd_data,
sc->sc_transfer.cmd_len);
- memset(sc->sc_transfer.cmd_data +
+ /* clear remaining command area */
+ memset(sc->cbw.CBWCDB +
sc->sc_transfer.cmd_len, 0,
sizeof(sc->cbw.CBWCDB) -
sc->sc_transfer.cmd_len);
diff --git a/freebsd/sys/dev/usb/usb.h b/freebsd/sys/dev/usb/usb.h
index 9492d491..bb5a60cd 100644
--- a/freebsd/sys/dev/usb/usb.h
+++ b/freebsd/sys/dev/usb/usb.h
@@ -493,8 +493,11 @@ typedef struct usb_interface_assoc_descriptor usb_interface_assoc_descriptor_t;
#define UICLASS_WIRELESS 0xe0
#define UISUBCLASS_RF 0x01
#define UIPROTO_BLUETOOTH 0x01
+#define UIPROTO_RNDIS 0x03
#define UICLASS_IAD 0xEF /* Interface Association Descriptor */
+#define UISUBCLASS_SYNC 0x01
+#define UIPROTO_ACTIVESYNC 0x01
#define UICLASS_APPL_SPEC 0xfe
#define UISUBCLASS_FIRMWARE_DOWNLOAD 1
diff --git a/freebsd/sys/dev/usb/usb_bus.h b/freebsd/sys/dev/usb/usb_bus.h
index 459219bd..702f623d 100644
--- a/freebsd/sys/dev/usb/usb_bus.h
+++ b/freebsd/sys/dev/usb/usb_bus.h
@@ -73,6 +73,7 @@ struct usb_bus {
struct usb_bus_msg attach_msg[2];
struct usb_bus_msg suspend_msg[2];
struct usb_bus_msg resume_msg[2];
+ struct usb_bus_msg reset_msg[2];
struct usb_bus_msg shutdown_msg[2];
/*
* This mutex protects the USB hardware:
diff --git a/freebsd/sys/dev/usb/usb_busdma.c b/freebsd/sys/dev/usb/usb_busdma.c
index 80cd8c37..7d98155c 100644
--- a/freebsd/sys/dev/usb/usb_busdma.c
+++ b/freebsd/sys/dev/usb/usb_busdma.c
@@ -213,9 +213,7 @@ usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset,
struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len)
{
struct usb_m_copy_in_arg arg = {cache, dst_offset};
- int error;
-
- error = m_apply(m, src_offset, src_len, &usbd_m_copy_in_cb, &arg);
+ (void) m_apply(m, src_offset, src_len, &usbd_m_copy_in_cb, &arg);
}
#endif
@@ -360,8 +358,7 @@ usb_dma_tag_create(struct usb_dma_tag *udt,
if (bus_dma_tag_create
( /* parent */ udt->tag_parent->tag,
/* alignment */ align,
- /* boundary */ (align == 1) ?
- USB_PAGE_SIZE : 0,
+ /* boundary */ 0,
/* lowaddr */ (2ULL << (udt->tag_parent->dma_bits - 1)) - 1,
/* highaddr */ BUS_SPACE_MAXADDR,
/* filter */ NULL,
@@ -420,6 +417,7 @@ usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs,
struct usb_page_cache *pc;
struct usb_page *pg;
usb_size_t rem;
+ bus_size_t off;
uint8_t owned;
pc = arg;
@@ -435,12 +433,13 @@ usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs,
if (error) {
goto done;
}
+
+ off = 0;
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--;
#ifdef USB_DEBUG
if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) {
/*
@@ -451,11 +450,19 @@ usb_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs,
goto done;
}
#endif
- while (nseg > 0) {
- nseg--;
- segs++;
+ while (pc->ismultiseg) {
+ off += USB_PAGE_SIZE;
+ if (off >= (segs->ds_len + rem)) {
+ /* page crossing */
+ nseg--;
+ segs++;
+ off = 0;
+ rem = 0;
+ if (nseg == 0)
+ break;
+ }
pg++;
- pg->physaddr = segs->ds_addr & ~(USB_PAGE_SIZE - 1);
+ pg->physaddr = (segs->ds_addr + off) & ~(USB_PAGE_SIZE - 1);
}
done:
diff --git a/freebsd/sys/dev/usb/usb_controller.h b/freebsd/sys/dev/usb/usb_controller.h
index ad871913..f23ade21 100644
--- a/freebsd/sys/dev/usb/usb_controller.h
+++ b/freebsd/sys/dev/usb/usb_controller.h
@@ -186,6 +186,7 @@ void usb_bus_mem_flush_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb);
uint8_t usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, usb_bus_mem_cb_t *cb);
void usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb);
uint16_t usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr);
+void usb_bus_reset_async_locked(struct usb_bus *bus);
#if USB_HAVE_TT_SUPPORT
uint8_t usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time);
#endif
diff --git a/freebsd/sys/dev/usb/usb_core.h b/freebsd/sys/dev/usb/usb_core.h
index 3dfd0d1a..48e5ee83 100644
--- a/freebsd/sys/dev/usb/usb_core.h
+++ b/freebsd/sys/dev/usb/usb_core.h
@@ -113,6 +113,8 @@ struct usb_xfer_flags_int {
uint8_t can_cancel_immed:1; /* set if USB transfer can be
* cancelled immediately */
uint8_t doing_callback:1; /* set if executing the callback */
+ uint8_t maxp_was_clamped:1; /* set if the max packet size
+ * was outside its allowed range */
};
/*
diff --git a/freebsd/sys/dev/usb/usb_dev.c b/freebsd/sys/dev/usb/usb_dev.c
index 7697b64c..3ff064ee 100644
--- a/freebsd/sys/dev/usb/usb_dev.c
+++ b/freebsd/sys/dev/usb/usb_dev.c
@@ -107,7 +107,7 @@ static void usb_dev_uninit(void *);
static int usb_fifo_uiomove(struct usb_fifo *, void *, int,
struct uio *);
static void usb_fifo_check_methods(struct usb_fifo_methods *);
-static struct usb_fifo *usb_fifo_alloc(void);
+static struct usb_fifo *usb_fifo_alloc(struct mtx *);
static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t,
uint8_t);
static void usb_loc_fill(struct usb_fs_privdata *,
@@ -122,6 +122,7 @@ static d_ioctl_t usb_ioctl;
static d_read_t usb_read;
static d_write_t usb_write;
static d_poll_t usb_poll;
+static d_kqfilter_t usb_kqfilter;
static d_ioctl_t usb_static_ioctl;
@@ -139,7 +140,8 @@ struct cdevsw usb_devsw = {
.d_flags = D_TRACKCLOSE,
.d_read = usb_read,
.d_write = usb_write,
- .d_poll = usb_poll
+ .d_poll = usb_poll,
+ .d_kqfilter = usb_kqfilter,
};
static struct cdev* usb_dev = NULL;
@@ -205,12 +207,18 @@ usb_ref_device(struct usb_cdev_privdata *cpd,
DPRINTFN(2, "no device at %u\n", cpd->dev_index);
goto error;
}
- if (cpd->udev->refcount == USB_DEV_REF_MAX) {
- DPRINTFN(2, "no dev ref\n");
+ if (cpd->udev->state == USB_STATE_DETACHED &&
+ (need_uref != 2)) {
+ DPRINTFN(2, "device is detached\n");
goto error;
}
if (need_uref) {
DPRINTFN(2, "ref udev - needed\n");
+
+ if (cpd->udev->refcount == USB_DEV_REF_MAX) {
+ DPRINTFN(2, "no dev ref\n");
+ goto error;
+ }
cpd->udev->refcount++;
mtx_unlock(&usb_ref_lock);
@@ -284,9 +292,8 @@ error:
usbd_enum_unlock(cpd->udev);
if (crd->is_uref) {
- if (--(cpd->udev->refcount) == 0) {
- cv_signal(&cpd->udev->ref_cv);
- }
+ cpd->udev->refcount--;
+ cv_broadcast(&cpd->udev->ref_cv);
}
mtx_unlock(&usb_ref_lock);
DPRINTFN(2, "fail\n");
@@ -352,24 +359,25 @@ usb_unref_device(struct usb_cdev_privdata *cpd,
crd->is_write = 0;
}
if (crd->is_uref) {
- if (--(cpd->udev->refcount) == 0) {
- cv_signal(&cpd->udev->ref_cv);
- }
crd->is_uref = 0;
+ cpd->udev->refcount--;
+ cv_broadcast(&cpd->udev->ref_cv);
}
mtx_unlock(&usb_ref_lock);
}
static struct usb_fifo *
-usb_fifo_alloc(void)
+usb_fifo_alloc(struct mtx *mtx)
{
struct usb_fifo *f;
f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO);
- if (f) {
+ if (f != NULL) {
cv_init(&f->cv_io, "FIFO-IO");
cv_init(&f->cv_drain, "FIFO-DRAIN");
+ f->priv_mtx = mtx;
f->refcount = 1;
+ knlist_init_mtx(&f->selinfo.si_note, mtx);
}
return (f);
}
@@ -493,7 +501,7 @@ usb_fifo_create(struct usb_cdev_privdata *cpd,
DPRINTFN(5, "dev_get_endpoint returned NULL\n");
return (EINVAL);
}
- f = usb_fifo_alloc();
+ f = usb_fifo_alloc(&udev->device_mtx);
if (f == NULL) {
DPRINTFN(5, "could not alloc tx fifo\n");
return (ENOMEM);
@@ -501,7 +509,6 @@ usb_fifo_create(struct usb_cdev_privdata *cpd,
/* update some fields */
f->fifo_index = n + USB_FIFO_TX;
f->dev_ep_index = e;
- f->priv_mtx = &udev->device_mtx;
f->priv_sc0 = ep;
f->methods = &usb_ugen_methods;
f->iface_index = ep->iface_index;
@@ -520,7 +527,7 @@ usb_fifo_create(struct usb_cdev_privdata *cpd,
DPRINTFN(5, "dev_get_endpoint returned NULL\n");
return (EINVAL);
}
- f = usb_fifo_alloc();
+ f = usb_fifo_alloc(&udev->device_mtx);
if (f == NULL) {
DPRINTFN(5, "could not alloc rx fifo\n");
return (ENOMEM);
@@ -528,7 +535,6 @@ usb_fifo_create(struct usb_cdev_privdata *cpd,
/* update some fields */
f->fifo_index = n + USB_FIFO_RX;
f->dev_ep_index = e;
- f->priv_mtx = &udev->device_mtx;
f->priv_sc0 = ep;
f->methods = &usb_ugen_methods;
f->iface_index = ep->iface_index;
@@ -595,6 +601,13 @@ usb_fifo_free(struct usb_fifo *f)
mtx_unlock(f->priv_mtx);
mtx_lock(&usb_ref_lock);
+ /*
+ * Check if the "f->refcount" variable reached zero
+ * during the unlocked time before entering wait:
+ */
+ if (f->refcount == 0)
+ break;
+
/* wait for sync */
cv_wait(&f->cv_drain, &usb_ref_lock);
}
@@ -606,6 +619,10 @@ usb_fifo_free(struct usb_fifo *f)
cv_destroy(&f->cv_io);
cv_destroy(&f->cv_drain);
+ knlist_clear(&f->selinfo.si_note, 0);
+ seldrain(&f->selinfo);
+ knlist_destroy(&f->selinfo.si_note);
+
free(f, M_USBDEV);
}
@@ -760,7 +777,12 @@ usb_fifo_close(struct usb_fifo *f, int fflags)
mtx_lock(f->priv_mtx);
/* clear current cdev private data pointer */
+ mtx_lock(&usb_ref_lock);
f->curr_cpd = NULL;
+ mtx_unlock(&usb_ref_lock);
+
+ /* check if we are watched by kevent */
+ KNOTE_LOCKED(&f->selinfo.si_note, 0);
/* check if we are selected */
if (f->flag_isselect) {
@@ -913,23 +935,12 @@ usb_close(void *arg)
DPRINTFN(2, "cpd=%p\n", cpd);
- err = usb_ref_device(cpd, &refs, 0);
- if (err)
+ err = usb_ref_device(cpd, &refs,
+ 2 /* uref and allow detached state */);
+ if (err) {
+ DPRINTFN(2, "Cannot grab USB reference when "
+ "closing USB file handle\n");
goto done;
-
- /*
- * If this function is not called directly from the root HUB
- * thread, there is usually a need to lock the enumeration
- * lock. Check this.
- */
- if (!usbd_enum_is_locked(cpd->udev)) {
-
- DPRINTFN(2, "Locking enumeration\n");
-
- /* reference device */
- err = usb_usb_ref_device(cpd, &refs);
- if (err)
- goto done;
}
if (cpd->fflags & FREAD) {
usb_fifo_close(refs.rxfifo, cpd->fflags);
@@ -1097,15 +1108,20 @@ usb_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int fflag, struct thread*
/* Wait for re-enumeration, if any */
- while (f->udev->re_enumerate_wait != 0) {
+ while (f->udev->re_enumerate_wait != USB_RE_ENUM_DONE) {
usb_unref_device(cpd, &refs);
usb_pause_mtx(NULL, hz / 128);
- if (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
- err = ENXIO;
- goto done;
+ while (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
+ if (usb_ref_device(cpd, &refs, 0)) {
+ /* device no longer exits */
+ err = ENXIO;
+ goto done;
+ }
+ usb_unref_device(cpd, &refs);
+ usb_pause_mtx(NULL, hz / 128);
}
}
@@ -1114,6 +1130,162 @@ done:
return (err);
}
+static void
+usb_filter_detach(struct knote *kn)
+{
+ struct usb_fifo *f = kn->kn_hook;
+ knlist_remove(&f->selinfo.si_note, kn, 0);
+}
+
+static int
+usb_filter_write(struct knote *kn, long hint)
+{
+ struct usb_cdev_privdata* cpd;
+ struct usb_fifo *f;
+ struct usb_mbuf *m;
+
+ DPRINTFN(2, "\n");
+
+ f = kn->kn_hook;
+
+ mtx_assert(f->priv_mtx, MA_OWNED);
+
+ cpd = f->curr_cpd;
+ if (cpd == NULL) {
+ m = (void *)1;
+ } else if (f->fs_ep_max == 0) {
+ 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;
+ }
+ }
+ return (m ? 1 : 0);
+}
+
+static int
+usb_filter_read(struct knote *kn, long hint)
+{
+ struct usb_cdev_privdata* cpd;
+ struct usb_fifo *f;
+ struct usb_mbuf *m;
+
+ DPRINTFN(2, "\n");
+
+ f = kn->kn_hook;
+
+ mtx_assert(f->priv_mtx, MA_OWNED);
+
+ cpd = f->curr_cpd;
+ if (cpd == NULL) {
+ m = (void *)1;
+ } else if (f->fs_ep_max == 0) {
+ if (f->flag_iserror) {
+ /* we have an 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);
+
+ /* start reading data, if any */
+ if (m == NULL)
+ (f->methods->f_start_read) (f);
+ }
+ } else {
+ if (f->flag_iscomplete) {
+ m = (void *)1;
+ } else {
+ m = NULL;
+ }
+ }
+ return (m ? 1 : 0);
+}
+
+static struct filterops usb_filtops_write = {
+ .f_isfd = 1,
+ .f_detach = usb_filter_detach,
+ .f_event = usb_filter_write,
+};
+
+static struct filterops usb_filtops_read = {
+ .f_isfd = 1,
+ .f_detach = usb_filter_detach,
+ .f_event = usb_filter_read,
+};
+
+
+/* ARGSUSED */
+static int
+usb_kqfilter(struct cdev* dev, struct knote *kn)
+{
+ struct usb_cdev_refdata refs;
+ struct usb_cdev_privdata* cpd;
+ struct usb_fifo *f;
+ int fflags;
+ int err = EINVAL;
+
+ DPRINTFN(2, "\n");
+
+ if (devfs_get_cdevpriv((void **)&cpd) != 0 ||
+ usb_ref_device(cpd, &refs, 0) != 0)
+ return (ENXIO);
+
+ fflags = cpd->fflags;
+
+ /* Figure out who needs service */
+ switch (kn->kn_filter) {
+ case EVFILT_WRITE:
+ if (fflags & FWRITE) {
+ f = refs.txfifo;
+ kn->kn_fop = &usb_filtops_write;
+ err = 0;
+ }
+ break;
+ case EVFILT_READ:
+ if (fflags & FREAD) {
+ f = refs.rxfifo;
+ kn->kn_fop = &usb_filtops_read;
+ err = 0;
+ }
+ break;
+ default:
+ err = EOPNOTSUPP;
+ break;
+ }
+
+ if (err == 0) {
+ kn->kn_hook = f;
+ mtx_lock(f->priv_mtx);
+ knlist_add(&f->selinfo.si_note, kn, 1);
+ mtx_unlock(f->priv_mtx);
+ }
+
+ usb_unref_device(cpd, &refs);
+ return (err);
+}
+
/* ARGSUSED */
static int
usb_poll(struct cdev* dev, int events, struct thread* td)
@@ -1181,7 +1353,7 @@ usb_poll(struct cdev* dev, int events, struct thread* td)
if (!refs.is_usbfs) {
if (f->flag_iserror) {
- /* we have and error */
+ /* we have an error */
m = (void *)1;
} else {
if (f->queue_data == NULL) {
@@ -1578,6 +1750,8 @@ usb_fifo_wakeup(struct usb_fifo *f)
{
usb_fifo_signal(f);
+ KNOTE_LOCKED(&f->selinfo.si_note, 0);
+
if (f->flag_isselect) {
selwakeup(&f->selinfo);
f->flag_isselect = 0;
@@ -1693,8 +1867,8 @@ usb_fifo_attach(struct usb_device *udev, void *priv_sc,
break;
}
- f_tx = usb_fifo_alloc();
- f_rx = usb_fifo_alloc();
+ f_tx = usb_fifo_alloc(priv_mtx);
+ f_rx = usb_fifo_alloc(priv_mtx);
if ((f_tx == NULL) || (f_rx == NULL)) {
usb_fifo_free(f_tx);
@@ -1705,7 +1879,6 @@ usb_fifo_attach(struct usb_device *udev, void *priv_sc,
f_tx->fifo_index = n + USB_FIFO_TX;
f_tx->dev_ep_index = -1;
- f_tx->priv_mtx = priv_mtx;
f_tx->priv_sc0 = priv_sc;
f_tx->methods = pm;
f_tx->iface_index = iface_index;
@@ -1713,7 +1886,6 @@ usb_fifo_attach(struct usb_device *udev, void *priv_sc,
f_rx->fifo_index = n + USB_FIFO_RX;
f_rx->dev_ep_index = -1;
- f_rx->priv_mtx = priv_mtx;
f_rx->priv_sc0 = priv_sc;
f_rx->methods = pm;
f_rx->iface_index = iface_index;
diff --git a/freebsd/sys/dev/usb/usb_device.c b/freebsd/sys/dev/usb/usb_device.c
index 78ae3d8c..e18c32df 100644
--- a/freebsd/sys/dev/usb/usb_device.c
+++ b/freebsd/sys/dev/usb/usb_device.c
@@ -96,7 +96,7 @@ static void usb_init_attach_arg(struct usb_device *,
struct usb_attach_arg *);
static void usb_suspend_resume_sub(struct usb_device *, device_t,
uint8_t);
-static void usbd_clear_stall_proc(struct usb_proc_msg *_pm);
+static usb_proc_callback_t usbd_clear_stall_proc;
static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t);
static void usbd_set_device_strings(struct usb_device *);
#if USB_HAVE_DEVCTL
@@ -437,6 +437,65 @@ usb_endpoint_foreach(struct usb_device *udev, struct usb_endpoint *ep)
}
/*------------------------------------------------------------------------*
+ * usb_wait_pending_ref_locked
+ *
+ * This function will wait for any USB references to go away before
+ * returning and disable further USB device refcounting on the
+ * specified USB device. This function is used when detaching a USB
+ * device.
+ *------------------------------------------------------------------------*/
+static void
+usb_wait_pending_ref_locked(struct usb_device *udev)
+{
+#if USB_HAVE_UGEN
+ const uint16_t refcount =
+ usb_proc_is_called_from(
+ &udev->bus->explore_proc) ? 1 : 2;
+
+ DPRINTF("Refcount = %d\n", (int)refcount);
+
+ while (1) {
+ /* wait for any pending references to go away */
+ mtx_lock(&usb_ref_lock);
+ if (udev->refcount == refcount) {
+ /* prevent further refs being taken */
+ udev->refcount = USB_DEV_REF_MAX;
+ mtx_unlock(&usb_ref_lock);
+ break;
+ }
+ usbd_enum_unlock(udev);
+ cv_wait(&udev->ref_cv, &usb_ref_lock);
+ mtx_unlock(&usb_ref_lock);
+ (void) usbd_enum_lock(udev);
+ }
+#endif
+}
+
+/*------------------------------------------------------------------------*
+ * usb_ref_restore_locked
+ *
+ * This function will restore the reference count value after a call
+ * to "usb_wait_pending_ref_locked()".
+ *------------------------------------------------------------------------*/
+static void
+usb_ref_restore_locked(struct usb_device *udev)
+{
+#if USB_HAVE_UGEN
+ const uint16_t refcount =
+ usb_proc_is_called_from(
+ &udev->bus->explore_proc) ? 1 : 2;
+
+ DPRINTF("Refcount = %d\n", (int)refcount);
+
+ /* restore reference count and wakeup waiters, if any */
+ mtx_lock(&usb_ref_lock);
+ udev->refcount = refcount;
+ cv_broadcast(&udev->ref_cv);
+ mtx_unlock(&usb_ref_lock);
+#endif
+}
+
+/*------------------------------------------------------------------------*
* usb_unconfigure
*
* This function will free all USB interfaces and USB endpoints belonging
@@ -1097,6 +1156,9 @@ usb_detach_device(struct usb_device *udev, uint8_t iface_index,
sx_assert(&udev->enum_sx, SA_LOCKED);
+ /* wait for pending refs to go away */
+ usb_wait_pending_ref_locked(udev);
+
/*
* First detach the child to give the child's detach routine a
* chance to detach the sub-devices in the correct order.
@@ -1123,6 +1185,8 @@ usb_detach_device(struct usb_device *udev, uint8_t iface_index,
usb_detach_device_sub(udev, &iface->subdev,
&iface->pnpinfo, flag);
}
+
+ usb_ref_restore_locked(udev);
}
/*------------------------------------------------------------------------*
@@ -1450,7 +1514,7 @@ usb_suspend_resume(struct usb_device *udev, uint8_t do_suspend)
static void
usbd_clear_stall_proc(struct usb_proc_msg *_pm)
{
- struct usb_clear_stall_msg *pm = (void *)_pm;
+ struct usb_udev_msg *pm = (void *)_pm;
struct usb_device *udev = pm->udev;
/* Change lock */
@@ -1679,10 +1743,14 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
err = usbd_setup_device_desc(udev, NULL);
if (err != 0) {
- /* XXX try to re-enumerate the device */
+ /* try to enumerate two more times */
err = usbd_req_re_enumerate(udev, NULL);
- if (err)
- goto done;
+ if (err != 0) {
+ err = usbd_req_re_enumerate(udev, NULL);
+ if (err != 0) {
+ goto done;
+ }
+ }
}
/*
@@ -2038,6 +2106,8 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
DPRINTFN(4, "udev=%p port=%d\n", udev, udev->port_no);
bus = udev->bus;
+
+ /* set DETACHED state to prevent any further references */
usb_set_device_state(udev, USB_STATE_DETACHED);
#if USB_HAVE_DEVCTL
@@ -2053,23 +2123,6 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
usb_free_symlink(udev->ugen_symlink);
udev->ugen_symlink = NULL;
}
-#endif
- /*
- * Unregister our device first which will prevent any further
- * references:
- */
- usb_bus_port_set_device(bus, udev->parent_hub ?
- udev->parent_hub->hub->ports + udev->port_index : NULL,
- NULL, USB_ROOT_HUB_ADDR);
-
-#if USB_HAVE_UGEN
- /* wait for all pending references to go away: */
- mtx_lock(&usb_ref_lock);
- udev->refcount--;
- while (udev->refcount != 0) {
- cv_wait(&udev->ref_cv, &usb_ref_lock);
- }
- mtx_unlock(&usb_ref_lock);
usb_destroy_dev(udev->ctrl_dev);
#endif
@@ -2082,6 +2135,11 @@ usb_free_device(struct usb_device *udev, uint8_t flag)
/* the following will get the device unconfigured in software */
usb_unconfigure(udev, USB_UNCFG_FLAG_FREE_EP0);
+ /* final device unregister after all character devices are closed */
+ usb_bus_port_set_device(bus, udev->parent_hub ?
+ udev->parent_hub->hub->ports + udev->port_index : NULL,
+ NULL, USB_ROOT_HUB_ADDR);
+
/* unsetup any leftover default USB transfers */
usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX);
@@ -2582,8 +2640,14 @@ usb_fifo_free_wrap(struct usb_device *udev,
/* no need to free this FIFO */
continue;
}
+ /* wait for pending refs to go away */
+ usb_wait_pending_ref_locked(udev);
+
/* free this FIFO */
usb_fifo_free(f);
+
+ /* restore refcount */
+ usb_ref_restore_locked(udev);
}
}
#endif
@@ -2615,8 +2679,14 @@ usb_set_device_state(struct usb_device *udev, enum usb_dev_state state)
DPRINTF("udev %p state %s -> %s\n", udev,
usb_statestr(udev->state), usb_statestr(state));
- udev->state = state;
+#if USB_HAVE_UGEN
+ mtx_lock(&usb_ref_lock);
+#endif
+ udev->state = state;
+#if USB_HAVE_UGEN
+ mtx_unlock(&usb_ref_lock);
+#endif
if (udev->bus->methods->device_state_change != NULL)
(udev->bus->methods->device_state_change) (udev);
}
diff --git a/freebsd/sys/dev/usb/usb_device.h b/freebsd/sys/dev/usb/usb_device.h
index 8e13e3de..361f5c3c 100644
--- a/freebsd/sys/dev/usb/usb_device.h
+++ b/freebsd/sys/dev/usb/usb_device.h
@@ -53,7 +53,7 @@ struct usb_symlink; /* UGEN */
#define USB_UNCFG_FLAG_NONE 0x00
#define USB_UNCFG_FLAG_FREE_EP0 0x02 /* endpoint zero is freed */
-struct usb_clear_stall_msg {
+struct usb_udev_msg {
struct usb_proc_msg hdr;
struct usb_device *udev;
};
@@ -179,8 +179,8 @@ union usb_device_scratch {
* these structures for every USB device.
*/
struct usb_device {
- struct usb_clear_stall_msg cs_msg[2]; /* generic clear stall
- * messages */
+ /* generic clear stall message */
+ struct usb_udev_msg cs_msg[2];
struct sx enum_sx;
struct sx sr_sx;
struct mtx device_mtx;
@@ -220,6 +220,7 @@ struct usb_device {
uint8_t address; /* device addess */
uint8_t device_index; /* device index in "bus->devices" */
uint8_t controller_slot_id; /* controller specific value */
+ uint8_t next_config_index; /* used by USB_RE_ENUM_SET_CONFIG */
uint8_t curr_config_index; /* current configuration index */
uint8_t curr_config_no; /* current configuration number */
uint8_t depth; /* distance from root HUB */
@@ -230,6 +231,10 @@ struct usb_device {
uint8_t driver_added_refcount; /* our driver added generation count */
uint8_t power_mode; /* see USB_POWER_XXX */
uint8_t re_enumerate_wait; /* set if re-enum. is in progress */
+#define USB_RE_ENUM_DONE 0
+#define USB_RE_ENUM_START 1
+#define USB_RE_ENUM_PWR_OFF 2
+#define USB_RE_ENUM_SET_CONFIG 3
uint8_t ifaces_max; /* number of interfaces present */
uint8_t endpoints_max; /* number of endpoints present */
@@ -301,4 +306,10 @@ void usbd_sr_lock(struct usb_device *);
void usbd_sr_unlock(struct usb_device *);
uint8_t usbd_enum_is_locked(struct usb_device *);
+#if USB_HAVE_TT_SUPPORT
+void uhub_tt_buffer_reset_async_locked(struct usb_device *, struct usb_endpoint *);
+#endif
+
+uint8_t uhub_count_active_host_ports(struct usb_device *, enum usb_dev_speed);
+
#endif /* _USB_DEVICE_H_ */
diff --git a/freebsd/sys/dev/usb/usb_freebsd.h b/freebsd/sys/dev/usb/usb_freebsd.h
index 06369a25..4cd1758a 100644
--- a/freebsd/sys/dev/usb/usb_freebsd.h
+++ b/freebsd/sys/dev/usb/usb_freebsd.h
@@ -45,6 +45,9 @@
#define USB_HAVE_PF 1
#endif /* __rtems__ */
+/* define zero ticks callout value */
+#define USB_CALLOUT_ZERO_TICKS 1
+
#define USB_TD_GET_PROC(td) (td)->td_proc
#define USB_PROC_GET_GID(td) (td)->p_pgid
diff --git a/freebsd/sys/dev/usb/usb_generic.c b/freebsd/sys/dev/usb/usb_generic.c
index a0b7f007..e1fc141f 100644
--- a/freebsd/sys/dev/usb/usb_generic.c
+++ b/freebsd/sys/dev/usb/usb_generic.c
@@ -612,24 +612,17 @@ ugen_set_config(struct usb_fifo *f, uint8_t index)
/* 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 (usbd_set_config_index(f->udev, index)) {
- return (EIO);
- }
- /* probe and attach */
- if (usb_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) {
+
+ if (usbd_start_set_config(f->udev, index) != 0)
return (EIO);
- }
+
return (0);
}
@@ -963,11 +956,6 @@ ugen_re_enumerate(struct usb_fifo *f)
DPRINTFN(6, "device mode\n");
return (ENOTTY);
}
- if (udev->parent_hub == NULL) {
- /* the root HUB cannot be re-enumerated */
- DPRINTFN(6, "cannot reset root HUB\n");
- return (EINVAL);
- }
/* make sure all FIFO's are gone */
/* else there can be a deadlock */
if (ugen_fs_uninit(f)) {
@@ -1751,16 +1739,11 @@ ugen_set_power_mode(struct usb_fifo *f, int mode)
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");
+ if (udev->flags.usb_mode == USB_MODE_HOST &&
+ udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+ udev->re_enumerate_wait = USB_RE_ENUM_PWR_OFF;
}
-
- /* clear port enable */
- err = usbd_req_clear_port_feature(udev->parent_hub,
- NULL, udev->port_no, UHF_PORT_ENABLE);
+ /* set power mode will wake up the explore thread */
break;
case USB_POWER_MODE_ON:
@@ -1808,9 +1791,9 @@ ugen_set_power_mode(struct usb_fifo *f, int mode)
/* if we are powered off we need to re-enumerate first */
if (old_mode == USB_POWER_MODE_OFF) {
- if (udev->flags.usb_mode == USB_MODE_HOST) {
- if (udev->re_enumerate_wait == 0)
- udev->re_enumerate_wait = 1;
+ if (udev->flags.usb_mode == USB_MODE_HOST &&
+ udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+ udev->re_enumerate_wait = USB_RE_ENUM_START;
}
/* set power mode will wake up the explore thread */
}
@@ -1833,6 +1816,46 @@ ugen_get_power_mode(struct usb_fifo *f)
}
static int
+ugen_get_port_path(struct usb_fifo *f, struct usb_device_port_path *dpp)
+{
+ struct usb_device *udev = f->udev;
+ struct usb_device *next;
+ unsigned int nlevel = 0;
+
+ if (udev == NULL)
+ goto error;
+
+ dpp->udp_bus = device_get_unit(udev->bus->bdev);
+ dpp->udp_index = udev->device_index;
+
+ /* count port levels */
+ next = udev;
+ while (next->parent_hub != NULL) {
+ nlevel++;
+ next = next->parent_hub;
+ }
+
+ /* check if too many levels */
+ if (nlevel > USB_DEVICE_PORT_PATH_MAX)
+ goto error;
+
+ /* store port index array */
+ next = udev;
+ while (next->parent_hub != NULL) {
+ nlevel--;
+
+ dpp->udp_port_no[nlevel] = next->port_no;
+ dpp->udp_port_level = nlevel;
+
+ next = next->parent_hub;
+ }
+ return (0); /* success */
+
+error:
+ return (EINVAL); /* failure */
+}
+
+static int
ugen_get_power_usage(struct usb_fifo *f)
{
struct usb_device *udev = f->udev;
@@ -2033,6 +2056,7 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
struct usb_device_stats *stat;
struct usb_fs_init *pinit;
struct usb_fs_uninit *puninit;
+ struct usb_device_port_path *dpp;
uint32_t *ptime;
void *addr;
int *pint;
@@ -2205,6 +2229,10 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
*u.pint = ugen_get_power_mode(f);
break;
+ case USB_GET_DEV_PORT_PATH:
+ error = ugen_get_port_path(f, u.dpp);
+ break;
+
case USB_GET_POWER_USAGE:
*u.pint = ugen_get_power_usage(f);
break;
diff --git a/freebsd/sys/dev/usb/usb_hub.c b/freebsd/sys/dev/usb/usb_hub.c
index 2dee6784..9b3bd076 100644
--- a/freebsd/sys/dev/usb/usb_hub.c
+++ b/freebsd/sys/dev/usb/usb_hub.c
@@ -52,7 +52,6 @@
#include <sys/priv.h>
#include <dev/usb/usb.h>
-#include <dev/usb/usb_ioctl.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
@@ -73,7 +72,13 @@
#include <dev/usb/usb_bus.h>
#define UHUB_INTR_INTERVAL 250 /* ms */
-#define UHUB_N_TRANSFER 1
+enum {
+ UHUB_INTR_TRANSFER,
+#if USB_HAVE_TT_SUPPORT
+ UHUB_RESET_TT_TRANSFER,
+#endif
+ UHUB_N_TRANSFER,
+};
#ifdef USB_DEBUG
static int uhub_debug = 0;
@@ -126,6 +131,9 @@ static bus_child_location_str_t uhub_child_location_string;
static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string;
static usb_callback_t uhub_intr_callback;
+#if USB_HAVE_TT_SUPPORT
+static usb_callback_t uhub_reset_tt_callback;
+#endif
static void usb_dev_resume_peer(struct usb_device *udev);
static void usb_dev_suspend_peer(struct usb_device *udev);
@@ -133,7 +141,7 @@ static uint8_t usb_peer_should_wakeup(struct usb_device *udev);
static const struct usb_config uhub_config[UHUB_N_TRANSFER] = {
- [0] = {
+ [UHUB_INTR_TRANSFER] = {
.type = UE_INTERRUPT,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_ANY,
@@ -143,6 +151,17 @@ static const struct usb_config uhub_config[UHUB_N_TRANSFER] = {
.callback = &uhub_intr_callback,
.interval = UHUB_INTR_INTERVAL,
},
+#if USB_HAVE_TT_SUPPORT
+ [UHUB_RESET_TT_TRANSFER] = {
+ .type = UE_CONTROL,
+ .endpoint = 0x00, /* Control pipe */
+ .direction = UE_DIR_ANY,
+ .bufsize = sizeof(struct usb_device_request),
+ .callback = &uhub_reset_tt_callback,
+ .timeout = 1000, /* 1 second */
+ .usb_mode = USB_MODE_HOST,
+ },
+#endif
};
/*
@@ -212,6 +231,279 @@ uhub_intr_callback(struct usb_xfer *xfer, usb_error_t error)
}
/*------------------------------------------------------------------------*
+ * uhub_reset_tt_proc
+ *
+ * This function starts the TT reset USB request
+ *------------------------------------------------------------------------*/
+#if USB_HAVE_TT_SUPPORT
+static void
+uhub_reset_tt_proc(struct usb_proc_msg *_pm)
+{
+ struct usb_udev_msg *pm = (void *)_pm;
+ struct usb_device *udev = pm->udev;
+ struct usb_hub *hub;
+ struct uhub_softc *sc;
+
+ hub = udev->hub;
+ if (hub == NULL)
+ return;
+ sc = hub->hubsoftc;
+ if (sc == NULL)
+ return;
+
+ /* Change lock */
+ USB_BUS_UNLOCK(udev->bus);
+ mtx_lock(&sc->sc_mtx);
+ /* Start transfer */
+ usbd_transfer_start(sc->sc_xfer[UHUB_RESET_TT_TRANSFER]);
+ /* Change lock */
+ mtx_unlock(&sc->sc_mtx);
+ USB_BUS_LOCK(udev->bus);
+}
+#endif
+
+/*------------------------------------------------------------------------*
+ * uhub_tt_buffer_reset_async_locked
+ *
+ * This function queues a TT reset for the given USB device and endpoint.
+ *------------------------------------------------------------------------*/
+#if USB_HAVE_TT_SUPPORT
+void
+uhub_tt_buffer_reset_async_locked(struct usb_device *child, struct usb_endpoint *ep)
+{
+ struct usb_device_request req;
+ struct usb_device *udev;
+ struct usb_hub *hub;
+ struct usb_port *up;
+ uint16_t wValue;
+ uint8_t port;
+
+ if (child == NULL || ep == NULL)
+ return;
+
+ udev = child->parent_hs_hub;
+ port = child->hs_port_no;
+
+ if (udev == NULL)
+ return;
+
+ hub = udev->hub;
+ if ((hub == NULL) ||
+ (udev->speed != USB_SPEED_HIGH) ||
+ (child->speed != USB_SPEED_LOW &&
+ child->speed != USB_SPEED_FULL) ||
+ (child->flags.usb_mode != USB_MODE_HOST) ||
+ (port == 0) || (ep->edesc == NULL)) {
+ /* not applicable */
+ return;
+ }
+
+ USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
+
+ up = hub->ports + port - 1;
+
+ if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
+ udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
+ port = 1;
+
+ /* if we already received a clear buffer request, reset the whole TT */
+ if (up->req_reset_tt.bRequest != 0) {
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_RESET_TT;
+ USETW(req.wValue, 0);
+ req.wIndex[0] = port;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ } else {
+ wValue = (ep->edesc->bEndpointAddress & 0xF) |
+ ((child->address & 0x7F) << 4) |
+ ((ep->edesc->bEndpointAddress & 0x80) << 8) |
+ ((ep->edesc->bmAttributes & 3) << 12);
+
+ req.bmRequestType = UT_WRITE_CLASS_OTHER;
+ req.bRequest = UR_CLEAR_TT_BUFFER;
+ USETW(req.wValue, wValue);
+ req.wIndex[0] = port;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+ }
+ up->req_reset_tt = req;
+ /* get reset transfer started */
+ usb_proc_msignal(&udev->bus->non_giant_callback_proc,
+ &hub->tt_msg[0], &hub->tt_msg[1]);
+}
+#endif
+
+#if USB_HAVE_TT_SUPPORT
+static void
+uhub_reset_tt_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+ struct uhub_softc *sc;
+ struct usb_device *udev;
+ struct usb_port *up;
+ uint8_t x;
+
+ DPRINTF("TT buffer reset\n");
+
+ sc = usbd_xfer_softc(xfer);
+ udev = sc->sc_udev;
+
+ switch (USB_GET_STATE(xfer)) {
+ case USB_ST_TRANSFERRED:
+ case USB_ST_SETUP:
+tr_setup:
+ USB_BUS_LOCK(udev->bus);
+ /* find first port which needs a TT reset */
+ for (x = 0; x != udev->hub->nports; x++) {
+ up = udev->hub->ports + x;
+
+ if (up->req_reset_tt.bRequest == 0)
+ continue;
+
+ /* copy in the transfer */
+ usbd_copy_in(xfer->frbuffers, 0, &up->req_reset_tt,
+ sizeof(up->req_reset_tt));
+ /* reset buffer */
+ memset(&up->req_reset_tt, 0, sizeof(up->req_reset_tt));
+
+ /* set length */
+ usbd_xfer_set_frame_len(xfer, 0, sizeof(up->req_reset_tt));
+ xfer->nframes = 1;
+ USB_BUS_UNLOCK(udev->bus);
+
+ usbd_transfer_submit(xfer);
+ return;
+ }
+ USB_BUS_UNLOCK(udev->bus);
+ break;
+
+ default:
+ if (error == USB_ERR_CANCELLED)
+ break;
+
+ DPRINTF("TT buffer reset failed (%s)\n", usbd_errstr(error));
+ goto tr_setup;
+ }
+}
+#endif
+
+/*------------------------------------------------------------------------*
+ * uhub_count_active_host_ports
+ *
+ * This function counts the number of active ports at the given speed.
+ *------------------------------------------------------------------------*/
+uint8_t
+uhub_count_active_host_ports(struct usb_device *udev, enum usb_dev_speed speed)
+{
+ struct uhub_softc *sc;
+ struct usb_device *child;
+ struct usb_hub *hub;
+ struct usb_port *up;
+ uint8_t retval = 0;
+ uint8_t x;
+
+ if (udev == NULL)
+ goto done;
+ hub = udev->hub;
+ if (hub == NULL)
+ goto done;
+ sc = hub->hubsoftc;
+ if (sc == NULL)
+ goto done;
+
+ for (x = 0; x != hub->nports; x++) {
+ up = hub->ports + x;
+ child = usb_bus_port_get_device(udev->bus, up);
+ if (child != NULL &&
+ child->flags.usb_mode == USB_MODE_HOST &&
+ child->speed == speed)
+ retval++;
+ }
+done:
+ return (retval);
+}
+
+void
+uhub_explore_handle_re_enumerate(struct usb_device *child)
+{
+ uint8_t do_unlock;
+ usb_error_t err;
+
+ /* check if device should be re-enumerated */
+ if (child->flags.usb_mode != USB_MODE_HOST)
+ return;
+
+ do_unlock = usbd_enum_lock(child);
+ switch (child->re_enumerate_wait) {
+ case USB_RE_ENUM_START:
+ err = usbd_set_config_index(child,
+ USB_UNCONFIG_INDEX);
+ if (err != 0) {
+ DPRINTF("Unconfigure failed: %s: Ignored.\n",
+ usbd_errstr(err));
+ }
+ if (child->parent_hub == NULL) {
+ /* the root HUB cannot be re-enumerated */
+ DPRINTFN(6, "cannot reset root HUB\n");
+ err = 0;
+ } else {
+ err = usbd_req_re_enumerate(child, NULL);
+ }
+ if (err == 0)
+ err = usbd_set_config_index(child, 0);
+ if (err == 0) {
+ err = usb_probe_and_attach(child,
+ USB_IFACE_INDEX_ANY);
+ }
+ child->re_enumerate_wait = USB_RE_ENUM_DONE;
+ break;
+
+ case USB_RE_ENUM_PWR_OFF:
+ /* get the device unconfigured */
+ err = usbd_set_config_index(child,
+ USB_UNCONFIG_INDEX);
+ if (err) {
+ DPRINTFN(0, "Could not unconfigure "
+ "device (ignored)\n");
+ }
+ if (child->parent_hub == NULL) {
+ /* the root HUB cannot be re-enumerated */
+ DPRINTFN(6, "cannot set port feature\n");
+ err = 0;
+ } else {
+ /* clear port enable */
+ err = usbd_req_clear_port_feature(child->parent_hub,
+ NULL, child->port_no, UHF_PORT_ENABLE);
+ if (err) {
+ DPRINTFN(0, "Could not disable port "
+ "(ignored)\n");
+ }
+ }
+ child->re_enumerate_wait = USB_RE_ENUM_DONE;
+ break;
+
+ case USB_RE_ENUM_SET_CONFIG:
+ err = usbd_set_config_index(child,
+ child->next_config_index);
+ if (err != 0) {
+ DPRINTF("Configure failed: %s: Ignored.\n",
+ usbd_errstr(err));
+ } else {
+ err = usb_probe_and_attach(child,
+ USB_IFACE_INDEX_ANY);
+ }
+ child->re_enumerate_wait = USB_RE_ENUM_DONE;
+ break;
+
+ default:
+ child->re_enumerate_wait = USB_RE_ENUM_DONE;
+ break;
+ }
+ if (do_unlock)
+ usbd_enum_unlock(child);
+}
+
+/*------------------------------------------------------------------------*
* uhub_explore_sub - subroutine
*
* Return values:
@@ -239,33 +531,7 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up)
goto done;
}
- /* check if device should be re-enumerated */
-
- if (child->flags.usb_mode == USB_MODE_HOST) {
- uint8_t do_unlock;
-
- do_unlock = usbd_enum_lock(child);
- if (child->re_enumerate_wait) {
- err = usbd_set_config_index(child,
- USB_UNCONFIG_INDEX);
- if (err != 0) {
- DPRINTF("Unconfigure failed: "
- "%s: Ignored.\n",
- usbd_errstr(err));
- }
- err = usbd_req_re_enumerate(child, NULL);
- if (err == 0)
- err = usbd_set_config_index(child, 0);
- if (err == 0) {
- err = usb_probe_and_attach(child,
- USB_IFACE_INDEX_ANY);
- }
- child->re_enumerate_wait = 0;
- err = 0;
- }
- if (do_unlock)
- usbd_enum_unlock(child);
- }
+ uhub_explore_handle_re_enumerate(child);
/* check if probe and attach should be done */
@@ -522,7 +788,10 @@ repeat:
*
* NOTE: This part is currently FreeBSD specific.
*/
- if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)
+ if (udev->parent_hub != NULL) {
+ /* inherit mode from the parent HUB */
+ mode = udev->parent_hub->flags.usb_mode;
+ } else if (sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)
mode = USB_MODE_DEVICE;
else
mode = USB_MODE_HOST;
@@ -1079,7 +1348,12 @@ uhub_attach(device_t dev)
hub->explore = &uhub_explore;
hub->nports = nports;
hub->hubudev = udev;
-
+#if USB_HAVE_TT_SUPPORT
+ hub->tt_msg[0].hdr.pm_callback = &uhub_reset_tt_proc;
+ hub->tt_msg[0].udev = udev;
+ hub->tt_msg[1].hdr.pm_callback = &uhub_reset_tt_proc;
+ hub->tt_msg[1].udev = udev;
+#endif
/* if self powered hub, give ports maximum current */
if (udev->flags.self_powered) {
hub->portpower = USB_MAX_POWER;
@@ -1181,11 +1455,9 @@ uhub_attach(device_t dev)
/* Start the interrupt endpoint, if any */
- if (sc->sc_xfer[0] != NULL) {
- mtx_lock(&sc->sc_mtx);
- usbd_transfer_start(sc->sc_xfer[0]);
- mtx_unlock(&sc->sc_mtx);
- }
+ mtx_lock(&sc->sc_mtx);
+ usbd_transfer_start(sc->sc_xfer[UHUB_INTR_TRANSFER]);
+ mtx_unlock(&sc->sc_mtx);
/* Enable automatic power save on all USB HUBs */
@@ -1215,6 +1487,7 @@ uhub_detach(device_t dev)
{
struct uhub_softc *sc = device_get_softc(dev);
struct usb_hub *hub = sc->sc_udev->hub;
+ struct usb_bus *bus = sc->sc_udev->bus;
struct usb_device *child;
uint8_t x;
@@ -1227,7 +1500,7 @@ uhub_detach(device_t dev)
/* Detach all ports */
for (x = 0; x != hub->nports; x++) {
- child = usb_bus_port_get_device(sc->sc_udev->bus, hub->ports + x);
+ child = usb_bus_port_get_device(bus, hub->ports + x);
if (child == NULL) {
continue;
@@ -1239,6 +1512,13 @@ uhub_detach(device_t dev)
usb_free_device(child, 0);
}
+#if USB_HAVE_TT_SUPPORT
+ /* Make sure our TT messages are not queued anywhere */
+ USB_BUS_LOCK(bus);
+ usb_proc_mwait(&bus->non_giant_callback_proc,
+ &hub->tt_msg[0], &hub->tt_msg[1]);
+ USB_BUS_UNLOCK(bus);
+#endif
free(hub, M_USBDEV);
sc->sc_udev->hub = NULL;
@@ -2070,9 +2350,10 @@ usbd_transfer_power_ref(struct usb_xfer *xfer, int val)
static uint8_t
usb_peer_should_wakeup(struct usb_device *udev)
{
- return ((udev->power_mode == USB_POWER_MODE_ON) ||
+ return (((udev->power_mode == USB_POWER_MODE_ON) &&
+ (udev->flags.usb_mode == USB_MODE_HOST)) ||
(udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
- (udev->re_enumerate_wait != 0) ||
+ (udev->re_enumerate_wait != USB_RE_ENUM_DONE) ||
(udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
(udev->pwr_save.write_refs != 0) ||
((udev->pwr_save.read_refs != 0) &&
@@ -2488,6 +2769,8 @@ usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode)
#if USB_HAVE_POWERD
usb_bus_power_update(udev->bus);
+#else
+ usb_needs_explore(udev->bus, 0 /* no probe */ );
#endif
}
@@ -2526,8 +2809,36 @@ usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode)
void
usbd_start_re_enumerate(struct usb_device *udev)
{
- if (udev->re_enumerate_wait == 0) {
- udev->re_enumerate_wait = 1;
+ if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+ udev->re_enumerate_wait = USB_RE_ENUM_START;
usb_needs_explore(udev->bus, 0);
}
}
+
+/*-----------------------------------------------------------------------*
+ * usbd_start_set_config
+ *
+ * This function starts setting a USB configuration. This function
+ * does not need to be called BUS-locked. This function does not wait
+ * until the set USB configuratino is completed.
+ *------------------------------------------------------------------------*/
+usb_error_t
+usbd_start_set_config(struct usb_device *udev, uint8_t index)
+{
+ if (udev->re_enumerate_wait == USB_RE_ENUM_DONE) {
+ if (udev->curr_config_index == index) {
+ /* no change needed */
+ return (0);
+ }
+ udev->next_config_index = index;
+ udev->re_enumerate_wait = USB_RE_ENUM_SET_CONFIG;
+ usb_needs_explore(udev->bus, 0);
+ return (0);
+ } else if (udev->re_enumerate_wait == USB_RE_ENUM_SET_CONFIG) {
+ if (udev->next_config_index == index) {
+ /* no change needed */
+ return (0);
+ }
+ }
+ return (USB_ERR_PENDING_REQUESTS);
+}
diff --git a/freebsd/sys/dev/usb/usb_hub.h b/freebsd/sys/dev/usb/usb_hub.h
index 23a1fa4f..557a0565 100644
--- a/freebsd/sys/dev/usb/usb_hub.h
+++ b/freebsd/sys/dev/usb/usb_hub.h
@@ -35,6 +35,9 @@ struct usb_port {
#define USB_RESTART_MAX 5
uint8_t device_index; /* zero means not valid */
enum usb_hc_mode usb_mode; /* host or device mode */
+#if USB_HAVE_TT_SUPPORT
+ struct usb_device_request req_reset_tt __aligned(4);
+#endif
};
/*
@@ -44,6 +47,9 @@ struct usb_hub {
struct usb_device *hubudev; /* the HUB device */
usb_error_t (*explore) (struct usb_device *hub);
void *hubsoftc;
+#if USB_HAVE_TT_SUPPORT
+ struct usb_udev_msg tt_msg[2];
+#endif
usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX];
uint16_t portpower; /* mA per USB port */
uint8_t isoc_last_time;
@@ -65,5 +71,6 @@ void usb_bus_power_update(struct usb_bus *bus);
void usb_bus_powerd(struct usb_bus *bus);
void uhub_root_intr(struct usb_bus *, const uint8_t *, uint8_t);
usb_error_t uhub_query_info(struct usb_device *, uint8_t *, uint8_t *);
+void uhub_explore_handle_re_enumerate(struct usb_device *);
#endif /* _USB_HUB_H_ */
diff --git a/freebsd/sys/dev/usb/usb_ioctl.h b/freebsd/sys/dev/usb/usb_ioctl.h
index 9e66bd5a..d5be169b 100644
--- a/freebsd/sys/dev/usb/usb_ioctl.h
+++ b/freebsd/sys/dev/usb/usb_ioctl.h
@@ -30,6 +30,7 @@
#define _USB_IOCTL_H_
#include <sys/ioccom.h>
+#include <sys/cdefs.h>
/* Building "kdump" depends on these includes */
@@ -41,6 +42,16 @@
#define USB_GENERIC_NAME "ugen"
#define USB_TEMPLATE_SYSCTL "hw.usb.template" /* integer type */
+/*
+ * Align IOCTL structures to hide differences when running 32-bit
+ * programs under 64-bit kernels:
+ */
+#ifdef COMPAT_32BIT
+#define USB_IOCTL_STRUCT_ALIGN(n) __aligned(n)
+#else
+#define USB_IOCTL_STRUCT_ALIGN(n)
+#endif
+
/* Definition of valid template sysctl values */
enum {
@@ -62,7 +73,7 @@ struct usb_read_dir {
#endif
uint32_t urd_startentry;
uint32_t urd_maxlen;
-};
+} USB_IOCTL_STRUCT_ALIGN(8);
struct usb_ctl_request {
#ifdef COMPAT_32BIT
@@ -74,12 +85,12 @@ struct usb_ctl_request {
uint16_t ucr_actlen; /* actual length transferred */
uint8_t ucr_addr; /* zero - currently not used */
struct usb_device_request ucr_request;
-};
+} USB_IOCTL_STRUCT_ALIGN(8);
struct usb_alt_interface {
uint8_t uai_interface_index;
uint8_t uai_alt_index;
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_gen_descriptor {
#ifdef COMPAT_32BIT
@@ -98,7 +109,7 @@ struct usb_gen_descriptor {
uint8_t ugd_endpt_index;
uint8_t ugd_report_type;
uint8_t reserved[8];
-};
+} USB_IOCTL_STRUCT_ALIGN(8);
struct usb_device_info {
uint16_t udi_productNo;
@@ -127,24 +138,33 @@ struct usb_device_info {
char udi_vendor[128];
char udi_serial[64];
char udi_release[8];
-};
+} USB_IOCTL_STRUCT_ALIGN(2);
+
+#define USB_DEVICE_PORT_PATH_MAX 32
+
+struct usb_device_port_path {
+ uint8_t udp_bus; /* which bus we are on */
+ uint8_t udp_index; /* which device index */
+ uint8_t udp_port_level; /* how many levels: 0, 1, 2 ... */
+ uint8_t udp_port_no[USB_DEVICE_PORT_PATH_MAX];
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_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 */
-};
+} USB_IOCTL_STRUCT_ALIGN(4);
struct usb_fs_start {
uint8_t ep_index;
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_fs_stop {
uint8_t ep_index;
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_fs_complete {
uint8_t ep_index;
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
/* This structure is used for all endpoint types */
struct usb_fs_endpoint {
@@ -177,7 +197,7 @@ struct usb_fs_endpoint {
/* timeout value for no timeout */
#define USB_FS_TIMEOUT_NONE 0
int status; /* see USB_ERR_XXX */
-};
+} USB_IOCTL_STRUCT_ALIGN(8);
struct usb_fs_init {
/* userland pointer to endpoints structure */
@@ -188,11 +208,11 @@ struct usb_fs_init {
#endif
/* maximum number of endpoints */
uint8_t ep_index_max;
-};
+} USB_IOCTL_STRUCT_ALIGN(8);
struct usb_fs_uninit {
uint8_t dummy; /* zero */
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_fs_open {
#define USB_FS_MAX_BUFSIZE (1 << 18)
@@ -204,15 +224,20 @@ struct usb_fs_open {
uint8_t dev_index; /* currently unused */
uint8_t ep_index;
uint8_t ep_no; /* bEndpointNumber */
-};
+} USB_IOCTL_STRUCT_ALIGN(4);
+
+struct usb_fs_open_stream {
+ struct usb_fs_open fs_open;
+ uint16_t stream_id; /* stream ID */
+} USB_IOCTL_STRUCT_ALIGN(4);
struct usb_fs_close {
uint8_t ep_index;
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_fs_clear_stall_sync {
uint8_t ep_index;
-};
+} USB_IOCTL_STRUCT_ALIGN(1);
struct usb_gen_quirk {
uint16_t index; /* Quirk Index */
@@ -222,11 +247,11 @@ struct usb_gen_quirk {
uint16_t bcdDeviceHigh; /* High Device Revision */
uint16_t reserved[2];
/*
- * String version of quirk including terminating zero. See UQ_XXX in
- * "usb_quirk.h".
+ * String version of quirk including terminating zero. See
+ * UQ_XXX in "usb_quirk.h".
*/
char quirkname[64 - 14];
-};
+} USB_IOCTL_STRUCT_ALIGN(2);
/* USB controller */
#define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request)
@@ -270,7 +295,8 @@ struct usb_gen_quirk {
#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 usb_read_dir)
-/* 128 - 134 unused */
+/* 128 - 133 unused */
+#define USB_GET_DEV_PORT_PATH _IOR ('U', 134, struct usb_device_port_path)
#define USB_GET_POWER_USAGE _IOR ('U', 135, int)
#define USB_SET_TX_FORCE_SHORT _IOW ('U', 136, int)
#define USB_SET_TX_TIMEOUT _IOW ('U', 137, int)
diff --git a/freebsd/sys/dev/usb/usb_msctest.c b/freebsd/sys/dev/usb/usb_msctest.c
index fcb9f026..4d28346c 100644
--- a/freebsd/sys/dev/usb/usb_msctest.c
+++ b/freebsd/sys/dev/usb/usb_msctest.c
@@ -85,7 +85,7 @@ enum {
DIR_NONE,
};
-#define SCSI_MAX_LEN 0x100
+#define SCSI_MAX_LEN MAX(0x100, BULK_SIZE)
#define SCSI_INQ_LEN 0x24
#define SCSI_SENSE_LEN 0xFF
@@ -141,8 +141,8 @@ struct bbb_csw {
struct bbb_transfer {
struct mtx mtx;
struct cv cv;
- struct bbb_cbw cbw;
- struct bbb_csw csw;
+ struct bbb_cbw *cbw;
+ struct bbb_csw *csw;
struct usb_xfer *xfer[ST_MAX];
@@ -152,6 +152,7 @@ struct bbb_transfer {
usb_size_t data_rem; /* bytes */
usb_timeout_t data_timeout; /* ms */
usb_frlength_t actlen; /* bytes */
+ usb_frlength_t buffer_size; /* bytes */
uint8_t cmd_len; /* bytes */
uint8_t dir;
@@ -160,7 +161,7 @@ struct bbb_transfer {
uint8_t status_try;
int error;
- uint8_t buffer[SCSI_MAX_LEN] __aligned(4);
+ uint8_t *buffer;
};
static usb_callback_t bbb_command_callback;
@@ -186,7 +187,6 @@ static const struct usb_config bbb_config[ST_MAX] = {
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_OUT,
.bufsize = sizeof(struct bbb_cbw),
- .flags = {.ext_buffer = 1,},
.callback = &bbb_command_callback,
.timeout = 4 * USB_MS_HZ, /* 4 seconds */
},
@@ -195,8 +195,8 @@ static const struct usb_config bbb_config[ST_MAX] = {
.type = UE_BULK,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
- .bufsize = BULK_SIZE,
- .flags = {.ext_buffer = 1,.proxy_buffer = 1,.short_xfer_ok = 1,},
+ .bufsize = SCSI_MAX_LEN,
+ .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,},
.callback = &bbb_data_read_callback,
.timeout = 4 * USB_MS_HZ, /* 4 seconds */
},
@@ -214,7 +214,7 @@ static const struct usb_config bbb_config[ST_MAX] = {
.type = UE_BULK,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_OUT,
- .bufsize = BULK_SIZE,
+ .bufsize = SCSI_MAX_LEN,
.flags = {.ext_buffer = 1,.proxy_buffer = 1,},
.callback = &bbb_data_write_callback,
.timeout = 4 * USB_MS_HZ, /* 4 seconds */
@@ -234,7 +234,7 @@ static const struct usb_config bbb_config[ST_MAX] = {
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
.bufsize = sizeof(struct bbb_csw),
- .flags = {.ext_buffer = 1,.short_xfer_ok = 1,},
+ .flags = {.short_xfer_ok = 1,},
.callback = &bbb_status_callback,
.timeout = 1 * USB_MS_HZ, /* 1 second */
},
@@ -243,7 +243,6 @@ static const struct usb_config bbb_config[ST_MAX] = {
static void
bbb_done(struct bbb_transfer *sc, int error)
{
-
sc->error = error;
sc->state = ST_COMMAND;
sc->status_try = 1;
@@ -292,18 +291,19 @@ bbb_command_callback(struct usb_xfer *xfer, usb_error_t error)
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, (uint32_t)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);
+ tag = UGETDW(sc->cbw->dCBWTag) + 1;
+ USETDW(sc->cbw->dCBWSignature, CBWSIGNATURE);
+ USETDW(sc->cbw->dCBWTag, tag);
+ USETDW(sc->cbw->dCBWDataTransferLength, (uint32_t)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");
}
- usbd_xfer_set_frame_data(xfer, 0, &sc->cbw, sizeof(sc->cbw));
+ usbd_xfer_set_frame_len(xfer, 0,
+ sizeof(struct bbb_cbw));
usbd_transfer_submit(xfer);
break;
@@ -390,7 +390,7 @@ bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
if (sc->data_rem == 0) {
bbb_transfer_start(sc, ST_STATUS);
- return;
+ break;
}
if (max_bulk > sc->data_rem) {
max_bulk = sc->data_rem;
@@ -398,7 +398,7 @@ bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_timeout(xfer, sc->data_timeout);
usbd_xfer_set_frame_data(xfer, 0, sc->data_ptr, max_bulk);
usbd_transfer_submit(xfer);
- return;
+ break;
default: /* Error */
if (error == USB_ERR_CANCELLED) {
@@ -406,8 +406,7 @@ bbb_data_write_callback(struct usb_xfer *xfer, usb_error_t error)
} else {
bbb_transfer_start(sc, ST_DATA_WR_CS);
}
- return;
-
+ break;
}
}
@@ -432,9 +431,9 @@ bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
/* very simple status check */
- if (actlen < (int)sizeof(sc->csw)) {
+ if (actlen < (int)sizeof(struct bbb_csw)) {
bbb_done(sc, USB_ERR_SHORT_XFER);
- } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) {
+ } else if (sc->csw->bCSWStatus == CSWSTATUS_GOOD) {
bbb_done(sc, 0); /* success */
} else {
bbb_done(sc, ERR_CSW_FAILED); /* error */
@@ -442,7 +441,8 @@ bbb_status_callback(struct usb_xfer *xfer, usb_error_t error)
break;
case USB_ST_SETUP:
- usbd_xfer_set_frame_data(xfer, 0, &sc->csw, sizeof(sc->csw));
+ usbd_xfer_set_frame_len(xfer, 0,
+ sizeof(struct bbb_csw));
usbd_transfer_submit(xfer);
break;
@@ -480,9 +480,9 @@ bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun,
sc->data_timeout = (data_timeout + USB_MS_HZ);
sc->actlen = 0;
sc->cmd_len = cmd_len;
- memset(&sc->cbw.CBWCDB, 0, sizeof(sc->cbw.CBWCDB));
- memcpy(&sc->cbw.CBWCDB, cmd_ptr, cmd_len);
- DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, (char *)sc->cbw.CBWCDB, ":");
+ memset(&sc->cbw->CBWCDB, 0, sizeof(sc->cbw->CBWCDB));
+ memcpy(&sc->cbw->CBWCDB, cmd_ptr, cmd_len);
+ DPRINTFN(1, "SCSI cmd = %*D\n", (int)cmd_len, (char *)sc->cbw->CBWCDB, ":");
mtx_lock(&sc->mtx);
usbd_transfer_start(sc->xfer[sc->state]);
@@ -551,6 +551,16 @@ bbb_attach(struct usb_device *udev, uint8_t iface_index)
bbb_detach(sc);
return (NULL);
}
+ /* store pointer to DMA buffers */
+ sc->buffer = usbd_xfer_get_frame_buffer(
+ sc->xfer[ST_DATA_RD], 0);
+ sc->buffer_size =
+ usbd_xfer_max_len(sc->xfer[ST_DATA_RD]);
+ sc->cbw = usbd_xfer_get_frame_buffer(
+ sc->xfer[ST_COMMAND], 0);
+ sc->csw = usbd_xfer_get_frame_buffer(
+ sc->xfer[ST_STATUS], 0);
+
return (sc);
}
@@ -826,8 +836,8 @@ usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method)
* TCTMobile needs DIR_IN flag. To get it, we
* supply a dummy data with the command.
*/
- err = bbb_command_start(sc, DIR_IN, 0, &sc->buffer,
- sizeof(sc->buffer), &scsi_tct_eject,
+ err = bbb_command_start(sc, DIR_IN, 0, sc->buffer,
+ sc->buffer_size, &scsi_tct_eject,
sizeof(scsi_tct_eject), USB_MS_HZ);
break;
default:
diff --git a/freebsd/sys/dev/usb/usb_process.c b/freebsd/sys/dev/usb/usb_process.c
index 59b26567..d36df36e 100644
--- a/freebsd/sys/dev/usb/usb_process.c
+++ b/freebsd/sys/dev/usb/usb_process.c
@@ -503,3 +503,15 @@ usb_proc_rewakeup(struct usb_process *up)
cv_signal(&up->up_cv);
}
}
+
+/*------------------------------------------------------------------------*
+ * usb_proc_is_called_from
+ *
+ * This function will return non-zero if called from inside the USB
+ * process passed as first argument. Else this function returns zero.
+ *------------------------------------------------------------------------*/
+int
+usb_proc_is_called_from(struct usb_process *up)
+{
+ return (up->up_curtd == curthread);
+}
diff --git a/freebsd/sys/dev/usb/usb_process.h b/freebsd/sys/dev/usb/usb_process.h
index 9b1a8534..06feee8b 100644
--- a/freebsd/sys/dev/usb/usb_process.h
+++ b/freebsd/sys/dev/usb/usb_process.h
@@ -79,6 +79,7 @@ void usb_proc_mwait(struct usb_process *up, void *pm0, void *pm1);
void usb_proc_free(struct usb_process *up);
void *usb_proc_msignal(struct usb_process *up, void *pm0, void *pm1);
void usb_proc_rewakeup(struct usb_process *up);
+int usb_proc_is_called_from(struct usb_process *up);
void usb_proc_explore_mwait(struct usb_device *, void *, void *);
void *usb_proc_explore_msignal(struct usb_device *, void *, void *);
diff --git a/freebsd/sys/dev/usb/usb_request.c b/freebsd/sys/dev/usb/usb_request.c
index 167a7228..e9137bd2 100644
--- a/freebsd/sys/dev/usb/usb_request.c
+++ b/freebsd/sys/dev/usb/usb_request.c
@@ -50,7 +50,6 @@
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
-#include <dev/usb/usb_ioctl.h>
#include <dev/usb/usbhid.h>
#define USB_DEBUG_VAR usb_debug
@@ -74,6 +73,11 @@ static int usb_no_cs_fail;
SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RW,
&usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set");
+static int usb_full_ddesc;
+
+SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RW,
+ &usb_full_ddesc, 0, "USB always read complete device descriptor, if set");
+
#ifdef USB_DEBUG
#ifdef USB_REQ_DEBUG
/* The following structures are used in connection to fault injection. */
@@ -712,6 +716,17 @@ done:
if ((mtx != NULL) && (mtx != &Giant))
mtx_lock(mtx);
+ switch (err) {
+ case USB_ERR_NORMAL_COMPLETION:
+ case USB_ERR_SHORT_XFER:
+ case USB_ERR_STALLED:
+ case USB_ERR_CANCELLED:
+ break;
+ default:
+ DPRINTF("I/O error - waiting a bit for TT cleanup\n");
+ usb_pause_mtx(mtx, hz / 16);
+ break;
+ }
return ((usb_error_t)err);
}
@@ -999,7 +1014,7 @@ usbd_req_get_desc(struct usb_device *udev,
USETW(req.wLength, min_len);
err = usbd_do_request_flags(udev, mtx, &req,
- desc, 0, NULL, 1000);
+ desc, 0, NULL, 500 /* ms */);
if (err) {
if (!retries) {
@@ -1884,32 +1899,41 @@ usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx)
*/
switch (udev->speed) {
case USB_SPEED_FULL:
- case USB_SPEED_LOW:
+ if (usb_full_ddesc != 0) {
+ /* get full device descriptor */
+ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
+ if (err == 0)
+ break;
+ }
+
+ /* get partial device descriptor, some devices crash on this */
err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc,
USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
- if (err != 0) {
- DPRINTFN(0, "getting device descriptor "
- "at addr %d failed, %s\n", udev->address,
- usbd_errstr(err));
- return (err);
- }
+ if (err != 0)
+ break;
+
+ /* get the full device descriptor */
+ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
break;
+
default:
DPRINTF("Minimum MaxPacketSize is large enough "
- "to hold the complete device descriptor\n");
- break;
- }
-
- /* get the full device descriptor */
- err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
+ "to hold the complete device descriptor or "
+ "only once MaxPacketSize choice\n");
- /* try one more time, if error */
- if (err)
+ /* get the full device descriptor */
err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
- if (err) {
- DPRINTF("addr=%d, getting full desc failed\n",
- udev->address);
+ /* try one more time, if error */
+ if (err != 0)
+ err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
+ break;
+ }
+
+ if (err != 0) {
+ DPRINTFN(0, "getting device descriptor "
+ "at addr %d failed, %s\n", udev->address,
+ usbd_errstr(err));
return (err);
}
@@ -1954,6 +1978,7 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
return (USB_ERR_INVAL);
}
retry:
+#if USB_HAVE_TT_SUPPORT
/*
* Try to reset the High Speed parent HUB of a LOW- or FULL-
* speed device, if any.
@@ -1961,15 +1986,24 @@ retry:
if (udev->parent_hs_hub != NULL &&
udev->speed != USB_SPEED_HIGH) {
DPRINTF("Trying to reset parent High Speed TT.\n");
- err = usbd_req_reset_tt(udev->parent_hs_hub, NULL,
- udev->hs_port_no);
+ if (udev->parent_hs_hub == parent_hub &&
+ (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) +
+ uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) {
+ /* we can reset the whole TT */
+ err = usbd_req_reset_tt(parent_hub, NULL,
+ udev->hs_port_no);
+ } else {
+ /* only reset a particular device and endpoint */
+ err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL,
+ udev->hs_port_no, old_addr, UE_CONTROL, 0);
+ }
if (err) {
DPRINTF("Resetting parent High "
"Speed TT failed (%s).\n",
usbd_errstr(err));
}
}
-
+#endif
/* Try to warm reset first */
if (parent_hub->speed == USB_SPEED_SUPER)
usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no);
diff --git a/freebsd/sys/dev/usb/usb_transfer.c b/freebsd/sys/dev/usb/usb_transfer.c
index b2528186..205a72f7 100644
--- a/freebsd/sys/dev/usb/usb_transfer.c
+++ b/freebsd/sys/dev/usb/usb_transfer.c
@@ -334,6 +334,7 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
usb_frcount_t n_frlengths;
usb_frcount_t n_frbuffers;
usb_frcount_t x;
+ uint16_t maxp_old;
uint8_t type;
uint8_t zmps;
@@ -419,6 +420,11 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
if (xfer->max_packet_count > parm->hc_max_packet_count) {
xfer->max_packet_count = parm->hc_max_packet_count;
}
+
+ /* store max packet size value before filtering */
+
+ maxp_old = xfer->max_packet_size;
+
/* filter "wMaxPacketSize" according to HC capabilities */
if ((xfer->max_packet_size > parm->hc_max_packet_size) ||
@@ -451,6 +457,13 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm)
}
}
+ /*
+ * Check if the max packet size was outside its allowed range
+ * and clamped to a valid value:
+ */
+ if (maxp_old != xfer->max_packet_size)
+ xfer->flags_int.maxp_was_clamped = 1;
+
/* compute "max_frame_size" */
usbd_update_max_frame_size(xfer);
@@ -2396,7 +2409,9 @@ usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer)
void
usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error)
{
- USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
+ struct usb_xfer_root *info = xfer->xroot;
+
+ USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED);
DPRINTF("err=%s\n", usbd_errstr(error));
@@ -2410,10 +2425,10 @@ usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error)
xfer->flags_int.control_act = 0;
return;
}
- /* only set transfer error if not already set */
- if (!xfer->error) {
+ /* only set transfer error, if not already set */
+ if (xfer->error == USB_ERR_NORMAL_COMPLETION)
xfer->error = error;
- }
+
/* stop any callouts */
usb_callout_stop(&xfer->timeout_handle);
@@ -2425,14 +2440,14 @@ usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error)
usbd_transfer_dequeue(xfer);
#if USB_HAVE_BUSDMA
- if (mtx_owned(xfer->xroot->xfer_mtx)) {
+ if (mtx_owned(info->xfer_mtx)) {
struct usb_xfer_queue *pq;
/*
* 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;
+ pq = &info->dma_q;
if (pq->curr == xfer) {
/* start the next BUS-DMA load, if any */
@@ -2442,10 +2457,10 @@ usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error)
#endif
/* keep some statistics */
if (xfer->error) {
- xfer->xroot->bus->stats_err.uds_requests
+ info->bus->stats_err.uds_requests
[xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++;
} else {
- xfer->xroot->bus->stats_ok.uds_requests
+ info->bus->stats_ok.uds_requests
[xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++;
}
@@ -2685,7 +2700,7 @@ usbd_transfer_timeout_ms(struct usb_xfer *xfer,
/* defer delay */
usb_callout_reset(&xfer->timeout_handle,
- USB_MS_TO_TICKS(ms), cb, xfer);
+ USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer);
}
/*------------------------------------------------------------------------*
@@ -2811,6 +2826,22 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer)
/* end of control transfer, if any */
xfer->flags_int.control_act = 0;
+#if USB_HAVE_TT_SUPPORT
+ switch (xfer->error) {
+ case USB_ERR_NORMAL_COMPLETION:
+ case USB_ERR_SHORT_XFER:
+ case USB_ERR_STALLED:
+ case USB_ERR_CANCELLED:
+ /* nothing to do */
+ break;
+ default:
+ /* try to reset the TT, if any */
+ USB_BUS_LOCK(bus);
+ uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint);
+ USB_BUS_UNLOCK(bus);
+ break;
+ }
+#endif
/* check if we should block the execution queue */
if ((xfer->error != USB_ERR_CANCELLED) &&
(xfer->flags.pipe_bof)) {
@@ -3377,3 +3408,13 @@ usbd_xfer_get_timestamp(struct usb_xfer *xfer)
{
return (xfer->isoc_time_complete);
}
+
+/*
+ * The following function returns non-zero if the max packet size
+ * field was clamped to a valid value. Else it returns zero.
+ */
+uint8_t
+usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer)
+{
+ return (xfer->flags_int.maxp_was_clamped);
+}
diff --git a/freebsd/sys/dev/usb/usbdi.h b/freebsd/sys/dev/usb/usbdi.h
index 245777f0..6ec26a4a 100644
--- a/freebsd/sys/dev/usb/usbdi.h
+++ b/freebsd/sys/dev/usb/usbdi.h
@@ -558,6 +558,7 @@ int usbd_xfer_is_stalled(struct usb_xfer *xfer);
void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag);
void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag);
uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer);
+uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer);
void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset,
const void *ptr, usb_frlength_t len);
@@ -574,6 +575,8 @@ void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset,
void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset,
usb_frlength_t len);
void usbd_start_re_enumerate(struct usb_device *udev);
+usb_error_t
+ usbd_start_set_config(struct usb_device *, uint8_t);
int usb_fifo_attach(struct usb_device *udev, void *priv_sc,
struct mtx *priv_mtx, struct usb_fifo_methods *pm,