mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 01:03:55 -06:00
ehci: add queuing support
Add packet queuing. Follow the qTD chain to see if there are more packets we can submit. Improves performance on larger transfers, especially with usb-host, as we don't have to wait for a packet to finish before sending the next one to the host for processing. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
cd6657156e
commit
773dc9cdfd
1 changed files with 51 additions and 9 deletions
|
@ -671,10 +671,6 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
|
||||||
{
|
{
|
||||||
EHCIPacket *p;
|
EHCIPacket *p;
|
||||||
|
|
||||||
#if 1
|
|
||||||
/* temporary, we don't handle multiple packets per queue (yet) */
|
|
||||||
assert(QTAILQ_EMPTY(&q->packets));
|
|
||||||
#endif
|
|
||||||
p = g_new0(EHCIPacket, 1);
|
p = g_new0(EHCIPacket, 1);
|
||||||
p->queue = q;
|
p->queue = q;
|
||||||
usb_packet_init(&p->packet);
|
usb_packet_init(&p->packet);
|
||||||
|
@ -1394,7 +1390,7 @@ static void ehci_execute_complete(EHCIQueue *q)
|
||||||
|
|
||||||
// 4.10.3
|
// 4.10.3
|
||||||
|
|
||||||
static int ehci_execute(EHCIPacket *p)
|
static int ehci_execute(EHCIPacket *p, const char *action)
|
||||||
{
|
{
|
||||||
USBEndpoint *ep;
|
USBEndpoint *ep;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1437,6 +1433,7 @@ static int ehci_execute(EHCIPacket *p)
|
||||||
usb_packet_setup(&p->packet, p->pid, ep);
|
usb_packet_setup(&p->packet, p->pid, ep);
|
||||||
usb_packet_map(&p->packet, &p->sgl);
|
usb_packet_map(&p->packet, &p->sgl);
|
||||||
|
|
||||||
|
trace_usb_ehci_packet_action(p->queue, p, action);
|
||||||
ret = usb_handle_packet(p->queue->dev, &p->packet);
|
ret = usb_handle_packet(p->queue->dev, &p->packet);
|
||||||
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
|
DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd "
|
||||||
"(total %d) endp %x ret %d\n",
|
"(total %d) endp %x ret %d\n",
|
||||||
|
@ -1713,7 +1710,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
|
||||||
}
|
}
|
||||||
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_queue_action(q, "resume");
|
trace_usb_ehci_packet_action(p->queue, p, "complete");
|
||||||
ehci_set_state(ehci, async, EST_EXECUTING);
|
ehci_set_state(ehci, async, EST_EXECUTING);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -1858,7 +1855,22 @@ static int ehci_state_fetchqtd(EHCIQueue *q, int async)
|
||||||
sizeof(EHCIqtd) >> 2);
|
sizeof(EHCIqtd) >> 2);
|
||||||
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
|
ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd);
|
||||||
|
|
||||||
if (qtd.token & QTD_TOKEN_ACTIVE) {
|
p = QTAILQ_FIRST(&q->packets);
|
||||||
|
while (p != NULL && p->qtdaddr != q->qtdaddr) {
|
||||||
|
/* should not happen (guest bug) */
|
||||||
|
ehci_free_packet(p);
|
||||||
|
p = QTAILQ_FIRST(&q->packets);
|
||||||
|
}
|
||||||
|
if (p != NULL) {
|
||||||
|
ehci_qh_do_overlay(q);
|
||||||
|
ehci_flush_qh(q);
|
||||||
|
if (p->async == EHCI_ASYNC_INFLIGHT) {
|
||||||
|
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
||||||
|
} else {
|
||||||
|
ehci_set_state(q->ehci, async, EST_EXECUTING);
|
||||||
|
}
|
||||||
|
again = 1;
|
||||||
|
} else if (qtd.token & QTD_TOKEN_ACTIVE) {
|
||||||
p = ehci_alloc_packet(q);
|
p = ehci_alloc_packet(q);
|
||||||
p->qtdaddr = q->qtdaddr;
|
p->qtdaddr = q->qtdaddr;
|
||||||
p->qtd = qtd;
|
p->qtd = qtd;
|
||||||
|
@ -1887,6 +1899,35 @@ static int ehci_state_horizqh(EHCIQueue *q, int async)
|
||||||
return again;
|
return again;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ehci_fill_queue(EHCIPacket *p, int async)
|
||||||
|
{
|
||||||
|
EHCIQueue *q = p->queue;
|
||||||
|
EHCIqtd qtd = p->qtd;
|
||||||
|
uint32_t qtdaddr;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (NLPTR_TBIT(qtd.altnext) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (NLPTR_TBIT(qtd.next) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
qtdaddr = qtd.next;
|
||||||
|
get_dwords(q->ehci, NLPTR_GET(qtdaddr),
|
||||||
|
(uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
|
||||||
|
ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
|
||||||
|
if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = ehci_alloc_packet(q);
|
||||||
|
p->qtdaddr = qtdaddr;
|
||||||
|
p->qtd = qtd;
|
||||||
|
p->usb_status = ehci_execute(p, "queue");
|
||||||
|
assert(p->usb_status = USB_RET_ASYNC);
|
||||||
|
p->async = EHCI_ASYNC_INFLIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int ehci_state_execute(EHCIQueue *q, int async)
|
static int ehci_state_execute(EHCIQueue *q, int async)
|
||||||
{
|
{
|
||||||
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
|
EHCIPacket *p = QTAILQ_FIRST(&q->packets);
|
||||||
|
@ -1916,17 +1957,18 @@ static int ehci_state_execute(EHCIQueue *q, int async)
|
||||||
ehci_set_usbsts(q->ehci, USBSTS_REC);
|
ehci_set_usbsts(q->ehci, USBSTS_REC);
|
||||||
}
|
}
|
||||||
|
|
||||||
p->usb_status = ehci_execute(p);
|
p->usb_status = ehci_execute(p, "process");
|
||||||
if (p->usb_status == USB_RET_PROCERR) {
|
if (p->usb_status == USB_RET_PROCERR) {
|
||||||
again = -1;
|
again = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (p->usb_status == USB_RET_ASYNC) {
|
if (p->usb_status == USB_RET_ASYNC) {
|
||||||
ehci_flush_qh(q);
|
ehci_flush_qh(q);
|
||||||
trace_usb_ehci_queue_action(q, "suspend");
|
trace_usb_ehci_packet_action(p->queue, p, "async");
|
||||||
p->async = EHCI_ASYNC_INFLIGHT;
|
p->async = EHCI_ASYNC_INFLIGHT;
|
||||||
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
ehci_set_state(q->ehci, async, EST_HORIZONTALQH);
|
||||||
again = 1;
|
again = 1;
|
||||||
|
ehci_fill_queue(p, async);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue