diff --git a/hw/usb.h b/hw/usb.h index ead03c9f6b..1fcf79c72f 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -351,6 +351,7 @@ struct USBPacket { USBEndpoint *ep; QEMUIOVector iov; uint64_t parameter; /* control transfers */ + bool short_not_ok; int result; /* transfer length or USB_RET_* status code */ /* Internal use by the USB layer. */ USBPacketState state; @@ -360,7 +361,8 @@ struct USBPacket { void usb_packet_init(USBPacket *p); void usb_packet_set_state(USBPacket *p, USBPacketState state); void usb_packet_check_state(USBPacket *p, USBPacketState expected); -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id); +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, + bool short_not_ok); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len); int usb_packet_map(USBPacket *p, QEMUSGList *sgl); void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl); diff --git a/hw/usb/core.c b/hw/usb/core.c index 5a97a0e412..f4a5ad2333 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -423,7 +423,7 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p) assert(QTAILQ_FIRST(&ep->queue) == p); assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK); - if (p->result < 0) { + if (p->result < 0 || (p->short_not_ok && (p->result < p->iov.size))) { ep->halted = true; } usb_packet_set_state(p, USB_PACKET_COMPLETE); @@ -532,7 +532,8 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state) p->state = state; } -void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id) +void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id, + bool short_not_ok) { assert(!usb_packet_is_inflight(p)); assert(p->iov.iov != NULL); @@ -541,6 +542,7 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id) p->ep = ep; p->result = 0; p->parameter = 0; + p->short_not_ok = short_not_ok; qemu_iovec_reset(&p->iov); usb_packet_set_state(p, USB_PACKET_SETUP); } diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 74a2587eba..8e8ec6b6c5 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1551,6 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action) USBEndpoint *ep; int ret; int endp; + bool spd; assert(p->async == EHCI_ASYNC_NONE || p->async == EHCI_ASYNC_INITIALIZED); @@ -1590,7 +1591,8 @@ static int ehci_execute(EHCIPacket *p, const char *action) return USB_RET_PROCERR; } - usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr); + spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0); + usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr, spd); usb_packet_map(&p->packet, &p->sgl); p->async = EHCI_ASYNC_INITIALIZED; } @@ -1660,7 +1662,7 @@ static int ehci_process_itd(EHCIState *ehci, dev = ehci_find_device(ehci, devaddr); ep = usb_ep_get(dev, pid, endp); if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { - usb_packet_setup(&ehci->ipacket, pid, ep, addr); + usb_packet_setup(&ehci->ipacket, pid, ep, addr, false); usb_packet_map(&ehci->ipacket, &ehci->isgl); ret = usb_handle_packet(dev, &ehci->ipacket); assert(ret != USB_RET_ASYNC); @@ -2085,9 +2087,6 @@ static int ehci_fill_queue(EHCIPacket *p) uint32_t qtdaddr, start_addr = p->qtdaddr; for (;;) { - if (NLPTR_TBIT(qtd.altnext) == 0) { - break; - } if (NLPTR_TBIT(qtd.next) != 0) { break; } diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index 212dd12fc7..c4309a4e6b 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -627,7 +627,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, dev = usb_find_device(&s->port, ep->faddr[idx]); uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); usb_packet_setup(&ep->packey[dir].p, pid, uep, - (dev->addr << 16) | (uep->nr << 8) | pid); + (dev->addr << 16) | (uep->nr << 8) | pid, false); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; ep->packey[dir].dir = dir; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 31dcfbb0c6..00e2e1acac 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -812,7 +812,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, } else { dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, addr); + usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); ret = usb_handle_packet(dev, &ohci->usb_packet); if (ret == USB_RET_ASYNC) { @@ -1012,7 +1012,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); - usb_packet_setup(&ohci->usb_packet, pid, ep, addr); + usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen); ret = usb_handle_packet(dev, &ohci->usb_packet); #ifdef DEBUG_PACKET diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 00dc9d538f..953897b64c 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -808,6 +808,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, UHCIAsync *async; int len = 0, max_len; uint8_t pid; + bool spd; USBDevice *dev; USBEndpoint *ep; @@ -852,13 +853,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; + spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0); dev = uhci_find_device(s, (td->token >> 8) & 0x7f); ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); if (ep_ret) { *ep_ret = ep; } - usb_packet_setup(&async->packet, pid, ep, addr); + usb_packet_setup(&async->packet, pid, ep, addr, spd); qemu_sglist_add(&async->sgl, td->buffer, max_len); usb_packet_map(&async->packet, &async->sgl); @@ -985,8 +987,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td, struct USBEndpoint *ep) UHCI_TD ptd; int ret; - ptd.ctrl = td->ctrl; - while (is_valid(plink) && !(ptd.ctrl & TD_CTRL_SPD)) { + while (is_valid(plink)) { pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd)); le32_to_cpus(&ptd.link); le32_to_cpus(&ptd.ctrl); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 1f437cc15f..ebfc5b892f 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1446,7 +1446,7 @@ static int xhci_setup_packet(XHCITransfer *xfer) ep = usb_ep_get(dev, dir, xfer->epid >> 1); } - usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr); + usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr, false); xhci_xfer_map(xfer); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", xfer->packet.pid, dev->addr, ep->nr); diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c index 44f1a64b35..3a258b4bd4 100644 --- a/hw/usb/host-linux.c +++ b/hw/usb/host-linux.c @@ -1224,7 +1224,8 @@ static int usb_linux_update_endp_table(USBHostDevice *s) usb_ep_set_type(&s->dev, pid, ep, type); usb_ep_set_ifnum(&s->dev, pid, ep, interface); if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) && - (type == USB_ENDPOINT_XFER_BULK)) { + (type == USB_ENDPOINT_XFER_BULK) && + (pid == USB_TOKEN_OUT)) { usb_ep_set_pipeline(&s->dev, pid, ep, true); } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 2283565b0c..22f671b3b1 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1270,6 +1270,16 @@ static void usbredir_interface_info(void *priv, } } +static void usbredir_set_pipeline(USBRedirDevice *dev, struct USBEndpoint *uep) +{ + if (uep->type != USB_ENDPOINT_XFER_BULK) { + return; + } + if (uep->pid == USB_TOKEN_OUT) { + uep->pipeline = true; + } +} + static void usbredir_ep_info(void *priv, struct usb_redir_ep_info_header *ep_info) { @@ -1311,9 +1321,7 @@ static void usbredir_ep_info(void *priv, dev->endpoint[i].max_packet_size = usb_ep->max_packet_size = ep_info->max_packet_size[i]; } - if (ep_info->type[i] == usb_redir_type_bulk) { - usb_ep->pipeline = true; - } + usbredir_set_pipeline(dev, usb_ep); } } @@ -1574,9 +1582,7 @@ static int usbredir_post_load(void *priv, int version_id) usb_ep->type = dev->endpoint[i].type; usb_ep->ifnum = dev->endpoint[i].interface; usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; - if (dev->endpoint[i].type == usb_redir_type_bulk) { - usb_ep->pipeline = true; - } + usbredir_set_pipeline(dev, usb_ep); } return 0; }