Print this page
MFV: illumos-gate@2aba3acda67326648fd60aaf2bfb4e18ee8c04ed
9816 Multi-TRB xhci transfers should use event data
9817 xhci needs to always set slot context
8550 increase xhci bulk transfer sgl count
9818 xhci_transfer_get_tdsize can return values that are too large
Reviewed by: Alex Wilson <alex.wilson@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Approved by: Joshua M. Clulow <josh@sysmgr.org>
Author: Robert Mustacchi <rm@joyent.com>

*** 8,18 **** * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2016 Joyent, Inc. */ /* * xHCI Endpoint Initialization and Management * --- 8,18 ---- * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright (c) 2018, Joyent, Inc. */ /* * xHCI Endpoint Initialization and Management *
*** 873,885 **** if (xhci_ring_trb_space(rp, xt->xt_ntrbs) == B_FALSE) return (USB_NO_RESOURCES); for (i = xt->xt_ntrbs - 1; i > 0; i--) { ! xhci_ring_trb_fill(rp, i, &xt->xt_trbs[i], B_TRUE); } ! xhci_ring_trb_fill(rp, 0U, &xt->xt_trbs[0], B_FALSE); XHCI_DMA_SYNC(rp->xr_dma, DDI_DMA_SYNC_FORDEV); xhci_ring_trb_produce(rp, xt->xt_ntrbs); list_insert_tail(&xep->xep_transfers, xt); --- 873,887 ---- if (xhci_ring_trb_space(rp, xt->xt_ntrbs) == B_FALSE) return (USB_NO_RESOURCES); for (i = xt->xt_ntrbs - 1; i > 0; i--) { ! xhci_ring_trb_fill(rp, i, &xt->xt_trbs[i], &xt->xt_trbs_pa[i], ! B_TRUE); } ! xhci_ring_trb_fill(rp, 0U, &xt->xt_trbs[0], &xt->xt_trbs_pa[0], ! B_FALSE); XHCI_DMA_SYNC(rp->xr_dma, DDI_DMA_SYNC_FORDEV); xhci_ring_trb_produce(rp, xt->xt_ntrbs); list_insert_tail(&xep->xep_transfers, xt);
*** 907,918 **** return (xhci_endpoint_ring(xhcip, xd, xep)); } static xhci_transfer_t * xhci_endpoint_determine_transfer(xhci_t *xhcip, xhci_endpoint_t *xep, ! xhci_trb_t *trb, int *offp) { xhci_transfer_t *xt; ASSERT(xhcip != NULL); ASSERT(offp != NULL); ASSERT(xep != NULL); --- 909,922 ---- return (xhci_endpoint_ring(xhcip, xd, xep)); } static xhci_transfer_t * xhci_endpoint_determine_transfer(xhci_t *xhcip, xhci_endpoint_t *xep, ! xhci_trb_t *trb, uint_t *offp) { + uint_t i; + uint64_t addr; xhci_transfer_t *xt; ASSERT(xhcip != NULL); ASSERT(offp != NULL); ASSERT(xep != NULL);
*** 920,934 **** ASSERT(MUTEX_HELD(&xhcip->xhci_lock)); if ((xt = list_head(&xep->xep_transfers)) == NULL) return (NULL); ! *offp = xhci_ring_trb_valid_range(&xep->xep_ring, LE_64(trb->trb_addr), ! xt->xt_ntrbs); ! if (*offp == -1) return (NULL); return (xt); } static void xhci_endpoint_reschedule_periodic(xhci_t *xhcip, xhci_device_t *xd, xhci_endpoint_t *xep, xhci_transfer_t *xt) --- 924,968 ---- ASSERT(MUTEX_HELD(&xhcip->xhci_lock)); if ((xt = list_head(&xep->xep_transfers)) == NULL) return (NULL); ! addr = LE_64(trb->trb_addr); ! ! /* ! * Check if this is the simple case of an event data. If it is, then all ! * we need to do is look and see its data matches the address of the ! * transfer. ! */ ! if (XHCI_TRB_GET_ED(LE_32(trb->trb_flags)) != 0) { ! if (LE_64(trb->trb_addr) != (uintptr_t)xt) return (NULL); + + *offp = xt->xt_ntrbs - 1; return (xt); + } + + /* + * This represents an error that has occurred. We need to check two + * different things. The first is that the TRB PA maps to one of the + * TRBs in the transfer. Secondly, we need to make sure that it makes + * sense in the context of the ring and our notion of where the tail is. + */ + for (i = 0; i < xt->xt_ntrbs; i++) { + if (xt->xt_trbs_pa[i] == addr) + break; + } + + if (i == xt->xt_ntrbs) + return (NULL); + + if (xhci_ring_trb_valid_range(&xep->xep_ring, LE_64(trb->trb_addr), + xt->xt_ntrbs) == -1) + return (NULL); + + *offp = i; + return (xt); } static void xhci_endpoint_reschedule_periodic(xhci_t *xhcip, xhci_device_t *xd, xhci_endpoint_t *xep, xhci_transfer_t *xt)
*** 993,1003 **** * nothing to do. We'll update everything and call back the framework once we * get the status stage. */ static boolean_t xhci_endpoint_control_callback(xhci_t *xhcip, xhci_device_t *xd, ! xhci_endpoint_t *xep, xhci_transfer_t *xt, int off, xhci_trb_t *trb) { int code; usb_ctrl_req_t *ucrp; xhci_transfer_t *rem; --- 1027,1037 ---- * nothing to do. We'll update everything and call back the framework once we * get the status stage. */ static boolean_t xhci_endpoint_control_callback(xhci_t *xhcip, xhci_device_t *xd, ! xhci_endpoint_t *xep, xhci_transfer_t *xt, uint_t off, xhci_trb_t *trb) { int code; usb_ctrl_req_t *ucrp; xhci_transfer_t *rem;
*** 1007,1020 **** ucrp = (usb_ctrl_req_t *)xt->xt_usba_req; /* * Now that we know what this TRB is for, was it for a data/normal stage * or is it the status stage. We cheat by looking at the last entry. If ! * it's a data stage, then we must have gotten a short write. In that ! * case, we should go through and check to make sure it's allowed. If ! * not, we need to fail the transfer, try to stop the ring, and make ! * callbacks. We'll clean up the xhci transfer at this time. */ if (off != xt->xt_ntrbs - 1) { uint_t remain; usb_ctrl_req_t *ucrp = (usb_ctrl_req_t *)xt->xt_usba_req; --- 1041,1053 ---- ucrp = (usb_ctrl_req_t *)xt->xt_usba_req; /* * Now that we know what this TRB is for, was it for a data/normal stage * or is it the status stage. We cheat by looking at the last entry. If ! * it's a data stage, then we must have gotten a short write. We record ! * this fact and whether we should consider the transfer fatal for the ! * subsequent status stage. */ if (off != xt->xt_ntrbs - 1) { uint_t remain; usb_ctrl_req_t *ucrp = (usb_ctrl_req_t *)xt->xt_usba_req;
*** 1148,1158 **** * Handle things which consist solely of normal tranfers, in other words, bulk * and interrupt transfers. */ static boolean_t xhci_endpoint_norm_callback(xhci_t *xhcip, xhci_device_t *xd, ! xhci_endpoint_t *xep, xhci_transfer_t *xt, int off, xhci_trb_t *trb) { int code; usb_cr_t cr; xhci_transfer_t *rem; int attrs; --- 1181,1191 ---- * Handle things which consist solely of normal tranfers, in other words, bulk * and interrupt transfers. */ static boolean_t xhci_endpoint_norm_callback(xhci_t *xhcip, xhci_device_t *xd, ! xhci_endpoint_t *xep, xhci_transfer_t *xt, uint_t off, xhci_trb_t *trb) { int code; usb_cr_t cr; xhci_transfer_t *rem; int attrs;
*** 1165,1178 **** xep->xep_type == USB_EP_ATTR_INTR); code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status)); if (code == XHCI_CODE_SHORT_XFER) { ! int residue; residue = XHCI_TRB_REMAIN(LE_32(trb->trb_status)); xt->xt_short = xt->xt_buffer.xdb_len - residue; } /* * If we have an interrupt from something that's not the last entry, * that must mean we had a short transfer, so there's nothing more for * us to do at the moment. We won't call back until everything's --- 1198,1217 ---- xep->xep_type == USB_EP_ATTR_INTR); code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status)); if (code == XHCI_CODE_SHORT_XFER) { ! uint_t residue; residue = XHCI_TRB_REMAIN(LE_32(trb->trb_status)); + + if (xep->xep_type == USB_EP_ATTR_BULK) { + VERIFY3U(XHCI_TRB_GET_ED(LE_32(trb->trb_flags)), !=, 0); + xt->xt_short = residue; + } else { xt->xt_short = xt->xt_buffer.xdb_len - residue; } + } /* * If we have an interrupt from something that's not the last entry, * that must mean we had a short transfer, so there's nothing more for * us to do at the moment. We won't call back until everything's
*** 1236,1246 **** mp->b_wptr += len; } cr = USB_CR_OK; out: ! VERIFY(xhci_ring_trb_consumed(&xep->xep_ring, LE_64(trb->trb_addr))); rem = list_remove_head(&xep->xep_transfers); VERIFY3P(rem, ==, xt); mutex_exit(&xhcip->xhci_lock); usba_hcdi_cb(xep->xep_pipe, urp, cr); --- 1275,1289 ---- mp->b_wptr += len; } cr = USB_CR_OK; out: ! /* ! * Don't use the address from the TRB here. When we're dealing with ! * event data that will be entirely wrong. ! */ ! VERIFY(xhci_ring_trb_consumed(&xep->xep_ring, xt->xt_trbs_pa[off])); rem = list_remove_head(&xep->xep_transfers); VERIFY3P(rem, ==, xt); mutex_exit(&xhcip->xhci_lock); usba_hcdi_cb(xep->xep_pipe, urp, cr);
*** 1253,1263 **** return (B_TRUE); } static boolean_t xhci_endpoint_isoch_callback(xhci_t *xhcip, xhci_device_t *xd, ! xhci_endpoint_t *xep, xhci_transfer_t *xt, int off, xhci_trb_t *trb) { int code; usb_cr_t cr; xhci_transfer_t *rem; usb_isoc_pkt_descr_t *desc; --- 1296,1306 ---- return (B_TRUE); } static boolean_t xhci_endpoint_isoch_callback(xhci_t *xhcip, xhci_device_t *xd, ! xhci_endpoint_t *xep, xhci_transfer_t *xt, uint_t off, xhci_trb_t *trb) { int code; usb_cr_t cr; xhci_transfer_t *rem; usb_isoc_pkt_descr_t *desc;
*** 1343,1362 **** boolean_t xhci_endpoint_transfer_callback(xhci_t *xhcip, xhci_trb_t *trb) { boolean_t ret; ! int slot, endpoint, code, off; xhci_device_t *xd; xhci_endpoint_t *xep; xhci_transfer_t *xt; boolean_t transfer_done; endpoint = XHCI_TRB_GET_EP(LE_32(trb->trb_flags)); slot = XHCI_TRB_GET_SLOT(LE_32(trb->trb_flags)); code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status)); mutex_enter(&xhcip->xhci_lock); xd = xhci_device_lookup_by_slot(xhcip, slot); if (xd == NULL) { xhci_error(xhcip, "received transfer trb with code %d for " "unknown slot %d and endpoint %d: resetting device", code, --- 1386,1440 ---- boolean_t xhci_endpoint_transfer_callback(xhci_t *xhcip, xhci_trb_t *trb) { boolean_t ret; ! int slot, endpoint, code; ! uint_t off; xhci_device_t *xd; xhci_endpoint_t *xep; xhci_transfer_t *xt; boolean_t transfer_done; endpoint = XHCI_TRB_GET_EP(LE_32(trb->trb_flags)); slot = XHCI_TRB_GET_SLOT(LE_32(trb->trb_flags)); code = XHCI_TRB_GET_CODE(LE_32(trb->trb_status)); + switch (code) { + case XHCI_CODE_RING_UNDERRUN: + case XHCI_CODE_RING_OVERRUN: + /* + * If we have an ISOC overrun or underrun then there will be no + * valid data pointer in the TRB associated with it. Just drive + * on. + */ + return (B_TRUE); + case XHCI_CODE_UNDEFINED: + xhci_error(xhcip, "received transfer trb with undefined fatal " + "error: resetting device"); + xhci_fm_runtime_reset(xhcip); + return (B_FALSE); + case XHCI_CODE_XFER_STOPPED: + case XHCI_CODE_XFER_STOPINV: + case XHCI_CODE_XFER_STOPSHORT: + /* + * This causes us to transition the endpoint to a stopped state. + * Each of these indicate a different possible state that we + * have to deal with. Effectively we're going to drop it and + * leave it up to the consumers to figure out what to do. For + * the moment, that's generally okay because stops are only used + * in cases where we're cleaning up outstanding reqs, etc. + * + * We do this before we check for the corresponding transfer as + * this will generally be generated by a command issued that's + * stopping the ring. + */ + return (B_TRUE); + default: + break; + } + mutex_enter(&xhcip->xhci_lock); xd = xhci_device_lookup_by_slot(xhcip, slot); if (xd == NULL) { xhci_error(xhcip, "received transfer trb with code %d for " "unknown slot %d and endpoint %d: resetting device", code,
*** 1379,1418 **** xhci_fm_runtime_reset(xhcip); return (B_FALSE); } /* ! * This TRB should be part of a transfer. If it's not, then we ignore ! * it. We also check whether or not it's for the first transfer. Because ! * the rings are serviced in order, it should be. */ if ((xt = xhci_endpoint_determine_transfer(xhcip, xep, trb, &off)) == NULL) { mutex_exit(&xhcip->xhci_lock); ! return (B_TRUE); } transfer_done = B_FALSE; switch (code) { case XHCI_CODE_SUCCESS: case XHCI_CODE_SHORT_XFER: /* Handled by endpoint logic */ break; - case XHCI_CODE_XFER_STOPPED: - case XHCI_CODE_XFER_STOPINV: - case XHCI_CODE_XFER_STOPSHORT: - /* - * This causes us to transition the endpoint to a stopped state. - * Each of these indicate a different possible state that we - * have to deal with. Effectively we're going to drop it and - * leave it up to the consumers to figure out what to do. For - * the moment, that's generally okay because stops are only used - * in cases where we're cleaning up outstanding reqs, etc. - */ - mutex_exit(&xhcip->xhci_lock); - return (B_TRUE); case XHCI_CODE_STALL: /* * This causes us to transition to the halted state; * however, downstream clients are able to handle this just * fine. --- 1457,1496 ---- xhci_fm_runtime_reset(xhcip); return (B_FALSE); } /* ! * The TRB that we recieved may be an event data TRB for a bulk ! * endpoint, a normal or short completion for any other endpoint or an ! * error. In all cases, we need to figure out what transfer this ! * corresponds to. If this is an error, then we need to make sure that ! * the generating ring has been cleaned up. ! * ! * TRBs should be delivered in order, based on the ring. If for some ! * reason we find something that doesn't add up here, then we need to ! * assume that something has gone horribly wrong in the system and issue ! * a runtime reset. We issue the runtime reset rather than just trying ! * to stop and flush the ring, because it's unclear if we could stop ! * the ring in time. */ if ((xt = xhci_endpoint_determine_transfer(xhcip, xep, trb, &off)) == NULL) { + xhci_error(xhcip, "received transfer trb with code %d, slot " + "%d, and endpoint %d, but does not match current transfer " + "for endpoint: resetting device", code, slot, endpoint); mutex_exit(&xhcip->xhci_lock); ! xhci_fm_runtime_reset(xhcip); ! return (B_FALSE); } transfer_done = B_FALSE; switch (code) { case XHCI_CODE_SUCCESS: case XHCI_CODE_SHORT_XFER: /* Handled by endpoint logic */ break; case XHCI_CODE_STALL: /* * This causes us to transition to the halted state; * however, downstream clients are able to handle this just * fine.
*** 1430,1448 **** --- 1508,1538 ---- case XHCI_CODE_SPLITERR: transfer_done = B_TRUE; xt->xt_cr = USB_CR_DEV_NOT_RESP; xep->xep_state |= XHCI_ENDPOINT_HALTED; break; + case XHCI_CODE_BW_OVERRUN: + transfer_done = B_TRUE; + xt->xt_cr = USB_CR_DATA_OVERRUN; + break; + case XHCI_CODE_DATA_BUF: + transfer_done = B_TRUE; + if (xt->xt_data_tohost) + xt->xt_cr = USB_CR_DATA_OVERRUN; + else + xt->xt_cr = USB_CR_DATA_UNDERRUN; + break; default: /* * Treat these as general unspecified errors that don't cause a * stop of the ring. Even if it does, a subsequent timeout * should occur which causes us to end up dropping a pipe reset * or at least issuing a reset of the device as part of * quiescing. */ transfer_done = B_TRUE; + xt->xt_cr = USB_CR_HC_HARDWARE_ERR; break; } if (transfer_done == B_TRUE) { xhci_transfer_t *alt;