diff options
Diffstat (limited to 'hw/usb-uhci.c')
| -rw-r--r-- | hw/usb-uhci.c | 152 |
1 files changed, 104 insertions, 48 deletions
diff --git a/hw/usb-uhci.c b/hw/usb-uhci.c index 405fa7b65e..824e3a5e8b 100644 --- a/hw/usb-uhci.c +++ b/hw/usb-uhci.c @@ -30,6 +30,8 @@ #include "pci.h" #include "qemu-timer.h" #include "usb-uhci.h" +#include "iov.h" +#include "dma.h" //#define DEBUG //#define DEBUG_DUMP_DATA @@ -93,17 +95,12 @@ static const char *pid2str(int pid) #endif #ifdef DEBUG_DUMP_DATA -static void dump_data(const uint8_t *data, int len) +static void dump_data(USBPacket *p, int ret) { - int i; - - printf("uhci: data: "); - for(i = 0; i < len; i++) - printf(" %02x", data[i]); - printf("\n"); + iov_hexdump(p->iov.iov, p->iov.niov, stderr, "uhci", ret); } #else -static void dump_data(const uint8_t *data, int len) {} +static void dump_data(USBPacket *p, int ret) {} #endif typedef struct UHCIState UHCIState; @@ -115,6 +112,7 @@ typedef struct UHCIState UHCIState; */ typedef struct UHCIAsync { USBPacket packet; + QEMUSGList sgl; UHCIState *uhci; QTAILQ_ENTRY(UHCIAsync) next; uint32_t td; @@ -122,7 +120,6 @@ typedef struct UHCIAsync { int8_t valid; uint8_t isoc; uint8_t done; - uint8_t buffer[2048]; } UHCIAsync; typedef struct UHCIPort { @@ -132,7 +129,7 @@ typedef struct UHCIPort { struct UHCIState { PCIDevice dev; - USBBus bus; + USBBus bus; /* Note unused when we're a companion controller */ uint16_t cmd; /* cmd register */ uint16_t status; uint16_t intr; /* interrupt enable register */ @@ -150,6 +147,10 @@ struct UHCIState { /* Active packets */ QTAILQ_HEAD(,UHCIAsync) async_pending; uint8_t num_ports_vmstate; + + /* Properties */ + char *masterbus; + uint32_t firstport; }; typedef struct UHCI_TD { @@ -175,12 +176,16 @@ static UHCIAsync *uhci_async_alloc(UHCIState *s) async->token = 0; async->done = 0; async->isoc = 0; + usb_packet_init(&async->packet); + qemu_sglist_init(&async->sgl, 1); return async; } static void uhci_async_free(UHCIState *s, UHCIAsync *async) { + usb_packet_cleanup(&async->packet); + qemu_sglist_destroy(&async->sgl); qemu_free(async); } @@ -606,6 +611,8 @@ static void uhci_detach(USBPort *port1) UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; + uhci_async_cancel_device(s, port1->dev); + /* set connect status */ if (port->ctrl & UHCI_PORT_CCS) { port->ctrl &= ~UHCI_PORT_CCS; @@ -620,11 +627,17 @@ static void uhci_detach(USBPort *port1) uhci_resume(s); } -static void uhci_wakeup(USBDevice *dev) +static void uhci_child_detach(USBPort *port1, USBDevice *child) { - USBBus *bus = usb_bus_from_device(dev); - UHCIState *s = container_of(bus, UHCIState, bus); - UHCIPort *port = s->ports + dev->port->index; + UHCIState *s = port1->opaque; + + uhci_async_cancel_device(s, child); +} + +static void uhci_wakeup(USBPort *port1) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { port->ctrl |= UHCI_PORT_RD; @@ -636,10 +649,10 @@ 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", - pid2str(p->pid), p->devaddr, p->devep, p->len); + DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %zd\n", + pid2str(p->pid), p->devaddr, p->devep, p->iov.size); if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) - dump_data(p->data, p->len); + dump_data(p, 0); ret = USB_RET_NODEV; for (i = 0; i < NB_PORTS && ret == USB_RET_NODEV; i++) { @@ -650,14 +663,14 @@ static int uhci_broadcast_packet(UHCIState *s, USBPacket *p) ret = usb_handle_packet(dev, p); } - DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); + DPRINTF("uhci: packet exit. ret %d len %zd\n", ret, p->iov.size); if (p->pid == USB_TOKEN_IN && ret > 0) - dump_data(p->data, ret); + dump_data(p, ret); return ret; } -static void uhci_async_complete(USBDevice *dev, USBPacket *packet); +static void uhci_async_complete(USBPort *port, USBPacket *packet); static void uhci_process_frame(UHCIState *s); /* return -1 if fatal error (frame must be stopped) @@ -672,7 +685,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - ret = async->packet.len; + ret = async->packet.result; if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -680,7 +693,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ if (ret < 0) goto out; - len = async->packet.len; + len = async->packet.result; td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff); /* The NAK bit may have been set by a previous frame, so clear it @@ -696,11 +709,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ goto out; } - if (len > 0) { - /* write the data back */ - cpu_physical_memory_write(td->buffer, async->buffer, len); - } - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ @@ -718,6 +726,9 @@ out: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } uhci_update_irq(s); return 1; @@ -725,6 +736,9 @@ out: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; s->status |= UHCI_STS_USBERR; + if (td->ctrl & TD_CTRL_IOC) { + *int_mask |= 0x01; + } uhci_update_irq(s); /* frame interrupted */ return -1; @@ -809,16 +823,14 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; - async->packet.pid = pid; - async->packet.devaddr = (td->token >> 8) & 0x7f; - async->packet.devep = (td->token >> 15) & 0xf; - async->packet.data = async->buffer; - async->packet.len = max_len; + usb_packet_setup(&async->packet, pid, (td->token >> 8) & 0x7f, + (td->token >> 15) & 0xf); + qemu_sglist_add(&async->sgl, td->buffer, max_len); + usb_packet_map(&async->packet, &async->sgl); switch(pid) { case USB_TOKEN_OUT: case USB_TOKEN_SETUP: - cpu_physical_memory_read(td->buffer, async->buffer, max_len); len = uhci_broadcast_packet(s, &async->packet); if (len >= 0) len = max_len; @@ -841,15 +853,16 @@ static int uhci_handle_td(UHCIState *s, uint32_t addr, UHCI_TD *td, uint32_t *in return 2; } - async->packet.len = len; + async->packet.result = len; done: len = uhci_complete_td(s, td, async, int_mask); + usb_packet_unmap(&async->packet); uhci_async_free(s, async); return len; } -static void uhci_async_complete(USBDevice *dev, USBPacket *packet) +static void uhci_async_complete(USBPort *port, USBPacket *packet) { UHCIAsync *async = container_of(packet, UHCIAsync, packet); UHCIState *s = async->uhci; @@ -1096,22 +1109,15 @@ static void uhci_map(PCIDevice *pci_dev, int region_num, register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); } -static void uhci_device_destroy(USBBus *bus, USBDevice *dev) -{ - UHCIState *s = container_of(bus, UHCIState, bus); - - uhci_async_cancel_device(s, dev); -} - static USBPortOps uhci_port_ops = { .attach = uhci_attach, .detach = uhci_detach, + .child_detach = uhci_child_detach, .wakeup = uhci_wakeup, .complete = uhci_async_complete, }; static USBBusOps uhci_bus_ops = { - .device_destroy = uhci_device_destroy, }; static int usb_uhci_common_initfn(PCIDevice *dev) @@ -1125,11 +1131,22 @@ static int usb_uhci_common_initfn(PCIDevice *dev) pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3 pci_conf[USB_SBRN] = USB_RELEASE_1; // release number - usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); - for(i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, - USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); - usb_port_location(&s->ports[i].port, NULL, i+1); + if (s->masterbus) { + USBPort *ports[NB_PORTS]; + for(i = 0; i < NB_PORTS; i++) { + ports[i] = &s->ports[i].port; + } + if (usb_register_companion(s->masterbus, ports, NB_PORTS, + s->firstport, s, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL) != 0) { + return -1; + } + } else { + usb_bus_new(&s->bus, &uhci_bus_ops, &s->dev.qdev); + for (i = 0; i < NB_PORTS; i++) { + usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + } } s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; @@ -1160,6 +1177,12 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) return usb_uhci_common_initfn(dev); } +static Property uhci_properties[] = { + DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), + DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static PCIDeviceInfo uhci_info[] = { { .qdev.name = "piix3-usb-uhci", @@ -1170,6 +1193,7 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371SB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ .qdev.name = "piix4-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1179,6 +1203,7 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_INTEL_82371AB_2, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ .qdev.name = "vt82c686b-usb-uhci", .qdev.size = sizeof(UHCIState), @@ -1188,6 +1213,37 @@ static PCIDeviceInfo uhci_info[] = { .device_id = PCI_DEVICE_ID_VIA_UHCI, .revision = 0x01, .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci1", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI1, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci2", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI2, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, + },{ + .qdev.name = "ich9-usb-uhci3", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_common_initfn, + .vendor_id = PCI_VENDOR_ID_INTEL, + .device_id = PCI_DEVICE_ID_INTEL_82801I_UHCI3, + .revision = 0x03, + .class_id = PCI_CLASS_SERIAL_USB, + .qdev.props = uhci_properties, },{ /* end of list */ } |