ehci: handle TD deactivation of inflight packets

Check the TDs of inflight packets, cancel
packets in case the guest clears the active bit.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Gerd Hoffmann 2012-08-21 14:03:09 +02:00
parent c7cdca3b85
commit 287fd3f1dd
1 changed files with 23 additions and 15 deletions

View File

@ -1816,11 +1816,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
q->dev = ehci_find_device(q->ehci, devaddr); q->dev = ehci_find_device(q->ehci, devaddr);
} }
if (p && p->async == EHCI_ASYNC_INFLIGHT) {
/* I/O still in progress -- skip queue */
ehci_set_state(ehci, async, EST_HORIZONTALQH);
goto out;
}
if (p && p->async == EHCI_ASYNC_FINISHED) { if (p && p->async == EHCI_ASYNC_FINISHED) {
/* I/O finished -- continue processing queue */ /* I/O finished -- continue processing queue */
trace_usb_ehci_packet_action(p->queue, p, "complete"); trace_usb_ehci_packet_action(p->queue, p, "complete");
@ -1969,28 +1964,41 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
p = QTAILQ_FIRST(&q->packets); p = QTAILQ_FIRST(&q->packets);
if (p != NULL && p->qtdaddr != q->qtdaddr) {
/* should not happen (guest bug) */
ehci_cancel_queue(q);
p = NULL;
}
if (p != NULL) { if (p != NULL) {
ehci_qh_do_overlay(q); if (p->qtdaddr != q->qtdaddr ||
(!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
(!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
p->qtd.bufptr[0] != qtd.bufptr[0]) {
/* guest bug: guest updated active QH or qTD underneath us */
ehci_cancel_queue(q);
p = NULL;
} else {
p->qtd = qtd;
ehci_qh_do_overlay(q);
}
}
if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
if (p != NULL) {
/* transfer canceled by guest (clear active) */
ehci_cancel_queue(q);
p = NULL;
}
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
} else if (p != NULL) {
if (p->async == EHCI_ASYNC_INFLIGHT) { if (p->async == EHCI_ASYNC_INFLIGHT) {
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
} else { } else {
ehci_set_state(q->ehci, q->async, EST_EXECUTING); ehci_set_state(q->ehci, q->async, EST_EXECUTING);
} }
again = 1; again = 1;
} else if (qtd.token & QTD_TOKEN_ACTIVE) { } else {
p = ehci_alloc_packet(q); p = ehci_alloc_packet(q);
p->qtdaddr = q->qtdaddr; p->qtdaddr = q->qtdaddr;
p->qtd = qtd; p->qtd = qtd;
ehci_set_state(q->ehci, q->async, EST_EXECUTE); ehci_set_state(q->ehci, q->async, EST_EXECUTE);
again = 1; again = 1;
} else {
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
} }
return again; return again;