]> git.proxmox.com Git - mirror_qemu.git/blobdiff - hw/usb-uhci.c
eepro100: fix mapping of flash memory
[mirror_qemu.git] / hw / usb-uhci.c
index 8babb0265e4ef239967383c59a3c33c6e2c124b7..7c45d7f5122246fc3822fd8d761f0fe45ff9099e 100644 (file)
@@ -29,6 +29,7 @@
 #include "usb.h"
 #include "pci.h"
 #include "qemu-timer.h"
+#include "usb-uhci.h"
 
 //#define DEBUG
 //#define DEBUG_DUMP_DATA
@@ -70,7 +71,7 @@
 #define NB_PORTS 2
 
 #ifdef DEBUG
-#define dprintf printf
+#define DPRINTF printf
 
 static const char *pid2str(int pid)
 {
@@ -83,7 +84,7 @@ static const char *pid2str(int pid)
 }
 
 #else
-#define dprintf(...)
+#define DPRINTF(...)
 #endif
 
 #ifdef DEBUG_DUMP_DATA
@@ -111,6 +112,7 @@ typedef struct UHCIAsync {
     uint32_t  td;
     uint32_t  token;
     int8_t    valid;
+    uint8_t   isoc;
     uint8_t   done;
     uint8_t   buffer[2048];
 } UHCIAsync;
@@ -130,6 +132,7 @@ typedef struct UHCIState {
     uint32_t fl_base_addr; /* frame list base address */
     uint8_t sof_timing;
     uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */
+    int64_t expire_time;
     QEMUTimer *frame_timer;
     UHCIPort ports[NB_PORTS];
 
@@ -163,6 +166,7 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s)
     async->td    = 0;
     async->token = 0;
     async->done  = 0;
+    async->isoc  = 0;
     async->next  = NULL;
 
     return async;
@@ -197,7 +201,7 @@ static void uhci_async_unlink(UHCIState *s, UHCIAsync *async)
 
 static void uhci_async_cancel(UHCIState *s, UHCIAsync *async)
 {
-    dprintf("uhci: cancel td 0x%x token 0x%x done %u\n",
+    DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n",
            async->td, async->token, async->done);
 
     if (!async->done)
@@ -328,7 +332,7 @@ static void uhci_reset(void *opaque)
     int i;
     UHCIPort *port;
 
-    dprintf("uhci: full reset\n");
+    DPRINTF("uhci: full reset\n");
 
     pci_conf = s->dev.config;
 
@@ -426,7 +430,7 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
     UHCIState *s = opaque;
 
     addr &= 0x1f;
-    dprintf("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
+    DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val);
 
     switch(addr) {
     case 0x00:
@@ -537,7 +541,7 @@ static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr)
         break;
     }
 
-    dprintf("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
+    DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val);
 
     return val;
 }
@@ -547,7 +551,7 @@ static void uhci_ioport_writel(void *opaque, uint32_t addr, uint32_t val)
     UHCIState *s = opaque;
 
     addr &= 0x1f;
-    dprintf("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
+    DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val);
 
     switch(addr) {
     case 0x08:
@@ -638,7 +642,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
 {
     int i, ret;
 
-    dprintf("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n",
+    DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n",
            pid2str(p->pid), p->devaddr, p->devep, p->len);
     if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP)
         dump_data(p->data, p->len);
@@ -652,7 +656,7 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p)
             ret = dev->info->handle_packet(dev, p);
     }
 
-    dprintf("uhci: packet exit. ret %d len %d\n", ret, p->len);
+    DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len);
     if (p->pid == USB_TOKEN_IN && ret > 0)
         dump_data(p->data, ret);
 
@@ -676,9 +680,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
 
     ret = async->packet.len;
 
-    if (td->ctrl & TD_CTRL_IOC)
-        *int_mask |= 0x01;
-
     if (td->ctrl & TD_CTRL_IOS)
         td->ctrl &= ~TD_CTRL_ACTIVE;
 
@@ -692,6 +693,8 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
        here.  The docs are somewhat unclear, but win2k relies on this
        behavior.  */
     td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK);
+    if (td->ctrl & TD_CTRL_IOC)
+        *int_mask |= 0x01;
 
     if (pid == USB_TOKEN_IN) {
         if (len > max_len) {
@@ -708,7 +711,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
         if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
             *int_mask |= 0x02;
             /* short packet: do not update QH */
-            dprintf("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
+            DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token);
             return 1;
         }
     }
@@ -749,6 +752,8 @@ out:
         if (err == 0) {
             td->ctrl &= ~TD_CTRL_ACTIVE;
             s->status |= UHCI_STS_USBERR;
+            if (td->ctrl & TD_CTRL_IOC)
+                *int_mask |= 0x01;
             uhci_update_irq(s);
         }
     }
@@ -761,13 +766,25 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
 {
     UHCIAsync *async;
     int len = 0, max_len;
-    uint8_t pid;
+    uint8_t pid, isoc;
+    uint32_t token;
 
     /* Is active ? */
     if (!(td->ctrl & TD_CTRL_ACTIVE))
         return 1;
 
-    async = uhci_async_find_td(s, addr, td->token);
+    /* token field is not unique for isochronous requests,
+     * so use the destination buffer 
+     */
+    if (td->ctrl & TD_CTRL_IOS) {
+        token = td->buffer;
+        isoc = 1;
+    } else {
+        token = td->token;
+        isoc = 0;
+    }
+
+    async = uhci_async_find_td(s, addr, token);
     if (async) {
         /* Already submitted */
         async->valid = 32;
@@ -784,9 +801,13 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in
     if (!async)
         return 1;
 
-    async->valid = 10;
+    /* valid needs to be large enough to handle 10 frame delay
+     * for initial isochronous requests
+     */
+    async->valid = 32;
     async->td    = addr;
-    async->token = td->token;
+    async->token = token;
+    async->isoc  = isoc;
 
     max_len = ((td->token >> 21) + 1) & 0x7ff;
     pid = td->token & 0xff;
@@ -838,11 +859,33 @@ static void uhci_async_complete(USBPacket *packet, void *opaque)
     UHCIState *s = opaque;
     UHCIAsync *async = (UHCIAsync *) packet;
 
-    dprintf("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
+    DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token);
 
-    async->done = 1;
+    if (async->isoc) {
+        UHCI_TD td;
+        uint32_t link = async->td;
+        uint32_t int_mask = 0, val;
+        int len;
+        cpu_physical_memory_read(link & ~0xf, (uint8_t *) &td, sizeof(td));
+        le32_to_cpus(&td.link);
+        le32_to_cpus(&td.ctrl);
+        le32_to_cpus(&td.token);
+        le32_to_cpus(&td.buffer);
 
-    uhci_process_frame(s);
+        uhci_async_unlink(s, async);
+        len = uhci_complete_td(s, &td, async, &int_mask);
+        s->pending_int_mask |= int_mask;
+
+        /* update the status bits of the TD */
+        val = cpu_to_le32(td.ctrl);
+        cpu_physical_memory_write((link & ~0xf) + 4,
+                                  (const uint8_t *)&val, sizeof(val));
+        uhci_async_free(s, async);
+    } else {
+        async->done = 1;
+        uhci_process_frame(s);
+    }
 }
 
 static int is_valid(uint32_t link)
@@ -898,7 +941,7 @@ static void uhci_process_frame(UHCIState *s)
 
     frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2);
 
-    dprintf("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
+    DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr);
 
     cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4);
     le32_to_cpus(&link);
@@ -920,7 +963,7 @@ static void uhci_process_frame(UHCIState *s)
                  * are already done, and async completion handler will re-process 
                  * the frame when something is ready.
                  */
-                dprintf("uhci: detected loop. qh 0x%x\n", link);
+                DPRINTF("uhci: detected loop. qh 0x%x\n", link);
                 break;
             }
 
@@ -928,7 +971,7 @@ static void uhci_process_frame(UHCIState *s)
             le32_to_cpus(&qh.link);
             le32_to_cpus(&qh.el_link);
 
-            dprintf("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
+            DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n",
                     link, qh.link, qh.el_link);
 
             if (!is_valid(qh.el_link)) {
@@ -950,7 +993,7 @@ static void uhci_process_frame(UHCIState *s)
         le32_to_cpus(&td.token);
         le32_to_cpus(&td.buffer);
 
-        dprintf("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
+        DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
                 link, td.link, td.ctrl, td.token, curr_qh);
 
         old_td_ctrl = td.ctrl;
@@ -968,7 +1011,7 @@ static void uhci_process_frame(UHCIState *s)
         }
 
         if (ret == 2 || ret == 1) {
-            dprintf("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
+            DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n",
                     link, ret == 2 ? "pend" : "skip",
                     td.link, td.ctrl, td.token, curr_qh);
 
@@ -978,7 +1021,7 @@ static void uhci_process_frame(UHCIState *s)
 
         /* completed TD */
 
-        dprintf("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
+        DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", 
                 link, td.link, td.ctrl, td.token, curr_qh);
 
         link = td.link;
@@ -993,7 +1036,7 @@ static void uhci_process_frame(UHCIState *s)
             if (!depth_first(link)) {
                /* done with this QH */
 
-               dprintf("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
+               DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n",
                        curr_qh, qh.link, qh.el_link);
 
                curr_qh = 0;
@@ -1004,13 +1047,15 @@ static void uhci_process_frame(UHCIState *s)
         /* go to the next entry */
     }
 
-    s->pending_int_mask = int_mask;
+    s->pending_int_mask |= int_mask;
 }
 
 static void uhci_frame_timer(void *opaque)
 {
     UHCIState *s = opaque;
-    int64_t expire_time;
+
+    /* prepare the timer for the next frame */
+    s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ);
 
     if (!(s->cmd & UHCI_CMD_RS)) {
         /* Full stop */
@@ -1018,7 +1063,7 @@ static void uhci_frame_timer(void *opaque)
         /* set hchalted bit in status - UHCI11D 2.1.2 */
         s->status |= UHCI_STS_HCHALTED;
 
-        dprintf("uhci: halted\n");
+        DPRINTF("uhci: halted\n");
         return;
     }
 
@@ -1028,11 +1073,12 @@ static void uhci_frame_timer(void *opaque)
         s->status  |= UHCI_STS_USBINT;
         uhci_update_irq(s);
     }
+    s->pending_int_mask = 0;
 
     /* Start new frame */
     s->frnum = (s->frnum + 1) & 0x7ff;
 
-    dprintf("uhci: new frame #%u\n" , s->frnum);
+    DPRINTF("uhci: new frame #%u\n" , s->frnum);
 
     uhci_async_validate_begin(s);
 
@@ -1040,10 +1086,7 @@ static void uhci_frame_timer(void *opaque)
 
     uhci_async_validate_end(s);
 
-    /* prepare the timer for the next frame */
-    expire_time = qemu_get_clock(vm_clock) +
-        (get_ticks_per_sec() / FRAME_TIMER_FREQ);
-    qemu_mod_timer(s->frame_timer, expire_time);
+    qemu_mod_timer(s->frame_timer, s->expire_time);
 }
 
 static void uhci_map(PCIDevice *pci_dev, int region_num,
@@ -1064,11 +1107,12 @@ static int usb_uhci_common_initfn(UHCIState *s)
     uint8_t *pci_conf = s->dev.config;
     int i;
 
-    pci_conf[0x08] = 0x01; // revision number
-    pci_conf[0x09] = 0x00;
+    pci_conf[PCI_REVISION_ID] = 0x01; // revision number
+    pci_conf[PCI_CLASS_PROG] = 0x00;
     pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB);
     pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type
-    pci_conf[0x3d] = 4; // interrupt pin 3
+    /* TODO: reset value should be 0. */
+    pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3
     pci_conf[0x60] = 0x10; // release number
 
     usb_bus_new(&s->bus, &s->dev.qdev);
@@ -1076,6 +1120,8 @@ static int usb_uhci_common_initfn(UHCIState *s)
         usb_register_port(&s->bus, &s->ports[i].port, s, i, uhci_attach);
     }
     s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s);
+    s->expire_time = qemu_get_clock(vm_clock) +
+        (get_ticks_per_sec() / FRAME_TIMER_FREQ);
     s->num_ports_vmstate = NB_PORTS;
 
     qemu_register_reset(uhci_reset, s);
@@ -1085,7 +1131,6 @@ static int usb_uhci_common_initfn(UHCIState *s)
     pci_register_bar(&s->dev, 4, 0x20,
                            PCI_BASE_ADDRESS_SPACE_IO, uhci_map);
 
-    vmstate_register(0, &vmstate_uhci, s);
     return 0;
 }
 
@@ -1111,12 +1156,14 @@ static int usb_uhci_piix4_initfn(PCIDevice *dev)
 
 static PCIDeviceInfo uhci_info[] = {
     {
-        .qdev.name    = "PIIX3 USB-UHCI",
+        .qdev.name    = "piix3-usb-uhci",
         .qdev.size    = sizeof(UHCIState),
+        .qdev.vmsd    = &vmstate_uhci,
         .init         = usb_uhci_piix3_initfn,
     },{
-        .qdev.name    = "PIIX4 USB-UHCI",
+        .qdev.name    = "piix4-usb-uhci",
         .qdev.size    = sizeof(UHCIState),
+        .qdev.vmsd    = &vmstate_uhci,
         .init         = usb_uhci_piix4_initfn,
     },{
         /* end of list */
@@ -1131,10 +1178,10 @@ device_init(uhci_register);
 
 void usb_uhci_piix3_init(PCIBus *bus, int devfn)
 {
-    pci_create_simple(bus, devfn, "PIIX3 USB-UHCI");
+    pci_create_simple(bus, devfn, "piix3-usb-uhci");
 }
 
 void usb_uhci_piix4_init(PCIBus *bus, int devfn)
 {
-    pci_create_simple(bus, devfn, "PIIX4 USB-UHCI");
+    pci_create_simple(bus, devfn, "piix4-usb-uhci");
 }