usb: Move short-not-ok handling to the core

After a short-not-ok packet ending short, we should not advance the queue.
Move enforcing this to the core, rather then handling it in the hcd code.

This may result in the queue now actually containing multiple input packets
(which would not happen before), and this requires special handling in
combination with pipelining, so disable pipleining for input endpoints
(for now).

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Hans de Goede 2012-10-24 18:14:09 +02:00 committed by Gerd Hoffmann
parent 0cae7b1a00
commit 6ba43f1f6b
9 changed files with 33 additions and 22 deletions

View File

@ -351,6 +351,7 @@ struct USBPacket {
USBEndpoint *ep; USBEndpoint *ep;
QEMUIOVector iov; QEMUIOVector iov;
uint64_t parameter; /* control transfers */ uint64_t parameter; /* control transfers */
bool short_not_ok;
int result; /* transfer length or USB_RET_* status code */ int result; /* transfer length or USB_RET_* status code */
/* Internal use by the USB layer. */ /* Internal use by the USB layer. */
USBPacketState state; USBPacketState state;
@ -360,7 +361,8 @@ struct USBPacket {
void usb_packet_init(USBPacket *p); void usb_packet_init(USBPacket *p);
void usb_packet_set_state(USBPacket *p, USBPacketState state); void usb_packet_set_state(USBPacket *p, USBPacketState state);
void usb_packet_check_state(USBPacket *p, USBPacketState expected); 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); void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len);
int usb_packet_map(USBPacket *p, QEMUSGList *sgl); int usb_packet_map(USBPacket *p, QEMUSGList *sgl);
void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl); void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl);

View File

@ -423,7 +423,7 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
assert(QTAILQ_FIRST(&ep->queue) == p); assert(QTAILQ_FIRST(&ep->queue) == p);
assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK); 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; ep->halted = true;
} }
usb_packet_set_state(p, USB_PACKET_COMPLETE); usb_packet_set_state(p, USB_PACKET_COMPLETE);
@ -532,7 +532,8 @@ void usb_packet_set_state(USBPacket *p, USBPacketState state)
p->state = 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(!usb_packet_is_inflight(p));
assert(p->iov.iov != NULL); 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->ep = ep;
p->result = 0; p->result = 0;
p->parameter = 0; p->parameter = 0;
p->short_not_ok = short_not_ok;
qemu_iovec_reset(&p->iov); qemu_iovec_reset(&p->iov);
usb_packet_set_state(p, USB_PACKET_SETUP); usb_packet_set_state(p, USB_PACKET_SETUP);
} }

View File

@ -1551,6 +1551,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
USBEndpoint *ep; USBEndpoint *ep;
int ret; int ret;
int endp; int endp;
bool spd;
assert(p->async == EHCI_ASYNC_NONE || assert(p->async == EHCI_ASYNC_NONE ||
p->async == EHCI_ASYNC_INITIALIZED); p->async == EHCI_ASYNC_INITIALIZED);
@ -1590,7 +1591,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
return USB_RET_PROCERR; 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); usb_packet_map(&p->packet, &p->sgl);
p->async = EHCI_ASYNC_INITIALIZED; p->async = EHCI_ASYNC_INITIALIZED;
} }
@ -1660,7 +1662,7 @@ static int ehci_process_itd(EHCIState *ehci,
dev = ehci_find_device(ehci, devaddr); dev = ehci_find_device(ehci, devaddr);
ep = usb_ep_get(dev, pid, endp); ep = usb_ep_get(dev, pid, endp);
if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { 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); usb_packet_map(&ehci->ipacket, &ehci->isgl);
ret = usb_handle_packet(dev, &ehci->ipacket); ret = usb_handle_packet(dev, &ehci->ipacket);
assert(ret != USB_RET_ASYNC); assert(ret != USB_RET_ASYNC);
@ -2085,9 +2087,6 @@ static int ehci_fill_queue(EHCIPacket *p)
uint32_t qtdaddr, start_addr = p->qtdaddr; uint32_t qtdaddr, start_addr = p->qtdaddr;
for (;;) { for (;;) {
if (NLPTR_TBIT(qtd.altnext) == 0) {
break;
}
if (NLPTR_TBIT(qtd.next) != 0) { if (NLPTR_TBIT(qtd.next) != 0) {
break; break;
} }

View File

@ -627,7 +627,7 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
dev = usb_find_device(&s->port, ep->faddr[idx]); dev = usb_find_device(&s->port, ep->faddr[idx]);
uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf);
usb_packet_setup(&ep->packey[dir].p, pid, uep, 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); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len);
ep->packey[dir].ep = ep; ep->packey[dir].ep = ep;
ep->packey[dir].dir = dir; ep->packey[dir].dir = dir;

View File

@ -812,7 +812,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
} else { } else {
dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); 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); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
ret = usb_handle_packet(dev, &ohci->usb_packet); ret = usb_handle_packet(dev, &ohci->usb_packet);
if (ret == USB_RET_ASYNC) { 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)); dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); 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); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
ret = usb_handle_packet(dev, &ohci->usb_packet); ret = usb_handle_packet(dev, &ohci->usb_packet);
#ifdef DEBUG_PACKET #ifdef DEBUG_PACKET

View File

@ -808,6 +808,7 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td,
UHCIAsync *async; UHCIAsync *async;
int len = 0, max_len; int len = 0, max_len;
uint8_t pid; uint8_t pid;
bool spd;
USBDevice *dev; USBDevice *dev;
USBEndpoint *ep; 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; max_len = ((td->token >> 21) + 1) & 0x7ff;
pid = td->token & 0xff; pid = td->token & 0xff;
spd = (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) != 0);
dev = uhci_find_device(s, (td->token >> 8) & 0x7f); dev = uhci_find_device(s, (td->token >> 8) & 0x7f);
ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf);
if (ep_ret) { if (ep_ret) {
*ep_ret = ep; *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); qemu_sglist_add(&async->sgl, td->buffer, max_len);
usb_packet_map(&async->packet, &async->sgl); 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; UHCI_TD ptd;
int ret; int ret;
ptd.ctrl = td->ctrl; while (is_valid(plink)) {
while (is_valid(plink) && !(ptd.ctrl & TD_CTRL_SPD)) {
pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd)); pci_dma_read(&s->dev, plink & ~0xf, &ptd, sizeof(ptd));
le32_to_cpus(&ptd.link); le32_to_cpus(&ptd.link);
le32_to_cpus(&ptd.ctrl); le32_to_cpus(&ptd.ctrl);

View File

@ -1446,7 +1446,7 @@ static int xhci_setup_packet(XHCITransfer *xfer)
ep = usb_ep_get(dev, dir, xfer->epid >> 1); 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); xhci_xfer_map(xfer);
DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
xfer->packet.pid, dev->addr, ep->nr); xfer->packet.pid, dev->addr, ep->nr);

View File

@ -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_type(&s->dev, pid, ep, type);
usb_ep_set_ifnum(&s->dev, pid, ep, interface); usb_ep_set_ifnum(&s->dev, pid, ep, interface);
if ((s->options & (1 << USB_HOST_OPT_PIPELINE)) && 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); usb_ep_set_pipeline(&s->dev, pid, ep, true);
} }

View File

@ -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, static void usbredir_ep_info(void *priv,
struct usb_redir_ep_info_header *ep_info) 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 = dev->endpoint[i].max_packet_size =
usb_ep->max_packet_size = ep_info->max_packet_size[i]; usb_ep->max_packet_size = ep_info->max_packet_size[i];
} }
if (ep_info->type[i] == usb_redir_type_bulk) { usbredir_set_pipeline(dev, usb_ep);
usb_ep->pipeline = true;
}
} }
} }
@ -1574,9 +1582,7 @@ static int usbredir_post_load(void *priv, int version_id)
usb_ep->type = dev->endpoint[i].type; usb_ep->type = dev->endpoint[i].type;
usb_ep->ifnum = dev->endpoint[i].interface; usb_ep->ifnum = dev->endpoint[i].interface;
usb_ep->max_packet_size = dev->endpoint[i].max_packet_size; usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
if (dev->endpoint[i].type == usb_redir_type_bulk) { usbredir_set_pipeline(dev, usb_ep);
usb_ep->pipeline = true;
}
} }
return 0; return 0;
} }