diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2015-02-02 14:27:13 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2015-02-13 10:34:19 +0100 |
commit | 7eeb079d84bc4abe9897be0047fc28a754e46ecd (patch) | |
tree | 9b47ac7055ce0cb1e2d86c684a2a7a1cd20c0c4d /freebsd/sys/dev/usb | |
parent | freebsd-to-rtems.py: Fix revert includes (diff) | |
download | rtems-libbsd-7eeb079d84bc4abe9897be0047fc28a754e46ecd.tar.bz2 |
Update to FreeBSD 9.3
Diffstat (limited to 'freebsd/sys/dev/usb')
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, |