diff options
-rw-r--r-- | hw/usb.h | 8 | ||||
-rw-r--r-- | hw/usb/bus.c | 8 | ||||
-rw-r--r-- | hw/usb/hcd-ehci.c | 19 | ||||
-rw-r--r-- | hw/usb/hcd-ohci.c | 30 | ||||
-rw-r--r-- | hw/usb/hcd-uhci.c | 1 | ||||
-rw-r--r-- | hw/usb/hcd-xhci.c | 7 |
6 files changed, 68 insertions, 5 deletions
@@ -307,6 +307,12 @@ typedef struct USBDeviceClass { */ void (*flush_ep_queue)(USBDevice *dev, USBEndpoint *ep); + /* + * Called by the hcd to let the device know the queue for an endpoint + * has been unlinked / stopped. Optional may be NULL. + */ + void (*ep_stopped)(USBDevice *dev, USBEndpoint *ep); + const char *product_desc; const USBDesc *usb_desc; } USBDeviceClass; @@ -539,6 +545,8 @@ void usb_device_set_interface(USBDevice *dev, int interface, void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep); +void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep); + const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 10260a13ac..180d1d739b 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -189,6 +189,14 @@ void usb_device_flush_ep_queue(USBDevice *dev, USBEndpoint *ep) } } +void usb_device_ep_stopped(USBDevice *dev, USBEndpoint *ep) +{ + USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev); + if (klass->ep_stopped) { + klass->ep_stopped(dev, ep); + } +} + static int usb_qdev_init(DeviceState *qdev) { USBDevice *dev = USB_DEVICE(qdev); diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 1713394c65..320b7e7239 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -622,6 +622,17 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) return q; } +static void ehci_queue_stopped(EHCIQueue *q) +{ + int endp = get_field(q->qh.epchar, QH_EPCHAR_EP); + + if (!q->last_pid || !q->dev) { + return; + } + + usb_device_ep_stopped(q->dev, usb_ep_get(q->dev, q->last_pid, endp)); +} + static int ehci_cancel_queue(EHCIQueue *q) { EHCIPacket *p; @@ -629,7 +640,7 @@ static int ehci_cancel_queue(EHCIQueue *q) p = QTAILQ_FIRST(&q->packets); if (p == NULL) { - return 0; + goto leave; } trace_usb_ehci_queue_action(q, "cancel"); @@ -637,6 +648,9 @@ static int ehci_cancel_queue(EHCIQueue *q) ehci_free_packet(p); packets++; } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); + +leave: + ehci_queue_stopped(q); return packets; } @@ -1392,6 +1406,9 @@ static int ehci_execute(EHCIPacket *p, const char *action) return -1; } + if (!ehci_verify_pid(p->queue, &p->qtd)) { + ehci_queue_stopped(p->queue); /* Mark the ep in the prev dir stopped */ + } p->pid = ehci_get_pid(&p->qtd); p->queue->last_pid = p->pid; endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 052c4a3037..29bafa6da9 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -430,6 +430,23 @@ static USBDevice *ohci_find_device(OHCIState *ohci, uint8_t addr) return NULL; } +static void ohci_stop_endpoints(OHCIState *ohci) +{ + USBDevice *dev; + int i, j; + + for (i = 0; i < ohci->num_ports; i++) { + dev = ohci->rhport[i].port.dev; + if (dev && dev->attached) { + usb_device_ep_stopped(dev, &dev->ep_ctl); + for (j = 0; j < USB_MAX_ENDPOINTS; j++) { + usb_device_ep_stopped(dev, &dev->ep_in[j]); + usb_device_ep_stopped(dev, &dev->ep_out[j]); + } + } + } +} + /* Reset the controller */ static void ohci_reset(void *opaque) { @@ -478,6 +495,7 @@ static void ohci_reset(void *opaque) usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } + ohci_stop_endpoints(ohci); DPRINTF("usb-ohci: Reset %s\n", ohci->name); } @@ -1147,6 +1165,8 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) if (ohci->async_td && addr == ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; + usb_device_ep_stopped(ohci->usb_packet.ep->dev, + ohci->usb_packet.ep); } continue; } @@ -1227,10 +1247,12 @@ static void ohci_frame_boundary(void *opaque) } /* Cancel all pending packets if either of the lists has been disabled. */ - if (ohci->async_td && - ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { - usb_cancel_packet(&ohci->usb_packet); - ohci->async_td = 0; + if (ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) { + if (ohci->async_td) { + usb_cancel_packet(&ohci->usb_packet); + ohci->async_td = 0; + } + ohci_stop_endpoints(ohci); } ohci->old_ctl = ohci->ctl; ohci_process_lists(ohci, 0); diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index bd3377e36a..0cd68cf355 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -226,6 +226,7 @@ static void uhci_queue_free(UHCIQueue *queue, const char *reason) async = QTAILQ_FIRST(&queue->asyncs); uhci_async_cancel(async); } + usb_device_ep_stopped(queue->ep->dev, queue->ep); trace_usb_uhci_queue_del(queue->token, reason); QTAILQ_REMOVE(&s->queues, queue, next); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index e2de71ef1a..40542b8d05 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1177,6 +1177,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, XHCISlot *slot; XHCIEPContext *epctx; int i, xferi, killed = 0; + USBEndpoint *ep = NULL; assert(slotid >= 1 && slotid <= xhci->numslots); assert(epid >= 1 && epid <= 31); @@ -1192,9 +1193,15 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, xferi = epctx->next_xfer; for (i = 0; i < TD_QUEUE; i++) { + if (epctx->transfers[xferi].packet.ep) { + ep = epctx->transfers[xferi].packet.ep; + } killed += xhci_ep_nuke_one_xfer(&epctx->transfers[xferi]); xferi = (xferi + 1) % TD_QUEUE; } + if (ep) { + usb_device_ep_stopped(ep->dev, ep); + } return killed; } |