Commit e696b1da authored by Hans de Goede's avatar Hans de Goede Committed by Gerd Hoffmann
Browse files

ehci: Add support for packets with both data and an error status

parent 01e26b0e
Loading
Loading
Loading
Loading
+77 −84
Original line number Diff line number Diff line
@@ -1126,16 +1126,16 @@ static int ehci_init_transfer(EHCIPacket *p)
    return 0;
}

static void ehci_finish_transfer(EHCIQueue *q, int status)
static void ehci_finish_transfer(EHCIQueue *q, int len)
{
    uint32_t cpage, offset;

    if (status > 0) {
    if (len > 0) {
        /* update cpage & offset */
        cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
        offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;

        offset += status;
        offset += len;
        cpage  += offset >> QTD_BUFPTR_SH;
        offset &= ~QTD_BUFPTR_MASK;

@@ -1168,7 +1168,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)

    trace_usb_ehci_packet_action(p->queue, p, "wakeup");
    p->async = EHCI_ASYNC_FINISHED;
    p->usb_status = packet->status ? packet->status : packet->actual_length;

    if (p->queue->async) {
        qemu_bh_schedule(p->queue->ehci->async_bh);
@@ -1178,17 +1177,21 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
static void ehci_execute_complete(EHCIQueue *q)
{
    EHCIPacket *p = QTAILQ_FIRST(&q->packets);
    uint32_t tbytes;

    assert(p != NULL);
    assert(p->qtdaddr == q->qtdaddr);
    assert(p->async == EHCI_ASYNC_INITIALIZED ||
           p->async == EHCI_ASYNC_FINISHED);

    DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
            q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
    DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, "
            "status %d, actual_length %d\n",
            q->qhaddr, q->qh.next, q->qtdaddr,
            p->packet.status, p->packet.actual_length);

    if (p->usb_status < 0) {
        switch (p->usb_status) {
    switch (p->packet.status) {
    case USB_RET_SUCCESS:
        break;
    case USB_RET_IOERROR:
    case USB_RET_NODEV:
        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
@@ -1208,16 +1211,15 @@ static void ehci_execute_complete(EHCIQueue *q)
        break;
    default:
        /* should not be triggerable */
            fprintf(stderr, "USB invalid response %d\n", p->usb_status);
        fprintf(stderr, "USB invalid response %d\n", p->packet.status);
        assert(0);
        break;
    }
    } else {
        // TODO check 4.12 for splits
        uint32_t tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);

    /* TODO check 4.12 for splits */
    tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
    if (tbytes && p->pid == USB_TOKEN_IN) {
            tbytes -= p->usb_status;
        tbytes -= p->packet.actual_length;
        if (tbytes) {
            /* 4.15.1.2 must raise int on a short input packet */
            ehci_raise_irq(q->ehci, USBSTS_INT);
@@ -1225,11 +1227,10 @@ static void ehci_execute_complete(EHCIQueue *q)
    } else {
        tbytes = 0;
    }

    DPRINTF("updating tbytes to %d\n", tbytes);
    set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
    }
    ehci_finish_transfer(q, p->usb_status);

    ehci_finish_transfer(q, p->packet.actual_length);
    usb_packet_unmap(&p->packet, &p->sgl);
    qemu_sglist_destroy(&p->sgl);
    p->async = EHCI_ASYNC_NONE;
@@ -1321,7 +1322,6 @@ static int ehci_process_itd(EHCIState *ehci,
{
    USBDevice *dev;
    USBEndpoint *ep;
    int ret;
    uint32_t i, len, pid, dir, devaddr, endp;
    uint32_t pg, off, ptr1, ptr2, max, mult;

@@ -1368,18 +1368,19 @@ static int ehci_process_itd(EHCIState *ehci,
                usb_packet_map(&ehci->ipacket, &ehci->isgl);
                usb_handle_packet(dev, &ehci->ipacket);
                usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
                ret = (ehci->ipacket.status == USB_RET_SUCCESS) ?
                      ehci->ipacket.actual_length : ehci->ipacket.status;
            } else {
                DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
                ret = USB_RET_NAK;
                ehci->ipacket.status = USB_RET_NAK;
                ehci->ipacket.actual_length = 0;
            }
            qemu_sglist_destroy(&ehci->isgl);

            if (ret < 0) {
                switch (ret) {
            switch (ehci->ipacket.status) {
            case USB_RET_SUCCESS:
                break;
            default:
                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
                fprintf(stderr, "Unexpected iso usb result: %d\n",
                        ehci->ipacket.status);
                /* Fall through */
            case USB_RET_IOERROR:
            case USB_RET_NODEV:
@@ -1395,18 +1396,15 @@ static int ehci_process_itd(EHCIState *ehci,
                break;
            case USB_RET_NAK:
                /* no data for us, so do a zero-length transfer */
                    ret = 0;
                ehci->ipacket.actual_length = 0;
                break;
            }
            }
            if (ret >= 0) {
            if (!dir) {
                    /* OUT */
                    set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
                set_field(&itd->transact[i], len - ehci->ipacket.actual_length,
                          ITD_XACT_LENGTH); /* OUT */
            } else {
                    /* IN */
                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
                }
                set_field(&itd->transact[i], ehci->ipacket.actual_length,
                          ITD_XACT_LENGTH); /* IN */
            }
            if (itd->transact[i] & ITD_XACT_IOC) {
                ehci_raise_irq(ehci, USBSTS_INT);
@@ -1862,11 +1860,6 @@ static int ehci_state_execute(EHCIQueue *q)
        }
        goto out;
    }
    if (p->packet.status == USB_RET_SUCCESS) {
        p->usb_status = p->packet.actual_length;
    } else {
        p->usb_status = p->packet.status;
    }

    ehci_set_state(q->ehci, q->async, EST_EXECUTING);
    again = 1;
@@ -1890,7 +1883,7 @@ static int ehci_state_executing(EHCIQueue *q)
    }

    /* 4.10.5 */
    if (p->usb_status == USB_RET_NAK) {
    if (p->packet.status == USB_RET_NAK) {
        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
    } else {
        ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
+0 −1
Original line number Diff line number Diff line
@@ -230,7 +230,6 @@ struct EHCIPacket {
    QEMUSGList sgl;
    int pid;
    enum async_state async;
    int usb_status;
};

struct EHCIQueue {