diff options
48 files changed, 2454 insertions, 854 deletions
diff --git a/lib/libusb20/libusb20_desc.c b/lib/libusb20/libusb20_desc.c index bba4d25..e0d2c54 100644 --- a/lib/libusb20/libusb20_desc.c +++ b/lib/libusb20/libusb20_desc.c @@ -238,23 +238,37 @@ const uint8_t * libusb20_desc_foreach(const struct libusb20_me_struct *pdesc, const uint8_t *psubdesc) { - const void *end; + const uint8_t *start; + const uint8_t *end; + const uint8_t *desc_next; - if (pdesc == NULL) { + /* be NULL safe */ + if (pdesc == NULL) return (NULL); - } - end = LIBUSB20_ADD_BYTES(pdesc->ptr, pdesc->len); - if (psubdesc == NULL) { - psubdesc = LIBUSB20_ADD_BYTES(pdesc->ptr, 0); - } else { - psubdesc = LIBUSB20_ADD_BYTES(psubdesc, psubdesc[0]); - } - return (((((const void *)psubdesc) >= ((void *)(pdesc->ptr))) && - (((const void *)psubdesc) < end) && - (LIBUSB20_ADD_BYTES(psubdesc, psubdesc[0]) >= ((void *)(pdesc->ptr))) && - (LIBUSB20_ADD_BYTES(psubdesc, psubdesc[0]) <= end) && - (psubdesc[0] >= 3)) ? psubdesc : NULL); + start = (const uint8_t *)pdesc->ptr; + end = LIBUSB20_ADD_BYTES(start, pdesc->len); + + /* get start of next descriptor */ + if (psubdesc == NULL) + psubdesc = start; + else + psubdesc = psubdesc + psubdesc[0]; + + /* check that the next USB descriptor is within the range */ + if ((psubdesc < start) || (psubdesc >= end)) + return (NULL); /* out of range, or EOD */ + + /* check start of the second next USB descriptor, if any */ + desc_next = psubdesc + psubdesc[0]; + if ((desc_next < start) || (desc_next > end)) + return (NULL); /* out of range */ + + /* check minimum descriptor length */ + if (psubdesc[0] < 3) + return (NULL); /* too short descriptor */ + + return (psubdesc); /* return start of next descriptor */ } /*------------------------------------------------------------------------* diff --git a/sys/dev/usb2/controller/at91dci.c b/sys/dev/usb2/controller/at91dci.c index a4e43df..5ed6924 100644 --- a/sys/dev/usb2/controller/at91dci.c +++ b/sys/dev/usb2/controller/at91dci.c @@ -261,42 +261,28 @@ at91dci_pull_down(struct at91dci_softc *sc) } static void -at91dci_wakeup_peer(struct at91dci_softc *sc) +at91dci_wakeup_peer(struct usb2_xfer *xfer) { - uint32_t temp; + struct at91dci_softc *sc = xfer->usb2_sc; + uint8_t use_polling; if (!(sc->sc_flags.status_suspend)) { return; } - temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE); - - if (!(temp & AT91_UDP_GSTATE_ESR)) { - return; - } - AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp); -} - -static void -at91dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) -{ - struct at91dci_softc *sc; - uint32_t temp; - - DPRINTFN(5, "is_on=%u\n", is_on); - - USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); - - sc = AT9100_DCI_BUS2SC(udev->bus); + use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0; - temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE); + AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, AT91_UDP_GSTATE_ESR); - if (is_on) { - temp |= AT91_UDP_GSTATE_ESR; + /* wait 8 milliseconds */ + if (use_polling) { + /* polling */ + DELAY(8000); } else { - temp &= ~AT91_UDP_GSTATE_ESR; + /* Wait for reset to complete. */ + usb2_pause_mtx(&sc->sc_bus.bus_mtx, 8); } - AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp); + AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, 0); } static void @@ -2120,7 +2106,7 @@ tr_handle_clear_port_feature: switch (value) { case UHF_PORT_SUSPEND: - at91dci_wakeup_peer(sc); + at91dci_wakeup_peer(xfer); break; case UHF_PORT_ENABLE: @@ -2492,5 +2478,4 @@ struct usb2_bus_methods at91dci_bus_methods = .set_stall = &at91dci_set_stall, .clear_stall = &at91dci_clear_stall, .vbus_interrupt = &at91dci_vbus_interrupt, - .rem_wakeup_set = &at91dci_rem_wakeup_set, }; diff --git a/sys/dev/usb2/controller/ehci2.c b/sys/dev/usb2/controller/ehci2.c index fdecd7c..d41fde7 100644 --- a/sys/dev/usb2/controller/ehci2.c +++ b/sys/dev/usb2/controller/ehci2.c @@ -84,7 +84,7 @@ SYSCTL_INT(_hw_usb2_ehci, OID_AUTO, no_hs, CTLFLAG_RW, &ehcinohighspeed, 0, "Disable High Speed USB"); static void ehci_dump_regs(ehci_softc_t *sc); -static void ehci_dump_sqh(ehci_qh_t *sqh); +static void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh); #endif @@ -101,7 +101,7 @@ extern struct usb2_pipe_methods ehci_root_intr_methods; static usb2_config_td_command_t ehci_root_ctrl_task; static void ehci_do_poll(struct usb2_bus *bus); -static void ehci_root_ctrl_poll(struct ehci_softc *sc); +static void ehci_root_ctrl_poll(ehci_softc_t *sc); static void ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error); static uint8_t ehci_check_transfer(struct usb2_xfer *xfer); static void ehci_timeout(void *arg); @@ -110,6 +110,7 @@ static usb2_sw_transfer_func_t ehci_root_intr_done; static usb2_sw_transfer_func_t ehci_root_ctrl_done; struct ehci_std_temp { + ehci_softc_t *sc; struct usb2_page_cache *pc; ehci_qtd_t *td; ehci_qtd_t *td_next; @@ -123,10 +124,27 @@ struct ehci_std_temp { uint8_t short_frames_ok; }; +/* + * Byte-order conversion functions. + */ +static uint32_t +htoehci32(ehci_softc_t *sc, const uint32_t v) +{ + return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ? + htobe32(v) : htole32(v)); +} + +static uint32_t +ehci32toh(ehci_softc_t *sc, const uint32_t v) +{ + return ((sc->sc_flags & EHCI_SCFLG_BIGEDESC) ? + be32toh(v) : le32toh(v)); +} + void ehci_iterate_hw_softc(struct usb2_bus *bus, usb2_bus_mem_sub_cb_t *cb) { - struct ehci_softc *sc = EHCI_BUS2SC(bus); + ehci_softc_t *sc = EHCI_BUS2SC(bus); uint32_t i; cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg, @@ -280,21 +298,21 @@ ehci_init(ehci_softc_t *sc) sc->sc_intr_p_last[i] = qh; qh->qh_self = - htole32(buf_res.physaddr) | - htole32(EHCI_LINK_QH); + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_QH); qh->qh_endp = - htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); + htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH)); qh->qh_endphub = - htole32(EHCI_QH_SET_MULT(1)); + htoehci32(sc, EHCI_QH_SET_MULT(1)); qh->qh_curqtd = 0; qh->qh_qtd.qtd_next = - htole32(EHCI_LINK_TERMINATE); + htoehci32(sc, EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_altnext = - htole32(EHCI_LINK_TERMINATE); + htoehci32(sc, EHCI_LINK_TERMINATE); qh->qh_qtd.qtd_status = - htole32(EHCI_QTD_HALTED); + htoehci32(sc, EHCI_QTD_HALTED); } /* @@ -329,7 +347,7 @@ ehci_init(ehci_softc_t *sc) qh = sc->sc_intr_p_last[0]; /* the last (1ms) QH terminates */ - qh->qh_link = htole32(EHCI_LINK_TERMINATE); + qh->qh_link = htoehci32(sc, EHCI_LINK_TERMINATE); } for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) { ehci_sitd_t *sitd; @@ -350,11 +368,11 @@ ehci_init(ehci_softc_t *sc) /* initialize full speed isochronous */ sitd->sitd_self = - htole32(buf_res.physaddr) | - htole32(EHCI_LINK_SITD); + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_SITD); sitd->sitd_back = - htole32(EHCI_LINK_TERMINATE); + htoehci32(sc, EHCI_LINK_TERMINATE); sitd->sitd_next = sc->sc_intr_p_last[i | (EHCI_VIRTUAL_FRAMELIST_COUNT / 2)]->qh_self; @@ -375,8 +393,8 @@ ehci_init(ehci_softc_t *sc) /* initialize high speed isochronous */ itd->itd_self = - htole32(buf_res.physaddr) | - htole32(EHCI_LINK_ITD); + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_ITD); itd->itd_next = sitd->sitd_self; @@ -421,20 +439,20 @@ ehci_init(ehci_softc_t *sc) /* init dummy QH that starts the async list */ qh->qh_self = - htole32(buf_res.physaddr) | - htole32(EHCI_LINK_QH); + htoehci32(sc, buf_res.physaddr) | + htoehci32(sc, EHCI_LINK_QH); /* fill the QH */ qh->qh_endp = - htole32(EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); - qh->qh_endphub = htole32(EHCI_QH_SET_MULT(1)); + htoehci32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL); + qh->qh_endphub = htoehci32(sc, EHCI_QH_SET_MULT(1)); qh->qh_link = qh->qh_self; qh->qh_curqtd = 0; /* fill the overlay qTD */ - qh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); - qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); - qh->qh_qtd.qtd_status = htole32(EHCI_QTD_HALTED); + qh->qh_qtd.qtd_next = htoehci32(sc, EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_altnext = htoehci32(sc, EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_status = htoehci32(sc, EHCI_QTD_HALTED); } /* flush all cache into memory */ @@ -442,7 +460,7 @@ ehci_init(ehci_softc_t *sc) #if USB_DEBUG if (ehcidebug) { - ehci_dump_sqh(sc->sc_async_p_last); + ehci_dump_sqh(sc, sc->sc_async_p_last); } #endif @@ -490,7 +508,7 @@ done: * shut down the controller when the system is going down */ void -ehci_detach(struct ehci_softc *sc) +ehci_detach(ehci_softc_t *sc) { USB_BUS_LOCK(&sc->sc_bus); @@ -510,7 +528,7 @@ ehci_detach(struct ehci_softc *sc) } void -ehci_suspend(struct ehci_softc *sc) +ehci_suspend(ehci_softc_t *sc) { uint32_t cmd; uint32_t hcr; @@ -564,7 +582,7 @@ ehci_suspend(struct ehci_softc *sc) } void -ehci_resume(struct ehci_softc *sc) +ehci_resume(ehci_softc_t *sc) { struct usb2_page_search buf_res; uint32_t cmd; @@ -723,9 +741,9 @@ ehci_dump_regs(ehci_softc_t *sc) } static void -ehci_dump_link(uint32_t link, int type) +ehci_dump_link(ehci_softc_t *sc, uint32_t link, int type) { - link = le32toh(link); + link = ehci32toh(sc, link); printf("0x%08x", link); if (link & EHCI_LINK_TERMINATE) printf("<T>"); @@ -752,16 +770,16 @@ ehci_dump_link(uint32_t link, int type) } static void -ehci_dump_qtd(ehci_qtd_t *qtd) +ehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd) { uint32_t s; printf(" next="); - ehci_dump_link(qtd->qtd_next, 0); + ehci_dump_link(sc, qtd->qtd_next, 0); printf(" altnext="); - ehci_dump_link(qtd->qtd_altnext, 0); + ehci_dump_link(sc, qtd->qtd_altnext, 0); printf("\n"); - s = le32toh(qtd->qtd_status); + s = ehci32toh(sc, qtd->qtd_status); printf(" status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n", s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s), EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s)); @@ -778,35 +796,35 @@ ehci_dump_qtd(ehci_qtd_t *qtd) for (s = 0; s < 5; s++) { printf(" buffer[%d]=0x%08x\n", s, - le32toh(qtd->qtd_buffer[s])); + ehci32toh(sc, qtd->qtd_buffer[s])); } for (s = 0; s < 5; s++) { printf(" buffer_hi[%d]=0x%08x\n", s, - le32toh(qtd->qtd_buffer_hi[s])); + ehci32toh(sc, qtd->qtd_buffer_hi[s])); } } static uint8_t -ehci_dump_sqtd(ehci_qtd_t *sqtd) +ehci_dump_sqtd(ehci_softc_t *sc, ehci_qtd_t *sqtd) { uint8_t temp; usb2_pc_cpu_invalidate(sqtd->page_cache); - printf("QTD(%p) at 0x%08x:\n", sqtd, le32toh(sqtd->qtd_self)); - ehci_dump_qtd(sqtd); - temp = (sqtd->qtd_next & htole32(EHCI_LINK_TERMINATE)) ? 1 : 0; + printf("QTD(%p) at 0x%08x:\n", sqtd, ehci32toh(sc, sqtd->qtd_self)); + ehci_dump_qtd(sc, sqtd); + temp = (sqtd->qtd_next & htoehci32(sc, EHCI_LINK_TERMINATE)) ? 1 : 0; return (temp); } static void -ehci_dump_sqtds(ehci_qtd_t *sqtd) +ehci_dump_sqtds(ehci_softc_t *sc, ehci_qtd_t *sqtd) { uint16_t i; uint8_t stop; stop = 0; for (i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) { - stop = ehci_dump_sqtd(sqtd); + stop = ehci_dump_sqtd(sc, sqtd); } if (sqtd) { printf("dump aborted, too many TDs\n"); @@ -814,16 +832,17 @@ ehci_dump_sqtds(ehci_qtd_t *sqtd) } static void -ehci_dump_sqh(ehci_qh_t *qh) +ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *qh) { - uint32_t endp, endphub; + uint32_t endp; + uint32_t endphub; usb2_pc_cpu_invalidate(qh->page_cache); - printf("QH(%p) at 0x%08x:\n", qh, le32toh(qh->qh_self) & ~0x1F); + printf("QH(%p) at 0x%08x:\n", qh, ehci32toh(sc, qh->qh_self) & ~0x1F); printf(" link="); - ehci_dump_link(qh->qh_link, 1); + ehci_dump_link(sc, qh->qh_link, 1); printf("\n"); - endp = le32toh(qh->qh_endp); + endp = ehci32toh(sc, qh->qh_endp); printf(" endp=0x%08x\n", endp); printf(" addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n", EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp), @@ -832,90 +851,90 @@ ehci_dump_sqh(ehci_qh_t *qh) printf(" mpl=0x%x ctl=%d nrl=%d\n", EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp), EHCI_QH_GET_NRL(endp)); - endphub = le32toh(qh->qh_endphub); + endphub = ehci32toh(sc, qh->qh_endphub); printf(" endphub=0x%08x\n", endphub); printf(" smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n", EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub), EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub), EHCI_QH_GET_MULT(endphub)); printf(" curqtd="); - ehci_dump_link(qh->qh_curqtd, 0); + ehci_dump_link(sc, qh->qh_curqtd, 0); printf("\n"); printf("Overlay qTD:\n"); - ehci_dump_qtd((void *)&qh->qh_qtd); + ehci_dump_qtd(sc, (void *)&qh->qh_qtd); } static void -ehci_dump_sitd(ehci_sitd_t *sitd) +ehci_dump_sitd(ehci_softc_t *sc, ehci_sitd_t *sitd) { usb2_pc_cpu_invalidate(sitd->page_cache); - printf("SITD(%p) at 0x%08x\n", sitd, le32toh(sitd->sitd_self) & ~0x1F); - printf(" next=0x%08x\n", le32toh(sitd->sitd_next)); + printf("SITD(%p) at 0x%08x\n", sitd, ehci32toh(sc, sitd->sitd_self) & ~0x1F); + printf(" next=0x%08x\n", ehci32toh(sc, sitd->sitd_next)); printf(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n", - le32toh(sitd->sitd_portaddr), - (sitd->sitd_portaddr & htole32(EHCI_SITD_SET_DIR_IN)) + ehci32toh(sc, sitd->sitd_portaddr), + (sitd->sitd_portaddr & htoehci32(sc, EHCI_SITD_SET_DIR_IN)) ? "in" : "out", - EHCI_SITD_GET_ADDR(le32toh(sitd->sitd_portaddr)), - EHCI_SITD_GET_ENDPT(le32toh(sitd->sitd_portaddr)), - EHCI_SITD_GET_PORT(le32toh(sitd->sitd_portaddr)), - EHCI_SITD_GET_HUBA(le32toh(sitd->sitd_portaddr))); - printf(" mask=0x%08x\n", le32toh(sitd->sitd_mask)); - printf(" status=0x%08x <%s> len=0x%x\n", le32toh(sitd->sitd_status), - (sitd->sitd_status & htole32(EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", - EHCI_SITD_GET_LEN(le32toh(sitd->sitd_status))); + EHCI_SITD_GET_ADDR(ehci32toh(sc, sitd->sitd_portaddr)), + EHCI_SITD_GET_ENDPT(ehci32toh(sc, sitd->sitd_portaddr)), + EHCI_SITD_GET_PORT(ehci32toh(sc, sitd->sitd_portaddr)), + EHCI_SITD_GET_HUBA(ehci32toh(sc, sitd->sitd_portaddr))); + printf(" mask=0x%08x\n", ehci32toh(sc, sitd->sitd_mask)); + printf(" status=0x%08x <%s> len=0x%x\n", ehci32toh(sc, sitd->sitd_status), + (sitd->sitd_status & htoehci32(sc, EHCI_SITD_ACTIVE)) ? "ACTIVE" : "", + EHCI_SITD_GET_LEN(ehci32toh(sc, sitd->sitd_status))); printf(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n", - le32toh(sitd->sitd_back), - le32toh(sitd->sitd_bp[0]), - le32toh(sitd->sitd_bp[1]), - le32toh(sitd->sitd_bp_hi[0]), - le32toh(sitd->sitd_bp_hi[1])); + ehci32toh(sc, sitd->sitd_back), + ehci32toh(sc, sitd->sitd_bp[0]), + ehci32toh(sc, sitd->sitd_bp[1]), + ehci32toh(sc, sitd->sitd_bp_hi[0]), + ehci32toh(sc, sitd->sitd_bp_hi[1])); } static void -ehci_dump_itd(ehci_itd_t *itd) +ehci_dump_itd(ehci_softc_t *sc, ehci_itd_t *itd) { usb2_pc_cpu_invalidate(itd->page_cache); - printf("ITD(%p) at 0x%08x\n", itd, le32toh(itd->itd_self) & ~0x1F); - printf(" next=0x%08x\n", le32toh(itd->itd_next)); - printf(" status[0]=0x%08x; <%s>\n", le32toh(itd->itd_status[0]), - (itd->itd_status[0] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[1]=0x%08x; <%s>\n", le32toh(itd->itd_status[1]), - (itd->itd_status[1] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[2]=0x%08x; <%s>\n", le32toh(itd->itd_status[2]), - (itd->itd_status[2] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[3]=0x%08x; <%s>\n", le32toh(itd->itd_status[3]), - (itd->itd_status[3] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[4]=0x%08x; <%s>\n", le32toh(itd->itd_status[4]), - (itd->itd_status[4] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[5]=0x%08x; <%s>\n", le32toh(itd->itd_status[5]), - (itd->itd_status[5] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[6]=0x%08x; <%s>\n", le32toh(itd->itd_status[6]), - (itd->itd_status[6] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" status[7]=0x%08x; <%s>\n", le32toh(itd->itd_status[7]), - (itd->itd_status[7] & htole32(EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); - printf(" bp[0]=0x%08x\n", le32toh(itd->itd_bp[0])); + printf("ITD(%p) at 0x%08x\n", itd, ehci32toh(sc, itd->itd_self) & ~0x1F); + printf(" next=0x%08x\n", ehci32toh(sc, itd->itd_next)); + printf(" status[0]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[0]), + (itd->itd_status[0] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[1]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[1]), + (itd->itd_status[1] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[2]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[2]), + (itd->itd_status[2] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[3]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[3]), + (itd->itd_status[3] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[4]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[4]), + (itd->itd_status[4] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[5]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[5]), + (itd->itd_status[5] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[6]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[6]), + (itd->itd_status[6] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" status[7]=0x%08x; <%s>\n", ehci32toh(sc, itd->itd_status[7]), + (itd->itd_status[7] & htoehci32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : ""); + printf(" bp[0]=0x%08x\n", ehci32toh(sc, itd->itd_bp[0])); printf(" addr=0x%02x; endpt=0x%01x\n", - EHCI_ITD_GET_ADDR(le32toh(itd->itd_bp[0])), - EHCI_ITD_GET_ENDPT(le32toh(itd->itd_bp[0]))); - printf(" bp[1]=0x%08x\n", le32toh(itd->itd_bp[1])); + EHCI_ITD_GET_ADDR(ehci32toh(sc, itd->itd_bp[0])), + EHCI_ITD_GET_ENDPT(ehci32toh(sc, itd->itd_bp[0]))); + printf(" bp[1]=0x%08x\n", ehci32toh(sc, itd->itd_bp[1])); printf(" dir=%s; mpl=0x%02x\n", - (le32toh(itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", - EHCI_ITD_GET_MPL(le32toh(itd->itd_bp[1]))); + (ehci32toh(sc, itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out", + EHCI_ITD_GET_MPL(ehci32toh(sc, itd->itd_bp[1]))); printf(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n", - le32toh(itd->itd_bp[2]), - le32toh(itd->itd_bp[3]), - le32toh(itd->itd_bp[4]), - le32toh(itd->itd_bp[5]), - le32toh(itd->itd_bp[6])); + ehci32toh(sc, itd->itd_bp[2]), + ehci32toh(sc, itd->itd_bp[3]), + ehci32toh(sc, itd->itd_bp[4]), + ehci32toh(sc, itd->itd_bp[5]), + ehci32toh(sc, itd->itd_bp[6])); printf(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n" " 0x%08x,0x%08x,0x%08x\n", - le32toh(itd->itd_bp_hi[0]), - le32toh(itd->itd_bp_hi[1]), - le32toh(itd->itd_bp_hi[2]), - le32toh(itd->itd_bp_hi[3]), - le32toh(itd->itd_bp_hi[4]), - le32toh(itd->itd_bp_hi[5]), - le32toh(itd->itd_bp_hi[6])); + ehci32toh(sc, itd->itd_bp_hi[0]), + ehci32toh(sc, itd->itd_bp_hi[1]), + ehci32toh(sc, itd->itd_bp_hi[2]), + ehci32toh(sc, itd->itd_bp_hi[3]), + ehci32toh(sc, itd->itd_bp_hi[4]), + ehci32toh(sc, itd->itd_bp_hi[5]), + ehci32toh(sc, itd->itd_bp_hi[6])); } static void @@ -936,12 +955,12 @@ ehci_dump_isoc(ehci_softc_t *sc) sitd = sc->sc_isoc_fs_p_last[pos]; while (itd && max && max--) { - ehci_dump_itd(itd); + ehci_dump_itd(sc, itd); itd = itd->prev; } while (sitd && max && max--) { - ehci_dump_sitd(sitd); + ehci_dump_sitd(sc, sitd); sitd = sitd->prev; } } @@ -1022,6 +1041,11 @@ _ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last) { DPRINTFN(11, "%p to %p\n", sqh, last); + if (sqh->prev != NULL) { + /* should not happen */ + DPRINTFN(0, "QH already linked!\n"); + return (last); + } /* (sc->sc_bus.mtx) must be locked */ sqh->next = last->next; @@ -1040,12 +1064,6 @@ _ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last) usb2_pc_cpu_flush(last->page_cache); -#if USB_DEBUG - if (ehcidebug > 5) { - printf("%s:\n", __FUNCTION__); - ehci_dump_sqh(sqh); - } -#endif return (sqh); } @@ -1109,14 +1127,6 @@ _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) sqh->next->prev = sqh->prev; usb2_pc_cpu_flush(sqh->next->page_cache); } - /* - * set the Terminate-bit in the e_next of the QH, in case - * the transferred packet was short so that the QH still - * points at the last used TD - */ - - sqh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE); - last = ((last == sqh) ? sqh->prev : last); sqh->prev = 0; @@ -1129,6 +1139,7 @@ _ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last) static usb2_error_t ehci_non_isoc_done_sub(struct usb2_xfer *xfer) { + ehci_softc_t *sc = xfer->usb2_sc; ehci_qtd_t *td; ehci_qtd_t *td_alt_next; uint32_t status; @@ -1140,7 +1151,7 @@ ehci_non_isoc_done_sub(struct usb2_xfer *xfer) while (1) { usb2_pc_cpu_invalidate(td->page_cache); - status = le32toh(td->qtd_status); + status = ehci32toh(sc, td->qtd_status); len = EHCI_QTD_GET_BYTES(status); @@ -1232,7 +1243,9 @@ ehci_non_isoc_done(struct usb2_xfer *xfer) #if USB_DEBUG if (ehcidebug > 10) { - ehci_dump_sqtds(xfer->td_transfer_first); + ehci_softc_t *sc = xfer->usb2_sc; + + ehci_dump_sqtds(sc, xfer->td_transfer_first); } #endif @@ -1282,6 +1295,7 @@ static uint8_t ehci_check_transfer(struct usb2_xfer *xfer) { struct usb2_pipe_methods *methods = xfer->pipe->methods; + ehci_softc_t *sc = xfer->usb2_sc; uint32_t status; @@ -1294,13 +1308,13 @@ ehci_check_transfer(struct usb2_xfer *xfer) td = xfer->td_transfer_last; usb2_pc_cpu_invalidate(td->page_cache); - status = le32toh(td->sitd_status); + status = ehci32toh(sc, td->sitd_status); /* also check if first is complete */ td = xfer->td_transfer_first; usb2_pc_cpu_invalidate(td->page_cache); - status |= le32toh(td->sitd_status); + status |= ehci32toh(sc, td->sitd_status); if (!(status & EHCI_SITD_ACTIVE)) { ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); @@ -1329,7 +1343,7 @@ ehci_check_transfer(struct usb2_xfer *xfer) td->itd_status[6] | td->itd_status[7]; /* if no transactions are active we continue */ - if (!(status & htole32(EHCI_ITD_ACTIVE))) { + if (!(status & htoehci32(sc, EHCI_ITD_ACTIVE))) { ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION); goto transferred; } @@ -1346,7 +1360,7 @@ ehci_check_transfer(struct usb2_xfer *xfer) while (1) { usb2_pc_cpu_invalidate(td->page_cache); - status = le32toh(td->qtd_status); + status = ehci32toh(sc, td->qtd_status); /* * if there is an active TD the transfer isn't done @@ -1520,7 +1534,7 @@ ehci_timeout(void *arg) static void ehci_do_poll(struct usb2_bus *bus) { - struct ehci_softc *sc = EHCI_BUS2SC(bus); + ehci_softc_t *sc = EHCI_BUS2SC(bus); USB_BUS_LOCK(&sc->sc_bus); ehci_interrupt_poll(sc); @@ -1542,7 +1556,7 @@ ehci_setup_standard_chain_sub(struct ehci_std_temp *temp) uint8_t shortpkt_old; uint8_t precompute; - qtd_altnext = htole32(EHCI_LINK_TERMINATE); + qtd_altnext = htoehci32(temp->sc, EHCI_LINK_TERMINATE); td_alt_next = NULL; buf_offset = 0; shortpkt_old = temp->shortpkt; @@ -1599,7 +1613,8 @@ restart: /* fill out current TD */ td->qtd_status = - temp->qtd_status | htole32(EHCI_QTD_SET_BYTES(average)); + temp->qtd_status | + htoehci32(temp->sc, EHCI_QTD_SET_BYTES(average)); if (average == 0) { @@ -1607,7 +1622,8 @@ restart: /* update data toggle, ZLP case */ - temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); + temp->qtd_status ^= + htoehci32(temp->sc, EHCI_QTD_TOGGLE_MASK); } td->len = 0; @@ -1627,7 +1643,8 @@ restart: if (((average + temp->max_frame_size - 1) / temp->max_frame_size) & 1) { - temp->qtd_status ^= htole32(EHCI_QTD_TOGGLE_MASK); + temp->qtd_status ^= + htoehci32(temp->sc, EHCI_QTD_TOGGLE_MASK); } } td->len = average; @@ -1639,7 +1656,8 @@ restart: /* fill out buffer pointers */ usb2_get_page(temp->pc, buf_offset, &buf_res); - td->qtd_buffer[0] = htole32(buf_res.physaddr); + td->qtd_buffer[0] = + htoehci32(temp->sc, buf_res.physaddr); td->qtd_buffer_hi[0] = 0; x = 1; @@ -1648,7 +1666,9 @@ restart: average -= EHCI_PAGE_SIZE; buf_offset += EHCI_PAGE_SIZE; usb2_get_page(temp->pc, buf_offset, &buf_res); - td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF)); + td->qtd_buffer[x] = + htoehci32(temp->sc, + buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[x] = 0; x++; } @@ -1663,7 +1683,9 @@ restart: */ buf_offset += average; usb2_get_page(temp->pc, buf_offset - 1, &buf_res); - td->qtd_buffer[x] = htole32(buf_res.physaddr & (~0xFFF)); + td->qtd_buffer[x] = + htoehci32(temp->sc, + buf_res.physaddr & (~0xFFF)); td->qtd_buffer_hi[x] = 0; } @@ -1717,6 +1739,7 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) temp.average = xfer->max_usb2_frame_size; temp.max_frame_size = xfer->max_frame_size; + temp.sc = xfer->usb2_sc; /* toggle the DMA set we are using */ xfer->flags_int.curr_dma_set ^= 1; @@ -1736,7 +1759,8 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) if (xfer->flags_int.control_xfr) { if (xfer->pipe->toggle_next) { /* DATA1 is next */ - temp.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); + temp.qtd_status |= + htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); } temp.auto_data_toggle = 0; } else { @@ -1745,14 +1769,16 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) if (usb2_get_speed(xfer->udev) != USB_SPEED_HIGH) { /* max 3 retries */ - temp.qtd_status |= htole32(EHCI_QTD_SET_CERR(3)); + temp.qtd_status |= + htoehci32(temp.sc, EHCI_QTD_SET_CERR(3)); } /* check if we should prepend a setup message */ if (xfer->flags_int.control_xfr) { if (xfer->flags_int.control_hdr) { - temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3)); + temp.qtd_status &= + htoehci32(temp.sc, EHCI_QTD_SET_CERR(3)); temp.qtd_status |= htole32 (EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) | @@ -1783,7 +1809,8 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) } /* keep previous data toggle and error count */ - temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3) | + temp.qtd_status &= + htoehci32(temp.sc, EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(1)); if (temp.len == 0) { @@ -1803,9 +1830,9 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) temp.qtd_status |= (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) ? - htole32(EHCI_QTD_ACTIVE | + htoehci32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) : - htole32(EHCI_QTD_ACTIVE | + htoehci32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT)); ehci_setup_standard_chain_sub(&temp); @@ -1821,14 +1848,14 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) * direction. */ - temp.qtd_status &= htole32(EHCI_QTD_SET_CERR(3) | + temp.qtd_status &= htoehci32(temp.sc, EHCI_QTD_SET_CERR(3) | EHCI_QTD_SET_TOGGLE(1)); temp.qtd_status |= (UE_GET_DIR(xfer->endpoint) == UE_DIR_OUT) ? - htole32(EHCI_QTD_ACTIVE | + htoehci32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) | EHCI_QTD_SET_TOGGLE(1)) : - htole32(EHCI_QTD_ACTIVE | + htoehci32(temp.sc, EHCI_QTD_ACTIVE | EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) | EHCI_QTD_SET_TOGGLE(1)); @@ -1841,9 +1868,9 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) td = temp.td; /* the last TD terminates the transfer: */ - td->qtd_next = htole32(EHCI_LINK_TERMINATE); - td->qtd_altnext = htole32(EHCI_LINK_TERMINATE); - td->qtd_status |= htole32(EHCI_QTD_IOC); + td->qtd_next = htoehci32(temp.sc, EHCI_LINK_TERMINATE); + td->qtd_altnext = htoehci32(temp.sc, EHCI_LINK_TERMINATE); + td->qtd_status |= htoehci32(temp.sc, EHCI_QTD_IOC); usb2_pc_cpu_flush(td->page_cache); @@ -1855,7 +1882,8 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) if (ehcidebug > 8) { DPRINTF("nexttog=%d; data before transfer:\n", xfer->pipe->toggle_next); - ehci_dump_sqtds(xfer->td_transfer_first); + ehci_dump_sqtds(temp.sc, + xfer->td_transfer_first); } #endif @@ -1892,7 +1920,7 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) } } - qh->qh_endp = htole32(qh_endp); + qh->qh_endp = htoehci32(temp.sc, qh_endp); qh_endphub = (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) | @@ -1901,38 +1929,42 @@ ehci_setup_standard_chain(struct usb2_xfer *xfer, ehci_qh_t **qh_last) EHCI_QH_SET_HUBA(xfer->udev->hs_hub_addr) | EHCI_QH_SET_PORT(xfer->udev->hs_port_no)); - qh->qh_endphub = htole32(qh_endphub); - qh->qh_curqtd = htole32(0); + qh->qh_endphub = htoehci32(temp.sc, qh_endphub); + qh->qh_curqtd = htoehci32(temp.sc, 0); /* fill the overlay qTD */ - qh->qh_qtd.qtd_status = htole32(0); + qh->qh_qtd.qtd_status = htoehci32(temp.sc, 0); if (temp.auto_data_toggle) { /* let the hardware compute the data toggle */ - qh->qh_endp &= ~htole32(EHCI_QH_DTC); + qh->qh_endp &= htoehci32(temp.sc, ~EHCI_QH_DTC); if (xfer->pipe->toggle_next) { /* DATA1 is next */ - qh->qh_qtd.qtd_status |= htole32(EHCI_QTD_SET_TOGGLE(1)); + qh->qh_qtd.qtd_status |= + htoehci32(temp.sc, EHCI_QTD_SET_TOGGLE(1)); } } td = xfer->td_transfer_first; qh->qh_qtd.qtd_next = td->qtd_self; - qh->qh_qtd.qtd_altnext = htole32(EHCI_LINK_TERMINATE); + qh->qh_qtd.qtd_altnext = + htoehci32(temp.sc, EHCI_LINK_TERMINATE); usb2_pc_cpu_flush(qh->page_cache); - EHCI_APPEND_QH(qh, *qh_last); + if (xfer->udev->pwr_save.suspended == 0) { + EHCI_APPEND_QH(qh, *qh_last); + } } static void ehci_root_intr_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { - struct ehci_softc *sc = xfer->usb2_sc; + ehci_softc_t *sc = xfer->usb2_sc; uint16_t i; uint16_t m; @@ -1992,11 +2024,11 @@ ehci_isoc_fs_done(ehci_softc_t *sc, struct usb2_xfer *xfer) #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("isoc FS-TD\n"); - ehci_dump_sitd(td); + ehci_dump_sitd(sc, td); } #endif usb2_pc_cpu_invalidate(td->page_cache); - status = le32toh(td->sitd_status); + status = ehci32toh(sc, td->sitd_status); len = EHCI_SITD_GET_LEN(status); @@ -2044,12 +2076,12 @@ ehci_isoc_hs_done(ehci_softc_t *sc, struct usb2_xfer *xfer) #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("isoc HS-TD\n"); - ehci_dump_itd(td); + ehci_dump_itd(sc, td); } #endif usb2_pc_cpu_invalidate(td->page_cache); - status = le32toh(td->itd_status[td_no]); + status = ehci32toh(sc, td->itd_status[td_no]); len = EHCI_ITD_GET_LEN(status); @@ -2102,7 +2134,8 @@ ehci_device_done(struct usb2_xfer *xfer, usb2_error_t error) if (ehcidebug > 8) { DPRINTF("nexttog=%d; data after transfer:\n", xfer->pipe->toggle_next); - ehci_dump_sqtds(xfer->td_transfer_first); + ehci_dump_sqtds(xfer->usb2_sc, + xfer->td_transfer_first); } #endif @@ -2322,6 +2355,7 @@ struct usb2_pipe_methods ehci_device_intr_methods = static void ehci_device_isoc_fs_open(struct usb2_xfer *xfer) { + ehci_softc_t *sc = xfer->usb2_sc; ehci_sitd_t *td; uint32_t sitd_portaddr; uint8_t ds; @@ -2335,7 +2369,7 @@ ehci_device_isoc_fs_open(struct usb2_xfer *xfer) if (UE_GET_DIR(xfer->endpoint) == UE_DIR_IN) { sitd_portaddr |= EHCI_SITD_SET_DIR_IN; } - sitd_portaddr = htole32(sitd_portaddr); + sitd_portaddr = htoehci32(sc, sitd_portaddr); /* initialize all TD's */ @@ -2352,7 +2386,7 @@ ehci_device_isoc_fs_open(struct usb2_xfer *xfer) * * micro-frame usage (8 microframes per 1ms) */ - td->sitd_back = htole32(EHCI_LINK_TERMINATE); + td->sitd_back = htoehci32(sc, EHCI_LINK_TERMINATE); usb2_pc_cpu_flush(td->page_cache); } @@ -2499,7 +2533,7 @@ ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) * non-zero length */ usb2_get_page(xfer->frbuffers, buf_offset, &buf_res); - td->sitd_bp[0] = htole32(buf_res.physaddr); + td->sitd_bp[0] = htoehci32(sc, buf_res.physaddr); buf_offset += *plen; /* * NOTE: We need to subtract one from the offset so @@ -2544,9 +2578,9 @@ ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) sitd_mask = (EHCI_SITD_SET_SMASK(sa) | EHCI_SITD_SET_CMASK(sb)); - td->sitd_bp[1] = htole32(temp); + td->sitd_bp[1] = htoehci32(sc, temp); - td->sitd_mask = htole32(sitd_mask); + td->sitd_mask = htoehci32(sc, sitd_mask); if (nframes == 0) { td->sitd_status = htole32 @@ -2563,7 +2597,7 @@ ehci_device_isoc_fs_enter(struct usb2_xfer *xfer) #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("FS-TD %d\n", nframes); - ehci_dump_sitd(td); + ehci_dump_sitd(sc, td); } #endif /* insert TD into schedule */ @@ -2606,6 +2640,7 @@ struct usb2_pipe_methods ehci_device_isoc_fs_methods = static void ehci_device_isoc_hs_open(struct usb2_xfer *xfer) { + ehci_softc_t *sc = xfer->usb2_sc; ehci_itd_t *td; uint32_t temp; uint8_t ds; @@ -2639,10 +2674,10 @@ ehci_device_isoc_hs_open(struct usb2_xfer *xfer) temp |= EHCI_ITD_SET_DIR_IN; } /* set maximum packet size */ - td->itd_bp[1] = htole32(temp); + td->itd_bp[1] = htoehci32(sc, temp); /* set transfer multiplier */ - td->itd_bp[2] = htole32(xfer->max_packet_count & 3); + td->itd_bp[2] = htoehci32(sc, xfer->max_packet_count & 3); usb2_pc_cpu_flush(td->page_cache); } @@ -2764,7 +2799,7 @@ ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) status = (EHCI_ITD_SET_LEN(*plen) | EHCI_ITD_ACTIVE | EHCI_ITD_SET_PG(0)); - td->itd_status[td_no] = htole32(status); + td->itd_status[td_no] = htoehci32(sc, status); itd_offset[td_no] = buf_offset; buf_offset += *plen; plen++; @@ -2787,14 +2822,14 @@ ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) /* get page address */ page_addr = buf_res.physaddr & ~0xFFF; /* update page address */ - td->itd_bp[0] &= htole32(0xFFF); - td->itd_bp[0] |= htole32(page_addr); + td->itd_bp[0] &= htoehci32(sc, 0xFFF); + td->itd_bp[0] |= htoehci32(sc, page_addr); for (x = 0; x != td_no; x++) { /* set page number and page offset */ status = (EHCI_ITD_SET_PG(page_no) | (buf_res.physaddr & 0xFFF)); - td->itd_status[x] |= htole32(status); + td->itd_status[x] |= htoehci32(sc, status); /* get next page offset */ if (itd_offset[x + 1] == buf_offset) { @@ -2817,20 +2852,20 @@ ehci_device_isoc_hs_enter(struct usb2_xfer *xfer) } page_no++; /* update page address */ - td->itd_bp[page_no] &= htole32(0xFFF); - td->itd_bp[page_no] |= htole32(page_addr); + td->itd_bp[page_no] &= htoehci32(sc, 0xFFF); + td->itd_bp[page_no] |= htoehci32(sc, page_addr); } } } /* set IOC bit if we are complete */ if (nframes == 0) { - td->itd_status[7] |= htole32(EHCI_ITD_IOC); + td->itd_status[7] |= htoehci32(sc, EHCI_ITD_IOC); } usb2_pc_cpu_flush(td->page_cache); #if USB_DEBUG if (ehcidebug > 15) { DPRINTF("HS-TD %d\n", nframes); - ehci_dump_itd(td); + ehci_dump_itd(sc, td); } #endif /* insert TD into schedule */ @@ -3001,7 +3036,7 @@ ehci_root_ctrl_start(struct usb2_xfer *xfer) } static void -ehci_root_ctrl_task(struct ehci_softc *sc, +ehci_root_ctrl_task(ehci_softc_t *sc, struct usb2_config_td_cc *cc, uint16_t refcount) { ehci_root_ctrl_poll(sc); @@ -3011,7 +3046,7 @@ static void ehci_root_ctrl_done(struct usb2_xfer *xfer, struct usb2_sw_transfer *std) { - struct ehci_softc *sc = xfer->usb2_sc; + ehci_softc_t *sc = xfer->usb2_sc; char *ptr; uint32_t port; uint32_t v; @@ -3176,7 +3211,32 @@ ehci_root_ctrl_done(struct usb2_xfer *xfer, EOWRITE4(sc, port, v & ~EHCI_PS_PE); break; case UHF_PORT_SUSPEND: - EOWRITE4(sc, port, v & ~EHCI_PS_SUSP); + if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) { + + /* + * waking up a High Speed device is rather + * complicated if + */ + EOWRITE4(sc, port, v | EHCI_PS_FPR); + } + /* wait 20ms for resume sequence to complete */ + if (use_polling) { + /* polling */ + DELAY(20 * 1000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, 20); + } + + EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP | + EHCI_PS_FPR | (3 << 10) /* High Speed */ )); + + /* settle time */ + if (use_polling) { + /* polling */ + DELAY(4 * 1000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, 4); + } break; case UHF_PORT_POWER: EOWRITE4(sc, port, v & ~EHCI_PS_PP); @@ -3223,7 +3283,8 @@ ehci_root_ctrl_done(struct usb2_xfer *xfer, (EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH) | (EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ? UHD_PORT_IND : 0)); - sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */ + /* XXX can't find out? */ + sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; for (l = 0; l < sc->sc_noport; l++) { /* XXX can't find out? */ sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] &= ~(1 << (l % 8)); @@ -3260,7 +3321,7 @@ ehci_root_ctrl_done(struct usb2_xfer *xfer, i |= UPS_CURRENT_CONNECT_STATUS; if (v & EHCI_PS_PE) i |= UPS_PORT_ENABLED; - if (v & EHCI_PS_SUSP) + if ((v & EHCI_PS_SUSP) && !(v & EHCI_PS_FPR)) i |= UPS_SUSPEND; if (v & EHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; @@ -3276,6 +3337,8 @@ ehci_root_ctrl_done(struct usb2_xfer *xfer, i |= UPS_C_PORT_ENABLED; if (v & EHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; + if (v & EHCI_PS_FPR) + i |= UPS_C_SUSPEND; if (sc->sc_isreset) i |= UPS_C_PORT_RESET; USETW(sc->sc_hub_desc.ps.wPortChange, i); @@ -3398,7 +3461,7 @@ done: } static void -ehci_root_ctrl_poll(struct ehci_softc *sc) +ehci_root_ctrl_poll(ehci_softc_t *sc) { usb2_sw_transfer(&sc->sc_root_ctrl, &ehci_root_ctrl_done); @@ -3621,7 +3684,7 @@ alloc_dma_set: td = page_info.buffer; /* init TD */ - td->itd_self = htole32(page_info.physaddr | EHCI_LINK_ITD); + td->itd_self = htoehci32(sc, page_info.physaddr | EHCI_LINK_ITD); td->obj_next = last_obj; td->page_cache = pc + n; @@ -3645,7 +3708,7 @@ alloc_dma_set: td = page_info.buffer; /* init TD */ - td->sitd_self = htole32(page_info.physaddr | EHCI_LINK_SITD); + td->sitd_self = htoehci32(sc, page_info.physaddr | EHCI_LINK_SITD); td->obj_next = last_obj; td->page_cache = pc + n; @@ -3669,7 +3732,7 @@ alloc_dma_set: qtd = page_info.buffer; /* init TD */ - qtd->qtd_self = htole32(page_info.physaddr); + qtd->qtd_self = htoehci32(sc, page_info.physaddr); qtd->obj_next = last_obj; qtd->page_cache = pc + n; @@ -3697,7 +3760,7 @@ alloc_dma_set: qh = page_info.buffer; /* init QH */ - qh->qh_self = htole32(page_info.physaddr | EHCI_LINK_QH); + qh->qh_self = htoehci32(sc, page_info.physaddr | EHCI_LINK_QH); qh->obj_next = last_obj; qh->page_cache = pc + n; @@ -3794,6 +3857,108 @@ ehci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) *pus = (188); /* microseconds */ } +static void +ehci_device_resume(struct usb2_device *udev) +{ + ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->udev == udev) { + + methods = xfer->pipe->methods; + + if ((methods == &ehci_device_bulk_methods) || + (methods == &ehci_device_ctrl_methods)) { + EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_async_p_last); + } + if (methods == &ehci_device_intr_methods) { + EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ehci_device_suspend(struct usb2_device *udev) +{ + ehci_softc_t *sc = EHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->udev == udev) { + + methods = xfer->pipe->methods; + + if ((methods == &ehci_device_bulk_methods) || + (methods == &ehci_device_ctrl_methods)) { + EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_async_p_last); + } + if (methods == &ehci_device_intr_methods) { + EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set], + sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ehci_set_hw_power(struct usb2_bus *bus) +{ + ehci_softc_t *sc = EHCI_BUS2SC(bus); + uint32_t temp; + uint32_t flags; + + DPRINTF("\n"); + + USB_BUS_LOCK(bus); + + flags = bus->hw_power_state; + + temp = EOREAD4(sc, EHCI_USBCMD); + + temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE); + + if (flags & (USB_HW_POWER_CONTROL | + USB_HW_POWER_BULK)) { + DPRINTF("Async is active\n"); + temp |= EHCI_CMD_ASE; + } + if (flags & (USB_HW_POWER_INTERRUPT | + USB_HW_POWER_ISOC)) { + DPRINTF("Periodic is active\n"); + temp |= EHCI_CMD_PSE; + } + EOWRITE4(sc, EHCI_USBCMD, temp); + + USB_BUS_UNLOCK(bus); + + return; +} + struct usb2_bus_methods ehci_bus_methods = { .pipe_init = ehci_pipe_init, @@ -3801,4 +3966,7 @@ struct usb2_bus_methods ehci_bus_methods = .xfer_unsetup = ehci_xfer_unsetup, .do_poll = ehci_do_poll, .get_dma_delay = ehci_get_dma_delay, + .device_resume = ehci_device_resume, + .device_suspend = ehci_device_suspend, + .set_hw_power = ehci_set_hw_power, }; diff --git a/sys/dev/usb2/controller/ehci2.h b/sys/dev/usb2/controller/ehci2.h index 6765ace..3dee6bd 100644 --- a/sys/dev/usb2/controller/ehci2.h +++ b/sys/dev/usb2/controller/ehci2.h @@ -160,6 +160,15 @@ #define EHCI_PS_CS 0x00000001 /* RO connect status */ #define EHCI_PS_CLEAR (EHCI_PS_OCC | EHCI_PS_PEC | EHCI_PS_CSC) +#define EHCI_USBMODE 0x68 /* RW USB Device mode register */ +#define EHCI_UM_CM 0x00000003 /* R/WO Controller Mode */ +#define EHCI_UM_CM_IDLE 0x0 /* Idle */ +#define EHCI_UM_CM_HOST 0x3 /* Host Controller */ +#define EHCI_UM_ES 0x00000004 /* R/WO Endian Select */ +#define EHCI_UM_ES_LE 0x0 /* Little-endian byte alignment */ +#define EHCI_UM_ES_BE 0x4 /* Big-endian byte alignment */ +#define EHCI_UM_SDIS 0x00000010 /* R/WO Stream Disable Mode */ + #define EHCI_PORT_RESET_COMPLETE 2 /* ms */ /* @@ -469,11 +478,12 @@ typedef struct ehci_softc { uint16_t sc_intr_stat[EHCI_VIRTUAL_FRAMELIST_COUNT]; uint16_t sc_id_vendor; /* vendor ID for root hub */ uint16_t sc_flags; /* chip specific flags */ -#define EHCI_SCFLG_SETMODE 0x0001 /* set bridge mode again after init - * (Marvell) */ -#define EHCI_SCFLG_FORCESPEED 0x0002 /* force speed (Marvell) */ -#define EHCI_SCFLG_NORESTERM 0x0004 /* don't terminate reset sequence - * (Marvell) */ +#define EHCI_SCFLG_SETMODE 0x0001 /* set bridge mode again after init */ +#define EHCI_SCFLG_FORCESPEED 0x0002 /* force speed */ +#define EHCI_SCFLG_NORESTERM 0x0004 /* don't terminate reset sequence */ +#define EHCI_SCFLG_BIGEDESC 0x0008 /* big-endian byte order descriptors */ +#define EHCI_SCFLG_BIGEMMIO 0x0010 /* big-endian byte order MMIO */ +#define EHCI_SCFLG_TT 0x0020 /* transaction translator present */ uint8_t sc_offs; /* offset to operational registers */ uint8_t sc_doorbell_disable; /* set on doorbell failure */ diff --git a/sys/dev/usb2/controller/musb2_otg.c b/sys/dev/usb2/controller/musb2_otg.c index e7119bf..e2a36e3 100644 --- a/sys/dev/usb2/controller/musb2_otg.c +++ b/sys/dev/usb2/controller/musb2_otg.c @@ -236,12 +236,6 @@ musbotg_wakeup_peer(struct usb2_xfer *xfer) } static void -musbotg_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) -{ - DPRINTFN(4, "is_on=%u\n", is_on); -} - -static void musbotg_set_address(struct musbotg_softc *sc, uint8_t addr) { DPRINTFN(4, "addr=%d\n", addr); @@ -2891,5 +2885,4 @@ struct usb2_bus_methods musbotg_bus_methods = .set_stall = &musbotg_set_stall, .clear_stall = &musbotg_clear_stall, .vbus_interrupt = &musbotg_vbus_interrupt, - .rem_wakeup_set = &musbotg_rem_wakeup_set, }; diff --git a/sys/dev/usb2/controller/ohci2.c b/sys/dev/usb2/controller/ohci2.c index b3f2f0e..99076d4 100644 --- a/sys/dev/usb2/controller/ohci2.c +++ b/sys/dev/usb2/controller/ohci2.c @@ -681,18 +681,22 @@ ohci_transfer_intr_enqueue(struct usb2_xfer *xfer) } } -#define OHCI_APPEND_QH(sed,td_self,last) (last) = _ohci_append_qh(sed,td_self,last) +#define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last) static ohci_ed_t * -_ohci_append_qh(ohci_ed_t *sed, uint32_t td_self, ohci_ed_t *last) +_ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last) { DPRINTFN(11, "%p to %p\n", sed, last); + if (sed->prev != NULL) { + /* should not happen */ + DPRINTFN(0, "ED already linked!\n"); + return (last); + } /* (sc->sc_bus.bus_mtx) must be locked */ sed->next = last->next; sed->ed_next = last->ed_next; sed->ed_tailp = 0; - sed->ed_headp = td_self; sed->prev = last; @@ -730,13 +734,6 @@ _ohci_remove_qh(ohci_ed_t *sed, ohci_ed_t *last) sed->next->prev = sed->prev; usb2_pc_cpu_flush(sed->next->page_cache); } - /* - * terminate transfer in case the transferred packet was - * short so that the ED still points at the last used TD - */ - sed->ed_flags |= htole32(OHCI_ED_SKIP); - sed->ed_headp = sed->ed_tailp; - last = ((last == sed) ? sed->prev : last); sed->prev = 0; @@ -1565,17 +1562,23 @@ ohci_setup_standard_chain(struct usb2_xfer *xfer, ohci_ed_t **ed_last) td = xfer->td_transfer_first; - OHCI_APPEND_QH(ed, td->td_self, *ed_last); + ed->ed_headp = td->td_self; - if (methods == &ohci_device_bulk_methods) { - ohci_softc_t *sc = xfer->usb2_sc; + if (xfer->udev->pwr_save.suspended == 0) { + OHCI_APPEND_QH(ed, *ed_last); - OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); - } - if (methods == &ohci_device_ctrl_methods) { - ohci_softc_t *sc = xfer->usb2_sc; + if (methods == &ohci_device_bulk_methods) { + ohci_softc_t *sc = xfer->usb2_sc; - OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); + } + if (methods == &ohci_device_ctrl_methods) { + ohci_softc_t *sc = xfer->usb2_sc; + + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); + } + } else { + usb2_pc_cpu_flush(ed->page_cache); } } @@ -2010,7 +2013,11 @@ ohci_device_isoc_enter(struct usb2_xfer *xfer) td = xfer->td_transfer_first; - OHCI_APPEND_QH(ed, td->itd_self, sc->sc_isoc_p_last); + ed->ed_headp = td->itd_self; + + /* isochronous transfers are not affected by suspend / resume */ + + OHCI_APPEND_QH(ed, sc->sc_isoc_p_last); } static void @@ -2740,6 +2747,115 @@ ohci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) *pus = (1125); /* microseconds */ } +static void +ohci_device_resume(struct usb2_device *udev) +{ + struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + ohci_ed_t *ed; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->udev == udev) { + + methods = xfer->pipe->methods; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (methods == &ohci_device_bulk_methods) { + OHCI_APPEND_QH(ed, sc->sc_bulk_p_last); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF); + } + if (methods == &ohci_device_ctrl_methods) { + OHCI_APPEND_QH(ed, sc->sc_ctrl_p_last); + OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF); + } + if (methods == &ohci_device_intr_methods) { + OHCI_APPEND_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ohci_device_suspend(struct usb2_device *udev) +{ + struct ohci_softc *sc = OHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + ohci_ed_t *ed; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->udev == udev) { + + methods = xfer->pipe->methods; + ed = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (methods == &ohci_device_bulk_methods) { + OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last); + } + if (methods == &ohci_device_ctrl_methods) { + OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last); + } + if (methods == &ohci_device_intr_methods) { + OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +ohci_set_hw_power(struct usb2_bus *bus) +{ + struct ohci_softc *sc = OHCI_BUS2SC(bus); + uint32_t temp; + uint32_t flags; + + DPRINTF("\n"); + + USB_BUS_LOCK(bus); + + flags = bus->hw_power_state; + + temp = OREAD4(sc, OHCI_CONTROL); + temp &= ~(OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE); + + if (flags & USB_HW_POWER_CONTROL) + temp |= OHCI_CLE; + + if (flags & USB_HW_POWER_BULK) + temp |= OHCI_BLE; + + if (flags & USB_HW_POWER_INTERRUPT) + temp |= OHCI_PLE; + + if (flags & USB_HW_POWER_ISOC) + temp |= OHCI_IE | OHCI_PLE; + + OWRITE4(sc, OHCI_CONTROL, temp); + + USB_BUS_UNLOCK(bus); + + return; +} + struct usb2_bus_methods ohci_bus_methods = { .pipe_init = ohci_pipe_init, @@ -2747,4 +2863,7 @@ struct usb2_bus_methods ohci_bus_methods = .xfer_unsetup = ohci_xfer_unsetup, .do_poll = ohci_do_poll, .get_dma_delay = ohci_get_dma_delay, + .device_resume = ohci_device_resume, + .device_suspend = ohci_device_suspend, + .set_hw_power = ohci_set_hw_power, }; diff --git a/sys/dev/usb2/controller/uhci2.c b/sys/dev/usb2/controller/uhci2.c index 478e54f..be80619 100644 --- a/sys/dev/usb2/controller/uhci2.c +++ b/sys/dev/usb2/controller/uhci2.c @@ -916,17 +916,19 @@ _uhci_append_td(uhci_td_t *std, uhci_td_t *last) return (std); } -#define UHCI_APPEND_QH(sqh,td,last) (last) = _uhci_append_qh(sqh,td,last) +#define UHCI_APPEND_QH(sqh,last) (last) = _uhci_append_qh(sqh,last) static uhci_qh_t * -_uhci_append_qh(uhci_qh_t *sqh, uhci_td_t *td, uhci_qh_t *last) +_uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last) { DPRINTFN(11, "%p to %p\n", sqh, last); + if (sqh->h_prev != NULL) { + /* should not happen */ + DPRINTFN(0, "QH already linked!\n"); + return (last); + } /* (sc->sc_bus.mtx) must be locked */ - sqh->e_next = td; - sqh->qh_e_next = td->td_self; - sqh->h_next = last->h_next; sqh->qh_h_next = last->qh_h_next; @@ -990,13 +992,6 @@ _uhci_remove_qh(uhci_qh_t *sqh, uhci_qh_t *last) sqh->h_next->h_prev = sqh->h_prev; usb2_pc_cpu_flush(sqh->h_next->page_cache); } - /* - * set the Terminate-bit in the e_next of the QH, in case - * the transferred packet was short so that the QH still - * points at the last used TD - */ - sqh->qh_e_next = htole32(UHCI_PTR_T); - last = ((last == sqh) ? sqh->h_prev : last); sqh->h_prev = 0; @@ -1459,10 +1454,12 @@ uhci_interrupt(uhci_softc_t *sc) } if (status & UHCI_STS_HCH) { /* no acknowledge needed */ - printf("%s: host controller halted\n", + DPRINTF("%s: host controller halted\n", __FUNCTION__); #if USB_DEBUG - uhci_dump_all(sc); + if (uhcidebug > 0) { + uhci_dump_all(sc); + } #endif } } @@ -1834,11 +1831,6 @@ uhci_device_done(struct usb2_xfer *xfer, usb2_error_t error) qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; if (qh) { usb2_pc_cpu_invalidate(qh->page_cache); - - qh->e_next = 0; - qh->qh_e_next = htole32(UHCI_PTR_T); - - usb2_pc_cpu_flush(qh->page_cache); } if (xfer->flags_int.bandwidth_reclaimed) { xfer->flags_int.bandwidth_reclaimed = 0; @@ -1907,9 +1899,16 @@ uhci_device_bulk_start(struct usb2_xfer *xfer) /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; - UHCI_APPEND_QH(qh, td, sc->sc_bulk_p_last); - uhci_add_loop(sc); - xfer->flags_int.bandwidth_reclaimed = 1; + qh->e_next = td; + qh->qh_e_next = td->td_self; + + if (xfer->udev->pwr_save.suspended == 0) { + UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); + uhci_add_loop(sc); + xfer->flags_int.bandwidth_reclaimed = 1; + } else { + usb2_pc_cpu_flush(qh->page_cache); + } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); @@ -1959,14 +1958,21 @@ uhci_device_ctrl_start(struct usb2_xfer *xfer) /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + qh->e_next = td; + qh->qh_e_next = td->td_self; + /* * NOTE: some devices choke on bandwidth- reclamation for control * transfers */ - if (xfer->udev->speed == USB_SPEED_LOW) { - UHCI_APPEND_QH(qh, td, sc->sc_ls_ctl_p_last); + if (xfer->udev->pwr_save.suspended == 0) { + if (xfer->udev->speed == USB_SPEED_LOW) { + UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); + } else { + UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); + } } else { - UHCI_APPEND_QH(qh, td, sc->sc_fs_ctl_p_last); + usb2_pc_cpu_flush(qh->page_cache); } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); @@ -2047,8 +2053,17 @@ uhci_device_intr_start(struct usb2_xfer *xfer) /* setup QH */ qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; - /* enter QHs into the controller data structures */ - UHCI_APPEND_QH(qh, td, sc->sc_intr_p_last[xfer->qh_pos]); + qh->e_next = td; + qh->qh_e_next = td->td_self; + + if (xfer->udev->pwr_save.suspended == 0) { + + /* enter QHs into the controller data structures */ + UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); + + } else { + usb2_pc_cpu_flush(qh->page_cache); + } /* put transfer on interrupt queue */ uhci_transfer_intr_enqueue(xfer); @@ -2659,7 +2674,7 @@ uhci_root_ctrl_done(struct usb2_xfer *xfer, break; case UHF_PORT_SUSPEND: x = URWMASK(UREAD2(sc, port)); - UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP); + UWRITE2(sc, port, x & ~(UHCI_PORTSC_SUSP)); break; case UHF_PORT_RESET: x = URWMASK(UREAD2(sc, port)); @@ -2681,11 +2696,13 @@ uhci_root_ctrl_done(struct usb2_xfer *xfer, sc->sc_isreset = 0; std->err = USB_ERR_NORMAL_COMPLETION; goto done; + case UHF_C_PORT_SUSPEND: + sc->sc_isresumed &= ~(1 << index); + break; case UHF_PORT_CONNECTION: case UHF_PORT_OVER_CURRENT: case UHF_PORT_POWER: case UHF_PORT_LOW_SPEED: - case UHF_C_PORT_SUSPEND: default: std->err = USB_ERR_IOERROR; goto done; @@ -2740,11 +2757,35 @@ uhci_root_ctrl_done(struct usb2_xfer *xfer, status |= UPS_OVERCURRENT_INDICATOR; if (x & UHCI_PORTSC_OCIC) change |= UPS_C_OVERCURRENT_INDICATOR; - if (x & UHCI_PORTSC_SUSP) - status |= UPS_SUSPEND; if (x & UHCI_PORTSC_LSDA) status |= UPS_LOW_SPEED; + if ((x & UHCI_PORTSC_PE) && (x & UHCI_PORTSC_RD)) { + /* need to do a write back */ + UWRITE2(sc, port, URWMASK(x)); + + /* wait 20ms for resume sequence to complete */ + if (use_polling) { + /* polling */ + DELAY(20 * 1000); + } else { + usb2_pause_mtx(&sc->sc_bus.bus_mtx, 20); + } + + /* clear suspend and resume detect */ + UWRITE2(sc, port, URWMASK(x) & ~(UHCI_PORTSC_RD | + UHCI_PORTSC_SUSP)); + + /* wait a little bit */ + usb2_pause_mtx(&sc->sc_bus.bus_mtx, 2); + + sc->sc_isresumed |= (1 << index); + + } else if (x & UHCI_PORTSC_SUSP) { + status |= UPS_SUSPEND; + } status |= UPS_PORT_POWER; + if (sc->sc_isresumed & (1 << index)) + change |= UPS_C_SUSPEND; if (sc->sc_isreset) change |= UPS_C_PORT_RESET; USETW(sc->sc_hub_desc.ps.wPortStatus, status); @@ -2894,13 +2935,15 @@ uhci_root_intr_check(void *arg) sc->sc_hub_idata[0] = 0; - if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) { + if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | + UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { sc->sc_hub_idata[0] |= 1 << 1; } - if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) { + if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | + UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) { sc->sc_hub_idata[0] |= 1 << 2; } - if ((sc->sc_hub_idata[0] == 0) || !(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) { + if (sc->sc_hub_idata[0] == 0) { /* * no change or controller not running, try again in a while */ @@ -3190,6 +3233,124 @@ uhci_get_dma_delay(struct usb2_bus *bus, uint32_t *pus) *pus = (1125); /* microseconds */ } +static void +uhci_device_resume(struct usb2_device *udev) +{ + struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + uhci_qh_t *qh; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->udev == udev) { + + methods = xfer->pipe->methods; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (methods == &uhci_device_bulk_methods) { + UHCI_APPEND_QH(qh, sc->sc_bulk_p_last); + uhci_add_loop(sc); + xfer->flags_int.bandwidth_reclaimed = 1; + } + if (methods == &uhci_device_ctrl_methods) { + if (xfer->udev->speed == USB_SPEED_LOW) { + UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last); + } else { + UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last); + } + } + if (methods == &uhci_device_intr_methods) { + UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +uhci_device_suspend(struct usb2_device *udev) +{ + struct uhci_softc *sc = UHCI_BUS2SC(udev->bus); + struct usb2_xfer *xfer; + struct usb2_pipe_methods *methods; + uhci_qh_t *qh; + + DPRINTF("\n"); + + USB_BUS_LOCK(udev->bus); + + TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) { + + if (xfer->udev == udev) { + + methods = xfer->pipe->methods; + qh = xfer->qh_start[xfer->flags_int.curr_dma_set]; + + if (xfer->flags_int.bandwidth_reclaimed) { + xfer->flags_int.bandwidth_reclaimed = 0; + uhci_rem_loop(sc); + } + if (methods == &uhci_device_bulk_methods) { + UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last); + } + if (methods == &uhci_device_ctrl_methods) { + if (xfer->udev->speed == USB_SPEED_LOW) { + UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last); + } else { + UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last); + } + } + if (methods == &uhci_device_intr_methods) { + UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]); + } + } + } + + USB_BUS_UNLOCK(udev->bus); + + return; +} + +static void +uhci_set_hw_power(struct usb2_bus *bus) +{ + struct uhci_softc *sc = UHCI_BUS2SC(bus); + uint32_t flags; + + DPRINTF("\n"); + + USB_BUS_LOCK(bus); + + flags = bus->hw_power_state; + + if (flags & (USB_HW_POWER_CONTROL | + USB_HW_POWER_BULK | + USB_HW_POWER_INTERRUPT | + USB_HW_POWER_ISOC)) { + DPRINTF("Some USB transfer is " + "active on %u.\n", + device_get_unit(sc->sc_bus.bdev)); + UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS)); + } else { + DPRINTF("Power save on %u.\n", + device_get_unit(sc->sc_bus.bdev)); + UHCICMD(sc, UHCI_CMD_MAXP); + } + + USB_BUS_UNLOCK(bus); + + return; +} + + struct usb2_bus_methods uhci_bus_methods = { .pipe_init = uhci_pipe_init, @@ -3197,4 +3358,7 @@ struct usb2_bus_methods uhci_bus_methods = .xfer_unsetup = uhci_xfer_unsetup, .do_poll = uhci_do_poll, .get_dma_delay = uhci_get_dma_delay, + .device_resume = uhci_device_resume, + .device_suspend = uhci_device_suspend, + .set_hw_power = uhci_set_hw_power, }; diff --git a/sys/dev/usb2/controller/uhci2.h b/sys/dev/usb2/controller/uhci2.h index 9be89ed..ee0b36d 100644 --- a/sys/dev/usb2/controller/uhci2.h +++ b/sys/dev/usb2/controller/uhci2.h @@ -300,7 +300,8 @@ typedef struct uhci_softc { uint8_t sc_addr; /* device address */ uint8_t sc_conf; /* device configuration */ - uint8_t sc_isreset; + uint8_t sc_isreset; /* bits set if a root hub is reset */ + uint8_t sc_isresumed; /* bits set if a port was resumed */ uint8_t sc_saved_sof; uint8_t sc_hub_idata[1]; diff --git a/sys/dev/usb2/controller/usb2_bus.h b/sys/dev/usb2/controller/usb2_bus.h index d9dc74e..f30d61f 100644 --- a/sys/dev/usb2/controller/usb2_bus.h +++ b/sys/dev/usb2/controller/usb2_bus.h @@ -54,10 +54,14 @@ struct usb2_bus { struct usb2_process explore_proc; struct usb2_bus_msg explore_msg[2]; struct usb2_bus_msg detach_msg[2]; - struct mtx bus_mtx; /* This mutex protects the USB - * hardware */ + struct usb2_bus_msg attach_msg[2]; + /* + * This mutex protects the USB hardware: + */ + struct mtx bus_mtx; struct usb2_perm perm; struct usb2_xfer_queue intr_q; + struct usb2_callout power_wdog; /* power management */ device_t parent; device_t bdev; /* filled by HC driver */ @@ -68,6 +72,7 @@ struct usb2_bus { struct usb2_bus_methods *methods; /* filled by HC driver */ struct usb2_device *devices[USB_MAX_DEVICES]; + uint32_t hw_power_state; /* see USB_HW_POWER_XXX */ uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; uint32_t transfer_count[4]; uint16_t isoc_time_last; /* in milliseconds */ diff --git a/sys/dev/usb2/controller/usb2_controller.c b/sys/dev/usb2/controller/usb2_controller.c index 5dd517d..ec1aff5 100644 --- a/sys/dev/usb2/controller/usb2_controller.c +++ b/sys/dev/usb2/controller/usb2_controller.c @@ -148,6 +148,9 @@ usb2_detach(device_t dev) /* was never setup properly */ return (0); } + /* Stop power watchdog */ + usb2_callout_drain(&bus->power_wdog); + /* Let the USB explore process detach all devices. */ USB_BUS_LOCK(bus); @@ -198,6 +201,11 @@ usb2_bus_explore(struct usb2_proc_msg *pm) mtx_lock(&Giant); /* + * First update the USB power state! + */ + usb2_bus_powerd(bus); + + /* * Explore the Root USB HUB. This call can sleep, * exiting Giant, which is actually Giant. */ @@ -246,24 +254,41 @@ usb2_bus_detach(struct usb2_proc_msg *pm) bus->bdev = NULL; } +static void +usb2_power_wdog(void *arg) +{ + struct usb2_bus *bus = arg; + + USB_BUS_LOCK_ASSERT(bus, MA_OWNED); + + usb2_callout_reset(&bus->power_wdog, + 4 * hz, usb2_power_wdog, arg); + + USB_BUS_UNLOCK(bus); + + usb2_bus_power_update(bus); + + return; +} + /*------------------------------------------------------------------------* - * usb2_attach_sub + * usb2_bus_attach * - * This function is the real USB bus attach code. It is factored out, - * hence it can be called at two different places in time. During - * bootup this function is called from "usb2_post_init". During - * hot-plug it is called directly from the "usb2_attach()" method. + * This function attaches USB in context of the explore thread. *------------------------------------------------------------------------*/ static void -usb2_attach_sub(device_t dev, struct usb2_bus *bus) +usb2_bus_attach(struct usb2_proc_msg *pm) { + struct usb2_bus *bus; struct usb2_device *child; + device_t dev; usb2_error_t err; uint8_t speed; - DPRINTF("\n"); + bus = ((struct usb2_bus_msg *)pm)->bus; + dev = bus->bdev; - mtx_assert(&Giant, MA_OWNED); + DPRINTF("\n"); switch (bus->usbrev) { case USB_REV_1_0: @@ -291,6 +316,9 @@ usb2_attach_sub(device_t dev, struct usb2_bus *bus) return; } + USB_BUS_UNLOCK(bus); + mtx_lock(&Giant); /* XXX not required by USB */ + /* Allocate the Root USB device */ child = usb2_alloc_device(bus->bdev, bus, NULL, 0, 0, 1, @@ -307,10 +335,36 @@ usb2_attach_sub(device_t dev, struct usb2_bus *bus) err = USB_ERR_NOMEM; } + mtx_unlock(&Giant); + USB_BUS_LOCK(bus); + if (err) { device_printf(bus->bdev, "Root HUB problem, error=%s\n", usb2_errstr(err)); } + + /* set softc - we are ready */ + device_set_softc(dev, bus); + + /* start watchdog - this function will unlock the BUS lock ! */ + usb2_power_wdog(bus); + + /* need to return locked */ + USB_BUS_LOCK(bus); +} + +/*------------------------------------------------------------------------* + * usb2_attach_sub + * + * This function creates a thread which runs the USB attach code. It + * is factored out, hence it can be called at two different places in + * time. During bootup this function is called from + * "usb2_post_init". During hot-plug it is called directly from the + * "usb2_attach()" method. + *------------------------------------------------------------------------*/ +static void +usb2_attach_sub(device_t dev, struct usb2_bus *bus) +{ /* Initialise USB process messages */ bus->explore_msg[0].hdr.pm_callback = &usb2_bus_explore; bus->explore_msg[0].bus = bus; @@ -322,13 +376,24 @@ usb2_attach_sub(device_t dev, struct usb2_bus *bus) bus->detach_msg[1].hdr.pm_callback = &usb2_bus_detach; bus->detach_msg[1].bus = bus; + bus->attach_msg[0].hdr.pm_callback = &usb2_bus_attach; + bus->attach_msg[0].bus = bus; + bus->attach_msg[1].hdr.pm_callback = &usb2_bus_attach; + bus->attach_msg[1].bus = bus; + /* Create a new USB process */ if (usb2_proc_setup(&bus->explore_proc, &bus->bus_mtx, USB_PRI_MED)) { printf("WARNING: Creation of USB explore process failed.\n"); + } else { + /* Get final attach going */ + USB_BUS_LOCK(bus); + if (usb2_proc_msignal(&bus->explore_proc, + &bus->attach_msg[0], &bus->attach_msg[1])) { + /* ignore */ + } + USB_BUS_UNLOCK(bus); } - /* set softc - we are ready */ - device_set_softc(dev, bus); } /*------------------------------------------------------------------------* @@ -433,6 +498,9 @@ usb2_bus_mem_alloc_all(struct usb2_bus *bus, bus_dma_tag_t dmat, mtx_init(&bus->bus_mtx, device_get_nameunit(bus->parent), NULL, MTX_DEF | MTX_RECURSE); + usb2_callout_init_mtx(&bus->power_wdog, + &bus->bus_mtx, CALLOUT_RETURNUNLOCKED); + TAILQ_INIT(&bus->intr_q.head); usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, diff --git a/sys/dev/usb2/controller/usb2_controller.h b/sys/dev/usb2/controller/usb2_controller.h index 496e8c2..d65c3d6 100644 --- a/sys/dev/usb2/controller/usb2_controller.h +++ b/sys/dev/usb2/controller/usb2_controller.h @@ -61,13 +61,35 @@ struct usb2_bus_methods { void (*xfer_setup) (struct usb2_setup_params *parm); void (*xfer_unsetup) (struct usb2_xfer *xfer); void (*get_dma_delay) (struct usb2_bus *, uint32_t *pdelay); + void (*device_suspend) (struct usb2_device *udev); + void (*device_resume) (struct usb2_device *udev); + void (*set_hw_power) (struct usb2_bus *bus); + /* + * The following flag is set if one or more control transfers are + * active: + */ +#define USB_HW_POWER_CONTROL 0x01 + /* + * The following flag is set if one or more bulk transfers are + * active: + */ +#define USB_HW_POWER_BULK 0x02 + /* + * The following flag is set if one or more interrupt transfers are + * active: + */ +#define USB_HW_POWER_INTERRUPT 0x04 + /* + * The following flag is set if one or more isochronous transfers + * are active: + */ +#define USB_HW_POWER_ISOC 0x08 /* USB Device mode only - Mandatory */ void (*get_hw_ep_profile) (struct usb2_device *udev, const struct usb2_hw_ep_profile **ppf, uint8_t ep_addr); void (*set_stall) (struct usb2_device *udev, struct usb2_xfer *xfer, struct usb2_pipe *pipe); void (*clear_stall) (struct usb2_device *udev, struct usb2_pipe *pipe); - void (*rem_wakeup_set) (struct usb2_device *udev, uint8_t is_on); /* USB Device mode only - Optional */ @@ -90,7 +112,6 @@ struct usb2_pipe_methods { /* Optional */ - uint8_t (*isdone) (struct usb2_xfer *xfer); void *info; /* Flags */ diff --git a/sys/dev/usb2/controller/uss820dci.c b/sys/dev/usb2/controller/uss820dci.c index d010a53..6db143a 100644 --- a/sys/dev/usb2/controller/uss820dci.c +++ b/sys/dev/usb2/controller/uss820dci.c @@ -223,29 +223,6 @@ uss820dci_wakeup_peer(struct uss820dci_softc *sc) } static void -uss820dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on) -{ - struct uss820dci_softc *sc; - uint8_t temp; - - DPRINTFN(5, "is_on=%u\n", is_on); - - USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); - - sc = USS820_DCI_BUS2SC(udev->bus); - - temp = USS820_READ_1(sc, USS820_SCR); - - if (is_on) { - temp |= USS820_SCR_RWUPE; - } else { - temp &= ~USS820_SCR_RWUPE; - } - - USS820_WRITE_1(sc, USS820_SCR, temp); -} - -static void uss820dci_set_address(struct uss820dci_softc *sc, uint8_t addr) { DPRINTFN(5, "addr=%d\n", addr); @@ -1375,6 +1352,7 @@ uss820dci_init(struct uss820dci_softc *sc) USS820_WRITE_1(sc, USS820_SCR, USS820_SCR_T_IRQ | USS820_SCR_IE_RESET | + /* USS820_SCR_RWUPE | */ USS820_SCR_IE_SUSP | USS820_SCR_IRQPOL); @@ -2518,5 +2496,4 @@ struct usb2_bus_methods uss820dci_bus_methods = .get_hw_ep_profile = &uss820dci_get_hw_ep_profile, .set_stall = &uss820dci_set_stall, .clear_stall = &uss820dci_clear_stall, - .rem_wakeup_set = &uss820dci_rem_wakeup_set, }; diff --git a/sys/dev/usb2/core/usb2_busdma.c b/sys/dev/usb2/core/usb2_busdma.c index 33851f0..be99e00 100644 --- a/sys/dev/usb2/core/usb2_busdma.c +++ b/sys/dev/usb2/core/usb2_busdma.c @@ -29,12 +29,15 @@ #include <dev/usb2/include/usb2_standard.h> #include <dev/usb2/include/usb2_defs.h> +#define USB_DEBUG_VAR usb2_debug + #include <dev/usb2/core/usb2_core.h> #include <dev/usb2/core/usb2_busdma.h> #include <dev/usb2/core/usb2_process.h> #include <dev/usb2/core/usb2_transfer.h> #include <dev/usb2/core/usb2_device.h> #include <dev/usb2/core/usb2_util.h> +#include <dev/usb2/core/usb2_debug.h> #include <dev/usb2/controller/usb2_controller.h> #include <dev/usb2/controller/usb2_bus.h> @@ -418,7 +421,16 @@ usb2_pc_common_mem_cb(void *arg, bus_dma_segment_t *segs, pc->page_offset_buf = rem; pc->page_offset_end += rem; nseg--; - +#if (USB_DEBUG != 0) + if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { + /* + * This check verifies that the physical address is correct: + */ + DPRINTFN(0, "Page offset was not preserved!\n"); + error = 1; + goto done; + } +#endif while (nseg > 0) { nseg--; segs++; @@ -788,7 +800,16 @@ usb2_pc_common_mem_cb(struct usb2_page_cache *pc, bus_dma_segment_t *segs, ext_seg = 0; } nseg--; - +#if (USB_DEBUG != 0) + if (rem != (USB_P2U(pc->buffer) & (USB_PAGE_SIZE - 1))) { + /* + * This check verifies that the physical address is correct: + */ + DPRINTFN(0, "Page offset was not preserved!\n"); + error = 1; + goto done; + } +#endif while (nseg > 0) { nseg--; segs++; diff --git a/sys/dev/usb2/core/usb2_core.h b/sys/dev/usb2/core/usb2_core.h index acc30a8..f1e5586 100644 --- a/sys/dev/usb2/core/usb2_core.h +++ b/sys/dev/usb2/core/usb2_core.h @@ -440,6 +440,7 @@ uint8_t usb2_get_interface_altindex(struct usb2_interface *iface); usb2_error_t usb2_set_alt_interface_index(struct usb2_device *udev, uint8_t iface_index, uint8_t alt_index); uint8_t usb2_get_speed(struct usb2_device *udev); +uint32_t usb2_get_isoc_fps(struct usb2_device *udev); usb2_error_t usb2_transfer_setup(struct usb2_device *udev, const uint8_t *ifaces, struct usb2_xfer **pxfer, const struct usb2_config *setup_start, uint16_t n_setup, @@ -464,5 +465,6 @@ void usb2_set_iface_perm(struct usb2_device *udev, uint8_t iface_index, uint32_t uid, uint32_t gid, uint16_t mode); uint8_t usb2_get_bus_index(struct usb2_device *udev); uint8_t usb2_get_device_index(struct usb2_device *udev); +void usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode); #endif /* _USB2_CORE_H_ */ diff --git a/sys/dev/usb2/core/usb2_device.c b/sys/dev/usb2/core/usb2_device.c index 2356cc5..805de0a 100644 --- a/sys/dev/usb2/core/usb2_device.c +++ b/sys/dev/usb2/core/usb2_device.c @@ -1186,7 +1186,7 @@ usb2_suspend_resume_sub(struct usb2_device *udev, device_t dev, uint8_t do_suspe } /*------------------------------------------------------------------------* - * usb2_suspend_resume_device + * usb2_suspend_resume * * The following function will suspend or resume the USB device. * @@ -1339,7 +1339,13 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, udev->bus = bus; udev->address = USB_START_ADDR; /* default value */ udev->plugtime = (uint32_t)ticks; + /* + * We need to force the power mode to "on" because there are plenty + * of USB devices out there that do not work very well with + * automatic suspend and resume! + */ udev->power_mode = USB_POWER_MODE_ON; + udev->pwr_save.last_xfer_time = ticks; /* we are not ready yet */ udev->refcount = 1; @@ -1450,7 +1456,11 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, if (err) { DPRINTFN(0, "getting device descriptor " "at addr %d failed!\n", udev->address); - goto done; + /* XXX try to re-enumerate the device */ + err = usb2_req_re_enumerate(udev, &Giant); + if (err) { + goto done; + } } DPRINTF("adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", @@ -1546,6 +1556,7 @@ usb2_alloc_device(device_t parent_dev, struct usb2_bus *bus, if (udev->flags.usb2_mode == USB_MODE_HOST) { uint8_t config_index; uint8_t config_quirk; + uint8_t set_config_failed = 0; /* * Most USB devices should attach to config index 0 by @@ -1580,11 +1591,27 @@ repeat_set_config: err = usb2_set_config_index(udev, config_index); sx_unlock(udev->default_sx + 1); if (err) { - DPRINTFN(0, "Failure selecting " - "configuration index %u: %s, port %u, addr %u\n", - config_index, usb2_errstr(err), udev->port_no, - udev->address); - + if (udev->ddesc.bNumConfigurations != 0) { + if (!set_config_failed) { + set_config_failed = 1; + /* XXX try to re-enumerate the device */ + err = usb2_req_re_enumerate( + udev, &Giant); + if (err == 0) + goto repeat_set_config; + } + DPRINTFN(0, "Failure selecting " + "configuration index %u: %s, port %u, " + "addr %u (ignored)\n", + config_index, usb2_errstr(err), udev->port_no, + udev->address); + } + /* + * Some USB devices do not have any + * configurations. Ignore any set config + * failures! + */ + err = 0; } else if (config_quirk) { /* user quirk selects configuration index */ } else if ((config_index + 1) < udev->ddesc.bNumConfigurations) { @@ -1608,7 +1635,7 @@ repeat_set_config: goto repeat_set_config; } } - } else if (usb2_test_huawei(udev, &uaa) == 0) { + } else if (usb2_test_huawei_autoinst_p(udev, &uaa) == 0) { DPRINTFN(0, "Found Huawei auto-install disk!\n"); err = USB_ERR_STALLED; /* fake an error */ } @@ -1670,6 +1697,11 @@ usb2_free_device(struct usb2_device *udev) bus = udev->bus; + printf("ugen%u.%u: <%s> at %s (disconnected)\n", + device_get_unit(bus->bdev), + udev->device_index, udev->manufacturer, + device_get_nameunit(bus->bdev)); + /* * Destroy UGEN symlink, if any */ @@ -1937,6 +1969,19 @@ usb2_get_speed(struct usb2_device *udev) return (udev->speed); } +uint32_t +usb2_get_isoc_fps(struct usb2_device *udev) +{ + ; /* indent fix */ + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + return (1000); + default: + return (8000); + } +} + struct usb2_device_descriptor * usb2_get_device_descriptor(struct usb2_device *udev) { @@ -2123,3 +2168,22 @@ usb2_fifo_free_wrap(struct usb2_device *udev, usb2_fifo_free(f); } } + +/*------------------------------------------------------------------------* + * usb2_peer_can_wakeup + * + * Return values: + * 0: Peer cannot do resume signalling. + * Else: Peer can do resume signalling. + *------------------------------------------------------------------------*/ +uint8_t +usb2_peer_can_wakeup(struct usb2_device *udev) +{ + const struct usb2_config_descriptor *cdp; + + cdp = udev->cdesc; + if ((cdp != NULL) && (udev->flags.usb2_mode == USB_MODE_HOST)) { + return (cdp->bmAttributes & UC_REMOTE_WAKEUP); + } + return (0); /* not supported */ +} diff --git a/sys/dev/usb2/core/usb2_device.h b/sys/dev/usb2/core/usb2_device.h index 96dcb50..11686e2 100644 --- a/sys/dev/usb2/core/usb2_device.h +++ b/sys/dev/usb2/core/usb2_device.h @@ -83,6 +83,18 @@ struct usb2_device_flags { }; /* + * The following structure is used for power-save purposes. The data + * in this structure is protected by the USB BUS lock. + */ +struct usb2_power_save { + int last_xfer_time; /* copy of "ticks" */ + uint32_t type_refs[4]; /* transfer reference count */ + uint32_t read_refs; /* data read references */ + uint32_t write_refs; /* data write references */ + uint8_t suspended; /* set if USB device is suspended */ +}; + +/* * The following structure defines an USB device. There exists one of * these structures for every USB device. */ @@ -96,6 +108,7 @@ struct usb2_device { struct usb2_interface ifaces[USB_IFACE_MAX]; struct usb2_pipe default_pipe; /* Control Endpoint 0 */ struct usb2_pipe pipes[USB_EP_MAX]; + struct usb2_power_save pwr_save;/* power save data */ struct usb2_bus *bus; /* our USB BUS */ device_t parent_dev; /* parent device */ @@ -169,5 +182,6 @@ void *usb2_find_descriptor(struct usb2_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); void usb_linux_free_device(struct usb_device *dev); +uint8_t usb2_peer_can_wakeup(struct usb2_device *udev); #endif /* _USB2_DEVICE_H_ */ diff --git a/sys/dev/usb2/core/usb2_dynamic.c b/sys/dev/usb2/core/usb2_dynamic.c index bf7ce59..3e0bdf1 100644 --- a/sys/dev/usb2/core/usb2_dynamic.c +++ b/sys/dev/usb2/core/usb2_dynamic.c @@ -39,6 +39,7 @@ static usb2_temp_get_desc_t usb2_temp_get_desc_w; static usb2_temp_setup_by_index_t usb2_temp_setup_by_index_w; static usb2_temp_unsetup_t usb2_temp_unsetup_w; static usb2_test_quirk_t usb2_test_quirk_w; +static usb2_test_huawei_autoinst_t usb2_test_huawei_autoinst_w; static usb2_quirk_ioctl_t usb2_quirk_ioctl_w; /* global variables */ @@ -46,6 +47,7 @@ usb2_temp_get_desc_t *usb2_temp_get_desc_p = &usb2_temp_get_desc_w; usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index_w; usb2_temp_unsetup_t *usb2_temp_unsetup_p = &usb2_temp_unsetup_w; usb2_test_quirk_t *usb2_test_quirk_p = &usb2_test_quirk_w; +usb2_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w; usb2_quirk_ioctl_t *usb2_quirk_ioctl_p = &usb2_quirk_ioctl_w; devclass_t usb2_devclass_ptr = NULL; @@ -86,6 +88,13 @@ usb2_temp_unsetup_w(struct usb2_device *udev) } } +static uint8_t +usb2_test_huawei_autoinst_w(struct usb2_device *udev, + struct usb2_attach_arg *uaa) +{ + return (USB_ERR_INVAL); +} + void usb2_quirk_unload(void *arg) { @@ -130,3 +139,17 @@ usb2_bus_unload(void *arg) pause("WAIT", hz); } + +void +usb2_test_huawei_unload(void *arg) +{ + /* reset function pointers */ + + usb2_test_huawei_autoinst_p = &usb2_test_huawei_autoinst_w; + + /* wait for CPU to exit the loaded functions, if any */ + + /* XXX this is a tradeoff */ + + pause("WAIT", 16*hz); +} diff --git a/sys/dev/usb2/core/usb2_dynamic.h b/sys/dev/usb2/core/usb2_dynamic.h index 7496929..2c45d09 100644 --- a/sys/dev/usb2/core/usb2_dynamic.h +++ b/sys/dev/usb2/core/usb2_dynamic.h @@ -37,6 +37,8 @@ struct usb2_device_request; typedef usb2_error_t (usb2_temp_setup_by_index_t)(struct usb2_device *udev, uint16_t index); +typedef usb2_error_t (usb2_test_huawei_autoinst_t)(struct usb2_device *udev, + struct usb2_attach_arg *uaa); typedef uint8_t (usb2_test_quirk_t)(const struct usb2_lookup_info *info, uint16_t quirk); typedef int (usb2_quirk_ioctl_t)(unsigned long cmd, caddr_t data, @@ -52,11 +54,13 @@ extern usb2_temp_get_desc_t *usb2_temp_get_desc_p; extern usb2_temp_setup_by_index_t *usb2_temp_setup_by_index_p; extern usb2_temp_unsetup_t *usb2_temp_unsetup_p; extern usb2_test_quirk_t *usb2_test_quirk_p; +extern usb2_test_huawei_autoinst_t *usb2_test_huawei_autoinst_p; extern usb2_quirk_ioctl_t *usb2_quirk_ioctl_p; extern devclass_t usb2_devclass_ptr; /* function prototypes */ +void usb2_test_huawei_unload(void *); void usb2_temp_unload(void *); void usb2_quirk_unload(void *); void usb2_bus_unload(void *); diff --git a/sys/dev/usb2/core/usb2_generic.c b/sys/dev/usb2/core/usb2_generic.c index fa09eef..9e5b34b 100644 --- a/sys/dev/usb2/core/usb2_generic.c +++ b/sys/dev/usb2/core/usb2_generic.c @@ -1711,24 +1711,21 @@ ugen_set_power_mode(struct usb2_fifo *f, int mode) break; case USB_POWER_MODE_ON: - /* enable port */ - err = usb2_req_set_port_feature(udev->parent_hub, - NULL, udev->port_no, UHF_PORT_ENABLE); - - /* FALLTHROUGH */ - case USB_POWER_MODE_SAVE: + break; + case USB_POWER_MODE_RESUME: - /* TODO: implement USB power save */ err = usb2_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); + mode = USB_POWER_MODE_SAVE; break; case USB_POWER_MODE_SUSPEND: - /* TODO: implement USB power save */ err = usb2_req_set_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_SUSPEND); + mode = USB_POWER_MODE_SAVE; break; + default: return (EINVAL); } @@ -1736,7 +1733,8 @@ ugen_set_power_mode(struct usb2_fifo *f, int mode) if (err) return (ENXIO); /* I/O failure */ - udev->power_mode = mode; /* update copy of power mode */ + /* set new power mode */ + usb2_set_power_mode(udev, mode); return (0); /* success */ } diff --git a/sys/dev/usb2/core/usb2_handle_request.c b/sys/dev/usb2/core/usb2_handle_request.c index d5714b4..c765997 100644 --- a/sys/dev/usb2/core/usb2_handle_request.c +++ b/sys/dev/usb2/core/usb2_handle_request.c @@ -390,10 +390,12 @@ usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on) udev->flags.remote_wakeup = 0; } - (bus->methods->rem_wakeup_set) (xfer->udev, is_on); - USB_BUS_UNLOCK(bus); + /* In case we are out of sync, update the power state. */ + + usb2_bus_power_update(udev->bus); + return (0); /* success */ } diff --git a/sys/dev/usb2/core/usb2_hub.c b/sys/dev/usb2/core/usb2_hub.c index 34b275a..0d08b948 100644 --- a/sys/dev/usb2/core/usb2_hub.c +++ b/sys/dev/usb2/core/usb2_hub.c @@ -34,6 +34,7 @@ #include <dev/usb2/include/usb2_mfunc.h> #include <dev/usb2/include/usb2_error.h> #include <dev/usb2/include/usb2_standard.h> +#include <dev/usb2/include/usb2_ioctl.h> #define USB_DEBUG_VAR uhub_debug @@ -61,6 +62,11 @@ SYSCTL_INT(_hw_usb2_uhub, OID_AUTO, debug, CTLFLAG_RW, &uhub_debug, 0, "Debug level"); #endif +static int usb2_power_timeout = 30; /* seconds */ + +SYSCTL_INT(_hw_usb2, OID_AUTO, power_timeout, CTLFLAG_RW, + &usb2_power_timeout, 0, "USB power timeout"); + struct uhub_current_state { uint16_t port_change; uint16_t port_status; @@ -86,6 +92,8 @@ struct uhub_softc { static device_probe_t uhub_probe; static device_attach_t uhub_attach; static device_detach_t uhub_detach; +static device_suspend_t uhub_suspend; +static device_resume_t uhub_resume; static bus_driver_added_t uhub_driver_added; static bus_child_location_str_t uhub_child_location_string; @@ -94,6 +102,9 @@ static bus_child_pnpinfo_str_t uhub_child_pnpinfo_string; static usb2_callback_t uhub_intr_callback; static usb2_callback_t uhub_intr_clear_stall_callback; +static void usb2_dev_resume_peer(struct usb2_device *udev); +static void usb2_dev_suspend_peer(struct usb2_device *udev); + static const struct usb2_config uhub_config[2] = { [0] = { @@ -133,8 +144,8 @@ static driver_t uhub_driver = DEVMETHOD(device_attach, uhub_attach), DEVMETHOD(device_detach, uhub_detach), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_suspend, uhub_suspend), + DEVMETHOD(device_resume, uhub_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(bus_child_location_str, uhub_child_location_string), @@ -302,8 +313,8 @@ repeat: /* first clear the port connection change bit */ - err = usb2_req_clear_port_feature - (udev, &Giant, portno, UHF_C_PORT_CONNECTION); + err = usb2_req_clear_port_feature(udev, &Giant, + portno, UHF_C_PORT_CONNECTION); if (err) { goto error; @@ -338,6 +349,12 @@ repeat: DPRINTF("Port %d is in Host Mode\n", portno); + if (sc->sc_st.port_status & UPS_SUSPEND) { + DPRINTF("Port %d was still " + "suspended, clearing.\n", portno); + err = usb2_req_clear_port_feature(sc->sc_udev, + &Giant, portno, UHF_PORT_SUSPEND); + } /* USB Host Mode */ /* wait for maximum device power up time */ @@ -346,8 +363,7 @@ repeat: /* reset port, which implies enabling it */ - err = usb2_req_reset_port - (udev, &Giant, portno); + err = usb2_req_reset_port(udev, &Giant, portno); if (err) { DPRINTFN(0, "port %d reset " @@ -411,8 +427,8 @@ error: } if (err == 0) { if (sc->sc_st.port_status & UPS_PORT_ENABLED) { - err = usb2_req_clear_port_feature - (sc->sc_udev, &Giant, + err = usb2_req_clear_port_feature( + sc->sc_udev, &Giant, portno, UHF_PORT_ENABLE); } } @@ -446,16 +462,17 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) /* first clear the port suspend change bit */ - err = usb2_req_clear_port_feature - (udev, &Giant, portno, UHF_C_PORT_SUSPEND); - + err = usb2_req_clear_port_feature(udev, &Giant, + portno, UHF_C_PORT_SUSPEND); if (err) { + DPRINTF("clearing suspend failed.\n"); goto done; } /* get fresh status */ err = uhub_read_port_status(sc, portno); if (err) { + DPRINTF("reading port status failed.\n"); goto done; } /* get current state */ @@ -465,12 +482,21 @@ uhub_suspend_resume_port(struct uhub_softc *sc, uint8_t portno) } else { is_suspend = 0; } + + DPRINTF("suspended=%u\n", is_suspend); + /* do the suspend or resume */ if (child) { - sx_xlock(child->default_sx + 1); - err = usb2_suspend_resume(child, is_suspend); - sx_unlock(child->default_sx + 1); + /* + * This code handle two cases: 1) Host Mode - we can only + * receive resume here 2) Device Mode - we can receive + * suspend and resume here + */ + if (is_suspend == 0) + usb2_dev_resume_peer(child); + else if (child->flags.usb2_mode == USB_MODE_DEVICE) + usb2_dev_suspend_peer(child); } done: return (err); @@ -502,6 +528,11 @@ uhub_explore(struct usb2_device *udev) if (udev->depth > USB_HUB_MAX_DEPTH) { return (USB_ERR_TOO_DEEP); } + if (udev->pwr_save.suspended) { + /* need to wait until the child signals resume */ + DPRINTF("Device is suspended!\n"); + return (0); + } for (x = 0; x != hub->nports; x++) { up = hub->ports + x; portno = x + 1; @@ -511,6 +542,15 @@ uhub_explore(struct usb2_device *udev) /* most likely the HUB is gone */ break; } + if (sc->sc_st.port_change & UPS_C_OVERCURRENT_INDICATOR) { + DPRINTF("Overcurrent on port %u.\n", portno); + err = usb2_req_clear_port_feature( + udev, &Giant, portno, UHF_C_PORT_OVER_CURRENT); + if (err) { + /* most likely the HUB is gone */ + break; + } + } if (!(sc->sc_flags & UHUB_FLAG_DID_EXPLORE)) { /* * Fake a connect status change so that the @@ -750,8 +790,8 @@ uhub_attach(device_t dev) } if (!err) { /* turn the power on */ - err = usb2_req_set_port_feature - (udev, &Giant, portno, UHF_PORT_POWER); + err = usb2_req_set_port_feature(udev, &Giant, + portno, UHF_PORT_POWER); } if (err) { DPRINTFN(0, "port %d power on failed, %s\n", @@ -774,6 +814,10 @@ uhub_attach(device_t dev) usb2_transfer_start(sc->sc_xfer[0]); USB_XFER_UNLOCK(sc->sc_xfer[0]); + /* Enable automatic power save on all USB HUBs */ + + usb2_set_power_mode(udev, USB_POWER_MODE_SAVE); + return (0); error: @@ -827,6 +871,22 @@ uhub_detach(device_t dev) return (0); } +static int +uhub_suspend(device_t dev) +{ + DPRINTF("\n"); + /* Sub-devices are not suspended here! */ + return (0); +} + +static int +uhub_resume(device_t dev) +{ + DPRINTF("\n"); + /* Sub-devices are not resumed here! */ + return (0); +} + static void uhub_driver_added(device_t dev, driver_t *driver) { @@ -1334,3 +1394,441 @@ usb2_needs_explore_all(void) max--; } } + +/*------------------------------------------------------------------------* + * usb2_bus_power_update + * + * This function will ensure that all USB devices on the given bus are + * properly suspended or resumed according to the device transfer + * state. + *------------------------------------------------------------------------*/ +void +usb2_bus_power_update(struct usb2_bus *bus) +{ + usb2_needs_explore(bus, 0 /* no probe */ ); +} + +/*------------------------------------------------------------------------* + * usb2_transfer_power_ref + * + * This function will modify the power save reference counts and + * wakeup the USB device associated with the given USB transfer, if + * needed. + *------------------------------------------------------------------------*/ +void +usb2_transfer_power_ref(struct usb2_xfer *xfer, int val) +{ + static const uint32_t power_mask[4] = { + [UE_CONTROL] = USB_HW_POWER_CONTROL, + [UE_BULK] = USB_HW_POWER_BULK, + [UE_INTERRUPT] = USB_HW_POWER_INTERRUPT, + [UE_ISOCHRONOUS] = USB_HW_POWER_ISOC, + }; + struct usb2_device *udev; + uint8_t needs_explore; + uint8_t needs_hw_power; + uint8_t xfer_type; + + udev = xfer->udev; + + if (udev->device_index == USB_ROOT_HUB_ADDR) { + /* no power save for root HUB */ + return; + } + USB_BUS_LOCK(udev->bus); + + xfer_type = xfer->pipe->edesc->bmAttributes & UE_XFERTYPE; + + udev->pwr_save.last_xfer_time = ticks; + udev->pwr_save.type_refs[xfer_type] += val; + + if (xfer->flags_int.control_xfr) { + udev->pwr_save.read_refs += val; + if (xfer->flags_int.usb2_mode == USB_MODE_HOST) { + /* + * it is not allowed to suspend during a control + * transfer + */ + udev->pwr_save.write_refs += val; + } + } else if (USB_GET_DATA_ISREAD(xfer)) { + udev->pwr_save.read_refs += val; + } else { + udev->pwr_save.write_refs += val; + } + + if (udev->pwr_save.suspended) + needs_explore = + (udev->pwr_save.write_refs != 0) || + ((udev->pwr_save.read_refs != 0) && + (usb2_peer_can_wakeup(udev) == 0)); + else + needs_explore = 0; + + if (!(udev->bus->hw_power_state & power_mask[xfer_type])) { + DPRINTF("Adding type %u to power state\n", xfer_type); + udev->bus->hw_power_state |= power_mask[xfer_type]; + needs_hw_power = 1; + } else { + needs_hw_power = 0; + } + + USB_BUS_UNLOCK(udev->bus); + + if (needs_explore) { + DPRINTF("update\n"); + usb2_bus_power_update(udev->bus); + } else if (needs_hw_power) { + DPRINTF("needs power\n"); + if (udev->bus->methods->set_hw_power != NULL) { + (udev->bus->methods->set_hw_power) (udev->bus); + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_bus_powerd + * + * This function implements the USB power daemon and is called + * regularly from the USB explore thread. + *------------------------------------------------------------------------*/ +void +usb2_bus_powerd(struct usb2_bus *bus) +{ + struct usb2_device *udev; + unsigned int temp; + unsigned int limit; + unsigned int mintime; + uint32_t type_refs[4]; + uint8_t x; + uint8_t rem_wakeup; + + limit = usb2_power_timeout; + if (limit == 0) + limit = hz; + else if (limit > 255) + limit = 255 * hz; + else + limit = limit * hz; + + DPRINTF("bus=%p\n", bus); + + USB_BUS_LOCK(bus); + + /* + * The root HUB device is never suspended + * and we simply skip it. + */ + for (x = USB_ROOT_HUB_ADDR + 1; + x != USB_MAX_DEVICES; x++) { + + udev = bus->devices[x]; + if (udev == NULL) + continue; + + rem_wakeup = usb2_peer_can_wakeup(udev); + + temp = ticks - udev->pwr_save.last_xfer_time; + + if ((udev->power_mode == USB_POWER_MODE_ON) || + (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) || + (udev->pwr_save.write_refs != 0) || + ((udev->pwr_save.read_refs != 0) && + (rem_wakeup == 0))) { + + /* check if we are suspended */ + if (udev->pwr_save.suspended != 0) { + USB_BUS_UNLOCK(bus); + usb2_dev_resume_peer(udev); + USB_BUS_LOCK(bus); + } + } else if (temp >= limit) { + + /* check if we are not suspended */ + if (udev->pwr_save.suspended == 0) { + USB_BUS_UNLOCK(bus); + usb2_dev_suspend_peer(udev); + USB_BUS_LOCK(bus); + } + } + } + + /* reset counters */ + + mintime = 0 - 1; + type_refs[0] = 0; + type_refs[1] = 0; + type_refs[2] = 0; + type_refs[3] = 0; + + /* Re-loop all the devices to get the actual state */ + + for (x = USB_ROOT_HUB_ADDR + 1; + x != USB_MAX_DEVICES; x++) { + + udev = bus->devices[x]; + if (udev == NULL) + continue; + + /* "last_xfer_time" can be updated by a resume */ + temp = ticks - udev->pwr_save.last_xfer_time; + + /* + * Compute minimum time since last transfer for the complete + * bus: + */ + if (temp < mintime) + mintime = temp; + + if (udev->pwr_save.suspended == 0) { + type_refs[0] += udev->pwr_save.type_refs[0]; + type_refs[1] += udev->pwr_save.type_refs[1]; + type_refs[2] += udev->pwr_save.type_refs[2]; + type_refs[3] += udev->pwr_save.type_refs[3]; + } + } + + if (mintime >= (1 * hz)) { + /* recompute power masks */ + DPRINTF("Recomputing power masks\n"); + bus->hw_power_state = 0; + if (type_refs[UE_CONTROL] != 0) + bus->hw_power_state |= USB_HW_POWER_CONTROL; + if (type_refs[UE_BULK] != 0) + bus->hw_power_state |= USB_HW_POWER_BULK; + if (type_refs[UE_INTERRUPT] != 0) + bus->hw_power_state |= USB_HW_POWER_INTERRUPT; + if (type_refs[UE_ISOCHRONOUS] != 0) + bus->hw_power_state |= USB_HW_POWER_ISOC; + } + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power != NULL) { + /* always update hardware power! */ + (bus->methods->set_hw_power) (bus); + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_dev_resume_peer + * + * This function will resume an USB peer and do the required USB + * signalling to get an USB device out of the suspended state. + *------------------------------------------------------------------------*/ +static void +usb2_dev_resume_peer(struct usb2_device *udev) +{ + struct usb2_bus *bus; + int err; + + /* be NULL safe */ + if (udev == NULL) + return; + + /* check if already resumed */ + if (udev->pwr_save.suspended == 0) + return; + + /* we need a parent HUB to do resume */ + if (udev->parent_hub == NULL) + return; + + DPRINTF("udev=%p\n", udev); + + if ((udev->flags.usb2_mode == USB_MODE_DEVICE) && + (udev->flags.remote_wakeup == 0)) { + /* + * If the host did not set the remote wakeup feature, we can + * not wake it up either! + */ + DPRINTF("remote wakeup is not set!\n"); + return; + } + /* get bus pointer */ + bus = udev->bus; + + /* resume parent hub first */ + usb2_dev_resume_peer(udev->parent_hub); + + /* resume current port (Valid in Host and Device Mode) */ + err = usb2_req_clear_port_feature(udev->parent_hub, + &Giant, udev->port_no, UHF_PORT_SUSPEND); + if (err) { + DPRINTFN(0, "Resuming port failed!\n"); + return; + } + /* resume settle time */ + usb2_pause_mtx(&Giant, USB_PORT_RESUME_DELAY); + + if (bus->methods->device_resume != NULL) { + /* resume USB device on the USB controller */ + (bus->methods->device_resume) (udev); + } + USB_BUS_LOCK(bus); + /* set that this device is now resumed */ + udev->pwr_save.suspended = 0; + /* make sure that we don't go into suspend right away */ + udev->pwr_save.last_xfer_time = ticks; + + /* make sure the needed power masks are on */ + if (udev->pwr_save.type_refs[UE_CONTROL] != 0) + bus->hw_power_state |= USB_HW_POWER_CONTROL; + if (udev->pwr_save.type_refs[UE_BULK] != 0) + bus->hw_power_state |= USB_HW_POWER_BULK; + if (udev->pwr_save.type_refs[UE_INTERRUPT] != 0) + bus->hw_power_state |= USB_HW_POWER_INTERRUPT; + if (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) + bus->hw_power_state |= USB_HW_POWER_ISOC; + USB_BUS_UNLOCK(bus); + + if (bus->methods->set_hw_power != NULL) { + /* always update hardware power! */ + (bus->methods->set_hw_power) (bus); + } + sx_xlock(udev->default_sx + 1); + /* notify all sub-devices about resume */ + err = usb2_suspend_resume(udev, 0); + sx_unlock(udev->default_sx + 1); + + /* check if peer has wakeup capability */ + if (usb2_peer_can_wakeup(udev)) { + /* clear remote wakeup */ + err = usb2_req_clear_device_feature(udev, + &Giant, UF_DEVICE_REMOTE_WAKEUP); + if (err) { + DPRINTFN(0, "Clearing device " + "remote wakeup failed: %s!\n", + usb2_errstr(err)); + } + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_dev_suspend_peer + * + * This function will suspend an USB peer and do the required USB + * signalling to get an USB device into the suspended state. + *------------------------------------------------------------------------*/ +static void +usb2_dev_suspend_peer(struct usb2_device *udev) +{ + struct usb2_device *hub; + struct usb2_device *child; + uint32_t temp; + int err; + uint8_t x; + uint8_t nports; + uint8_t suspend_parent; + +repeat: + /* be NULL safe */ + if (udev == NULL) + return; + + /* check if already suspended */ + if (udev->pwr_save.suspended) + return; + + /* we need a parent HUB to do suspend */ + if (udev->parent_hub == NULL) + return; + + DPRINTF("udev=%p\n", udev); + + /* check if all devices on the parent hub are suspended */ + hub = udev->parent_hub; + if (hub != NULL) { + nports = hub->hub->nports; + suspend_parent = 1; + + for (x = 0; x != nports; x++) { + + child = usb2_bus_port_get_device(hub->bus, + hub->hub->ports + x); + + if (child == NULL) + continue; + + if (child->pwr_save.suspended) + continue; + + if (child == udev) + continue; + + /* another device on the HUB is not suspended */ + suspend_parent = 0; + + break; + } + } else { + suspend_parent = 0; + } + + sx_xlock(udev->default_sx + 1); + /* notify all sub-devices about suspend */ + err = usb2_suspend_resume(udev, 1); + sx_unlock(udev->default_sx + 1); + + if (usb2_peer_can_wakeup(udev)) { + /* allow device to do remote wakeup */ + err = usb2_req_set_device_feature(udev, + &Giant, UF_DEVICE_REMOTE_WAKEUP); + if (err) { + DPRINTFN(0, "Setting device " + "remote wakeup failed!\n"); + } + } + USB_BUS_LOCK(udev->bus); + /* + * Set that this device is suspended. This variable must be set + * before calling USB controller suspend callbacks. + */ + udev->pwr_save.suspended = 1; + USB_BUS_UNLOCK(udev->bus); + + if (udev->bus->methods->device_suspend != NULL) { + + /* suspend device on the USB controller */ + (udev->bus->methods->device_suspend) (udev); + + /* do DMA delay */ + temp = usb2_get_dma_delay(udev->bus); + usb2_pause_mtx(&Giant, temp); + + } + /* suspend current port */ + err = usb2_req_set_port_feature(udev->parent_hub, + &Giant, udev->port_no, UHF_PORT_SUSPEND); + if (err) { + DPRINTFN(0, "Suspending port failed\n"); + return; + } + if (suspend_parent) { + udev = udev->parent_hub; + goto repeat; + } + return; +} + +/*------------------------------------------------------------------------* + * usb2_set_power_mode + * + * This function will set the power mode, see USB_POWER_MODE_XXX for a + * USB device. + *------------------------------------------------------------------------*/ +void +usb2_set_power_mode(struct usb2_device *udev, uint8_t power_mode) +{ + /* filter input argument */ + if (power_mode != USB_POWER_MODE_ON) { + power_mode = USB_POWER_MODE_SAVE; + } + udev->power_mode = power_mode; /* update copy of power mode */ + + usb2_bus_power_update(udev->bus); + + return; +} diff --git a/sys/dev/usb2/core/usb2_hub.h b/sys/dev/usb2/core/usb2_hub.h index 8ef618e..87d85b3 100644 --- a/sys/dev/usb2/core/usb2_hub.h +++ b/sys/dev/usb2/core/usb2_hub.h @@ -74,5 +74,7 @@ struct usb2_device *usb2_bus_port_get_device(struct usb2_bus *bus, struct usb2_port *up); void usb2_needs_explore(struct usb2_bus *bus, uint8_t do_probe); void usb2_needs_explore_all(void); +void usb2_bus_power_update(struct usb2_bus *bus); +void usb2_bus_powerd(struct usb2_bus *bus); #endif /* _USB2_HUB_H_ */ diff --git a/sys/dev/usb2/core/usb2_msctest.c b/sys/dev/usb2/core/usb2_msctest.c index ef616ce..452f389 100644 --- a/sys/dev/usb2/core/usb2_msctest.c +++ b/sys/dev/usb2/core/usb2_msctest.c @@ -36,7 +36,6 @@ #include <dev/usb2/include/usb2_mfunc.h> #include <dev/usb2/include/usb2_error.h> #include <dev/usb2/include/usb2_standard.h> -#include <dev/usb2/include/usb2_devid.h> #define USB_DEBUG_VAR usb2_debug @@ -577,163 +576,3 @@ done: free(sc, M_USB); return (err); } - -/* - * NOTE: The entries marked with XXX should be checked for the correct - * speed indication to set the buffer sizes. - */ -static const struct usb2_device_id u3g_devs[] = { - /* OEM: Option */ - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, - {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, - /* OEM: Qualcomm, Inc. */ - {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, - /* OEM: Huawei */ - {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))}, - {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))}, - /* OEM: Novatel */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, - {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, - /* OEM: Merlin */ - {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - /* OEM: Sierra Wireless: */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ - /* Sierra TruInstaller device ID */ - {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))}, -}; - -static void -u3g_sierra_init(struct usb2_device *udev) -{ - struct usb2_device_request req; - - DPRINTFN(0, "\n"); - - req.bmRequestType = UT_VENDOR; - req.bRequest = UR_SET_INTERFACE; - USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); - USETW(req.wIndex, UHF_PORT_CONNECTION); - USETW(req.wLength, 0); - - if (usb2_do_request_flags(udev, NULL, &req, - NULL, 0, NULL, USB_MS_HZ)) { - /* ignore any errors */ - } - return; -} - -static void -u3g_huawei_init(struct usb2_device *udev) -{ - struct usb2_device_request req; - - DPRINTFN(0, "\n"); - - req.bmRequestType = UT_WRITE_DEVICE; - req.bRequest = UR_SET_FEATURE; - USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); - USETW(req.wIndex, UHF_PORT_SUSPEND); - USETW(req.wLength, 0); - - if (usb2_do_request_flags(udev, NULL, &req, - NULL, 0, NULL, USB_MS_HZ)) { - /* ignore any errors */ - } - return; -} - -int -usb2_lookup_huawei(struct usb2_attach_arg *uaa) -{ - /* Calling the lookup function will also set the driver info! */ - return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); -} - -/* - * The following function handles 3G modem devices (E220, Mobile, - * etc.) with auto-install flash disks for Windows/MacOSX on the first - * interface. After some command or some delay they change appearance - * to a modem. - */ -usb2_error_t -usb2_test_huawei(struct usb2_device *udev, struct usb2_attach_arg *uaa) -{ - struct usb2_interface *iface; - struct usb2_interface_descriptor *id; - uint32_t flags; - - if (udev == NULL) { - return (USB_ERR_INVAL); - } - iface = usb2_get_iface(udev, 0); - if (iface == NULL) { - return (USB_ERR_INVAL); - } - id = iface->idesc; - if (id == NULL) { - return (USB_ERR_INVAL); - } - if (id->bInterfaceClass != UICLASS_MASS) { - return (USB_ERR_INVAL); - } - if (usb2_lookup_huawei(uaa)) { - /* no device match */ - return (USB_ERR_INVAL); - } - flags = USB_GET_DRIVER_INFO(uaa); - - if (flags & U3GFL_HUAWEI_INIT) { - u3g_huawei_init(udev); - } else if (flags & U3GFL_SCSI_EJECT) { - return (usb2_test_autoinstall(udev, 0, 1)); - } else if (flags & U3GFL_SIERRA_INIT) { - u3g_sierra_init(udev); - } else { - /* no quirks */ - return (USB_ERR_INVAL); - } - return (0); /* success */ -} diff --git a/sys/dev/usb2/core/usb2_msctest.h b/sys/dev/usb2/core/usb2_msctest.h index cdd335f..5bf64d0 100644 --- a/sys/dev/usb2/core/usb2_msctest.h +++ b/sys/dev/usb2/core/usb2_msctest.h @@ -29,30 +29,5 @@ usb2_error_t usb2_test_autoinstall(struct usb2_device *udev, uint8_t iface_index, uint8_t do_eject); -usb2_error_t usb2_test_huawei(struct usb2_device *udev, - struct usb2_attach_arg *uaa); -int usb2_lookup_huawei(struct usb2_attach_arg *uaa); - -/* Huawei specific defines */ - -#define U3GINFO(flag,speed) ((flag)|((speed) * 256)) -#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256) - -#define U3GFL_NONE 0x00 -#define U3GFL_HUAWEI_INIT 0x01 /* Requires init command (Huawei - * cards) */ -#define U3GFL_SCSI_EJECT 0x02 /* Requires SCSI eject command - * (Novatel) */ -#define U3GFL_SIERRA_INIT 0x04 /* Requires init command (Sierra - * cards) */ - -#define U3GSP_GPRS 0 -#define U3GSP_EDGE 1 -#define U3GSP_CDMA 2 -#define U3GSP_UMTS 3 -#define U3GSP_HSDPA 4 -#define U3GSP_HSUPA 5 -#define U3GSP_HSPA 6 -#define U3GSP_MAX 7 #endif /* _USB2_MSCTEST_H_ */ diff --git a/sys/dev/usb2/core/usb2_request.c b/sys/dev/usb2/core/usb2_request.c index 030a022..b1eb81f 100644 --- a/sys/dev/usb2/core/usb2_request.c +++ b/sys/dev/usb2/core/usb2_request.c @@ -611,7 +611,8 @@ usb2_req_get_desc(struct usb2_device *udev, struct mtx *mtx, void *desc, } USETW(req.wLength, min_len); - err = usb2_do_request(udev, mtx, &req, desc); + err = usb2_do_request_flags(udev, mtx, &req, + desc, 0, NULL, 1000); if (err) { if (!retries) { @@ -1326,6 +1327,7 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) struct usb2_device *parent_hub; usb2_error_t err; uint8_t old_addr; + uint8_t do_retry = 1; if (udev->flags.usb2_mode != USB_MODE_HOST) { return (USB_ERR_INVAL); @@ -1335,6 +1337,7 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) if (parent_hub == NULL) { return (USB_ERR_INVAL); } +retry: err = usb2_req_reset_port(parent_hub, mtx, udev->port_no); if (err) { DPRINTFN(0, "addr=%d, port reset failed\n", old_addr); @@ -1355,9 +1358,8 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) err = usb2_req_set_address(udev, mtx, old_addr); if (err) { /* XXX ignore any errors! */ - DPRINTFN(0, "addr=%d, set address failed\n", + DPRINTFN(0, "addr=%d, set address failed! (ignored)\n", old_addr); - err = 0; } /* restore device address */ udev->address = old_addr; @@ -1381,7 +1383,57 @@ usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx) goto done; } done: + if (err && do_retry) { + /* give the USB firmware some time to load */ + usb2_pause_mtx(mtx, 500); + /* no more retries after this retry */ + do_retry = 0; + /* try again */ + goto retry; + } /* restore address */ udev->address = old_addr; return (err); } + +/*------------------------------------------------------------------------* + * usb2_req_clear_device_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, + uint16_t sel) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_CLEAR_FEATURE; + USETW(req.wValue, sel); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} + +/*------------------------------------------------------------------------* + * usb2_req_set_device_feature + * + * Returns: + * 0: Success + * Else: Failure + *------------------------------------------------------------------------*/ +usb2_error_t +usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, + uint16_t sel) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, sel); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + return (usb2_do_request(udev, mtx, &req, 0)); +} diff --git a/sys/dev/usb2/core/usb2_request.h b/sys/dev/usb2/core/usb2_request.h index 3869968..b33e0a1 100644 --- a/sys/dev/usb2/core/usb2_request.h +++ b/sys/dev/usb2/core/usb2_request.h @@ -89,6 +89,8 @@ usb2_error_t usb2_req_set_report(struct usb2_device *udev, struct mtx *mtx, void *data, uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id); usb2_error_t usb2_req_re_enumerate(struct usb2_device *udev, struct mtx *mtx); +usb2_error_t usb2_req_clear_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); +usb2_error_t usb2_req_set_device_feature(struct usb2_device *udev, struct mtx *mtx, uint16_t sel); #define usb2_do_request(u,m,r,d) \ usb2_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT) diff --git a/sys/dev/usb2/core/usb2_transfer.c b/sys/dev/usb2/core/usb2_transfer.c index 1cf6956..f574436 100644 --- a/sys/dev/usb2/core/usb2_transfer.c +++ b/sys/dev/usb2/core/usb2_transfer.c @@ -120,7 +120,6 @@ static const struct usb2_config usb2_control_ep_cfg[USB_DEFAULT_XFER_MAX] = { /* function prototypes */ static void usb2_update_max_frame_size(struct usb2_xfer *); -static uint32_t usb2_get_dma_delay(struct usb2_bus *); static void usb2_transfer_unsetup_sub(struct usb2_xfer_root *, uint8_t); static void usb2_control_transfer_init(struct usb2_xfer *); static uint8_t usb2_start_hardware_sub(struct usb2_xfer *); @@ -161,7 +160,7 @@ usb2_update_max_frame_size(struct usb2_xfer *xfer) * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ -static uint32_t +uint32_t usb2_get_dma_delay(struct usb2_bus *bus) { uint32_t temp = 0; @@ -1374,6 +1373,9 @@ usb2_start_hardware(struct usb2_xfer *xfer) /* set "transferring" flag */ xfer->flags_int.transferring = 1; + /* increment power reference */ + usb2_transfer_power_ref(xfer, 1); + /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": @@ -1886,6 +1888,9 @@ usb2_callback_wrapper(struct usb2_xfer_queue *pq) USB_BUS_LOCK(xfer->udev->bus); goto done; } + /* decrement power reference */ + usb2_transfer_power_ref(xfer, -1); + xfer->flags_int.transferring = 0; if (xfer->error) { diff --git a/sys/dev/usb2/core/usb2_transfer.h b/sys/dev/usb2/core/usb2_transfer.h index 1351046..698509c 100644 --- a/sys/dev/usb2/core/usb2_transfer.h +++ b/sys/dev/usb2/core/usb2_transfer.h @@ -124,5 +124,7 @@ usb2_callback_t usb2_handle_request_callback; usb2_callback_t usb2_do_clear_stall_callback; void usb2_transfer_timeout_ms(struct usb2_xfer *xfer, void (*cb) (void *arg), uint32_t ms); +uint32_t usb2_get_dma_delay(struct usb2_bus *bus); +void usb2_transfer_power_ref(struct usb2_xfer *xfer, int val); #endif /* _USB2_TRANSFER_H_ */ diff --git a/sys/dev/usb2/ethernet/if_axe2.c b/sys/dev/usb2/ethernet/if_axe2.c index 0231235..64f2376 100644 --- a/sys/dev/usb2/ethernet/if_axe2.c +++ b/sys/dev/usb2/ethernet/if_axe2.c @@ -186,6 +186,7 @@ static void axe_cfg_cmd(struct axe_softc *, uint16_t, uint16_t, uint16_t, void *); static void axe_cfg_ax88178_init(struct axe_softc *); static void axe_cfg_ax88772_init(struct axe_softc *); +static int axe_get_phyno(struct axe_softc *, int); static const struct usb2_config axe_config[AXE_ENDPT_MAX] = { @@ -335,34 +336,26 @@ axe_cfg_miibus_readreg(device_t dev, int phy, int reg) do_unlock = 1; } -#if 0 - /* - * The chip tells us the MII address of any supported - * PHYs attached to the chip, so only read from those. - */ - - if ((sc->sc_phyaddrs[0] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[0])) { - val = 0; - goto done; - } - if ((sc->sc_phyaddrs[1] != AXE_NOPHY) && (phy != sc->sc_phyaddrs[1])) { - val = 0; - goto done; - } -#endif - if ((sc->sc_phyaddrs[0] != 0xFF) && (sc->sc_phyaddrs[0] != phy)) { + if (sc->sc_phyno != phy) { val = 0; goto done; } + axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cfg_cmd(sc, AXE_CMD_MII_READ_REG, reg, phy, &val); axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); val = le16toh(val); - - if (val && (val != 0xffff)) { - sc->sc_phyaddrs[0] = phy; + if ((sc->sc_flags & AXE_FLAG_772) != 0 && reg == MII_BMSR) { + /* + * BMSR of AX88772 indicates that it supports extended + * capability but the extended status register is + * revered for embedded ethernet PHY. So clear the + * extended capability bit of BMSR. + */ + val &= ~BMSR_EXTCAP; } + done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); @@ -386,10 +379,14 @@ axe_cfg_miibus_writereg(device_t dev, int phy, int reg, int val) do_unlock = 1; } + if (sc->sc_phyno != phy) + goto done; + axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_SW, 0, 0, NULL); axe_cfg_cmd(sc, AXE_CMD_MII_WRITE_REG, reg, phy, &val); axe_cfg_cmd(sc, AXE_CMD_MII_OPMODE_HW, 0, 0, NULL); +done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); } @@ -401,6 +398,7 @@ axe_cfg_miibus_statchg(device_t dev) { struct axe_softc *sc = device_get_softc(dev); struct mii_data *mii = GET_MII(sc); + struct ifnet *ifp; uint16_t val; uint8_t do_unlock; @@ -412,15 +410,40 @@ axe_cfg_miibus_statchg(device_t dev) do_unlock = 1; } - if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) - val = AXE_MEDIA_FULL_DUPLEX; - else - val = 0; + ifp = sc->sc_ifp; + if (mii == NULL || ifp == NULL || + (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + goto done; - if (sc->sc_flags & (AXE_FLAG_772 | AXE_FLAG_178)) { + sc->sc_flags &= ~AXE_FLAG_LINK; + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID)) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->sc_flags |= AXE_FLAG_LINK; + break; + case IFM_1000_T: + if ((sc->sc_flags & AXE_FLAG_178) == 0) + break; + sc->sc_flags |= AXE_FLAG_LINK; + break; + default: + break; + } + } - val |= (AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC); + /* Lost link, do nothing. */ + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) + goto done; + val = 0; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) + val |= AXE_MEDIA_FULL_DUPLEX; + if (sc->sc_flags & (AXE_FLAG_178 | AXE_FLAG_772)) { + val |= AXE_178_MEDIA_RX_EN | AXE_178_MEDIA_MAGIC; + if ((sc->sc_flags & AXE_FLAG_178) != 0) + val |= AXE_178_MEDIA_ENCK; switch (IFM_SUBTYPE(mii->mii_media_active)) { case IFM_1000_T: val |= AXE_178_MEDIA_GMII | AXE_178_MEDIA_ENCK; @@ -434,6 +457,7 @@ axe_cfg_miibus_statchg(device_t dev) } } axe_cfg_cmd(sc, AXE_CMD_WRITE_MEDIA, 0, val, NULL); +done: if (do_unlock) { mtx_unlock(&sc->sc_mtx); } @@ -467,7 +491,6 @@ axe_cfg_ifmedia_upd(struct axe_softc *sc, /* not ready */ return; } - sc->sc_flags |= AXE_FLAG_WAIT_LINK; if (mii->mii_instance) { struct mii_softc *miisc; @@ -550,6 +573,30 @@ axe_cfg_reset(struct axe_softc *sc) err = usb2_config_td_sleep(&sc->sc_config_td, hz / 100); } +static int +axe_get_phyno(struct axe_softc *sc, int sel) +{ + int phyno; + + switch (AXE_PHY_TYPE(sc->sc_phyaddrs[sel])) { + case PHY_TYPE_100_HOME: + case PHY_TYPE_GIG: + phyno = AXE_PHY_NO(sc->sc_phyaddrs[sel]); + break; + case PHY_TYPE_SPECIAL: + /* FALLTHROUGH */ + case PHY_TYPE_RSVD: + /* FALLTHROUGH */ + case PHY_TYPE_NON_SUP: + /* FALLTHROUGH */ + default: + phyno = -1; + break; + } + + return (phyno); +} + /* * Probe for a AX88172 chip. */ @@ -617,8 +664,6 @@ axe_attach(device_t dev) } mtx_lock(&sc->sc_mtx); - sc->sc_flags |= AXE_FLAG_WAIT_LINK; - /* start setup */ usb2_config_td_queue_command @@ -687,6 +732,9 @@ axe_cfg_ax88178_init(struct axe_softc *sc) axe_cfg_cmd(sc, AXE_CMD_SW_RESET_REG, 0, AXE_SW_RESET_PRL | AXE_178_RESET_MAGIC, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); + /* Enable MII/GMII/RGMII interface to work with external PHY. */ + axe_cfg_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0, NULL); + err = usb2_config_td_sleep(&sc->sc_config_td, hz / 4); axe_cfg_cmd(sc, AXE_CMD_RXCTL_WRITE, 0, 0, NULL); } @@ -701,7 +749,7 @@ axe_cfg_ax88772_init(struct axe_softc *sc) axe_cfg_cmd(sc, AXE_CMD_WRITE_GPIO, 0, 0x00b0, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 16); - if (sc->sc_phyaddrs[1] == AXE_INTPHY) { + if (sc->sc_phyno == AXE_772_PHY_NO_EPHY) { /* ask for the embedded PHY */ axe_cfg_cmd(sc, AXE_CMD_SW_PHY_SELECT, 0, 0x01, NULL); err = usb2_config_td_sleep(&sc->sc_config_td, hz / 64); @@ -752,6 +800,19 @@ axe_cfg_first_time_setup(struct axe_softc *sc, * Load PHY indexes first. Needed by axe_xxx_init(). */ axe_cfg_cmd(sc, AXE_CMD_READ_PHYID, 0, 0, sc->sc_phyaddrs); +#if 1 + device_printf(sc->sc_dev, "PHYADDR 0x%02x:0x%02x\n", + sc->sc_phyaddrs[0], sc->sc_phyaddrs[1]); +#endif + sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_PRI); + if (sc->sc_phyno == -1) + sc->sc_phyno = axe_get_phyno(sc, AXE_PHY_SEL_SEC); + if (sc->sc_phyno == -1) { + device_printf(sc->sc_dev, + "no valid PHY address found, " + "assuming PHY address 0\n"); + sc->sc_phyno = 0; + } if (sc->sc_flags & AXE_FLAG_178) { axe_cfg_ax88178_init(sc); @@ -771,12 +832,6 @@ axe_cfg_first_time_setup(struct axe_softc *sc, */ axe_cfg_cmd(sc, AXE_CMD_READ_IPG012, 0, 0, sc->sc_ipgs); - /* - * Work around broken adapters that appear to lie about - * their PHY addresses. - */ - sc->sc_phyaddrs[0] = sc->sc_phyaddrs[1] = 0xFF; - mtx_unlock(&sc->sc_mtx); ifp = if_alloc(IFT_ETHER); @@ -1108,7 +1163,7 @@ axe_bulk_write_callback(struct usb2_xfer *xfer) usb2_transfer_start(sc->sc_xfer[2]); goto done; } - if (sc->sc_flags & AXE_FLAG_WAIT_LINK) { + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { /* * don't send anything if there is no link ! */ @@ -1204,19 +1259,17 @@ axe_cfg_tick(struct axe_softc *sc, return; } mii_tick(mii); - - mii_pollstat(mii); - - if ((sc->sc_flags & AXE_FLAG_WAIT_LINK) && - (mii->mii_media_status & IFM_ACTIVE) && - (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)) { - sc->sc_flags &= ~AXE_FLAG_WAIT_LINK; - } sc->sc_media_active = mii->mii_media_active; sc->sc_media_status = mii->mii_media_status; - + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { + axe_cfg_miibus_statchg(sc->sc_dev); + /* XXX */ + if ((sc->sc_flags & AXE_FLAG_LINK) == 0) { + sc->sc_media_active = IFM_ETHER | IFM_NONE; + sc->sc_media_status = IFM_AVALID; + } + } /* start stopped transfers, if any */ - axe_start_transfers(sc); } @@ -1444,7 +1497,7 @@ axe_cfg_pre_stop(struct axe_softc *sc, sc->sc_flags &= ~(AXE_FLAG_HL_READY | AXE_FLAG_LL_READY); - sc->sc_flags |= AXE_FLAG_WAIT_LINK; + sc->sc_flags &= ~AXE_FLAG_LINK; /* * stop all the transfers, if not already stopped: diff --git a/sys/dev/usb2/ethernet/if_axe2_reg.h b/sys/dev/usb2/ethernet/if_axe2_reg.h index 9228f77..79c745f 100644 --- a/sys/dev/usb2/ethernet/if_axe2_reg.h +++ b/sys/dev/usb2/ethernet/if_axe2_reg.h @@ -135,8 +135,23 @@ #define AXE_178_RXCMD_MFB_8192 0x0200 #define AXE_178_RXCMD_MFB_16384 0x0300 -#define AXE_NOPHY 0xE0 -#define AXE_INTPHY 0x10 +#define AXE_PHY_SEL_PRI 1 +#define AXE_PHY_SEL_SEC 0 +#define AXE_PHY_TYPE_MASK 0xE0 +#define AXE_PHY_TYPE_SHIFT 5 +#define AXE_PHY_TYPE(x) \ + (((x) & AXE_PHY_TYPE_MASK) >> AXE_PHY_TYPE_SHIFT) + +#define PHY_TYPE_100_HOME 0 /* 10/100 or 1M HOME PHY */ +#define PHY_TYPE_GIG 1 /* Gigabit PHY */ +#define PHY_TYPE_SPECIAL 4 /* Special case */ +#define PHY_TYPE_RSVD 5 /* Reserved */ +#define PHY_TYPE_NON_SUP 7 /* Non-supported PHY */ + +#define AXE_PHY_NO_MASK 0x1F +#define AXE_PHY_NO(x) ((x) & AXE_PHY_NO_MASK) + +#define AXE_772_PHY_NO_EPHY 0x10 /* Embedded 10/100 PHY of AX88772 */ #define AXE_BULK_BUF_SIZE 16384 /* bytes */ @@ -170,12 +185,14 @@ struct axe_softc { device_t sc_miibus; device_t sc_dev; + int sc_phyno; + uint32_t sc_unit; uint32_t sc_media_active; uint32_t sc_media_status; uint16_t sc_flags; -#define AXE_FLAG_WAIT_LINK 0x0001 +#define AXE_FLAG_LINK 0x0001 #define AXE_FLAG_INTR_STALL 0x0002 #define AXE_FLAG_READ_STALL 0x0004 #define AXE_FLAG_WRITE_STALL 0x0008 diff --git a/sys/dev/usb2/image/uscanner2.c b/sys/dev/usb2/image/uscanner2.c index fa939ef..4ce13a8 100644 --- a/sys/dev/usb2/image/uscanner2.c +++ b/sys/dev/usb2/image/uscanner2.c @@ -266,6 +266,7 @@ static const struct usb2_device_id uscanner_devs[] = { {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4100C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4200C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4300C, 0)}, + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4470C, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_4670V, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_S20, 0)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_5200C, 0)}, diff --git a/sys/dev/usb2/include/usb2_devid.h b/sys/dev/usb2/include/usb2_devid.h index 08c4b71..c5b5109 100644 --- a/sys/dev/usb2/include/usb2_devid.h +++ b/sys/dev/usb2/include/usb2_devid.h @@ -4,7 +4,7 @@ * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. * * generated from: - * FreeBSD: head/sys/dev/usb/usbdevs 185998 2008-12-12 18:34:27Z thompsa + * FreeBSD: src/sys/dev/usb/usbdevs,v 1.390 2008/12/23 13:09:17 remko Exp */ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ @@ -533,6 +533,7 @@ #define USB_VENDOR_FOSSIL 0x0e67 /* Fossil, Inc */ #define USB_VENDOR_GMATE 0x0e7e /* G.Mate, Inc */ #define USB_VENDOR_OTI 0x0ea0 /* Ours Technology */ +#define USB_VENDOR_YISO 0x0eab /* Yiso Wireless Co. */ #define USB_VENDOR_PILOTECH 0x0eaf /* Pilotech */ #define USB_VENDOR_NOVATECH 0x0eb0 /* NovaTech */ #define USB_VENDOR_ITEGNO 0x0eba /* iTegno */ @@ -814,6 +815,7 @@ /* Alcor Micro, Inc. products */ #define USB_PRODUCT_ALCOR2_KBD_HUB 0x2802 /* Kbd Hub */ +#define USB_PRODUCT_ALCOR_TRANSCEND 0x6387 /* Transcend JetFlash Drive */ #define USB_PRODUCT_ALCOR_MA_KBD_HUB 0x9213 /* MacAlly Kbd Hub */ #define USB_PRODUCT_ALCOR_AU9814 0x9215 /* AU9814 Hub */ #define USB_PRODUCT_ALCOR_UMCR_9361 0x9361 /* USB Multimedia Card Reader */ @@ -1286,6 +1288,7 @@ #define USB_PRODUCT_FTDI_EMCU2D 0xe88a /* Expert mouseCLOCK USB II */ #define USB_PRODUCT_FTDI_PCMSFU 0xe88b /* Precision Clock MSF USB */ #define USB_PRODUCT_FTDI_EMCU2H 0xe88c /* Expert mouseCLOCK USB II HBG */ +#define USB_PRODUCT_FTDI_MAXSTREAM 0xee18 /* Maxstream PKG-U */ #define USB_PRODUCT_FTDI_USBSERIAL 0xfa00 /* Matrix Orbital USB Serial */ #define USB_PRODUCT_FTDI_MX2_3 0xfa01 /* Matrix Orbital MX2 or MX3 */ #define USB_PRODUCT_FTDI_MX4_5 0xfa02 /* Matrix Orbital MX4 or MX5 */ @@ -1423,6 +1426,7 @@ #define USB_PRODUCT_HP_2200C 0x0605 /* ScanJet 2200C */ #define USB_PRODUCT_HP_5300C 0x0701 /* Scanjet 5300C */ #define USB_PRODUCT_HP_4400C 0x0705 /* Scanjet 4400C */ +#define USB_PRODUCT_HP_4470C 0x0805 /* Scanjet 4470C */ #define USB_PRODUCT_HP_82x0C 0x0b01 /* Scanjet 82x0C */ #define USB_PRODUCT_HP_2300D 0x0b17 /* Laserjet 2300d */ #define USB_PRODUCT_HP_970CSE 0x1004 /* Deskjet 970Cse */ @@ -2008,6 +2012,7 @@ #define USB_PRODUCT_QUALCOMM2_CDMA_MSM 0x3196 /* CDMA Technologies MSM modem */ #define USB_PRODUCT_QUALCOMMINC_CDMA_MSM 0x0001 /* CDMA Technologies MSM modem */ #define USB_PRODUCT_QUALCOMMINC_ZTE_STOR 0x2000 /* USB ZTE Storage */ +#define USB_PRODUCT_QUALCOMMINC_AC8700 0xfffe /* CDMA 1xEVDO USB modem */ /* Qtronix products */ #define USB_PRODUCT_QTRONIX_980N 0x2011 /* Scorpion-980N keyboard */ @@ -2469,6 +2474,9 @@ #define USB_PRODUCT_YANO_U640MO 0x0101 /* U640MO-03 */ #define USB_PRODUCT_YANO_FW800HD 0x05fc /* METALWEAR-HDD */ +/* Yiso Wireless Co. products */ +#define USB_PRODUCT_YISO_C893 0xc893 /* CDMA 2000 1xEVDO PC Card */ + /* Z-Com products */ #define USB_PRODUCT_ZCOM_M4Y750 0x0001 /* M4Y-750 */ #define USB_PRODUCT_ZCOM_XI725 0x0002 /* XI-725/726 */ diff --git a/sys/dev/usb2/include/usb2_devtable.h b/sys/dev/usb2/include/usb2_devtable.h index e678540..afc9ba9 100644 --- a/sys/dev/usb2/include/usb2_devtable.h +++ b/sys/dev/usb2/include/usb2_devtable.h @@ -4,7 +4,7 @@ * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. * * generated from: - * FreeBSD: head/sys/dev/usb/usbdevs 185998 2008-12-12 18:34:27Z thompsa + * FreeBSD: src/sys/dev/usb/usbdevs,v 1.390 2008/12/23 13:09:17 remko Exp */ /* $NetBSD: usbdevs,v 1.392 2004/12/29 08:38:44 imp Exp $ */ @@ -623,6 +623,12 @@ const struct usb_knowndev usb_knowndevs[] = { "Kbd Hub", }, { + USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_TRANSCEND, + 0, + "Alcor Micro", + "Transcend JetFlash Drive", + }, + { USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_MA_KBD_HUB, 0, "Alcor Micro", @@ -2495,6 +2501,12 @@ const struct usb_knowndev usb_knowndevs[] = { "Expert mouseCLOCK USB II HBG", }, { + USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, + 0, + "Future Technology Devices", + "Maxstream PKG-U", + }, + { USB_VENDOR_FTDI, USB_PRODUCT_FTDI_USBSERIAL, 0, "Future Technology Devices", @@ -3041,6 +3053,12 @@ const struct usb_knowndev usb_knowndevs[] = { "Scanjet 4400C", }, { + USB_VENDOR_HP, USB_PRODUCT_HP_4470C, + 0, + "Hewlett Packard", + "Scanjet 4470C", + }, + { USB_VENDOR_HP, USB_PRODUCT_HP_82x0C, 0, "Hewlett Packard", @@ -5471,6 +5489,12 @@ const struct usb_knowndev usb_knowndevs[] = { "USB ZTE Storage", }, { + USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_AC8700, + 0, + "Qualcomm, Incorporated", + "CDMA 1xEVDO USB modem", + }, + { USB_VENDOR_QTRONIX, USB_PRODUCT_QTRONIX_980N, 0, "Qtronix", @@ -7145,6 +7169,12 @@ const struct usb_knowndev usb_knowndevs[] = { "METALWEAR-HDD", }, { + USB_VENDOR_YISO, USB_PRODUCT_YISO_C893, + 0, + "Yiso Wireless Co.", + "CDMA 2000 1xEVDO PC Card", + }, + { USB_VENDOR_ZCOM, USB_PRODUCT_ZCOM_M4Y750, 0, "Z-Com", @@ -10055,6 +10085,12 @@ const struct usb_knowndev usb_knowndevs[] = { NULL, }, { + USB_VENDOR_YISO, 0, + USB_KNOWNDEV_NOPROD, + "Yiso Wireless Co.", + NULL, + }, + { USB_VENDOR_PILOTECH, 0, USB_KNOWNDEV_NOPROD, "Pilotech", diff --git a/sys/dev/usb2/include/usb2_ioctl.h b/sys/dev/usb2/include/usb2_ioctl.h index 2a679c9..a4ec979 100644 --- a/sys/dev/usb2/include/usb2_ioctl.h +++ b/sys/dev/usb2/include/usb2_ioctl.h @@ -39,13 +39,6 @@ #define USB_DEVICE_NAME "usb" #define USB_GENERIC_NAME "ugen" -/* definition of USB power mode */ -#define USB_POWER_MODE_OFF 0 /* turn off device */ -#define USB_POWER_MODE_ON 1 /* always on */ -#define USB_POWER_MODE_SAVE 2 /* automatic suspend and resume */ -#define USB_POWER_MODE_SUSPEND 3 /* force suspend */ -#define USB_POWER_MODE_RESUME 4 /* force resume */ - struct usb2_read_dir { void *urd_data; uint32_t urd_startentry; diff --git a/sys/dev/usb2/include/usb2_standard.h b/sys/dev/usb2/include/usb2_standard.h index 3671a51..fbe6f36 100644 --- a/sys/dev/usb2/include/usb2_standard.h +++ b/sys/dev/usb2/include/usb2_standard.h @@ -48,12 +48,20 @@ #define USB_POWER_DOWN_TIME 200 /* ms */ #define USB_PORT_POWER_DOWN_TIME 100 /* ms */ +/* Definition of software USB power modes */ +#define USB_POWER_MODE_OFF 0 /* turn off device */ +#define USB_POWER_MODE_ON 1 /* always on */ +#define USB_POWER_MODE_SAVE 2 /* automatic suspend and resume */ +#define USB_POWER_MODE_SUSPEND 3 /* force suspend */ +#define USB_POWER_MODE_RESUME 4 /* force resume */ + #if 0 /* These are the values from the USB specification. */ #define USB_PORT_RESET_DELAY 10 /* ms */ #define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ #define USB_PORT_RESET_RECOVERY 10 /* ms */ #define USB_PORT_POWERUP_DELAY 100 /* ms */ +#define USB_PORT_RESUME_DELAY 20 /* ms */ #define USB_SET_ADDRESS_SETTLE 2 /* ms */ #define USB_RESUME_DELAY (20*5) /* ms */ #define USB_RESUME_WAIT 10 /* ms */ @@ -65,6 +73,7 @@ #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 */ diff --git a/sys/dev/usb2/serial/u3g2.c b/sys/dev/usb2/serial/u3g2.c index a15f081..4e0bfab 100644 --- a/sys/dev/usb2/serial/u3g2.c +++ b/sys/dev/usb2/serial/u3g2.c @@ -37,7 +37,7 @@ #include <dev/usb2/include/usb2_standard.h> #include <dev/usb2/include/usb2_mfunc.h> #include <dev/usb2/include/usb2_error.h> -#include <dev/usb2/include/usb2_cdc.h> +#include <dev/usb2/include/usb2_defs.h> #define USB_DEBUG_VAR u3g_debug @@ -50,6 +50,8 @@ #include <dev/usb2/core/usb2_util.h> #include <dev/usb2/core/usb2_busdma.h> #include <dev/usb2/core/usb2_msctest.h> +#include <dev/usb2/core/usb2_dynamic.h> +#include <dev/usb2/core/usb2_device.h> #include <dev/usb2/serial/usb2_serial.h> @@ -66,6 +68,20 @@ SYSCTL_INT(_hw_usb2_u3g, OID_AUTO, debug, CTLFLAG_RW, #define U3G_CONFIG_INDEX 0 #define U3G_BSIZE 2048 +#define U3GSP_GPRS 0 +#define U3GSP_EDGE 1 +#define U3GSP_CDMA 2 +#define U3GSP_UMTS 3 +#define U3GSP_HSDPA 4 +#define U3GSP_HSUPA 5 +#define U3GSP_HSPA 6 +#define U3GSP_MAX 7 + +#define U3GFL_NONE 0x00 /* No flags */ +#define U3GFL_HUAWEI_INIT 0x01 /* Init command required */ +#define U3GFL_SCSI_EJECT 0x02 /* SCSI eject command required */ +#define U3GFL_SIERRA_INIT 0x04 /* Init command required */ + struct u3g_speeds_s { uint32_t ispeed; uint32_t ospeed; @@ -98,6 +114,8 @@ static void u3g_stop_read(struct usb2_com_softc *ucom); static void u3g_start_write(struct usb2_com_softc *ucom); static void u3g_stop_write(struct usb2_com_softc *ucom); +static int u3g_driver_loaded(struct module *mod, int what, void *arg); + static const struct usb2_config u3g_config[U3G_N_TRANSFER] = { [0] = { @@ -151,10 +169,195 @@ static driver_t u3g_driver = { .size = sizeof(struct u3g_softc), }; -DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, NULL, 0); +DRIVER_MODULE(u3g, ushub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0); MODULE_DEPEND(u3g, usb2_serial, 1, 1, 1); MODULE_DEPEND(u3g, usb2_core, 1, 1, 1); +/* Huawei specific defines */ + +#define U3GINFO(flag,speed) ((flag)|((speed) * 256)) +#define U3G_GET_SPEED(uaa) (USB_GET_DRIVER_INFO(uaa) / 256) + +/* + * NOTE: The entries marked with XXX should be checked for the correct + * speed indication to set the buffer sizes. + */ +static const struct usb2_device_id u3g_devs[] = { + /* OEM: Option */ + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GQUAD, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GT3GPLUS, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAX36, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_GTMAXHSUPA, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_OPTION, USB_PRODUCT_OPTION_VODAFONEMC3G, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, + /* OEM: Qualcomm, Inc. */ + {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_ZTE_STOR, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_QUALCOMMINC, USB_PRODUCT_QUALCOMMINC_CDMA_MSM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, + /* OEM: Huawei */ + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_MOBILE, U3GINFO(U3GSP_HSDPA, U3GFL_HUAWEI_INIT))}, + {USB_VPI(USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220, U3GINFO(U3GSP_HSPA, U3GFL_HUAWEI_INIT))}, + /* OEM: Novatel */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_CDMA_MODEM, U3GINFO(U3GSP_CDMA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ES620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_MC950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U727, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U740_2, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_U870, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V620, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V640, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V720, U3GINFO(U3GSP_UMTS, U3GFL_SCSI_EJECT))}, /* XXX */ + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_V740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_X950D, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_XU870, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_NOVATEL, USB_PRODUCT_NOVATEL_ZEROCD, U3GINFO(U3GSP_HSUPA, U3GFL_SCSI_EJECT))}, + {USB_VPI(USB_VENDOR_DELL, USB_PRODUCT_DELL_U740, U3GINFO(U3GSP_HSDPA, U3GFL_SCSI_EJECT))}, + /* OEM: Merlin */ + {USB_VPI(USB_VENDOR_MERLIN, USB_PRODUCT_MERLIN_V620, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + /* OEM: Sierra Wireless: */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD580, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD595, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC595U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC597E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_C597, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC880U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881E, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC881U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM5625, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MINI5725, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AIRCARD875, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_2, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8755_3, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8765, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_AC875U, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8775_2, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSDPA, U3GFL_NONE))}, + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8780, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8781, U3GINFO(U3GSP_UMTS, U3GFL_NONE))}, /* XXX */ + {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_HS2300, U3GINFO(U3GSP_HSPA, U3GFL_NONE))}, /* XXX */ + /* Sierra TruInstaller device ID */ + {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_TRUINSTALL, U3GINFO(U3GSP_UMTS, U3GFL_SIERRA_INIT))}, +}; + +static void +u3g_sierra_init(struct usb2_device *udev) +{ + struct usb2_device_request req; + + DPRINTFN(0, "\n"); + + req.bmRequestType = UT_VENDOR; + req.bRequest = UR_SET_INTERFACE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, UHF_PORT_CONNECTION); + USETW(req.wLength, 0); + + if (usb2_do_request_flags(udev, NULL, &req, + NULL, 0, NULL, USB_MS_HZ)) { + /* ignore any errors */ + } + return; +} + +static void +u3g_huawei_init(struct usb2_device *udev) +{ + struct usb2_device_request req; + + DPRINTFN(0, "\n"); + + req.bmRequestType = UT_WRITE_DEVICE; + req.bRequest = UR_SET_FEATURE; + USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); + USETW(req.wIndex, UHF_PORT_SUSPEND); + USETW(req.wLength, 0); + + if (usb2_do_request_flags(udev, NULL, &req, + NULL, 0, NULL, USB_MS_HZ)) { + /* ignore any errors */ + } + return; +} + +static int +u3g_lookup_huawei(struct usb2_attach_arg *uaa) +{ + /* Calling the lookup function will also set the driver info! */ + return (usb2_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); +} + +/* + * The following function handles 3G modem devices (E220, Mobile, + * etc.) with auto-install flash disks for Windows/MacOSX on the first + * interface. After some command or some delay they change appearance + * to a modem. + */ +static usb2_error_t +u3g_test_huawei_autoinst(struct usb2_device *udev, + struct usb2_attach_arg *uaa) +{ + struct usb2_interface *iface; + struct usb2_interface_descriptor *id; + uint32_t flags; + + if (udev == NULL) { + return (USB_ERR_INVAL); + } + iface = usb2_get_iface(udev, 0); + if (iface == NULL) { + return (USB_ERR_INVAL); + } + id = iface->idesc; + if (id == NULL) { + return (USB_ERR_INVAL); + } + if (id->bInterfaceClass != UICLASS_MASS) { + return (USB_ERR_INVAL); + } + if (u3g_lookup_huawei(uaa)) { + /* no device match */ + return (USB_ERR_INVAL); + } + flags = USB_GET_DRIVER_INFO(uaa); + + if (flags & U3GFL_HUAWEI_INIT) { + u3g_huawei_init(udev); + } else if (flags & U3GFL_SCSI_EJECT) { + return (usb2_test_autoinstall(udev, 0, 1)); + } else if (flags & U3GFL_SIERRA_INIT) { + u3g_sierra_init(udev); + } else { + /* no quirks */ + return (USB_ERR_INVAL); + } + return (0); /* success */ +} + +static int +u3g_driver_loaded(struct module *mod, int what, void *arg) +{ + switch (what) { + case MOD_LOAD: + /* register our autoinstall handler */ + usb2_test_huawei_autoinst_p = &u3g_test_huawei_autoinst; + break; + case MOD_UNLOAD: + usb2_test_huawei_unload(NULL); + break; + default: + return (EOPNOTSUPP); + } + return (0); +} + static int u3g_probe(device_t self) { @@ -169,7 +372,7 @@ u3g_probe(device_t self) if (uaa->info.bInterfaceClass != UICLASS_VENDOR) { return (ENXIO); } - return (usb2_lookup_huawei(uaa)); + return (u3g_lookup_huawei(uaa)); } static int diff --git a/sys/dev/usb2/serial/uchcom2.c b/sys/dev/usb2/serial/uchcom2.c index 2a49c72..fef2a89 100644 --- a/sys/dev/usb2/serial/uchcom2.c +++ b/sys/dev/usb2/serial/uchcom2.c @@ -196,8 +196,6 @@ static const struct usb2_device_id uchcom_devs[] = { /* protypes */ -static int uchcom_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int, - struct thread *); static int uchcom_pre_param(struct usb2_com_softc *, struct termios *); static void uchcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); @@ -297,7 +295,6 @@ struct usb2_com_callback uchcom_callback = { .usb2_com_cfg_set_break = &uchcom_cfg_set_break, .usb2_com_cfg_param = &uchcom_cfg_param, .usb2_com_pre_param = &uchcom_pre_param, - .usb2_com_ioctl = &uchcom_ioctl, .usb2_com_start_read = &uchcom_start_read, .usb2_com_stop_read = &uchcom_stop_read, .usb2_com_start_write = &uchcom_start_write, @@ -738,13 +735,6 @@ uchcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) *msr = sc->sc_msr; } -static int -uchcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, - struct thread *td) -{ - return (ENOTTY); -} - static void uchcom_cfg_set_dtr(struct usb2_com_softc *ucom, uint8_t onoff) { diff --git a/sys/dev/usb2/serial/uftdi2.c b/sys/dev/usb2/serial/uftdi2.c index 8484afe..b686ec5 100644 --- a/sys/dev/usb2/serial/uftdi2.c +++ b/sys/dev/usb2/serial/uftdi2.c @@ -249,6 +249,7 @@ static struct usb2_device_id uftdi_devs[] = { {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2D, UFTDI_TYPE_8U232AM)}, {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_PCMSFU, UFTDI_TYPE_8U232AM)}, {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_EMCU2H, UFTDI_TYPE_8U232AM)}, + {USB_VPI(USB_VENDOR_FTDI, USB_PRODUCT_FTDI_MAXSTREAM, UFTDI_TYPE_8U232AM)}, {USB_VPI(USB_VENDOR_SIIG2, USB_PRODUCT_SIIG2_US2308, UFTDI_TYPE_8U232AM)}, {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_VALUECAN, UFTDI_TYPE_8U232AM)}, {USB_VPI(USB_VENDOR_INTREPIDCS, USB_PRODUCT_INTREPIDCS_NEOVI, UFTDI_TYPE_8U232AM)}, @@ -470,6 +471,7 @@ uftdi_read_callback(struct usb2_xfer *xfer) { struct uftdi_softc *sc = xfer->priv_sc; uint8_t buf[2]; + uint8_t ftdi_msr; uint8_t msr; uint8_t lsr; @@ -481,9 +483,19 @@ uftdi_read_callback(struct usb2_xfer *xfer) } usb2_copy_out(xfer->frbuffers, 0, buf, 2); - msr = FTDI_GET_MSR(buf); + ftdi_msr = FTDI_GET_MSR(buf); lsr = FTDI_GET_LSR(buf); + msr = 0; + if (ftdi_msr & FTDI_SIO_CTS_MASK) + msr |= SER_CTS; + if (ftdi_msr & FTDI_SIO_DSR_MASK) + msr |= SER_DSR; + if (ftdi_msr & FTDI_SIO_RI_MASK) + msr |= SER_RI; + if (ftdi_msr & FTDI_SIO_RLSD_MASK) + msr |= SER_DCD; + if ((sc->sc_msr != msr) || ((sc->sc_lsr & FTDI_LSR_MASK) != (lsr & FTDI_LSR_MASK))) { DPRINTF("status change msr=0x%02x (0x%02x) " diff --git a/sys/dev/usb2/serial/uplcom2.c b/sys/dev/usb2/serial/uplcom2.c index 9a51eb5..6e556e5 100644 --- a/sys/dev/usb2/serial/uplcom2.c +++ b/sys/dev/usb2/serial/uplcom2.c @@ -171,8 +171,6 @@ static void uplcom_start_write(struct usb2_com_softc *); static void uplcom_stop_write(struct usb2_com_softc *); static void uplcom_cfg_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); -static int uplcom_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int, - struct thread *); static void uplcom_cfg_do_request(struct uplcom_softc *, struct usb2_device_request *, void *); @@ -260,7 +258,6 @@ struct usb2_com_callback uplcom_callback = { .usb2_com_cfg_set_break = &uplcom_cfg_set_break, .usb2_com_cfg_param = &uplcom_cfg_param, .usb2_com_pre_param = &uplcom_pre_param, - .usb2_com_ioctl = &uplcom_ioctl, .usb2_com_start_read = &uplcom_start_read, .usb2_com_stop_read = &uplcom_stop_read, .usb2_com_start_write = &uplcom_start_write, @@ -768,13 +765,6 @@ uplcom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) *msr = sc->sc_msr; } -static int -uplcom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int flag, - struct thread *td) -{ - return (ENOTTY); -} - static void uplcom_intr_callback(struct usb2_xfer *xfer) { diff --git a/sys/dev/usb2/serial/uvscom2.c b/sys/dev/usb2/serial/uvscom2.c index 8f4094f..92577d4 100644 --- a/sys/dev/usb2/serial/uvscom2.c +++ b/sys/dev/usb2/serial/uvscom2.c @@ -172,8 +172,6 @@ static void uvscom_start_write(struct usb2_com_softc *); static void uvscom_stop_write(struct usb2_com_softc *); static void uvscom_cfg_get_status(struct usb2_com_softc *, uint8_t *, uint8_t *); -static int uvscom_ioctl(struct usb2_com_softc *, uint32_t, caddr_t, int, - struct thread *); static void uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t); static uint16_t uvscom_cfg_read_status(struct uvscom_softc *); @@ -247,7 +245,6 @@ static const struct usb2_com_callback uvscom_callback = { .usb2_com_cfg_close = &uvscom_cfg_close, .usb2_com_pre_open = &uvscom_pre_open, .usb2_com_pre_param = &uvscom_pre_param, - .usb2_com_ioctl = &uvscom_ioctl, .usb2_com_start_read = &uvscom_start_read, .usb2_com_stop_read = &uvscom_stop_read, .usb2_com_start_write = &uvscom_start_write, @@ -753,13 +750,6 @@ uvscom_cfg_get_status(struct usb2_com_softc *ucom, uint8_t *lsr, uint8_t *msr) *msr = sc->sc_msr; } -static int -uvscom_ioctl(struct usb2_com_softc *ucom, uint32_t cmd, caddr_t data, int fflag, - struct thread *td) -{ - return (ENOTTY); -} - static void uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value) { diff --git a/sys/dev/usb2/sound/uaudio2.c b/sys/dev/usb2/sound/uaudio2.c index b843ef1..614db85 100644 --- a/sys/dev/usb2/sound/uaudio2.c +++ b/sys/dev/usb2/sound/uaudio2.c @@ -80,22 +80,27 @@ #include <dev/sound/chip.h> #include "feeder_if.h" +static int uaudio_default_rate = 96000; +static int uaudio_default_bits = 32; +static int uaudio_default_channels = 2; + #if USB_DEBUG static int uaudio_debug = 0; SYSCTL_NODE(_hw_usb2, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, debug, CTLFLAG_RW, &uaudio_debug, 0, "uaudio debug level"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_rate, CTLFLAG_RW, + &uaudio_default_rate, 0, "uaudio default sample rate"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_bits, CTLFLAG_RW, + &uaudio_default_bits, 0, "uaudio default sample bits"); +SYSCTL_INT(_hw_usb2_uaudio, OID_AUTO, default_channels, CTLFLAG_RW, + &uaudio_default_channels, 0, "uaudio default sample channels"); #endif -static uint32_t uaudio_default_rate = 96000; -static uint8_t uaudio_default_bits = 32; -static uint8_t uaudio_default_channels = 2; - +#define UAUDIO_MINFRAMES 16 /* must be factor of 8 due HS-USB */ #define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ -#define UAUDIO_NFRAMES 25 /* ms of sound in each request */ #define UAUDIO_RECURSE_LIMIT 24 /* rounds */ -#define UAUDIO_DEFAULT_BUFSZ ((2 * 96000 * 4 * 2) / (1000 / UAUDIO_NCHANBUFS)) /* bytes */ #define MAKE_WORD(h,l) (((h) << 8) | (l)) #define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1) @@ -154,6 +159,7 @@ struct uaudio_chan { uint8_t *cur; /* current position in upper layer * buffer */ + uint32_t intr_size; /* in bytes */ uint32_t block_size; uint32_t sample_rate; uint32_t format; @@ -389,13 +395,13 @@ static const char *uaudio_mixer_get_terminal_name(uint16_t); #endif static const struct usb2_config - uaudio_cfg_record_full_speed[UAUDIO_NCHANBUFS] = { + uaudio_cfg_record[UAUDIO_NCHANBUFS] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UAUDIO_NFRAMES, + .mh.frames = UAUDIO_MINFRAMES, .mh.flags = {.short_xfer_ok = 1,}, .mh.callback = &uaudio_chan_record_callback, }, @@ -405,66 +411,20 @@ static const struct usb2_config .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UAUDIO_NFRAMES, + .mh.frames = UAUDIO_MINFRAMES, .mh.flags = {.short_xfer_ok = 1,}, .mh.callback = &uaudio_chan_record_callback, }, }; static const struct usb2_config - uaudio_cfg_record_high_speed[UAUDIO_NCHANBUFS] = { - [0] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = (UAUDIO_NFRAMES * 8), - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &uaudio_chan_record_callback, - }, - - [1] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_IN, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = (UAUDIO_NFRAMES * 8), - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &uaudio_chan_record_callback, - }, -}; - -static const struct usb2_config - uaudio_cfg_play_full_speed[UAUDIO_NCHANBUFS] = { - [0] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UAUDIO_NFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &uaudio_chan_play_callback, - }, - - [1] = { - .type = UE_ISOCHRONOUS, - .endpoint = UE_ADDR_ANY, - .direction = UE_DIR_OUT, - .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = UAUDIO_NFRAMES, - .mh.flags = {.short_xfer_ok = 1,}, - .mh.callback = &uaudio_chan_play_callback, - }, -}; - -static const struct usb2_config - uaudio_cfg_play_high_speed[UAUDIO_NCHANBUFS] = { + uaudio_cfg_play[UAUDIO_NCHANBUFS] = { [0] = { .type = UE_ISOCHRONOUS, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = (UAUDIO_NFRAMES * 8), + .mh.frames = UAUDIO_MINFRAMES, .mh.flags = {.short_xfer_ok = 1,}, .mh.callback = &uaudio_chan_play_callback, }, @@ -474,7 +434,7 @@ static const struct usb2_config .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .mh.bufsize = 0, /* use "wMaxPacketSize * frames" */ - .mh.frames = (UAUDIO_NFRAMES * 8), + .mh.frames = UAUDIO_MINFRAMES, .mh.flags = {.short_xfer_ok = 1,}, .mh.callback = &uaudio_chan_play_callback, }, @@ -706,10 +666,6 @@ uaudio_attach_sub(device_t dev, kobj_class_t mixer_class, kobj_class_t chan_clas struct uaudio_softc *sc = device_get_softc(device_get_parent(dev)); char status[SND_STATUSLEN]; - if (bootverbose) { - device_printf(dev, "using a default buffer " - "size of %u bytes\n", UAUDIO_DEFAULT_BUFSZ); - } uaudio_mixer_init(sc); if (sc->sc_uq_audio_swap_lr) { @@ -1066,19 +1022,20 @@ uaudio_chan_fill_info_sub(struct uaudio_softc *sc, struct usb2_device *udev, chan->iface_index = curidx; chan->iface_alt_index = alt_index; - chan->usb2_cfg = - (ep_dir == UE_DIR_IN) ? - ((fps == 1000) ? - uaudio_cfg_record_full_speed : - uaudio_cfg_record_high_speed) : - ((fps == 1000) ? - uaudio_cfg_play_full_speed : - uaudio_cfg_play_high_speed); - + if (ep_dir == UE_DIR_IN) + chan->usb2_cfg = + uaudio_cfg_record; + else + chan->usb2_cfg = + uaudio_cfg_play; sample_size = ((chan->p_asf1d->bNrChannels * chan->p_asf1d->bBitResolution) / 8); + /* + * NOTE: "chan->bytes_per_frame" + * should not be zero! + */ chan->bytes_per_frame = ((rate / fps) * sample_size); if (sc->sc_sndstat_valid) { @@ -1103,15 +1060,26 @@ uaudio_chan_fill_info(struct uaudio_softc *sc, struct usb2_device *udev) { uint32_t rate = uaudio_default_rate; uint32_t z; - uint16_t fps = (usb2_get_speed(udev) == USB_SPEED_HIGH) ? 8000 : 1000; + uint16_t fps = usb2_get_isoc_fps(udev); uint8_t bits = uaudio_default_bits; uint8_t y; uint8_t channels = uaudio_default_channels; uint8_t x; bits -= (bits % 8); + if ((bits == 0) || (bits > 32)) { + /* set a valid value */ + bits = 32; + } rate -= (rate % fps); - + if ((rate == 0) || (rate > 192000)) { + /* set a valid value */ + rate = 192000 - (192000 % fps); + } + if ((channels == 0) || (channels > 2)) { + /* set a valid value */ + channels = 2; + } if (sbuf_new(&sc->sc_sndstat, NULL, 4096, SBUF_AUTOEXTEND)) { sc->sc_sndstat_valid = 1; } @@ -1141,21 +1109,23 @@ uaudio_chan_play_callback(struct usb2_xfer *xfer) { struct uaudio_chan *ch = xfer->priv_sc; uint32_t *p_len = xfer->frlengths; - uint32_t total = (sndbuf_getblkcnt(ch->pcm_buf) * - sndbuf_getblksz(ch->pcm_buf)) / 2; + uint32_t total; uint32_t blockcount; uint32_t n; uint32_t offset; /* allow dynamic sizing of play buffer */ + total = ch->intr_size; + + /* allow dynamic sizing of play buffer */ blockcount = total / ch->bytes_per_frame; - /* align to 8 units */ - blockcount &= ~7; + /* align units */ + blockcount -= (blockcount % UAUDIO_MINFRAMES); /* range check - min */ if (blockcount == 0) { - blockcount = 8; + blockcount = UAUDIO_MINFRAMES; } /* range check - max */ if (blockcount > xfer->max_frame_count) { @@ -1230,21 +1200,23 @@ uaudio_chan_record_callback(struct usb2_xfer *xfer) uint32_t *p_len = xfer->frlengths; uint32_t n; uint32_t m; - uint32_t total = (sndbuf_getblkcnt(ch->pcm_buf) * - sndbuf_getblksz(ch->pcm_buf)) / 2; + uint32_t total; uint32_t blockcount; uint32_t offset0; uint32_t offset1; /* allow dynamic sizing of play buffer */ + total = ch->intr_size; + + /* allow dynamic sizing of play buffer */ blockcount = total / ch->bytes_per_frame; - /* align to 8 units */ - blockcount &= ~7; + /* align units */ + blockcount -= (blockcount % UAUDIO_MINFRAMES); /* range check - min */ if (blockcount == 0) { - blockcount = 8; + blockcount = UAUDIO_MINFRAMES; } /* range check - max */ if (blockcount > xfer->max_frame_count) { @@ -1326,21 +1298,30 @@ uaudio_chan_init(struct uaudio_softc *sc, struct snd_dbuf *b, { struct uaudio_chan *ch = ((dir == PCMDIR_PLAY) ? &sc->sc_play_chan : &sc->sc_rec_chan); + uint32_t buf_size; uint8_t endpoint; uint8_t iface_index; uint8_t alt_index; usb2_error_t err; - ch->buf = malloc(UAUDIO_DEFAULT_BUFSZ, M_DEVBUF, M_WAITOK | M_ZERO); + /* compute required buffer size */ + buf_size = (ch->bytes_per_frame * UAUDIO_MINFRAMES); + + /* setup interrupt interval */ + ch->intr_size = buf_size; + /* double buffering */ + buf_size *= 2; + + ch->buf = malloc(buf_size, M_DEVBUF, M_WAITOK | M_ZERO); if (ch->buf == NULL) { goto error; } - if (sndbuf_setup(b, ch->buf, UAUDIO_DEFAULT_BUFSZ) != 0) { + if (sndbuf_setup(b, ch->buf, buf_size) != 0) { goto error; } ch->start = ch->buf; - ch->end = ch->buf + UAUDIO_DEFAULT_BUFSZ; + ch->end = ch->buf + buf_size; ch->cur = ch->buf; ch->pcm_ch = c; ch->pcm_mtx = c->lock; @@ -1437,16 +1418,14 @@ int uaudio_chan_set_param_fragments(struct uaudio_chan *ch, uint32_t blocksize, uint32_t blockcount) { - uint32_t max = sndbuf_getmaxsize(ch->pcm_buf); - - RANGE(blocksize, 128, max / 2); - - blockcount = max / blocksize; - RANGE(blockcount, 2, 512); + /* we only support one size */ + blocksize = ch->intr_size; + blockcount = 2; if ((sndbuf_getblksz(ch->pcm_buf) != blocksize) || (sndbuf_getblkcnt(ch->pcm_buf) != blockcount)) { - + DPRINTFN(1, "resizing to %u x " + "%u bytes\n", blockcount, blocksize); if (sndbuf_resize(ch->pcm_buf, blockcount, blocksize)) { DPRINTFN(0, "failed to resize sound buffer, count=%u, " "size=%u\n", blockcount, blocksize); @@ -2672,7 +2651,10 @@ uaudio_mixer_fill_info(struct uaudio_softc *sc, struct usb2_device *udev, DPRINTF("invalid Audio Control header\n"); goto done; } - wTotalLen = UGETW(cd->wTotalLength); + /* "wTotalLen" is allowed to be corrupt */ + wTotalLen = UGETW(acdp->wTotalLength) - acdp->bLength; + + /* get USB audio revision */ sc->sc_audio_rev = UGETW(acdp->bcdADC); DPRINTFN(3, "found AC header, vers=%03x, len=%d\n", diff --git a/sys/dev/usb2/storage/umass2.c b/sys/dev/usb2/storage/umass2.c index cfdfe77..1771982 100644 --- a/sys/dev/usb2/storage/umass2.c +++ b/sys/dev/usb2/storage/umass2.c @@ -400,6 +400,10 @@ static const struct umass_devdescr umass_devdescr[] = { UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_GETMAXLUN }, + {USB_VENDOR_ALCOR, USB_PRODUCT_ALCOR_TRANSCEND, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + NO_GETMAXLUN + }, {USB_VENDOR_ASAHIOPTICAL, USB_PRODUCT_ASAHIOPTICAL_OPTIO230, RID_WILDCARD, UMASS_PROTO_SCSI | UMASS_PROTO_BBB, NO_INQUIRY @@ -636,10 +640,10 @@ static const struct umass_devdescr umass_devdescr[] = { UMASS_PROTO_SCSI, NO_GETMAXLUN }, - { USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_SDS_HOTFIND_D, RID_WILDCARD, - UMASS_PROTO_SCSI | UMASS_PROTO_BBB, - NO_GETMAXLUN | NO_SYNCHRONIZE_CACHE - }, + {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_SDS_HOTFIND_D, RID_WILDCARD, + UMASS_PROTO_SCSI | UMASS_PROTO_BBB, + NO_GETMAXLUN | NO_SYNCHRONIZE_CACHE + }, {USB_VENDOR_ONSPEC, USB_PRODUCT_ONSPEC_CFMS_RW, RID_WILDCARD, UMASS_PROTO_SCSI, NO_QUIRKS diff --git a/sys/dev/usb2/storage/ustorage2_fs.c b/sys/dev/usb2/storage/ustorage2_fs.c index 62981d7..6c606d5 100644 --- a/sys/dev/usb2/storage/ustorage2_fs.c +++ b/sys/dev/usb2/storage/ustorage2_fs.c @@ -1301,6 +1301,7 @@ ustorage_fs_mode_select(struct ustorage_fs_softc *sc) static uint8_t ustorage_fs_synchronize_cache(struct ustorage_fs_softc *sc) { +#if 0 struct ustorage_fs_lun *currlun = sc->sc_transfer.currlun; uint8_t rc; @@ -1311,6 +1312,7 @@ ustorage_fs_synchronize_cache(struct ustorage_fs_softc *sc) if (rc) { currlun->sense_data = SS_WRITE_ERROR; } +#endif return (0); } diff --git a/sys/dev/usb2/wlan/if_ural2.c b/sys/dev/usb2/wlan/if_ural2.c index 2213439..3279e42 100644 --- a/sys/dev/usb2/wlan/if_ural2.c +++ b/sys/dev/usb2/wlan/if_ural2.c @@ -189,7 +189,6 @@ static const struct usb2_device_id ural_devs[] = { {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_2, 0)}, {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2570_3, 0)}, - {USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)}, {USB_VPI(USB_VENDOR_SIEMENS2, USB_PRODUCT_SIEMENS2_WL54G, 0)}, {USB_VPI(USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG, 0)}, {USB_VPI(USB_VENDOR_SPHAIRON, USB_PRODUCT_SPHAIRON_UB801R, 0)}, @@ -787,8 +786,9 @@ ural_cfg_first_time_setup(struct ural_softc *sc, /* retrieve MAC address and various other things from EEPROM */ ural_cfg_read_eeprom(sc); - printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s\n", - sc->sc_name, sc->sc_asic_rev, ural_get_rf(sc->sc_rf_rev)); + printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s (0x%02x)\n", + sc->sc_name, sc->sc_asic_rev, ural_get_rf(sc->sc_rf_rev), + sc->sc_rf_rev); mtx_unlock(&sc->sc_mtx); @@ -958,8 +958,11 @@ ural_config_copy(struct ural_softc *sc, static const char * ural_get_rf(int rev) { + ; /* style fix */ + switch (rev) { - case RAL_RF_2522:return "RT2522"; + case RAL_RF_2522: + return "RT2522"; case RAL_RF_2523: return "RT2523"; case RAL_RF_2524: diff --git a/sys/dev/usb2/wlan/if_zyd2.c b/sys/dev/usb2/wlan/if_zyd2.c index 2e29d37..5615080 100644 --- a/sys/dev/usb2/wlan/if_zyd2.c +++ b/sys/dev/usb2/wlan/if_zyd2.c @@ -155,6 +155,7 @@ static int zyd_newstate_cb(struct ieee80211vap *, static void zyd_cfg_amrr_start(struct zyd_softc *); static void zyd_update_mcast_cb(struct ifnet *); static void zyd_update_promisc_cb(struct ifnet *); +static void zyd_cfg_get_macaddr(struct zyd_softc *sc); static const struct zyd_phy_pair zyd_def_phy[] = ZYD_DEF_PHY; static const struct zyd_phy_pair zyd_def_phyB[] = ZYD_DEF_PHYB; @@ -758,6 +759,17 @@ zyd_cfg_rfwrite(struct zyd_softc *sc, uint32_t value) zyd_cfg_cmd(sc, ZYD_CMD_RFCFG, &req, 4 + (2 * rf->width), NULL, 0, 0); } +/*------------------------------------------------------------------------* + * zyd_cfg_rfwrite_cr + *------------------------------------------------------------------------*/ +static void +zyd_cfg_rfwrite_cr(struct zyd_softc *sc, uint32_t val) +{ + zyd_cfg_write16(sc, ZYD_CR244, (val >> 16) & 0xff); + zyd_cfg_write16(sc, ZYD_CR243, (val >> 8) & 0xff); + zyd_cfg_write16(sc, ZYD_CR242, (val >> 0) & 0xff); +} + static void zyd_bulk_read_clear_stall_callback(struct usb2_xfer *xfer) { @@ -1148,10 +1160,24 @@ zyd_cfg_unlock_phy(struct zyd_softc *sc) static void zyd_cfg_set_beacon_interval(struct zyd_softc *sc, uint32_t bintval) { - /* XXX this is probably broken.. */ - zyd_cfg_write32(sc, ZYD_CR_ATIM_WND_PERIOD, bintval - 2); - zyd_cfg_write32(sc, ZYD_CR_PRE_TBTT, bintval - 1); - zyd_cfg_write32(sc, ZYD_CR_BCN_INTERVAL, bintval); + uint32_t val; + + zyd_cfg_read32(sc, ZYD_CR_ATIM_WND_PERIOD, &val); + sc->sc_atim_wnd = val; + zyd_cfg_read32(sc, ZYD_CR_PRE_TBTT, &val); + sc->sc_pre_tbtt = val; + sc->sc_bcn_int = bintval; + + if (sc->sc_bcn_int <= 5) + sc->sc_bcn_int = 5; + if (sc->sc_pre_tbtt < 4 || sc->sc_pre_tbtt >= sc->sc_bcn_int) + sc->sc_pre_tbtt = sc->sc_bcn_int - 1; + if (sc->sc_atim_wnd >= sc->sc_pre_tbtt) + sc->sc_atim_wnd = sc->sc_pre_tbtt - 1; + + zyd_cfg_write32(sc, ZYD_CR_ATIM_WND_PERIOD, sc->sc_atim_wnd); + zyd_cfg_write32(sc, ZYD_CR_PRE_TBTT, sc->sc_pre_tbtt); + zyd_cfg_write32(sc, ZYD_CR_BCN_INTERVAL, sc->sc_bcn_int); } /* @@ -1163,7 +1189,7 @@ zyd_rf_name(uint8_t type) static const char *const zyd_rfs[] = { "unknown", "unknown", "UW2451", "UCHIP", "AL2230", "AL7230B", "THETA", "AL2210", "MAXIM_NEW", "GCT", - "PV2000", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", + "AL2230S", "RALINK", "INTERSIL", "RFMD", "MAXIM_NEW2", "PHILIPS" }; @@ -1235,36 +1261,106 @@ static void zyd_cfg_rf_al2230_init(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY; - static const uint32_t rfini[] = ZYD_AL2230_RF; + static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; + static const struct zyd_phy_pair phypll[] = { + {ZYD_CR251, 0x2f}, {ZYD_CR251, 0x3f}, + {ZYD_CR138, 0x28}, {ZYD_CR203, 0x06} + }; + static const uint32_t rfini1[] = ZYD_AL2230_RF_PART1; + static const uint32_t rfini2[] = ZYD_AL2230_RF_PART2; + static const uint32_t rfini3[] = ZYD_AL2230_RF_PART3; uint32_t i; /* init RF-dependent PHY registers */ - for (i = 0; i != INDEXES(phyini); i++) { + for (i = 0; i != INDEXES(phyini); i++) zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); - } - /* init AL2230 radio */ - for (i = 0; i != INDEXES(rfini); i++) { - zyd_cfg_rfwrite(sc, rfini[i]); + if ((sc->sc_rf_rev == ZYD_RF_AL2230S) || (sc->sc_al2230s != 0)) { + for (i = 0; i != INDEXES(phy2230s); i++) + zyd_cfg_write16(sc, phy2230s[i].reg, phy2230s[i].val); } + /* init AL2230 radio */ + for (i = 0; i != INDEXES(rfini1); i++) + zyd_cfg_rfwrite(sc, rfini1[i]); + + if ((sc->sc_rf_rev == ZYD_RF_AL2230S) || (sc->sc_al2230s != 0)) + zyd_cfg_rfwrite(sc, 0x000824); + else + zyd_cfg_rfwrite(sc, 0x0005a4); + + for (i = 0; i != INDEXES(rfini2); i++) + zyd_cfg_rfwrite(sc, rfini2[i]); + + for (i = 0; i != INDEXES(phypll); i++) + zyd_cfg_write16(sc, phypll[i].reg, phypll[i].val); + + for (i = 0; i != INDEXES(rfini3); i++) + zyd_cfg_rfwrite(sc, rfini3[i]); +} + +static void +zyd_cfg_rf_al2230_fini(struct zyd_softc *sc, struct zyd_rf *rf) +{ + static const struct zyd_phy_pair phy[] = ZYD_AL2230_PHY_FINI_PART1; + uint32_t i; + + for (i = 0; i != INDEXES(phy); i++) + zyd_cfg_write16(sc, phy[i].reg, phy[i].val); + + if (sc->sc_newphy != 0) + zyd_cfg_write16(sc, ZYD_CR9, 0xe1); + zyd_cfg_write16(sc, ZYD_CR203, 0x6); } static void zyd_cfg_rf_al2230_init_b(struct zyd_softc *sc, struct zyd_rf *rf) { static const struct zyd_phy_pair phyini[] = ZYD_AL2230_PHY_B; - static const uint32_t rfini[] = ZYD_AL2230_RF_B; + static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; + static const struct zyd_phy_pair phy2[] = ZYD_AL2230_PHY_PART2; + static const struct zyd_phy_pair phy3[] = ZYD_AL2230_PHY_PART3; + static const struct zyd_phy_pair phy2230s[] = ZYD_AL2230S_PHY_INIT; + static const uint32_t rfini_part1[] = ZYD_AL2230_RF_B_PART1; + static const uint32_t rfini_part2[] = ZYD_AL2230_RF_B_PART2; + static const uint32_t rfini_part3[] = ZYD_AL2230_RF_B_PART3; + static const uint32_t zyd_al2230_chtable[][3] = ZYD_AL2230_CHANTABLE; uint32_t i; + for (i = 0; i != INDEXES(phy1); i++) + zyd_cfg_write16(sc, phy1[i].reg, phy1[i].val); + /* init RF-dependent PHY registers */ - for (i = 0; i != INDEXES(phyini); i++) { + for (i = 0; i != INDEXES(phyini); i++) zyd_cfg_write16(sc, phyini[i].reg, phyini[i].val); - } - /* init AL2230 radio */ - for (i = 0; i != INDEXES(rfini); i++) { - zyd_cfg_rfwrite(sc, rfini[i]); - } + if ((sc->sc_rf_rev == ZYD_RF_AL2230S) || (sc->sc_al2230s != 0)) + for (i = 0; i != INDEXES(phy2230s); i++) + zyd_cfg_write16(sc, phy2230s[i].reg, phy2230s[i].val); + + for (i = 0; i != 3; i++) + zyd_cfg_rfwrite_cr(sc, zyd_al2230_chtable[0][i]); + + for (i = 0; i != INDEXES(rfini_part1); i++) + zyd_cfg_rfwrite_cr(sc, rfini_part1[i]); + + if ((sc->sc_rf_rev == ZYD_RF_AL2230S) || (sc->sc_al2230s != 0)) + zyd_cfg_rfwrite(sc, 0x241000); + else + zyd_cfg_rfwrite(sc, 0x25a000); + + for (i = 0; i != INDEXES(rfini_part2); i++) + zyd_cfg_rfwrite_cr(sc, rfini_part2[i]); + + for (i = 0; i != INDEXES(phy2); i++) + zyd_cfg_write16(sc, phy2[i].reg, phy2[i].val); + + for (i = 0; i != INDEXES(rfini_part3); i++) + zyd_cfg_rfwrite_cr(sc, rfini_part3[i]); + + for (i = 0; i < INDEXES(phy3); i++) + zyd_cfg_write16(sc, phy3[i].reg, phy3[i].val); + + zyd_cfg_rf_al2230_fini(sc, rf); } /* @@ -1274,16 +1370,60 @@ static void zyd_cfg_rf_al2230_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, uint8_t channel) { + static const struct zyd_phy_pair phy1[] = { + {ZYD_CR138, 0x28}, {ZYD_CR203, 0x06}, + }; static const struct { uint32_t r1, r2, r3; } rfprog[] = ZYD_AL2230_CHANTABLE; + uint32_t i; zyd_cfg_rfwrite(sc, rfprog[channel - 1].r1); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r2); zyd_cfg_rfwrite(sc, rfprog[channel - 1].r3); - zyd_cfg_write16(sc, ZYD_CR138, 0x28); - zyd_cfg_write16(sc, ZYD_CR203, 0x06); + for (i = 0; i != INDEXES(phy1); i++) + zyd_cfg_write16(sc, phy1[i].reg, phy1[i].val); +} + +static void +zyd_cfg_rf_al2230_set_channel_b(struct zyd_softc *sc, + struct zyd_rf *rf, uint8_t chan) +{ + static const struct zyd_phy_pair phy1[] = ZYD_AL2230_PHY_PART1; + static const struct { + uint32_t r1, r2, r3; + } rfprog[] = ZYD_AL2230_CHANTABLE_B; + uint32_t i; + + for (i = 0; i != INDEXES(phy1); i++) + zyd_cfg_write16(sc, phy1[i].reg, phy1[i].val); + + zyd_cfg_rfwrite_cr(sc, rfprog[chan - 1].r1); + zyd_cfg_rfwrite_cr(sc, rfprog[chan - 1].r2); + zyd_cfg_rfwrite_cr(sc, rfprog[chan - 1].r3); + + zyd_cfg_rf_al2230_fini(sc, rf); +} + +#define ZYD_AL2230_PHY_BANDEDGE6 \ +{ \ + { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ + { ZYD_CR47, 0x1e } \ +} + +static void +zyd_cfg_rf_al2230_bandedge6(struct zyd_softc *sc, + struct zyd_rf *rf, uint8_t chan) +{ + struct zyd_phy_pair r[] = ZYD_AL2230_PHY_BANDEDGE6; + uint32_t i; + + if ((chan == 1) || (chan == 11)) + r[0].val = 0x12; + + for (i = 0; i < INDEXES(r); i++) + zyd_cfg_write16(sc, r[i].reg, r[i].val); } /* @@ -1413,7 +1553,6 @@ zyd_cfg_rf_al2210_set_channel(struct zyd_softc *sc, struct zyd_rf *rf, zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp & ~1); zyd_cfg_write32(sc, ZYD_CR_RADIO_PD, tmp | 1); zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x05); - zyd_cfg_write32(sc, ZYD_CR_RFCFG, 0x00); zyd_cfg_write16(sc, ZYD_CR47, 0x1e); @@ -1617,12 +1756,16 @@ zyd_cfg_rf_init_hw(struct zyd_softc *sc, struct zyd_rf *rf) rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL2230: - if (sc->sc_mac_rev == ZYD_ZD1211B) + case ZYD_RF_AL2230S: + if (sc->sc_mac_rev == ZYD_ZD1211B) { rf->cfg_init_hw = zyd_cfg_rf_al2230_init_b; - else + rf->cfg_set_channel = zyd_cfg_rf_al2230_set_channel_b; + } else { rf->cfg_init_hw = zyd_cfg_rf_al2230_init; + rf->cfg_set_channel = zyd_cfg_rf_al2230_set_channel; + } rf->cfg_switch_radio = zyd_cfg_rf_al2230_switch_radio; - rf->cfg_set_channel = zyd_cfg_rf_al2230_set_channel; + rf->cfg_bandedge6 = zyd_cfg_rf_al2230_bandedge6; rf->width = 24; /* 24-bit RF values */ break; case ZYD_RF_AL7230B: @@ -1689,6 +1832,9 @@ zyd_cfg_hw_init(struct zyd_softc *sc) zyd_cfg_write32(sc, ZYD_CR_GPI_EN, 0); zyd_cfg_write32(sc, ZYD_MAC_CONT_WIN_LIMIT, 0x7f043f); + /* set mandatory rates - XXX assumes 802.11b/g */ + zyd_cfg_write32(sc, ZYD_MAC_MAN_RATE, 0x150f); + /* disable interrupts */ zyd_cfg_write32(sc, ZYD_CR_INTERRUPT, 0); @@ -1698,7 +1844,7 @@ zyd_cfg_hw_init(struct zyd_softc *sc) for (; phyp->reg != 0; phyp++) { zyd_cfg_write16(sc, phyp->reg, phyp->val); } - if (sc->sc_fix_cr157) { + if ((sc->sc_mac_rev == ZYD_ZD1211) && sc->sc_fix_cr157) { zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); zyd_cfg_write32(sc, ZYD_CR157, tmp >> 8); } @@ -1707,20 +1853,6 @@ zyd_cfg_hw_init(struct zyd_softc *sc) /* HMAC init */ zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000020); zyd_cfg_write32(sc, ZYD_CR_ADDA_MBIAS_WT, 0x30000808); - - if (sc->sc_mac_rev == ZYD_ZD1211) { - zyd_cfg_write32(sc, ZYD_MAC_RETRY, 0x00000002); - } else { - zyd_cfg_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202); - zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); - zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); - zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); - zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); - zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); - zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); - zyd_cfg_write32(sc, ZYD_MACB_TXOP, 0x01800824); - } - zyd_cfg_write32(sc, ZYD_MAC_SNIFFER, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_RXFILTER, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_GHTBL, 0x00000000); @@ -1732,12 +1864,28 @@ zyd_cfg_hw_init(struct zyd_softc *sc) zyd_cfg_write32(sc, ZYD_MAC_ACK_EXT, 0x00000080); zyd_cfg_write32(sc, ZYD_CR_ADDA_PWR_DWN, 0x00000000); zyd_cfg_write32(sc, ZYD_MAC_SIFS_ACK_TIME, 0x00000100); - zyd_cfg_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0547c032); zyd_cfg_write32(sc, ZYD_CR_RX_PE_DELAY, 0x00000070); zyd_cfg_write32(sc, ZYD_CR_PS_CTRL, 0x10000000); zyd_cfg_write32(sc, ZYD_MAC_RTSCTSRATE, 0x02030203); - zyd_cfg_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); + zyd_cfg_write32(sc, ZYD_MAC_AFTER_PNP, 1); zyd_cfg_write32(sc, ZYD_MAC_BACKOFF_PROTECT, 0x00000114); + zyd_cfg_write32(sc, ZYD_MAC_DIFS_EIFS_SIFS, 0x0a47c032); + zyd_cfg_write32(sc, ZYD_MAC_CAM_MODE, 0x3); + + if (sc->sc_mac_rev == ZYD_ZD1211) { + zyd_cfg_write32(sc, ZYD_MAC_RETRY, 0x00000002); + zyd_cfg_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0640); + } else { + zyd_cfg_write32(sc, ZYD_MACB_MAX_RETRY, 0x02020202); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL4, 0x007f003f); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL3, 0x007f003f); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL2, 0x003f001f); + zyd_cfg_write32(sc, ZYD_MACB_TXPWR_CTL1, 0x001f000f); + zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL1, 0x00280028); + zyd_cfg_write32(sc, ZYD_MACB_AIFS_CTL2, 0x008C003C); + zyd_cfg_write32(sc, ZYD_MACB_TXOP, 0x01800824); + zyd_cfg_write32(sc, ZYD_MAC_RX_THRESHOLD, 0x000c0eff); + } /* init beacon interval to 100ms */ zyd_cfg_set_beacon_interval(sc, 100); @@ -1756,20 +1904,19 @@ zyd_cfg_read_eeprom(struct zyd_softc *sc) uint16_t val; /* read MAC address */ - zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P1, &tmp); - sc->sc_myaddr[0] = tmp & 0xff; - sc->sc_myaddr[1] = tmp >> 8; - sc->sc_myaddr[2] = tmp >> 16; - sc->sc_myaddr[3] = tmp >> 24; - zyd_cfg_read32(sc, ZYD_EEPROM_MAC_ADDR_P2, &tmp); - sc->sc_myaddr[4] = tmp & 0xff; - sc->sc_myaddr[5] = tmp >> 8; + zyd_cfg_get_macaddr(sc); + /* read product data */ zyd_cfg_read32(sc, ZYD_EEPROM_POD, &tmp); sc->sc_rf_rev = tmp & 0x0f; - sc->sc_fix_cr47 = (tmp >> 8) & 0x01; + sc->sc_ledtype = (tmp >> 4) & 0x01; + sc->sc_cckgain = (tmp >> 8) & 0x01; sc->sc_fix_cr157 = (tmp >> 13) & 0x01; sc->sc_pa_rev = (tmp >> 16) & 0x0f; + sc->sc_al2230s = (tmp >> 7) & 0x01; + sc->sc_bandedge6 = (tmp >> 21) & 0x01; + sc->sc_newphy = (tmp >> 31) & 0x01; + sc->sc_txled = ((tmp & (1 << 24)) && (tmp & (1 << 29))) ? 0 : 1; /* read regulatory domain (currently unused) */ zyd_cfg_read32(sc, ZYD_EEPROM_SUBID, &tmp); @@ -1801,6 +1948,21 @@ zyd_cfg_read_eeprom(struct zyd_softc *sc) } static void +zyd_cfg_get_macaddr(struct zyd_softc *sc) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = ZYD_READFWDATAREQ; + USETW(req.wValue, ZYD_EEPROM_MAC_ADDR_P1); + USETW(req.wIndex, 0); + USETW(req.wLength, IEEE80211_ADDR_LEN); + + zyd_cfg_usbrequest(sc, &req, sc->sc_myaddr); + return; +} + +static void zyd_cfg_set_mac_addr(struct zyd_softc *sc, const uint8_t *addr) { uint32_t tmp; @@ -2270,17 +2432,18 @@ zyd_cfg_set_chan(struct zyd_softc *sc, zyd_cfg_write16(sc, ZYD_CR67, sc->sc_ofdm36_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR66, sc->sc_ofdm48_cal[chan - 1]); zyd_cfg_write16(sc, ZYD_CR65, sc->sc_ofdm54_cal[chan - 1]); - zyd_cfg_write16(sc, ZYD_CR68, sc->sc_pwr_cal[chan - 1]); - zyd_cfg_write16(sc, ZYD_CR69, 0x28); zyd_cfg_write16(sc, ZYD_CR69, 0x2a); } - if (sc->sc_fix_cr47) { + if (sc->sc_cckgain) { /* set CCK baseband gain from EEPROM */ zyd_cfg_read32(sc, ZYD_EEPROM_PHY_REG, &tmp); zyd_cfg_write16(sc, ZYD_CR47, tmp & 0xff); } + if (sc->sc_bandedge6 && (sc->sc_rf.cfg_bandedge6 != NULL)) { + (sc->sc_rf.cfg_bandedge6) (sc, &sc->sc_rf, chan); + } zyd_cfg_write32(sc, ZYD_CR_CONFIG_PHILIPS, 0); zyd_cfg_unlock_phy(sc); @@ -2349,7 +2512,7 @@ zyd_cfg_init(struct zyd_softc *sc, else if (cc->ic_curmode == IEEE80211_MODE_11A) zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x1500); else /* assumes 802.11b/g */ - zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0x000f); + zyd_cfg_write32(sc, ZYD_MAC_BAS_RATE, 0xff0f); /* set mandatory rates */ if (cc->ic_curmode == IEEE80211_MODE_11B) diff --git a/sys/dev/usb2/wlan/if_zyd2_reg.h b/sys/dev/usb2/wlan/if_zyd2_reg.h index bdf1bc0..de145f7 100644 --- a/sys/dev/usb2/wlan/if_zyd2_reg.h +++ b/sys/dev/usb2/wlan/if_zyd2_reg.h @@ -101,6 +101,7 @@ #define ZYD_MAC_CONT_WIN_LIMIT 0x96f0 /* Contention window limit */ #define ZYD_MAC_TX_PKT 0x96f4 /* Tx total packet count read */ #define ZYD_MAC_DL_CTRL 0x96f8 /* Download control */ +#define ZYD_MAC_CAM_MODE 0x9700 /* CAM: Continuous Access Mode */ #define ZYD_MACB_TXPWR_CTL1 0x9b00 #define ZYD_MACB_TXPWR_CTL2 0x9b04 #define ZYD_MACB_TXPWR_CTL3 0x9b08 @@ -127,8 +128,8 @@ #define ZYD_EEPROM_PWR_CAL 0xf81f /* Calibration */ #define ZYD_EEPROM_PWR_INT 0xf827 /* Calibration */ #define ZYD_EEPROM_ALLOWEDCHAN 0xf82f /* Allowed CH mask, 1 bit each */ -#define ZYD_EEPROM_PHY_REG 0xf831 /* PHY registers */ #define ZYD_EEPROM_DEVICE_VER 0xf837 /* Device version */ +#define ZYD_EEPROM_PHY_REG 0xf83c /* PHY registers */ #define ZYD_EEPROM_36M_CAL 0xf83f /* Calibration */ #define ZYD_EEPROM_11A_INT 0xf847 /* Interpolation */ #define ZYD_EEPROM_48M_CAL 0xf84f /* Calibration */ @@ -161,7 +162,7 @@ #define ZYD_RF_AL2210 0x7 #define ZYD_RF_MAXIM_NEW 0x8 #define ZYD_RF_GCT 0x9 -#define ZYD_RF_PV2000 0xa /* not supported yet */ +#define ZYD_RF_AL2230S 0xa #define ZYD_RF_RALINK 0xb /* not supported yet */ #define ZYD_RF_INTERSIL 0xc /* not supported yet */ #define ZYD_RF_RFMD 0xd @@ -437,7 +438,7 @@ { ZYD_CR37, 0x00 }, { ZYD_CR38, 0x38 }, { ZYD_CR39, 0x0c }, \ { ZYD_CR40, 0x84 }, { ZYD_CR41, 0x2a }, { ZYD_CR42, 0x80 }, \ { ZYD_CR43, 0x10 }, { ZYD_CR44, 0x12 }, { ZYD_CR46, 0xff }, \ - { ZYD_CR47, 0x08 }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ + { ZYD_CR47, 0x1e }, { ZYD_CR48, 0x26 }, { ZYD_CR49, 0x5b }, \ { ZYD_CR64, 0xd0 }, { ZYD_CR65, 0x04 }, { ZYD_CR66, 0x58 }, \ { ZYD_CR67, 0xc9 }, { ZYD_CR68, 0x88 }, { ZYD_CR69, 0x41 }, \ { ZYD_CR70, 0x23 }, { ZYD_CR71, 0x10 }, { ZYD_CR72, 0xff }, \ @@ -459,7 +460,7 @@ { ZYD_CR5, 0x00 }, { ZYD_CR6, 0x00 }, { ZYD_CR7, 0x00 }, \ { ZYD_CR8, 0x00 }, { ZYD_CR9, 0x20 }, { ZYD_CR12, 0xf0 }, \ { ZYD_CR20, 0x0e }, { ZYD_CR21, 0x0e }, { ZYD_CR27, 0x10 }, \ - { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x30 }, { ZYD_CR83, 0x24 }, \ + { ZYD_CR44, 0x33 }, { ZYD_CR47, 0x1E }, { ZYD_CR83, 0x24 }, \ { ZYD_CR84, 0x04 }, { ZYD_CR85, 0x00 }, { ZYD_CR86, 0x0C }, \ { ZYD_CR87, 0x12 }, { ZYD_CR88, 0x0C }, { ZYD_CR89, 0x00 }, \ { ZYD_CR90, 0x10 }, { ZYD_CR91, 0x08 }, { ZYD_CR93, 0x00 }, \ @@ -470,19 +471,18 @@ { ZYD_CR111, 0x27 }, { ZYD_CR112, 0x27 }, { ZYD_CR113, 0x27 }, \ { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, { ZYD_CR116, 0x24 }, \ { ZYD_CR117, 0xfc }, { ZYD_CR118, 0xfa }, { ZYD_CR120, 0x4f }, \ - { ZYD_CR123, 0x27 }, { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, \ - { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ - { ZYD_CR131, 0x0C }, { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, \ - { ZYD_CR138, 0xa0 }, { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, \ - { ZYD_CR141, 0x82 }, { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, \ - { ZYD_CR144, 0x6c }, { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, \ - { ZYD_CR149, 0x50 }, { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, \ - { ZYD_CR160, 0xfe }, { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, \ - { ZYD_CR163, 0xfa }, { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, \ - { ZYD_CR166, 0xbe }, { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, \ - { ZYD_CR169, 0xba }, { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, \ - { ZYD_CR204, 0x7d }, { ZYD_CR203, 0x30 }, \ - { 0, 0 } \ + { ZYD_CR125, 0xaa }, { ZYD_CR127, 0x03 }, { ZYD_CR128, 0x14 }, \ + { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, { ZYD_CR131, 0x0C }, \ + { ZYD_CR136, 0xdf }, { ZYD_CR137, 0x40 }, { ZYD_CR138, 0xa0 }, \ + { ZYD_CR139, 0xb0 }, { ZYD_CR140, 0x99 }, { ZYD_CR141, 0x82 }, \ + { ZYD_CR142, 0x54 }, { ZYD_CR143, 0x1c }, { ZYD_CR144, 0x6c }, \ + { ZYD_CR147, 0x07 }, { ZYD_CR148, 0x4c }, { ZYD_CR149, 0x50 }, \ + { ZYD_CR150, 0x0e }, { ZYD_CR151, 0x18 }, { ZYD_CR160, 0xfe }, \ + { ZYD_CR161, 0xee }, { ZYD_CR162, 0xaa }, { ZYD_CR163, 0xfa }, \ + { ZYD_CR164, 0xfa }, { ZYD_CR165, 0xea }, { ZYD_CR166, 0xbe }, \ + { ZYD_CR167, 0xbe }, { ZYD_CR168, 0x6a }, { ZYD_CR169, 0xba }, \ + { ZYD_CR170, 0xba }, { ZYD_CR171, 0xba }, { ZYD_CR204, 0x7d }, \ + { ZYD_CR203, 0x30 }, { 0, 0} \ } #define ZYD_DEF_PHYB \ @@ -590,8 +590,6 @@ { 0x181a60, 0x1c0000 } \ } - - #define ZYD_AL2230_PHY \ { \ { ZYD_CR15, 0x20 }, { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, \ @@ -617,34 +615,73 @@ #define ZYD_AL2230_PHY_B \ { \ - { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2b }, \ + { ZYD_CR10, 0x89 }, { ZYD_CR15, 0x20 }, { ZYD_CR17, 0x2B }, \ { ZYD_CR23, 0x40 }, { ZYD_CR24, 0x20 }, { ZYD_CR26, 0x93 }, \ { ZYD_CR28, 0x3e }, { ZYD_CR29, 0x00 }, { ZYD_CR33, 0x28 }, \ { ZYD_CR34, 0x30 }, { ZYD_CR35, 0x3e }, { ZYD_CR41, 0x24 }, \ { ZYD_CR44, 0x32 }, { ZYD_CR46, 0x99 }, { ZYD_CR47, 0x1e }, \ - { ZYD_CR48, 0x00 }, { ZYD_CR49, 0x00 }, { ZYD_CR51, 0x01 }, \ + { ZYD_CR48, 0x06 }, { ZYD_CR49, 0xf9 }, { ZYD_CR51, 0x01 }, \ { ZYD_CR52, 0x80 }, { ZYD_CR53, 0x7e }, { ZYD_CR65, 0x00 }, \ { ZYD_CR66, 0x00 }, { ZYD_CR67, 0x00 }, { ZYD_CR68, 0x00 }, \ { ZYD_CR69, 0x28 }, { ZYD_CR79, 0x58 }, { ZYD_CR80, 0x30 }, \ { ZYD_CR81, 0x30 }, { ZYD_CR87, 0x0a }, { ZYD_CR89, 0x04 }, \ { ZYD_CR91, 0x00 }, { ZYD_CR92, 0x0a }, { ZYD_CR98, 0x8d }, \ - { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR106, 0x24 }, \ - { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, { ZYD_CR110, 0x1f }, \ - { ZYD_CR111, 0x1f }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \ + { ZYD_CR99, 0x00 }, { ZYD_CR101, 0x13 }, { ZYD_CR102, 0x27 }, \ + { ZYD_CR106, 0x24 }, { ZYD_CR107, 0x2a }, { ZYD_CR109, 0x13 }, \ + { ZYD_CR110, 0x1f }, { ZYD_CR111, 0x1f }, { ZYD_CR112, 0x1f }, \ + { ZYD_CR113, 0x27 }, { ZYD_CR114, 0x27 }, { ZYD_CR115, 0x26 }, \ { ZYD_CR116, 0x24 }, { ZYD_CR117, 0xfa }, { ZYD_CR118, 0xfa }, \ { ZYD_CR119, 0x10 }, { ZYD_CR120, 0x4f }, { ZYD_CR121, 0x6c }, \ { ZYD_CR122, 0xfc }, { ZYD_CR123, 0x57 }, { ZYD_CR125, 0xad }, \ { ZYD_CR126, 0x6c }, { ZYD_CR127, 0x03 }, { ZYD_CR137, 0x50 }, \ { ZYD_CR138, 0xa8 }, { ZYD_CR144, 0xac }, { ZYD_CR150, 0x0d }, \ - { ZYD_CR252, 0x00 }, { ZYD_CR253, 0x00 } \ + { ZYD_CR252, 0x34 }, { ZYD_CR253, 0x34 } \ } -#define ZYD_AL2230_RF \ +#define ZYD_AL2230_PHY_PART1 \ +{ \ + { ZYD_CR240, 0x57 }, { ZYD_CR9, 0xe0 } \ +} + +#define ZYD_AL2230_PHY_PART2 \ +{ \ + { ZYD_CR251, 0x2f }, { ZYD_CR251, 0x7f }, \ +} + +#define ZYD_AL2230_PHY_PART3 \ +{ \ + { ZYD_CR128, 0x14 }, { ZYD_CR129, 0x12 }, { ZYD_CR130, 0x10 }, \ +} + +#define ZYD_AL2230S_PHY_INIT \ +{ \ + { ZYD_CR47, 0x1e }, { ZYD_CR106, 0x22 }, { ZYD_CR107, 0x2a }, \ + { ZYD_CR109, 0x13 }, { ZYD_CR118, 0xf8 }, { ZYD_CR119, 0x12 }, \ + { ZYD_CR122, 0xe0 }, { ZYD_CR128, 0x10 }, { ZYD_CR129, 0x0e }, \ + { ZYD_CR130, 0x10 } \ +} + +#define ZYD_AL2230_PHY_FINI_PART1 \ +{ \ + { ZYD_CR80, 0x30 }, { ZYD_CR81, 0x30 }, { ZYD_CR79, 0x58 }, \ + { ZYD_CR12, 0xf0 }, { ZYD_CR77, 0x1b }, { ZYD_CR78, 0x58 }, \ + { ZYD_CR203, 0x06 }, { ZYD_CR240, 0x80 }, \ +} + +#define ZYD_AL2230_RF_PART1 \ +{ \ + 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3 \ +} + +#define ZYD_AL2230_RF_PART2 \ { \ - 0x03f790, 0x033331, 0x00000d, 0x0b3331, 0x03b812, 0x00fff3, \ 0x000da4, 0x0f4dc5, 0x0805b6, 0x011687, 0x000688, 0x0403b9, \ - 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f, 0x00d00f, \ - 0x004c0f, 0x00540f, 0x00700f, 0x00500f \ + 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00500f \ +} + +#define ZYD_AL2230_RF_PART3 \ +{ \ + 0x00d00f, 0x004c0f, 0x00540f, 0x00700f, 0x00500f \ } #define ZYD_AL2230_RF_B \ @@ -654,6 +691,22 @@ 0x00dbba, 0x00099b, 0x0bdffc, 0x00000d, 0x00580f \ } +#define ZYD_AL2230_RF_B_PART1 \ +{ \ + 0x8cccd0, 0x481dc0, 0xcfff00, 0x25a000 \ +} + +#define ZYD_AL2230_RF_B_PART2 \ +{ \ + 0x25a000, 0xa3b2f0, 0x6da010, 0xe36280, 0x116000, 0x9dc020, \ + 0x5ddb00, 0xd99000, 0x3ffbd0, 0xb00000, 0xf01a00 \ +} + +#define ZYD_AL2230_RF_B_PART3 \ +{ \ + 0xf01b00, 0xf01e00, 0xf01a00 \ +} + #define ZYD_AL2230_CHANTABLE \ { \ { 0x03f790, 0x033331, 0x00000d }, \ @@ -672,7 +725,23 @@ { 0x03e7c0, 0x066661, 0x00000d } \ } - +#define ZYD_AL2230_CHANTABLE_B \ +{ \ + { 0x09efc0, 0x8cccc0, 0xb00000 }, \ + { 0x09efc0, 0x8cccd0, 0xb00000 }, \ + { 0x09e7c0, 0x8cccc0, 0xb00000 }, \ + { 0x09e7c0, 0x8cccd0, 0xb00000 }, \ + { 0x05efc0, 0x8cccc0, 0xb00000 }, \ + { 0x05efc0, 0x8cccd0, 0xb00000 }, \ + { 0x05e7c0, 0x8cccc0, 0xb00000 }, \ + { 0x05e7c0, 0x8cccd0, 0xb00000 }, \ + { 0x0defc0, 0x8cccc0, 0xb00000 }, \ + { 0x0defc0, 0x8cccd0, 0xb00000 }, \ + { 0x0de7c0, 0x8cccc0, 0xb00000 }, \ + { 0x0de7c0, 0x8cccd0, 0xb00000 }, \ + { 0x03efc0, 0x8cccc0, 0xb00000 }, \ + { 0x03e7c0, 0x866660, 0xb00000 } \ +} #define ZYD_AL7230B_PHY_1 \ { \ @@ -744,8 +813,6 @@ { 0x03ec00, 0x866660 } \ } - - #define ZYD_AL2210_PHY \ { \ { ZYD_CR9, 0xe0 }, { ZYD_CR10, 0x91 }, { ZYD_CR12, 0x90 }, \ @@ -771,8 +838,6 @@ 0x019a80, 0x019b40 \ } - - #define ZYD_GCT_PHY \ { \ { ZYD_CR47, 0x1e }, { ZYD_CR15, 0xdc }, { ZYD_CR113, 0xc0 }, \ @@ -801,8 +866,6 @@ 0x1a3000, 0x1ab000 \ } - - #define ZYD_MAXIM_PHY \ { \ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ @@ -855,8 +918,6 @@ { 0x199a4, 0x20a53 } \ } - - #define ZYD_MAXIM2_PHY \ { \ { ZYD_CR23, 0x40 }, { ZYD_CR15, 0x20 }, { ZYD_CR28, 0x3e }, \ @@ -919,6 +980,7 @@ */ #define ZYD_DOWNLOADREQ 0x30 #define ZYD_DOWNLOADSTS 0x31 +#define ZYD_READFWDATAREQ 0x32 /* possible values for register ZYD_CR_INTERRUPT */ #define ZYD_HWINT_MASK 0x004f0000 @@ -950,10 +1012,15 @@ /* helpers for register ZYD_MAC_RXFILTER */ #define ZYD_FILTER_MONITOR 0xffffffff -#define ZYD_FILTER_BSS \ - (ZYD_FILTER_ASS_RSP | ZYD_FILTER_REASS_RSP | \ - ZYD_FILTER_PRB_RSP | ZYD_FILTER_BCN | ZYD_FILTER_DEASS | \ - ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH) +#define ZYD_FILTER_BSS \ + (ZYD_FILTER_ASS_REQ | ZYD_FILTER_ASS_RSP | \ + ZYD_FILTER_REASS_REQ | ZYD_FILTER_REASS_RSP | \ + ZYD_FILTER_PRB_REQ | ZYD_FILTER_PRB_RSP | \ + (0x3 << 6) | \ + ZYD_FILTER_BCN | ZYD_FILTER_ATIM | ZYD_FILTER_DEASS | \ + ZYD_FILTER_AUTH | ZYD_FILTER_DEAUTH | \ + (0x7 << 13) | \ + ZYD_FILTER_PS_POLL | ZYD_FILTER_ACK) #define ZYD_FILTER_HOSTAP \ (ZYD_FILTER_ASS_REQ | ZYD_FILTER_REASS_REQ | \ ZYD_FILTER_PRB_REQ | ZYD_FILTER_DEASS | ZYD_FILTER_AUTH | \ @@ -1138,6 +1205,8 @@ struct zyd_rf { void (*cfg_init_hw) (struct zyd_softc *, struct zyd_rf *); void (*cfg_switch_radio) (struct zyd_softc *, uint8_t on); void (*cfg_set_channel) (struct zyd_softc *, struct zyd_rf *, uint8_t); + void (*cfg_bandedge6) (struct zyd_softc *, struct zyd_rf *, uint8_t); + uint8_t width; }; @@ -1239,6 +1308,10 @@ struct zyd_softc { uint32_t sc_rxtap_len; uint32_t sc_txtap_len; uint32_t sc_unit; + uint32_t sc_atim_wnd; + uint32_t sc_pre_tbtt; + uint32_t sc_bcn_int; + int sc_ns_arg; uint16_t sc_firmware_base; @@ -1254,7 +1327,12 @@ struct zyd_softc { uint8_t sc_mac_rev; uint8_t sc_rf_rev; uint8_t sc_pa_rev; - uint8_t sc_fix_cr47; + uint8_t sc_al2230s; + uint8_t sc_cckgain; + uint8_t sc_bandedge6; + uint8_t sc_newphy; + uint8_t sc_ledtype; + uint8_t sc_txled; uint8_t sc_fix_cr157; uint8_t sc_pwr_cal[14]; uint8_t sc_pwr_int[14]; diff --git a/sys/modules/usb2/Makefile b/sys/modules/usb2/Makefile index 8785d65..1cee9ca 100644 --- a/sys/modules/usb2/Makefile +++ b/sys/modules/usb2/Makefile @@ -57,7 +57,7 @@ SUBDIR += misc_fm SUBDIR += quirk SUBDIR += scanner SUBDIR += serial -#SUBDIR += serial_3g +SUBDIR += serial_3g SUBDIR += serial_ark SUBDIR += serial_bsa SUBDIR += serial_bser diff --git a/sys/modules/usb2/serial_3g/Makefile b/sys/modules/usb2/serial_3g/Makefile new file mode 100644 index 0000000..1361083 --- /dev/null +++ b/sys/modules/usb2/serial_3g/Makefile @@ -0,0 +1,38 @@ +# +# $FreeBSD$ +# +# Copyright (c) 2008 Hans Petter Selasky. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +S=${.CURDIR}/../../.. + +.PATH: $S/dev/usb2/serial + +KMOD=usb2_serial_3g +SRCS= +SRCS+= bus_if.h usb2_if.h device_if.h vnode_if.h +SRCS+= opt_usb.h opt_bus.h opt_compat.h +SRCS+= u3g2.c + +.include <bsd.kmod.mk> |