diff --git a/devicemodel/hw/pci/xhci.c b/devicemodel/hw/pci/xhci.c index 4e1820111..a4823a32a 100644 --- a/devicemodel/hw/pci/xhci.c +++ b/devicemodel/hw/pci/xhci.c @@ -2650,12 +2650,11 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer, trbflags = trb->dwTrb3; UPRINTF(LDBG, "xfer[%d] done?%u:%d trb %x %016lx %x " - "(err %d) IOC?%d, chained %d\r\n", + "(err %d) IOC?%d, type %d\r\n", i, xfer->data[i].stat, xfer->data[i].blen, - XHCI_TRB_3_TYPE_GET(trbflags), evtrb.qwTrb0, - trbflags, err, - trb->dwTrb3 & XHCI_TRB_3_IOC_BIT ? 1 : 0, - xfer->data[i].chained); + XHCI_TRB_3_TYPE_GET(trbflags), evtrb.qwTrb0, trbflags, + err, trb->dwTrb3 & XHCI_TRB_3_IOC_BIT ? 1 : 0, + xfer->data[i].type); if (xfer->data[i].stat < USB_BLOCK_HANDLED) { xfer->head = (int)i; @@ -2668,14 +2667,14 @@ pci_xhci_xfer_complete(struct pci_xhci_vdev *xdev, struct usb_xfer *xfer, edtla += xfer->data[i].bdone; trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (xfer->data[i].ccs); - if (xfer->data[i].chained == 1) { + if (xfer->data[i].type == USB_DATA_PART) { rem_len += xfer->data[i].blen; i = (i + 1) % USB_MAX_XFER_BLOCKS; - /* When the chained == 1, this 'continue' will delay - * the IOC behavior which could decrease the number - * of virtual interrupts. This could GREATLY improve - * the performance especially under ISOCH scenario. + /* This 'continue' will delay the IOC behavior which + * could decrease the number of virtual interrupts. + * This could GREATLY improve the performance especially + * under ISOCH scenario. */ continue; } else @@ -2818,6 +2817,7 @@ pci_xhci_handle_transfer(struct pci_xhci_vdev *xdev, struct xhci_trb *setup_trb; struct usb_xfer *xfer; struct usb_block *xfer_block; + struct usb_block *prev_block; uint64_t val; uint32_t trbflags; int do_intr, err; @@ -2837,6 +2837,7 @@ retry: do_retry = 0; do_intr = 0; setup_trb = NULL; + prev_block = NULL; while (1) { pci_xhci_dump_trb(trb); @@ -2920,8 +2921,17 @@ retry: trb->dwTrb2 & 0x1FFFF, (void *)addr, ccs); - if (xfer_block && (trb->dwTrb3 & XHCI_TRB_3_CHAIN_BIT)) - xfer_block->chained = 1; + if (!xfer_block) { + err = XHCI_TRB_ERROR_STALL; + goto errout; + } + + if (trb->dwTrb3 & XHCI_TRB_3_CHAIN_BIT) + xfer_block->type = USB_DATA_PART; + else + xfer_block->type = USB_DATA_FULL; + + prev_block = xfer_block; break; case XHCI_TRB_TYPE_STATUS_STAGE: @@ -2948,6 +2958,10 @@ retry: } if ((epid > 1) && (trbflags & XHCI_TRB_3_IOC_BIT)) xfer_block->stat = USB_BLOCK_HANDLED; + + if (prev_block && prev_block->type == USB_DATA_PART) + prev_block->type = USB_DATA_FULL; + break; default: diff --git a/devicemodel/hw/platform/usb_pmapper.c b/devicemodel/hw/platform/usb_pmapper.c index 160d93c26..40e378c53 100644 --- a/devicemodel/hw/platform/usb_pmapper.c +++ b/devicemodel/hw/platform/usb_pmapper.c @@ -274,7 +274,8 @@ usb_dev_comp_cb(struct libusb_transfer *trn) if (d > block->blen) d = block->blen; - if (block->buf) { + if (block->type == USB_DATA_PART || + block->type == USB_DATA_FULL) { if (r->in == TOKEN_IN) { memcpy(block->buf, buf + buf_idx, d); buf_idx += d; @@ -292,7 +293,7 @@ usb_dev_comp_cb(struct libusb_transfer *trn) idx = (idx + 1) % USB_MAX_XFER_BLOCKS; i++; - } while (block->chained == 1); + } while (block->type == USB_DATA_PART); } stall_out: @@ -386,14 +387,15 @@ usb_dev_prepare_xfer(struct usb_xfer *xfer, int *count, int *size) idx = (idx + 1) % USB_MAX_XFER_BLOCKS; continue; } - if (block->buf && block->blen > 0) { + if (block->type == USB_DATA_PART || + block->type == USB_DATA_FULL) { if (!found) { found = 1; first = idx; } c++; s += block->blen; - } else if (!block->buf || !block->blen) { + } else if (block->type == USB_DATA_NONE) { /* there are two cases: * 1. LINK trb is in the middle of trbs. * 2. LINK trb is a single trb. @@ -801,7 +803,7 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx) continue; } - if (xfer->data[idx].chained == 1) { + if (xfer->data[idx].type == USB_DATA_PART) { idx = (idx + 1) % USB_MAX_XFER_BLOCKS; continue; } @@ -838,7 +840,8 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx) if (!dir) { for (i = 0, j = 0, buf_idx = 0; j < blk_count; ++i) { b = &xfer->data[(blk_start + i) % USB_MAX_XFER_BLOCKS]; - if (b->buf) { + if (b->type == USB_DATA_FULL || + b->type == USB_DATA_PART) { memcpy(&r->buffer[buf_idx], b->buf, b->blen); buf_idx += b->blen; j++; @@ -850,13 +853,13 @@ usb_dev_data(void *pdata, struct usb_xfer *xfer, int dir, int epctx) for (i = 0, j = 0, idx = blk_start; i < blk_count; ++i) { int len = xfer->data[idx].blen; - if (len <= 0) { + if (xfer->data[idx].type == USB_DATA_NONE) { idx = (idx + 1) % USB_MAX_XFER_BLOCKS; i--; continue; } - if (xfer->data[idx].chained == 1) { + if (xfer->data[idx].type == USB_DATA_PART) { r->trn->iso_packet_desc[j].length += len; idx = (idx + 1) % USB_MAX_XFER_BLOCKS; continue; diff --git a/devicemodel/hw/usb_core.c b/devicemodel/hw/usb_core.c index e1d1b9ccd..ce1f61c2d 100644 --- a/devicemodel/hw/usb_core.c +++ b/devicemodel/hw/usb_core.c @@ -116,7 +116,7 @@ usb_block_append(struct usb_xfer *xfer, void *buf, int blen, xb->ccs = ccs; xb->stat = USB_BLOCK_FREE; xb->bdone = 0; - xb->chained = 0; + xb->type = USB_DATA_NONE; xfer->ndata++; xfer->tail = (xfer->tail + 1) % USB_MAX_XFER_BLOCKS; return xb; diff --git a/devicemodel/include/usb_core.h b/devicemodel/include/usb_core.h index 24cff1f2f..0c1dd6a56 100644 --- a/devicemodel/include/usb_core.h +++ b/devicemodel/include/usb_core.h @@ -142,6 +142,12 @@ struct usb_hci { int hci_port; }; +enum usb_block_type { + USB_DATA_NONE, + USB_DATA_PART, + USB_DATA_FULL +}; + /* * Each xfer block is mapped to the hci transfer block. * On input into the device handler, blen is set to the lenght of buf. @@ -155,9 +161,9 @@ struct usb_block { enum usb_block_stat stat; /* processed status */ void *hci_data; /* HCI private reference */ int ccs; - int chained; uint32_t streamid; uint64_t trbnext; /* next TRB guest address */ + enum usb_block_type type; }; struct usb_xfer {