+/* Get an array of dwords from main memory */
+static inline int get_dwords(EHCIState *ehci, uint32_t addr,
+ uint32_t *buf, int num)
+{
+ int i;
+
+ if (!ehci->as) {
+ ehci_raise_irq(ehci, USBSTS_HSE);
+ ehci->usbcmd &= ~USBCMD_RUNSTOP;
+ trace_usb_ehci_dma_error();
+ return -1;
+ }
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ dma_memory_read(ehci->as, addr, buf, sizeof(*buf));
+ *buf = le32_to_cpu(*buf);
+ }
+
+ return num;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(EHCIState *ehci, uint32_t addr,
+ uint32_t *buf, int num)
+{
+ int i;
+
+ if (!ehci->as) {
+ ehci_raise_irq(ehci, USBSTS_HSE);
+ ehci->usbcmd &= ~USBCMD_RUNSTOP;
+ trace_usb_ehci_dma_error();
+ return -1;
+ }
+
+ for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+ uint32_t tmp = cpu_to_le32(*buf);
+ dma_memory_write(ehci->as, addr, &tmp, sizeof(tmp));
+ }
+
+ return num;
+}
+
+static int ehci_get_pid(EHCIqtd *qtd)
+{
+ switch (get_field(qtd->token, QTD_TOKEN_PID)) {
+ case 0:
+ return USB_TOKEN_OUT;
+ case 1:
+ return USB_TOKEN_IN;
+ case 2:
+ return USB_TOKEN_SETUP;
+ default:
+ fprintf(stderr, "bad token\n");
+ return 0;
+ }
+}
+
+static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh)
+{
+ uint32_t devaddr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
+ uint32_t endp = get_field(qh->epchar, QH_EPCHAR_EP);
+ if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
+ (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
+ (qh->current_qtd != q->qh.current_qtd) ||
+ (q->async && qh->next_qtd != q->qh.next_qtd) ||
+ (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd,
+ 7 * sizeof(uint32_t)) != 0) ||
+ (q->dev != NULL && q->dev->addr != devaddr)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd)
+{
+ if (p->qtdaddr != p->queue->qtdaddr ||
+ (p->queue->async && !NLPTR_TBIT(p->qtd.next) &&
+ (p->qtd.next != qtd->next)) ||
+ (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) ||
+ p->qtd.token != qtd->token ||
+ p->qtd.bufptr[0] != qtd->bufptr[0]) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static bool ehci_verify_pid(EHCIQueue *q, EHCIqtd *qtd)
+{
+ int ep = get_field(q->qh.epchar, QH_EPCHAR_EP);
+ int pid = ehci_get_pid(qtd);
+
+ /* Note the pid changing is normal for ep 0 (the control ep) */
+ if (q->last_pid && ep != 0 && pid != q->last_pid) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+/* Finish executing and writeback a packet outside of the regular
+ fetchqh -> fetchqtd -> execute -> writeback cycle */
+static void ehci_writeback_async_complete_packet(EHCIPacket *p)
+{
+ EHCIQueue *q = p->queue;
+ EHCIqtd qtd;
+ EHCIqh qh;
+ int state;
+
+ /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */
+ get_dwords(q->ehci, NLPTR_GET(q->qhaddr),
+ (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
+ get_dwords(q->ehci, NLPTR_GET(q->qtdaddr),
+ (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2);
+ if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) {
+ p->async = EHCI_ASYNC_INITIALIZED;
+ ehci_free_packet(p);
+ return;
+ }
+
+ state = ehci_get_state(q->ehci, q->async);
+ ehci_state_executing(q);
+ ehci_state_writeback(q); /* Frees the packet! */
+ if (!(q->qh.token & QTD_TOKEN_HALT)) {
+ ehci_state_advqueue(q);
+ }
+ ehci_set_state(q->ehci, q->async, state);
+}
+