diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-04 11:33:00 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-11-04 15:28:21 +0100 |
commit | af5333e0a02b2295304d4e029b15ee15a4fe2b3a (patch) | |
tree | c5c43680d374f58b487eeeaf18fb7ec6b84ba074 /freebsd/sys/dev/usb | |
parent | BUS_SPACE(9): Use simple memory model for ARM (diff) | |
download | rtems-libbsd-af5333e0a02b2295304d4e029b15ee15a4fe2b3a.tar.bz2 |
Update to FreeBSD 8.4
Diffstat (limited to 'freebsd/sys/dev/usb')
44 files changed, 2514 insertions, 1177 deletions
diff --git a/freebsd/sys/dev/usb/controller/ehci.c b/freebsd/sys/dev/usb/controller/ehci.c index 8a98f608..d126e9a9 100644 --- a/freebsd/sys/dev/usb/controller/ehci.c +++ b/freebsd/sys/dev/usb/controller/ehci.c @@ -56,7 +56,6 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -98,20 +97,20 @@ static int ehciiaadbug = 0; static int ehcilostintrbug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ehci, CTLFLAG_RW, 0, "USB ehci"); -SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW, +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &ehcidebug, 0, "Debug level"); -SYSCTL_INT(_hw_usb_ehci, OID_AUTO, no_hs, CTLFLAG_RW, - &ehcinohighspeed, 0, "Disable High Speed USB"); -SYSCTL_INT(_hw_usb_ehci, OID_AUTO, iaadbug, CTLFLAG_RW, - &ehciiaadbug, 0, "Enable doorbell bug workaround"); -SYSCTL_INT(_hw_usb_ehci, OID_AUTO, lostintrbug, CTLFLAG_RW, - &ehcilostintrbug, 0, "Enable lost interrupt bug workaround"); - TUNABLE_INT("hw.usb.ehci.debug", &ehcidebug); +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, no_hs, CTLFLAG_RW | CTLFLAG_TUN, + &ehcinohighspeed, 0, "Disable High Speed USB"); TUNABLE_INT("hw.usb.ehci.no_hs", &ehcinohighspeed); +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, iaadbug, CTLFLAG_RW | CTLFLAG_TUN, + &ehciiaadbug, 0, "Enable doorbell bug workaround"); TUNABLE_INT("hw.usb.ehci.iaadbug", &ehciiaadbug); +SYSCTL_INT(_hw_usb_ehci, OID_AUTO, lostintrbug, CTLFLAG_RW | CTLFLAG_TUN, + &ehcilostintrbug, 0, "Enable lost interrupt bug workaround"); TUNABLE_INT("hw.usb.ehci.lostintrbug", &ehcilostintrbug); + static void ehci_dump_regs(ehci_softc_t *sc); static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh); @@ -191,7 +190,7 @@ ehci_reset(ehci_softc_t *sc) EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET); for (i = 0; i < 100; i++) { - usb_pause_mtx(NULL, hz / 1000); + usb_pause_mtx(NULL, hz / 128); hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET; if (!hcr) { if (sc->sc_flags & (EHCI_SCFLG_SETMODE | EHCI_SCFLG_BIGEMMIO)) { @@ -215,7 +214,7 @@ ehci_reset(ehci_softc_t *sc) return (0); } } - device_printf(sc->sc_bus.bdev, "reset timeout\n"); + device_printf(sc->sc_bus.bdev, "Reset timeout\n"); return (USB_ERR_IOERROR); } @@ -227,7 +226,7 @@ ehci_hcreset(ehci_softc_t *sc) EOWRITE4(sc, EHCI_USBCMD, 0); /* Halt controller */ for (i = 0; i < 100; i++) { - usb_pause_mtx(NULL, hz / 1000); + usb_pause_mtx(NULL, hz / 128); hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; if (hcr) break; @@ -240,7 +239,60 @@ ehci_hcreset(ehci_softc_t *sc) */ device_printf(sc->sc_bus.bdev, "stop timeout\n"); - return ehci_reset(sc); + return (ehci_reset(sc)); +} + +static int +ehci_init_sub(struct ehci_softc *sc) +{ + struct usb_page_search buf_res; + uint32_t cparams; + uint32_t hcr; + uint8_t i; + + cparams = EREAD4(sc, EHCI_HCCPARAMS); + + DPRINTF("cparams=0x%x\n", cparams); + + if (EHCI_HCC_64BIT(cparams)) { + DPRINTF("HCC uses 64-bit structures\n"); + + /* MUST clear segment register if 64 bit capable */ + EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); + } + + usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); + EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); + + usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); + EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); + + /* enable interrupts */ + EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); + + /* turn on controller */ + EOWRITE4(sc, EHCI_USBCMD, + EHCI_CMD_ITC_1 | /* 1 microframes interrupt delay */ + (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | + EHCI_CMD_ASE | + EHCI_CMD_PSE | + EHCI_CMD_RS); + + /* Take over port ownership */ + EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); + + for (i = 0; i < 100; i++) { + usb_pause_mtx(NULL, hz / 128); + hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; + if (!hcr) { + break; + } + } + if (hcr) { + device_printf(sc->sc_bus.bdev, "Run timeout\n"); + return (USB_ERR_IOERROR); + } + return (USB_ERR_NORMAL_COMPLETION); } usb_error_t @@ -249,8 +301,6 @@ ehci_init(ehci_softc_t *sc) struct usb_page_search buf_res; uint32_t version; uint32_t sparams; - uint32_t cparams; - uint32_t hcr; uint16_t i; uint16_t x; uint16_t y; @@ -262,6 +312,8 @@ ehci_init(ehci_softc_t *sc) usb_callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0); usb_callout_init_mtx(&sc->sc_tmo_poll, &sc->sc_bus.bus_mtx, 0); + sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); + #ifdef USB_DEBUG if (ehciiaadbug) sc->sc_flags |= EHCI_SCFLG_IAADBUG; @@ -272,8 +324,6 @@ ehci_init(ehci_softc_t *sc) } #endif - sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); - version = EHCI_HCIVERSION(EREAD4(sc, EHCI_CAPLEN_HCIVERSION)); device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n", version >> 8, version & 0xff); @@ -282,15 +332,6 @@ ehci_init(ehci_softc_t *sc) DPRINTF("sparams=0x%x\n", sparams); sc->sc_noport = EHCI_HCS_N_PORTS(sparams); - cparams = EREAD4(sc, EHCI_HCCPARAMS); - DPRINTF("cparams=0x%x\n", cparams); - - if (EHCI_HCC_64BIT(cparams)) { - DPRINTF("HCC uses 64-bit structures\n"); - - /* MUST clear segment register if 64 bit capable */ - EWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); - } sc->sc_bus.usbrev = USB_REV_2_0; /* Reset the controller */ @@ -467,9 +508,6 @@ ehci_init(ehci_softc_t *sc) [i & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1)]->itd_self; } } - /* setup sync list pointer */ - EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); - usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); if (1) { @@ -514,35 +552,8 @@ ehci_init(ehci_softc_t *sc) } #endif - /* setup async list pointer */ - EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); - - - /* enable interrupts */ - EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); - - /* turn on controller */ - EOWRITE4(sc, EHCI_USBCMD, - EHCI_CMD_ITC_1 | /* 1 microframes interrupt delay */ - (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) | - EHCI_CMD_ASE | - EHCI_CMD_PSE | - EHCI_CMD_RS); - - /* Take over port ownership */ - EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF); - - for (i = 0; i < 100; i++) { - usb_pause_mtx(NULL, hz / 1000); - hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; - if (!hcr) { - break; - } - } - if (hcr) { - device_printf(sc->sc_bus.bdev, "run timeout\n"); - return (USB_ERR_IOERROR); - } + /* finial setup */ + err = ehci_init_sub(sc); if (!err) { /* catch any lost interrupts */ @@ -576,137 +587,28 @@ ehci_detach(ehci_softc_t *sc) usb_callout_drain(&sc->sc_tmo_poll); } -void +static void ehci_suspend(ehci_softc_t *sc) { - uint32_t cmd; - uint32_t hcr; - uint8_t i; - - USB_BUS_LOCK(&sc->sc_bus); - - for (i = 1; i <= sc->sc_noport; i++) { - cmd = EOREAD4(sc, EHCI_PORTSC(i)); - if (((cmd & EHCI_PS_PO) == 0) && - ((cmd & EHCI_PS_PE) == EHCI_PS_PE)) { - EOWRITE4(sc, EHCI_PORTSC(i), - cmd | EHCI_PS_SUSP); - } - } - - sc->sc_cmd = EOREAD4(sc, EHCI_USBCMD); - - cmd = sc->sc_cmd & ~(EHCI_CMD_ASE | EHCI_CMD_PSE); - EOWRITE4(sc, EHCI_USBCMD, cmd); - - for (i = 0; i < 100; i++) { - hcr = EOREAD4(sc, EHCI_USBSTS) & - (EHCI_STS_ASS | EHCI_STS_PSS); - - if (hcr == 0) { - break; - } - usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); - } - - if (hcr != 0) { - device_printf(sc->sc_bus.bdev, "reset timeout\n"); - } - cmd &= ~EHCI_CMD_RS; - EOWRITE4(sc, EHCI_USBCMD, cmd); - - for (i = 0; i < 100; i++) { - hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; - if (hcr == EHCI_STS_HCH) { - break; - } - usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); - } + DPRINTF("stopping the HC\n"); - if (hcr != EHCI_STS_HCH) { - device_printf(sc->sc_bus.bdev, - "config timeout\n"); - } - USB_BUS_UNLOCK(&sc->sc_bus); + /* reset HC */ + ehci_hcreset(sc); } -void +static void ehci_resume(ehci_softc_t *sc) { - struct usb_page_search buf_res; - uint32_t cmd; - uint32_t hcr; - uint8_t i; - - USB_BUS_LOCK(&sc->sc_bus); - - /* restore things in case the bios doesn't */ - EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0); - - usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res); - EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr); - - usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res); - EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH); - - EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs); - - hcr = 0; - for (i = 1; i <= sc->sc_noport; i++) { - cmd = EOREAD4(sc, EHCI_PORTSC(i)); - if (((cmd & EHCI_PS_PO) == 0) && - ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) { - EOWRITE4(sc, EHCI_PORTSC(i), - cmd | EHCI_PS_FPR); - hcr = 1; - } - } - - if (hcr) { - usb_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_WAIT)); - - for (i = 1; i <= sc->sc_noport; i++) { - cmd = EOREAD4(sc, EHCI_PORTSC(i)); - if (((cmd & EHCI_PS_PO) == 0) && - ((cmd & EHCI_PS_SUSP) == EHCI_PS_SUSP)) { - EOWRITE4(sc, EHCI_PORTSC(i), - cmd & ~EHCI_PS_FPR); - } - } - } - EOWRITE4(sc, EHCI_USBCMD, sc->sc_cmd); - - for (i = 0; i < 100; i++) { - hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH; - if (hcr != EHCI_STS_HCH) { - break; - } - usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 1000); - } - if (hcr == EHCI_STS_HCH) { - device_printf(sc->sc_bus.bdev, "config timeout\n"); - } - - USB_BUS_UNLOCK(&sc->sc_bus); + /* reset HC */ + ehci_hcreset(sc); - usb_pause_mtx(NULL, - USB_MS_TO_TICKS(USB_RESUME_WAIT)); + /* setup HC */ + ehci_init_sub(sc); /* catch any lost interrupts */ ehci_do_poll(&sc->sc_bus); } -void -ehci_shutdown(ehci_softc_t *sc) -{ - DPRINTF("stopping the HC\n"); - - if (ehci_hcreset(sc)) { - DPRINTF("reset failed!\n"); - } -} - #ifdef USB_DEBUG static void ehci_dump_regs(ehci_softc_t *sc) @@ -1183,6 +1085,28 @@ _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) return (last); } +static void +ehci_data_toggle_update(struct usb_xfer *xfer, uint16_t actlen, uint16_t xlen) +{ + uint16_t rem; + uint8_t dt; + + /* count number of full packets */ + dt = (actlen / xfer->max_packet_size) & 1; + + /* compute remainder */ + rem = actlen % xfer->max_packet_size; + + if (rem > 0) + dt ^= 1; /* short packet at the end */ + else if (actlen != xlen) + dt ^= 1; /* zero length packet at the end */ + else if (xlen == 0) + dt ^= 1; /* zero length transfer */ + + xfer->endpoint->toggle_next ^= dt; +} + static usb_error_t ehci_non_isoc_done_sub(struct usb_xfer *xfer) { @@ -1216,7 +1140,10 @@ ehci_non_isoc_done_sub(struct usb_xfer *xfer) status |= EHCI_QTD_HALTED; } else if (xfer->aframes != xfer->nframes) { xfer->frlengths[xfer->aframes] += td->len - len; + /* manually update data toggle */ + ehci_data_toggle_update(xfer, td->len - len, td->len); } + /* Check for last transfer */ if (((void *)td) == xfer->td_transfer_last) { td = NULL; @@ -1298,9 +1225,6 @@ ehci_non_isoc_done(struct usb_xfer *xfer) status = hc32toh(sc, qh->qh_qtd.qtd_status); - xfer->endpoint->toggle_next = - (status & EHCI_QTD_TOGGLE_MASK) ? 1 : 0; - /* reset scanner */ xfer->td_transfer_cache = xfer->td_transfer_first; @@ -1879,6 +1803,8 @@ ehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last) if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { + xfer->endpoint->toggle_next = 0; + temp.qtd_status &= htohc32(temp.sc, EHCI_QTD_SET_CERR(3)); temp.qtd_status |= htohc32(temp.sc, @@ -2474,9 +2400,9 @@ ehci_device_isoc_fs_open(struct usb_xfer *xfer) EHCI_SITD_SET_HUBA(xfer->xroot->udev->hs_hub_addr) | EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no); - if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) { + if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) sitd_portaddr |= EHCI_SITD_SET_DIR_IN; - } + sitd_portaddr = htohc32(sc, sitd_portaddr); /* initialize all TD's */ @@ -2512,9 +2438,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) { struct usb_page_search buf_res; ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus); - struct usb_fs_isoc_schedule *fss_start; - struct usb_fs_isoc_schedule *fss_end; - struct usb_fs_isoc_schedule *fss; ehci_sitd_t *td; ehci_sitd_t *td_last = NULL; ehci_sitd_t **pp_last; @@ -2526,7 +2449,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) uint16_t tlen; uint8_t sa; uint8_t sb; - uint8_t error; #ifdef USB_DEBUG uint8_t once = 1; @@ -2571,9 +2493,8 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) * pre-compute when the isochronous transfer will be finished: */ xfer->isoc_time_complete = - usbd_fs_isoc_schedule_isoc_time_expand - (xfer->xroot->udev, &fss_start, &fss_end, nframes) + buf_offset + - xfer->nframes; + usb_isoc_time_expand(&sc->sc_bus, nframes) + + buf_offset + xfer->nframes; /* get the real number of frames */ @@ -2596,19 +2517,14 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) xfer->qh_pos = xfer->endpoint->isoc_next; - fss = fss_start + (xfer->qh_pos % USB_ISOC_TIME_MAX); - while (nframes--) { if (td == NULL) { panic("%s:%d: out of TD's\n", __FUNCTION__, __LINE__); } - if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) { + if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) pp_last = &sc->sc_isoc_fs_p_last[0]; - } - if (fss >= fss_end) { - fss = fss_start; - } + /* reuse sitd_portaddr and sitd_back from last transfer */ if (*plen > xfer->max_frame_size) { @@ -2623,17 +2539,19 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) #endif *plen = xfer->max_frame_size; } - /* - * We currently don't care if the ISOCHRONOUS schedule is - * full! - */ - error = usbd_fs_isoc_schedule_alloc(fss, &sa, *plen); - if (error) { + + /* allocate a slot */ + + sa = usbd_fs_isoc_schedule_alloc_slot(xfer, + xfer->isoc_time_complete - nframes - 1); + + if (sa == 255) { /* - * The FULL speed schedule is FULL! Set length - * to zero. + * Schedule is FULL, set length to zero: */ + *plen = 0; + sa = USB_FS_ISOC_UFRAME_MAX - 1; } if (*plen) { /* @@ -2713,7 +2631,6 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) pp_last++; plen++; - fss++; td_last = td; td = td->obj_next; } @@ -2723,11 +2640,29 @@ ehci_device_isoc_fs_enter(struct usb_xfer *xfer) /* update isoc_next */ xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1); + + /* + * We don't allow cancelling of the SPLIT transaction USB FULL + * speed transfer, because it disturbs the bandwidth + * computation algorithm. + */ + xfer->flags_int.can_cancel_immed = 0; } static void ehci_device_isoc_fs_start(struct usb_xfer *xfer) { + /* + * We don't allow cancelling of the SPLIT transaction USB FULL + * speed transfer, because it disturbs the bandwidth + * computation algorithm. + */ + xfer->flags_int.can_cancel_immed = 0; + + /* set a default timeout */ + if (xfer->timeout == 0) + xfer->timeout = 500; /* ms */ + /* put transfer on interrupt queue */ ehci_transfer_intr_enqueue(xfer); } @@ -3087,13 +3022,8 @@ static const struct ehci_config_desc ehci_confd = { static const struct usb_hub_descriptor ehci_hubd = { - 0, /* dynamic length */ - UDESC_HUB, - 0, - {0, 0}, - 0, - 0, - {0}, + .bDescLength = 0, /* dynamic length */ + .bDescriptorType = UDESC_HUB, }; static void @@ -3348,7 +3278,7 @@ ehci_roothub_exec(struct usb_device *udev, break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; - bzero(sc->sc_hub_desc.temp, 16); + memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "get port status i=%d\n", @@ -3441,7 +3371,7 @@ ehci_roothub_exec(struct usb_device *udev, /* Wait for reset to complete. */ usb_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY)); + USB_MS_TO_TICKS(usb_port_root_reset_delay)); /* Terminate reset sequence. */ if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM)) @@ -3887,8 +3817,24 @@ ehci_device_suspend(struct usb_device *udev) } USB_BUS_UNLOCK(udev->bus); +} - return; +static void +ehci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) +{ + struct ehci_softc *sc = EHCI_BUS2SC(bus); + + switch (state) { + case USB_HW_POWER_SUSPEND: + case USB_HW_POWER_SHUTDOWN: + ehci_suspend(sc); + break; + case USB_HW_POWER_RESUME: + ehci_resume(sc); + break; + default: + break; + } } static void @@ -3934,6 +3880,7 @@ struct usb_bus_methods ehci_bus_methods = .device_resume = ehci_device_resume, .device_suspend = ehci_device_suspend, .set_hw_power = ehci_set_hw_power, + .set_hw_power_sleep = ehci_set_hw_power_sleep, .roothub_exec = ehci_roothub_exec, .xfer_poll = ehci_do_poll, }; diff --git a/freebsd/sys/dev/usb/controller/ehci.h b/freebsd/sys/dev/usb/controller/ehci.h index 19d32426..a64d48a0 100644 --- a/freebsd/sys/dev/usb/controller/ehci.h +++ b/freebsd/sys/dev/usb/controller/ehci.h @@ -14,13 +14,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -340,8 +333,6 @@ typedef struct ehci_softc { uint32_t sc_terminate_self; /* TD short packet termination pointer */ uint32_t sc_eintrs; - uint32_t sc_cmd; /* shadow of cmd register during - * suspend */ uint16_t sc_intr_stat[EHCI_VIRTUAL_FRAMELIST_COUNT]; uint16_t sc_id_vendor; /* vendor ID for root hub */ @@ -478,9 +469,6 @@ usb_bus_mem_cb_t ehci_iterate_hw_softc; usb_error_t ehci_reset(ehci_softc_t *sc); usb_error_t ehci_init(ehci_softc_t *sc); void ehci_detach(struct ehci_softc *sc); -void ehci_suspend(struct ehci_softc *sc); -void ehci_resume(struct ehci_softc *sc); -void ehci_shutdown(ehci_softc_t *sc); void ehci_interrupt(ehci_softc_t *sc); #endif /* _EHCI_H_ */ diff --git a/freebsd/sys/dev/usb/controller/ohci.c b/freebsd/sys/dev/usb/controller/ohci.c index 6390156a..19ecf296 100644 --- a/freebsd/sys/dev/usb/controller/ohci.c +++ b/freebsd/sys/dev/usb/controller/ohci.c @@ -45,7 +45,6 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -84,9 +83,8 @@ __FBSDID("$FreeBSD$"); static int ohcidebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ohci, CTLFLAG_RW, 0, "USB ohci"); -SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW, +SYSCTL_INT(_hw_usb_ohci, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &ohcidebug, 0, "ohci debug level"); - TUNABLE_INT("hw.usb.ohci.debug", &ohcidebug); static void ohci_dumpregs(ohci_softc_t *); @@ -169,7 +167,7 @@ ohci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb) } static usb_error_t -ohci_controller_init(ohci_softc_t *sc) +ohci_controller_init(ohci_softc_t *sc, int do_suspend) { struct usb_page_search buf_res; uint32_t i; @@ -236,6 +234,11 @@ reset: } #endif + if (do_suspend) { + OWRITE4(sc, OHCI_CONTROL, OHCI_HCFS_SUSPEND); + return (USB_ERR_NORMAL_COMPLETION); + } + /* The controller is now in SUSPEND state, we have 2ms to finish. */ /* set up HC registers */ @@ -418,13 +421,12 @@ ohci_init(ohci_softc_t *sc) sc->sc_bus.usbrev = USB_REV_1_0; - if (ohci_controller_init(sc)) { + if (ohci_controller_init(sc, 0) != 0) return (USB_ERR_INVAL); - } else { - /* catch any lost interrupts */ - ohci_do_poll(&sc->sc_bus); - return (USB_ERR_NORMAL_COMPLETION); - } + + /* catch any lost interrupts */ + ohci_do_poll(&sc->sc_bus); + return (USB_ERR_NORMAL_COMPLETION); } /* @@ -448,75 +450,32 @@ ohci_detach(struct ohci_softc *sc) usb_callout_drain(&sc->sc_tmo_rhsc); } -/* NOTE: suspend/resume is called from - * interrupt context and cannot sleep! - */ -void +static void ohci_suspend(ohci_softc_t *sc) { - uint32_t ctl; - - USB_BUS_LOCK(&sc->sc_bus); + DPRINTF("\n"); #ifdef USB_DEBUG - DPRINTF("\n"); - if (ohcidebug > 2) { + if (ohcidebug > 2) ohci_dumpregs(sc); - } #endif - ctl = OREAD4(sc, OHCI_CONTROL) & ~OHCI_HCFS_MASK; - if (sc->sc_control == 0) { - /* - * Preserve register values, in case that APM BIOS - * does not recover them. - */ - sc->sc_control = ctl; - sc->sc_intre = OREAD4(sc, OHCI_INTERRUPT_ENABLE); - } - ctl |= OHCI_HCFS_SUSPEND; - OWRITE4(sc, OHCI_CONTROL, ctl); - - usb_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_WAIT)); - - USB_BUS_UNLOCK(&sc->sc_bus); + /* reset HC and leave it suspended */ + ohci_controller_init(sc, 1); } -void +static void ohci_resume(ohci_softc_t *sc) { - uint32_t ctl; + DPRINTF("\n"); #ifdef USB_DEBUG - DPRINTF("\n"); - if (ohcidebug > 2) { + if (ohcidebug > 2) ohci_dumpregs(sc); - } #endif - /* some broken BIOSes never initialize the Controller chip */ - ohci_controller_init(sc); - - USB_BUS_LOCK(&sc->sc_bus); - if (sc->sc_intre) { - OWRITE4(sc, OHCI_INTERRUPT_ENABLE, - sc->sc_intre & (OHCI_ALL_INTRS | OHCI_MIE)); - } - if (sc->sc_control) - ctl = sc->sc_control; - else - ctl = OREAD4(sc, OHCI_CONTROL); - ctl |= OHCI_HCFS_RESUME; - OWRITE4(sc, OHCI_CONTROL, ctl); - usb_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_DELAY)); - ctl = (ctl & ~OHCI_HCFS_MASK) | OHCI_HCFS_OPERATIONAL; - OWRITE4(sc, OHCI_CONTROL, ctl); - usb_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_RESUME_RECOVERY)); - sc->sc_control = sc->sc_intre = 0; - USB_BUS_UNLOCK(&sc->sc_bus); + /* some broken BIOSes never initialize the Controller chip */ + ohci_controller_init(sc, 0); /* catch any lost interrupts */ ohci_do_poll(&sc->sc_bus); @@ -2122,13 +2081,8 @@ struct ohci_config_desc ohci_confd = static const struct usb_hub_descriptor ohci_hubd = { - 0, /* dynamic length */ - UDESC_HUB, - 0, - {0, 0}, - 0, - 0, - {0}, + .bDescLength = 0, /* dynamic length */ + .bDescriptorType = UDESC_HUB, }; static usb_error_t @@ -2350,7 +2304,7 @@ ohci_roothub_exec(struct usb_device *udev, case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): len = 16; - bzero(sc->sc_hub_desc.temp, 16); + memset(sc->sc_hub_desc.temp, 0, 16); break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(9, "get port status i=%d\n", @@ -2391,7 +2345,7 @@ ohci_roothub_exec(struct usb_device *udev, for (v = 0;; v++) { if (v < 12) { usb_pause_mtx(&sc->sc_bus.bus_mtx, - USB_MS_TO_TICKS(USB_PORT_ROOT_RESET_DELAY)); + USB_MS_TO_TICKS(usb_port_root_reset_delay)); if ((OREAD4(sc, port) & UPS_RESET) == 0) { break; @@ -2716,6 +2670,24 @@ ohci_device_suspend(struct usb_device *udev) } static void +ohci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state) +{ + struct ohci_softc *sc = OHCI_BUS2SC(bus); + + switch (state) { + case USB_HW_POWER_SUSPEND: + case USB_HW_POWER_SHUTDOWN: + ohci_suspend(sc); + break; + case USB_HW_POWER_RESUME: + ohci_resume(sc); + break; + default: + break; + } +} + +static void ohci_set_hw_power(struct usb_bus *bus) { struct ohci_softc *sc = OHCI_BUS2SC(bus); @@ -2759,6 +2731,7 @@ struct usb_bus_methods ohci_bus_methods = .device_resume = ohci_device_resume, .device_suspend = ohci_device_suspend, .set_hw_power = ohci_set_hw_power, + .set_hw_power_sleep = ohci_set_hw_power_sleep, .roothub_exec = ohci_roothub_exec, .xfer_poll = ohci_do_poll, }; diff --git a/freebsd/sys/dev/usb/controller/ohci.h b/freebsd/sys/dev/usb/controller/ohci.h index c878af07..fad1a9fe 100644 --- a/freebsd/sys/dev/usb/controller/ohci.h +++ b/freebsd/sys/dev/usb/controller/ohci.h @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -250,8 +243,6 @@ typedef struct ohci_softc { bus_space_handle_t sc_io_hdl; uint32_t sc_eintrs; /* enabled interrupts */ - uint32_t sc_control; /* Preserved during suspend/standby */ - uint32_t sc_intre; uint16_t sc_intr_stat[OHCI_NO_EDS]; uint16_t sc_id_vendor; @@ -269,8 +260,6 @@ usb_bus_mem_cb_t ohci_iterate_hw_softc; usb_error_t ohci_init(ohci_softc_t *sc); void ohci_detach(struct ohci_softc *sc); -void ohci_suspend(ohci_softc_t *sc); -void ohci_resume(ohci_softc_t *sc); void ohci_interrupt(ohci_softc_t *sc); #endif /* _OHCI_H_ */ diff --git a/freebsd/sys/dev/usb/controller/ohcireg.h b/freebsd/sys/dev/usb/controller/ohcireg.h index 9127a02a..7f14875c 100644 --- a/freebsd/sys/dev/usb/controller/ohcireg.h +++ b/freebsd/sys/dev/usb/controller/ohcireg.h @@ -15,13 +15,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED diff --git a/freebsd/sys/dev/usb/controller/usb_controller.c b/freebsd/sys/dev/usb/controller/usb_controller.c index f4220daf..ce3263dd 100644 --- a/freebsd/sys/dev/usb/controller/usb_controller.c +++ b/freebsd/sys/dev/usb/controller/usb_controller.c @@ -36,7 +36,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -63,12 +62,17 @@ #include <dev/usb/usb_controller.h> #include <dev/usb/usb_bus.h> +#include <dev/usb/usb_pf.h> +#include <rtems/bsd/local/usb_if.h> /* function prototypes */ static device_probe_t usb_probe; static device_attach_t usb_attach; static device_detach_t usb_detach; +static device_suspend_t usb_suspend; +static device_resume_t usb_resume; +static device_shutdown_t usb_shutdown; static void usb_attach_sub(device_t, struct usb_bus *); @@ -85,8 +89,18 @@ SYSCTL_INT(_hw_usb_ctrl, OID_AUTO, debug, CTLFLAG_RW, &usb_ctrl_debug, 0, #ifndef __rtems__ static int usb_no_boot_wait = 0; TUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait); -SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, - "No device enumerate waiting at boot."); +SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RD|CTLFLAG_TUN, &usb_no_boot_wait, 0, + "No USB device enumerate waiting at boot."); + +static int usb_no_suspend_wait = 0; +TUNABLE_INT("hw.usb.no_suspend_wait", &usb_no_suspend_wait); +SYSCTL_INT(_hw_usb, OID_AUTO, no_suspend_wait, CTLFLAG_RW|CTLFLAG_TUN, + &usb_no_suspend_wait, 0, "No USB device waiting at system suspend."); + +static int usb_no_shutdown_wait = 0; +TUNABLE_INT("hw.usb.no_shutdown_wait", &usb_no_shutdown_wait); +SYSCTL_INT(_hw_usb, OID_AUTO, no_shutdown_wait, CTLFLAG_RW|CTLFLAG_TUN, + &usb_no_shutdown_wait, 0, "No USB device waiting at system shutdown."); #endif /* __rtems__ */ static devclass_t usb_devclass; @@ -95,9 +109,9 @@ static device_method_t usb_methods[] = { DEVMETHOD(device_probe, usb_probe), DEVMETHOD(device_attach, usb_attach), DEVMETHOD(device_detach, usb_detach), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), - DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, usb_suspend), + DEVMETHOD(device_resume, usb_resume), + DEVMETHOD(device_shutdown, usb_shutdown), {0, 0} }; @@ -190,12 +204,12 @@ usb_detach(device_t dev) usb_root_mount_rel(bus); USB_BUS_LOCK(bus); - if (usb_proc_msignal(&bus->explore_proc, - &bus->detach_msg[0], &bus->detach_msg[1])) { - /* ignore */ - } - /* Wait for detach to complete */ + /* Queue detach job */ + usb_proc_msignal(&bus->explore_proc, + &bus->detach_msg[0], &bus->detach_msg[1]); + + /* Wait for detach to complete */ usb_proc_mwait(&bus->explore_proc, &bus->detach_msg[0], &bus->detach_msg[1]); @@ -214,6 +228,96 @@ usb_detach(device_t dev) usb_proc_free(&bus->control_xfer_proc); +#if USB_HAVE_PF + usbpf_detach(bus); +#endif + return (0); +} + +/*------------------------------------------------------------------------* + * usb_suspend + *------------------------------------------------------------------------*/ +static int +usb_suspend(device_t dev) +{ + struct usb_bus *bus = device_get_softc(dev); + + DPRINTF("\n"); + + if (bus == NULL) { + /* was never setup properly */ + return (0); + } + + USB_BUS_LOCK(bus); + usb_proc_msignal(&bus->explore_proc, + &bus->suspend_msg[0], &bus->suspend_msg[1]); +#ifndef __rtems__ + if (usb_no_suspend_wait == 0) { + /* wait for suspend callback to be executed */ + usb_proc_mwait(&bus->explore_proc, + &bus->suspend_msg[0], &bus->suspend_msg[1]); + } +#endif /* __rtems__ */ + USB_BUS_UNLOCK(bus); + + return (0); +} + +/*------------------------------------------------------------------------* + * usb_resume + *------------------------------------------------------------------------*/ +static int +usb_resume(device_t dev) +{ + struct usb_bus *bus = device_get_softc(dev); + + DPRINTF("\n"); + + if (bus == NULL) { + /* was never setup properly */ + return (0); + } + + USB_BUS_LOCK(bus); + usb_proc_msignal(&bus->explore_proc, + &bus->resume_msg[0], &bus->resume_msg[1]); + USB_BUS_UNLOCK(bus); + + return (0); +} + +/*------------------------------------------------------------------------* + * usb_shutdown + *------------------------------------------------------------------------*/ +static int +usb_shutdown(device_t dev) +{ + struct usb_bus *bus = device_get_softc(dev); + + DPRINTF("\n"); + + if (bus == NULL) { + /* was never setup properly */ + return (0); + } + + device_printf(bus->bdev, "Controller shutdown\n"); + + USB_BUS_LOCK(bus); +#ifndef __rtems__ + usb_proc_msignal(&bus->explore_proc, + &bus->shutdown_msg[0], &bus->shutdown_msg[1]); + if (usb_no_shutdown_wait == 0) { + /* wait for shutdown callback to be executed */ + usb_proc_mwait(&bus->explore_proc, + &bus->shutdown_msg[0], &bus->shutdown_msg[1]); + } +#endif /* __rtems__ */ + USB_BUS_UNLOCK(bus); + + device_printf(bus->bdev, "Controller shutdown complete\n"); + return (0); } @@ -231,6 +335,9 @@ usb_bus_explore(struct usb_proc_msg *pm) bus = ((struct usb_bus_msg *)pm)->bus; udev = bus->devices[USB_ROOT_HUB_ADDR]; + if (bus->no_explore != 0) + return; + if (udev && udev->hub) { if (bus->do_probe) { @@ -301,6 +408,168 @@ usb_bus_detach(struct usb_proc_msg *pm) bus->bdev = NULL; } +/*------------------------------------------------------------------------* + * usb_bus_suspend + * + * This function is used to suspend the USB contoller. + *------------------------------------------------------------------------*/ +static void +usb_bus_suspend(struct usb_proc_msg *pm) +{ + struct usb_bus *bus; + struct usb_device *udev; + usb_error_t err; + uint8_t do_unlock; + + bus = ((struct usb_bus_msg *)pm)->bus; + udev = bus->devices[USB_ROOT_HUB_ADDR]; + + if (udev == NULL || bus->bdev == NULL) + return; + + USB_BUS_UNLOCK(bus); + + /* + * We use the shutdown event here because the suspend and + * resume events are reserved for the USB port suspend and + * resume. The USB system suspend is implemented like full + * shutdown and all connected USB devices will be disconnected + * subsequently. At resume all USB devices will be + * re-connected again. + */ + + bus_generic_shutdown(bus->bdev); + + do_unlock = usbd_enum_lock(udev); + + err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); + if (err) + device_printf(bus->bdev, "Could not unconfigure root HUB\n"); + + USB_BUS_LOCK(bus); + bus->hw_power_state = 0; + bus->no_explore = 1; + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power != NULL) + (bus->methods->set_hw_power) (bus); + + if (bus->methods->set_hw_power_sleep != NULL) + (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SUSPEND); + + if (do_unlock) + usbd_enum_unlock(udev); + + USB_BUS_LOCK(bus); +} + +/*------------------------------------------------------------------------* + * usb_bus_resume + * + * This function is used to resume the USB contoller. + *------------------------------------------------------------------------*/ +static void +usb_bus_resume(struct usb_proc_msg *pm) +{ + struct usb_bus *bus; + struct usb_device *udev; + usb_error_t err; + uint8_t do_unlock; + + bus = ((struct usb_bus_msg *)pm)->bus; + udev = bus->devices[USB_ROOT_HUB_ADDR]; + + if (udev == NULL || bus->bdev == NULL) + return; + + USB_BUS_UNLOCK(bus); + + do_unlock = usbd_enum_lock(udev); +#if 0 + DEVMETHOD(usb_take_controller, NULL); /* dummy */ +#endif + USB_TAKE_CONTROLLER(device_get_parent(bus->bdev)); + + USB_BUS_LOCK(bus); + bus->hw_power_state = + USB_HW_POWER_CONTROL | + USB_HW_POWER_BULK | + USB_HW_POWER_INTERRUPT | + USB_HW_POWER_ISOC | + USB_HW_POWER_NON_ROOT_HUB; + bus->no_explore = 0; + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power_sleep != NULL) + (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_RESUME); + + if (bus->methods->set_hw_power != NULL) + (bus->methods->set_hw_power) (bus); + + /* restore USB configuration to index 0 */ + err = usbd_set_config_index(udev, 0); + if (err) + device_printf(bus->bdev, "Could not configure root HUB\n"); + + /* probe and attach */ + err = usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY); + if (err) { + device_printf(bus->bdev, "Could not probe and " + "attach root HUB\n"); + } + + if (do_unlock) + usbd_enum_unlock(udev); + + USB_BUS_LOCK(bus); +} + +/*------------------------------------------------------------------------* + * usb_bus_shutdown + * + * This function is used to shutdown the USB contoller. + *------------------------------------------------------------------------*/ +static void +usb_bus_shutdown(struct usb_proc_msg *pm) +{ + struct usb_bus *bus; + struct usb_device *udev; + usb_error_t err; + uint8_t do_unlock; + + bus = ((struct usb_bus_msg *)pm)->bus; + udev = bus->devices[USB_ROOT_HUB_ADDR]; + + if (udev == NULL || bus->bdev == NULL) + return; + + USB_BUS_UNLOCK(bus); + + bus_generic_shutdown(bus->bdev); + + do_unlock = usbd_enum_lock(udev); + + err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX); + if (err) + device_printf(bus->bdev, "Could not unconfigure root HUB\n"); + + USB_BUS_LOCK(bus); + bus->hw_power_state = 0; + bus->no_explore = 1; + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power != NULL) + (bus->methods->set_hw_power) (bus); + + if (bus->methods->set_hw_power_sleep != NULL) + (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SHUTDOWN); + + if (do_unlock) + usbd_enum_unlock(udev); + + USB_BUS_LOCK(bus); +} + static void usb_power_wdog(void *arg) { @@ -370,7 +639,7 @@ usb_bus_attach(struct usb_proc_msg *pm) case USB_REV_3_0: speed = USB_SPEED_SUPER; - device_printf(bus->bdev, "4.8Gbps Super Speed USB v3.0\n"); + device_printf(bus->bdev, "5.0Gbps Super Speed USB v3.0\n"); break; default: @@ -379,8 +648,6 @@ usb_bus_attach(struct usb_proc_msg *pm) return; } - USB_BUS_UNLOCK(bus); - /* default power_mask value */ bus->hw_power_state = USB_HW_POWER_CONTROL | @@ -389,13 +656,15 @@ usb_bus_attach(struct usb_proc_msg *pm) USB_HW_POWER_ISOC | USB_HW_POWER_NON_ROOT_HUB; + USB_BUS_UNLOCK(bus); + /* make sure power is set at least once */ if (bus->methods->set_hw_power != NULL) { (bus->methods->set_hw_power) (bus); } - /* Allocate the Root USB device */ + /* allocate the Root USB device */ child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, speed, USB_MODE_HOST); @@ -442,6 +711,9 @@ usb_attach_sub(device_t dev, struct usb_bus *bus) usb_devclass_ptr = devclass_find("usbus"); mtx_unlock(&Giant); +#if USB_HAVE_PF + usbpf_attach(bus); +#endif /* Initialise USB process messages */ bus->explore_msg[0].hdr.pm_callback = &usb_bus_explore; bus->explore_msg[0].bus = bus; @@ -458,31 +730,44 @@ usb_attach_sub(device_t dev, struct usb_bus *bus) bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach; bus->attach_msg[1].bus = bus; + bus->suspend_msg[0].hdr.pm_callback = &usb_bus_suspend; + bus->suspend_msg[0].bus = bus; + bus->suspend_msg[1].hdr.pm_callback = &usb_bus_suspend; + bus->suspend_msg[1].bus = bus; + + bus->resume_msg[0].hdr.pm_callback = &usb_bus_resume; + bus->resume_msg[0].bus = bus; + bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume; + bus->resume_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; + bus->shutdown_msg[1].bus = bus; + /* Create USB explore and callback processes */ if (usb_proc_create(&bus->giant_callback_proc, &bus->bus_mtx, pname, USB_PRI_MED)) { - printf("WARNING: Creation of USB Giant " + device_printf(dev, "WARNING: Creation of USB Giant " "callback process failed.\n"); } else if (usb_proc_create(&bus->non_giant_callback_proc, &bus->bus_mtx, pname, USB_PRI_HIGH)) { - printf("WARNING: Creation of USB non-Giant " + device_printf(dev, "WARNING: Creation of USB non-Giant " "callback process failed.\n"); } else if (usb_proc_create(&bus->explore_proc, &bus->bus_mtx, pname, USB_PRI_MED)) { - printf("WARNING: Creation of USB explore " + device_printf(dev, "WARNING: Creation of USB explore " "process failed.\n"); } else if (usb_proc_create(&bus->control_xfer_proc, &bus->bus_mtx, pname, USB_PRI_MED)) { - printf("WARNING: Creation of USB control transfer " + device_printf(dev, "WARNING: Creation of USB control transfer " "process failed.\n"); } else { /* Get final attach going */ USB_BUS_LOCK(bus); - if (usb_proc_msignal(&bus->explore_proc, - &bus->attach_msg[0], &bus->attach_msg[1])) { - /* ignore */ - } + usb_proc_msignal(&bus->explore_proc, + &bus->attach_msg[0], &bus->attach_msg[1]); USB_BUS_UNLOCK(bus); /* Do initial explore */ diff --git a/freebsd/sys/dev/usb/quirk/usb_quirk.c b/freebsd/sys/dev/usb/quirk/usb_quirk.c index 6f50452b..6ae14acf 100644 --- a/freebsd/sys/dev/usb/quirk/usb_quirk.c +++ b/freebsd/sys/dev/usb/quirk/usb_quirk.c @@ -36,7 +36,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -97,11 +96,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK(SILICONPORTALS, YAPPHONE, 0x100, 0x100, UQ_AU_INP_ASYNC), USB_QUIRK(LOGITECH, UN53B, 0x0000, 0xffff, UQ_NO_STRINGS), USB_QUIRK(ELSA, MODEM1, 0x0000, 0xffff, UQ_CFG_INDEX_1), - - /* - * XXX The following quirks should have a more specific revision - * number: - */ + /* Quirks for printer devices */ USB_QUIRK(HP, 895C, 0x0000, 0xffff, UQ_BROKEN_BIDIR), USB_QUIRK(HP, 880C, 0x0000, 0xffff, UQ_BROKEN_BIDIR), USB_QUIRK(HP, 815C, 0x0000, 0xffff, UQ_BROKEN_BIDIR), @@ -130,6 +125,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK(METAGEEK2, WISPYDBX, 0x0000, 0xffff, UQ_KBD_IGNORE, UQ_HID_IGNORE), USB_QUIRK(TENX, UAUDIO0, 0x0101, 0x0101, UQ_AUDIO_SWAP_LR), /* MS keyboards do weird things */ + USB_QUIRK(MICROSOFT, NATURAL4000, 0x0000, 0xFFFF, UQ_KBD_BOOTPROTO), USB_QUIRK(MICROSOFT, WLINTELLIMOUSE, 0x0000, 0xffff, UQ_MS_LEADING_BYTE), /* umodem(4) device quirks */ USB_QUIRK(METRICOM, RICOCHET_GS, 0x100, 0x100, UQ_ASSUME_CM_OVER_DATA), @@ -139,7 +135,9 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK(SIEMENS2, ES75, 0x000, 0x000, UQ_ASSUME_CM_OVER_DATA), USB_QUIRK(QUALCOMM, CDMA_MSM, 0x0000, 0xffff, UQ_ASSUME_CM_OVER_DATA), USB_QUIRK(QUALCOMM2, CDMA_MSM, 0x0000, 0xffff, UQ_ASSUME_CM_OVER_DATA), + USB_QUIRK(CURITEL, UM150, 0x0000, 0xffff, UQ_ASSUME_CM_OVER_DATA), USB_QUIRK(CURITEL, UM175, 0x0000, 0xffff, UQ_ASSUME_CM_OVER_DATA), + USB_QUIRK(VERTEX, VW110L, 0x0000, 0xffff, UQ_ASSUME_CM_OVER_DATA), /* USB Mass Storage Class Quirks */ USB_QUIRK_VP(USB_VENDOR_ASAHIOPTICAL, 0, UQ_MSC_NO_RS_CLEAR_UA, @@ -154,12 +152,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { UQ_MSC_FORCE_PROTO_SCSI), USB_QUIRK(AIPTEK, POCKETCAM3M, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI), - USB_QUIRK(AIPTEK2, SUNPLUS_TECH, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(ALCOR, SDCR_6335, 0x0000, 0xffff, UQ_MSC_NO_TEST_UNIT_READY, - UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(ALCOR, SDCR_6362, 0x0000, 0xffff, UQ_MSC_NO_TEST_UNIT_READY, - UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(ALCOR, AU6390, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(ALCOR, UMCR_9361, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_GETMAXLUN), USB_QUIRK(ALCOR, TRANSCEND, 0x0142, 0x0142, UQ_MSC_FORCE_WIRE_BBB, @@ -181,22 +173,21 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK(CENTURY, EX35QUAT, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_FORCE_SHORT_INQ, UQ_MSC_NO_START_STOP, UQ_MSC_IGNORE_RESIDUE), - USB_QUIRK(CENTURY, EX35SW4_SB4, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(CYPRESS, XX6830XX, 0x0000, 0xffff, UQ_MSC_NO_GETMAXLUN, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(DESKNOTE, UCR_61S2B, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI), USB_QUIRK(DMI, CFSM_RW, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_GETMAXLUN), - USB_QUIRK(DMI, DISK, 0x000, 0xffff, UQ_MSC_NO_SYNC_CACHE), + USB_QUIRK(EMTEC, RUF2PS, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(EPSON, STYLUS_875DC, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY), USB_QUIRK(EPSON, STYLUS_895, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_GETMAXLUN), USB_QUIRK(FEIYA, 5IN1, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI), + USB_QUIRK(FEIYA, ELANGO, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(FREECOM, DVD, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI), - USB_QUIRK(FREECOM, HDD, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(FUJIPHOTO, MASS0100, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI_I, UQ_MSC_FORCE_PROTO_ATAPI, UQ_MSC_NO_RS_CLEAR_UA, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(GENESYS, GL641USB2IDE, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, @@ -240,7 +231,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK(IOMEGA, ZIP100, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_TEST_UNIT_READY), /* XXX ZIP drives can also use ATAPI */ - USB_QUIRK(JMICRON, JM20336, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(JMICRON, JM20337, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_SYNC_CACHE), @@ -287,8 +277,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { UQ_MSC_FORCE_PROTO_ATAPI), USB_QUIRK(MYSON, HEDEN, 0x0000, 0xffff, UQ_MSC_IGNORE_RESIDUE, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(MYSON, HEDEN_8813, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(MYSON, STARREADER, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(NEODIO, ND3260, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_FORCE_SHORT_INQ), USB_QUIRK(NETAC, CF_CARD, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, @@ -315,7 +303,7 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY), USB_QUIRK(ONSPEC, READER, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI), USB_QUIRK(ONSPEC, UCF100, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, - UQ_MSC_FORCE_PROTO_ATAPI, UQ_MSC_NO_INQUIRY | UQ_MSC_NO_GETMAXLUN), + UQ_MSC_FORCE_PROTO_ATAPI, UQ_MSC_NO_INQUIRY, UQ_MSC_NO_GETMAXLUN), USB_QUIRK(ONSPEC2, IMAGEMATE_SDDR55, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_GETMAXLUN), USB_QUIRK(PANASONIC, KXL840AN, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, @@ -325,7 +313,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK(PANASONIC, KXLCB35AN, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI), USB_QUIRK(PANASONIC, LS120CAM, 0x0000, 0xffff, UQ_MSC_FORCE_PROTO_UFI), - USB_QUIRK(PHILIPS, SPE3030CC, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(PLEXTOR, 40_12_40U, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_TEST_UNIT_READY), USB_QUIRK(PNY, ATTACHE2, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, @@ -336,7 +323,6 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { USB_QUIRK_VP(USB_VENDOR_SAMSUNG_TECHWIN, USB_PRODUCT_SAMSUNG_TECHWIN_DIGIMAX_410, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY), - USB_QUIRK(SAMSUNG, YP_U4, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), USB_QUIRK(SANDISK, SDDR05A, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_READ_CAP_OFFBY1, UQ_MSC_NO_GETMAXLUN), @@ -353,6 +339,8 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_IGNORE_RESIDUE), USB_QUIRK(SANDISK, SDDR31, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_READ_CAP_OFFBY1), + USB_QUIRK(SANDISK, IMAGEMATE_SDDR289, 0x0000, 0xffff, + UQ_MSC_NO_SYNC_CACHE, UQ_MSC_NO_GETMAXLUN), USB_QUIRK(SCANLOGIC, SL11R, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_ATAPI, UQ_MSC_NO_INQUIRY), USB_QUIRK(SHUTTLE, EUSB, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_CBI_I, @@ -456,12 +444,39 @@ static struct usb_quirk_entry usb_quirks[USB_DEV_QUIRKS_MAX] = { UQ_MSC_FORCE_PROTO_ATAPI), USB_QUIRK(MEIZU, M6_SL, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_INQUIRY, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(ACTIONS, MP4, 0x0000, 0xffff, UQ_MSC_FORCE_WIRE_BBB, - UQ_MSC_FORCE_PROTO_SCSI, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(ASUS, GMSC, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(CHIPSBANK, USBMEMSTICK, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(CHIPSBANK, USBMEMSTICK1, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), - USB_QUIRK(NEWLINK, USB2IDEBRIDGE, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE), + + /* Non-standard USB MIDI devices */ + USB_QUIRK(ROLAND, UM1, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SC8850, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SD90, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, UM880N, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, UA100, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, UM4, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, U8, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, UM2, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SC8820, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, PC300, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SK500, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SCD70, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, UM550, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SD20, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, SD80, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(ROLAND, UA700, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(MEDELI, DD305, 0x0000, 0xffff, UQ_SINGLE_CMD_MIDI, UQ_MATCH_VENDOR_ONLY), + + /* Non-standard USB AUDIO devices */ + USB_QUIRK(MAUDIO, FASTTRACKULTRA, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + USB_QUIRK(MAUDIO, FASTTRACKULTRA8R, 0x0000, 0xffff, UQ_AU_VENDOR_CLASS), + + /* + * Quirks for manufacturers which USB devices does not respond + * after issuing non-supported commands: + */ + USB_QUIRK(ALCOR, DUMMY, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE, UQ_MSC_NO_TEST_UNIT_READY, UQ_MATCH_VENDOR_ONLY), + USB_QUIRK(APPLE, DUMMY, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE, UQ_MATCH_VENDOR_ONLY), + USB_QUIRK(FEIYA, DUMMY, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE, UQ_MATCH_VENDOR_ONLY), + USB_QUIRK(REALTEK, DUMMY, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE, UQ_MATCH_VENDOR_ONLY), + USB_QUIRK(INITIO, DUMMY, 0x0000, 0xffff, UQ_MSC_NO_SYNC_CACHE, UQ_MATCH_VENDOR_ONLY), }; #undef USB_QUIRK_VP #undef USB_QUIRK @@ -480,6 +495,7 @@ static const char *usb_quirk_str[USB_QUIRK_MAX] = { [UQ_HID_IGNORE] = "UQ_HID_IGNORE", [UQ_KBD_IGNORE] = "UQ_KBD_IGNORE", [UQ_KBD_BOOTPROTO] = "UQ_KBD_BOOTPROTO", + [UQ_UMS_IGNORE] = "UQ_UMS_IGNORE", [UQ_MS_BAD_CLASS] = "UQ_MS_BAD_CLASS", [UQ_MS_LEADING_BYTE] = "UQ_MS_LEADING_BYTE", [UQ_MS_REVZ] = "UQ_MS_REVZ", @@ -526,6 +542,9 @@ static const char *usb_quirk_str[USB_QUIRK_MAX] = { [UQ_MSC_EJECT_SAEL_M460] = "UQ_MSC_EJECT_SAEL_M460", [UQ_MSC_EJECT_HUAWEISCSI] = "UQ_MSC_EJECT_HUAWEISCSI", [UQ_MSC_EJECT_TCT] = "UQ_MSC_EJECT_TCT", + [UQ_BAD_MIDI] = "UQ_BAD_MIDI", + [UQ_AU_VENDOR_CLASS] = "UQ_AU_VENDOR_CLASS", + [UQ_SINGLE_CMD_MIDI] = "UQ_SINGLE_CMD_MIDI", }; /*------------------------------------------------------------------------* @@ -553,9 +572,9 @@ usb_test_quirk_by_info(const struct usbd_lookup_info *info, uint16_t quirk) uint16_t x; uint16_t y; - if (quirk == UQ_NONE) { - return (0); - } + if (quirk == UQ_NONE) + goto done; + mtx_lock(&usb_quirk_mtx); for (x = 0; x != USB_DEV_QUIRKS_MAX; x++) { @@ -589,7 +608,8 @@ usb_test_quirk_by_info(const struct usbd_lookup_info *info, uint16_t quirk) break; } mtx_unlock(&usb_quirk_mtx); - return (0); +done: + return (0); /* no quirk match */ } static struct usb_quirk_entry * @@ -770,7 +790,7 @@ usb_quirk_ioctl(unsigned long cmd, caddr_t data, } if (x == USB_SUB_QUIRKS_MAX) { /* all quirk entries are unused - release */ - memset(pqe, 0, sizeof(pqe)); + memset(pqe, 0, sizeof(*pqe)); } mtx_unlock(&usb_quirk_mtx); return (0); /* success */ diff --git a/freebsd/sys/dev/usb/quirk/usb_quirk.h b/freebsd/sys/dev/usb/quirk/usb_quirk.h index 522b15a4..f2c10dd8 100644 --- a/freebsd/sys/dev/usb/quirk/usb_quirk.h +++ b/freebsd/sys/dev/usb/quirk/usb_quirk.h @@ -29,7 +29,7 @@ enum { /* - * Keep in sync with theusb_quirk_str usb_quirk.c, and with the + * Keep in sync with usb_quirk_str in usb_quirk.c, and with * share/man/man4/usb_quirk.4 */ UQ_NONE, /* not a valid quirk */ @@ -49,6 +49,7 @@ enum { UQ_HID_IGNORE, /* device should be ignored by hid class */ UQ_KBD_IGNORE, /* device should be ignored by kbd class */ UQ_KBD_BOOTPROTO, /* device should set the boot protocol */ + UQ_UMS_IGNORE, /* device should be ignored by ums class */ UQ_MS_BAD_CLASS, /* doesn't identify properly */ UQ_MS_LEADING_BYTE, /* mouse sends an unknown leading byte */ UQ_MS_REVZ, /* mouse has Z-axis reversed */ @@ -64,7 +65,10 @@ enum { UQ_CFG_INDEX_0, /* select configuration index 0 by default */ UQ_ASSUME_CM_OVER_DATA, /* assume cm over data feature */ - /* USB Mass Storage Quirks. See "storage/umass.c" for a detailed description. */ + /* + * USB Mass Storage Quirks. See "storage/umass.c" for a + * detailed description. + */ UQ_MSC_NO_TEST_UNIT_READY, /* send start/stop instead of TUR */ UQ_MSC_NO_RS_CLEAR_UA, /* does not reset Unit Att. */ UQ_MSC_NO_START_STOP, /* does not support start/stop */ @@ -100,6 +104,10 @@ enum { UQ_MSC_EJECT_HUAWEISCSI, /* ejects after Huawei SCSI command */ UQ_MSC_EJECT_TCT, /* ejects after TCT SCSI command */ + UQ_BAD_MIDI, /* device claims MIDI class, but isn't */ + UQ_AU_VENDOR_CLASS, /* audio device uses vendor and not audio class */ + UQ_SINGLE_CMD_MIDI, /* at most one command per USB packet */ + USB_QUIRK_MAX }; diff --git a/freebsd/sys/dev/usb/storage/umass.c b/freebsd/sys/dev/usb/storage/umass.c index f59062d9..9c448466 100644 --- a/freebsd/sys/dev/usb/storage/umass.c +++ b/freebsd/sys/dev/usb/storage/umass.c @@ -112,7 +112,6 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -177,9 +176,8 @@ __FBSDID("$FreeBSD$"); static int umass_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, umass, CTLFLAG_RW, 0, "USB umass"); -SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW, +SYSCTL_INT(_hw_usb_umass, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &umass_debug, 0, "umass debug level"); - TUNABLE_INT("hw.usb.umass.debug", &umass_debug); #else #define DIF(...) do { } while (0) @@ -726,6 +724,11 @@ MODULE_VERSION(umass, 1); * USB device probe/attach/detach */ +static const STRUCT_USB_HOST_ID __used umass_devs[] = { + /* generic mass storage class */ + {USB_IFACE_CLASS(UICLASS_MASS),}, +}; + static uint16_t umass_get_proto(struct usb_interface *iface) { @@ -787,6 +790,7 @@ umass_probe_proto(device_t dev, struct usb_attach_arg *uaa) uint32_t proto = umass_get_proto(uaa->iface); memset(&ret, 0, sizeof(ret)); + ret.error = BUS_PROBE_GENERIC; /* Search for protocol enforcement */ @@ -875,10 +879,6 @@ umass_probe(device_t dev) if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } - if (uaa->use_generic == 0) { - /* give other drivers a try first */ - return (ENXIO); - } temp = umass_probe_proto(dev, uaa); return (temp.error); @@ -894,7 +894,7 @@ umass_attach(device_t dev) int32_t err; /* - * NOTE: the softc struct is bzero-ed in device_set_driver. + * NOTE: the softc struct is cleared in device_set_driver. * We can safely call umass_detach without specifically * initializing the struct. */ @@ -1028,12 +1028,6 @@ umass_attach(device_t dev) sc->cam_scsi_sense.opcode = REQUEST_SENSE; sc->cam_scsi_test_unit_ready.opcode = TEST_UNIT_READY; - /* - * some devices need a delay after that the configuration value is - * set to function properly: - */ - usb_pause_mtx(NULL, hz); - /* register the SIM */ err = umass_cam_attach_sim(sc); if (err) { @@ -1316,11 +1310,13 @@ umass_t_bbb_command_callback(struct usb_xfer *xfer, usb_error_t error) } sc->cbw.bCDBLength = sc->sc_transfer.cmd_len; - bcopy(sc->sc_transfer.cmd_data, sc->cbw.CBWCDB, + memcpy(sc->cbw.CBWCDB, sc->sc_transfer.cmd_data, sc->sc_transfer.cmd_len); - bzero(sc->sc_transfer.cmd_data + sc->sc_transfer.cmd_len, - sizeof(sc->cbw.CBWCDB) - sc->sc_transfer.cmd_len); + memset(sc->sc_transfer.cmd_data + + sc->sc_transfer.cmd_len, 0, + sizeof(sc->cbw.CBWCDB) - + sc->sc_transfer.cmd_len); DIF(UDMASS_BBB, umass_bbb_dump_cbw(sc, &sc->cbw)); @@ -1491,9 +1487,9 @@ umass_t_bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) /* Zero missing parts of the CSW: */ - if (actlen < sizeof(sc->csw)) { - bzero(&sc->csw, sizeof(sc->csw)); - } + if (actlen < (int)sizeof(sc->csw)) + memset(&sc->csw, 0, sizeof(sc->csw)); + pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &sc->csw, actlen); @@ -1856,9 +1852,23 @@ umass_t_cbi_command_callback(struct usb_xfer *xfer, usb_error_t error) break; default: /* Error */ - umass_tr_error(xfer, error); - /* skip reset */ - sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; + /* + * STALL on the control pipe can be result of the command error. + * Attempt to clear this STALL same as for bulk pipe also + * results in command completion interrupt, but ASC/ASCQ there + * look like not always valid, so don't bother about it. + */ + if ((error == USB_ERR_STALLED) || + (sc->sc_transfer.callback == &umass_cam_cb)) { + sc->sc_transfer.ccb = NULL; + (sc->sc_transfer.callback) + (sc, ccb, sc->sc_transfer.data_len, + STATUS_CMD_UNKNOWN); + } else { + umass_tr_error(xfer, error); + /* skip reset */ + sc->sc_last_xfer_index = UMASS_T_CBI_COMMAND; + } break; } } @@ -2011,7 +2021,7 @@ umass_t_cbi_status_callback(struct usb_xfer *xfer, usb_error_t error) switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - if (actlen < sizeof(sc->sbl)) { + if (actlen < (int)sizeof(sc->sbl)) { goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); @@ -2616,17 +2626,9 @@ umass_cam_cb(struct umass_softc *sc, union ccb *ccb, uint32_t residue, /* * The wire protocol failed and will hopefully have * recovered. We return an error to CAM and let CAM - * retry the command if necessary. In case of SCSI IO - * commands we ask the CAM layer to check the - * condition first. This is a quick hack to make - * certain devices work. + * retry the command if necessary. */ - if (ccb->ccb_h.func_code == XPT_SCSI_IO) { - ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; - ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; - } else { - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - } + ccb->ccb_h.status = CAM_REQ_CMP_ERR; xpt_done(ccb); break; } @@ -2761,7 +2763,7 @@ umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, if (sc->sc_quirks & NO_TEST_UNIT_READY) { DPRINTF(sc, UDMASS_SCSI, "Converted TEST_UNIT_READY " "to START_UNIT\n"); - bzero(sc->sc_transfer.cmd_data, cmd_len); + memset(sc->sc_transfer.cmd_data, 0, cmd_len); sc->sc_transfer.cmd_data[0] = START_STOP_UNIT; sc->sc_transfer.cmd_data[4] = SSS_START; return (1); @@ -2774,14 +2776,14 @@ umass_scsi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, * information. */ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { - bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; return (1); } break; } - bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); return (1); } @@ -2816,10 +2818,11 @@ umass_rbc_transform(struct umass_softc *sc, uint8_t *cmd_ptr, uint8_t cmd_len) case REQUEST_SENSE: case PREVENT_ALLOW: - bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); if ((sc->sc_quirks & RBC_PAD_TO_12) && (cmd_len < 12)) { - bzero(sc->sc_transfer.cmd_data + cmd_len, 12 - cmd_len); + memset(sc->sc_transfer.cmd_data + cmd_len, + 0, 12 - cmd_len); cmd_len = 12; } sc->sc_transfer.cmd_len = cmd_len; @@ -2847,7 +2850,7 @@ umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, sc->sc_transfer.cmd_len = UFI_COMMAND_LENGTH; /* Zero the command data */ - bzero(sc->sc_transfer.cmd_data, UFI_COMMAND_LENGTH); + memset(sc->sc_transfer.cmd_data, 0, UFI_COMMAND_LENGTH); switch (cmd_ptr[0]) { /* @@ -2904,7 +2907,7 @@ umass_ufi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, return (0); /* failure */ } - bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); return (1); /* success */ } @@ -2925,7 +2928,7 @@ umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, sc->sc_transfer.cmd_len = ATAPI_COMMAND_LENGTH; /* Zero the command data */ - bzero(sc->sc_transfer.cmd_data, ATAPI_COMMAND_LENGTH); + memset(sc->sc_transfer.cmd_data, 0, ATAPI_COMMAND_LENGTH); switch (cmd_ptr[0]) { /* @@ -2939,7 +2942,7 @@ umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, * information. */ if (sc->sc_quirks & FORCE_SHORT_INQUIRY) { - bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); sc->sc_transfer.cmd_data[4] = SHORT_INQUIRY_LENGTH; return (1); @@ -3000,7 +3003,7 @@ umass_atapi_transform(struct umass_softc *sc, uint8_t *cmd_ptr, break; } - bcopy(cmd_ptr, sc->sc_transfer.cmd_data, cmd_len); + memcpy(sc->sc_transfer.cmd_data, cmd_ptr, cmd_len); return (1); /* success */ } diff --git a/freebsd/sys/dev/usb/usb.h b/freebsd/sys/dev/usb/usb.h index 433a0471..2f216d27 100644 --- a/freebsd/sys/dev/usb/usb.h +++ b/freebsd/sys/dev/usb/usb.h @@ -93,31 +93,29 @@ MALLOC_DECLARE(M_USBHC); #define USB_POWER_MODE_SUSPEND 3 /* force suspend */ #define USB_POWER_MODE_RESUME 4 /* force resume */ -#if 0 /* These are the values from the USB specification. */ -#define USB_PORT_RESET_DELAY 10 /* ms */ -#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ -#define USB_PORT_RESET_RECOVERY 10 /* ms */ -#define USB_PORT_POWERUP_DELAY 100 /* ms */ -#define USB_PORT_RESUME_DELAY 20 /* ms */ -#define USB_SET_ADDRESS_SETTLE 2 /* ms */ -#define USB_RESUME_DELAY (20*5) /* ms */ -#define USB_RESUME_WAIT 10 /* ms */ -#define USB_RESUME_RECOVERY 10 /* ms */ -#define USB_EXTRA_POWER_UP_TIME 0 /* ms */ -#else +#define USB_PORT_RESET_DELAY_SPEC 10 /* ms */ +#define USB_PORT_ROOT_RESET_DELAY_SPEC 50 /* ms */ +#define USB_PORT_RESET_RECOVERY_SPEC 10 /* ms */ +#define USB_PORT_POWERUP_DELAY_SPEC 100 /* ms */ +#define USB_PORT_RESUME_DELAY_SPEC 20 /* ms */ +#define USB_SET_ADDRESS_SETTLE_SPEC 2 /* ms */ +#define USB_RESUME_DELAY_SPEC (20*5) /* ms */ +#define USB_RESUME_WAIT_SPEC 10 /* ms */ +#define USB_RESUME_RECOVERY_SPEC 10 /* ms */ +#define USB_EXTRA_POWER_UP_TIME_SPEC 0 /* ms */ + /* Allow for marginal and non-conforming devices. */ -#define USB_PORT_RESET_DELAY 50 /* ms */ -#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ -#define USB_PORT_RESET_RECOVERY 250 /* ms */ -#define USB_PORT_POWERUP_DELAY 300 /* ms */ -#define USB_PORT_RESUME_DELAY (20*2) /* ms */ -#define USB_SET_ADDRESS_SETTLE 10 /* ms */ -#define USB_RESUME_DELAY (50*5) /* ms */ -#define USB_RESUME_WAIT 50 /* ms */ -#define USB_RESUME_RECOVERY 50 /* ms */ -#define USB_EXTRA_POWER_UP_TIME 20 /* ms */ -#endif +#define USB_PORT_RESET_DELAY 50 /* ms */ +#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ +#define USB_PORT_RESET_RECOVERY 250 /* ms */ +#define USB_PORT_POWERUP_DELAY 300 /* ms */ +#define USB_PORT_RESUME_DELAY (20*2) /* ms */ +#define USB_SET_ADDRESS_SETTLE 10 /* ms */ +#define USB_RESUME_DELAY (50*5) /* ms */ +#define USB_RESUME_WAIT 50 /* ms */ +#define USB_RESUME_RECOVERY 50 /* ms */ +#define USB_EXTRA_POWER_UP_TIME 20 /* ms */ #define USB_MIN_POWER 100 /* mA */ #define USB_MAX_POWER 500 /* mA */ @@ -323,7 +321,7 @@ struct usb_devcap_usb2ext_descriptor { uByte bLength; uByte bDescriptorType; uByte bDevCapabilityType; - uByte bmAttributes; + uDWord bmAttributes; #define USB_V2EXT_LPM 0x02 } __packed; typedef struct usb_devcap_usb2ext_descriptor usb_devcap_usb2ext_descriptor_t; @@ -336,7 +334,7 @@ struct usb_devcap_ss_descriptor { uWord wSpeedsSupported; uByte bFunctionalitySupport; uByte bU1DevExitLat; - uByte bU2DevExitLat; + uWord wU2DevExitLat; } __packed; typedef struct usb_devcap_ss_descriptor usb_devcap_ss_descriptor_t; @@ -686,7 +684,9 @@ struct usb_port_status { #define UPS_PORT_LS_HOT_RST 0x09 #define UPS_PORT_LS_COMP_MODE 0x0A #define UPS_PORT_LS_LOOPBACK 0x0B +#define UPS_PORT_LS_RESUME 0x0F #define UPS_PORT_POWER 0x0100 +#define UPS_PORT_POWER_SS 0x0200 /* super-speed only */ #define UPS_LOW_SPEED 0x0200 #define UPS_HIGH_SPEED 0x0400 #define UPS_OTHER_SPEED 0x0600 /* currently FreeBSD specific */ diff --git a/freebsd/sys/dev/usb/usb_bus.h b/freebsd/sys/dev/usb/usb_bus.h index c9840fcb..459219bd 100644 --- a/freebsd/sys/dev/usb/usb_bus.h +++ b/freebsd/sys/dev/usb/usb_bus.h @@ -71,6 +71,9 @@ struct usb_bus { struct usb_bus_msg explore_msg[2]; struct usb_bus_msg detach_msg[2]; 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 shutdown_msg[2]; /* * This mutex protects the USB hardware: */ @@ -88,6 +91,8 @@ struct usb_bus { struct usb_bus_methods *methods; /* filled by HC driver */ struct usb_device **devices; + struct ifnet *ifp; /* only for USB Packet Filter */ + usb_power_mask_t hw_power_state; /* see USB_HW_POWER_XXX */ usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; @@ -98,17 +103,8 @@ struct usb_bus { enum usb_revision usbrev; /* USB revision. See "USB_REV_XXX". */ uint8_t devices_max; /* maximum number of USB devices */ - uint8_t do_probe; /* set if USB BUS should be re-probed */ - - /* - * The scratch area can only be used inside the explore thread - * belonging to the give serial bus. - */ - union { - struct usb_hw_ep_scratch hw_ep_scratch[1]; - struct usb_temp_setup temp_setup[1]; - uint8_t data[255]; - } scratch[1]; + uint8_t do_probe; /* set if USB should be re-probed */ + uint8_t no_explore; /* don't explore USB ports */ }; #endif /* _USB_BUS_H_ */ diff --git a/freebsd/sys/dev/usb/usb_busdma.c b/freebsd/sys/dev/usb/usb_busdma.c index e08bcbd3..4aa8e1f4 100644 --- a/freebsd/sys/dev/usb/usb_busdma.c +++ b/freebsd/sys/dev/usb/usb_busdma.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -83,9 +82,9 @@ void usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res) { +#if USB_HAVE_BUSDMA struct usb_page *page; -#if USB_HAVE_BUSDMA if (pc->page_start) { /* Case 1 - something has been loaded into DMA */ @@ -111,7 +110,7 @@ usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, res->length = USB_PAGE_SIZE - offset; res->physaddr = page->physaddr + offset; } else { - res->length = 0 - 1; + res->length = (usb_size_t)-1; res->physaddr = page->physaddr + offset; } if (!pc->buffer) { @@ -126,7 +125,7 @@ usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, /* Case 2 - Plain PIO */ res->buffer = USB_ADD_BYTES(pc->buffer, offset); - res->length = 0 - 1; + res->length = (usb_size_t)-1; #if USB_HAVE_BUSDMA res->physaddr = 0; #endif @@ -148,7 +147,7 @@ usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, if (buf_res.length > len) { buf_res.length = len; } - bcopy(ptr, buf_res.buffer, buf_res.length); + memcpy(buf_res.buffer, ptr, buf_res.length); offset += buf_res.length; len -= buf_res.length; @@ -270,7 +269,7 @@ usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, if (res.length > len) { res.length = len; } - bcopy(res.buffer, ptr, res.length); + memcpy(ptr, res.buffer, res.length); offset += res.length; len -= res.length; @@ -328,7 +327,7 @@ usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, if (res.length > len) { res.length = len; } - bzero(res.buffer, res.length); + memset(res.buffer, 0, res.length); offset += res.length; len -= res.length; @@ -563,7 +562,7 @@ usb_pc_alloc_mem(struct usb_page_cache *pc, struct usb_page *pg, bus_dmamem_free(utag->tag, ptr, map); goto error; } - bzero(ptr, size); + memset(ptr, 0, size); usb_pc_cpu_flush(pc); @@ -800,7 +799,7 @@ usb_dma_tag_setup(struct usb_dma_parent_tag *udpt, struct mtx *mtx, usb_dma_callback_t *func, uint8_t ndmabits, uint8_t nudt) { - bzero(udpt, sizeof(*udpt)); + memset(udpt, 0, sizeof(*udpt)); /* sanity checking */ if ((nudt == 0) || @@ -821,7 +820,7 @@ usb_dma_tag_setup(struct usb_dma_parent_tag *udpt, udpt->dma_bits = ndmabits; while (nudt--) { - bzero(udt, sizeof(*udt)); + memset(udt, 0, sizeof(*udt)); udt->tag_parent = udpt; udt++; } diff --git a/freebsd/sys/dev/usb/usb_cdc.h b/freebsd/sys/dev/usb/usb_cdc.h index 23fd111c..b8f59fae 100644 --- a/freebsd/sys/dev/usb/usb_cdc.h +++ b/freebsd/sys/dev/usb/usb_cdc.h @@ -17,13 +17,6 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED diff --git a/freebsd/sys/dev/usb/usb_controller.h b/freebsd/sys/dev/usb/usb_controller.h index 6b15dab9..ad871913 100644 --- a/freebsd/sys/dev/usb/usb_controller.h +++ b/freebsd/sys/dev/usb/usb_controller.h @@ -40,7 +40,6 @@ struct usb_page_cache; struct usb_setup_params; struct usb_hw_ep_profile; struct usb_fs_isoc_schedule; -struct usb_config_descriptor; struct usb_endpoint_descriptor; /* typedefs */ @@ -66,7 +65,7 @@ struct usb_bus_methods { void (*device_suspend) (struct usb_device *); void (*device_resume) (struct usb_device *); void (*set_hw_power) (struct usb_bus *); - + void (*set_hw_power_sleep) (struct usb_bus *, uint32_t); /* * The following flag is set if one or more control transfers are * active: @@ -92,6 +91,18 @@ struct usb_bus_methods { * are present on the given USB bus: */ #define USB_HW_POWER_NON_ROOT_HUB 0x10 + /* + * The following flag is set if we are suspending + */ +#define USB_HW_POWER_SUSPEND 0x20 + /* + * The following flag is set if we are resuming + */ +#define USB_HW_POWER_RESUME 0x40 + /* + * The following flag is set if we are shutting down + */ +#define USB_HW_POWER_SHUTDOWN 0x60 /* USB Device mode only - Mandatory */ @@ -169,57 +180,14 @@ struct usb_hw_ep_profile { uint8_t support_out:1; /* OUT-token is supported */ }; -/* - * The following structure is used when trying to allocate hardware - * endpoints for an USB configuration in USB device side mode. - */ -struct usb_hw_ep_scratch_sub { - const struct usb_hw_ep_profile *pf; - uint16_t max_frame_size; - uint8_t hw_endpoint_out; - uint8_t hw_endpoint_in; - uint8_t needs_ep_type; - uint8_t needs_in:1; - uint8_t needs_out:1; -}; - -/* - * The following structure is used when trying to allocate hardware - * endpoints for an USB configuration in USB device side mode. - */ -struct usb_hw_ep_scratch { - struct usb_hw_ep_scratch_sub ep[USB_EP_MAX]; - struct usb_hw_ep_scratch_sub *ep_max; - struct usb_config_descriptor *cd; - struct usb_device *udev; - struct usb_bus_methods *methods; - uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16]; - uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16]; -}; - -/* - * The following structure is used when generating USB descriptors - * from USB templates. - */ -struct usb_temp_setup { - void *buf; - usb_size_t size; - enum usb_dev_speed usb_speed; - uint8_t self_powered; - uint8_t bNumEndpoints; - uint8_t bInterfaceNumber; - uint8_t bAlternateSetting; - uint8_t bConfigurationValue; - usb_error_t err; -}; - /* prototypes */ 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); -uint16_t usbd_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev, struct usb_fs_isoc_schedule **pp_start, struct usb_fs_isoc_schedule **pp_end, uint16_t isoc_time); -uint8_t usbd_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len); +#if USB_HAVE_TT_SUPPORT +uint8_t usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time); +#endif #endif /* _USB_CONTROLLER_H_ */ diff --git a/freebsd/sys/dev/usb/usb_core.c b/freebsd/sys/dev/usb/usb_core.c index 38504363..27e07adf 100644 --- a/freebsd/sys/dev/usb/usb_core.c +++ b/freebsd/sys/dev/usb/usb_core.c @@ -40,7 +40,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> diff --git a/freebsd/sys/dev/usb/usb_debug.c b/freebsd/sys/dev/usb/usb_debug.c index 40dafa20..159bf7ea 100644 --- a/freebsd/sys/dev/usb/usb_debug.c +++ b/freebsd/sys/dev/usb/usb_debug.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -66,11 +65,59 @@ int usb_debug = 0; SYSCTL_NODE(_hw, OID_AUTO, usb, CTLFLAG_RW, 0, "USB debugging"); -SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW, +SYSCTL_INT(_hw_usb, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &usb_debug, 0, "Debug level"); - TUNABLE_INT("hw.usb.debug", &usb_debug); +#ifdef USB_DEBUG +/* + * Sysctls to modify timings/delays + */ +static SYSCTL_NODE(_hw_usb, OID_AUTO, timings, CTLFLAG_RW, 0, "Timings"); +static int usb_timings_sysctl_handler(SYSCTL_HANDLER_ARGS); + +TUNABLE_INT("hw.usb.timings.port_reset_delay", (int *)&usb_port_reset_delay); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_reset_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_port_reset_delay, sizeof(usb_port_reset_delay), + usb_timings_sysctl_handler, "IU", "Port Reset Delay"); +TUNABLE_INT("hw.usb.timings.port_root_reset_delay", (int *)&usb_port_root_reset_delay); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_root_reset_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_port_root_reset_delay, sizeof(usb_port_root_reset_delay), + usb_timings_sysctl_handler, "IU", "Root Port Reset Delay"); +TUNABLE_INT("hw.usb.timings.port_reset_recovery", (int *)&usb_port_reset_recovery); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_reset_recovery, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_port_reset_recovery, sizeof(usb_port_reset_recovery), + usb_timings_sysctl_handler, "IU", "Port Reset Recovery"); +TUNABLE_INT("hw.usb.timings.port_powerup_delay", (int *)&usb_port_powerup_delay); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_powerup_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_port_powerup_delay, sizeof(usb_port_powerup_delay), + usb_timings_sysctl_handler, "IU", "Port PowerUp Delay"); +TUNABLE_INT("hw.usb.timings.port_resume_delay", (int *)&usb_port_resume_delay); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, port_resume_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_port_resume_delay, sizeof(usb_port_resume_delay), + usb_timings_sysctl_handler, "IU", "Port Resume Delay"); +TUNABLE_INT("hw.usb.timings.set_address_settle", (int *)&usb_set_address_settle); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, set_address_settle, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_set_address_settle, sizeof(usb_set_address_settle), + usb_timings_sysctl_handler, "IU", "Set Address Settle"); +TUNABLE_INT("hw.usb.timings.resume_delay", (int *)&usb_resume_delay); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, resume_delay, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_resume_delay, sizeof(usb_resume_delay), + usb_timings_sysctl_handler, "IU", "Resume Delay"); +TUNABLE_INT("hw.usb.timings.resume_wait", (int *)&usb_resume_wait); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, resume_wait, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_resume_wait, sizeof(usb_resume_wait), + usb_timings_sysctl_handler, "IU", "Resume Wait"); +TUNABLE_INT("hw.usb.timings.resume_recovery", (int *)&usb_resume_recovery); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, resume_recovery, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_resume_recovery, sizeof(usb_resume_recovery), + usb_timings_sysctl_handler, "IU", "Resume Recovery"); +TUNABLE_INT("hw.usb.timings.extra_power_up_time", (int *)&usb_extra_power_up_time); +SYSCTL_PROC(_hw_usb_timings, OID_AUTO, extra_power_up_time, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_TUN, + &usb_extra_power_up_time, sizeof(usb_extra_power_up_time), + usb_timings_sysctl_handler, "IU", "Extra PowerUp Time"); +#endif + /*------------------------------------------------------------------------* * usb_dump_iface * @@ -177,3 +224,87 @@ usb_dump_xfer(struct usb_xfer *xfer) xfer->endpoint->edesc->bEndpointAddress, xfer->endpoint->edesc->bmAttributes); } + +#ifdef USB_DEBUG +unsigned int usb_port_reset_delay = USB_PORT_RESET_DELAY; +unsigned int usb_port_root_reset_delay = USB_PORT_ROOT_RESET_DELAY; +unsigned int usb_port_reset_recovery = USB_PORT_RESET_RECOVERY; +unsigned int usb_port_powerup_delay = USB_PORT_POWERUP_DELAY; +unsigned int usb_port_resume_delay = USB_PORT_RESUME_DELAY; +unsigned int usb_set_address_settle = USB_SET_ADDRESS_SETTLE; +unsigned int usb_resume_delay = USB_RESUME_DELAY; +unsigned int usb_resume_wait = USB_RESUME_WAIT; +unsigned int usb_resume_recovery = USB_RESUME_RECOVERY; +unsigned int usb_extra_power_up_time = USB_EXTRA_POWER_UP_TIME; + +/*------------------------------------------------------------------------* + * usb_timings_sysctl_handler + * + * This function updates timings variables, adjusting them where necessary. + *------------------------------------------------------------------------*/ +static int usb_timings_sysctl_handler(SYSCTL_HANDLER_ARGS) +{ + int error = 0; + unsigned int val; + + /* + * Attempt to get a coherent snapshot by making a copy of the data. + */ + if (arg1) + val = *(unsigned int *)arg1; + else + val = arg2; + error = SYSCTL_OUT(req, &val, sizeof(int)); + if (error || !req->newptr) + return (error); + + if (!arg1) + return EPERM; + + error = SYSCTL_IN(req, &val, sizeof(unsigned int)); + if (error) + return (error); + + /* + * Now make sure the values are decent, and certainly no lower than + * what the USB spec prescribes. + */ + unsigned int *p = (unsigned int *)arg1; + if (p == &usb_port_reset_delay) { + if (val < USB_PORT_RESET_DELAY_SPEC) + return (EINVAL); + } else if (p == &usb_port_root_reset_delay) { + if (val < USB_PORT_ROOT_RESET_DELAY_SPEC) + return (EINVAL); + } else if (p == &usb_port_reset_recovery) { + if (val < USB_PORT_RESET_RECOVERY_SPEC) + return (EINVAL); + } else if (p == &usb_port_powerup_delay) { + if (val < USB_PORT_POWERUP_DELAY_SPEC) + return (EINVAL); + } else if (p == &usb_port_resume_delay) { + if (val < USB_PORT_RESUME_DELAY_SPEC) + return (EINVAL); + } else if (p == &usb_set_address_settle) { + if (val < USB_SET_ADDRESS_SETTLE_SPEC) + return (EINVAL); + } else if (p == &usb_resume_delay) { + if (val < USB_RESUME_DELAY_SPEC) + return (EINVAL); + } else if (p == &usb_resume_wait) { + if (val < USB_RESUME_WAIT_SPEC) + return (EINVAL); + } else if (p == &usb_resume_recovery) { + if (val < USB_RESUME_RECOVERY_SPEC) + return (EINVAL); + } else if (p == &usb_extra_power_up_time) { + if (val < USB_EXTRA_POWER_UP_TIME_SPEC) + return (EINVAL); + } else { + /* noop */ + } + + *p = val; + return 0; +} +#endif diff --git a/freebsd/sys/dev/usb/usb_debug.h b/freebsd/sys/dev/usb/usb_debug.h index 8718c89b..038ba7f4 100644 --- a/freebsd/sys/dev/usb/usb_debug.h +++ b/freebsd/sys/dev/usb/usb_debug.h @@ -59,4 +59,28 @@ void usb_dump_queue(struct usb_endpoint *ep); void usb_dump_endpoint(struct usb_endpoint *ep); void usb_dump_xfer(struct usb_xfer *xfer); +#ifdef USB_DEBUG +extern unsigned int usb_port_reset_delay; +extern unsigned int usb_port_root_reset_delay; +extern unsigned int usb_port_reset_recovery; +extern unsigned int usb_port_powerup_delay; +extern unsigned int usb_port_resume_delay; +extern unsigned int usb_set_address_settle; +extern unsigned int usb_resume_delay; +extern unsigned int usb_resume_wait; +extern unsigned int usb_resume_recovery; +extern unsigned int usb_extra_power_up_time; +#else +#define usb_port_reset_delay USB_PORT_RESET_DELAY +#define usb_port_root_reset_delay USB_PORT_ROOT_RESET_DELAY +#define usb_port_reset_recovery USB_PORT_RESET_RECOVERY +#define usb_port_powerup_delay USB_PORT_POWERUP_DELAY +#define usb_port_resume_delay USB_PORT_RESUME_DELAY +#define usb_set_address_settle USB_SET_ADDRESS_SETTLE +#define usb_resume_delay USB_RESUME_DELAY +#define usb_resume_wait USB_RESUME_WAIT +#define usb_resume_recovery USB_RESUME_RECOVERY +#define usb_extra_power_up_time USB_EXTRA_POWER_UP_TIME +#endif + #endif /* _USB_DEBUG_H_ */ diff --git a/freebsd/sys/dev/usb/usb_dev.c b/freebsd/sys/dev/usb/usb_dev.c index eaad5952..a22de719 100644 --- a/freebsd/sys/dev/usb/usb_dev.c +++ b/freebsd/sys/dev/usb/usb_dev.c @@ -37,7 +37,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -87,9 +86,8 @@ static int usb_fifo_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, dev, CTLFLAG_RW, 0, "USB device"); -SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW, +SYSCTL_INT(_hw_usb_dev, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &usb_fifo_debug, 0, "Debug Level"); - TUNABLE_INT("hw.usb.dev.debug", &usb_fifo_debug); #endif @@ -220,10 +218,10 @@ usb_ref_device(struct usb_cdev_privdata *cpd, mtx_unlock(&usb_ref_lock); /* - * We need to grab the sx-lock before grabbing the - * FIFO refs to avoid deadlock at detach! + * We need to grab the enumeration SX-lock before + * grabbing the FIFO refs to avoid deadlock at detach! */ - usbd_enum_lock(cpd->udev); + crd->do_unlock = usbd_enum_lock(cpd->udev); mtx_lock(&usb_ref_lock); @@ -284,9 +282,10 @@ usb_ref_device(struct usb_cdev_privdata *cpd, return (0); error: - if (crd->is_uref) { + if (crd->do_unlock) usbd_enum_unlock(cpd->udev); + if (crd->is_uref) { if (--(cpd->udev->refcount) == 0) { cv_signal(&cpd->udev->ref_cv); } @@ -338,7 +337,7 @@ usb_unref_device(struct usb_cdev_privdata *cpd, DPRINTFN(2, "cpd=%p is_uref=%d\n", cpd, crd->is_uref); - if (crd->is_uref) + if (crd->do_unlock) usbd_enum_unlock(cpd->udev); mtx_lock(&usb_ref_lock); @@ -916,10 +915,23 @@ usb_close(void *arg) DPRINTFN(2, "cpd=%p\n", cpd); - err = usb_ref_device(cpd, &refs, 1); - if (err) { - free(cpd, M_USBDEV); - return; + err = usb_ref_device(cpd, &refs, 0); + if (err) + 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); @@ -927,10 +939,9 @@ usb_close(void *arg) if (cpd->fflags & FWRITE) { usb_fifo_close(refs.txfifo, cpd->fflags); } - usb_unref_device(cpd, &refs); +done: free(cpd, M_USBDEV); - return; } static void @@ -1646,14 +1657,13 @@ usb_fifo_check_methods(struct usb_fifo_methods *pm) int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, - struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, + struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode) { struct usb_fifo *f_tx; struct usb_fifo *f_rx; char devname[32]; uint8_t n; - struct usb_fs_privdata* pd; f_sc->fp[USB_FIFO_TX] = NULL; f_sc->fp[USB_FIFO_RX] = NULL; @@ -1724,7 +1734,7 @@ usb_fifo_attach(struct usb_device *udev, void *priv_sc, if (pm->basename[n] == NULL) { continue; } - if (subunit == 0xFFFF) { + if (subunit < 0) { if (snprintf(devname, sizeof(devname), "%s%u%s", pm->basename[n], unit, pm->postfix[n] ? @@ -1733,7 +1743,7 @@ usb_fifo_attach(struct usb_device *udev, void *priv_sc, } } else { if (snprintf(devname, sizeof(devname), - "%s%u.%u%s", pm->basename[n], + "%s%u.%d%s", pm->basename[n], unit, subunit, pm->postfix[n] ? pm->postfix[n] : "")) { /* ignore */ @@ -1751,22 +1761,10 @@ usb_fifo_attach(struct usb_device *udev, void *priv_sc, usb_alloc_symlink(devname); } - /* - * Initialize device private data - this is used to find the - * actual USB device itself. - */ - pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, M_WAITOK | M_ZERO); - pd->bus_index = device_get_unit(udev->bus->bdev); - pd->dev_index = udev->device_index; - pd->ep_addr = -1; /* not an endpoint */ - pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index; - pd->mode = FREAD|FWRITE; - - /* Now, create the device itself */ - f_sc->dev = make_dev(&usb_devsw, 0, uid, gid, mode, - "%s", devname); - /* XXX setting si_drv1 and creating the device is not atomic! */ - f_sc->dev->si_drv1 = pd; + /* Create the device */ + f_sc->dev = usb_make_dev(udev, devname, -1, + f_tx->fifo_index & f_rx->fifo_index, + FREAD|FWRITE, uid, gid, mode); } DPRINTFN(2, "attached %p/%p\n", f_tx, f_rx); @@ -1815,14 +1813,8 @@ usb_fifo_free_buffer(struct usb_fifo *f) } /* reset queues */ - bzero(&f->free_q, sizeof(f->free_q)); - bzero(&f->used_q, sizeof(f->used_q)); -} - -static void -usb_fifo_cleanup(void* ptr) -{ - free(ptr, M_USBDEV); + memset(&f->free_q, 0, sizeof(f->free_q)); + memset(&f->used_q, 0, sizeof(f->used_q)); } void @@ -1837,11 +1829,9 @@ usb_fifo_detach(struct usb_fifo_sc *f_sc) f_sc->fp[USB_FIFO_TX] = NULL; f_sc->fp[USB_FIFO_RX] = NULL; - if (f_sc->dev != NULL) { - destroy_dev_sched_cb(f_sc->dev, - usb_fifo_cleanup, f_sc->dev->si_drv1); - f_sc->dev = NULL; - } + usb_destroy_dev(f_sc->dev); + + f_sc->dev = NULL; DPRINTFN(2, "detached %p\n", f_sc); } @@ -1923,7 +1913,7 @@ usb_fifo_put_data_linear(struct usb_fifo *f, void *ptr, io_len = MIN(len, m->cur_data_len); - bcopy(ptr, m->cur_data_ptr, io_len); + memcpy(m->cur_data_ptr, ptr, io_len); m->cur_data_len = io_len; ptr = USB_ADD_BYTES(ptr, io_len); @@ -2066,7 +2056,7 @@ usb_fifo_get_data_linear(struct usb_fifo *f, void *ptr, io_len = MIN(len, m->cur_data_len); - bcopy(m->cur_data_ptr, ptr, io_len); + memcpy(ptr, m->cur_data_ptr, io_len); len -= io_len; ptr = USB_ADD_BYTES(ptr, io_len); diff --git a/freebsd/sys/dev/usb/usb_dev.h b/freebsd/sys/dev/usb/usb_dev.h index aa9197f5..9a7cf210 100644 --- a/freebsd/sys/dev/usb/usb_dev.h +++ b/freebsd/sys/dev/usb/usb_dev.h @@ -82,6 +82,7 @@ struct usb_cdev_refdata { uint8_t is_write; /* location has write access */ uint8_t is_uref; /* USB refcount decr. needed */ uint8_t is_usbfs; /* USB-FS is active */ + uint8_t do_unlock; /* USB enum unlock needed */ }; struct usb_fs_privdata { diff --git a/freebsd/sys/dev/usb/usb_device.c b/freebsd/sys/dev/usb/usb_device.c index a2db2727..a38e3d1c 100644 --- a/freebsd/sys/dev/usb/usb_device.c +++ b/freebsd/sys/dev/usb/usb_device.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -90,7 +89,7 @@ static void usb_init_endpoint(struct usb_device *, uint8_t, struct usb_endpoint *); static void usb_unconfigure(struct usb_device *, uint8_t); static void usb_detach_device_sub(struct usb_device *, device_t *, - uint8_t); + char **, uint8_t); static uint8_t usb_probe_and_attach_sub(struct usb_device *, struct usb_attach_arg *); static void usb_init_attach_arg(struct usb_device *, @@ -100,13 +99,13 @@ static void usb_suspend_resume_sub(struct usb_device *, device_t, static void usbd_clear_stall_proc(struct usb_proc_msg *_pm); 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_UGEN +#if USB_HAVE_DEVCTL static void usb_notify_addq(const char *type, struct usb_device *); +#endif +#if USB_HAVE_UGEN static void usb_fifo_free_wrap(struct usb_device *, uint8_t, uint8_t); -static struct cdev *usb_make_dev(struct usb_device *, int, int); static void usb_cdev_create(struct usb_device *); static void usb_cdev_free(struct usb_device *); -static void usb_cdev_cleanup(void *); #endif /* This variable is global to allow easy access to it: */ @@ -115,7 +114,7 @@ int usb_template = 0; #ifndef __rtems__ TUNABLE_INT("hw.usb.usb_template", &usb_template); -SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RW, +SYSCTL_INT(_hw_usb, OID_AUTO, template, CTLFLAG_RW | CTLFLAG_TUN, &usb_template, 0, "Selected USB device side template"); #endif /* __rtems__ */ @@ -126,11 +125,11 @@ static int usb_lang_mask = 0x00FF; #ifndef __rtems__ TUNABLE_INT("hw.usb.usb_lang_id", &usb_lang_id); -SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_id, CTLFLAG_RW, +SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_id, CTLFLAG_RW | CTLFLAG_TUN, &usb_lang_id, 0, "Preferred USB language ID"); TUNABLE_INT("hw.usb.usb_lang_mask", &usb_lang_mask); -SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_mask, CTLFLAG_RW, +SYSCTL_INT(_hw_usb, OID_AUTO, usb_lang_mask, CTLFLAG_RW | CTLFLAG_TUN, &usb_lang_mask, 0, "Preferred USB language mask"); #endif /* __rtems__ */ @@ -450,13 +449,8 @@ usb_unconfigure(struct usb_device *udev, uint8_t flag) { uint8_t do_unlock; - /* automatic locking */ - if (usbd_enum_is_locked(udev)) { - do_unlock = 0; - } else { - do_unlock = 1; - usbd_enum_lock(udev); - } + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); /* detach all interface drivers */ usb_detach_device(udev, USB_IFACE_INDEX_ANY, flag); @@ -519,13 +513,8 @@ usbd_set_config_index(struct usb_device *udev, uint8_t index) DPRINTFN(6, "udev=%p index=%d\n", udev, index); - /* automatic locking */ - if (usbd_enum_is_locked(udev)) { - do_unlock = 0; - } else { - do_unlock = 1; - usbd_enum_lock(udev); - } + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); usb_unconfigure(udev, 0); @@ -757,10 +746,13 @@ usb_config_parse(struct usb_device *udev, uint8_t iface_index, uint8_t cmd) if (do_init) { /* setup the USB interface structure */ iface->idesc = id; - /* default setting */ - iface->parent_iface_index = USB_IFACE_INDEX_ANY; /* set alternate index */ iface->alt_index = alt_index; + /* set default interface parent */ + if (iface_index == USB_IFACE_INDEX_ANY) { + iface->parent_iface_index = + USB_IFACE_INDEX_ANY; + } } DPRINTFN(5, "found idesc nendpt=%d\n", id->bNumEndpoints); @@ -875,13 +867,9 @@ usbd_set_alt_interface_index(struct usb_device *udev, usb_error_t err; uint8_t do_unlock; - /* automatic locking */ - if (usbd_enum_is_locked(udev)) { - do_unlock = 0; - } else { - do_unlock = 1; - usbd_enum_lock(udev); - } + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); + if (iface == NULL) { err = USB_ERR_INVAL; goto done; @@ -918,7 +906,6 @@ usbd_set_alt_interface_index(struct usb_device *udev, done: if (do_unlock) usbd_enum_unlock(udev); - return (err); } @@ -1039,9 +1026,10 @@ usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index) *------------------------------------------------------------------------*/ static void usb_detach_device_sub(struct usb_device *udev, device_t *ppdev, - uint8_t flag) + char **ppnpinfo, uint8_t flag) { device_t dev; + char *pnpinfo; int err; dev = *ppdev; @@ -1073,11 +1061,17 @@ usb_detach_device_sub(struct usb_device *udev, device_t *ppdev, goto error; } } + + pnpinfo = *ppnpinfo; + if (pnpinfo != NULL) { + *ppnpinfo = NULL; + free(pnpinfo, M_USBDEV); + } return; error: /* Detach is not allowed to fail in the USB world */ - panic("A USB driver would not detach\n"); + panic("usb_detach_device_sub: A USB driver would not detach\n"); } /*------------------------------------------------------------------------* @@ -1126,7 +1120,8 @@ usb_detach_device(struct usb_device *udev, uint8_t iface_index, /* looks like the end of the USB interfaces */ break; } - usb_detach_device_sub(udev, &iface->subdev, flag); + usb_detach_device_sub(udev, &iface->subdev, + &iface->pnpinfo, flag); } } @@ -1228,17 +1223,20 @@ usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, { struct usb_interface *iface; + if (udev == NULL) { + /* nothing to do */ + return; + } iface = usbd_get_iface(udev, iface_index); - if (iface) { + if (iface != NULL) iface->parent_iface_index = parent_index; - } } static void usb_init_attach_arg(struct usb_device *udev, struct usb_attach_arg *uaa) { - bzero(uaa, sizeof(*uaa)); + memset(uaa, 0, sizeof(*uaa)); uaa->device = udev; uaa->usb_mode = udev->flags.usb_mode; @@ -1278,13 +1276,8 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) DPRINTF("udev == NULL\n"); return (USB_ERR_INVAL); } - /* automatic locking */ - if (usbd_enum_is_locked(udev)) { - do_unlock = 0; - } else { - do_unlock = 1; - usbd_enum_lock(udev); - } + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); if (udev->curr_config_index == USB_UNCONFIG_INDEX) { /* do nothing - no configuration has been set */ @@ -1294,6 +1287,21 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) usb_init_attach_arg(udev, &uaa); + /* + * If the whole USB device is targeted, invoke the USB event + * handler(s): + */ + if (iface_index == USB_IFACE_INDEX_ANY) { + + EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa); + + if (uaa.dev_state != UAA_DEV_READY) { + /* leave device unconfigured */ + usb_unconfigure(udev, 0); + goto done; + } + } + /* Check if only one interface should be probed: */ if (iface_index != USB_IFACE_INDEX_ANY) { i = iface_index; @@ -1331,7 +1339,6 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) uaa.info.bIfaceIndex = i; uaa.info.bIfaceNum = iface->idesc->bInterfaceNumber; - uaa.use_generic = 0; uaa.driver_info = 0; /* reset driver_info */ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", @@ -1341,32 +1348,22 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index) uaa.info.bIfaceIndex, uaa.info.bIfaceNum); - /* try specific interface drivers first */ - - if (usb_probe_and_attach_sub(udev, &uaa)) { - /* ignore */ - } - /* try generic interface drivers last */ - - uaa.use_generic = 1; - uaa.driver_info = 0; /* reset driver_info */ - - if (usb_probe_and_attach_sub(udev, &uaa)) { - /* ignore */ - } - } + usb_probe_and_attach_sub(udev, &uaa); - if (uaa.temp_dev) { - /* remove the last created child; it is unused */ - - if (device_delete_child(udev->parent_dev, uaa.temp_dev)) { + /* + * Remove the leftover child, if any, to enforce that + * a new nomatch devd event is generated for the next + * interface if no driver is found: + */ + if (uaa.temp_dev == NULL) + continue; + if (device_delete_child(udev->parent_dev, uaa.temp_dev)) DPRINTFN(0, "device delete child failed\n"); - } + uaa.temp_dev = NULL; } done: if (do_unlock) usbd_enum_unlock(udev); - return (0); } @@ -1490,12 +1487,12 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *adev; struct usb_device *hub; uint8_t *scratch_ptr; - size_t scratch_size; usb_error_t err; uint8_t device_index; uint8_t config_index; uint8_t config_quirk; uint8_t set_config_failed; + uint8_t do_unlock; DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, " "port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n", @@ -1530,11 +1527,8 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, return (NULL); } /* initialise our SX-lock */ - sx_init_flags(&udev->ctrl_sx, "USB device SX lock", SX_DUPOK); - - /* initialise our SX-lock */ sx_init_flags(&udev->enum_sx, "USB config SX lock", SX_DUPOK); - sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_DUPOK); + sx_init_flags(&udev->sr_sx, "USB suspend and resume SX lock", SX_NOWITNESS); cv_init(&udev->ctrlreq_cv, "WCTRL"); cv_init(&udev->ref_cv, "UGONE"); @@ -1618,10 +1612,12 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, LIST_INIT(&udev->pd_list); /* Create the control endpoint device */ - udev->ctrl_dev = usb_make_dev(udev, 0, FREAD|FWRITE); + udev->ctrl_dev = usb_make_dev(udev, NULL, 0, 0, + FREAD|FWRITE, UID_ROOT, GID_OPERATOR, 0600); /* Create a link from /dev/ugenX.X to the default endpoint */ - make_dev_alias(udev->ctrl_dev, "%s", udev->ugen_name); + if (udev->ctrl_dev != NULL) + make_dev_alias(udev->ctrl_dev->cdev, "%s", udev->ugen_name); #endif /* Initialise device */ if (bus->methods->device_init != NULL) { @@ -1712,8 +1708,11 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, * device descriptor. If no strings are present there we * simply disable all USB strings. */ - scratch_ptr = udev->bus->scratch[0].data; - scratch_size = sizeof(udev->bus->scratch[0].data); + + /* Protect scratch area */ + do_unlock = usbd_enum_lock(udev); + + scratch_ptr = udev->scratch.data; if (udev->ddesc.iManufacturer || udev->ddesc.iProduct || @@ -1738,7 +1737,7 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, mask = usb_lang_mask; /* align length correctly */ - scratch_ptr[0] &= ~1; + scratch_ptr[0] &= ~1U; /* fix compiler warning */ langid = 0; @@ -1759,6 +1758,9 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus, udev->langid = langid; } + if (do_unlock) + usbd_enum_unlock(udev); + /* assume 100mA bus powered for now. Changed when configured. */ udev->power = USB_MIN_POWER; /* fetch the vendor and product strings from the device */ @@ -1842,12 +1844,20 @@ repeat_set_config: } } } -#ifndef __rtems__ - EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa); -#endif /* __rtems__ */ - if (uaa.dev_state != UAA_DEV_READY) { - /* leave device unconfigured */ - usb_unconfigure(udev, 0); + if (set_config_failed == 0 && config_index == 0 && + usb_test_quirk(&uaa, UQ_MSC_NO_SYNC_CACHE) == 0 && + usb_test_quirk(&uaa, UQ_MSC_NO_GETMAXLUN) == 0) { + + /* + * Try to figure out if there are any MSC quirks we + * should apply automatically: + */ + err = usb_msc_auto_quirk(udev, 0); + + if (err != 0) { + set_config_failed = 1; + goto repeat_set_config; + } } config_done: @@ -1866,7 +1876,9 @@ config_done: printf("%s: <%s> at %s\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(udev->bus->bdev)); +#endif +#if USB_HAVE_DEVCTL usb_notify_addq("ATTACH", udev); #endif done: @@ -1881,11 +1893,12 @@ done: } #if USB_HAVE_UGEN -static struct cdev * -usb_make_dev(struct usb_device *udev, int ep, int mode) +struct usb_fs_privdata * +usb_make_dev(struct usb_device *udev, const char *devname, int ep, + int fi, int rwmode, uid_t uid, gid_t gid, int mode) { struct usb_fs_privdata* pd; - char devname[20]; + char buffer[32]; /* Store information to locate ourselves again later */ pd = malloc(sizeof(struct usb_fs_privdata), M_USBDEV, @@ -1893,16 +1906,39 @@ usb_make_dev(struct usb_device *udev, int ep, int mode) pd->bus_index = device_get_unit(udev->bus->bdev); pd->dev_index = udev->device_index; pd->ep_addr = ep; - pd->mode = mode; + pd->fifo_index = fi; + pd->mode = rwmode; /* Now, create the device itself */ - snprintf(devname, sizeof(devname), "%u.%u.%u", - pd->bus_index, pd->dev_index, pd->ep_addr); - pd->cdev = make_dev(&usb_devsw, 0, UID_ROOT, - GID_OPERATOR, 0600, USB_DEVICE_DIR "/%s", devname); + if (devname == NULL) { + devname = buffer; + snprintf(buffer, sizeof(buffer), USB_DEVICE_DIR "/%u.%u.%u", + pd->bus_index, pd->dev_index, pd->ep_addr); + } + + pd->cdev = make_dev(&usb_devsw, 0, uid, gid, mode, "%s", devname); + + if (pd->cdev == NULL) { + DPRINTFN(0, "Failed to create device %s\n", devname); + free(pd, M_USBDEV); + return (NULL); + } + + /* XXX setting si_drv1 and creating the device is not atomic! */ pd->cdev->si_drv1 = pd; - return (pd->cdev); + return (pd); +} + +void +usb_destroy_dev(struct usb_fs_privdata *pd) +{ + if (pd == NULL) + return; + + destroy_dev(pd->cdev); + + free(pd, M_USBDEV); } static void @@ -1912,7 +1948,6 @@ usb_cdev_create(struct usb_device *udev) struct usb_endpoint_descriptor *ed; struct usb_descriptor *desc; struct usb_fs_privdata* pd; - struct cdev *dev; int inmode, outmode, inmask, outmask, mode; uint8_t ep; @@ -1954,14 +1989,16 @@ usb_cdev_create(struct usb_device *udev) /* Create all available endpoints except EP0 */ for (ep = 1; ep < 16; ep++) { - mode = inmask & (1 << ep) ? inmode : 0; - mode |= outmask & (1 << ep) ? outmode : 0; + mode = (inmask & (1 << ep)) ? inmode : 0; + mode |= (outmask & (1 << ep)) ? outmode : 0; if (mode == 0) continue; /* no IN or OUT endpoint */ - dev = usb_make_dev(udev, ep, mode); - pd = dev->si_drv1; - LIST_INSERT_HEAD(&udev->pd_list, pd, pd_next); + pd = usb_make_dev(udev, NULL, ep, 0, + mode, UID_ROOT, GID_OPERATOR, 0600); + + if (pd != NULL) + LIST_INSERT_HEAD(&udev->pd_list, pd, pd_next); } } @@ -1969,25 +2006,16 @@ static void usb_cdev_free(struct usb_device *udev) { struct usb_fs_privdata* pd; - struct cdev* pcdev; DPRINTFN(2, "Freeing device nodes\n"); while ((pd = LIST_FIRST(&udev->pd_list)) != NULL) { KASSERT(pd->cdev->si_drv1 == pd, ("privdata corrupt")); - pcdev = pd->cdev; - pd->cdev = NULL; LIST_REMOVE(pd, pd_next); - if (pcdev != NULL) - destroy_dev_sched_cb(pcdev, usb_cdev_cleanup, pd); - } -} -static void -usb_cdev_cleanup(void* arg) -{ - free(arg, M_USBDEV); + usb_destroy_dev(pd); + } } #endif @@ -2012,9 +2040,11 @@ usb_free_device(struct usb_device *udev, uint8_t flag) bus = udev->bus; usb_set_device_state(udev, USB_STATE_DETACHED); -#if USB_HAVE_UGEN +#if USB_HAVE_DEVCTL usb_notify_addq("DETACH", udev); +#endif +#if USB_HAVE_UGEN printf("%s: <%s> at %s (disconnected)\n", udev->ugen_name, usb_get_manufacturer(udev), device_get_nameunit(bus->bdev)); @@ -2041,8 +2071,7 @@ usb_free_device(struct usb_device *udev, uint8_t flag) } mtx_unlock(&usb_ref_lock); - destroy_dev_sched_cb(udev->ctrl_dev, usb_cdev_cleanup, - udev->ctrl_dev->si_drv1); + usb_destroy_dev(udev->ctrl_dev); #endif if (udev->flags.usb_mode == USB_MODE_DEVICE) { @@ -2068,7 +2097,6 @@ usb_free_device(struct usb_device *udev, uint8_t flag) &udev->cs_msg[0], &udev->cs_msg[1]); USB_BUS_UNLOCK(udev->bus); - sx_destroy(&udev->ctrl_sx); sx_destroy(&udev->enum_sx); sx_destroy(&udev->sr_sx); @@ -2231,9 +2259,13 @@ usbd_set_device_strings(struct usb_device *udev) size_t temp_size; uint16_t vendor_id; uint16_t product_id; + uint8_t do_unlock; + + /* Protect scratch area */ + do_unlock = usbd_enum_lock(udev); - temp_ptr = (char *)udev->bus->scratch[0].data; - temp_size = sizeof(udev->bus->scratch[0].data); + temp_ptr = (char *)udev->scratch.data; + temp_size = sizeof(udev->scratch.data); vendor_id = UGETW(udd->idVendor); product_id = UGETW(udd->idProduct); @@ -2288,6 +2320,9 @@ usbd_set_device_strings(struct usb_device *udev) snprintf(temp_ptr, temp_size, "product 0x%04x", product_id); udev->product = strdup(temp_ptr, M_USB); } + + if (do_unlock) + usbd_enum_unlock(udev); } /* @@ -2350,8 +2385,22 @@ uint8_t usb_test_quirk(const struct usb_attach_arg *uaa, uint16_t quirk) { uint8_t found; + uint8_t x; + + if (quirk == UQ_NONE) + return (0); + + /* search the automatic per device quirks first */ + + for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { + if (uaa->device->autoQuirk[x] == quirk) + return (1); + } + + /* search global quirk table, if any */ found = (usb_test_quirk_p) (&uaa->info, quirk); + return (found); } @@ -2381,7 +2430,7 @@ usbd_get_device_index(struct usb_device *udev) return (udev->device_index); } -#if USB_HAVE_UGEN +#if USB_HAVE_DEVCTL /*------------------------------------------------------------------------* * usb_notify_addq * @@ -2417,29 +2466,37 @@ usb_notify_addq_compat(const char *type, struct usb_device *udev) /* String it all together. */ snprintf(data, buf_size, "%s" +#if USB_HAVE_UGEN "%s " +#endif + "at port=%u " "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " - "at " - "port=%u " - "on " - "%s\n", +#if USB_HAVE_UGEN + "on %s\n" +#endif + "", ntype, +#if USB_HAVE_UGEN udev->ugen_name, +#endif + udev->port_no, UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, usb_get_serial(udev), - UGETW(udev->ddesc.bcdDevice), - udev->port_no, - udev->parent_hub != NULL ? + UGETW(udev->ddesc.bcdDevice) +#if USB_HAVE_UGEN + , udev->parent_hub != NULL ? udev->parent_hub->ugen_name : - device_get_nameunit(device_get_parent(udev->bus->bdev))); + device_get_nameunit(device_get_parent(udev->bus->bdev)) +#endif + ); devctl_queue_data(data); } @@ -2459,7 +2516,10 @@ usb_notify_addq(const char *type, struct usb_device *udev) /* announce the device */ sb = sbuf_new_auto(); sbuf_printf(sb, +#if USB_HAVE_UGEN + "ugen=%s " "cdev=%s " +#endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " @@ -2468,8 +2528,14 @@ usb_notify_addq(const char *type, struct usb_device *udev) "release=0x%04x " "mode=%s " "port=%u " - "parent=%s\n", +#if USB_HAVE_UGEN + "parent=%s" +#endif + "", +#if USB_HAVE_UGEN udev->ugen_name, + udev->ugen_name, +#endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, @@ -2477,10 +2543,13 @@ usb_notify_addq(const char *type, struct usb_device *udev) usb_get_serial(udev), UGETW(udev->ddesc.bcdDevice), (udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", - udev->port_no, - udev->parent_hub != NULL ? - udev->parent_hub->ugen_name : - device_get_nameunit(device_get_parent(udev->bus->bdev))); + udev->port_no +#if USB_HAVE_UGEN + , udev->parent_hub != NULL ? + udev->parent_hub->ugen_name : + device_get_nameunit(device_get_parent(udev->bus->bdev)) +#endif + ); sbuf_finish(sb); devctl_notify("USB", "DEVICE", type, sbuf_data(sb)); sbuf_delete(sb); @@ -2495,7 +2564,10 @@ usb_notify_addq(const char *type, struct usb_device *udev) sb = sbuf_new_auto(); sbuf_printf(sb, +#if USB_HAVE_UGEN + "ugen=%s " "cdev=%s " +#endif "vendor=0x%04x " "product=0x%04x " "devclass=0x%02x " @@ -2507,8 +2579,11 @@ usb_notify_addq(const char *type, struct usb_device *udev) "endpoints=%d " "intclass=0x%02x " "intsubclass=0x%02x " - "intprotocol=0x%02x\n", + "intprotocol=0x%02x", +#if USB_HAVE_UGEN + udev->ugen_name, udev->ugen_name, +#endif UGETW(udev->ddesc.idVendor), UGETW(udev->ddesc.idProduct), udev->ddesc.bDeviceClass, @@ -2526,7 +2601,9 @@ usb_notify_addq(const char *type, struct usb_device *udev) sbuf_delete(sb); } } +#endif +#if USB_HAVE_UGEN /*------------------------------------------------------------------------* * usb_fifo_free_wrap * @@ -2633,11 +2710,17 @@ usbd_device_attached(struct usb_device *udev) return (udev->state > USB_STATE_DETACHED); } -/* The following function locks enumerating the given USB device. */ - -void +/* + * The following function locks enumerating the given USB device. If + * the lock is already grabbed this function returns zero. Else a + * non-zero value is returned. + */ +uint8_t usbd_enum_lock(struct usb_device *udev) { + if (sx_xlocked(&udev->enum_sx)) + return (0); + sx_xlock(&udev->enum_sx); sx_xlock(&udev->sr_sx); /* @@ -2646,6 +2729,7 @@ usbd_enum_lock(struct usb_device *udev) * locked multiple times. */ mtx_lock(&Giant); + return (1); } /* The following function unlocks enumerating the given USB device. */ @@ -2691,3 +2775,51 @@ usbd_enum_is_locked(struct usb_device *udev) { return (sx_xlocked(&udev->enum_sx)); } + +/* + * The following function is used to set the per-interface specific + * plug and play information. The string referred to by the pnpinfo + * argument can safely be freed after calling this function. The + * pnpinfo of an interface will be reset at device detach or when + * passing a NULL argument to this function. This function + * returns zero on success, else a USB_ERR_XXX failure code. + */ + +usb_error_t +usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo) +{ + struct usb_interface *iface; + + iface = usbd_get_iface(udev, iface_index); + if (iface == NULL) + return (USB_ERR_INVAL); + + if (iface->pnpinfo != NULL) { + free(iface->pnpinfo, M_USBDEV); + iface->pnpinfo = NULL; + } + + if (pnpinfo == NULL || pnpinfo[0] == 0) + return (0); /* success */ + + iface->pnpinfo = strdup(pnpinfo, M_USBDEV); + if (iface->pnpinfo == NULL) + return (USB_ERR_NOMEM); + + return (0); /* success */ +} + +usb_error_t +usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk) +{ + uint8_t x; + + for (x = 0; x != USB_MAX_AUTO_QUIRK; x++) { + if (udev->autoQuirk[x] == 0 || + udev->autoQuirk[x] == quirk) { + udev->autoQuirk[x] = quirk; + return (0); /* success */ + } + } + return (USB_ERR_NOMEM); +} diff --git a/freebsd/sys/dev/usb/usb_device.h b/freebsd/sys/dev/usb/usb_device.h index c8bc5eb9..03ddf1e6 100644 --- a/freebsd/sys/dev/usb/usb_device.h +++ b/freebsd/sys/dev/usb/usb_device.h @@ -27,8 +27,18 @@ #ifndef _USB_DEVICE_H_ #define _USB_DEVICE_H_ -struct usb_symlink; /* UGEN */ +#ifndef USB_GLOBAL_INCLUDE_FILE +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_transfer.h> +#endif + +struct usb_bus_methods; +struct usb_config_descriptor; struct usb_device; /* linux compat */ +struct usb_fs_privdata; +struct usb_hw_ep_profile; +struct usb_symlink; /* UGEN */ #define USB_CTRL_XFER_MAX 2 @@ -107,13 +117,70 @@ struct usb_power_save { }; /* + * The following structure is used when trying to allocate hardware + * endpoints for an USB configuration in USB device side mode. + */ +struct usb_hw_ep_scratch_sub { + const struct usb_hw_ep_profile *pf; + uint16_t max_frame_size; + uint8_t hw_endpoint_out; + uint8_t hw_endpoint_in; + uint8_t needs_ep_type; + uint8_t needs_in:1; + uint8_t needs_out:1; +}; + +/* + * The following structure is used when trying to allocate hardware + * endpoints for an USB configuration in USB device side mode. + */ +struct usb_hw_ep_scratch { + struct usb_hw_ep_scratch_sub ep[USB_EP_MAX]; + struct usb_hw_ep_scratch_sub *ep_max; + struct usb_config_descriptor *cd; + struct usb_device *udev; + struct usb_bus_methods *methods; + uint8_t bmOutAlloc[(USB_EP_MAX + 15) / 16]; + uint8_t bmInAlloc[(USB_EP_MAX + 15) / 16]; +}; + +/* + * The following structure is used when generating USB descriptors + * from USB templates. + */ +struct usb_temp_setup { + void *buf; + usb_size_t size; + enum usb_dev_speed usb_speed; + uint8_t self_powered; + uint8_t bNumEndpoints; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bConfigurationValue; + usb_error_t err; +}; + +/* + * The scratch area for USB devices. Access to this structure is + * protected by the enumeration SX lock. + */ +union usb_device_scratch { + struct usb_hw_ep_scratch hw_ep_scratch[1]; + struct usb_temp_setup temp_setup[1]; + struct { + struct usb_xfer dummy; + struct usb_setup_params parm; + } xfer_setup[1]; + uint8_t data[255]; +}; + +/* * The following structure defines an USB device. There exists one of * these structures for every USB device. */ struct usb_device { struct usb_clear_stall_msg cs_msg[2]; /* generic clear stall * messages */ - struct sx ctrl_sx; struct sx enum_sx; struct sx sr_sx; struct mtx device_mtx; @@ -135,7 +202,7 @@ struct usb_device { #if USB_HAVE_UGEN struct usb_fifo *fifo[USB_FIFO_MAX]; struct usb_symlink *ugen_symlink; /* our generic symlink */ - struct cdev *ctrl_dev; /* Control Endpoint 0 device node */ + struct usb_fs_privdata *ctrl_dev; /* Control Endpoint 0 device node */ LIST_HEAD(,usb_fs_privdata) pd_list; char ugen_name[20]; /* name of ugenX.X device */ #endif @@ -187,6 +254,12 @@ struct usb_device { struct usb_host_endpoint *linux_endpoint_end; uint16_t devnum; #endif + + uint32_t clear_stall_errors; /* number of clear-stall failures */ + + uint16_t autoQuirk[USB_MAX_AUTO_QUIRK]; /* dynamic quirks */ + + union usb_device_scratch scratch; }; /* globals */ @@ -200,6 +273,11 @@ struct usb_device *usb_alloc_device(device_t parent_dev, struct usb_bus *bus, struct usb_device *parent_hub, uint8_t depth, uint8_t port_index, uint8_t port_no, enum usb_dev_speed speed, enum usb_hc_mode mode); +#if USB_HAVE_UGEN +struct usb_fs_privdata *usb_make_dev(struct usb_device *, const char *, + int, int, int, uid_t, gid_t, int); +void usb_destroy_dev(struct usb_fs_privdata *); +#endif usb_error_t usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index); void usb_detach_device(struct usb_device *, uint8_t, uint8_t); @@ -218,7 +296,7 @@ struct usb_endpoint *usb_endpoint_foreach(struct usb_device *udev, struct usb_en void usb_set_device_state(struct usb_device *, enum usb_dev_state); enum usb_dev_state usb_get_device_state(struct usb_device *); -void usbd_enum_lock(struct usb_device *); +uint8_t usbd_enum_lock(struct usb_device *); void usbd_enum_unlock(struct usb_device *); void usbd_sr_lock(struct usb_device *); void usbd_sr_unlock(struct usb_device *); diff --git a/freebsd/sys/dev/usb/usb_dynamic.c b/freebsd/sys/dev/usb/usb_dynamic.c index cb54d10f..31e78225 100644 --- a/freebsd/sys/dev/usb/usb_dynamic.c +++ b/freebsd/sys/dev/usb/usb_dynamic.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> diff --git a/freebsd/sys/dev/usb/usb_error.c b/freebsd/sys/dev/usb/usb_error.c index 4505490d..9f6e4569 100644 --- a/freebsd/sys/dev/usb/usb_error.c +++ b/freebsd/sys/dev/usb/usb_error.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> diff --git a/freebsd/sys/dev/usb/usb_freebsd.h b/freebsd/sys/dev/usb/usb_freebsd.h index 3045ec32..8f9bb4c6 100644 --- a/freebsd/sys/dev/usb/usb_freebsd.h +++ b/freebsd/sys/dev/usb/usb_freebsd.h @@ -34,6 +34,7 @@ /* Default USB configuration */ #ifndef __rtems__ #define USB_HAVE_UGEN 1 +#define USB_HAVE_DEVCTL 1 #define USB_HAVE_BUSDMA 1 #define USB_HAVE_COMPAT_LINUX 1 #define USB_HAVE_USER_IO 1 @@ -41,6 +42,7 @@ #define USB_HAVE_TT_SUPPORT 1 #define USB_HAVE_POWERD 1 #define USB_HAVE_MSCTEST 1 +#define USB_HAVE_PF 1 #endif /* __rtems__ */ #define USB_TD_GET_PROC(td) (td)->td_proc @@ -58,6 +60,9 @@ #define USB_HUB_MAX_DEPTH 5 #define USB_EP0_BUFSIZE 1024 /* bytes */ +#define USB_CS_RESET_LIMIT 20 /* failures = 20 * 50 ms = 1sec */ + +#define USB_MAX_AUTO_QUIRK 4 /* maximum number of dynamic quirks */ typedef uint32_t usb_timeout_t; /* milliseconds */ typedef uint32_t usb_frlength_t; /* bytes */ diff --git a/freebsd/sys/dev/usb/usb_generic.c b/freebsd/sys/dev/usb/usb_generic.c index 60b24794..4e439e15 100644 --- a/freebsd/sys/dev/usb/usb_generic.c +++ b/freebsd/sys/dev/usb/usb_generic.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -130,9 +129,8 @@ struct usb_fifo_methods usb_ugen_methods = { static int ugen_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic"); -SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW, &ugen_debug, +SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &ugen_debug, 0, "Debug level"); - TUNABLE_INT("hw.usb.ugen.debug", &ugen_debug); #endif @@ -243,7 +241,7 @@ ugen_open_pipe_write(struct usb_fifo *f) /* transfers are already opened */ return (0); } - bzero(usb_config, sizeof(usb_config)); + memset(usb_config, 0, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; @@ -311,7 +309,7 @@ ugen_open_pipe_read(struct usb_fifo *f) /* transfers are already opened */ return (0); } - bzero(usb_config, sizeof(usb_config)); + memset(usb_config, 0, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; @@ -717,13 +715,20 @@ ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) return (error); } +/* + * This function is called having the enumeration SX locked which + * protects the scratch area used. + */ static int ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { - void *ptr = f->udev->bus->scratch[0].data; - uint16_t size = sizeof(f->udev->bus->scratch[0].data); + void *ptr; + uint16_t size; int error; + ptr = f->udev->scratch.data; + size = sizeof(f->udev->scratch.data); + if (usbd_req_get_string_desc(f->udev, NULL, ptr, size, ugd->ugd_lang_id, ugd->ugd_string_index)) { error = EINVAL; @@ -955,18 +960,22 @@ ugen_re_enumerate(struct usb_fifo *f) } if (udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ + 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)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } - if (udev->re_enumerate_wait == 0) { - udev->re_enumerate_wait = 1; - usb_needs_explore(udev->bus, 0); - } + /* start re-enumeration of device */ + usbd_start_re_enumerate(udev); return (0); } @@ -1396,10 +1405,12 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) } u; struct usb_endpoint *ep; struct usb_endpoint_descriptor *ed; + struct usb_xfer *xfer; int error = 0; uint8_t iface_index; uint8_t isread; uint8_t ep_index; + uint8_t pre_scale; u.addr = addr; @@ -1421,11 +1432,11 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) case USB_FS_START: error = ugen_fs_copy_in(f, u.pstart->ep_index); - if (error) { + if (error) break; - } mtx_lock(f->priv_mtx); - usbd_transfer_start(f->fs_xfer[u.pstart->ep_index]); + xfer = f->fs_xfer[u.pstart->ep_index]; + usbd_transfer_start(xfer); mtx_unlock(f->priv_mtx); break; @@ -1435,7 +1446,19 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) break; } mtx_lock(f->priv_mtx); - usbd_transfer_stop(f->fs_xfer[u.pstop->ep_index]); + xfer = f->fs_xfer[u.pstart->ep_index]; + if (usbd_transfer_pending(xfer)) { + usbd_transfer_stop(xfer); + /* + * Check if the USB transfer was stopped + * before it was even started. Else a cancel + * callback will be pending. + */ + if (!xfer->flags_int.transferring) { + ugen_fs_set_complete(xfer->priv_sc, + USB_P2U(xfer->priv_fifo)); + } + } mtx_unlock(f->priv_mtx); break; @@ -1451,6 +1474,12 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; } + if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { + pre_scale = 1; + u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; + } else { + pre_scale = 0; + } if (u.popen->max_frames > USB_FS_MAX_FRAMES) { u.popen->max_frames = USB_FS_MAX_FRAMES; break; @@ -1471,13 +1500,15 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) } iface_index = ep->iface_index; - bzero(usb_config, sizeof(usb_config)); + memset(usb_config, 0, sizeof(usb_config)); usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; + if (pre_scale != 0) + usb_config[0].flags.pre_scale_frames = 1; usb_config[0].callback = &ugen_ctrl_fs_callback; usb_config[0].timeout = 0; /* no timeout */ usb_config[0].frames = u.popen->max_frames; @@ -1519,6 +1550,10 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) f->fs_xfer[u.popen->ep_index]->max_frame_size; u.popen->max_bufsize = f->fs_xfer[u.popen->ep_index]->max_data_length; + /* update number of frames */ + u.popen->max_frames = + f->fs_xfer[u.popen->ep_index]->nframes; + /* store index of endpoint */ f->fs_xfer[u.popen->ep_index]->priv_fifo = ((uint8_t *)0) + u.popen->ep_index; } else { @@ -2139,7 +2174,16 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) break; } + /* + * Detach the currently attached driver. + */ usb_detach_device(f->udev, n, 0); + + /* + * Set parent to self, this should keep attach away + * until the next set configuration event. + */ + usbd_set_parent_iface(f->udev, n, n); break; case USB_SET_POWER_MODE: diff --git a/freebsd/sys/dev/usb/usb_handle_request.c b/freebsd/sys/dev/usb/usb_handle_request.c index 97d30394..d1df80a1 100644 --- a/freebsd/sys/dev/usb/usb_handle_request.c +++ b/freebsd/sys/dev/usb/usb_handle_request.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -148,6 +147,7 @@ usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) { struct usb_device *udev = xfer->xroot->udev; usb_error_t err = 0; + uint8_t do_unlock; /* * We need to protect against other threads doing probe and @@ -155,7 +155,8 @@ usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) */ USB_XFER_UNLOCK(xfer); - usbd_enum_lock(udev); + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); if (conf_no == USB_UNCONFIG_NO) { conf_no = USB_UNCONFIG_INDEX; @@ -178,7 +179,8 @@ usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) goto done; } done: - usbd_enum_unlock(udev); + if (do_unlock) + usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (err); } @@ -190,13 +192,8 @@ usb_check_alt_setting(struct usb_device *udev, uint8_t do_unlock; usb_error_t err = 0; - /* automatic locking */ - if (usbd_enum_is_locked(udev)) { - do_unlock = 0; - } else { - do_unlock = 1; - usbd_enum_lock(udev); - } + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) err = USB_ERR_INVAL; @@ -225,6 +222,7 @@ usb_handle_iface_request(struct usb_xfer *xfer, int error; uint8_t iface_index; uint8_t temp_state; + uint8_t do_unlock; if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { iface_index = req.wIndex[0]; /* unicast */ @@ -238,7 +236,8 @@ usb_handle_iface_request(struct usb_xfer *xfer, */ USB_XFER_UNLOCK(xfer); - usbd_enum_lock(udev); + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); error = ENXIO; @@ -354,17 +353,20 @@ tr_repeat: goto tr_stalled; } tr_valid: - usbd_enum_unlock(udev); + if (do_unlock) + usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (0); tr_short: - usbd_enum_unlock(udev); + if (do_unlock) + usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (USB_ERR_SHORT_XFER); tr_stalled: - usbd_enum_unlock(udev); + if (do_unlock) + usbd_enum_unlock(udev); USB_XFER_LOCK(xfer); return (USB_ERR_STALLED); } @@ -440,8 +442,10 @@ usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) USB_BUS_UNLOCK(bus); +#if USB_HAVE_POWERD /* In case we are out of sync, update the power state. */ usb_bus_power_update(udev->bus); +#endif return (0); /* success */ } @@ -467,7 +471,6 @@ usb_handle_request(struct usb_xfer *xfer) uint16_t rem; /* data remainder */ uint16_t max_len; /* max fragment length */ uint16_t wValue; - uint16_t wIndex; uint8_t state; uint8_t is_complete = 1; usb_error_t err; @@ -533,11 +536,10 @@ usb_handle_request(struct usb_xfer *xfer) /* get some request fields decoded */ wValue = UGETW(req.wValue); - wIndex = UGETW(req.wIndex); DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, - req.bRequest, wValue, wIndex, off, rem, state); + req.bRequest, wValue, UGETW(req.wIndex), off, rem, state); /* demultiplex the control request */ diff --git a/freebsd/sys/dev/usb/usb_hid.c b/freebsd/sys/dev/usb/usb_hid.c index e99cdb0b..0dc146df 100644 --- a/freebsd/sys/dev/usb/usb_hid.c +++ b/freebsd/sys/dev/usb/usb_hid.c @@ -21,13 +21,6 @@ __FBSDID("$FreeBSD$"); * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED @@ -50,7 +43,6 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -435,7 +427,7 @@ hid_get_item(struct hid_data *s, struct hid_item *h) s->loc_size = dval & mask; break; case 8: - hid_switch_rid(s, c, dval); + hid_switch_rid(s, c, dval & mask); break; case 9: /* mask because value is unsigned */ @@ -622,7 +614,7 @@ hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id) * hid_locate *------------------------------------------------------------------------*/ int -hid_locate(const void *desc, usb_size_t size, uint32_t u, enum hid_kind k, +hid_locate(const void *desc, usb_size_t size, int32_t u, enum hid_kind k, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id) { struct hid_data *d; @@ -712,10 +704,47 @@ hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *l } /*------------------------------------------------------------------------* + * hid_put_data + *------------------------------------------------------------------------*/ +void +hid_put_data_unsigned(uint8_t *buf, usb_size_t len, + struct hid_location *loc, unsigned int value) +{ + uint32_t hpos = loc->pos; + uint32_t hsize = loc->size; + uint64_t data; + uint64_t mask; + uint32_t rpos; + uint8_t n; + + DPRINTFN(11, "hid_put_data: loc %d/%d = %u\n", hpos, hsize, value); + + /* Range check and limit */ + if (hsize == 0) + return; + if (hsize > 32) + hsize = 32; + + /* Put data in a safe way */ + rpos = (hpos / 8); + n = (hsize + 7) / 8; + data = ((uint64_t)value) << (hpos % 8); + mask = ((1ULL << hsize) - 1ULL) << (hpos % 8); + rpos += n; + while (n--) { + rpos--; + if (rpos < len) { + buf[rpos] &= ~(mask >> (8 * n)); + buf[rpos] |= (data >> (8 * n)); + } + } +} + +/*------------------------------------------------------------------------* * hid_is_collection *------------------------------------------------------------------------*/ int -hid_is_collection(const void *desc, usb_size_t size, uint32_t usage) +hid_is_collection(const void *desc, usb_size_t size, int32_t usage) { struct hid_data *hd; struct hid_item hi; diff --git a/freebsd/sys/dev/usb/usb_hub.c b/freebsd/sys/dev/usb/usb_hub.c index c20bde30..c5887797 100644 --- a/freebsd/sys/dev/usb/usb_hub.c +++ b/freebsd/sys/dev/usb/usb_hub.c @@ -40,7 +40,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -80,9 +79,8 @@ static int uhub_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, uhub, CTLFLAG_RW, 0, "USB HUB"); -SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0, +SYSCTL_INT(_hw_usb_uhub, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &uhub_debug, 0, "Debug level"); - TUNABLE_INT("hw.usb.uhub.debug", &uhub_debug); #endif @@ -112,6 +110,7 @@ struct uhub_softc { #define UHUB_PROTO(sc) ((sc)->sc_udev->ddesc.bDeviceProtocol) #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB) #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT) +#define UHUB_IS_MULTI_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBMTT) #define UHUB_IS_SUPER_SPEED(sc) (UHUB_PROTO(sc) == UDPROTO_SSHUB) /* prototypes for type checking: */ @@ -243,11 +242,18 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) /* check if device should be re-enumerated */ if (child->flags.usb_mode == USB_MODE_HOST) { - usbd_enum_lock(child); + 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) - err = usbd_req_re_enumerate(child, NULL); + 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) { @@ -257,7 +263,8 @@ uhub_explore_sub(struct uhub_softc *sc, struct usb_port *up) child->re_enumerate_wait = 0; err = 0; } - usbd_enum_unlock(child); + if (do_unlock) + usbd_enum_unlock(child); } /* check if probe and attach should be done */ @@ -325,6 +332,7 @@ uhub_reattach_port(struct uhub_softc *sc, uint8_t portno) enum usb_dev_speed speed; enum usb_hc_mode mode; usb_error_t err; + uint16_t power_mask; uint8_t timeout; DPRINTF("reattaching port %d\n", portno); @@ -367,10 +375,27 @@ repeat: } /* check if there is no power on the port and print a warning */ - if (!(sc->sc_st.port_status & UPS_PORT_POWER)) { + switch (udev->speed) { + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + case USB_SPEED_LOW: + power_mask = UPS_PORT_POWER; + break; + case USB_SPEED_SUPER: + if (udev->parent_hub == NULL) + power_mask = UPS_PORT_POWER; + else + power_mask = UPS_PORT_POWER_SS; + break; + default: + power_mask = 0; + break; + } + if (!(sc->sc_st.port_status & power_mask)) { DPRINTF("WARNING: strange, connected port %d " "has no power\n", portno); } + /* check if the device is in Host Mode */ if (!(sc->sc_st.port_status & UPS_PORT_MODE_DEVICE)) { @@ -394,7 +419,7 @@ repeat: /* wait for maximum device power up time */ usb_pause_mtx(NULL, - USB_MS_TO_TICKS(USB_PORT_POWERUP_DELAY)); + USB_MS_TO_TICKS(usb_port_powerup_delay)); /* reset port, which implies enabling it */ @@ -607,12 +632,15 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) } } else { switch (UPS_PORT_LINK_STATE_GET(sc->sc_st.port_status)) { - case UPS_PORT_LS_U0: - case UPS_PORT_LS_U1: + case UPS_PORT_LS_U3: + is_suspend = 1; + break; + case UPS_PORT_LS_SS_INA: + usbd_req_warm_reset_port(udev, NULL, portno); is_suspend = 0; break; default: - is_suspend = 1; + is_suspend = 0; break; } } @@ -629,8 +657,7 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) */ if (is_suspend == 0) usb_dev_resume_peer(child); - else if ((child->flags.usb_mode == USB_MODE_DEVICE) || - (usb_device_20_compatible(child) == 0)) + else if (child->flags.usb_mode == USB_MODE_DEVICE) usb_dev_suspend_peer(child); } done: @@ -688,6 +715,7 @@ uhub_explore(struct usb_device *udev) usb_error_t err; uint8_t portno; uint8_t x; + uint8_t do_unlock; hub = udev->hub; sc = hub->hubsoftc; @@ -704,6 +732,13 @@ uhub_explore(struct usb_device *udev) DPRINTF("Device is suspended!\n"); return (0); } + + /* + * Make sure we don't race against user-space applications + * like LibUSB: + */ + do_unlock = usbd_enum_lock(udev); + for (x = 0; x != hub->nports; x++) { up = hub->ports + x; portno = x + 1; @@ -765,7 +800,8 @@ uhub_explore(struct usb_device *udev) break; } } - if (sc->sc_st.port_change & (UPS_C_SUSPEND | UPS_C_PORT_LINK_STATE)) { + if (sc->sc_st.port_change & (UPS_C_SUSPEND | + UPS_C_PORT_LINK_STATE)) { err = uhub_suspend_resume_port(sc, portno); if (err) { /* most likely the HUB is gone */ @@ -781,6 +817,9 @@ uhub_explore(struct usb_device *udev) up->restartcnt = 0; } + if (do_unlock) + usbd_enum_unlock(udev); + /* initial status checked */ sc->sc_flags |= UHUB_FLAG_DID_EXPLORE; @@ -916,6 +955,16 @@ uhub_attach(device_t dev) "bus powered HUB. HUB ignored\n"); goto error; } + + if (UHUB_IS_MULTI_TT(sc)) { + err = usbd_set_alt_interface_index(udev, 0, 1); + if (err) { + device_printf(dev, "MTT could not be enabled\n"); + goto error; + } + device_printf(dev, "MTT enabled\n"); + } + /* get HUB descriptor */ DPRINTFN(2, "Getting HUB descriptor\n"); @@ -936,7 +985,7 @@ uhub_attach(device_t dev) /* get power delay */ pwrdly = ((hubdesc20.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + - USB_EXTRA_POWER_UP_TIME); + usb_extra_power_up_time); /* get complete HUB descriptor */ if (nports >= 8) { @@ -981,7 +1030,7 @@ uhub_attach(device_t dev) /* get power delay */ pwrdly = ((hubdesc30.bPwrOn2PwrGood * UHD_PWRON_FACTOR) + - USB_EXTRA_POWER_UP_TIME); + usb_extra_power_up_time); /* get complete HUB descriptor */ if (nports >= 8) { @@ -1010,7 +1059,7 @@ uhub_attach(device_t dev) /* default number of ports */ nports = 1; /* default power delay */ - pwrdly = ((10 * UHD_PWRON_FACTOR) + USB_EXTRA_POWER_UP_TIME); + pwrdly = ((10 * UHD_PWRON_FACTOR) + usb_extra_power_up_time); break; } if (nports == 0) { @@ -1025,10 +1074,6 @@ uhub_attach(device_t dev) } udev->hub = hub; -#if USB_HAVE_TT_SUPPORT - /* init FULL-speed ISOCHRONOUS schedule */ - usbd_fs_isoc_schedule_init_all(hub->fs_isoc_schedule); -#endif /* initialize HUB structure */ hub->hubsoftc = sc; hub->explore = &uhub_explore; @@ -1332,15 +1377,21 @@ uhub_child_pnpinfo_string(device_t parent, device_t child, "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\" " "release=0x%04x " - "intclass=0x%02x intsubclass=0x%02x", + "mode=%s " + "intclass=0x%02x intsubclass=0x%02x " + "intprotocol=0x%02x " "%s%s", UGETW(res.udev->ddesc.idVendor), UGETW(res.udev->ddesc.idProduct), res.udev->ddesc.bDeviceClass, res.udev->ddesc.bDeviceSubClass, usb_get_serial(res.udev), UGETW(res.udev->ddesc.bcdDevice), + (res.udev->flags.usb_mode == USB_MODE_HOST) ? "host" : "device", iface->idesc->bInterfaceClass, - iface->idesc->bInterfaceSubClass); + iface->idesc->bInterfaceSubClass, + iface->idesc->bInterfaceProtocol, + iface->pnpinfo ? " " : "", + iface->pnpinfo ? iface->pnpinfo : ""); } else { if (buflen) { buf[0] = '\0'; @@ -1393,7 +1444,7 @@ static uint8_t usb_intr_find_best_slot(usb_size_t *ptr, uint8_t start, uint8_t end, uint8_t mask) { - usb_size_t min = 0 - 1; + usb_size_t min = (usb_size_t)-1; usb_size_t sum; uint8_t x; uint8_t y; @@ -1618,42 +1669,6 @@ usb_hs_bandwidth_free(struct usb_xfer *xfer) } /*------------------------------------------------------------------------* - * usbd_fs_isoc_schedule_init_sub - * - * This function initialises an USB FULL speed isochronous schedule - * entry. - *------------------------------------------------------------------------*/ -#if USB_HAVE_TT_SUPPORT -static void -usbd_fs_isoc_schedule_init_sub(struct usb_fs_isoc_schedule *fss) -{ - fss->total_bytes = (USB_FS_ISOC_UFRAME_MAX * - USB_FS_BYTES_PER_HS_UFRAME); - fss->frame_bytes = (USB_FS_BYTES_PER_HS_UFRAME); - fss->frame_slot = 0; -} -#endif - -/*------------------------------------------------------------------------* - * usbd_fs_isoc_schedule_init_all - * - * This function will reset the complete USB FULL speed isochronous - * bandwidth schedule. - *------------------------------------------------------------------------*/ -#if USB_HAVE_TT_SUPPORT -void -usbd_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss) -{ - struct usb_fs_isoc_schedule *fss_end = fss + USB_ISOC_TIME_MAX; - - while (fss != fss_end) { - usbd_fs_isoc_schedule_init_sub(fss); - fss++; - } -} -#endif - -/*------------------------------------------------------------------------* * usb_isoc_time_expand * * This function will expand the time counter from 7-bit to 16-bit. @@ -1685,114 +1700,130 @@ usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr) } /*------------------------------------------------------------------------* - * usbd_fs_isoc_schedule_isoc_time_expand + * usbd_fs_isoc_schedule_alloc_slot * - * This function does multiple things. First of all it will expand the - * passed isochronous time, which is the return value. Then it will - * store where the current FULL speed isochronous schedule is - * positioned in time and where the end is. See "pp_start" and - * "pp_end" arguments. + * This function will allocate bandwidth for an isochronous FULL speed + * transaction in the FULL speed schedule. * * Returns: - * Expanded version of "isoc_time". - * - * NOTE: This function depends on being called regularly with - * intervals less than "USB_ISOC_TIME_MAX". + * <8: Success + * Else: Error *------------------------------------------------------------------------*/ #if USB_HAVE_TT_SUPPORT -uint16_t -usbd_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev, - struct usb_fs_isoc_schedule **pp_start, - struct usb_fs_isoc_schedule **pp_end, - uint16_t isoc_time) +uint8_t +usbd_fs_isoc_schedule_alloc_slot(struct usb_xfer *isoc_xfer, uint16_t isoc_time) { - struct usb_fs_isoc_schedule *fss_end; - struct usb_fs_isoc_schedule *fss_a; - struct usb_fs_isoc_schedule *fss_b; - struct usb_hub *hs_hub; + struct usb_xfer *xfer; + struct usb_xfer *pipe_xfer; + struct usb_bus *bus; + usb_frlength_t len; + usb_frlength_t data_len; + uint16_t delta; + uint16_t slot; + uint8_t retval; - isoc_time = usb_isoc_time_expand(udev->bus, isoc_time); + data_len = 0; + slot = 0; - hs_hub = udev->parent_hs_hub->hub; + bus = isoc_xfer->xroot->bus; - if (hs_hub != NULL) { + TAILQ_FOREACH(xfer, &bus->intr_q.head, wait_entry) { - fss_a = hs_hub->fs_isoc_schedule + - (hs_hub->isoc_last_time % USB_ISOC_TIME_MAX); + /* skip self, if any */ - hs_hub->isoc_last_time = isoc_time; + if (xfer == isoc_xfer) + continue; + + /* check if this USB transfer is going through the same TT */ + + if (xfer->xroot->udev->parent_hs_hub != + isoc_xfer->xroot->udev->parent_hs_hub) { + continue; + } + if ((isoc_xfer->xroot->udev->parent_hs_hub-> + ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT) && + (xfer->xroot->udev->hs_port_no != + isoc_xfer->xroot->udev->hs_port_no)) { + continue; + } + if (xfer->endpoint->methods != isoc_xfer->endpoint->methods) + continue; - fss_b = hs_hub->fs_isoc_schedule + - (isoc_time % USB_ISOC_TIME_MAX); + /* check if isoc_time is part of this transfer */ - fss_end = hs_hub->fs_isoc_schedule + USB_ISOC_TIME_MAX; + delta = xfer->isoc_time_complete - isoc_time; + if (delta > 0 && delta <= xfer->nframes) { + delta = xfer->nframes - delta; - *pp_start = hs_hub->fs_isoc_schedule; - *pp_end = fss_end; + len = xfer->frlengths[delta]; + len += 8; + len *= 7; + len /= 6; - while (fss_a != fss_b) { - if (fss_a == fss_end) { - fss_a = hs_hub->fs_isoc_schedule; + data_len += len; + } + + /* check double buffered transfers */ + + TAILQ_FOREACH(pipe_xfer, &xfer->endpoint->endpoint_q.head, + wait_entry) { + + /* skip self, if any */ + + if (pipe_xfer == isoc_xfer) continue; + + /* check if isoc_time is part of this transfer */ + + delta = pipe_xfer->isoc_time_complete - isoc_time; + if (delta > 0 && delta <= pipe_xfer->nframes) { + delta = pipe_xfer->nframes - delta; + + len = pipe_xfer->frlengths[delta]; + len += 8; + len *= 7; + len /= 6; + + data_len += len; } - usbd_fs_isoc_schedule_init_sub(fss_a); - fss_a++; } + } - } else { - - *pp_start = NULL; - *pp_end = NULL; + while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) { + data_len -= USB_FS_BYTES_PER_HS_UFRAME; + slot++; } - return (isoc_time); -} -#endif -/*------------------------------------------------------------------------* - * usbd_fs_isoc_schedule_alloc - * - * This function will allocate bandwidth for an isochronous FULL speed - * transaction in the FULL speed schedule. The microframe slot where - * the transaction should be started is stored in the byte pointed to - * by "pstart". The "len" argument specifies the length of the - * transaction in bytes. - * - * Returns: - * 0: Success - * Else: Error - *------------------------------------------------------------------------*/ -#if USB_HAVE_TT_SUPPORT -uint8_t -usbd_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss, - uint8_t *pstart, uint16_t len) -{ - uint8_t slot = fss->frame_slot; + /* check for overflow */ + + if (slot >= USB_FS_ISOC_UFRAME_MAX) + return (255); - /* Compute overhead and bit-stuffing */ + retval = slot; - len += 8; + delta = isoc_xfer->isoc_time_complete - isoc_time; + if (delta > 0 && delta <= isoc_xfer->nframes) { + delta = isoc_xfer->nframes - delta; - len *= 7; - len /= 6; + len = isoc_xfer->frlengths[delta]; + len += 8; + len *= 7; + len /= 6; - if (len > fss->total_bytes) { - *pstart = 0; /* set some dummy value */ - return (1); /* error */ + data_len += len; } - if (len > 0) { - fss->total_bytes -= len; + while (data_len >= USB_FS_BYTES_PER_HS_UFRAME) { + data_len -= USB_FS_BYTES_PER_HS_UFRAME; + slot++; + } - while (len >= fss->frame_bytes) { - len -= fss->frame_bytes; - fss->frame_bytes = USB_FS_BYTES_PER_HS_UFRAME; - fss->frame_slot++; - } + /* check for overflow */ - fss->frame_bytes -= len; - } - *pstart = slot; - return (0); /* success */ + if (slot >= USB_FS_ISOC_UFRAME_MAX) + return (255); + + return (retval); } #endif @@ -2046,7 +2077,6 @@ usb_peer_should_wakeup(struct usb_device *udev) (udev->pwr_save.write_refs != 0) || ((udev->pwr_save.read_refs != 0) && (udev->flags.usb_mode == USB_MODE_HOST) && - (usb_device_20_compatible(udev) != 0) && (usb_peer_can_wakeup(udev) == 0))); } @@ -2112,7 +2142,7 @@ usb_bus_powerd(struct usb_bus *bus) /* reset counters */ - mintime = 0 - 1; + mintime = (usb_ticks_t)-1; type_refs[0] = 0; type_refs[1] = 0; type_refs[2] = 0; @@ -2149,7 +2179,7 @@ usb_bus_powerd(struct usb_bus *bus) } } - if (mintime >= (1 * hz)) { + if (mintime >= (usb_ticks_t)(1 * hz)) { /* recompute power masks */ DPRINTF("Recomputing power masks\n"); bus->hw_power_state = 0; @@ -2226,10 +2256,18 @@ usb_dev_resume_peer(struct usb_device *udev) DPRINTFN(0, "Resuming port failed\n"); return; } + } else { + /* resume current port (Valid in Host and Device Mode) */ + err = usbd_req_set_port_link_state(udev->parent_hub, + NULL, udev->port_no, UPS_PORT_LS_U0); + if (err) { + DPRINTFN(0, "Resuming port failed\n"); + return; + } } /* resume settle time */ - usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY)); + usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay)); if (bus->methods->device_resume != NULL) { /* resume USB device on the USB controller */ @@ -2267,8 +2305,7 @@ usb_dev_resume_peer(struct usb_device *udev) usbd_sr_unlock(udev); /* check if peer has wakeup capability */ - if (usb_peer_can_wakeup(udev) && - usb_device_20_compatible(udev)) { + if (usb_peer_can_wakeup(udev)) { /* clear remote wakeup */ err = usbd_req_clear_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); @@ -2329,8 +2366,7 @@ repeat: } } - if (usb_peer_can_wakeup(udev) && - usb_device_20_compatible(udev)) { + if (usb_peer_can_wakeup(udev)) { /* * This request needs to be done before we set * "udev->flags.self_suspended": @@ -2362,8 +2398,7 @@ repeat: USB_BUS_UNLOCK(udev->bus); if (err != 0) { - if (usb_peer_can_wakeup(udev) && - usb_device_20_compatible(udev)) { + if (usb_peer_can_wakeup(udev)) { /* allow device to do remote wakeup */ err = usbd_req_clear_device_feature(udev, NULL, UF_DEVICE_REMOTE_WAKEUP); @@ -2385,7 +2420,7 @@ repeat: NULL, udev->port_no, UHF_PORT_SUSPEND); /* resume settle time */ - usb_pause_mtx(NULL, USB_MS_TO_TICKS(USB_PORT_RESUME_DELAY)); + usb_pause_mtx(NULL, USB_MS_TO_TICKS(usb_port_resume_delay)); } DPRINTF("Suspend was cancelled!\n"); return; @@ -2419,6 +2454,14 @@ repeat: DPRINTFN(0, "Suspending port failed\n"); return; } + } else { + /* suspend current port */ + err = usbd_req_set_port_link_state(udev->parent_hub, + NULL, udev->port_no, UPS_PORT_LS_U3); + if (err) { + DPRINTFN(0, "Suspending port failed\n"); + return; + } } udev = udev->parent_hub; @@ -2472,3 +2515,19 @@ usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode) /* use fixed power mode given by hardware driver */ return (temp); } + +/*------------------------------------------------------------------------* + * usbd_start_re_enumerate + * + * This function starts re-enumeration of the given USB device. This + * function does not need to be called BUS-locked. This function does + * not wait until the re-enumeration is completed. + *------------------------------------------------------------------------*/ +void +usbd_start_re_enumerate(struct usb_device *udev) +{ + if (udev->re_enumerate_wait == 0) { + udev->re_enumerate_wait = 1; + usb_needs_explore(udev->bus, 0); + } +} diff --git a/freebsd/sys/dev/usb/usb_hub.h b/freebsd/sys/dev/usb/usb_hub.h index 0f595997..23a1fa4f 100644 --- a/freebsd/sys/dev/usb/usb_hub.h +++ b/freebsd/sys/dev/usb/usb_hub.h @@ -38,22 +38,9 @@ struct usb_port { }; /* - * The following structure defines how many bytes are - * left in an 1ms USB time slot. - */ -struct usb_fs_isoc_schedule { - uint16_t total_bytes; - uint8_t frame_bytes; - uint8_t frame_slot; -}; - -/* * The following structure defines an USB HUB. */ struct usb_hub { -#if USB_HAVE_TT_SUPPORT - struct usb_fs_isoc_schedule fs_isoc_schedule[USB_ISOC_TIME_MAX]; -#endif struct usb_device *hubudev; /* the HUB device */ usb_error_t (*explore) (struct usb_device *hub); void *hubsoftc; @@ -68,7 +55,6 @@ struct usb_hub { void usb_hs_bandwidth_alloc(struct usb_xfer *xfer); void usb_hs_bandwidth_free(struct usb_xfer *xfer); -void usbd_fs_isoc_schedule_init_all(struct usb_fs_isoc_schedule *fss); void usb_bus_port_set_device(struct usb_bus *bus, struct usb_port *up, struct usb_device *udev, uint8_t device_index); struct usb_device *usb_bus_port_get_device(struct usb_bus *bus, diff --git a/freebsd/sys/dev/usb/usb_ioctl.h b/freebsd/sys/dev/usb/usb_ioctl.h index f9018e28..9af6ee5c 100644 --- a/freebsd/sys/dev/usb/usb_ioctl.h +++ b/freebsd/sys/dev/usb/usb_ioctl.h @@ -39,15 +39,37 @@ #define USB_DEVICE_NAME "usbctl" #define USB_DEVICE_DIR "usb" #define USB_GENERIC_NAME "ugen" +#define USB_TEMPLATE_SYSCTL "hw.usb.template" /* integer type */ + +/* Definition of valid template sysctl values */ + +enum { + USB_TEMP_MSC, /* USB Mass Storage */ + USB_TEMP_CDCE, /* USB CDC Ethernet */ + USB_TEMP_MTP, /* Message Transfer Protocol */ + USB_TEMP_MODEM, /* USB CDC Modem */ + USB_TEMP_AUDIO, /* USB Audio */ + USB_TEMP_KBD, /* USB Keyboard */ + USB_TEMP_MOUSE, /* USB Mouse */ + USB_TEMP_MAX, +}; struct usb_read_dir { +#ifdef COMPAT_32BIT + uint64_t urd_data; +#else void *urd_data; +#endif uint32_t urd_startentry; uint32_t urd_maxlen; }; struct usb_ctl_request { +#ifdef COMPAT_32BIT + uint64_t ucr_data; +#else void *ucr_data; +#endif uint16_t ucr_flags; uint16_t ucr_actlen; /* actual length transferred */ uint8_t ucr_addr; /* zero - currently not used */ @@ -60,7 +82,11 @@ struct usb_alt_interface { }; struct usb_gen_descriptor { +#ifdef COMPAT_32BIT + uint64_t ugd_data; +#else void *ugd_data; +#endif uint16_t ugd_lang_id; uint16_t ugd_maxlen; uint16_t ugd_actlen; @@ -126,9 +152,14 @@ struct usb_fs_endpoint { * NOTE: isochronous USB transfer only use one buffer, but can have * multiple frame lengths ! */ +#ifdef COMPAT_32BIT + uint64_t ppBuffer; + uint64_t pLength; +#else void **ppBuffer; /* pointer to userland buffers */ uint32_t *pLength; /* pointer to frame lengths, updated * to actual length */ +#endif uint32_t nFrames; /* number of frames */ uint32_t aFrames; /* actual number of frames */ uint16_t flags; @@ -150,7 +181,11 @@ struct usb_fs_endpoint { struct usb_fs_init { /* userland pointer to endpoints structure */ +#ifdef COMPAT_32BIT + uint64_t pEndpoints; +#else struct usb_fs_endpoint *pEndpoints; +#endif /* maximum number of endpoints */ uint8_t ep_index_max; }; @@ -162,8 +197,9 @@ struct usb_fs_uninit { struct usb_fs_open { #define USB_FS_MAX_BUFSIZE (1 << 18) uint32_t max_bufsize; -#define USB_FS_MAX_FRAMES (1 << 12) - uint32_t max_frames; +#define USB_FS_MAX_FRAMES (1U << 12) +#define USB_FS_MAX_FRAMES_PRE_SCALE (1U << 31) /* for ISOCHRONOUS transfers */ + uint32_t max_frames; /* read and write */ uint16_t max_packet_length; /* read only */ uint8_t dev_index; /* currently unused */ uint8_t ep_index; @@ -253,6 +289,10 @@ struct usb_gen_quirk { #define USB_GET_CM_OVER_DATA _IOR ('U', 180, int) #define USB_SET_CM_OVER_DATA _IOW ('U', 181, int) +/* GPIO control */ +#define USB_GET_GPIO _IOR ('U', 182, int) +#define USB_SET_GPIO _IOW ('U', 183, int) + /* USB file system interface */ #define USB_FS_START _IOW ('U', 192, struct usb_fs_start) #define USB_FS_STOP _IOW ('U', 193, struct usb_fs_stop) diff --git a/freebsd/sys/dev/usb/usb_lookup.c b/freebsd/sys/dev/usb/usb_lookup.c index b7ce2522..ed752fed 100644 --- a/freebsd/sys/dev/usb/usb_lookup.c +++ b/freebsd/sys/dev/usb/usb_lookup.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -45,6 +44,8 @@ #include <sys/callout.h> #include <sys/malloc.h> #include <sys/priv.h> +#include <sys/limits.h> +#include <sys/endian.h> #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> @@ -108,13 +109,6 @@ usbd_lookup_id_by_info(const struct usb_device_id *id, usb_size_t sizeof_id, (id->bDeviceProtocol != info->bDeviceProtocol)) { continue; } - if ((info->bDeviceClass == 0xFF) && - (!(id->match_flag_vendor)) && - ((id->match_flag_int_class) || - (id->match_flag_int_subclass) || - (id->match_flag_int_protocol))) { - continue; - } if ((id->match_flag_int_class) && (id->bInterfaceClass != info->bInterfaceClass)) { continue; @@ -154,3 +148,108 @@ usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, } return (ENXIO); } + +/*------------------------------------------------------------------------* + * Export the USB device ID format we use to userspace tools. + *------------------------------------------------------------------------*/ +#if BYTE_ORDER == BIG_ENDIAN +#define U16_XOR "8" +#define U32_XOR "12" +#define U64_XOR "56" +#define U8_BITFIELD_XOR "7" +#define U16_BITFIELD_XOR "15" +#define U32_BITFIELD_XOR "31" +#define U64_BITFIELD_XOR "63" +#else +#define U16_XOR "0" +#define U32_XOR "0" +#define U64_XOR "0" +#define U8_BITFIELD_XOR "0" +#define U16_BITFIELD_XOR "0" +#define U32_BITFIELD_XOR "0" +#define U64_BITFIELD_XOR "0" +#endif + +#if USB_HAVE_COMPAT_LINUX +#define MFL_SIZE "1" +#else +#define MFL_SIZE "0" +#endif + +#ifdef KLD_MODULE +static const char __section("bus_autoconf_format") __used usb_id_format[] = { + + /* Declare that three different sections use the same format */ + + "usb_host_id{256,:}" + "usb_device_id{256,:}" + "usb_dual_id{256,:}" + + /* List size of fields in the usb_device_id structure */ + +#if ULONG_MAX >= 0xFFFFFFFFUL + "unused{0,8}" + "unused{0,8}" + "unused{0,8}" + "unused{0,8}" +#if ULONG_MAX >= 0xFFFFFFFFFFFFFFFFULL + "unused{0,8}" + "unused{0,8}" + "unused{0,8}" + "unused{0,8}" +#endif +#else +#error "Please update code." +#endif + + "idVendor[0]{" U16_XOR ",8}" + "idVendor[1]{" U16_XOR ",8}" + "idProduct[0]{" U16_XOR ",8}" + "idProduct[1]{" U16_XOR ",8}" + "bcdDevice_lo[0]{" U16_XOR ",8}" + "bcdDevice_lo[1]{" U16_XOR ",8}" + "bcdDevice_hi[0]{" U16_XOR ",8}" + "bcdDevice_hi[1]{" U16_XOR ",8}" + + "bDeviceClass{0,8}" + "bDeviceSubClass{0,8}" + "bDeviceProtocol{0,8}" + "bInterfaceClass{0,8}" + "bInterfaceSubClass{0,8}" + "bInterfaceProtocol{0,8}" + + "mf_vendor{" U8_BITFIELD_XOR ",1}" + "mf_product{" U8_BITFIELD_XOR ",1}" + "mf_dev_lo{" U8_BITFIELD_XOR ",1}" + "mf_dev_hi{" U8_BITFIELD_XOR ",1}" + + "mf_dev_class{" U8_BITFIELD_XOR ",1}" + "mf_dev_subclass{" U8_BITFIELD_XOR ",1}" + "mf_dev_protocol{" U8_BITFIELD_XOR ",1}" + "mf_int_class{" U8_BITFIELD_XOR ",1}" + + "mf_int_subclass{" U8_BITFIELD_XOR ",1}" + "mf_int_protocol{" U8_BITFIELD_XOR ",1}" + "unused{" U8_BITFIELD_XOR ",6}" + + "mfl_vendor{" U16_XOR "," MFL_SIZE "}" + "mfl_product{" U16_XOR "," MFL_SIZE "}" + "mfl_dev_lo{" U16_XOR "," MFL_SIZE "}" + "mfl_dev_hi{" U16_XOR "," MFL_SIZE "}" + + "mfl_dev_class{" U16_XOR "," MFL_SIZE "}" + "mfl_dev_subclass{" U16_XOR "," MFL_SIZE "}" + "mfl_dev_protocol{" U16_XOR "," MFL_SIZE "}" + "mfl_int_class{" U16_XOR "," MFL_SIZE "}" + + "mfl_int_subclass{" U16_XOR "," MFL_SIZE "}" + "mfl_int_protocol{" U16_XOR "," MFL_SIZE "}" + "unused{" U16_XOR "," MFL_SIZE "}" + "unused{" U16_XOR "," MFL_SIZE "}" + + "unused{" U16_XOR "," MFL_SIZE "}" + "unused{" U16_XOR "," MFL_SIZE "}" + "unused{" U16_XOR "," MFL_SIZE "}" + "unused{" U16_XOR "," MFL_SIZE "}" +}; +#endif diff --git a/freebsd/sys/dev/usb/usb_mbuf.c b/freebsd/sys/dev/usb/usb_mbuf.c index f2649679..c6ee8ee5 100644 --- a/freebsd/sys/dev/usb/usb_mbuf.c +++ b/freebsd/sys/dev/usb/usb_mbuf.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> diff --git a/freebsd/sys/dev/usb/usb_msctest.c b/freebsd/sys/dev/usb/usb_msctest.c index 77f199be..018809d7 100644 --- a/freebsd/sys/dev/usb/usb_msctest.c +++ b/freebsd/sys/dev/usb/usb_msctest.c @@ -2,7 +2,7 @@ /* $FreeBSD$ */ /*- - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008,2011 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -42,7 +42,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -87,7 +86,10 @@ enum { DIR_NONE, }; +#define SCSI_MAX_LEN 0x100 #define SCSI_INQ_LEN 0x24 +#define SCSI_SENSE_LEN 0xFF + static uint8_t scsi_test_unit_ready[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_inquiry[] = { 0x12, 0x00, 0x00, 0x00, SCSI_INQ_LEN, 0x00 }; static uint8_t scsi_rezero_init[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -100,6 +102,10 @@ static uint8_t scsi_huawei_eject[] = { 0x11, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t scsi_tct_eject[] = { 0x06, 0xf5, 0x04, 0x02, 0x52, 0x70 }; +static uint8_t scsi_sync_cache[] = { 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; +static uint8_t scsi_request_sense[] = { 0x03, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define BULK_SIZE 64 /* dummy */ #define ERR_CSW_FAILED -1 @@ -153,7 +159,7 @@ struct bbb_transfer { uint8_t status_try; int error; - uint8_t buffer[256]; + uint8_t buffer[SCSI_MAX_LEN] __aligned(4); }; static usb_callback_t bbb_command_callback; @@ -167,7 +173,7 @@ static void bbb_done(struct bbb_transfer *, int); static void bbb_transfer_start(struct bbb_transfer *, uint8_t); static void bbb_data_clear_stall_callback(struct usb_xfer *, uint8_t, uint8_t); -static uint8_t bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t, +static int bbb_command_start(struct bbb_transfer *, uint8_t, uint8_t, void *, size_t, void *, size_t, usb_timeout_t); static struct bbb_transfer *bbb_attach(struct usb_device *, uint8_t); static void bbb_detach(struct bbb_transfer *); @@ -179,6 +185,7 @@ 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 */ }, @@ -188,7 +195,7 @@ static const struct usb_config bbb_config[ST_MAX] = { .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = BULK_SIZE, - .flags = {.proxy_buffer = 1,.short_xfer_ok = 1,}, + .flags = {.ext_buffer = 1,.proxy_buffer = 1,.short_xfer_ok = 1,}, .callback = &bbb_data_read_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, @@ -207,7 +214,7 @@ static const struct usb_config bbb_config[ST_MAX] = { .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = BULK_SIZE, - .flags = {.proxy_buffer = 1,}, + .flags = {.ext_buffer = 1,.proxy_buffer = 1,}, .callback = &bbb_data_write_callback, .timeout = 4 * USB_MS_HZ, /* 4 seconds */ }, @@ -226,7 +233,7 @@ static const struct usb_config bbb_config[ST_MAX] = { .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = sizeof(struct bbb_csw), - .flags = {.short_xfer_ok = 1,}, + .flags = {.ext_buffer = 1,.short_xfer_ok = 1,}, .callback = &bbb_status_callback, .timeout = 1 * USB_MS_HZ, /* 1 second */ }, @@ -414,7 +421,8 @@ static void bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct bbb_transfer *sc = usbd_xfer_softc(xfer); - int actlen, sumlen; + int actlen; + int sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); @@ -423,7 +431,7 @@ bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) /* very simple status check */ - if (actlen < sizeof(sc->csw)) { + if (actlen < (int)sizeof(sc->csw)) { bbb_done(sc, USB_ERR_SHORT_XFER); } else if (sc->csw.bCSWStatus == CSWSTATUS_GOOD) { bbb_done(sc, 0); /* success */ @@ -458,7 +466,7 @@ bbb_status_callback(struct usb_xfer *xfer, usb_error_t error) * 0: Success * Else: Failure *------------------------------------------------------------------------*/ -static uint8_t +static int bbb_command_start(struct bbb_transfer *sc, uint8_t dir, uint8_t lun, void *data_ptr, size_t data_len, void *cmd_ptr, size_t cmd_len, usb_timeout_t data_timeout) @@ -471,8 +479,8 @@ 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; - bzero(&sc->cbw.CBWCDB, sizeof(sc->cbw.CBWCDB)); - bcopy(cmd_ptr, &sc->cbw.CBWCDB, 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, &sc->cbw.CBWCDB, ":"); mtx_lock(&sc->mtx); @@ -492,6 +500,19 @@ bbb_attach(struct usb_device *udev, uint8_t iface_index) struct usb_interface_descriptor *id; struct bbb_transfer *sc; usb_error_t err; + uint8_t do_unlock; + + /* Prevent re-enumeration */ + do_unlock = usbd_enum_lock(udev); + + /* + * Make sure any driver which is hooked up to this interface, + * like umass is gone: + */ + usb_detach_device(udev, iface_index, 0); + + if (do_unlock) + usbd_enum_unlock(udev); iface = usbd_get_iface(udev, iface_index); if (iface == NULL) @@ -552,9 +573,10 @@ int usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index) { struct bbb_transfer *sc; - usb_error_t err; - uint8_t timeout, is_cdrom; + uint8_t timeout; + uint8_t is_cdrom; uint8_t sid_type; + int err; sc = bbb_attach(udev, iface_index); if (sc == NULL) @@ -580,6 +602,142 @@ usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index) return (is_cdrom); } +static uint8_t +usb_msc_get_max_lun(struct usb_device *udev, uint8_t iface_index) +{ + struct usb_device_request req; + usb_error_t err; + uint8_t buf = 0; + + + /* The Get Max Lun command is a class-specific request. */ + req.bmRequestType = UT_READ_CLASS_INTERFACE; + req.bRequest = 0xFE; /* GET_MAX_LUN */ + USETW(req.wValue, 0); + req.wIndex[0] = iface_index; + req.wIndex[1] = 0; + USETW(req.wLength, 1); + + err = usbd_do_request(udev, NULL, &req, &buf); + if (err) + buf = 0; + + return (buf); +} + +usb_error_t +usb_msc_auto_quirk(struct usb_device *udev, uint8_t iface_index) +{ + struct bbb_transfer *sc; + uint8_t timeout; + uint8_t is_no_direct; + uint8_t sid_type; + int err; + + sc = bbb_attach(udev, iface_index); + if (sc == NULL) + return (0); + + /* + * Some devices need a delay after that the configuration + * value is set to function properly: + */ + usb_pause_mtx(NULL, hz); + + if (usb_msc_get_max_lun(udev, iface_index) == 0) { + DPRINTF("Device has only got one LUN.\n"); + usbd_add_dynamic_quirk(udev, UQ_MSC_NO_GETMAXLUN); + } + + is_no_direct = 1; + for (timeout = 4; timeout; timeout--) { + err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, + SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), + USB_MS_HZ); + + if (err == 0 && sc->actlen > 0) { + sid_type = sc->buffer[0] & 0x1F; + if (sid_type == 0x00) + is_no_direct = 0; + break; + } else if (err != ERR_CSW_FAILED) + break; /* non retryable error */ + usb_pause_mtx(NULL, hz); + } + + if (is_no_direct) { + DPRINTF("Device is not direct access.\n"); + goto done; + } + + err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, + &scsi_test_unit_ready, sizeof(scsi_test_unit_ready), + USB_MS_HZ); + + if (err != 0) { + + if (err != ERR_CSW_FAILED) + goto error; + } + + err = bbb_command_start(sc, DIR_IN, 0, NULL, 0, + &scsi_sync_cache, sizeof(scsi_sync_cache), + USB_MS_HZ); + + if (err != 0) { + + if (err != ERR_CSW_FAILED) + goto error; + + DPRINTF("Device doesn't handle synchronize cache\n"); + + usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); + } + + /* clear sense status of any failed commands on the device */ + + err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, + SCSI_INQ_LEN, &scsi_inquiry, sizeof(scsi_inquiry), + USB_MS_HZ); + + DPRINTF("Inquiry = %d\n", err); + + if (err != 0) { + + if (err != ERR_CSW_FAILED) + goto error; + } + + err = bbb_command_start(sc, DIR_IN, 0, sc->buffer, + SCSI_SENSE_LEN, &scsi_request_sense, + sizeof(scsi_request_sense), USB_MS_HZ); + + DPRINTF("Request sense = %d\n", err); + + if (err != 0) { + + if (err != ERR_CSW_FAILED) + goto error; + } + +done: + bbb_detach(sc); + return (0); + +error: + bbb_detach(sc); + + DPRINTF("Device did not respond, enabling all quirks\n"); + + usbd_add_dynamic_quirk(udev, UQ_MSC_NO_SYNC_CACHE); + usbd_add_dynamic_quirk(udev, UQ_MSC_NO_TEST_UNIT_READY); + + /* Need to re-enumerate the device */ + usbd_req_re_enumerate(udev, NULL); + + return (USB_ERR_STALLED); +} + usb_error_t usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method) { diff --git a/freebsd/sys/dev/usb/usb_msctest.h b/freebsd/sys/dev/usb/usb_msctest.h index 807d5f5d..e4a717fe 100644 --- a/freebsd/sys/dev/usb/usb_msctest.h +++ b/freebsd/sys/dev/usb/usb_msctest.h @@ -40,5 +40,7 @@ int usb_iface_is_cdrom(struct usb_device *udev, uint8_t iface_index); usb_error_t usb_msc_eject(struct usb_device *udev, uint8_t iface_index, int method); +usb_error_t usb_msc_auto_quirk(struct usb_device *udev, + uint8_t iface_index); #endif /* _USB_MSCTEST_H_ */ diff --git a/freebsd/sys/dev/usb/usb_parse.c b/freebsd/sys/dev/usb/usb_parse.c index 6b055db7..f2529d12 100644 --- a/freebsd/sys/dev/usb/usb_parse.c +++ b/freebsd/sys/dev/usb/usb_parse.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> diff --git a/freebsd/sys/dev/usb/usb_pf.h b/freebsd/sys/dev/usb/usb_pf.h new file mode 100644 index 00000000..9d51e98c --- /dev/null +++ b/freebsd/sys/dev/usb/usb_pf.h @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_USB_PF_H +#define _DEV_USB_PF_H + +struct usbpf_pkthdr { + uint32_t up_totlen; /* Total length including all headers */ + uint32_t up_busunit; /* Host controller unit number */ + uint8_t up_address; /* USB device index */ + uint8_t up_mode; /* Mode of transfer */ +#define USBPF_MODE_HOST 0 +#define USBPF_MODE_DEVICE 1 + uint8_t up_type; /* points SUBMIT / DONE */ + uint8_t up_xfertype; /* Transfer type, see USB2.0 spec. */ + uint32_t up_flags; /* Transfer flags */ +#define USBPF_FLAG_FORCE_SHORT_XFER (1 << 0) +#define USBPF_FLAG_SHORT_XFER_OK (1 << 1) +#define USBPF_FLAG_SHORT_FRAMES_OK (1 << 2) +#define USBPF_FLAG_PIPE_BOF (1 << 3) +#define USBPF_FLAG_PROXY_BUFFER (1 << 4) +#define USBPF_FLAG_EXT_BUFFER (1 << 5) +#define USBPF_FLAG_MANUAL_STATUS (1 << 6) +#define USBPF_FLAG_NO_PIPE_OK (1 << 7) +#define USBPF_FLAG_STALL_PIPE (1 << 8) + uint32_t up_status; /* Transfer status */ +#define USBPF_STATUS_OPEN (1 << 0) +#define USBPF_STATUS_TRANSFERRING (1 << 1) +#define USBPF_STATUS_DID_DMA_DELAY (1 << 2) +#define USBPF_STATUS_DID_CLOSE (1 << 3) +#define USBPF_STATUS_DRAINING (1 << 4) +#define USBPF_STATUS_STARTED (1 << 5) +#define USBPF_STATUS_BW_RECLAIMED (1 << 6) +#define USBPF_STATUS_CONTROL_XFR (1 << 7) +#define USBPF_STATUS_CONTROL_HDR (1 << 8) +#define USBPF_STATUS_CONTROL_ACT (1 << 9) +#define USBPF_STATUS_CONTROL_STALL (1 << 10) +#define USBPF_STATUS_SHORT_FRAMES_OK (1 << 11) +#define USBPF_STATUS_SHORT_XFER_OK (1 << 12) +#define USBPF_STATUS_BDMA_ENABLE (1 << 13) +#define USBPF_STATUS_BDMA_NO_POST_SYNC (1 << 14) +#define USBPF_STATUS_BDMA_SETUP (1 << 15) +#define USBPF_STATUS_ISOCHRONOUS_XFR (1 << 16) +#define USBPF_STATUS_CURR_DMA_SET (1 << 17) +#define USBPF_STATUS_CAN_CANCEL_IMMED (1 << 18) +#define USBPF_STATUS_DOING_CALLBACK (1 << 19) + uint32_t up_error; /* USB error, see USB_ERR_XXX */ + uint32_t up_interval; /* For interrupt and isoc (ms) */ + uint32_t up_frames; /* Number of following frames */ + uint32_t up_packet_size; /* Packet size used */ + uint32_t up_packet_count; /* Packet count used */ + uint32_t up_endpoint; /* USB endpoint / stream ID */ + uint8_t up_speed; /* USB speed, see USB_SPEED_XXX */ + /* sizeof(struct usbpf_pkthdr) == 128 bytes */ + uint8_t up_reserved[83]; +}; + +struct usbpf_framehdr { + /* + * The frame length field excludes length of frame header and + * any alignment. + */ + uint32_t length; +#define USBPF_FRAME_ALIGN(x) (((x) + 3) & ~3) + uint32_t flags; +#define USBPF_FRAMEFLAG_READ (1 << 0) +#define USBPF_FRAMEFLAG_DATA_FOLLOWS (1 << 1) +}; + +#define USBPF_HDR_LEN 128 /* bytes */ +#define USBPF_FRAME_HDR_LEN 8 /* bytes */ + +extern uint8_t usbpf_pkthdr_size_ok[ + (sizeof(struct usbpf_pkthdr) == USBPF_HDR_LEN) ? 1 : -1]; +extern uint8_t usbpf_framehdr_size_ok[ + (sizeof(struct usbpf_framehdr) == USBPF_FRAME_HDR_LEN) ? 1 : -1]; + +#define USBPF_XFERTAP_SUBMIT 0 +#define USBPF_XFERTAP_DONE 1 + +#ifdef _KERNEL +void usbpf_attach(struct usb_bus *); +void usbpf_detach(struct usb_bus *); +void usbpf_xfertap(struct usb_xfer *, int); +#endif + +#endif diff --git a/freebsd/sys/dev/usb/usb_process.c b/freebsd/sys/dev/usb/usb_process.c index 8cb00473..99e2d306 100644 --- a/freebsd/sys/dev/usb/usb_process.c +++ b/freebsd/sys/dev/usb/usb_process.c @@ -36,7 +36,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -70,11 +69,17 @@ static int usb_pcount; #define USB_THREAD_CREATE(f, s, p, ...) \ kproc_kthread_add((f), (s), &usbproc, (p), RFHIGHPID, \ 0, "usb", __VA_ARGS__) +#if (__FreeBSD_version >= 900000) +#define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check() +#else +#define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check(curthread) +#endif #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) #define USB_THREAD_EXIT(err) kthread_exit() #else #define USB_THREAD_CREATE(f, s, p, ...) \ kthread_create((f), (s), (p), RFHIGHPID, 0, __VA_ARGS__) +#define USB_THREAD_SUSPEND_CHECK() kthread_suspend_check(curproc) #define USB_THREAD_SUSPEND(p) kthread_suspend(p,0) #define USB_THREAD_EXIT(err) kthread_exit(err) #endif @@ -83,9 +88,8 @@ static int usb_pcount; static int usb_proc_debug; SYSCTL_NODE(_hw_usb, OID_AUTO, proc, CTLFLAG_RW, 0, "USB process"); -SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RW, &usb_proc_debug, 0, +SYSCTL_INT(_hw_usb_proc, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &usb_proc_debug, 0, "Debug level"); - TUNABLE_INT("hw.usb.proc.debug", &usb_proc_debug); #endif @@ -101,6 +105,11 @@ usb_process(void *arg) struct usb_proc_msg *pm; struct thread *td; + /* in case of attach error, check for suspended */ +#ifndef __rtems__ + USB_THREAD_SUSPEND_CHECK(); +#endif /* __rtems__ */ + /* adjust priority */ td = curthread; #ifndef __rtems__ @@ -365,7 +374,12 @@ usb_proc_is_gone(struct usb_process *up) if (up->up_gone) return (1); - mtx_assert(up->up_mtx, MA_OWNED); + /* + * Allow calls when up_mtx is NULL, before the USB process + * structure is initialised. + */ + if (up->up_mtx != NULL) + mtx_assert(up->up_mtx, MA_OWNED); return (0); } @@ -471,7 +485,7 @@ usb_proc_drain(struct usb_process *up) /*------------------------------------------------------------------------* * usb_proc_rewakeup * - * This function is called to re-wakeup the the given USB + * This function is called to re-wakeup the given USB * process. This usually happens after that the USB system has been in * polling mode, like during a panic. This function must be called * having "up->up_mtx" locked. diff --git a/freebsd/sys/dev/usb/usb_process.h b/freebsd/sys/dev/usb/usb_process.h index b4159af1..23cf6607 100644 --- a/freebsd/sys/dev/usb/usb_process.h +++ b/freebsd/sys/dev/usb/usb_process.h @@ -27,11 +27,13 @@ #ifndef _USB_PROCESS_H_ #define _USB_PROCESS_H_ +#include <sys/interrupt.h> #include <sys/priority.h> +#include <sys/runq.h> /* defines */ -#define USB_PRI_HIGH PI_NET -#define USB_PRI_MED PI_DISK +#define USB_PRI_HIGH PI_SWI(SWI_NET) +#define USB_PRI_MED PI_SWI(SWI_CAMBIO) #define USB_PROC_WAIT_TIMEOUT 2 #define USB_PROC_WAIT_DRAIN 1 diff --git a/freebsd/sys/dev/usb/usb_request.c b/freebsd/sys/dev/usb/usb_request.c index 8d17c202..cb3b030c 100644 --- a/freebsd/sys/dev/usb/usb_request.c +++ b/freebsd/sys/dev/usb/usb_request.c @@ -36,7 +36,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -70,22 +69,19 @@ #include <dev/usb/usb_bus.h> #include <sys/ctype.h> -#ifdef USB_DEBUG -static int usb_pr_poll_delay = USB_PORT_RESET_DELAY; -static int usb_pr_recovery_delay = USB_PORT_RESET_RECOVERY; +static int usb_no_cs_fail; -SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW, - &usb_pr_poll_delay, 0, "USB port reset poll delay in ms"); -SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW, - &usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms"); +SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RW, + &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set"); +#ifdef USB_DEBUG #ifdef USB_REQ_DEBUG /* The following structures are used in connection to fault injection. */ struct usb_ctrl_debug { int bus_index; /* target bus */ int dev_index; /* target address */ int ds_fail; /* fail data stage */ - int ss_fail; /* fail data stage */ + int ss_fail; /* fail status stage */ int ds_delay; /* data stage delay in ms */ int ss_delay; /* status stage delay in ms */ int bmRequestType_value; @@ -241,6 +237,10 @@ usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: +tr_transferred: + /* reset error counter */ + udev->clear_stall_errors = 0; + if (ep == NULL) goto tr_setup; /* device was unconfigured */ if (ep->edesc && @@ -292,8 +292,30 @@ tr_setup: goto tr_setup; default: - if (xfer->error == USB_ERR_CANCELLED) { + if (error == USB_ERR_CANCELLED) break; + + DPRINTF("Clear stall failed.\n"); + + /* + * Some VMs like VirtualBox always return failure on + * clear-stall which we sometimes should just ignore. + */ + if (usb_no_cs_fail) + goto tr_transferred; + if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) + goto tr_setup; + + if (error == USB_ERR_TIMEOUT) { + udev->clear_stall_errors = USB_CS_RESET_LIMIT; + DPRINTF("Trying to re-enumerate.\n"); + usbd_start_re_enumerate(udev); + } else { + udev->clear_stall_errors++; + if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) { + DPRINTF("Trying to re-enumerate.\n"); + usbd_start_re_enumerate(udev); + } } goto tr_setup; } @@ -362,9 +384,8 @@ usbd_get_hr_func(struct usb_device *udev) * than 30 seconds is treated like a 30 second timeout. This USB stack * does not allow control requests without a timeout. * - * NOTE: This function is thread safe. All calls to - * "usbd_do_request_flags" will be serialised by the use of an - * internal "sx_lock". + * NOTE: This function is thread safe. All calls to "usbd_do_request_flags" + * will be serialized by the use of the USB device enumeration lock. * * Returns: * 0: Success @@ -388,7 +409,7 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, uint16_t length; uint16_t temp; uint16_t acttemp; - uint8_t enum_locked; + uint8_t do_unlock; if (timeout < 50) { /* timeout is too small */ @@ -400,8 +421,6 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, } length = UGETW(req->wLength); - enum_locked = usbd_enum_is_locked(udev); - DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x " "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n", udev, req->bmRequestType, req->bRequest, @@ -432,17 +451,16 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, } /* - * We need to allow suspend and resume at this point, else the - * control transfer will timeout if the device is suspended! + * Grab the USB device enumeration SX-lock serialization is + * achieved when multiple threads are involved: */ - if (enum_locked) - usbd_sr_unlock(udev); + do_unlock = usbd_enum_lock(udev); /* - * Grab the default sx-lock so that serialisation - * is achieved when multiple threads are involved: + * We need to allow suspend and resume at this point, else the + * control transfer will timeout if the device is suspended! */ - sx_xlock(&udev->ctrl_sx); + usbd_sr_unlock(udev); hr_func = usbd_get_hr_func(udev); @@ -489,7 +507,7 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, } } else #endif - bcopy(desc, data, length); + memcpy(data, desc, length); } goto done; /* success */ } @@ -686,10 +704,10 @@ usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, USB_XFER_UNLOCK(xfer); done: - sx_xunlock(&udev->ctrl_sx); + usbd_sr_lock(udev); - if (enum_locked) - usbd_sr_lock(udev); + if (do_unlock) + usbd_enum_unlock(udev); if ((mtx != NULL) && (mtx != &Giant)) mtx_lock(mtx); @@ -757,52 +775,52 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) struct usb_port_status ps; usb_error_t err; uint16_t n; + uint16_t status; + uint16_t change; -#ifdef USB_DEBUG - uint16_t pr_poll_delay; - uint16_t pr_recovery_delay; + DPRINTF("\n"); -#endif - err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET); - if (err) { + /* clear any leftover port reset changes first */ + usbd_req_clear_port_feature( + udev, mtx, port, UHF_C_PORT_RESET); + + /* assert port reset on the given port */ + err = usbd_req_set_port_feature( + udev, mtx, port, UHF_PORT_RESET); + + /* check for errors */ + if (err) goto done; - } #ifdef USB_DEBUG - /* range check input parameters */ - pr_poll_delay = usb_pr_poll_delay; - if (pr_poll_delay < 1) { - pr_poll_delay = 1; - } else if (pr_poll_delay > 1000) { - pr_poll_delay = 1000; - } - pr_recovery_delay = usb_pr_recovery_delay; - if (pr_recovery_delay > 1000) { - pr_recovery_delay = 1000; - } #endif n = 0; while (1) { -#ifdef USB_DEBUG /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); - n += pr_poll_delay; -#else - /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); - n += USB_PORT_RESET_DELAY; -#endif + usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); + n += usb_port_reset_delay; err = usbd_req_get_port_status(udev, mtx, &ps, port); - if (err) { + if (err) goto done; - } + + status = UGETW(ps.wPortStatus); + change = UGETW(ps.wPortChange); + /* if the device disappeared, just give up */ - if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { + if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; - } + /* check if reset is complete */ - if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) { + if (change & UPS_C_PORT_RESET) break; - } + + /* + * Some Virtual Machines like VirtualBox 4.x fail to + * generate a port reset change event. Check if reset + * is no longer asserted. + */ + if (!(status & UPS_RESET)) + break; + /* check for timeout */ if (n > 1000) { n = 0; @@ -813,21 +831,16 @@ usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_PORT_RESET); - if (err) { + if (err) goto done; - } + /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } -#ifdef USB_DEBUG - /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); -#else /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); -#endif + usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d reset returning error=%s\n", @@ -849,57 +862,64 @@ done: * disabled. *------------------------------------------------------------------------*/ usb_error_t -usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) +usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, + uint8_t port) { struct usb_port_status ps; usb_error_t err; uint16_t n; + uint16_t status; + uint16_t change; -#ifdef USB_DEBUG - uint16_t pr_poll_delay; - uint16_t pr_recovery_delay; + DPRINTF("\n"); -#endif - err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET); - if (err) { + err = usbd_req_get_port_status(udev, mtx, &ps, port); + if (err) goto done; + + status = UGETW(ps.wPortStatus); + + switch (UPS_PORT_LINK_STATE_GET(status)) { + case UPS_PORT_LS_U3: + case UPS_PORT_LS_COMP_MODE: + case UPS_PORT_LS_LOOPBACK: + case UPS_PORT_LS_SS_INA: + break; + default: + DPRINTF("Wrong state for warm reset\n"); + return (0); } -#ifdef USB_DEBUG - /* range check input parameters */ - pr_poll_delay = usb_pr_poll_delay; - if (pr_poll_delay < 1) { - pr_poll_delay = 1; - } else if (pr_poll_delay > 1000) { - pr_poll_delay = 1000; - } - pr_recovery_delay = usb_pr_recovery_delay; - if (pr_recovery_delay > 1000) { - pr_recovery_delay = 1000; - } -#endif + + /* clear any leftover warm port reset changes first */ + usbd_req_clear_port_feature(udev, mtx, + port, UHF_C_BH_PORT_RESET); + + /* set warm port reset */ + err = usbd_req_set_port_feature(udev, mtx, + port, UHF_BH_PORT_RESET); + if (err) + goto done; + n = 0; while (1) { -#ifdef USB_DEBUG - /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay)); - n += pr_poll_delay; -#else /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY)); - n += USB_PORT_RESET_DELAY; -#endif + usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay)); + n += usb_port_reset_delay; err = usbd_req_get_port_status(udev, mtx, &ps, port); - if (err) { + if (err) goto done; - } + + status = UGETW(ps.wPortStatus); + change = UGETW(ps.wPortChange); + /* if the device disappeared, just give up */ - if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) { + if (!(status & UPS_CURRENT_CONNECT_STATUS)) goto done; - } + /* check if reset is complete */ - if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) { + if (change & UPS_C_BH_PORT_RESET) break; - } + /* check for timeout */ if (n > 1000) { n = 0; @@ -910,21 +930,16 @@ usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port) /* clear port reset first */ err = usbd_req_clear_port_feature( udev, mtx, port, UHF_C_BH_PORT_RESET); - if (err) { + if (err) goto done; - } + /* check for timeout */ if (n == 0) { err = USB_ERR_TIMEOUT; goto done; } -#ifdef USB_DEBUG - /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay)); -#else /* wait for the device to recover from reset */ - usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY)); -#endif + usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery)); done: DPRINTFN(2, "port %d warm reset returning error=%s\n", @@ -1119,14 +1134,21 @@ usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf, } /* - * Filter by default - we don't allow greater and less than - * signs because they might confuse the dmesg printouts! + * Filter by default - We only allow alphanumerical + * and a few more to avoid any problems with scripts + * and daemons. */ - if ((*s == '<') || (*s == '>') || (!isprint(*s))) { - /* silently skip bad character */ - continue; + if (isalpha(*s) || + isdigit(*s) || + *s == '-' || + *s == '+' || + *s == ' ' || + *s == '.' || + *s == ',') { + /* allowed */ + s++; } - s++; + /* silently skip bad character */ } *s = 0; /* zero terminate resulting string */ return (USB_ERR_NORMAL_COMPLETION); @@ -1222,7 +1244,7 @@ usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx, goto done; } /* Extra sanity checking */ - if (UGETW(d->wTotalLength) < sizeof(*d)) { + if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) { err = USB_ERR_INVAL; } done: @@ -1476,7 +1498,7 @@ usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr) done: /* allow device time to set new address */ usb_pause_mtx(mtx, - USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE)); + USB_MS_TO_TICKS(usb_set_address_settle)); return (err); } @@ -1725,7 +1747,7 @@ usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data, struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; - if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) { + if ((iface == NULL) || (iface->idesc == NULL)) { return (USB_ERR_INVAL); } DPRINTFN(5, "len=%d\n", len); @@ -1932,6 +1954,27 @@ usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx) return (USB_ERR_INVAL); } retry: + /* + * Try to reset the High Speed parent HUB of a LOW- or FULL- + * speed device, if any. + */ + 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 (err) { + DPRINTF("Resetting parent High " + "Speed TT failed (%s).\n", + usbd_errstr(err)); + } + } + + /* Try to warm reset first */ + if (parent_hub->speed == USB_SPEED_SUPER) + usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no); + + /* Try to reset the parent HUB port. */ err = usbd_req_reset_port(parent_hub, mtx, udev->port_no); if (err) { DPRINTFN(0, "addr=%d, port reset failed, %s\n", @@ -2029,3 +2072,89 @@ usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx, USETW(req.wLength, 0); return (usbd_do_request(udev, mtx, &req, 0)); } + +/*------------------------------------------------------------------------* + * usbd_req_reset_tt + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, + uint8_t port) +{ + struct usb_device_request req; + + /* For single TT HUBs the port should be 1 */ + + if (udev->ddesc.bDeviceClass == UDCLASS_HUB && + udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) + port = 1; + + 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); + return (usbd_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usbd_req_clear_tt_buffer + * + * For single TT HUBs the port should be 1. + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, + uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint) +{ + struct usb_device_request req; + uint16_t wValue; + + /* For single TT HUBs the port should be 1 */ + + if (udev->ddesc.bDeviceClass == UDCLASS_HUB && + udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT) + port = 1; + + wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) | + ((endpoint & 0x80) << 8) | ((type & 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); + return (usbd_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usbd_req_set_port_link_state + * + * USB 3.0 specific request + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb_error_t +usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx, + uint8_t port, uint8_t link_state) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_CLASS_OTHER; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UHF_PORT_LINK_STATE); + req.wIndex[0] = port; + req.wIndex[1] = link_state; + USETW(req.wLength, 0); + return (usbd_do_request(udev, mtx, &req, 0)); +} diff --git a/freebsd/sys/dev/usb/usb_request.h b/freebsd/sys/dev/usb/usb_request.h index 12f373d5..74823af2 100644 --- a/freebsd/sys/dev/usb/usb_request.h +++ b/freebsd/sys/dev/usb/usb_request.h @@ -85,5 +85,11 @@ usb_error_t usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx, uint8_t port, uint8_t timeout); usb_error_t usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx, uint16_t depth); +usb_error_t usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx, + uint8_t port); +usb_error_t usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx, + uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint); +usb_error_t usbd_req_set_port_link_state(struct usb_device *udev, + struct mtx *mtx, uint8_t port, uint8_t link_state); #endif /* _USB_REQUEST_H_ */ diff --git a/freebsd/sys/dev/usb/usb_transfer.c b/freebsd/sys/dev/usb/usb_transfer.c index 17a492e5..a8b0b0ac 100644 --- a/freebsd/sys/dev/usb/usb_transfer.c +++ b/freebsd/sys/dev/usb/usb_transfer.c @@ -24,7 +24,7 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - */ + */ #include <sys/stdint.h> #include <sys/stddef.h> @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -45,6 +44,7 @@ #include <sys/callout.h> #include <sys/malloc.h> #include <sys/priv.h> +#include <sys/proc.h> #include <dev/usb/usb.h> #include <dev/usb/usbdi.h> @@ -62,6 +62,7 @@ #include <dev/usb/usb_controller.h> #include <dev/usb/usb_bus.h> +#include <dev/usb/usb_pf.h> #ifdef __rtems__ #include <machine/rtems-bsd-cache.h> #endif /* __rtems__ */ @@ -228,12 +229,12 @@ usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ - if (size >= PAGE_SIZE) { + if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_obj = 1; } else { /* compute number of objects per page */ - n_obj = (PAGE_SIZE / size); + n_obj = (USB_PAGE_SIZE / size); /* * Compute number of DMA chunks, rounded up * to nearest one: @@ -483,6 +484,8 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; + if (xfer->flags.pre_scale_frames != 0) + xfer->nframes <<= (3 - xfer->fps_shift); break; } @@ -674,9 +677,13 @@ usbd_transfer_setup_sub(struct usb_setup_params *parm) } xfer->max_data_length -= REQ_SIZE; } - /* setup "frlengths" */ + /* + * Setup "frlengths" and shadow "frlengths" for keeping the + * initial frame lengths when a USB transfer is complete. This + * information is useful when computing isochronous offsets. + */ xfer->frlengths = parm->xfer_length_ptr; - parm->xfer_length_ptr += n_frlengths; + parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; @@ -833,20 +840,17 @@ usbd_transfer_setup(struct usb_device *udev, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { - struct usb_xfer dummy; - struct usb_setup_params parm; const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; + struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; + usb_error_t error = 0; uint16_t n; uint16_t refcount; - - parm.err = 0; - refcount = 0; - info = NULL; + uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); @@ -865,31 +869,40 @@ usbd_transfer_setup(struct usb_device *udev, DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } - /* sanity checks */ + + /* more sanity checks */ + for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { - parm.err = USB_ERR_BAD_BUFSIZE; + error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { - parm.err = USB_ERR_NO_CALLBACK; + error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } - if (parm.err) { - goto done; - } - bzero(&parm, sizeof(parm)); + if (error) + return (error); + + /* Protect scratch area */ + do_unlock = usbd_enum_lock(udev); + + refcount = 0; + info = NULL; - parm.udev = udev; - parm.speed = usbd_get_speed(udev); - parm.hc_max_packet_count = 1; + parm = &udev->scratch.xfer_setup[0].parm; + memset(parm, 0, sizeof(*parm)); - if (parm.speed >= USB_SPEED_MAX) { - parm.err = USB_ERR_INVAL; + parm->udev = udev; + parm->speed = usbd_get_speed(udev); + parm->hc_max_packet_count = 1; + + if (parm->speed >= USB_SPEED_MAX) { + parm->err = USB_ERR_INVAL; goto done; } /* setup all transfers */ @@ -904,22 +917,22 @@ usbd_transfer_setup(struct usb_device *udev, info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; - info->memory_size = parm.size[0]; + info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA - info->dma_page_cache_start = USB_ADD_BYTES(buf, parm.size[4]); - info->dma_page_cache_end = USB_ADD_BYTES(buf, parm.size[5]); + info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); + info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif - info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm.size[5]); - info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm.size[2]); + info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); + info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, - parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, - xfer_mtx, &usb_bdma_done_event, 32, parm.dma_tag_max); + parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, + xfer_mtx, &usb_bdma_done_event, 32, parm->dma_tag_max); #endif info->bus = udev->bus; @@ -954,9 +967,9 @@ usbd_transfer_setup(struct usb_device *udev, } /* reset sizes */ - parm.size[0] = 0; - parm.buf = buf; - parm.size[0] += sizeof(info[0]); + parm->size[0] = 0; + parm->buf = buf; + parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { @@ -975,22 +988,22 @@ usbd_transfer_setup(struct usb_device *udev, if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; - parm.err = USB_ERR_NO_PIPE; + parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ - parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ - parm.curr_setup = setup; + parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ - xfer = USB_ADD_BYTES(buf, parm.size[0]); + xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; @@ -1005,26 +1018,26 @@ usbd_transfer_setup(struct usb_device *udev, * before we have allocated any * memory: */ - xfer = &dummy; - bzero(&dummy, sizeof(dummy)); + xfer = &udev->scratch.xfer_setup[0].dummy; + memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; - parm.size[0] += sizeof(xfer[0]); - parm.methods = xfer->endpoint->methods; - parm.curr_xfer = xfer; + parm->size[0] += sizeof(xfer[0]); + parm->methods = xfer->endpoint->methods; + parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ - (udev->bus->methods->xfer_setup) (&parm); + (udev->bus->methods->xfer_setup) (parm); /* check for error */ - if (parm.err) + if (parm->err) goto done; if (buf) { @@ -1039,7 +1052,7 @@ usbd_transfer_setup(struct usb_device *udev, */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) - parm.err = USB_ERR_INVAL; + parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; @@ -1062,22 +1075,22 @@ usbd_transfer_setup(struct usb_device *udev, } /* check for error */ - if (parm.err) + if (parm->err) goto done; } - if (buf || parm.err) { + if (buf != NULL || parm->err != 0) goto done; - } - if (refcount == 0) { - /* no transfers - nothing to do ! */ + + /* if no transfers, nothing to do */ + if (refcount == 0) goto done; - } + /* align data properly */ - parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ - parm.size[1] = parm.size[0]; + parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on @@ -1085,72 +1098,72 @@ usbd_transfer_setup(struct usb_device *udev, * for maximum number of DMA tags per endpoint * is two. */ - parm.dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX); + parm->dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ - parm.dma_tag_max += 8; + parm->dma_tag_max += 8; - parm.dma_tag_p += parm.dma_tag_max; + parm->dma_tag_p += parm->dma_tag_max; - parm.size[0] += ((uint8_t *)parm.dma_tag_p) - + parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ - parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ - parm.size[3] = parm.size[0]; + parm->size[3] = parm->size[0]; - parm.size[0] += ((uint8_t *)parm.dma_page_ptr) - + parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ - parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ - parm.size[4] = parm.size[0]; + parm->size[4] = parm->size[0]; - parm.size[0] += ((uint8_t *)parm.dma_page_cache_ptr) - + parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ - parm.size[5] = parm.size[0]; + parm->size[5] = parm->size[0]; - parm.size[0] += ((uint8_t *)parm.xfer_page_cache_ptr) - + parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ - parm.size[2] = parm.size[0]; + parm->size[2] = parm->size[0]; /* align data properly */ - parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); - parm.size[6] = parm.size[0]; + parm->size[6] = parm->size[0]; - parm.size[0] += ((uint8_t *)parm.xfer_length_ptr) - + parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ - parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); + parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ - buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO); + buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { - parm.err = USB_ERR_NOMEM; + parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", - parm.size[0]); + parm->size[0]); goto done; } - parm.dma_tag_p = USB_ADD_BYTES(buf, parm.size[1]); - parm.dma_page_ptr = USB_ADD_BYTES(buf, parm.size[3]); - parm.dma_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[4]); - parm.xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[5]); - parm.xfer_length_ptr = USB_ADD_BYTES(buf, parm.size[6]); + parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); + parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); + parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); + parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); + parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: @@ -1166,10 +1179,17 @@ done: usbd_transfer_unsetup_sub(info, 0); } } - if (parm.err) { + + /* check if any errors happened */ + if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); - } - return (parm.err); + + error = parm->err; + + if (do_unlock) + usbd_enum_unlock(udev); + + return (error); } /*------------------------------------------------------------------------* @@ -1178,7 +1198,9 @@ done: static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { +#if USB_HAVE_BUSDMA struct usb_page_cache *pc; +#endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); @@ -1605,9 +1627,12 @@ usbd_transfer_submit(struct usb_xfer *xfer) USB_BUS_UNLOCK(bus); return; } - /* compute total transfer length */ + /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { + /* make a copy of the frlenghts[] */ + xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; + /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ @@ -1699,11 +1724,12 @@ usbd_pipe_enter(struct usb_xfer *xfer) DPRINTF("enter\n"); + /* the transfer can now be cancelled */ + xfer->flags_int.can_cancel_immed = 1; + /* enter the transfer */ (ep->methods->enter) (xfer); - xfer->flags_int.can_cancel_immed = 1; - /* check for transfer error */ if (xfer->error) { /* some error has happened */ @@ -1940,6 +1966,17 @@ usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) return (&xfer->frbuffers[frindex]); } +void * +usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) +{ + struct usb_page_search page_info; + + KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); + + usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); + return (page_info.buffer); +} + /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * @@ -1996,6 +2033,22 @@ usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, *len = xfer->frlengths[frindex]; } +/*------------------------------------------------------------------------* + * usbd_xfer_old_frame_length + * + * This function returns the framelength of the given frame at the + * time the transfer was submitted. This function can be used to + * compute the starting data pointer of the next isochronous frame + * when an isochronous transfer has completed. + *------------------------------------------------------------------------*/ +usb_frlength_t +usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) +{ + KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); + + return (xfer->frlengths[frindex + xfer->max_frame_count]); +} + void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) @@ -2226,6 +2279,10 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq) } } +#if USB_HAVE_PF + if (xfer->usb_state != USB_ST_SETUP) + usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); +#endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); @@ -2413,12 +2470,18 @@ usbd_transfer_start_cb(void *arg) DPRINTF("start\n"); - /* start the transfer */ - (ep->methods->start) (xfer); +#if USB_HAVE_PF + usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); +#endif + /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; - /* check for error */ + /* start USB transfer, if no error */ + if (xfer->error == 0) + (ep->methods->start) (xfer); + + /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); @@ -2590,12 +2653,17 @@ usbd_pipe_start(struct usb_xfer_queue *pq) } DPRINTF("start\n"); - /* start USB transfer */ - (ep->methods->start) (xfer); - +#if USB_HAVE_PF + usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); +#endif + /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; - /* check for error */ + /* start USB transfer, if no error */ + if (xfer->error == 0) + (ep->methods->start) (xfer); + + /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); @@ -2690,7 +2758,8 @@ usbd_callback_wrapper_sub(struct usb_xfer *xfer) (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, - (void *)&usb_dma_delay_done_cb, temp); + (void (*)(void *))&usb_dma_delay_done_cb, + temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ @@ -2921,6 +2990,11 @@ repeat: usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* + * Reset clear stall error counter. + */ + udev->clear_stall_errors = 0; + + /* * Try to setup a new USB transfer for the * default control endpoint: */ diff --git a/freebsd/sys/dev/usb/usb_util.c b/freebsd/sys/dev/usb/usb_util.c index 14c0476a..aeafa8a5 100644 --- a/freebsd/sys/dev/usb/usb_util.c +++ b/freebsd/sys/dev/usb/usb_util.c @@ -34,7 +34,6 @@ #include <sys/systm.h> #include <sys/kernel.h> #include <sys/bus.h> -#include <sys/linker_set.h> #include <sys/module.h> #include <rtems/bsd/sys/lock.h> #include <sys/mutex.h> @@ -99,6 +98,7 @@ device_set_usb_desc(device_t dev) struct usb_interface *iface; char *temp_p; usb_error_t err; + uint8_t do_unlock; if (dev == NULL) { /* should not happen */ @@ -120,19 +120,26 @@ device_set_usb_desc(device_t dev) err = 0; } - temp_p = (char *)udev->bus->scratch[0].data; + /* Protect scratch area */ + do_unlock = usbd_enum_lock(udev); - if (!err) { + temp_p = (char *)udev->scratch.data; + + if (err == 0) { /* try to get the interface string ! */ - err = usbd_req_get_string_any - (udev, NULL, temp_p, - sizeof(udev->bus->scratch), iface->idesc->iInterface); + err = usbd_req_get_string_any(udev, NULL, temp_p, + sizeof(udev->scratch.data), + iface->idesc->iInterface); } - if (err) { + if (err != 0) { /* use default description */ usb_devinfo(udev, temp_p, - sizeof(udev->bus->scratch)); + sizeof(udev->scratch.data)); } + + if (do_unlock) + usbd_enum_unlock(udev); + device_set_desc_copy(dev, temp_p); device_printf(dev, "<%s> on %s\n", temp_p, device_get_nameunit(udev->bus->bdev)); diff --git a/freebsd/sys/dev/usb/usbdi.h b/freebsd/sys/dev/usb/usbdi.h index 1c2d412b..245777f0 100644 --- a/freebsd/sys/dev/usb/usbdi.h +++ b/freebsd/sys/dev/usb/usbdi.h @@ -37,6 +37,7 @@ struct usb_page_search; struct usb_process; struct usb_proc_msg; struct usb_mbuf; +struct usb_fs_privdata; struct mbuf; typedef enum { /* keep in sync with usb_errstr_table */ @@ -171,6 +172,7 @@ struct usb_interface { struct usb_host_interface *cur_altsetting; struct usb_device *linux_udev; void *bsd_priv_sc; /* device specific information */ + char *pnpinfo; /* additional PnP-info for this interface */ uint8_t num_altsetting; /* number of alternate settings */ uint8_t bsd_iface_index; }; @@ -194,6 +196,16 @@ struct usb_xfer_flags { uint8_t stall_pipe:1; /* set if the endpoint belonging to * this USB transfer should be stalled * before starting this transfer! */ + uint8_t pre_scale_frames:1; /* "usb_config->frames" is + * assumed to give the + * buffering time in + * milliseconds and is + * converted into the nearest + * number of frames when the + * USB transfer is setup. This + * option only has effect for + * ISOCHRONOUS transfers. + */ }; /* @@ -217,6 +229,27 @@ struct usb_config { }; /* + * Use these macro when defining USB device ID arrays if you want to + * have your driver module automatically loaded in host, device or + * both modes respectivly: + */ +#ifndef __rtems__ +#define STRUCT_USB_HOST_ID \ + struct usb_device_id __section("usb_host_id") +#define STRUCT_USB_DEVICE_ID \ + struct usb_device_id __section("usb_device_id") +#define STRUCT_USB_DUAL_ID \ + struct usb_device_id __section("usb_dual_id") +#else /* __rtems__ */ +#define STRUCT_USB_HOST_ID \ + struct usb_device_id +#define STRUCT_USB_DEVICE_ID \ + struct usb_device_id +#define STRUCT_USB_DUAL_ID \ + struct usb_device_id +#endif /* __rtems__ */ + +/* * The following structure is used when looking up an USB driver for * an USB device. It is inspired by the Linux structure called * "usb_device_id". @@ -247,12 +280,15 @@ struct usb_device_id { uint8_t match_flag_product:1; uint8_t match_flag_dev_lo:1; uint8_t match_flag_dev_hi:1; + uint8_t match_flag_dev_class:1; uint8_t match_flag_dev_subclass:1; uint8_t match_flag_dev_protocol:1; uint8_t match_flag_int_class:1; + uint8_t match_flag_int_subclass:1; uint8_t match_flag_int_protocol:1; + uint8_t match_flag_unused:6; #if USB_HAVE_COMPAT_LINUX /* which fields to match against */ @@ -268,7 +304,10 @@ struct usb_device_id { #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #endif -}; +} __aligned(32); + +/* check that the size of the structure above is correct */ +extern char usb_device_id_assert[(sizeof(struct usb_device_id) == 32) ? 1 : -1]; #define USB_VENDOR(vend) \ .match_flag_vendor = 1, .idVendor = (vend) @@ -346,7 +385,6 @@ struct usb_attach_arg { struct usb_interface *iface; /* current interface */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t port; - uint8_t use_generic; /* hint for generic drivers */ uint8_t dev_state; #define UAA_DEV_READY 0 #define UAA_DEV_DISABLED 1 @@ -421,7 +459,7 @@ struct usb_fifo_methods { struct usb_fifo_sc { struct usb_fifo *fp[2]; - struct cdev* dev; + struct usb_fs_privdata *dev; }; const char *usbd_errstr(usb_error_t error); @@ -444,6 +482,10 @@ enum usb_hc_mode usbd_get_mode(struct usb_device *udev); enum usb_dev_speed usbd_get_speed(struct usb_device *udev); void device_set_usb_desc(device_t dev); void usb_pause_mtx(struct mtx *mtx, int _ticks); +usb_error_t usbd_set_pnpinfo(struct usb_device *udev, + uint8_t iface_index, const char *pnpinfo); +usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, + uint16_t quirk); const struct usb_device_id *usbd_lookup_id_by_info( const struct usb_device_id *id, usb_size_t sizeof_id, @@ -483,10 +525,13 @@ void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_device_attached(struct usb_device *udev); +usb_frlength_t + usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes); struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex); +void *usbd_xfer_get_frame_buffer(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_softc(struct usb_xfer *xfer); void *usbd_xfer_get_priv(struct usb_xfer *xfer); void usbd_xfer_set_priv(struct usb_xfer *xfer, void *); @@ -528,10 +573,11 @@ void 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); 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); int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, - struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, + struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode); void usb_fifo_detach(struct usb_fifo_sc *f_sc); int usb_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize, diff --git a/freebsd/sys/dev/usb/usbhid.h b/freebsd/sys/dev/usb/usbhid.h index c07454f7..f40232aa 100644 --- a/freebsd/sys/dev/usb/usbhid.h +++ b/freebsd/sys/dev/usb/usbhid.h @@ -226,14 +226,16 @@ void hid_end_parse(struct hid_data *s); int hid_get_item(struct hid_data *s, struct hid_item *h); int hid_report_size(const void *buf, usb_size_t len, enum hid_kind k, uint8_t *id); -int hid_locate(const void *desc, usb_size_t size, uint32_t usage, +int hid_locate(const void *desc, usb_size_t size, int32_t usage, enum hid_kind kind, uint8_t index, struct hid_location *loc, uint32_t *flags, uint8_t *id); int32_t hid_get_data(const uint8_t *buf, usb_size_t len, struct hid_location *loc); uint32_t hid_get_data_unsigned(const uint8_t *buf, usb_size_t len, struct hid_location *loc); -int hid_is_collection(const void *desc, usb_size_t size, uint32_t usage); +void hid_put_data_unsigned(uint8_t *buf, usb_size_t len, + struct hid_location *loc, unsigned int value); +int hid_is_collection(const void *desc, usb_size_t size, int32_t usage); struct usb_hid_descriptor *hid_get_descriptor_from_usb( struct usb_config_descriptor *cd, struct usb_interface_descriptor *id); |