Commit a60fce0b authored by Anthony Liguori's avatar Anthony Liguori
Browse files

Merge remote-tracking branch 'kraxel/usb.26' into staging

parents 63236c15 5b1cdb4e
Loading
Loading
Loading
Loading
+3 −8
Original line number Diff line number Diff line
@@ -771,13 +771,12 @@ static void tusb6010_reset(DeviceState *dev)
    for (i = 0; i < 15; i++) {
        s->rx_config[i] = s->tx_config[i] = 0;
    }
    musb_reset(s->musb);
}

static int tusb6010_init(SysBusDevice *dev)
{
    TUSBState *s = FROM_SYSBUS(TUSBState, dev);
    qemu_irq *musb_irqs;
    int i;
    s->otg_timer = qemu_new_timer_ns(vm_clock, tusb_otg_tick, s);
    s->pwr_timer = qemu_new_timer_ns(vm_clock, tusb_power_tick, s);
    memory_region_init_io(&s->iomem[1], &tusb_async_ops, s, "tusb-async",
@@ -785,12 +784,8 @@ static int tusb6010_init(SysBusDevice *dev)
    sysbus_init_mmio_region(dev, &s->iomem[0]);
    sysbus_init_mmio_region(dev, &s->iomem[1]);
    sysbus_init_irq(dev, &s->irq);
    qdev_init_gpio_in(&dev->qdev, tusb6010_irq, __musb_irq_max + 1);
    musb_irqs = g_new0(qemu_irq, __musb_irq_max);
    for (i = 0; i < __musb_irq_max; i++) {
        musb_irqs[i] = qdev_get_gpio_in(&dev->qdev, i + 1);
    }
    s->musb = musb_init(musb_irqs);
    qdev_init_gpio_in(&dev->qdev, tusb6010_irq, musb_irq_max + 1);
    s->musb = musb_init(&dev->qdev, 1);
    return 0;
}

+64 −46
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
#include "qdev.h"
#include "sysemu.h"
#include "monitor.h"
#include "trace.h"

static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);

@@ -73,9 +74,13 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
    dev->info = info;
    dev->auto_attach = 1;
    QLIST_INIT(&dev->strings);
    rc = usb_claim_port(dev);
    if (rc == 0) {
        rc = dev->info->init(dev);
    if (rc == 0 && dev->auto_attach)
    }
    if (rc == 0 && dev->auto_attach) {
        rc = usb_device_attach(dev);
    }
    return rc;
}

@@ -89,6 +94,9 @@ static int usb_qdev_exit(DeviceState *qdev)
    if (dev->info->handle_destroy) {
        dev->info->handle_destroy(dev);
    }
    if (dev->port) {
        usb_release_port(dev);
    }
    return 0;
}

@@ -205,21 +213,13 @@ void usb_unregister_port(USBBus *bus, USBPort *port)
    bus->nfree--;
}

static int do_attach(USBDevice *dev)
int usb_claim_port(USBDevice *dev)
{
    USBBus *bus = usb_bus_from_device(dev);
    USBPort *port;

    if (dev->attached) {
        error_report("Error: tried to attach usb device %s twice\n",
                dev->product_desc);
        return -1;
    }
    if (bus->nfree == 0) {
        error_report("Error: tried to attach usb device %s to a bus with no free ports\n",
                dev->product_desc);
        return -1;
    }
    assert(dev->port == NULL);

    if (dev->port_path) {
        QTAILQ_FOREACH(port, &bus->free, next) {
            if (strcmp(port->path, dev->port_path) == 0) {
@@ -227,68 +227,86 @@ static int do_attach(USBDevice *dev)
            }
        }
        if (port == NULL) {
            error_report("Error: usb port %s (bus %s) not found\n",
            error_report("Error: usb port %s (bus %s) not found (in use?)\n",
                         dev->port_path, bus->qbus.name);
            return -1;
        }
    } else {
        port = QTAILQ_FIRST(&bus->free);
        if (bus->nfree == 1 && strcmp(dev->qdev.info->name, "usb-hub") != 0) {
            /* Create a new hub and chain it on */
            usb_create_simple(bus, "usb-hub");
        }
    if (!(port->speedmask & dev->speedmask)) {
        error_report("Warning: speed mismatch trying to attach usb device %s to bus %s\n",
                dev->product_desc, bus->qbus.name);
        if (bus->nfree == 0) {
            error_report("Error: tried to attach usb device %s to a bus "
                         "with no free ports\n", dev->product_desc);
            return -1;
        }
        port = QTAILQ_FIRST(&bus->free);
    }
    trace_usb_port_claim(bus->busnr, port->path);

    dev->attached++;
    QTAILQ_REMOVE(&bus->free, port, next);
    bus->nfree--;

    usb_attach(port, dev);
    dev->port = port;
    port->dev = dev;

    QTAILQ_INSERT_TAIL(&bus->used, port, next);
    bus->nused++;

    return 0;
}

int usb_device_attach(USBDevice *dev)
void usb_release_port(USBDevice *dev)
{
    USBBus *bus = usb_bus_from_device(dev);
    USBPort *port = dev->port;

    if (bus->nfree == 1 && dev->port_path == NULL) {
        /* Create a new hub and chain it on
           (unless a physical port location is specified). */
        usb_create_simple(bus, "usb-hub");
    }
    return do_attach(dev);
    assert(port != NULL);
    trace_usb_port_release(bus->busnr, port->path);

    QTAILQ_REMOVE(&bus->used, port, next);
    bus->nused--;

    dev->port = NULL;
    port->dev = NULL;

    QTAILQ_INSERT_TAIL(&bus->free, port, next);
    bus->nfree++;
}

int usb_device_detach(USBDevice *dev)
int usb_device_attach(USBDevice *dev)
{
    USBBus *bus = usb_bus_from_device(dev);
    USBPort *port;
    USBPort *port = dev->port;

    if (!dev->attached) {
        error_report("Error: tried to detach unattached usb device %s\n",
                dev->product_desc);
    assert(port != NULL);
    assert(!dev->attached);
    trace_usb_port_attach(bus->busnr, port->path);

    if (!(port->speedmask & dev->speedmask)) {
        error_report("Warning: speed mismatch trying to attach "
                     "usb device %s to bus %s\n",
                     dev->product_desc, bus->qbus.name);
        return -1;
    }
    dev->attached--;

    QTAILQ_FOREACH(port, &bus->used, next) {
        if (port->dev == dev)
            break;
    dev->attached++;
    usb_attach(port);

    return 0;
}
    assert(port != NULL);

    QTAILQ_REMOVE(&bus->used, port, next);
    bus->nused--;
int usb_device_detach(USBDevice *dev)
{
    USBBus *bus = usb_bus_from_device(dev);
    USBPort *port = dev->port;

    usb_attach(port, NULL);
    assert(port != NULL);
    assert(dev->attached);
    trace_usb_port_detach(bus->busnr, port->path);

    QTAILQ_INSERT_TAIL(&bus->free, port, next);
    bus->nfree++;
    usb_detach(port);
    dev->attached--;
    return 0;
}

+84 −164
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include "qemu-common.h"
#include "qemu-error.h"
#include "usb.h"
#include "usb-desc.h"
#include "monitor.h"

#include "hw/ccid.h"
@@ -306,56 +307,7 @@ typedef struct USBCCIDState {
 *   0dc3:1004 Athena Smartcard Solutions, Inc.
 */

static const uint8_t qemu_ccid_dev_descriptor[] = {
        0x12,       /*  u8 bLength; */
        USB_DT_DEVICE, /*  u8 bDescriptorType; Device */
        0x10, 0x01, /*  u16 bcdUSB; v1.1 */

        0x00,       /*  u8  bDeviceClass; */
        0x00,       /*  u8  bDeviceSubClass; */
        0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
        0x40,       /*  u8  bMaxPacketSize0; 8 Bytes (valid: 8,16,32,64) */

        /* Vendor and product id are arbitrary.  */
                    /*  u16 idVendor  */
        CCID_VENDOR_ID & 0xff, CCID_VENDOR_ID >> 8,
                    /*  u16 idProduct */
        CCID_PRODUCT_ID & 0xff, CCID_PRODUCT_ID >> 8,
                    /*  u16 bcdDevice */
        CCID_DEVICE_VERSION & 0xff, CCID_DEVICE_VERSION >> 8,
        0x01,       /*  u8  iManufacturer; */
        0x02,       /*  u8  iProduct; */
        0x03,       /*  u8  iSerialNumber; */
        0x01,       /*  u8  bNumConfigurations; */
};

static const uint8_t qemu_ccid_config_descriptor[] = {

        /* one configuration */
        0x09,       /* u8  bLength; */
        USB_DT_CONFIG, /* u8  bDescriptorType; Configuration */
        0x5d, 0x00, /* u16 wTotalLength; 9+9+54+7+7+7 */
        0x01,       /* u8  bNumInterfaces; (1) */
        0x01,       /* u8  bConfigurationValue; */
        0x00,       /* u8  iConfiguration; */
        0xe0,       /* u8  bmAttributes;
                                 Bit 7: must be set,
                                     6: Self-powered,
                                     5: Remote wakeup,
                                     4..0: resvd */
        100/2,      /* u8  MaxPower; 50 == 100mA */

        /* one interface */
        0x09,       /* u8  if_bLength; */
        USB_DT_INTERFACE, /* u8  if_bDescriptorType; Interface */
        0x00,       /* u8  if_bInterfaceNumber; */
        0x00,       /* u8  if_bAlternateSetting; */
        0x03,       /* u8  if_bNumEndpoints; */
        0x0b,       /* u8  if_bInterfaceClass; Smart Card Device Class */
        0x00,       /* u8  if_bInterfaceSubClass; Subclass code */
        0x00,       /* u8  if_bInterfaceProtocol; Protocol code */
        0x04,       /* u8  if_iInterface; Index of string descriptor */

static const uint8_t qemu_ccid_descriptor[] = {
        /* Smart Card Device Class Descriptor */
        0x36,       /* u8  bLength; */
        0x21,       /* u8  bDescriptorType; Functional */
@@ -439,38 +391,81 @@ static const uint8_t qemu_ccid_config_descriptor[] = {
                     *                  02h PIN Modification
                     */
        0x01,       /* u8  bMaxCCIDBusySlots; */
};

        /* Interrupt-IN endpoint */
        0x07,       /* u8  ep_bLength; */
                    /* u8  ep_bDescriptorType; Endpoint */
        USB_DT_ENDPOINT,
                    /* u8  ep_bEndpointAddress; IN Endpoint 1 */
        0x80 | CCID_INT_IN_EP,
        0x03,       /* u8  ep_bmAttributes; Interrupt */
                    /* u16 ep_wMaxPacketSize; */
        CCID_MAX_PACKET_SIZE & 0xff, (CCID_MAX_PACKET_SIZE >> 8),
        0xff,       /* u8  ep_bInterval; */

        /* Bulk-In endpoint */
        0x07,       /* u8  ep_bLength; */
                    /* u8  ep_bDescriptorType; Endpoint */
        USB_DT_ENDPOINT,
                    /* u8  ep_bEndpointAddress; IN Endpoint 2 */
        0x80 | CCID_BULK_IN_EP,
        0x02,       /* u8  ep_bmAttributes; Bulk */
        0x40, 0x00, /* u16 ep_wMaxPacketSize; */
        0x00,       /* u8  ep_bInterval; */

        /* Bulk-Out endpoint */
        0x07,       /* u8  ep_bLength; */
                    /* u8  ep_bDescriptorType; Endpoint */
        USB_DT_ENDPOINT,
                    /* u8  ep_bEndpointAddress; OUT Endpoint 3 */
        CCID_BULK_OUT_EP,
        0x02,       /* u8  ep_bmAttributes; Bulk */
        0x40, 0x00, /* u16 ep_wMaxPacketSize; */
        0x00,       /* u8  ep_bInterval; */
enum {
    STR_MANUFACTURER = 1,
    STR_PRODUCT,
    STR_SERIALNUMBER,
    STR_INTERFACE,
};

static const USBDescStrings desc_strings = {
    [STR_MANUFACTURER]  = "QEMU " QEMU_VERSION,
    [STR_PRODUCT]       = "QEMU USB CCID",
    [STR_SERIALNUMBER]  = "1",
    [STR_INTERFACE]     = "CCID Interface",
};

static const USBDescIface desc_iface0 = {
    .bInterfaceNumber              = 0,
    .bNumEndpoints                 = 3,
    .bInterfaceClass               = 0x0b,
    .bInterfaceSubClass            = 0x00,
    .bInterfaceProtocol            = 0x00,
    .iInterface                    = STR_INTERFACE,
    .ndesc                         = 1,
    .descs = (USBDescOther[]) {
        {
            /* smartcard descriptor */
            .data = qemu_ccid_descriptor,
        },
    },
    .eps = (USBDescEndpoint[]) {
        {
            .bEndpointAddress      = USB_DIR_IN | CCID_INT_IN_EP,
            .bmAttributes          = USB_ENDPOINT_XFER_INT,
            .bInterval             = 255,
            .wMaxPacketSize        = 64,
        },{
            .bEndpointAddress      = USB_DIR_IN | CCID_BULK_IN_EP,
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize        = 64,
        },{
            .bEndpointAddress      = USB_DIR_OUT | CCID_BULK_OUT_EP,
            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
            .wMaxPacketSize        = 64,
        },
    }
};

static const USBDescDevice desc_device = {
    .bcdUSB                        = 0x0110,
    .bMaxPacketSize0               = 64,
    .bNumConfigurations            = 1,
    .confs = (USBDescConfig[]) {
        {
            .bNumInterfaces        = 1,
            .bConfigurationValue   = 1,
            .bmAttributes          = 0xa0,
            .bMaxPower             = 50,
            .nif = 1,
            .ifs = &desc_iface0,
        },
    },
};

static const USBDesc desc_ccid = {
    .id = {
        .idVendor          = CCID_VENDOR_ID,
        .idProduct         = CCID_PRODUCT_ID,
        .bcdDevice         = CCID_DEVICE_VERSION,
        .iManufacturer     = STR_MANUFACTURER,
        .iProduct          = STR_PRODUCT,
        .iSerialNumber     = STR_SERIALNUMBER,
    },
    .full = &desc_device,
    .str  = desc_strings,
};

static bool ccid_has_pending_answers(USBCCIDState *s)
@@ -610,87 +605,12 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
    int ret = 0;

    DPRINTF(s, 1, "got control %x, value %x\n", request, value);
    switch (request) {
    case DeviceRequest | USB_REQ_GET_STATUS:
        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
        data[1] = 0x00;
        ret = 2;
        break;
    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
        if (value == USB_DEVICE_REMOTE_WAKEUP) {
            dev->remote_wakeup = 0;
        } else {
            goto fail;
        }
        ret = 0;
        break;
    case DeviceOutRequest | USB_REQ_SET_FEATURE:
        if (value == USB_DEVICE_REMOTE_WAKEUP) {
            dev->remote_wakeup = 1;
        } else {
            goto fail;
        }
        ret = 0;
        break;
    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
        dev->addr = value;
        ret = 0;
        break;
    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
        switch (value >> 8) {
        case USB_DT_DEVICE:
            memcpy(data, qemu_ccid_dev_descriptor,
                   sizeof(qemu_ccid_dev_descriptor));
            ret = sizeof(qemu_ccid_dev_descriptor);
            break;
        case USB_DT_CONFIG:
            memcpy(data, qemu_ccid_config_descriptor,
                   sizeof(qemu_ccid_config_descriptor));
            ret = sizeof(qemu_ccid_config_descriptor);
            break;
        case USB_DT_STRING:
            switch (value & 0xff) {
            case 0:
                /* language ids */
                data[0] = 4;
                data[1] = 3;
                data[2] = 0x09;
                data[3] = 0x04;
                ret = 4;
                break;
            case 1:
                /* vendor description */
                ret = set_usb_string(data, CCID_VENDOR_DESCRIPTION);
                break;
            case 2:
                /* product description */
                ret = set_usb_string(data, CCID_PRODUCT_DESCRIPTION);
                break;
            case 3:
                /* serial number */
                ret = set_usb_string(data, CCID_SERIAL_NUMBER_STRING);
                break;
            case 4:
                /* interface name */
                ret = set_usb_string(data, CCID_INTERFACE_NAME);
                break;
            default:
                goto fail;
            }
            break;
        default:
            goto fail;
    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
    if (ret >= 0) {
        return ret;
    }
        break;
    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
        data[0] = 1;
        ret = 1;
        break;
    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
        /* Only one configuration - we just ignore the request */
        ret = 0;
        break;

    switch (request) {
    case DeviceRequest | USB_REQ_GET_INTERFACE:
        data[0] = 0;
        ret = 1;
@@ -698,9 +618,6 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
        ret = 0;
        break;
    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
        ret = 0;
        break;

        /* Class specific requests.  */
    case InterfaceOutClass | CCID_CONTROL_ABORT:
@@ -716,7 +633,6 @@ static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
        ret = USB_RET_STALL;
        break;
    default:
fail:
        DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
                request, value);
        ret = USB_RET_STALL;
@@ -895,6 +811,7 @@ static void ccid_on_slot_change(USBCCIDState *s, bool full)
        s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
    }
    s->notify_slot_change = true;
    usb_wakeup(&s->dev);
}

static void ccid_write_data_block_error(
@@ -1075,6 +992,7 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
            break;
        default:
            DPRINTF(s, 1, "Bad endpoint\n");
            ret = USB_RET_STALL;
            break;
        }
        break;
@@ -1256,6 +1174,7 @@ static int ccid_initfn(USBDevice *dev)
{
    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);

    usb_desc_init(dev);
    qbus_create_inplace(&s->bus.qbus, &ccid_bus_info, &dev->qdev, NULL);
    s->bus.qbus.allow_hotplug = 1;
    s->card = NULL;
@@ -1381,6 +1300,7 @@ static struct USBDeviceInfo ccid_info = {
    .qdev.desc      = "CCID Rev 1.1 smartcard reader",
    .qdev.size      = sizeof(USBCCIDState),
    .init           = ccid_initfn,
    .usb_desc       = &desc_ccid,
    .handle_packet  = usb_generic_handle_packet,
    .handle_reset   = ccid_handle_reset,
    .handle_control = ccid_handle_control,
+1 −1
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ struct USBDescEndpoint {

struct USBDescOther {
    uint8_t                   length;
    uint8_t                   *data;
    const uint8_t             *data;
};

typedef const char *USBDescStrings[256];
+53 −12
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ typedef enum {
    EST_FETCHENTRY,
    EST_FETCHQH,
    EST_FETCHITD,
    EST_FETCHSITD,
    EST_ADVANCEQUEUE,
    EST_FETCHQTD,
    EST_EXECUTE,
@@ -646,6 +647,13 @@ static void ehci_trace_itd(EHCIState *s, target_phys_addr_t addr, EHCIitd *itd)
                       get_field(itd->bufptr[0], ITD_BUFPTR_DEVADDR));
}

static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
                            EHCIsitd *sitd)
{
    trace_usb_ehci_sitd(addr, sitd->next,
                        (bool)(sitd->results & SITD_RESULTS_ACTIVE));
}

/* queue management */

static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async)
@@ -849,8 +857,8 @@ static void ehci_reset(void *opaque)
     */
    for(i = 0; i < NB_PORTS; i++) {
        devs[i] = s->ports[i].dev;
        if (devs[i]) {
            usb_attach(&s->ports[i], NULL);
        if (devs[i] && devs[i]->attached) {
            usb_detach(&s->ports[i]);
        }
    }

@@ -870,8 +878,8 @@ static void ehci_reset(void *opaque)
        } else {
            s->portsc[i] = PORTSC_PPOWER;
        }
        if (devs[i]) {
            usb_attach(&s->ports[i], devs[i]);
        if (devs[i] && devs[i]->attached) {
            usb_attach(&s->ports[i]);
        }
    }
    ehci_queues_rip_all(s);
@@ -937,15 +945,15 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
        return;
    }

    if (dev) {
        usb_attach(&s->ports[port], NULL);
    if (dev && dev->attached) {
        usb_detach(&s->ports[port]);
    }

    *portsc &= ~PORTSC_POWNER;
    *portsc |= owner;

    if (dev) {
        usb_attach(&s->ports[port], dev);
    if (dev && dev->attached) {
        usb_attach(&s->ports[port]);
    }
}

@@ -969,8 +977,8 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)

    if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
        trace_usb_ehci_port_reset(port, 0);
        if (dev) {
            usb_attach(&s->ports[port], dev);
        if (dev && dev->attached) {
            usb_attach(&s->ports[port]);
            usb_send_msg(dev, USB_MSG_RESET);
            *portsc &= ~PORTSC_CSC;
        }
@@ -979,7 +987,7 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
         *  Table 2.16 Set the enable bit(and enable bit change) to indicate
         *  to SW that this port has a high speed device attached
         */
        if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
        if (dev && dev->attached && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
            val |= PORTSC_PED;
        }
    }
@@ -1584,8 +1592,13 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async)
        again = 1;
        break;

    case NLPTR_TYPE_STITD:
        ehci_set_state(ehci, async, EST_FETCHSITD);
        again = 1;
        break;

    default:
        // TODO: handle siTD and FSTN types
        /* TODO: handle FSTN type */
        fprintf(stderr, "FETCHENTRY: entry at %X is of type %d "
                "which is not supported yet\n", entry, NLPTR_TYPE_GET(entry));
        return -1;
@@ -1701,6 +1714,30 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async)
    return 1;
}

static int ehci_state_fetchsitd(EHCIState *ehci, int async)
{
    uint32_t entry;
    EHCIsitd sitd;

    assert(!async);
    entry = ehci_get_fetch_addr(ehci, async);

    get_dwords(NLPTR_GET(entry), (uint32_t *)&sitd,
               sizeof(EHCIsitd) >> 2);
    ehci_trace_sitd(ehci, entry, &sitd);

    if (!(sitd.results & SITD_RESULTS_ACTIVE)) {
        /* siTD is not active, nothing to do */;
    } else {
        /* TODO: split transfers are not implemented */
        fprintf(stderr, "WARNING: Skipping active siTD\n");
    }

    ehci_set_fetch_addr(ehci, async, sitd.next);
    ehci_set_state(ehci, async, EST_FETCHENTRY);
    return 1;
}

/* Section 4.10.2 - paragraph 3 */
static int ehci_state_advqueue(EHCIQueue *q, int async)
{
@@ -1976,6 +2013,10 @@ static void ehci_advance_state(EHCIState *ehci,
            again = ehci_state_fetchitd(ehci, async);
            break;

        case EST_FETCHSITD:
            again = ehci_state_fetchsitd(ehci, async);
            break;

        case EST_ADVANCEQUEUE:
            again = ehci_state_advqueue(q, async);
            break;
Loading