summary refs log tree commit diff stats
path: root/hw/usb-ehci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb-ehci.c')
-rw-r--r--hw/usb-ehci.c439
1 files changed, 270 insertions, 169 deletions
diff --git a/hw/usb-ehci.c b/hw/usb-ehci.c
index 91fb7dea93..2b43895315 100644
--- a/hw/usb-ehci.c
+++ b/hw/usb-ehci.c
@@ -20,9 +20,6 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * TODO:
- *  o Downstream port handoff
  */
 
 #include "hw.h"
@@ -31,6 +28,7 @@
 #include "pci.h"
 #include "monitor.h"
 #include "trace.h"
+#include "dma.h"
 
 #define EHCI_DEBUG   0
 
@@ -103,10 +101,10 @@
 #define PORTSC_BEGIN         PORTSC
 #define PORTSC_END           (PORTSC + 4 * NB_PORTS)
 /*
- * Bits that are reserverd or are read-only are masked out of values
+ * Bits that are reserved or are read-only are masked out of values
  * written to us by software
  */
-#define PORTSC_RO_MASK       0x007021c5
+#define PORTSC_RO_MASK       0x007001c0
 #define PORTSC_RWC_MASK      0x0000002a
 #define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
 #define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
@@ -133,7 +131,7 @@
 #define FRAME_TIMER_NS   (1000000000 / FRAME_TIMER_FREQ)
 
 #define NB_MAXINTRATE    8        // Max rate at which controller issues ints
-#define NB_PORTS         4        // Number of downstream ports
+#define NB_PORTS         6        // Number of downstream ports
 #define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
 #define MAX_ITERATIONS   20       // Max number of QH before we break the loop
 #define MAX_QH           100      // Max allowable queue heads in a chain
@@ -272,6 +270,7 @@ typedef struct EHCIqtd {
 
     uint32_t bufptr[5];               // Standard buffer pointer
 #define QTD_BUFPTR_MASK               0xfffff000
+#define QTD_BUFPTR_SH                 12
 } EHCIqtd;
 
 /*  EHCI spec version 1.0 Section 3.6
@@ -360,7 +359,7 @@ struct EHCIQueue {
     uint32_t qtdaddr;      // address QTD read from
 
     USBPacket packet;
-    uint8_t buffer[BUFF_SIZE];
+    QEMUSGList sgl;
     int pid;
     uint32_t tbytes;
     enum async_state async;
@@ -373,7 +372,7 @@ struct EHCIState {
     qemu_irq irq;
     target_phys_addr_t mem_base;
     int mem;
-    int num_ports;
+    int companion_count;
 
     /* properties */
     uint32_t freq;
@@ -409,6 +408,7 @@ struct EHCIState {
     int astate;                        // Current state in asynchronous schedule
     int pstate;                        // Current state in periodic schedule
     USBPort ports[NB_PORTS];
+    USBPort *companion_ports[NB_PORTS];
     uint32_t usbsts_pending;
     QTAILQ_HEAD(, EHCIQueue) queues;
 
@@ -416,7 +416,7 @@ struct EHCIState {
     uint32_t p_fetch_addr;   // which address to look at next
 
     USBPacket ipacket;
-    uint8_t ibuffer[BUFF_SIZE];
+    QEMUSGList isgl;
     int isoch_pause;
 
     uint64_t last_run_ns;
@@ -731,17 +731,17 @@ static void ehci_attach(USBPort *port)
 
     trace_usb_ehci_port_attach(port->index, port->dev->product_desc);
 
+    if (*portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->dev = port->dev;
+        companion->ops->attach(companion);
+        return;
+    }
+
     *portsc |= PORTSC_CONNECT;
     *portsc |= PORTSC_CSC;
 
-    /*
-     *  If a high speed device is attached then we own this port(indicated
-     *  by zero in the PORTSC_POWNER bit field) so set the status bit
-     *  and set an interrupt if enabled.
-     */
-    if ( !(*portsc & PORTSC_POWNER)) {
-        ehci_set_interrupt(s, USBSTS_PCD);
-    }
+    ehci_set_interrupt(s, USBSTS_PCD);
 }
 
 static void ehci_detach(USBPort *port)
@@ -751,17 +751,88 @@ static void ehci_detach(USBPort *port)
 
     trace_usb_ehci_port_detach(port->index);
 
-    *portsc &= ~PORTSC_CONNECT;
+    if (*portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->ops->detach(companion);
+        companion->dev = NULL;
+        return;
+    }
+
+    ehci_queues_rip_device(s, port->dev);
+
+    *portsc &= ~(PORTSC_CONNECT|PORTSC_PED);
     *portsc |= PORTSC_CSC;
 
-    /*
-     *  If a high speed device is attached then we own this port(indicated
-     *  by zero in the PORTSC_POWNER bit field) so set the status bit
-     *  and set an interrupt if enabled.
-     */
-    if ( !(*portsc & PORTSC_POWNER)) {
-        ehci_set_interrupt(s, USBSTS_PCD);
+    ehci_set_interrupt(s, USBSTS_PCD);
+}
+
+static void ehci_child_detach(USBPort *port, USBDevice *child)
+{
+    EHCIState *s = port->opaque;
+    uint32_t portsc = s->portsc[port->index];
+
+    if (portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->ops->child_detach(companion, child);
+        companion->dev = NULL;
+        return;
     }
+
+    ehci_queues_rip_device(s, child);
+}
+
+static void ehci_wakeup(USBPort *port)
+{
+    EHCIState *s = port->opaque;
+    uint32_t portsc = s->portsc[port->index];
+
+    if (portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        if (companion->ops->wakeup) {
+            companion->ops->wakeup(companion);
+        }
+    }
+}
+
+static int ehci_register_companion(USBBus *bus, USBPort *ports[],
+                                   uint32_t portcount, uint32_t firstport)
+{
+    EHCIState *s = container_of(bus, EHCIState, bus);
+    uint32_t i;
+
+    if (firstport + portcount > NB_PORTS) {
+        qerror_report(QERR_INVALID_PARAMETER_VALUE, "firstport",
+                      "firstport on masterbus");
+        error_printf_unless_qmp(
+            "firstport value of %u makes companion take ports %u - %u, which "
+            "is outside of the valid range of 0 - %u\n", firstport, firstport,
+            firstport + portcount - 1, NB_PORTS - 1);
+        return -1;
+    }
+
+    for (i = 0; i < portcount; i++) {
+        if (s->companion_ports[firstport + i]) {
+            qerror_report(QERR_INVALID_PARAMETER_VALUE, "masterbus",
+                          "an USB masterbus");
+            error_printf_unless_qmp(
+                "port %u on masterbus %s already has a companion assigned\n",
+                firstport + i, bus->qbus.name);
+            return -1;
+        }
+    }
+
+    for (i = 0; i < portcount; i++) {
+        s->companion_ports[firstport + i] = ports[i];
+        s->ports[firstport + i].speedmask |=
+            USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL;
+        /* Ensure devs attached before the initial reset go to the companion */
+        s->portsc[firstport + i] = PORTSC_POWNER;
+    }
+
+    s->companion_count++;
+    s->mmio[0x05] = (s->companion_count << 4) | portcount;
+
+    return 0;
 }
 
 /* 4.1 host controller initialization */
@@ -769,9 +840,21 @@ static void ehci_reset(void *opaque)
 {
     EHCIState *s = opaque;
     int i;
+    USBDevice *devs[NB_PORTS];
 
     trace_usb_ehci_reset();
 
+    /*
+     * Do the detach before touching portsc, so that it correctly gets send to
+     * us or to our companion based on PORTSC_POWNER before the reset.
+     */
+    for(i = 0; i < NB_PORTS; i++) {
+        devs[i] = s->ports[i].dev;
+        if (devs[i]) {
+            usb_attach(&s->ports[i], NULL);
+        }
+    }
+
     memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
 
     s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
@@ -783,10 +866,13 @@ static void ehci_reset(void *opaque)
     s->attach_poll_counter = 0;
 
     for(i = 0; i < NB_PORTS; i++) {
-        s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
-
-        if (s->ports[i].dev) {
-            usb_attach(&s->ports[i], s->ports[i].dev);
+        if (s->companion_ports[i]) {
+            s->portsc[i] = PORTSC_POWNER | PORTSC_PPOWER;
+        } else {
+            s->portsc[i] = PORTSC_PPOWER;
+        }
+        if (devs[i]) {
+            usb_attach(&s->ports[i], devs[i]);
         }
     }
     ehci_queues_rip_all(s);
@@ -836,43 +922,67 @@ static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
     exit(1);
 }
 
+static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
+{
+    USBDevice *dev = s->ports[port].dev;
+    uint32_t *portsc = &s->portsc[port];
+    uint32_t orig;
+
+    if (s->companion_ports[port] == NULL)
+        return;
+
+    owner = owner & PORTSC_POWNER;
+    orig  = *portsc & PORTSC_POWNER;
+
+    if (!(owner ^ orig)) {
+        return;
+    }
+
+    if (dev) {
+        usb_attach(&s->ports[port], NULL);
+    }
+
+    *portsc &= ~PORTSC_POWNER;
+    *portsc |= owner;
+
+    if (dev) {
+        usb_attach(&s->ports[port], dev);
+    }
+}
+
 static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
 {
     uint32_t *portsc = &s->portsc[port];
-    int rwc;
     USBDevice *dev = s->ports[port].dev;
 
-    rwc = val & PORTSC_RWC_MASK;
+    /* Clear rwc bits */
+    *portsc &= ~(val & PORTSC_RWC_MASK);
+    /* The guest may clear, but not set the PED bit */
+    *portsc &= val | ~PORTSC_PED;
+    /* POWNER is masked out by RO_MASK as it is RO when we've no companion */
+    handle_port_owner_write(s, port, val);
+    /* And finally apply RO_MASK */
     val &= PORTSC_RO_MASK;
 
-    // handle_read_write_clear(&val, portsc, PORTSC_PEDC | PORTSC_CSC);
-
-    *portsc &= ~rwc;
-
     if ((val & PORTSC_PRESET) && !(*portsc & PORTSC_PRESET)) {
         trace_usb_ehci_port_reset(port, 1);
     }
 
     if (!(val & PORTSC_PRESET) &&(*portsc & PORTSC_PRESET)) {
         trace_usb_ehci_port_reset(port, 0);
-        usb_attach(&s->ports[port], dev);
-
-        // TODO how to handle reset of ports with no device
         if (dev) {
+            usb_attach(&s->ports[port], dev);
             usb_send_msg(dev, USB_MSG_RESET);
-        }
-
-        if (s->ports[port].dev) {
             *portsc &= ~PORTSC_CSC;
         }
 
-        /*  Table 2.16 Set the enable bit(and enable bit change) to indicate
+        /*
+         *  Table 2.16 Set the enable bit(and enable bit change) to indicate
          *  to SW that this port has a high speed device attached
-         *
-         *  TODO - when to disable?
          */
-        val |= PORTSC_PED;
-        val |= PORTSC_PEDC;
+        if (dev && (dev->speedmask & USB_SPEED_MASK_HIGH)) {
+            val |= PORTSC_PED;
+        }
     }
 
     *portsc &= ~PORTSC_RO_MASK;
@@ -955,7 +1065,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
         val &= 0x1;
         if (val) {
             for(i = 0; i < NB_PORTS; i++)
-                s->portsc[i] &= ~PORTSC_POWNER;
+                handle_port_owner_write(s, i, 0);
         }
         break;
 
@@ -1057,68 +1167,75 @@ static int ehci_qh_do_overlay(EHCIQueue *q)
     return 0;
 }
 
-static int ehci_buffer_rw(EHCIQueue *q, int bytes, int rw)
+static int ehci_init_transfer(EHCIQueue *q)
 {
-    int bufpos = 0;
-    int cpage, offset;
-    uint32_t head;
-    uint32_t tail;
-
-
-    if (!bytes) {
-        return 0;
-    }
-
-    cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE);
-    if (cpage > 4) {
-        fprintf(stderr, "cpage out of range (%d)\n", cpage);
-        return USB_RET_PROCERR;
-    }
+    uint32_t cpage, offset, bytes, plen;
+    target_phys_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;
+    qemu_sglist_init(&q->sgl, 5);
 
-    do {
-        /* start and end of this page */
-        head = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
-        tail = head + ~QTD_BUFPTR_MASK + 1;
-        /* add offset into page */
-        head |= offset;
-
-        if (bytes <= (tail - head)) {
-            tail = head + bytes;
+    while (bytes > 0) {
+        if (cpage > 4) {
+            fprintf(stderr, "cpage out of range (%d)\n", cpage);
+            return USB_RET_PROCERR;
         }
 
-        trace_usb_ehci_data(rw, cpage, offset, head, tail-head, bufpos);
-        cpu_physical_memory_rw(head, q->buffer + bufpos, tail - head, rw);
-
-        bufpos += (tail - head);
-        offset += (tail - head);
-        bytes -= (tail - head);
-
-        if (bytes > 0) {
-            cpage++;
+        page  = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK;
+        page += offset;
+        plen  = bytes;
+        if (plen > 4096 - offset) {
+            plen = 4096 - offset;
             offset = 0;
+            cpage++;
         }
-    } while (bytes > 0);
 
-    /* save cpage */
-    set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
+        qemu_sglist_add(&q->sgl, page, plen);
+        bytes -= plen;
+    }
+    return 0;
+}
 
-    /* save offset into cpage */
-    q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
-    q->qh.bufptr[0] |= offset;
+static void ehci_finish_transfer(EHCIQueue *q, int status)
+{
+    uint32_t cpage, offset;
 
-    return 0;
+    qemu_sglist_destroy(&q->sgl);
+
+    if (status > 0) {
+        /* update cpage & offset */
+        cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
+        offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
+
+        offset += status;
+        cpage  += offset >> QTD_BUFPTR_SH;
+        offset &= ~QTD_BUFPTR_MASK;
+
+        set_field(&q->qh.token, cpage, QTD_TOKEN_CPAGE);
+        q->qh.bufptr[0] &= QTD_BUFPTR_MASK;
+        q->qh.bufptr[0] |= offset;
+    }
 }
 
-static void ehci_async_complete_packet(USBDevice *dev, USBPacket *packet)
+static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
 {
-    EHCIQueue *q = container_of(packet, EHCIQueue, packet);
+    EHCIQueue *q;
+    EHCIState *s = port->opaque;
+    uint32_t portsc = s->portsc[port->index];
 
+    if (portsc & PORTSC_POWNER) {
+        USBPort *companion = s->companion_ports[port->index];
+        companion->ops->complete(companion, 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->len;
+    q->usb_status = packet->result;
 }
 
 static void ehci_execute_complete(EHCIQueue *q)
@@ -1178,10 +1295,6 @@ err:
         }
 
         if (q->tbytes && q->pid == USB_TOKEN_IN) {
-            if (ehci_buffer_rw(q, q->usb_status, 1) != 0) {
-                q->usb_status = USB_RET_PROCERR;
-                return;
-            }
             q->tbytes -= q->usb_status;
         } else {
             q->tbytes = 0;
@@ -1190,6 +1303,8 @@ err:
         DPRINTF("updating tbytes to %d\n", q->tbytes);
         set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES);
     }
+    ehci_finish_transfer(q, q->usb_status);
+    usb_packet_unmap(&q->packet);
 
     q->qh.token ^= QTD_TOKEN_DTOGGLE;
     q->qh.token &= ~QTD_TOKEN_ACTIVE;
@@ -1229,8 +1344,7 @@ static int ehci_execute(EHCIQueue *q)
         default: fprintf(stderr, "bad token\n"); break;
     }
 
-    if ((q->tbytes && q->pid != USB_TOKEN_IN) &&
-        (ehci_buffer_rw(q, q->tbytes, 0) != 0)) {
+    if (ehci_init_transfer(q) != 0) {
         return USB_RET_PROCERR;
     }
 
@@ -1239,30 +1353,26 @@ static int ehci_execute(EHCIQueue *q)
 
     ret = USB_RET_NODEV;
 
+    usb_packet_setup(&q->packet, q->pid, devadr, endp);
+    usb_packet_map(&q->packet, &q->sgl);
+
     // TO-DO: associating device with ehci port
     for(i = 0; i < NB_PORTS; i++) {
         port = &q->ehci->ports[i];
         dev = port->dev;
 
-        // TODO sometime we will also need to check if we are the port owner
-
         if (!(q->ehci->portsc[i] &(PORTSC_CONNECT))) {
             DPRINTF("Port %d, no exec, not connected(%08X)\n",
                     i, q->ehci->portsc[i]);
             continue;
         }
 
-        q->packet.pid = q->pid;
-        q->packet.devaddr = devadr;
-        q->packet.devep = endp;
-        q->packet.data = q->buffer;
-        q->packet.len = q->tbytes;
-
         ret = usb_handle_packet(dev, &q->packet);
 
-        DPRINTF("submit: qh %x next %x qtd %x pid %x len %d (total %d) endp %x ret %d\n",
+        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,
-                q->packet.len, q->tbytes, endp, ret);
+                q->packet.iov.size, q->tbytes, endp, ret);
 
         if (ret != USB_RET_NODEV) {
             break;
@@ -1286,7 +1396,7 @@ static int ehci_process_itd(EHCIState *ehci,
     USBPort *port;
     USBDevice *dev;
     int ret;
-    uint32_t i, j, len, len1, len2, pid, dir, devaddr, endp;
+    uint32_t i, j, len, pid, dir, devaddr, endp;
     uint32_t pg, off, ptr1, ptr2, max, mult;
 
     dir =(itd->bufptr[1] & ITD_BUFPTR_DIRECTION);
@@ -1311,45 +1421,31 @@ static int ehci_process_itd(EHCIState *ehci,
                 return USB_RET_PROCERR;
             }
 
+            qemu_sglist_init(&ehci->isgl, 2);
             if (off + len > 4096) {
                 /* transfer crosses page border */
-                len2 = off + len - 4096;
-                len1 = len - len2;
+                uint32_t len2 = off + len - 4096;
+                uint32_t len1 = len - len2;
+                qemu_sglist_add(&ehci->isgl, ptr1 + off, len1);
+                qemu_sglist_add(&ehci->isgl, ptr2, len2);
             } else {
-                len1 = len;
-                len2 = 0;
+                qemu_sglist_add(&ehci->isgl, ptr1 + off, len);
             }
 
-            if (!dir) {
-                pid = USB_TOKEN_OUT;
-                trace_usb_ehci_data(0, pg, off, ptr1 + off, len1, 0);
-                cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 0);
-                if (len2) {
-                    trace_usb_ehci_data(0, pg+1, 0, ptr2, len2, len1);
-                    cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 0);
-                }
-            } else {
-                pid = USB_TOKEN_IN;
-            }
+            pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT;
 
-            ret = USB_RET_NODEV;
+            usb_packet_setup(&ehci->ipacket, pid, devaddr, endp);
+            usb_packet_map(&ehci->ipacket, &ehci->isgl);
 
+            ret = USB_RET_NODEV;
             for (j = 0; j < NB_PORTS; j++) {
                 port = &ehci->ports[j];
                 dev = port->dev;
 
-                // TODO sometime we will also need to check if we are the port owner
-
                 if (!(ehci->portsc[j] &(PORTSC_CONNECT))) {
                     continue;
                 }
 
-                ehci->ipacket.pid = pid;
-                ehci->ipacket.devaddr = devaddr;
-                ehci->ipacket.devep = endp;
-                ehci->ipacket.data = ehci->ibuffer;
-                ehci->ipacket.len = len;
-
                 ret = usb_handle_packet(dev, &ehci->ipacket);
 
                 if (ret != USB_RET_NODEV) {
@@ -1357,6 +1453,9 @@ static int ehci_process_itd(EHCIState *ehci,
                 }
             }
 
+            usb_packet_unmap(&ehci->ipacket);
+            qemu_sglist_destroy(&ehci->isgl);
+
 #if 0
             /*  In isoch, there is no facility to indicate a NAK so let's
              *  instead just complete a zero-byte transaction.  Setting
@@ -1394,20 +1493,6 @@ static int ehci_process_itd(EHCIState *ehci,
                     set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
                 } else {
                     /* IN */
-                    if (len1 > ret) {
-                        len1 = ret;
-                    }
-                    if (len2 > ret - len1) {
-                        len2 = ret - len1;
-                    }
-                    if (len1) {
-                        trace_usb_ehci_data(1, pg, off, ptr1 + off, len1, 0);
-                        cpu_physical_memory_rw(ptr1 + off, &ehci->ibuffer[0], len1, 1);
-                    }
-                    if (len2) {
-                        trace_usb_ehci_data(1, pg+1, 0, ptr2, len2, len1);
-                        cpu_physical_memory_rw(ptr2, &ehci->ibuffer[len1], len2, 1);
-                    }
                     set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
                 }
 
@@ -2117,38 +2202,55 @@ static void ehci_map(PCIDevice *pci_dev, int region_num,
     cpu_register_physical_memory(addr, size, s->mem);
 }
 
-static void ehci_device_destroy(USBBus *bus, USBDevice *dev)
-{
-    EHCIState *s = container_of(bus, EHCIState, bus);
-
-    ehci_queues_rip_device(s, dev);
-}
-
 static int usb_ehci_initfn(PCIDevice *dev);
 
 static USBPortOps ehci_port_ops = {
     .attach = ehci_attach,
     .detach = ehci_detach,
+    .child_detach = ehci_child_detach,
+    .wakeup = ehci_wakeup,
     .complete = ehci_async_complete_packet,
 };
 
 static USBBusOps ehci_bus_ops = {
-    .device_destroy = ehci_device_destroy,
+    .register_companion = ehci_register_companion,
+};
+
+static const VMStateDescription vmstate_ehci = {
+    .name = "ehci",
+    .unmigratable = 1,
 };
 
-static PCIDeviceInfo ehci_info = {
-    .qdev.name    = "usb-ehci",
-    .qdev.size    = sizeof(EHCIState),
-    .init         = usb_ehci_initfn,
-    .vendor_id    = PCI_VENDOR_ID_INTEL,
-    .device_id    = PCI_DEVICE_ID_INTEL_82801D,
-    .revision     = 0x10,
-    .class_id     = PCI_CLASS_SERIAL_USB,
-    .qdev.props   = (Property[]) {
-        DEFINE_PROP_UINT32("freq",      EHCIState, freq, FRAME_TIMER_FREQ),
-        DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
-        DEFINE_PROP_END_OF_LIST(),
-    },
+static Property ehci_properties[] = {
+    DEFINE_PROP_UINT32("freq",      EHCIState, freq, FRAME_TIMER_FREQ),
+    DEFINE_PROP_UINT32("maxframes", EHCIState, maxframes, 128),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static PCIDeviceInfo ehci_info[] = {
+    {
+        .qdev.name    = "usb-ehci",
+        .qdev.size    = sizeof(EHCIState),
+        .qdev.vmsd    = &vmstate_ehci,
+        .init         = usb_ehci_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82801D, /* ich4 */
+        .revision     = 0x10,
+        .class_id     = PCI_CLASS_SERIAL_USB,
+        .qdev.props   = ehci_properties,
+    },{
+        .qdev.name    = "ich9-usb-ehci1",
+        .qdev.size    = sizeof(EHCIState),
+        .qdev.vmsd    = &vmstate_ehci,
+        .init         = usb_ehci_initfn,
+        .vendor_id    = PCI_VENDOR_ID_INTEL,
+        .device_id    = PCI_DEVICE_ID_INTEL_82801I_EHCI1,
+        .revision     = 0x03,
+        .class_id     = PCI_CLASS_SERIAL_USB,
+        .qdev.props   = ehci_properties,
+    },{
+        /* end of list */
+    }
 };
 
 static int usb_ehci_initfn(PCIDevice *dev)
@@ -2206,7 +2308,6 @@ static int usb_ehci_initfn(PCIDevice *dev)
     for(i = 0; i < NB_PORTS; i++) {
         usb_register_port(&s->bus, &s->ports[i], s, i, &ehci_port_ops,
                           USB_SPEED_MASK_HIGH);
-        usb_port_location(&s->ports[i], NULL, i+1);
         s->ports[i].dev = 0;
     }
 
@@ -2228,7 +2329,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
 
 static void ehci_register(void)
 {
-    pci_qdev_register(&ehci_info);
+    pci_qdev_register_many(ehci_info);
 }
 device_init(ehci_register);