@@ -179,6 +179,7 @@ struct USBEndpoint {
uint8_t ifnum;
int max_packet_size;
bool pipeline;
+ bool halted;
USBDevice *dev;
QTAILQ_HEAD(, USBPacket) queue;
};
@@ -375,6 +376,8 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw);
int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
+void usb_ep_process_queue(USBEndpoint *ep);
+void usb_ep_clear_halt(USBEndpoint *ep);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
@@ -382,12 +382,20 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
usb_packet_check_state(p, USB_PACKET_SETUP);
assert(p->ep != NULL);
+ /* Submitting a new packet clears halt */
+ p->ep->halted = false;
+
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
ret = usb_process_one(p);
if (ret == USB_RET_ASYNC) {
usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else {
+ /*
+ * When pipelining is enabled usb-devices must always return async,
+ * otherwise packets can complete out of order!
+ */
+ assert(!p->ep->pipeline);
p->result = ret;
usb_packet_set_state(p, USB_PACKET_COMPLETE);
}
@@ -402,33 +410,26 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
/* Notify the controller that an async packet is complete. This should only
be called for packets previously deferred by returning USB_RET_ASYNC from
handle_packet. */
-void usb_packet_complete(USBDevice *dev, USBPacket *p)
+static void __usb_packet_complete(USBDevice *dev, USBPacket *p)
{
USBEndpoint *ep = p->ep;
- int ret;
- usb_packet_check_state(p, USB_PACKET_ASYNC);
- assert(QTAILQ_FIRST(&ep->queue) == p);
+ if (p->result < 0) {
+// ep->halted = true;
+ }
usb_packet_set_state(p, USB_PACKET_COMPLETE);
QTAILQ_REMOVE(&ep->queue, p, queue);
dev->port->ops->complete(dev->port, p);
+}
- while (!QTAILQ_EMPTY(&ep->queue)) {
- p = QTAILQ_FIRST(&ep->queue);
- if (p->state == USB_PACKET_ASYNC) {
- break;
- }
- usb_packet_check_state(p, USB_PACKET_QUEUED);
- ret = usb_process_one(p);
- if (ret == USB_RET_ASYNC) {
- usb_packet_set_state(p, USB_PACKET_ASYNC);
- break;
- }
- p->result = ret;
- usb_packet_set_state(p, USB_PACKET_COMPLETE);
- QTAILQ_REMOVE(&ep->queue, p, queue);
- dev->port->ops->complete(dev->port, p);
- }
+void usb_packet_complete(USBDevice *dev, USBPacket *p)
+{
+ USBEndpoint *ep = p->ep;
+
+ usb_packet_check_state(p, USB_PACKET_ASYNC);
+ assert(QTAILQ_FIRST(&ep->queue) == p);
+ __usb_packet_complete(dev, p);
+ usb_ep_process_queue(ep);
}
/* Cancel an active packet. The packed must have been deferred by
@@ -702,3 +703,30 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->pipeline = enabled;
}
+
+void usb_ep_process_queue(USBEndpoint *ep)
+{
+ USBPacket *p;
+ int ret;
+
+ while (!ep->halted && !QTAILQ_EMPTY(&ep->queue)) {
+ p = QTAILQ_FIRST(&ep->queue);
+ if (p->state == USB_PACKET_ASYNC) {
+ break;
+ }
+ usb_packet_check_state(p, USB_PACKET_QUEUED);
+ ret = usb_process_one(p);
+ if (ret == USB_RET_ASYNC) {
+ usb_packet_set_state(p, USB_PACKET_ASYNC);
+ break;
+ }
+ p->result = ret;
+ __usb_packet_complete(ep->dev, p);
+ }
+}
+
+void usb_ep_clear_halt(USBEndpoint *ep)
+{
+ ep->halted = false;
+ usb_ep_process_queue(ep);
+}
@@ -366,6 +366,7 @@ struct EHCIQueue {
uint64_t ts;
int async;
int revalidate;
+ bool halted;
/* cached data from guest - needs to be flushed
* when guest removes an entry (doorbell, handshake sequence)
@@ -740,6 +741,7 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
static void ehci_free_packet(EHCIPacket *p)
{
+ printf("queue %p packet %p free\n", p->queue, p);
trace_usb_ehci_packet_action(p->queue, p, "free");
if (p->async == EHCI_ASYNC_INFLIGHT) {
usb_cancel_packet(&p->packet);
@@ -1773,6 +1775,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
if (q->revalidate && (q->qh.epchar != qh.epchar ||
q->qh.epcap != qh.epcap ||
q->qh.current_qtd != qh.current_qtd)) {
+ printf("queue %p packet %p qtdaddr %08x freeing because of cancel\n", q, NULL, q->qtdaddr);
ehci_free_queue(q);
q = ehci_alloc_queue(ehci, entry, async);
q->seen++;
@@ -1786,6 +1789,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
if (q->dev != NULL && q->dev->addr != devaddr) {
if (!QTAILQ_EMPTY(&q->packets)) {
/* should not happen (guest bug) */
+ printf("guest bug 2!!!\n");
while ((p = QTAILQ_FIRST(&q->packets)) != NULL) {
ehci_free_packet(p);
}
@@ -1796,18 +1800,22 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
q->dev = ehci_find_device(q->ehci, devaddr);
}
- if (p && p->async == EHCI_ASYNC_INFLIGHT) {
+ if (!q->halted && p && p->async == EHCI_ASYNC_INFLIGHT) {
+// if (p->packet.ep->halted && !(q->qh.token & QTD_TOKEN_HALT)) {
+// usb_ep_clear_halt(p->packet.ep);
+// }
/* I/O still in progress -- skip queue */
ehci_set_state(ehci, async, EST_HORIZONTALQH);
goto out;
}
- if (p && p->async == EHCI_ASYNC_FINISHED) {
+ if (!q->halted && p && p->async == EHCI_ASYNC_FINISHED) {
/* I/O finished -- continue processing queue */
trace_usb_ehci_packet_action(p->queue, p, "complete");
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x completing from fetchqh\n", q, p, p->qtdaddr);
ehci_set_state(ehci, async, EST_EXECUTING);
goto out;
}
-
if (async && (q->qh.epchar & QH_EPCHAR_H)) {
/* EHCI spec version 1.0 Section 4.8.3 & 4.10.1 */
@@ -1842,6 +1850,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
ehci_set_state(ehci, async, EST_FETCHQTD);
} else {
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x advqueue fetchqh\n", q, NULL, q->qtdaddr);
/* EHCI spec version 1.0 Section 4.10.2 */
ehci_set_state(ehci, async, EST_ADVANCEQUEUE);
}
@@ -1951,19 +1961,31 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
p = QTAILQ_FIRST(&q->packets);
while (p != NULL && p->qtdaddr != q->qtdaddr) {
/* should not happen (guest bug) */
+ printf("guest bug!!!\n");
ehci_free_packet(p);
p = QTAILQ_FIRST(&q->packets);
}
if (p != NULL) {
ehci_qh_do_overlay(q);
- if (p->async == EHCI_ASYNC_INFLIGHT) {
+ switch (p->async) {
+ case EHCI_ASYNC_NONE:
+ ehci_set_state(q->ehci, q->async, EST_EXECUTING);
+ break;
+ case EHCI_ASYNC_INFLIGHT:
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
- } else {
+ break;
+ case EHCI_ASYNC_FINISHED:
+ trace_usb_ehci_packet_action(q, p, "complete");
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x completing from fetchqtd\n", q, p, p->qtdaddr);
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
+ break;
}
again = 1;
} else if (qtd.token & QTD_TOKEN_ACTIVE) {
p = ehci_alloc_packet(q);
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x ep %d added (fetchqtd)\n", q, p, q->qtdaddr, (q->qh.epchar & QH_EPCHAR_EP_MASK) >> QH_EPCHAR_EP_SH);
p->qtdaddr = q->qtdaddr;
p->qtd = qtd;
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
@@ -2012,6 +2034,8 @@ static void ehci_fill_queue(EHCIPacket *p)
break;
}
p = ehci_alloc_packet(q);
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x added (fill_queue)\n", q, p, qtdaddr);
p->qtdaddr = qtdaddr;
p->qtd = qtd;
p->usb_status = ehci_execute(p, "queue");
@@ -2076,10 +2100,15 @@ static int ehci_state_executing(EHCIQueue *q)
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
int again = 0;
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x completing, async %d\n", q, p, p->qtdaddr, p->async);
+
assert(p != NULL);
assert(p->qtdaddr == q->qtdaddr);
ehci_execute_complete(q);
+ if ((q->qh.epchar & QH_EPCHAR_EP_MASK) == 0)
+ printf("queue %p packet %p qtdaddr %08x completed, status %d\n", q, p, p->qtdaddr, p->usb_status);
if (p->usb_status == USB_RET_ASYNC) {
goto out;
}
@@ -2137,6 +2166,7 @@ static int ehci_state_writeback(EHCIQueue *q)
* bit is clear.
*/
if (q->qh.token & QTD_TOKEN_HALT) {
+ q->halted = true;
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
} else {