diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-11-14 08:50:18 -0600 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-11-14 08:50:18 -0600 |
commit | ce5e5b522e8d3b848b6445c34226b69c77c46403 (patch) | |
tree | 9cd13246a20784e657c3d6d686e0282d1da5ffba /hw/usb/combined-packet.c | |
parent | bf0dfb69f860a7894958068fba45f5268dddd6be (diff) | |
parent | 9d1530470bba5d8bec4796c0b43b874d5f9ef017 (diff) | |
download | hqemu-ce5e5b522e8d3b848b6445c34226b69c77c46403.zip hqemu-ce5e5b522e8d3b848b6445c34226b69c77c46403.tar.gz |
Merge remote-tracking branch 'kraxel/usb.70' into staging
* kraxel/usb.70:
ehci: fix migration
xhci: Fix some DMA host endian bugs
usb/combined-packet: Move freeing of combined to usb_combined_packet_remove()
xhci: Add support for packets with both data and an error status
ehci: Add support for packets with both data and an error status
ehci: Get rid of the magical PROC_ERR status
usb-redir: Allow packets to have both data and an error-status
usb: split packet result into actual_length + status
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Diffstat (limited to 'hw/usb/combined-packet.c')
-rw-r--r-- | hw/usb/combined-packet.c | 58 |
1 files changed, 31 insertions, 27 deletions
diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index 3904e71..4a0c299 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -31,12 +31,16 @@ static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p) p->combined = combined; } +/* Note will free combined when the last packet gets removed */ static void usb_combined_packet_remove(USBCombinedPacket *combined, USBPacket *p) { assert(p->combined == combined); p->combined = NULL; QTAILQ_REMOVE(&combined->packets, p, combined_entry); + if (QTAILQ_EMPTY(&combined->packets)) { + g_free(combined); + } } /* Also handles completion of non combined packets for pipelined input eps */ @@ -45,9 +49,8 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) USBCombinedPacket *combined = p->combined; USBEndpoint *ep = p->ep; USBPacket *next; - enum { completing, complete, leftover }; - int result, state = completing; - bool short_not_ok; + int status, actual_length; + bool short_not_ok, done = false; if (combined == NULL) { usb_packet_complete_one(dev, p); @@ -56,37 +59,39 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets)); - result = combined->first->result; + status = combined->first->status; + actual_length = combined->first->actual_length; short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { - if (state == completing) { + if (!done) { /* Distribute data over uncombined packets */ - if (result >= p->iov.size) { - p->result = p->iov.size; + if (actual_length >= p->iov.size) { + p->actual_length = p->iov.size; } else { /* Send short or error packet to complete the transfer */ - p->result = result; - state = complete; + p->actual_length = actual_length; + done = true; + } + /* Report status on the last packet */ + if (done || next == NULL) { + p->status = status; + } else { + p->status = USB_RET_SUCCESS; } p->short_not_ok = short_not_ok; + /* Note will free combined when the last packet gets removed! */ usb_combined_packet_remove(combined, p); usb_packet_complete_one(dev, p); - result -= p->result; + actual_length -= p->actual_length; } else { /* Remove any leftover packets from the queue */ - state = leftover; - p->result = USB_RET_REMOVE_FROM_QUEUE; + p->status = USB_RET_REMOVE_FROM_QUEUE; + /* Note will free combined on the last packet! */ dev->port->ops->complete(dev->port, p); } } - /* - * If we had leftover packets the hcd driver will have cancelled them - * and usb_combined_packet_cancel has already freed combined! - */ - if (state != leftover) { - g_free(combined); - } + /* Do not use combined here, it has been freed! */ leave: /* Check if there are packets in the queue waiting for our completion */ usb_ep_combine_input_packets(ep); @@ -97,14 +102,13 @@ void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p) { USBCombinedPacket *combined = p->combined; assert(combined != NULL); + USBPacket *first = p->combined->first; + /* Note will free combined on the last packet! */ usb_combined_packet_remove(combined, p); - if (p == combined->first) { + if (p == first) { usb_device_cancel_packet(dev, p); } - if (QTAILQ_EMPTY(&combined->packets)) { - g_free(combined); - } } /* @@ -117,7 +121,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) { USBPacket *p, *u, *next, *prev = NULL, *first = NULL; USBPort *port = ep->dev->port; - int ret, totalsize; + int totalsize; assert(ep->pipeline); assert(ep->pid == USB_TOKEN_IN); @@ -125,7 +129,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) { /* Empty the queue on a halt */ if (ep->halted) { - p->result = USB_RET_REMOVE_FROM_QUEUE; + p->status = USB_RET_REMOVE_FROM_QUEUE; port->ops->complete(port, p); continue; } @@ -166,8 +170,8 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) next == NULL || /* Work around for Linux usbfs bulk splitting + migration */ (totalsize == 16348 && p->int_req)) { - ret = usb_device_handle_data(ep->dev, first); - assert(ret == USB_RET_ASYNC); + usb_device_handle_data(ep->dev, first); + assert(first->status == USB_RET_ASYNC); if (first->combined) { QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) { usb_packet_set_state(u, USB_PACKET_ASYNC); |