X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Fusb-uhci.c;h=95dd4a0cd232b209079ecef1c737998ff095dd8b;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=42417fedae895787fd1a5d6fb33def17d5c8eabb;hpb=bb36d4708bc93874af95db54da2a9cee0d30a84d;p=qemu diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 42417fe..95dd4a0 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -1,8 +1,8 @@ /* * USB UHCI controller emulation - * + * * Copyright (c) 2005 Fabrice Bellard - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -25,7 +25,10 @@ //#define DEBUG //#define DEBUG_PACKET +//#define DEBUG_ISOCH +#define UHCI_CMD_FGR (1 << 4) +#define UHCI_CMD_EGSM (1 << 3) #define UHCI_CMD_GRESET (1 << 2) #define UHCI_CMD_HCRESET (1 << 1) #define UHCI_CMD_RS (1 << 0) @@ -63,7 +66,6 @@ typedef struct UHCIPort { USBPort port; uint16_t ctrl; - USBDevice *dev; /* connected device */ } UHCIPort; typedef struct UHCIState { @@ -77,6 +79,19 @@ typedef struct UHCIState { uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ QEMUTimer *frame_timer; UHCIPort ports[NB_PORTS]; + + /* Interrupts that should be raised at the end of the current frame. */ + uint32_t pending_int_mask; + /* For simplicity of implementation we only allow a single pending USB + request. This means all usb traffic on this controller is effectively + suspended until that transfer completes. When the transfer completes + the next transfer from that queue will be processed. However + other queues will not be processed until the next frame. The solution + is to allow multiple pending requests. */ + uint32_t async_qh; + uint32_t async_frame_addr; + USBPacket usb_packet; + uint8_t usb_buf[2048]; } UHCIState; typedef struct UHCI_TD { @@ -106,7 +121,7 @@ static void uhci_update_irq(UHCIState *s) } else { level = 0; } - pci_set_irq(&s->dev, 0, level); + qemu_set_irq(s->dev.irq[3], level); } static void uhci_reset(UHCIState *s) @@ -128,15 +143,67 @@ static void uhci_reset(UHCIState *s) for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; port->ctrl = 0x0080; - if (port->dev) - uhci_attach(&port->port, port->dev); + if (port->port.dev) + uhci_attach(&port->port, port->port.dev); } } +static void uhci_save(QEMUFile *f, void *opaque) +{ + UHCIState *s = opaque; + uint8_t num_ports = NB_PORTS; + int i; + + pci_device_save(&s->dev, f); + + qemu_put_8s(f, &num_ports); + for (i = 0; i < num_ports; ++i) + qemu_put_be16s(f, &s->ports[i].ctrl); + qemu_put_be16s(f, &s->cmd); + qemu_put_be16s(f, &s->status); + qemu_put_be16s(f, &s->intr); + qemu_put_be16s(f, &s->frnum); + qemu_put_be32s(f, &s->fl_base_addr); + qemu_put_8s(f, &s->sof_timing); + qemu_put_8s(f, &s->status2); + qemu_put_timer(f, s->frame_timer); +} + +static int uhci_load(QEMUFile *f, void *opaque, int version_id) +{ + UHCIState *s = opaque; + uint8_t num_ports; + int i, ret; + + if (version_id > 1) + return -EINVAL; + + ret = pci_device_load(&s->dev, f); + if (ret < 0) + return ret; + + qemu_get_8s(f, &num_ports); + if (num_ports != NB_PORTS) + return -EINVAL; + + for (i = 0; i < num_ports; ++i) + qemu_get_be16s(f, &s->ports[i].ctrl); + qemu_get_be16s(f, &s->cmd); + qemu_get_be16s(f, &s->status); + qemu_get_be16s(f, &s->intr); + qemu_get_be16s(f, &s->frnum); + qemu_get_be32s(f, &s->fl_base_addr); + qemu_get_8s(f, &s->sof_timing); + qemu_get_8s(f, &s->status2); + qemu_get_timer(f, s->frame_timer); + + return 0; +} + static void uhci_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) { UHCIState *s = opaque; - + addr &= 0x1f; switch(addr) { case 0x0c: @@ -154,6 +221,7 @@ static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr) switch(addr) { case 0x0c: val = s->sof_timing; + break; default: val = 0xff; break; @@ -164,7 +232,7 @@ static uint32_t uhci_ioport_readb(void *opaque, uint32_t addr) static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) { UHCIState *s = opaque; - + addr &= 0x1f; #ifdef DEBUG printf("uhci writew port=0x%04x val=0x%04x\n", addr, val); @@ -174,6 +242,9 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) if ((val & UHCI_CMD_RS) && !(s->cmd & UHCI_CMD_RS)) { /* start frame processing */ qemu_mod_timer(s->frame_timer, qemu_get_clock(vm_clock)); + s->status &= ~UHCI_STS_HCHALTED; + } else if (!(val & UHCI_CMD_RS)) { + s->status |= UHCI_STS_HCHALTED; } if (val & UHCI_CMD_GRESET) { UHCIPort *port; @@ -183,16 +254,15 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) /* send reset on the USB bus */ for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; - dev = port->dev; + dev = port->port.dev; if (dev) { - dev->handle_packet(dev, - USB_MSG_RESET, 0, 0, NULL, 0); + usb_send_msg(dev, USB_MSG_RESET); } } uhci_reset(s); return; } - if (val & UHCI_CMD_GRESET) { + if (val & UHCI_CMD_HCRESET) { uhci_reset(s); return; } @@ -224,13 +294,12 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val) if (n >= NB_PORTS) return; port = &s->ports[n]; - dev = port->dev; + dev = port->port.dev; if (dev) { /* port reset */ - if ( (val & UHCI_PORT_RESET) && + if ( (val & UHCI_PORT_RESET) && !(port->ctrl & UHCI_PORT_RESET) ) { - dev->handle_packet(dev, - USB_MSG_RESET, 0, 0, NULL, 0); + usb_send_msg(dev, USB_MSG_RESET); } } port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb); @@ -265,7 +334,7 @@ static uint32_t uhci_ioport_readw(void *opaque, uint32_t addr) UHCIPort *port; int n; n = (addr >> 1) & 7; - if (n >= NB_PORTS) + if (n >= NB_PORTS) goto read_default; port = &s->ports[n]; val = port->ctrl; @@ -314,51 +383,68 @@ static uint32_t uhci_ioport_readl(void *opaque, uint32_t addr) return val; } +/* signal resume if controller suspended */ +static void uhci_resume (void *opaque) +{ + UHCIState *s = (UHCIState *)opaque; + + if (!s) + return; + + if (s->cmd & UHCI_CMD_EGSM) { + s->cmd |= UHCI_CMD_FGR; + s->status |= UHCI_STS_RD; + uhci_update_irq(s); + } +} + static void uhci_attach(USBPort *port1, USBDevice *dev) { UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; if (dev) { - if (port->dev) { + if (port->port.dev) { usb_attach(port1, NULL); } /* set connect status */ - if (!(port->ctrl & UHCI_PORT_CCS)) { - port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; - } + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + /* update speed */ if (dev->speed == USB_SPEED_LOW) port->ctrl |= UHCI_PORT_LSDA; else port->ctrl &= ~UHCI_PORT_LSDA; - port->dev = dev; + + uhci_resume(s); + + port->port.dev = dev; /* send the attach message */ - dev->handle_packet(dev, - USB_MSG_ATTACH, 0, 0, NULL, 0); + usb_send_msg(dev, USB_MSG_ATTACH); } else { /* set connect status */ - if (!(port->ctrl & UHCI_PORT_CCS)) { - port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + if (port->ctrl & UHCI_PORT_CCS) { + port->ctrl &= ~UHCI_PORT_CCS; + port->ctrl |= UHCI_PORT_CSC; } /* disable port */ if (port->ctrl & UHCI_PORT_EN) { port->ctrl &= ~UHCI_PORT_EN; port->ctrl |= UHCI_PORT_ENC; } - dev = port->dev; + + uhci_resume(s); + + dev = port->port.dev; if (dev) { /* send the detach message */ - dev->handle_packet(dev, - USB_MSG_DETACH, 0, 0, NULL, 0); + usb_send_msg(dev, USB_MSG_DETACH); } - port->dev = NULL; + port->port.dev = NULL; } } -static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, - uint8_t devaddr, uint8_t devep, - uint8_t *data, int len) +static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) { UHCIPort *port; USBDevice *dev; @@ -367,18 +453,18 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, #ifdef DEBUG_PACKET { const char *pidstr; - switch(pid) { + switch(p->pid) { case USB_TOKEN_SETUP: pidstr = "SETUP"; break; case USB_TOKEN_IN: pidstr = "IN"; break; case USB_TOKEN_OUT: pidstr = "OUT"; break; default: pidstr = "?"; break; } printf("frame %d: pid=%s addr=0x%02x ep=%d len=%d\n", - s->frnum, pidstr, devaddr, devep, len); - if (pid != USB_TOKEN_IN) { + s->frnum, pidstr, p->devaddr, p->devep, p->len); + if (p->pid != USB_TOKEN_IN) { printf(" data_out="); - for(i = 0; i < len; i++) { - printf(" %02x", data[i]); + for(i = 0; i < p->len; i++) { + printf(" %02x", p->data[i]); } printf("\n"); } @@ -386,19 +472,19 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, #endif for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; - dev = port->dev; + dev = port->port.dev; if (dev && (port->ctrl & UHCI_PORT_EN)) { - ret = dev->handle_packet(dev, pid, - devaddr, devep, - data, len); + ret = dev->handle_packet(dev, p); if (ret != USB_RET_NODEV) { #ifdef DEBUG_PACKET - { + if (ret == USB_RET_ASYNC) { + printf("usb-uhci: Async packet\n"); + } else { printf(" ret=%d ", ret); - if (pid == USB_TOKEN_IN && ret > 0) { + if (p->pid == USB_TOKEN_IN && ret > 0) { printf("data_in="); for(i = 0; i < ret; i++) { - printf(" %02x", data[i]); + printf(" %02x", p->data[i]); } } printf("\n"); @@ -411,41 +497,32 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, return USB_RET_NODEV; } +static void uhci_async_complete_packet(USBPacket * packet, void *opaque); + /* return -1 if fatal error (frame must be stopped) 0 if TD successful 1 if TD unsuccessful or inactive */ -static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) +static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask, + int completion) { uint8_t pid; - uint8_t buf[1280]; - int len, max_len, err, ret; + int len = 0, max_len, err, ret = 0; + /* ??? This is wrong for async completion. */ if (td->ctrl & TD_CTRL_IOC) { *int_mask |= 0x01; } - + if (!(td->ctrl & TD_CTRL_ACTIVE)) return 1; /* TD is active */ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - switch(pid) { - case USB_TOKEN_OUT: - case USB_TOKEN_SETUP: - cpu_physical_memory_read(td->buffer, buf, max_len); - ret = uhci_broadcast_packet(s, pid, - (td->token >> 8) & 0x7f, - (td->token >> 15) & 0xf, - buf, max_len); - len = max_len; - break; - case USB_TOKEN_IN: - ret = uhci_broadcast_packet(s, pid, - (td->token >> 8) & 0x7f, - (td->token >> 15) & 0xf, - buf, max_len); + + if (completion && (s->async_qh || s->async_frame_addr)) { + ret = s->usb_packet.len; if (ret >= 0) { len = ret; if (len > max_len) { @@ -454,24 +531,64 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) } if (len > 0) { /* write the data back */ - cpu_physical_memory_write(td->buffer, buf, len); + cpu_physical_memory_write(td->buffer, s->usb_buf, len); } } else { len = 0; } - break; - default: - /* invalid pid : frame interrupted */ - s->status |= UHCI_STS_HCPERR; - uhci_update_irq(s); - return -1; + s->async_qh = 0; + s->async_frame_addr = 0; + } else if (!completion) { + s->usb_packet.pid = pid; + s->usb_packet.devaddr = (td->token >> 8) & 0x7f; + s->usb_packet.devep = (td->token >> 15) & 0xf; + s->usb_packet.data = s->usb_buf; + s->usb_packet.len = max_len; + s->usb_packet.complete_cb = uhci_async_complete_packet; + s->usb_packet.complete_opaque = s; + switch(pid) { + case USB_TOKEN_OUT: + case USB_TOKEN_SETUP: + cpu_physical_memory_read(td->buffer, s->usb_buf, max_len); + ret = uhci_broadcast_packet(s, &s->usb_packet); + len = max_len; + break; + case USB_TOKEN_IN: + ret = uhci_broadcast_packet(s, &s->usb_packet); + if (ret >= 0) { + len = ret; + if (len > max_len) { + len = max_len; + ret = USB_RET_BABBLE; + } + if (len > 0) { + /* write the data back */ + cpu_physical_memory_write(td->buffer, s->usb_buf, len); + } + } else { + len = 0; + } + break; + default: + /* invalid pid : frame interrupted */ + s->status |= UHCI_STS_HCPERR; + uhci_update_irq(s); + return -1; + } + } + + if (ret == USB_RET_ASYNC) { + return 2; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; if (ret >= 0) { td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); - td->ctrl &= ~TD_CTRL_ACTIVE; - if (pid == USB_TOKEN_IN && + /* The NAK bit may have been set by a previous frame, so clear it + here. The docs are somewhat unclear, but win2k relies on this + behavior. */ + td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK); + if (pid == USB_TOKEN_IN && (td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; @@ -496,7 +613,7 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) uhci_update_irq(s); } } - td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | + td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); return 1; case USB_RET_NAK: @@ -517,6 +634,96 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) } } +static void uhci_async_complete_packet(USBPacket * packet, void *opaque) +{ + UHCIState *s = opaque; + UHCI_QH qh; + UHCI_TD td; + uint32_t link; + uint32_t old_td_ctrl; + uint32_t val; + uint32_t frame_addr; + int ret; + + /* Handle async isochronous packet completion */ + frame_addr = s->async_frame_addr; + if (frame_addr) { + cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); + le32_to_cpus(&link); + + 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); + old_td_ctrl = td.ctrl; + ret = uhci_handle_td(s, &td, &s->pending_int_mask, 1); + + /* update the status bits of the TD */ + if (old_td_ctrl != td.ctrl) { + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + } + if (ret == 2) { + s->async_frame_addr = frame_addr; + } else if (ret == 0) { + /* update qh element link */ + val = cpu_to_le32(td.link); + cpu_physical_memory_write(frame_addr, + (const uint8_t *)&val, + sizeof(val)); + } + return; + } + + link = s->async_qh; + if (!link) { + /* This should never happen. It means a TD somehow got removed + without cancelling the associated async IO request. */ + return; + } + cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); + le32_to_cpus(&qh.link); + le32_to_cpus(&qh.el_link); + /* Re-process the queue containing the async packet. */ + while (1) { + cpu_physical_memory_read(qh.el_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); + old_td_ctrl = td.ctrl; + ret = uhci_handle_td(s, &td, &s->pending_int_mask, 1); + + /* update the status bits of the TD */ + if (old_td_ctrl != td.ctrl) { + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((qh.el_link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + } + if (ret < 0) + break; /* interrupted frame */ + if (ret == 2) { + s->async_qh = link; + break; + } else if (ret == 0) { + /* update qh element link */ + qh.el_link = td.link; + val = cpu_to_le32(qh.el_link); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, + sizeof(val)); + if (!(qh.el_link & 4)) + break; + } + break; + } +} + static void uhci_frame_timer(void *opaque) { UHCIState *s = opaque; @@ -525,11 +732,22 @@ static void uhci_frame_timer(void *opaque) int int_mask, cnt, ret; UHCI_TD td; UHCI_QH qh; + uint32_t old_async_qh; if (!(s->cmd & UHCI_CMD_RS)) { qemu_del_timer(s->frame_timer); + /* set hchalted bit in status - UHCI11D 2.1.2 */ + s->status |= UHCI_STS_HCHALTED; return; } + /* Complete the previous frame. */ + s->frnum = (s->frnum + 1) & 0x7ff; + if (s->pending_int_mask) { + s->status2 |= s->pending_int_mask; + s->status |= UHCI_STS_USBINT; + uhci_update_irq(s); + } + old_async_qh = s->async_qh; frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); le32_to_cpus(&link); @@ -541,6 +759,12 @@ static void uhci_frame_timer(void *opaque) /* valid frame */ if (link & 2) { /* QH */ + if (link == s->async_qh) { + /* We've found a previously issues packet. + Nothing else to do. */ + old_async_qh = 0; + break; + } cpu_physical_memory_read(link & ~0xf, (uint8_t *)&qh, sizeof(qh)); le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); @@ -551,33 +775,40 @@ static void uhci_frame_timer(void *opaque) } else if (qh.el_link & 2) { /* QH */ link = qh.el_link; + } else if (s->async_qh) { + /* We can only cope with one pending packet. Keep looking + for the previously issued packet. */ + link = qh.link; } else { /* TD */ if (--cnt == 0) break; - cpu_physical_memory_read(qh.el_link & ~0xf, + cpu_physical_memory_read(qh.el_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); old_td_ctrl = td.ctrl; - ret = uhci_handle_td(s, &td, &int_mask); + ret = uhci_handle_td(s, &td, &int_mask, 0); + /* update the status bits of the TD */ if (old_td_ctrl != td.ctrl) { val = cpu_to_le32(td.ctrl); - cpu_physical_memory_write((qh.el_link & ~0xf) + 4, - (const uint8_t *)&val, + cpu_physical_memory_write((qh.el_link & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); } if (ret < 0) break; /* interrupted frame */ - if (ret == 0) { + if (ret == 2) { + s->async_qh = link; + } else if (ret == 0) { /* update qh element link */ qh.el_link = td.link; val = cpu_to_le32(qh.el_link); - cpu_physical_memory_write((link & ~0xf) + 4, - (const uint8_t *)&val, + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); if (qh.el_link & 4) { /* depth first */ @@ -594,33 +825,46 @@ static void uhci_frame_timer(void *opaque) le32_to_cpus(&td.ctrl); le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); + + /* Handle isochonous transfer. */ + /* FIXME: might be more than one isoc in frame */ old_td_ctrl = td.ctrl; - ret = uhci_handle_td(s, &td, &int_mask); + ret = uhci_handle_td(s, &td, &int_mask, 0); + /* update the status bits of the TD */ if (old_td_ctrl != td.ctrl) { val = cpu_to_le32(td.ctrl); - cpu_physical_memory_write((link & ~0xf) + 4, - (const uint8_t *)&val, + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); } if (ret < 0) break; /* interrupted frame */ + if (ret == 2) { + s->async_frame_addr = frame_addr; + } link = td.link; } } - s->frnum = (s->frnum + 1) & 0x7ff; - if (int_mask) { - s->status2 |= int_mask; - s->status |= UHCI_STS_USBINT; - uhci_update_irq(s); + s->pending_int_mask = int_mask; + if (old_async_qh) { + /* A previously started transfer has disappeared from the transfer + list. There's nothing useful we can do with it now, so just + discard the packet and hope it wasn't too important. */ +#ifdef DEBUG + printf("Discarding USB packet\n"); +#endif + usb_cancel_packet(&s->usb_packet); + s->async_qh = 0; } + /* prepare the timer for the next frame */ - expire_time = qemu_get_clock(vm_clock) + + expire_time = qemu_get_clock(vm_clock) + (ticks_per_sec / FRAME_TIMER_FREQ); qemu_mod_timer(s->frame_timer, expire_time); } -static void uhci_map(PCIDevice *pci_dev, int region_num, +static void uhci_map(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { UHCIState *s = (UHCIState *)pci_dev; @@ -633,17 +877,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); } -void usb_uhci_init(PCIBus *bus, USBPort **usb_ports) +void usb_uhci_piix3_init(PCIBus *bus, int devfn) { UHCIState *s; uint8_t *pci_conf; - UHCIPort *port; int i; s = (UHCIState *)pci_register_device(bus, "USB-UHCI", sizeof(UHCIState), - -1, - NULL, NULL); + devfn, NULL, NULL); pci_conf = s->dev.config; pci_conf[0x00] = 0x86; pci_conf[0x01] = 0x80; @@ -654,19 +896,53 @@ void usb_uhci_init(PCIBus *bus, USBPort **usb_ports) pci_conf[0x0a] = 0x03; pci_conf[0x0b] = 0x0c; pci_conf[0x0e] = 0x00; // header_type - pci_conf[0x3d] = 1; // interrupt pin 0 - + pci_conf[0x3d] = 4; // interrupt pin 3 + pci_conf[0x60] = 0x10; // release number + for(i = 0; i < NB_PORTS; i++) { - port = &s->ports[i]; - port->port.opaque = s; - port->port.index = i; - port->port.attach = uhci_attach; - usb_ports[i] = &port->port; + qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); + } + s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); + + uhci_reset(s); + + /* Use region 4 for consistency with real hardware. BSD guests seem + to rely on this. */ + pci_register_io_region(&s->dev, 4, 0x20, + PCI_ADDRESS_SPACE_IO, uhci_map); +} + +void usb_uhci_piix4_init(PCIBus *bus, int devfn) +{ + UHCIState *s; + uint8_t *pci_conf; + int i; + + s = (UHCIState *)pci_register_device(bus, + "USB-UHCI", sizeof(UHCIState), + devfn, NULL, NULL); + pci_conf = s->dev.config; + pci_conf[0x00] = 0x86; + pci_conf[0x01] = 0x80; + pci_conf[0x02] = 0x12; + pci_conf[0x03] = 0x71; + pci_conf[0x08] = 0x01; // revision number + pci_conf[0x09] = 0x00; + pci_conf[0x0a] = 0x03; + pci_conf[0x0b] = 0x0c; + pci_conf[0x0e] = 0x00; // header_type + pci_conf[0x3d] = 4; // interrupt pin 3 + pci_conf[0x60] = 0x10; // release number + + for(i = 0; i < NB_PORTS; i++) { + qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); uhci_reset(s); - pci_register_io_region(&s->dev, 0, 0x20, + /* Use region 4 for consistency with real hardware. BSD guests seem + to rely on this. */ + pci_register_io_region(&s->dev, 4, 0x20, PCI_ADDRESS_SPACE_IO, uhci_map); }