diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2012-05-09 17:06:36 +0200 |
---|---|---|
committer | Gerd Hoffmann <kraxel@redhat.com> | 2012-06-07 10:02:22 +0200 |
commit | eb36a88e6b3a93001ba224aab5e631d251acae65 (patch) | |
tree | db3ce633afb322abb59f7ca915b4e971eb831c4e /hw/usb | |
parent | 348f10374ac50b2e4511764fc5fae7cacef95ffa (diff) |
ehci: add EHCIPacket
Add a separate EHCIPacket struct and move fields over from EHCIQueue.
Preparing for supporting multiple packets per queue being in flight at
the same time. No functional changes yet.
Fix some codestyle issues along the way.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw/usb')
-rw-r--r-- | hw/usb/hcd-ehci.c | 234 |
1 files changed, 157 insertions, 77 deletions
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index e759c996ce..03ae09fc94 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -334,6 +334,7 @@ typedef struct EHCIfstn { uint32_t backptr; // Standard next link pointer } EHCIfstn; +typedef struct EHCIPacket EHCIPacket; typedef struct EHCIQueue EHCIQueue; typedef struct EHCIState EHCIState; @@ -343,6 +344,21 @@ enum async_state { EHCI_ASYNC_FINISHED, }; +struct EHCIPacket { + EHCIQueue *queue; + QTAILQ_ENTRY(EHCIPacket) next; + + EHCIqtd qtd; /* copy of current QTD (being worked on) */ + uint32_t qtdaddr; /* address QTD read from */ + + USBPacket packet; + QEMUSGList sgl; + int pid; + uint32_t tbytes; + enum async_state async; + int usb_status; +}; + struct EHCIQueue { EHCIState *ehci; QTAILQ_ENTRY(EHCIQueue) next; @@ -352,17 +368,10 @@ struct EHCIQueue { /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) */ - EHCIqh qh; // copy of current QH (being worked on) - uint32_t qhaddr; // address QH read from - EHCIqtd qtd; // copy of current QTD (being worked on) - uint32_t qtdaddr; // address QTD read from - - USBPacket packet; - QEMUSGList sgl; - int pid; - uint32_t tbytes; - enum async_state async; - int usb_status; + EHCIqh qh; /* copy of current QH (being worked on) */ + uint32_t qhaddr; /* address QH read from */ + uint32_t qtdaddr; /* address QTD read from */ + QTAILQ_HEAD(, EHCIPacket) packets; }; typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; @@ -655,6 +664,35 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, (bool)(sitd->results & SITD_RESULTS_ACTIVE)); } +/* packet management */ + +static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) +{ + 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->queue = q; + usb_packet_init(&p->packet); + QTAILQ_INSERT_TAIL(&q->packets, p, next); + trace_usb_ehci_packet_action(p->queue, p, "alloc"); + return p; +} + +static void ehci_free_packet(EHCIPacket *p) +{ + trace_usb_ehci_packet_action(p->queue, p, "free"); + if (p->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&p->packet); + } + QTAILQ_REMOVE(&p->queue->packets, p, next); + usb_packet_cleanup(&p->packet); + g_free(p); +} + /* queue management */ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) @@ -664,7 +702,7 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) q = g_malloc0(sizeof(*q)); q->ehci = ehci; - usb_packet_init(&q->packet); + QTAILQ_INIT(&q->packets); QTAILQ_INSERT_HEAD(head, q, next); trace_usb_ehci_queue_action(q, "alloc"); return q; @@ -673,9 +711,11 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) static void ehci_free_queue(EHCIQueue *q, int async) { EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues; + EHCIPacket *p; + trace_usb_ehci_queue_action(q, "free"); - if (q->async == EHCI_ASYNC_INFLIGHT) { - usb_cancel_packet(&q->packet); + while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { + ehci_free_packet(p); } QTAILQ_REMOVE(head, q, next); g_free(q); @@ -718,10 +758,11 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q, *tmp; + int addr; QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (!usb_packet_is_inflight(&q->packet) || - q->packet.ep->dev != dev) { + addr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); + if (addr != dev->addr) { continue; } ehci_free_queue(q, async); @@ -1169,21 +1210,25 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr, static int ehci_qh_do_overlay(EHCIQueue *q) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int i; int dtoggle; int ping; int eps; int reload; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + // remember values in fields to preserve in qh after overlay dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE; ping = q->qh.token & QTD_TOKEN_PING; - q->qh.current_qtd = q->qtdaddr; - q->qh.next_qtd = q->qtd.next; - q->qh.altnext_qtd = q->qtd.altnext; - q->qh.token = q->qtd.token; + q->qh.current_qtd = p->qtdaddr; + q->qh.next_qtd = p->qtd.next; + q->qh.altnext_qtd = p->qtd.altnext; + q->qh.token = p->qtd.token; eps = get_field(q->qh.epchar, QH_EPCHAR_EPS); @@ -1196,7 +1241,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q) set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); for (i = 0; i < 5; i++) { - q->qh.bufptr[i] = q->qtd.bufptr[i]; + q->qh.bufptr[i] = p->qtd.bufptr[i]; } if (!(q->qh.epchar & QH_EPCHAR_DTC)) { @@ -1214,15 +1259,15 @@ static int ehci_qh_do_overlay(EHCIQueue *q) return 0; } -static int ehci_init_transfer(EHCIQueue *q) +static int ehci_init_transfer(EHCIPacket *p) { uint32_t cpage, offset, bytes, plen; dma_addr_t page; - cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); - bytes = get_field(q->qh.token, QTD_TOKEN_TBYTES); - offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK; - pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5); + cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); + bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); + offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; + pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5); while (bytes > 0) { if (cpage > 4) { @@ -1230,7 +1275,7 @@ static int ehci_init_transfer(EHCIQueue *q) return USB_RET_PROCERR; } - page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; + page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; page += offset; plen = bytes; if (plen > 4096 - offset) { @@ -1239,7 +1284,7 @@ static int ehci_init_transfer(EHCIQueue *q) cpage++; } - qemu_sglist_add(&q->sgl, page, plen); + qemu_sglist_add(&p->sgl, page, plen); bytes -= plen; } return 0; @@ -1249,8 +1294,6 @@ static void ehci_finish_transfer(EHCIQueue *q, int status) { uint32_t cpage, offset; - qemu_sglist_destroy(&q->sgl); - if (status > 0) { /* update cpage & offset */ cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); @@ -1268,7 +1311,7 @@ static void ehci_finish_transfer(EHCIQueue *q, int status) static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { - EHCIQueue *q; + EHCIPacket *p; EHCIState *s = port->opaque; uint32_t portsc = s->portsc[port->index]; @@ -1278,23 +1321,27 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) return; } - q = container_of(packet, EHCIQueue, packet); - trace_usb_ehci_queue_action(q, "wakeup"); - assert(q->async == EHCI_ASYNC_INFLIGHT); - q->async = EHCI_ASYNC_FINISHED; - q->usb_status = packet->result; + p = container_of(packet, EHCIPacket, packet); + trace_usb_ehci_packet_action(p->queue, p, "wakeup"); + assert(p->async == EHCI_ASYNC_INFLIGHT); + p->async = EHCI_ASYNC_FINISHED; + p->usb_status = packet->result; } static void ehci_execute_complete(EHCIQueue *q) { - assert(q->async != EHCI_ASYNC_INFLIGHT); - q->async = EHCI_ASYNC_NONE; + EHCIPacket *p = QTAILQ_FIRST(&q->packets); + + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + assert(p->async != EHCI_ASYNC_INFLIGHT); + p->async = EHCI_ASYNC_NONE; DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); - if (q->usb_status < 0) { - switch(q->usb_status) { + if (p->usb_status < 0) { + switch (p->usb_status) { case USB_RET_IOERROR: case USB_RET_NODEV: q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); @@ -1314,28 +1361,29 @@ static void ehci_execute_complete(EHCIQueue *q) break; default: /* should not be triggerable */ - fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status); + fprintf(stderr, "USB invalid response %d\n", p->usb_status); assert(0); break; } - } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { - q->usb_status = USB_RET_BABBLE; + } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) { + p->usb_status = USB_RET_BABBLE; q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); ehci_record_interrupt(q->ehci, USBSTS_ERRINT); } else { // TODO check 4.12 for splits - if (q->tbytes && q->pid == USB_TOKEN_IN) { - q->tbytes -= q->usb_status; + if (p->tbytes && p->pid == USB_TOKEN_IN) { + p->tbytes -= p->usb_status; } else { - q->tbytes = 0; + p->tbytes = 0; } - DPRINTF("updating tbytes to %d\n", q->tbytes); - set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES); + DPRINTF("updating tbytes to %d\n", p->tbytes); + set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES); } - ehci_finish_transfer(q, q->usb_status); - usb_packet_unmap(&q->packet); + ehci_finish_transfer(q, p->usb_status); + qemu_sglist_destroy(&p->sgl); + usb_packet_unmap(&p->packet); q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; @@ -1349,32 +1397,44 @@ static void ehci_execute_complete(EHCIQueue *q) static int ehci_execute(EHCIQueue *q) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); USBDevice *dev; USBEndpoint *ep; int ret; int endp; int devadr; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) { fprintf(stderr, "Attempting to execute inactive QH\n"); return USB_RET_PROCERR; } - q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; - if (q->tbytes > BUFF_SIZE) { + p->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; + if (p->tbytes > BUFF_SIZE) { fprintf(stderr, "Request for more bytes than allowed\n"); return USB_RET_PROCERR; } - q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; - switch(q->pid) { - case 0: q->pid = USB_TOKEN_OUT; break; - case 1: q->pid = USB_TOKEN_IN; break; - case 2: q->pid = USB_TOKEN_SETUP; break; - default: fprintf(stderr, "bad token\n"); break; + p->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; + switch (p->pid) { + case 0: + p->pid = USB_TOKEN_OUT; + break; + case 1: + p->pid = USB_TOKEN_IN; + break; + case 2: + p->pid = USB_TOKEN_SETUP; + break; + default: + fprintf(stderr, "bad token\n"); + break; } - if (ehci_init_transfer(q) != 0) { + if (ehci_init_transfer(p) != 0) { return USB_RET_PROCERR; } @@ -1383,12 +1443,12 @@ static int ehci_execute(EHCIQueue *q) /* TODO: associating device with ehci port */ dev = ehci_find_device(q->ehci, devadr); - ep = usb_ep_get(dev, q->pid, endp); + ep = usb_ep_get(dev, p->pid, endp); - usb_packet_setup(&q->packet, q->pid, ep); - usb_packet_map(&q->packet, &q->sgl); + usb_packet_setup(&p->packet, p->pid, ep); + usb_packet_map(&p->packet, &p->sgl); - ret = usb_handle_packet(dev, &q->packet); + ret = usb_handle_packet(dev, &p->packet); DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " "(total %d) endp %x ret %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->pid, @@ -1601,6 +1661,7 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { + EHCIPacket *p; uint32_t entry; EHCIQueue *q; @@ -1609,6 +1670,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) if (NULL == q) { q = ehci_alloc_queue(ehci, async); } + p = QTAILQ_FIRST(&q->packets); q->qhaddr = entry; q->seen++; @@ -1623,12 +1685,12 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); - if (q->async == EHCI_ASYNC_INFLIGHT) { + if (p && p->async == EHCI_ASYNC_INFLIGHT) { /* I/O still in progress -- skip queue */ ehci_set_state(ehci, async, EST_HORIZONTALQH); goto out; } - if (q->async == EHCI_ASYNC_FINISHED) { + if (p && p->async == EHCI_ASYNC_FINISHED) { /* I/O finished -- continue processing queue */ trace_usb_ehci_queue_action(q, "resume"); ehci_set_state(ehci, async, EST_EXECUTING); @@ -1767,13 +1829,18 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) /* Section 4.10.2 - paragraph 4 */ static int ehci_state_fetchqtd(EHCIQueue *q, int async) { + EHCIqtd qtd; + EHCIPacket *p; int again = 0; - get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd, + get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); - ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd); + ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); - if (q->qtd.token & QTD_TOKEN_ACTIVE) { + if (qtd.token & QTD_TOKEN_ACTIVE) { + p = ehci_alloc_packet(q); + p->qtdaddr = q->qtdaddr; + p->qtd = qtd; ehci_set_state(q->ehci, async, EST_EXECUTE); again = 1; } else { @@ -1818,8 +1885,12 @@ static void ehci_flush_qh(EHCIQueue *q) static int ehci_state_execute(EHCIQueue *q, int async) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + if (ehci_qh_do_overlay(q) != 0) { return -1; } @@ -1841,15 +1912,15 @@ static int ehci_state_execute(EHCIQueue *q, int async) ehci_set_usbsts(q->ehci, USBSTS_REC); } - q->usb_status = ehci_execute(q); - if (q->usb_status == USB_RET_PROCERR) { + p->usb_status = ehci_execute(q); + if (p->usb_status == USB_RET_PROCERR) { again = -1; goto out; } - if (q->usb_status == USB_RET_ASYNC) { + if (p->usb_status == USB_RET_ASYNC) { ehci_flush_qh(q); trace_usb_ehci_queue_action(q, "suspend"); - q->async = EHCI_ASYNC_INFLIGHT; + p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, async, EST_HORIZONTALQH); again = 1; goto out; @@ -1864,13 +1935,17 @@ out: static int ehci_state_executing(EHCIQueue *q, int async) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + ehci_execute_complete(q); - if (q->usb_status == USB_RET_ASYNC) { + if (p->usb_status == USB_RET_ASYNC) { goto out; } - if (q->usb_status == USB_RET_PROCERR) { + if (p->usb_status == USB_RET_PROCERR) { again = -1; goto out; } @@ -1885,7 +1960,7 @@ static int ehci_state_executing(EHCIQueue *q, int async) } /* 4.10.5 */ - if (q->usb_status == USB_RET_NAK) { + if (p->usb_status == USB_RET_NAK) { ehci_set_state(q->ehci, async, EST_HORIZONTALQH); } else { ehci_set_state(q->ehci, async, EST_WRITEBACK); @@ -1901,12 +1976,17 @@ out: static int ehci_state_writeback(EHCIQueue *q, int async) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; /* Write back the QTD from the QH area */ - ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd); - put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd, + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + + ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd); + put_dwords(q->ehci, NLPTR_GET(p->qtdaddr), (uint32_t *) &q->qh.next_qtd, sizeof(EHCIqtd) >> 2); + ehci_free_packet(p); /* * EHCI specs say go horizontal here. |