From 4aed20e2d70f4353164399a173f20c3ab435b4eb Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 09:18:05 +0200 Subject: [PATCH 01/37] uhci: fix bandwidth management uhci_process_frame() can be invoked multiple times per frame, so accounting usb bandwith in a local variable doesn't fly, use a variable in UHCIState instead. Also check the limit more frequently. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 22 ++++++++++++---------- trace-events | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 9e211a0bb4..48ad35c78d 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -131,6 +131,7 @@ struct UHCIState { uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ int64_t expire_time; QEMUTimer *frame_timer; + uint32_t frame_bytes; UHCIPort ports[NB_PORTS]; /* Interrupts that should be raised at the end of the current frame. */ @@ -985,7 +986,7 @@ static void uhci_fill_queue(UHCIState *s, UHCI_TD *td) static void uhci_process_frame(UHCIState *s) { uint32_t frame_addr, link, old_td_ctrl, val, int_mask; - uint32_t curr_qh, td_count = 0, bytes_count = 0; + uint32_t curr_qh, td_count = 0; int cnt, ret; UHCI_TD td; UHCI_QH qh; @@ -1002,6 +1003,12 @@ static void uhci_process_frame(UHCIState *s) qhdb_reset(&qhdb); for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { + if (s->frame_bytes >= 1280) { + /* We've reached the usb 1.1 bandwidth, which is + 1280 bytes/frame, stop processing */ + trace_usb_uhci_frame_stop_bandwidth(); + break; + } if (is_qh(link)) { /* QH */ trace_usb_uhci_qh_load(link & ~0xf); @@ -1011,18 +1018,12 @@ static void uhci_process_frame(UHCIState *s) * We're going in circles. Which is not a bug because * HCD is allowed to do that as part of the BW management. * - * Stop processing here if - * (a) no transaction has been done since we've been - * here last time, or - * (b) we've reached the usb 1.1 bandwidth, which is - * 1280 bytes/frame. + * Stop processing here if no transaction has been done + * since we've been here last time. */ if (td_count == 0) { trace_usb_uhci_frame_loop_stop_idle(); break; - } else if (bytes_count >= 1280) { - trace_usb_uhci_frame_loop_stop_bandwidth(); - break; } else { trace_usb_uhci_frame_loop_continue(); td_count = 0; @@ -1085,7 +1086,7 @@ static void uhci_process_frame(UHCIState *s) trace_usb_uhci_td_complete(curr_qh & ~0xf, link & ~0xf); link = td.link; td_count++; - bytes_count += (td.ctrl & 0x7ff) + 1; + s->frame_bytes += (td.ctrl & 0x7ff) + 1; if (curr_qh) { /* update QH element link */ @@ -1118,6 +1119,7 @@ static void uhci_frame_timer(void *opaque) /* prepare the timer for the next frame */ s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ); + s->frame_bytes = 0; if (!(s->cmd & UHCI_CMD_RS)) { /* Full stop */ diff --git a/trace-events b/trace-events index 45c6bc1271..560e57b117 100644 --- a/trace-events +++ b/trace-events @@ -263,8 +263,8 @@ usb_uhci_reset(void) "=== RESET ===" usb_uhci_schedule_start(void) "" usb_uhci_schedule_stop(void) "" usb_uhci_frame_start(uint32_t num) "nr %d" +usb_uhci_frame_stop_bandwidth(void) "" usb_uhci_frame_loop_stop_idle(void) "" -usb_uhci_frame_loop_stop_bandwidth(void) "" usb_uhci_frame_loop_continue(void) "" usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr %04x, ret 0x04%x" usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr %04x, val 0x04%x" From 9a16c5950d9ce38671a1ac259dcde3e707767922 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 09:33:07 +0200 Subject: [PATCH 02/37] uhci: use bottom half Schedule bottom half on completion of async packets instead of calling uhci_process_frame directly. This way we run uhci_process_frame only once in case multiple packets finish in a row. Also check whenever there is bandwidth left before scheduling uhci_process_frame. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 48ad35c78d..91bcc7e358 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -131,6 +131,7 @@ struct UHCIState { uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ int64_t expire_time; QEMUTimer *frame_timer; + QEMUBH *bh; uint32_t frame_bytes; UHCIPort ports[NB_PORTS]; @@ -370,6 +371,7 @@ static void uhci_reset(void *opaque) } uhci_async_cancel_all(s); + qemu_bh_cancel(s->bh); uhci_update_irq(s); } @@ -906,7 +908,9 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) uhci_async_free(async); } else { async->done = 1; - uhci_process_frame(s); + if (s->frame_bytes < 1280) { + qemu_bh_schedule(s->bh); + } } } @@ -1113,6 +1117,12 @@ out: s->pending_int_mask |= int_mask; } +static void uhci_bh(void *opaque) +{ + UHCIState *s = opaque; + uhci_process_frame(s); +} + static void uhci_frame_timer(void *opaque) { UHCIState *s = opaque; @@ -1120,6 +1130,7 @@ static void uhci_frame_timer(void *opaque) /* prepare the timer for the next frame */ s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ); s->frame_bytes = 0; + qemu_bh_cancel(s->bh); if (!(s->cmd & UHCI_CMD_RS)) { /* Full stop */ @@ -1206,6 +1217,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev) USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); } } + s->bh = qemu_bh_new(uhci_bh, s); s->frame_timer = qemu_new_timer_ns(vm_clock, uhci_frame_timer, s); s->num_ports_vmstate = NB_PORTS; QTAILQ_INIT(&s->queues); From 40141d12de84b67528354ec88075f66f5afd0cc7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 10:02:53 +0200 Subject: [PATCH 03/37] uhci: make bandwidth tunable Add a property for the uhci bandwidth. Can be used to make uhci emulation run faster than real hardware. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 91bcc7e358..2e7c8f96a9 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -133,6 +133,7 @@ struct UHCIState { QEMUTimer *frame_timer; QEMUBH *bh; uint32_t frame_bytes; + uint32_t frame_bandwidth; UHCIPort ports[NB_PORTS]; /* Interrupts that should be raised at the end of the current frame. */ @@ -908,7 +909,7 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet) uhci_async_free(async); } else { async->done = 1; - if (s->frame_bytes < 1280) { + if (s->frame_bytes < s->frame_bandwidth) { qemu_bh_schedule(s->bh); } } @@ -1007,7 +1008,7 @@ static void uhci_process_frame(UHCIState *s) qhdb_reset(&qhdb); for (cnt = FRAME_MAX_LOOPS; is_valid(link) && cnt; cnt--) { - if (s->frame_bytes >= 1280) { + if (s->frame_bytes >= s->frame_bandwidth) { /* We've reached the usb 1.1 bandwidth, which is 1280 bytes/frame, stop processing */ trace_usb_uhci_frame_stop_bandwidth(); @@ -1258,6 +1259,7 @@ static int usb_uhci_exit(PCIDevice *dev) static Property uhci_properties[] = { DEFINE_PROP_STRING("masterbus", UHCIState, masterbus), DEFINE_PROP_UINT32("firstport", UHCIState, firstport, 0), + DEFINE_PROP_UINT32("bandwidth", UHCIState, frame_bandwidth, 1280), DEFINE_PROP_END_OF_LIST(), }; From 7dd0dfd7f744b35bcf7b3b0fb7601bd9e88c064f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 12:57:34 +0200 Subject: [PATCH 04/37] uhci: fix trace format strings Signed-off-by: Gerd Hoffmann --- trace-events | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trace-events b/trace-events index 560e57b117..dd057bc7ad 100644 --- a/trace-events +++ b/trace-events @@ -266,10 +266,10 @@ usb_uhci_frame_start(uint32_t num) "nr %d" usb_uhci_frame_stop_bandwidth(void) "" usb_uhci_frame_loop_stop_idle(void) "" usb_uhci_frame_loop_continue(void) "" -usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr %04x, ret 0x04%x" -usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr %04x, val 0x04%x" -usb_uhci_mmio_readl(uint32_t addr, uint32_t val) "addr %04x, ret 0x08%x" -usb_uhci_mmio_writel(uint32_t addr, uint32_t val) "addr %04x, val 0x08%x" +usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%04x" +usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%04x" +usb_uhci_mmio_readl(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%08x" +usb_uhci_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%08x" usb_uhci_queue_add(uint32_t token) "token 0x%x" usb_uhci_queue_del(uint32_t token) "token 0x%x" usb_uhci_packet_add(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x" From 5852d3bfe16c85d4dabc4e0d21658fc680a756e5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 14 May 2012 14:02:14 +0200 Subject: [PATCH 05/37] uhci: zap uhci_pre_save Cancel transactions before saving vmstate is pretty pointless and just causes disruptions. We need to cancel them before *loading* vmstate, but in that case uhci_reset() handles it already and no special action is needed. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 2e7c8f96a9..3ea388c8fa 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -376,13 +376,6 @@ static void uhci_reset(void *opaque) uhci_update_irq(s); } -static void uhci_pre_save(void *opaque) -{ - UHCIState *s = opaque; - - uhci_async_cancel_all(s); -} - static const VMStateDescription vmstate_uhci_port = { .name = "uhci port", .version_id = 1, @@ -399,7 +392,6 @@ static const VMStateDescription vmstate_uhci = { .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, - .pre_save = uhci_pre_save, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, UHCIState), VMSTATE_UINT8_EQUAL(num_ports_vmstate, UHCIState), From 973002c11460efd3c17fe61a76711a103e30e1f9 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 25 May 2012 12:53:47 +0200 Subject: [PATCH 06/37] uhci: fix irq routing The multifunction ich9 ehci controller with uhci companions uses a different interrupt pin for each function. The three uhci devices get pins A, B and C, whereas ehci uses pin D. This way the guest can assign different IRQ lines to each controller. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-uhci.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 3ea388c8fa..9871e24f50 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -138,6 +138,7 @@ struct UHCIState { /* Interrupts that should be raised at the end of the current frame. */ uint32_t pending_int_mask; + int irq_pin; /* Active packets */ QTAILQ_HEAD(, UHCIQueue) queues; @@ -340,7 +341,7 @@ static void uhci_update_irq(UHCIState *s) } else { level = 0; } - qemu_set_irq(s->dev.irq[3], level); + qemu_set_irq(s->dev.irq[s->irq_pin], level); } static void uhci_reset(void *opaque) @@ -1184,15 +1185,31 @@ static USBBusOps uhci_bus_ops = { static int usb_uhci_common_initfn(PCIDevice *dev) { + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); UHCIState *s = DO_UPCAST(UHCIState, dev, dev); uint8_t *pci_conf = s->dev.config; int i; pci_conf[PCI_CLASS_PROG] = 0x00; /* TODO: reset value should be 0. */ - pci_conf[PCI_INTERRUPT_PIN] = 4; /* interrupt pin D */ pci_conf[USB_SBRN] = USB_RELEASE_1; // release number + switch (pc->device_id) { + case PCI_DEVICE_ID_INTEL_82801I_UHCI1: + s->irq_pin = 0; /* A */ + break; + case PCI_DEVICE_ID_INTEL_82801I_UHCI2: + s->irq_pin = 1; /* B */ + break; + case PCI_DEVICE_ID_INTEL_82801I_UHCI3: + s->irq_pin = 2; /* C */ + break; + default: + s->irq_pin = 3; /* D */ + break; + } + pci_config_set_interrupt_pin(pci_conf, s->irq_pin + 1); + if (s->masterbus) { USBPort *ports[NB_PORTS]; for(i = 0; i < NB_PORTS; i++) { From 18eef3bc4eb80fee2cf4a9f18cb87784854d30c1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 16 May 2012 14:20:03 +0200 Subject: [PATCH 07/37] scsi: prepare migration code for usb-storage support usb-storage can't handle requests in one go as the data transfer can be splitted into lots of usb packets. Because of that there can be normal in-flight requests at savevm time and we need to handle that. With other scsi hba's this happens only in case i/o is stopped due to errors and there are pending requests which need to be restarted (req->retry = true). So, first we need to save req->retry and then handle the req->retry = false case. Write requests are handled fine already. For read requests we have to save the buffer as we will not restart the request (and thus not refill the buffer) on the target host. Cc: Paolo Bonzini Signed-off-by: Gerd Hoffmann --- hw/scsi-bus.c | 8 ++++---- hw/scsi-disk.c | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index f10f3ec25c..4a798210ce 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1507,10 +1507,9 @@ static void put_scsi_requests(QEMUFile *f, void *pv, size_t size) QTAILQ_FOREACH(req, &s->requests, next) { assert(!req->io_canceled); assert(req->status == -1); - assert(req->retry); assert(req->enqueued); - qemu_put_sbyte(f, 1); + qemu_put_sbyte(f, req->retry ? 1 : 2); qemu_put_buffer(f, req->cmd.buf, sizeof(req->cmd.buf)); qemu_put_be32s(f, &req->tag); qemu_put_be32s(f, &req->lun); @@ -1528,8 +1527,9 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) { SCSIDevice *s = pv; SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); + int8_t sbyte; - while (qemu_get_sbyte(f)) { + while ((sbyte = qemu_get_sbyte(f)) > 0) { uint8_t buf[SCSI_CMD_BUF_SIZE]; uint32_t tag; uint32_t lun; @@ -1539,6 +1539,7 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) qemu_get_be32s(f, &tag); qemu_get_be32s(f, &lun); req = scsi_req_new(s, tag, lun, buf, NULL); + req->retry = (sbyte == 1); if (bus->info->load_request) { req->hba_private = bus->info->load_request(f, req); } @@ -1547,7 +1548,6 @@ static int get_scsi_requests(QEMUFile *f, void *pv, size_t size) } /* Just restart it later. */ - req->retry = true; scsi_req_enqueue_internal(req); /* At this point, the request will be kept alive by the reference diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 045c764d9b..1691491c03 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -132,8 +132,14 @@ static void scsi_disk_save_request(QEMUFile *f, SCSIRequest *req) qemu_put_be64s(f, &r->sector); qemu_put_be32s(f, &r->sector_count); qemu_put_be32s(f, &r->buflen); - if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { - qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); + if (r->buflen) { + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); + } else if (!req->retry) { + uint32_t len = r->iov.iov_len; + qemu_put_be32s(f, &len); + qemu_put_buffer(f, r->iov.iov_base, r->iov.iov_len); + } } } @@ -148,6 +154,12 @@ static void scsi_disk_load_request(QEMUFile *f, SCSIRequest *req) scsi_init_iovec(r, r->buflen); if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len); + } else if (!r->req.retry) { + uint32_t len; + qemu_get_be32s(f, &len); + r->iov.iov_len = len; + assert(r->iov.iov_len <= r->buflen); + qemu_get_buffer(f, r->iov.iov_base, r->iov.iov_len); } } From 0659879e6e537c511147d4da71934bf9d367de50 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 14 May 2012 14:00:02 +0200 Subject: [PATCH 08/37] usb-storage: remove MSDState->residue We have the field twice, once in MSDState directly and one in the status word struct. Drop one. Signed-off-by: Gerd Hoffmann --- hw/usb/dev-storage.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index a96c0b9e5e..e6652f9bcf 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -51,7 +51,6 @@ typedef struct { uint32_t scsi_len; uint8_t *scsi_buf; uint32_t data_len; - uint32_t residue; struct usb_msd_csw csw; SCSIRequest *req; SCSIBus bus; @@ -229,11 +228,10 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r USBPacket *p = s->packet; DPRINTF("Command complete %d tag 0x%x\n", status, req->tag); - s->residue = s->data_len; s->csw.sig = cpu_to_le32(0x53425355); s->csw.tag = cpu_to_le32(req->tag); - s->csw.residue = cpu_to_le32(s->residue); + s->csw.residue = cpu_to_le32(s->data_len); s->csw.status = status != 0; if (s->packet) { @@ -378,7 +376,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) } DPRINTF("Command tag 0x%x flags %08x len %d data %d\n", tag, cbw.flags, cbw.cmd_len, s->data_len); - s->residue = 0; + assert(le32_to_cpu(s->csw.residue) == 0); s->scsi_len = 0; s->req = scsi_req_new(s->scsi_dev, tag, 0, cbw.cmd, NULL); scsi_req_enqueue(s->req); @@ -397,7 +395,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (s->scsi_len) { usb_msd_copy_data(s, p); } - if (s->residue) { + if (le32_to_cpu(s->csw.residue)) { int len = p->iov.size - p->result; if (len) { usb_packet_skip(p, len); @@ -458,7 +456,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) if (s->scsi_len) { usb_msd_copy_data(s, p); } - if (s->residue) { + if (le32_to_cpu(s->csw.residue)) { int len = p->iov.size - p->result; if (len) { usb_packet_skip(p, len); From 1e6ed80b90e3d9632532b4c496d26b0d101ced6b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2012 14:30:20 +0200 Subject: [PATCH 09/37] usb-storage: add usb_msd_packet_complete() Factor out packet completion to a separate function which cares to get the MSDState->packet update right. Signed-off-by: Gerd Hoffmann --- hw/usb/dev-storage.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index e6652f9bcf..924a86f964 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -200,6 +200,18 @@ static void usb_msd_send_status(MSDState *s, USBPacket *p) memset(&s->csw, 0, sizeof(s->csw)); } +static void usb_msd_packet_complete(MSDState *s) +{ + USBPacket *p = s->packet; + + /* Set s->packet to NULL before calling usb_packet_complete + because another request may be issued before + usb_packet_complete returns. */ + DPRINTF("Packet complete %p\n", p); + s->packet = NULL; + usb_packet_complete(&s->dev, p); +} + static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) { MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); @@ -212,12 +224,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) usb_msd_copy_data(s, p); p = s->packet; if (p && p->result == p->iov.size) { - /* Set s->packet to NULL before calling usb_packet_complete - because another request may be issued before - usb_packet_complete returns. */ - DPRINTF("Packet complete %p\n", p); - s->packet = NULL; - usb_packet_complete(&s->dev, p); + usb_msd_packet_complete(s); } } } @@ -250,8 +257,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r s->mode = USB_MSDM_CSW; } } - s->packet = NULL; - usb_packet_complete(&s->dev, p); + usb_msd_packet_complete(s); } else if (s->data_len == 0) { s->mode = USB_MSDM_CSW; } @@ -281,10 +287,8 @@ static void usb_msd_handle_reset(USBDevice *dev) assert(s->req == NULL); if (s->packet) { - USBPacket *p = s->packet; - s->packet = NULL; - p->result = USB_RET_STALL; - usb_packet_complete(dev, p); + s->packet->result = USB_RET_STALL; + usb_msd_packet_complete(s); } s->mode = USB_MSDM_CBW; From 1dc903673aa3afee296aaa4664404fc5d373b79a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 16 May 2012 15:03:40 +0200 Subject: [PATCH 10/37] usb-storage: add scsi_off, remove scsi_buf Repace the running buffer pointer (scsi_buf) with a buffer offset field (scsi_off). The later is alot easier to live-migrate. Signed-off-by: Gerd Hoffmann --- hw/usb/dev-storage.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 924a86f964..5b9a286766 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -48,8 +48,8 @@ struct usb_msd_csw { typedef struct { USBDevice dev; enum USBMSDMode mode; + uint32_t scsi_off; uint32_t scsi_len; - uint8_t *scsi_buf; uint32_t data_len; struct usb_msd_csw csw; SCSIRequest *req; @@ -178,9 +178,9 @@ static void usb_msd_copy_data(MSDState *s, USBPacket *p) len = p->iov.size - p->result; if (len > s->scsi_len) len = s->scsi_len; - usb_packet_copy(p, s->scsi_buf, len); + usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len); s->scsi_len -= len; - s->scsi_buf += len; + s->scsi_off += len; s->data_len -= len; if (s->scsi_len == 0 || s->data_len == 0) { scsi_req_continue(s->req); @@ -219,7 +219,7 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len) assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV)); s->scsi_len = len; - s->scsi_buf = scsi_req_get_buf(req); + s->scsi_off = 0; if (p) { usb_msd_copy_data(s, p); p = s->packet; From 5de88b1d4679b560f1350aa4559342ba6a8d69ef Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 14 May 2012 13:56:40 +0200 Subject: [PATCH 11/37] usb-storage: migration support With all scsi migration support bits in place the final step is pretty simple ;) Signed-off-by: Gerd Hoffmann --- hw/usb/dev-storage.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 5b9a286766..097d7b4a6d 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -506,6 +506,17 @@ static void usb_msd_password_cb(void *opaque, int err) qdev_unplug(&s->dev.qdev, NULL); } +static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req) +{ + MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent); + + /* nothing to load, just store req in our state struct */ + assert(s->req == NULL); + scsi_req_ref(req); + s->req = req; + return NULL; +} + static const struct SCSIBusInfo usb_msd_scsi_info = { .tcq = false, .max_target = 0, @@ -513,7 +524,8 @@ static const struct SCSIBusInfo usb_msd_scsi_info = { .transfer_data = usb_msd_transfer_data, .complete = usb_msd_command_complete, - .cancel = usb_msd_request_cancelled + .cancel = usb_msd_request_cancelled, + .load_request = usb_msd_load_request, }; static int usb_msd_initfn(USBDevice *dev) @@ -633,11 +645,18 @@ static USBDevice *usb_msd_init(USBBus *bus, const char *filename) static const VMStateDescription vmstate_usb_msd = { .name = "usb-storage", - .unmigratable = 1, /* FIXME: handle transactions which are in flight */ .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField []) { VMSTATE_USB_DEVICE(dev, MSDState), + VMSTATE_UINT32(mode, MSDState), + VMSTATE_UINT32(scsi_len, MSDState), + VMSTATE_UINT32(scsi_off, MSDState), + VMSTATE_UINT32(data_len, MSDState), + VMSTATE_UINT32(csw.sig, MSDState), + VMSTATE_UINT32(csw.tag, MSDState), + VMSTATE_UINT32(csw.residue, MSDState), + VMSTATE_UINT8(csw.status, MSDState), VMSTATE_END_OF_LIST() } }; From 64619739c4cf32db60b5c9599e98ececd7640060 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Fri, 11 May 2012 11:36:37 -0300 Subject: [PATCH 12/37] xhci: Clean up reset function Properly register reset function via the device class. Signed-off-by: Jan Kiszka Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 5cf1a64699..4bc1e0ece1 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2296,9 +2296,9 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) } } -static void xhci_reset(void *opaque) +static void xhci_reset(DeviceState *dev) { - XHCIState *xhci = opaque; + XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev.qdev, dev); int i; DPRINTF("xhci: full reset\n"); @@ -2506,7 +2506,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) } xhci->usbcmd = val & 0xc0f; if (val & USBCMD_HCRST) { - xhci_reset(xhci); + xhci_reset(&xhci->pci_dev.qdev); } xhci_irq_update(xhci); break; @@ -2831,8 +2831,6 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) for (i = 0; i < MAXSLOTS; i++) { xhci->slots[i].enabled = 0; } - - qemu_register_reset(xhci_reset, xhci); } static int usb_xhci_initfn(struct PCIDevice *dev) @@ -2895,6 +2893,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_xhci; dc->props = xhci_properties; + dc->reset = xhci_reset; k->init = usb_xhci_initfn; k->vendor_id = PCI_VENDOR_ID_NEC; k->device_id = PCI_DEVICE_ID_NEC_UPD720200; From 2d754a10e4c43e9d83013ac83f8df7d1c856f0a0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 12:49:31 +0200 Subject: [PATCH 13/37] xhci: trace: mmio reads+writes Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 163 +++++++++++++++++++++++++++++----------------- trace-events | 12 ++++ 2 files changed, 117 insertions(+), 58 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 4bc1e0ece1..84d714a609 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -23,6 +23,7 @@ #include "hw/usb.h" #include "hw/pci.h" #include "hw/msi.h" +#include "trace.h" //#define DEBUG_XHCI //#define DEBUG_DATA @@ -2301,7 +2302,7 @@ static void xhci_reset(DeviceState *dev) XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev.qdev, dev); int i; - DPRINTF("xhci: full reset\n"); + trace_usb_xhci_reset(); if (!(xhci->usbsts & USBSTS_HCH)) { fprintf(stderr, "xhci: reset while running!\n"); } @@ -2342,77 +2343,98 @@ static void xhci_reset(DeviceState *dev) static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) { - DPRINTF("xhci_cap_read(0x%x)\n", reg); + uint32_t ret; switch (reg) { case 0x00: /* HCIVERSION, CAPLENGTH */ - return 0x01000000 | LEN_CAP; + ret = 0x01000000 | LEN_CAP; + break; case 0x04: /* HCSPARAMS 1 */ - return (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS; + ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS; + break; case 0x08: /* HCSPARAMS 2 */ - return 0x0000000f; + ret = 0x0000000f; + break; case 0x0c: /* HCSPARAMS 3 */ - return 0x00000000; + ret = 0x00000000; + break; case 0x10: /* HCCPARAMS */ -#if TARGET_PHYS_ADDR_BITS > 32 - return 0x00081001; -#else - return 0x00081000; -#endif + if (sizeof(dma_addr_t) == 4) { + ret = 0x00081000; + } else { + ret = 0x00081001; + } + break; case 0x14: /* DBOFF */ - return OFF_DOORBELL; + ret = OFF_DOORBELL; + break; case 0x18: /* RTSOFF */ - return OFF_RUNTIME; + ret = OFF_RUNTIME; + break; /* extended capabilities */ case 0x20: /* Supported Protocol:00 */ -#if USB3_PORTS > 0 - return 0x02000402; /* USB 2.0 */ -#else - return 0x02000002; /* USB 2.0 */ -#endif + ret = 0x02000402; /* USB 2.0 */ + break; case 0x24: /* Supported Protocol:04 */ - return 0x20425455; /* "USB " */ + ret = 0x20425455; /* "USB " */ + break; case 0x28: /* Supported Protocol:08 */ - return 0x00000001 | (USB2_PORTS<<8); + ret = 0x00000001 | (USB2_PORTS<<8); + break; case 0x2c: /* Supported Protocol:0c */ - return 0x00000000; /* reserved */ -#if USB3_PORTS > 0 + ret = 0x00000000; /* reserved */ + break; case 0x30: /* Supported Protocol:00 */ - return 0x03000002; /* USB 3.0 */ + ret = 0x03000002; /* USB 3.0 */ + break; case 0x34: /* Supported Protocol:04 */ - return 0x20425455; /* "USB " */ + ret = 0x20425455; /* "USB " */ + break; case 0x38: /* Supported Protocol:08 */ - return 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8); + ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8); + break; case 0x3c: /* Supported Protocol:0c */ - return 0x00000000; /* reserved */ -#endif + ret = 0x00000000; /* reserved */ + break; default: fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg); + ret = 0; } - return 0; + + trace_usb_xhci_cap_read(reg, ret); + return ret; } static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg) { uint32_t port = reg >> 4; + uint32_t ret; + if (port >= MAXPORTS) { fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); - return 0; + ret = 0; + goto out; } switch (reg & 0xf) { case 0x00: /* PORTSC */ - return xhci->ports[port].portsc; + ret = xhci->ports[port].portsc; + break; case 0x04: /* PORTPMSC */ case 0x08: /* PORTLI */ - return 0; + ret = 0; + break; case 0x0c: /* reserved */ default: fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n", port, reg); - return 0; + ret = 0; } + +out: + trace_usb_xhci_port_read(port, reg & 0x0f, ret); + return ret; } static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) @@ -2420,6 +2442,8 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) uint32_t port = reg >> 4; uint32_t portsc; + trace_usb_xhci_port_write(port, reg & 0x0f, val); + if (port >= MAXPORTS) { fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); return; @@ -2457,7 +2481,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) { - DPRINTF("xhci_oper_read(0x%x)\n", reg); + uint32_t ret; if (reg >= 0x400) { return xhci_port_read(xhci, reg - 0x400); @@ -2465,38 +2489,50 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) switch (reg) { case 0x00: /* USBCMD */ - return xhci->usbcmd; + ret = xhci->usbcmd; + break; case 0x04: /* USBSTS */ - return xhci->usbsts; + ret = xhci->usbsts; + break; case 0x08: /* PAGESIZE */ - return 1; /* 4KiB */ + ret = 1; /* 4KiB */ + break; case 0x14: /* DNCTRL */ - return xhci->dnctrl; + ret = xhci->dnctrl; + break; case 0x18: /* CRCR low */ - return xhci->crcr_low & ~0xe; + ret = xhci->crcr_low & ~0xe; + break; case 0x1c: /* CRCR high */ - return xhci->crcr_high; + ret = xhci->crcr_high; + break; case 0x30: /* DCBAAP low */ - return xhci->dcbaap_low; + ret = xhci->dcbaap_low; + break; case 0x34: /* DCBAAP high */ - return xhci->dcbaap_high; + ret = xhci->dcbaap_high; + break; case 0x38: /* CONFIG */ - return xhci->config; + ret = xhci->config; + break; default: fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg); + ret = 0; } - return 0; + + trace_usb_xhci_oper_read(reg, ret); + return ret; } static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) { - DPRINTF("xhci_oper_write(0x%x, 0x%08x)\n", reg, val); - if (reg >= 0x400) { xhci_port_write(xhci, reg - 0x400, val); return; } + trace_usb_xhci_oper_write(reg, val); + switch (reg) { case 0x00: /* USBCMD */ if ((val & USBCMD_RS) && !(xhci->usbcmd & USBCMD_RS)) { @@ -2552,35 +2588,46 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) { - DPRINTF("xhci_runtime_read(0x%x)\n", reg); + uint32_t ret; switch (reg) { case 0x00: /* MFINDEX */ fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n"); - return xhci->mfindex; + ret = xhci->mfindex; + break; case 0x20: /* IMAN */ - return xhci->iman; + ret = xhci->iman; + break; case 0x24: /* IMOD */ - return xhci->imod; + ret = xhci->imod; + break; case 0x28: /* ERSTSZ */ - return xhci->erstsz; + ret = xhci->erstsz; + break; case 0x30: /* ERSTBA low */ - return xhci->erstba_low; + ret = xhci->erstba_low; + break; case 0x34: /* ERSTBA high */ - return xhci->erstba_high; + ret = xhci->erstba_high; + break; case 0x38: /* ERDP low */ - return xhci->erdp_low; + ret = xhci->erdp_low; + break; case 0x3c: /* ERDP high */ - return xhci->erdp_high; + ret = xhci->erdp_high; + break; default: fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); + ret = 0; } - return 0; + + trace_usb_xhci_runtime_read(reg, ret); + return ret; } static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) { - DPRINTF("xhci_runtime_write(0x%x, 0x%08x)\n", reg, val); + trace_usb_xhci_runtime_read(reg, val); switch (reg) { case 0x20: /* IMAN */ @@ -2623,14 +2670,14 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg) { - DPRINTF("xhci_doorbell_read(0x%x)\n", reg); /* doorbells always read as 0 */ + trace_usb_xhci_doorbell_read(reg, 0); return 0; } static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val) { - DPRINTF("xhci_doorbell_write(0x%x, 0x%08x)\n", reg, val); + trace_usb_xhci_doorbell_write(reg, val); if (!xhci_running(xhci)) { fprintf(stderr, "xhci: wrote doorbell while xHC stopped or paused\n"); diff --git a/trace-events b/trace-events index dd057bc7ad..b70b1612ee 100644 --- a/trace-events +++ b/trace-events @@ -289,6 +289,18 @@ usb_uhci_td_nextqh(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" usb_uhci_td_async(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" +# hw/usb/hcd-xhci.c +usb_xhci_reset(void) "=== RESET ===" +usb_xhci_cap_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_oper_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_port_read(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, ret 0x%08x" +usb_xhci_runtime_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_doorbell_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" +usb_xhci_oper_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" +usb_xhci_port_write(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, val 0x%08x" +usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" +usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" + # hw/usb/desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d" From fc0ddaca33c3b4b9bf52cb15b62bdb17b84d9fa8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 14:54:57 +0200 Subject: [PATCH 14/37] xhci: trace: run+stop Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 5 ++--- trace-events | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 84d714a609..43875aabf9 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -813,14 +813,13 @@ static void xhci_er_reset(XHCIState *xhci) static void xhci_run(XHCIState *xhci) { - DPRINTF("xhci_run()\n"); - + trace_usb_xhci_run(); xhci->usbsts &= ~USBSTS_HCH; } static void xhci_stop(XHCIState *xhci) { - DPRINTF("xhci_stop()\n"); + trace_usb_xhci_stop(); xhci->usbsts |= USBSTS_HCH; xhci->crcr_low &= ~CRCR_CRR; } diff --git a/trace-events b/trace-events index b70b1612ee..1b2db05582 100644 --- a/trace-events +++ b/trace-events @@ -291,6 +291,8 @@ usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" # hw/usb/hcd-xhci.c usb_xhci_reset(void) "=== RESET ===" +usb_xhci_run(void) "" +usb_xhci_stop(void) "" usb_xhci_cap_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" usb_xhci_oper_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x" usb_xhci_port_read(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, ret 0x%08x" From 7acd279f835e9f3b82e98df53b8ff97e2d922bb0 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 13:25:09 +0200 Subject: [PATCH 15/37] xhci: trace: irq + events Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 12 ++++-------- trace-events | 3 +++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 43875aabf9..689088866c 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -422,7 +422,6 @@ typedef struct XHCIEvRingSeg { uint32_t rsvd; } XHCIEvRingSeg; -#ifdef DEBUG_XHCI static const char *TRBType_names[] = { [TRB_RESERVED] = "TRB_RESERVED", [TR_NORMAL] = "TR_NORMAL", @@ -474,7 +473,6 @@ static const char *trb_name(XHCITRB *trb) return lookup_name(TRB_TYPE(*trb), TRBType_names, ARRAY_SIZE(TRBType_names)); } -#endif static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); @@ -506,14 +504,13 @@ static void xhci_irq_update(XHCIState *xhci) level = 1; } - DPRINTF("xhci_irq_update(): %d\n", level); - if (xhci->msi && msi_enabled(&xhci->pci_dev)) { if (level) { - DPRINTF("xhci_irq_update(): MSI signal\n"); + trace_usb_xhci_irq_msi(0); msi_notify(&xhci->pci_dev, 0); } } else { + trace_usb_xhci_irq_intx(level); qemu_set_irq(xhci->irq, level); } } @@ -543,9 +540,8 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) } ev_trb.control = cpu_to_le32(ev_trb.control); - DPRINTF("xhci_write_event(): [%d] %016"PRIx64" %08x %08x %s\n", - xhci->er_ep_idx, ev_trb.parameter, ev_trb.status, ev_trb.control, - trb_name(&ev_trb)); + trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb), + ev_trb.parameter, ev_trb.status, ev_trb.control); addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx; pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE); diff --git a/trace-events b/trace-events index 1b2db05582..974e5a359d 100644 --- a/trace-events +++ b/trace-events @@ -302,6 +302,9 @@ usb_xhci_oper_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" usb_xhci_port_write(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, val 0x%08x" usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" +usb_xhci_irq_intx(uint32_t level) "level %d" +usb_xhci_irq_msi(uint32_t nr) "nr %d" +usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x" # hw/usb/desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" From 0703a4a7a67999dfda1a67cb7e0a6b4d2aefb239 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 14:23:42 +0200 Subject: [PATCH 16/37] xhci: trace: ring fetch Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 10 ++-------- trace-events | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 689088866c..08fdf9475a 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -701,10 +701,8 @@ static TRBType xhci_ring_fetch(XHCIState *xhci, XHCIRing *ring, XHCITRB *trb, le32_to_cpus(&trb->status); le32_to_cpus(&trb->control); - DPRINTF("xhci: TRB fetched [" DMA_ADDR_FMT "]: " - "%016" PRIx64 " %08x %08x %s\n", - ring->dequeue, trb->parameter, trb->status, trb->control, - trb_name(trb)); + trace_usb_xhci_fetch_trb(ring->dequeue, trb_name(trb), + trb->parameter, trb->status, trb->control); if ((trb->control & TRB_C) != ring->ccs) { return 0; @@ -743,10 +741,6 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) le32_to_cpus(&trb.status); le32_to_cpus(&trb.control); - DPRINTF("xhci: TRB peeked [" DMA_ADDR_FMT "]: " - "%016" PRIx64 " %08x %08x\n", - dequeue, trb.parameter, trb.status, trb.control); - if ((trb.control & TRB_C) != ccs) { return -length; } diff --git a/trace-events b/trace-events index 974e5a359d..09ed1093bd 100644 --- a/trace-events +++ b/trace-events @@ -305,6 +305,7 @@ usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x" usb_xhci_irq_intx(uint32_t level) "level %d" usb_xhci_irq_msi(uint32_t nr) "nr %d" usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" # hw/usb/desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" From c1f6b49326fee1c39f1db70fdb1e2a1df60318a2 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 14:26:15 +0200 Subject: [PATCH 17/37] xhci: trace: endpoints Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 22 +++++----------------- trace-events | 5 +++++ 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 08fdf9475a..c50e4077f7 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -842,11 +842,10 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, dma_addr_t dequeue; int i; + trace_usb_xhci_ep_enable(slotid, epid); assert(slotid >= 1 && slotid <= MAXSLOTS); assert(epid >= 1 && epid <= 31); - DPRINTF("xhci_enable_ep(%d, %d)\n", slotid, epid); - slot = &xhci->slots[slotid-1]; if (slot->eps[epid-1]) { fprintf(stderr, "xhci: slot %d ep %d already enabled!\n", slotid, epid); @@ -961,11 +960,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, XHCISlot *slot; XHCIEPContext *epctx; + trace_usb_xhci_ep_disable(slotid, epid); assert(slotid >= 1 && slotid <= MAXSLOTS); assert(epid >= 1 && epid <= 31); - DPRINTF("xhci_disable_ep(%d, %d)\n", slotid, epid); - slot = &xhci->slots[slotid-1]; if (!slot->eps[epid-1]) { @@ -991,8 +989,7 @@ static TRBCCode xhci_stop_ep(XHCIState *xhci, unsigned int slotid, XHCISlot *slot; XHCIEPContext *epctx; - DPRINTF("xhci_stop_ep(%d, %d)\n", slotid, epid); - + trace_usb_xhci_ep_stop(slotid, epid); assert(slotid >= 1 && slotid <= MAXSLOTS); if (epid < 1 || epid > 31) { @@ -1026,10 +1023,9 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, XHCIEPContext *epctx; USBDevice *dev; + trace_usb_xhci_ep_reset(slotid, epid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_reset_ep(%d, %d)\n", slotid, epid); - if (epid < 1 || epid > 31) { fprintf(stderr, "xhci: bad ep %d\n", epid); return CC_TRB_ERROR; @@ -1654,9 +1650,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid int length; int i; + trace_usb_xhci_ep_kick(slotid, epid); assert(slotid >= 1 && slotid <= MAXSLOTS); assert(epid >= 1 && epid <= 31); - DPRINTF("xhci_kick_ep(%d, %d)\n", slotid, epid); if (!xhci->slots[slotid-1].enabled) { fprintf(stderr, "xhci: xhci_kick_ep for disabled slot %d\n", slotid); @@ -1698,21 +1694,14 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid while (1) { XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer]; if (xfer->running_async || xfer->running_retry || xfer->backgrounded) { - DPRINTF("xhci: ep is busy (#%d,%d,%d,%d)\n", - epctx->next_xfer, xfer->running_async, - xfer->running_retry, xfer->backgrounded); break; - } else { - DPRINTF("xhci: ep: using #%d\n", epctx->next_xfer); } length = xhci_ring_chain_length(xhci, &epctx->ring); if (length < 0) { - DPRINTF("xhci: incomplete TD (%d TRBs)\n", -length); break; } else if (length == 0) { break; } - DPRINTF("xhci: fetching %d-TRB TD\n", length); if (xfer->trbs && xfer->trb_alloced < length) { xfer->trb_count = 0; xfer->trb_alloced = 0; @@ -1747,7 +1736,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid } if (epctx->state == EP_HALTED) { - DPRINTF("xhci: ep halted, stopping schedule\n"); break; } if (xfer->running_retry) { diff --git a/trace-events b/trace-events index 09ed1093bd..74fdf6ab14 100644 --- a/trace-events +++ b/trace-events @@ -306,6 +306,11 @@ usb_xhci_irq_intx(uint32_t level) "level %d" usb_xhci_irq_msi(uint32_t nr) "nr %d" usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x" usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" # hw/usb/desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" From 97df650bb9b4952e6e93ec3466086645714d5ddd Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 14:27:08 +0200 Subject: [PATCH 18/37] xhci: trace: transfers Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 18 ++++++++++-------- trace-events | 6 ++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index c50e4077f7..7e823da0d8 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1402,12 +1402,14 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) static int xhci_complete_packet(XHCITransfer *xfer, int ret) { if (ret == USB_RET_ASYNC) { + trace_usb_xhci_xfer_async(xfer); xfer->running_async = 1; xfer->running_retry = 0; xfer->complete = 0; xfer->cancelled = 0; return 0; } else if (ret == USB_RET_NAK) { + trace_usb_xhci_xfer_nak(xfer); xfer->running_async = 0; xfer->running_retry = 1; xfer->complete = 0; @@ -1422,10 +1424,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) if (ret >= 0) { xfer->status = CC_SUCCESS; xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1); + trace_usb_xhci_xfer_success(xfer, ret); return 0; } /* error */ + trace_usb_xhci_xfer_error(xfer, ret); switch (ret) { case USB_RET_NODEV: xfer->status = CC_USB_TRANSACTION_ERROR; @@ -1461,11 +1465,12 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) USBDevice *dev; int ret; - DPRINTF("xhci_fire_ctl_transfer(slot=%d)\n", xfer->slotid); - trb_setup = &xfer->trbs[0]; trb_status = &xfer->trbs[xfer->trb_count-1]; + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, + trb_setup->parameter >> 48); + /* at most one Event Data TRB allowed after STATUS */ if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { trb_status--; @@ -1606,15 +1611,14 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext unsigned int length = 0; XHCITRB *trb; - DPRINTF("xhci_fire_transfer(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); - for (i = 0; i < xfer->trb_count; i++) { trb = &xfer->trbs[i]; if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) { length += trb->status & 0x1ffff; } } - DPRINTF("xhci: total TD length=%d\n", length); + + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length); if (!epctx->has_bg) { xfer->data_length = length; @@ -1670,15 +1674,13 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid XHCITransfer *xfer = epctx->retry; int result; - DPRINTF("xhci: retry nack'ed transfer ...\n"); + trace_usb_xhci_xfer_retry(xfer); assert(xfer->running_retry); xhci_setup_packet(xfer, xfer->packet.ep->dev); result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); if (result == USB_RET_NAK) { - DPRINTF("xhci: ... xfer still nacked\n"); return; } - DPRINTF("xhci: ... result %d\n", result); xhci_complete_packet(xfer, result); assert(!xfer->running_retry); epctx->retry = NULL; diff --git a/trace-events b/trace-events index 74fdf6ab14..74f661ca14 100644 --- a/trace-events +++ b/trace-events @@ -311,6 +311,12 @@ usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" +usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t length) "%p: slotid %d, epid %d, length %d" +usb_xhci_xfer_async(void *xfer) "%p" +usb_xhci_xfer_nak(void *xfer) "%p" +usb_xhci_xfer_retry(void *xfer) "%p" +usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d" +usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d" # hw/usb/desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" From 348f10374ac50b2e4511764fc5fae7cacef95ffa Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 15:15:27 +0200 Subject: [PATCH 19/37] xhci: trace: slots Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 12 ++++++------ trace-events | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 7e823da0d8..6c2ff024e0 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1750,8 +1750,8 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid) { + trace_usb_xhci_slot_enable(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_enable_slot(%d)\n", slotid); xhci->slots[slotid-1].enabled = 1; xhci->slots[slotid-1].port = 0; memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31); @@ -1763,8 +1763,8 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) { int i; + trace_usb_xhci_slot_disable(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_disable_slot(%d)\n", slotid); for (i = 1; i <= 31; i++) { if (xhci->slots[slotid-1].eps[i-1]) { @@ -1790,8 +1790,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, int i; TRBCCode res; + trace_usb_xhci_slot_address(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_address_slot(%d)\n", slotid); dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high); pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx)); @@ -1877,8 +1877,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid, int i; TRBCCode res; + trace_usb_xhci_slot_configure(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_configure_slot(%d)\n", slotid); ictx = xhci_mask64(pictx); octx = xhci->slots[slotid-1].ctx; @@ -1965,8 +1965,8 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid, uint32_t islot_ctx[4]; uint32_t slot_ctx[4]; + trace_usb_xhci_slot_evaluate(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_evaluate_slot(%d)\n", slotid); ictx = xhci_mask64(pictx); octx = xhci->slots[slotid-1].ctx; @@ -2028,8 +2028,8 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid) dma_addr_t octx; int i; + trace_usb_xhci_slot_reset(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); - DPRINTF("xhci_reset_slot(%d)\n", slotid); octx = xhci->slots[slotid-1].ctx; diff --git a/trace-events b/trace-events index 74f661ca14..293eddbbdb 100644 --- a/trace-events +++ b/trace-events @@ -306,6 +306,12 @@ usb_xhci_irq_intx(uint32_t level) "level %d" usb_xhci_irq_msi(uint32_t nr) "nr %d" usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x" usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_slot_enable(uint32_t slotid) "slotid %d" +usb_xhci_slot_disable(uint32_t slotid) "slotid %d" +usb_xhci_slot_address(uint32_t slotid) "slotid %d" +usb_xhci_slot_configure(uint32_t slotid) "slotid %d" +usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d" +usb_xhci_slot_reset(uint32_t slotid) "slotid %d" usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d" From eb36a88e6b3a93001ba224aab5e631d251acae65 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 9 May 2012 17:06:36 +0200 Subject: [PATCH 20/37] ehci: add EHCIPacket Add a separate EHCIPacket struct and move fields over from EHCIQueue. Preparing for supporting multiple packets per queue being in flight at the same time. No functional changes yet. Fix some codestyle issues along the way. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 234 +++++++++++++++++++++++++++++++--------------- trace-events | 1 + 2 files changed, 158 insertions(+), 77 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index e759c996ce..03ae09fc94 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -334,6 +334,7 @@ typedef struct EHCIfstn { uint32_t backptr; // Standard next link pointer } EHCIfstn; +typedef struct EHCIPacket EHCIPacket; typedef struct EHCIQueue EHCIQueue; typedef struct EHCIState EHCIState; @@ -343,6 +344,21 @@ enum async_state { EHCI_ASYNC_FINISHED, }; +struct EHCIPacket { + EHCIQueue *queue; + QTAILQ_ENTRY(EHCIPacket) next; + + EHCIqtd qtd; /* copy of current QTD (being worked on) */ + uint32_t qtdaddr; /* address QTD read from */ + + USBPacket packet; + QEMUSGList sgl; + int pid; + uint32_t tbytes; + enum async_state async; + int usb_status; +}; + struct EHCIQueue { EHCIState *ehci; QTAILQ_ENTRY(EHCIQueue) next; @@ -352,17 +368,10 @@ struct EHCIQueue { /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) */ - EHCIqh qh; // copy of current QH (being worked on) - uint32_t qhaddr; // address QH read from - EHCIqtd qtd; // copy of current QTD (being worked on) - uint32_t qtdaddr; // address QTD read from - - USBPacket packet; - QEMUSGList sgl; - int pid; - uint32_t tbytes; - enum async_state async; - int usb_status; + EHCIqh qh; /* copy of current QH (being worked on) */ + uint32_t qhaddr; /* address QH read from */ + uint32_t qtdaddr; /* address QTD read from */ + QTAILQ_HEAD(, EHCIPacket) packets; }; typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; @@ -655,6 +664,35 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, (bool)(sitd->results & SITD_RESULTS_ACTIVE)); } +/* packet management */ + +static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) +{ + EHCIPacket *p; + +#if 1 + /* temporary, we don't handle multiple packets per queue (yet) */ + assert(QTAILQ_EMPTY(&q->packets)); +#endif + p = g_new0(EHCIPacket, 1); + p->queue = q; + usb_packet_init(&p->packet); + QTAILQ_INSERT_TAIL(&q->packets, p, next); + trace_usb_ehci_packet_action(p->queue, p, "alloc"); + return p; +} + +static void ehci_free_packet(EHCIPacket *p) +{ + trace_usb_ehci_packet_action(p->queue, p, "free"); + if (p->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&p->packet); + } + QTAILQ_REMOVE(&p->queue->packets, p, next); + usb_packet_cleanup(&p->packet); + g_free(p); +} + /* queue management */ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) @@ -664,7 +702,7 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) q = g_malloc0(sizeof(*q)); q->ehci = ehci; - usb_packet_init(&q->packet); + QTAILQ_INIT(&q->packets); QTAILQ_INSERT_HEAD(head, q, next); trace_usb_ehci_queue_action(q, "alloc"); return q; @@ -673,9 +711,11 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) static void ehci_free_queue(EHCIQueue *q, int async) { EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues; + EHCIPacket *p; + trace_usb_ehci_queue_action(q, "free"); - if (q->async == EHCI_ASYNC_INFLIGHT) { - usb_cancel_packet(&q->packet); + while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { + ehci_free_packet(p); } QTAILQ_REMOVE(head, q, next); g_free(q); @@ -718,10 +758,11 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q, *tmp; + int addr; QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - if (!usb_packet_is_inflight(&q->packet) || - q->packet.ep->dev != dev) { + addr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); + if (addr != dev->addr) { continue; } ehci_free_queue(q, async); @@ -1169,21 +1210,25 @@ static inline int put_dwords(EHCIState *ehci, uint32_t addr, static int ehci_qh_do_overlay(EHCIQueue *q) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int i; int dtoggle; int ping; int eps; int reload; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + // remember values in fields to preserve in qh after overlay dtoggle = q->qh.token & QTD_TOKEN_DTOGGLE; ping = q->qh.token & QTD_TOKEN_PING; - q->qh.current_qtd = q->qtdaddr; - q->qh.next_qtd = q->qtd.next; - q->qh.altnext_qtd = q->qtd.altnext; - q->qh.token = q->qtd.token; + q->qh.current_qtd = p->qtdaddr; + q->qh.next_qtd = p->qtd.next; + q->qh.altnext_qtd = p->qtd.altnext; + q->qh.token = p->qtd.token; eps = get_field(q->qh.epchar, QH_EPCHAR_EPS); @@ -1196,7 +1241,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q) set_field(&q->qh.altnext_qtd, reload, QH_ALTNEXT_NAKCNT); for (i = 0; i < 5; i++) { - q->qh.bufptr[i] = q->qtd.bufptr[i]; + q->qh.bufptr[i] = p->qtd.bufptr[i]; } if (!(q->qh.epchar & QH_EPCHAR_DTC)) { @@ -1214,15 +1259,15 @@ static int ehci_qh_do_overlay(EHCIQueue *q) return 0; } -static int ehci_init_transfer(EHCIQueue *q) +static int ehci_init_transfer(EHCIPacket *p) { uint32_t cpage, offset, bytes, plen; dma_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; - pci_dma_sglist_init(&q->sgl, &q->ehci->dev, 5); + cpage = get_field(p->qtd.token, QTD_TOKEN_CPAGE); + bytes = get_field(p->qtd.token, QTD_TOKEN_TBYTES); + offset = p->qtd.bufptr[0] & ~QTD_BUFPTR_MASK; + pci_dma_sglist_init(&p->sgl, &p->queue->ehci->dev, 5); while (bytes > 0) { if (cpage > 4) { @@ -1230,7 +1275,7 @@ static int ehci_init_transfer(EHCIQueue *q) return USB_RET_PROCERR; } - page = q->qh.bufptr[cpage] & QTD_BUFPTR_MASK; + page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; page += offset; plen = bytes; if (plen > 4096 - offset) { @@ -1239,7 +1284,7 @@ static int ehci_init_transfer(EHCIQueue *q) cpage++; } - qemu_sglist_add(&q->sgl, page, plen); + qemu_sglist_add(&p->sgl, page, plen); bytes -= plen; } return 0; @@ -1249,8 +1294,6 @@ static void ehci_finish_transfer(EHCIQueue *q, int status) { uint32_t cpage, offset; - qemu_sglist_destroy(&q->sgl); - if (status > 0) { /* update cpage & offset */ cpage = get_field(q->qh.token, QTD_TOKEN_CPAGE); @@ -1268,7 +1311,7 @@ static void ehci_finish_transfer(EHCIQueue *q, int status) static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) { - EHCIQueue *q; + EHCIPacket *p; EHCIState *s = port->opaque; uint32_t portsc = s->portsc[port->index]; @@ -1278,23 +1321,27 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *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->result; + p = container_of(packet, EHCIPacket, packet); + trace_usb_ehci_packet_action(p->queue, p, "wakeup"); + assert(p->async == EHCI_ASYNC_INFLIGHT); + p->async = EHCI_ASYNC_FINISHED; + p->usb_status = packet->result; } static void ehci_execute_complete(EHCIQueue *q) { - assert(q->async != EHCI_ASYNC_INFLIGHT); - q->async = EHCI_ASYNC_NONE; + EHCIPacket *p = QTAILQ_FIRST(&q->packets); + + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + assert(p->async != EHCI_ASYNC_INFLIGHT); + p->async = EHCI_ASYNC_NONE; DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); - if (q->usb_status < 0) { - switch(q->usb_status) { + if (p->usb_status < 0) { + switch (p->usb_status) { case USB_RET_IOERROR: case USB_RET_NODEV: q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR); @@ -1314,28 +1361,29 @@ static void ehci_execute_complete(EHCIQueue *q) break; default: /* should not be triggerable */ - fprintf(stderr, "USB invalid response %d to handle\n", q->usb_status); + fprintf(stderr, "USB invalid response %d\n", p->usb_status); assert(0); break; } - } else if ((q->usb_status > q->tbytes) && (q->pid == USB_TOKEN_IN)) { - q->usb_status = USB_RET_BABBLE; + } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) { + p->usb_status = USB_RET_BABBLE; q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE); ehci_record_interrupt(q->ehci, USBSTS_ERRINT); } else { // TODO check 4.12 for splits - if (q->tbytes && q->pid == USB_TOKEN_IN) { - q->tbytes -= q->usb_status; + if (p->tbytes && p->pid == USB_TOKEN_IN) { + p->tbytes -= p->usb_status; } else { - q->tbytes = 0; + p->tbytes = 0; } - DPRINTF("updating tbytes to %d\n", q->tbytes); - set_field(&q->qh.token, q->tbytes, QTD_TOKEN_TBYTES); + DPRINTF("updating tbytes to %d\n", p->tbytes); + set_field(&q->qh.token, p->tbytes, QTD_TOKEN_TBYTES); } - ehci_finish_transfer(q, q->usb_status); - usb_packet_unmap(&q->packet); + ehci_finish_transfer(q, p->usb_status); + qemu_sglist_destroy(&p->sgl); + usb_packet_unmap(&p->packet); q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; @@ -1349,32 +1397,44 @@ static void ehci_execute_complete(EHCIQueue *q) static int ehci_execute(EHCIQueue *q) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); USBDevice *dev; USBEndpoint *ep; int ret; int endp; int devadr; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) { fprintf(stderr, "Attempting to execute inactive QH\n"); return USB_RET_PROCERR; } - q->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; - if (q->tbytes > BUFF_SIZE) { + p->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; + if (p->tbytes > BUFF_SIZE) { fprintf(stderr, "Request for more bytes than allowed\n"); return USB_RET_PROCERR; } - q->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; - switch(q->pid) { - case 0: q->pid = USB_TOKEN_OUT; break; - case 1: q->pid = USB_TOKEN_IN; break; - case 2: q->pid = USB_TOKEN_SETUP; break; - default: fprintf(stderr, "bad token\n"); break; + p->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; + switch (p->pid) { + case 0: + p->pid = USB_TOKEN_OUT; + break; + case 1: + p->pid = USB_TOKEN_IN; + break; + case 2: + p->pid = USB_TOKEN_SETUP; + break; + default: + fprintf(stderr, "bad token\n"); + break; } - if (ehci_init_transfer(q) != 0) { + if (ehci_init_transfer(p) != 0) { return USB_RET_PROCERR; } @@ -1383,12 +1443,12 @@ static int ehci_execute(EHCIQueue *q) /* TODO: associating device with ehci port */ dev = ehci_find_device(q->ehci, devadr); - ep = usb_ep_get(dev, q->pid, endp); + ep = usb_ep_get(dev, p->pid, endp); - usb_packet_setup(&q->packet, q->pid, ep); - usb_packet_map(&q->packet, &q->sgl); + usb_packet_setup(&p->packet, p->pid, ep); + usb_packet_map(&p->packet, &p->sgl); - ret = usb_handle_packet(dev, &q->packet); + ret = usb_handle_packet(dev, &p->packet); 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, @@ -1601,6 +1661,7 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { + EHCIPacket *p; uint32_t entry; EHCIQueue *q; @@ -1609,6 +1670,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) if (NULL == q) { q = ehci_alloc_queue(ehci, async); } + p = QTAILQ_FIRST(&q->packets); q->qhaddr = entry; q->seen++; @@ -1623,12 +1685,12 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); - if (q->async == EHCI_ASYNC_INFLIGHT) { + if (p && p->async == EHCI_ASYNC_INFLIGHT) { /* I/O still in progress -- skip queue */ ehci_set_state(ehci, async, EST_HORIZONTALQH); goto out; } - if (q->async == EHCI_ASYNC_FINISHED) { + if (p && p->async == EHCI_ASYNC_FINISHED) { /* I/O finished -- continue processing queue */ trace_usb_ehci_queue_action(q, "resume"); ehci_set_state(ehci, async, EST_EXECUTING); @@ -1767,13 +1829,18 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) /* Section 4.10.2 - paragraph 4 */ static int ehci_state_fetchqtd(EHCIQueue *q, int async) { + EHCIqtd qtd; + EHCIPacket *p; int again = 0; - get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qtd, + get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); - ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &q->qtd); + ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); - if (q->qtd.token & QTD_TOKEN_ACTIVE) { + if (qtd.token & QTD_TOKEN_ACTIVE) { + p = ehci_alloc_packet(q); + p->qtdaddr = q->qtdaddr; + p->qtd = qtd; ehci_set_state(q->ehci, async, EST_EXECUTE); again = 1; } else { @@ -1818,8 +1885,12 @@ static void ehci_flush_qh(EHCIQueue *q) static int ehci_state_execute(EHCIQueue *q, int async) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + if (ehci_qh_do_overlay(q) != 0) { return -1; } @@ -1841,15 +1912,15 @@ static int ehci_state_execute(EHCIQueue *q, int async) ehci_set_usbsts(q->ehci, USBSTS_REC); } - q->usb_status = ehci_execute(q); - if (q->usb_status == USB_RET_PROCERR) { + p->usb_status = ehci_execute(q); + if (p->usb_status == USB_RET_PROCERR) { again = -1; goto out; } - if (q->usb_status == USB_RET_ASYNC) { + if (p->usb_status == USB_RET_ASYNC) { ehci_flush_qh(q); trace_usb_ehci_queue_action(q, "suspend"); - q->async = EHCI_ASYNC_INFLIGHT; + p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, async, EST_HORIZONTALQH); again = 1; goto out; @@ -1864,13 +1935,17 @@ out: static int ehci_state_executing(EHCIQueue *q, int async) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + ehci_execute_complete(q); - if (q->usb_status == USB_RET_ASYNC) { + if (p->usb_status == USB_RET_ASYNC) { goto out; } - if (q->usb_status == USB_RET_PROCERR) { + if (p->usb_status == USB_RET_PROCERR) { again = -1; goto out; } @@ -1885,7 +1960,7 @@ static int ehci_state_executing(EHCIQueue *q, int async) } /* 4.10.5 */ - if (q->usb_status == USB_RET_NAK) { + if (p->usb_status == USB_RET_NAK) { ehci_set_state(q->ehci, async, EST_HORIZONTALQH); } else { ehci_set_state(q->ehci, async, EST_WRITEBACK); @@ -1901,12 +1976,17 @@ out: static int ehci_state_writeback(EHCIQueue *q, int async) { + EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; /* Write back the QTD from the QH area */ - ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), (EHCIqtd*) &q->qh.next_qtd); - put_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &q->qh.next_qtd, + assert(p != NULL); + assert(p->qtdaddr == q->qtdaddr); + + ehci_trace_qtd(q, NLPTR_GET(p->qtdaddr), (EHCIqtd *) &q->qh.next_qtd); + put_dwords(q->ehci, NLPTR_GET(p->qtdaddr), (uint32_t *) &q->qh.next_qtd, sizeof(EHCIqtd) >> 2); + ehci_free_packet(p); /* * EHCI specs say go horizontal here. diff --git a/trace-events b/trace-events index 293eddbbdb..f70523c47e 100644 --- a/trace-events +++ b/trace-events @@ -257,6 +257,7 @@ usb_ehci_port_detach(uint32_t port) "detach port #%d" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t len, uint32_t bufpos) "write %d, cpage %d, offset 0x%03x, addr 0x%08x, len %d, bufpos %d" usb_ehci_queue_action(void *q, const char *action) "q %p: %s" +usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" # hw/usb/hcd-uhci.c usb_uhci_reset(void) "=== RESET ===" From 4224558f87371f87929df1064ddb733104fe5f15 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 10 May 2012 12:10:47 +0200 Subject: [PATCH 21/37] ehci: make ehci_execute work on EHCIPacket instead of EHCIQueue This way it is possible to use ehci_execute to submit others than the first EHCIPacket of the EHCIQueue. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 03ae09fc94..a7cf2820af 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1395,30 +1395,26 @@ static void ehci_execute_complete(EHCIQueue *q) // 4.10.3 -static int ehci_execute(EHCIQueue *q) +static int ehci_execute(EHCIPacket *p) { - EHCIPacket *p = QTAILQ_FIRST(&q->packets); USBDevice *dev; USBEndpoint *ep; int ret; int endp; int devadr; - assert(p != NULL); - assert(p->qtdaddr == q->qtdaddr); - - if ( !(q->qh.token & QTD_TOKEN_ACTIVE)) { - fprintf(stderr, "Attempting to execute inactive QH\n"); + if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { + fprintf(stderr, "Attempting to execute inactive qtd\n"); return USB_RET_PROCERR; } - p->tbytes = (q->qh.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; + p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH; if (p->tbytes > BUFF_SIZE) { fprintf(stderr, "Request for more bytes than allowed\n"); return USB_RET_PROCERR; } - p->pid = (q->qh.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; + p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH; switch (p->pid) { case 0: p->pid = USB_TOKEN_OUT; @@ -1438,11 +1434,11 @@ static int ehci_execute(EHCIQueue *q) return USB_RET_PROCERR; } - endp = get_field(q->qh.epchar, QH_EPCHAR_EP); - devadr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); + endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); + devadr = get_field(p->queue->qh.epchar, QH_EPCHAR_DEVADDR); /* TODO: associating device with ehci port */ - dev = ehci_find_device(q->ehci, devadr); + dev = ehci_find_device(p->queue->ehci, devadr); ep = usb_ep_get(dev, p->pid, endp); usb_packet_setup(&p->packet, p->pid, ep); @@ -1912,7 +1908,7 @@ static int ehci_state_execute(EHCIQueue *q, int async) ehci_set_usbsts(q->ehci, USBSTS_REC); } - p->usb_status = ehci_execute(q); + p->usb_status = ehci_execute(p); if (p->usb_status == USB_RET_PROCERR) { again = -1; goto out; From e59928b3d148f97658e804fa724cdaa75dc8935a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 10 May 2012 12:18:45 +0200 Subject: [PATCH 22/37] ehci: cache USBDevice in EHCIQueue Keep a USBDevice pointer in EHCIQueue so we don't have to lookup the device on each usb packet submission. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index a7cf2820af..d7131bd4d1 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -371,6 +371,7 @@ struct EHCIQueue { EHCIqh qh; /* copy of current QH (being worked on) */ uint32_t qhaddr; /* address QH read from */ uint32_t qtdaddr; /* address QTD read from */ + USBDevice *dev; QTAILQ_HEAD(, EHCIPacket) packets; }; @@ -758,11 +759,9 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q, *tmp; - int addr; QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - addr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); - if (addr != dev->addr) { + if (q->dev != dev) { continue; } ehci_free_queue(q, async); @@ -1397,11 +1396,9 @@ static void ehci_execute_complete(EHCIQueue *q) static int ehci_execute(EHCIPacket *p) { - USBDevice *dev; USBEndpoint *ep; int ret; int endp; - int devadr; if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { fprintf(stderr, "Attempting to execute inactive qtd\n"); @@ -1435,16 +1432,12 @@ static int ehci_execute(EHCIPacket *p) } endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); - devadr = get_field(p->queue->qh.epchar, QH_EPCHAR_DEVADDR); - - /* TODO: associating device with ehci port */ - dev = ehci_find_device(p->queue->ehci, devadr); - ep = usb_ep_get(dev, p->pid, endp); + ep = usb_ep_get(p->queue->dev, p->pid, endp); usb_packet_setup(&p->packet, p->pid, ep); usb_packet_map(&p->packet, &p->sgl); - ret = usb_handle_packet(dev, &p->packet); + ret = usb_handle_packet(p->queue->dev, &p->packet); 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, @@ -1658,7 +1651,7 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { EHCIPacket *p; - uint32_t entry; + uint32_t entry, devaddr; EHCIQueue *q; entry = ehci_get_fetch_addr(ehci, async); @@ -1681,6 +1674,20 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); + devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR); + if (q->dev != NULL && q->dev->addr != devaddr) { + if (!QTAILQ_EMPTY(&q->packets)) { + /* should not happen (guest bug) */ + while ((p = QTAILQ_FIRST(&q->packets)) != NULL) { + ehci_free_packet(p); + } + } + q->dev = NULL; + } + if (q->dev == NULL) { + q->dev = ehci_find_device(q->ehci, devaddr); + } + if (p && p->async == EHCI_ASYNC_INFLIGHT) { /* I/O still in progress -- skip queue */ ehci_set_state(ehci, async, EST_HORIZONTALQH); From cd6657156e99a5c8a5d1b64ab818288d48c1295f Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 10 May 2012 14:13:41 +0200 Subject: [PATCH 23/37] ehci: move ehci_flush_qh Move ehci_flush_qh() function up in the source code. No code change. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d7131bd4d1..f21b4bec9f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1553,6 +1553,24 @@ static int ehci_process_itd(EHCIState *ehci, return 0; } + +/* + * Write the qh back to guest physical memory. This step isn't + * in the EHCI spec but we need to do it since we don't share + * physical memory with our guest VM. + * + * The first three dwords are read-only for the EHCI, so skip them + * when writing back the qh. + */ +static void ehci_flush_qh(EHCIQueue *q) +{ + uint32_t *qh = (uint32_t *) &q->qh; + uint32_t dwords = sizeof(EHCIqh) >> 2; + uint32_t addr = NLPTR_GET(q->qhaddr); + + put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); +} + /* This state is the entry point for asynchronous schedule * processing. Entry here consitutes a EHCI start event state (4.8.5) */ @@ -1869,23 +1887,6 @@ static int ehci_state_horizqh(EHCIQueue *q, int async) return again; } -/* - * Write the qh back to guest physical memory. This step isn't - * in the EHCI spec but we need to do it since we don't share - * physical memory with our guest VM. - * - * The first three dwords are read-only for the EHCI, so skip them - * when writing back the qh. - */ -static void ehci_flush_qh(EHCIQueue *q) -{ - uint32_t *qh = (uint32_t *) &q->qh; - uint32_t dwords = sizeof(EHCIqh) >> 2; - uint32_t addr = NLPTR_GET(q->qhaddr); - - put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); -} - static int ehci_state_execute(EHCIQueue *q, int async) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); From 773dc9cdfd4a2c55f0f3343b8e120011d96831e5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 10 May 2012 14:12:38 +0200 Subject: [PATCH 24/37] ehci: add queuing support Add packet queuing. Follow the qTD chain to see if there are more packets we can submit. Improves performance on larger transfers, especially with usb-host, as we don't have to wait for a packet to finish before sending the next one to the host for processing. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 60 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f21b4bec9f..ccaa94772c 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -671,10 +671,6 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) { EHCIPacket *p; -#if 1 - /* temporary, we don't handle multiple packets per queue (yet) */ - assert(QTAILQ_EMPTY(&q->packets)); -#endif p = g_new0(EHCIPacket, 1); p->queue = q; usb_packet_init(&p->packet); @@ -1394,7 +1390,7 @@ static void ehci_execute_complete(EHCIQueue *q) // 4.10.3 -static int ehci_execute(EHCIPacket *p) +static int ehci_execute(EHCIPacket *p, const char *action) { USBEndpoint *ep; int ret; @@ -1437,6 +1433,7 @@ static int ehci_execute(EHCIPacket *p) usb_packet_setup(&p->packet, p->pid, ep); usb_packet_map(&p->packet, &p->sgl); + trace_usb_ehci_packet_action(p->queue, p, action); ret = usb_handle_packet(p->queue->dev, &p->packet); DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd " "(total %d) endp %x ret %d\n", @@ -1713,7 +1710,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } if (p && p->async == EHCI_ASYNC_FINISHED) { /* I/O finished -- continue processing queue */ - trace_usb_ehci_queue_action(q, "resume"); + trace_usb_ehci_packet_action(p->queue, p, "complete"); ehci_set_state(ehci, async, EST_EXECUTING); goto out; } @@ -1858,7 +1855,22 @@ static int ehci_state_fetchqtd(EHCIQueue *q, int async) sizeof(EHCIqtd) >> 2); ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); - if (qtd.token & QTD_TOKEN_ACTIVE) { + p = QTAILQ_FIRST(&q->packets); + while (p != NULL && p->qtdaddr != q->qtdaddr) { + /* should not happen (guest bug) */ + ehci_free_packet(p); + p = QTAILQ_FIRST(&q->packets); + } + if (p != NULL) { + ehci_qh_do_overlay(q); + ehci_flush_qh(q); + if (p->async == EHCI_ASYNC_INFLIGHT) { + ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + } else { + ehci_set_state(q->ehci, async, EST_EXECUTING); + } + again = 1; + } else if (qtd.token & QTD_TOKEN_ACTIVE) { p = ehci_alloc_packet(q); p->qtdaddr = q->qtdaddr; p->qtd = qtd; @@ -1887,6 +1899,35 @@ static int ehci_state_horizqh(EHCIQueue *q, int async) return again; } +static void ehci_fill_queue(EHCIPacket *p, int async) +{ + EHCIQueue *q = p->queue; + EHCIqtd qtd = p->qtd; + uint32_t qtdaddr; + + for (;;) { + if (NLPTR_TBIT(qtd.altnext) == 0) { + break; + } + if (NLPTR_TBIT(qtd.next) != 0) { + break; + } + qtdaddr = qtd.next; + get_dwords(q->ehci, NLPTR_GET(qtdaddr), + (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); + ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd); + if (!(qtd.token & QTD_TOKEN_ACTIVE)) { + break; + } + p = ehci_alloc_packet(q); + p->qtdaddr = qtdaddr; + p->qtd = qtd; + p->usb_status = ehci_execute(p, "queue"); + assert(p->usb_status = USB_RET_ASYNC); + p->async = EHCI_ASYNC_INFLIGHT; + } +} + static int ehci_state_execute(EHCIQueue *q, int async) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); @@ -1916,17 +1957,18 @@ static int ehci_state_execute(EHCIQueue *q, int async) ehci_set_usbsts(q->ehci, USBSTS_REC); } - p->usb_status = ehci_execute(p); + p->usb_status = ehci_execute(p, "process"); if (p->usb_status == USB_RET_PROCERR) { again = -1; goto out; } if (p->usb_status == USB_RET_ASYNC) { ehci_flush_qh(q); - trace_usb_ehci_queue_action(q, "suspend"); + trace_usb_ehci_packet_action(p->queue, p, "async"); p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, async, EST_HORIZONTALQH); again = 1; + ehci_fill_queue(p, async); goto out; } From 8f6d5e26b1a2c4e87810b2798f005566a20cfea7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 08:56:49 +0200 Subject: [PATCH 25/37] ehci: tweak queue initialization Little tweak for the queue initialization, set the QH address in the allocation function. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index ccaa94772c..aa67af6286 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -692,13 +692,14 @@ static void ehci_free_packet(EHCIPacket *p) /* queue management */ -static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, int async) +static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q; q = g_malloc0(sizeof(*q)); q->ehci = ehci; + q->qhaddr = addr; QTAILQ_INIT(&q->packets); QTAILQ_INSERT_HEAD(head, q, next); trace_usb_ehci_queue_action(q, "alloc"); @@ -1672,12 +1673,11 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) entry = ehci_get_fetch_addr(ehci, async); q = ehci_find_queue_by_qh(ehci, entry, async); if (NULL == q) { - q = ehci_alloc_queue(ehci, async); + q = ehci_alloc_queue(ehci, entry, async); } p = QTAILQ_FIRST(&q->packets); - q->qhaddr = entry; - q->seen++; + q->seen++; if (q->seen > 1) { /* we are going in circles -- stop processing */ ehci_set_state(ehci, async, EST_ACTIVE); From ae0138a8eab27c9816b2f1b6a47b2cf1c580a1ca Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 09:05:15 +0200 Subject: [PATCH 26/37] ehci: add async field to EHCIQueue Keep track whenever a EHCIQueue is part of the async or periodic schedule. This way we don't have to pass around the async flag everywhere but can look it up from the EHCIQueue struct when needed. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 92 +++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index aa67af6286..f363f1436a 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -364,6 +364,7 @@ struct EHCIQueue { QTAILQ_ENTRY(EHCIQueue) next; uint32_t seen; uint64_t ts; + int async; /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) @@ -700,15 +701,16 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) q = g_malloc0(sizeof(*q)); q->ehci = ehci; q->qhaddr = addr; + q->async = async; QTAILQ_INIT(&q->packets); QTAILQ_INSERT_HEAD(head, q, next); trace_usb_ehci_queue_action(q, "alloc"); return q; } -static void ehci_free_queue(EHCIQueue *q, int async) +static void ehci_free_queue(EHCIQueue *q) { - EHCIQueueHead *head = async ? &q->ehci->aqueues : &q->ehci->pqueues; + EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; EHCIPacket *p; trace_usb_ehci_queue_action(q, "free"); @@ -748,7 +750,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) /* allow 0.25 sec idle */ continue; } - ehci_free_queue(q, async); + ehci_free_queue(q); } } @@ -761,7 +763,7 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) if (q->dev != dev) { continue; } - ehci_free_queue(q, async); + ehci_free_queue(q); } } @@ -771,7 +773,7 @@ static void ehci_queues_rip_all(EHCIState *ehci, int async) EHCIQueue *q, *tmp; QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - ehci_free_queue(q, async); + ehci_free_queue(q); } } @@ -1806,7 +1808,7 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async) } /* Section 4.10.2 - paragraph 3 */ -static int ehci_state_advqueue(EHCIQueue *q, int async) +static int ehci_state_advqueue(EHCIQueue *q) { #if 0 /* TO-DO: 4.10.2 - paragraph 2 @@ -1825,27 +1827,27 @@ static int ehci_state_advqueue(EHCIQueue *q, int async) if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { q->qtdaddr = q->qh.altnext_qtd; - ehci_set_state(q->ehci, async, EST_FETCHQTD); + ehci_set_state(q->ehci, q->async, EST_FETCHQTD); /* * next qTD is valid */ } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { q->qtdaddr = q->qh.next_qtd; - ehci_set_state(q->ehci, async, EST_FETCHQTD); + ehci_set_state(q->ehci, q->async, EST_FETCHQTD); /* * no valid qTD, try next QH */ } else { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); } return 1; } /* Section 4.10.2 - paragraph 4 */ -static int ehci_state_fetchqtd(EHCIQueue *q, int async) +static int ehci_state_fetchqtd(EHCIQueue *q) { EHCIqtd qtd; EHCIPacket *p; @@ -1865,41 +1867,41 @@ static int ehci_state_fetchqtd(EHCIQueue *q, int async) ehci_qh_do_overlay(q); ehci_flush_qh(q); if (p->async == EHCI_ASYNC_INFLIGHT) { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); } else { - ehci_set_state(q->ehci, async, EST_EXECUTING); + ehci_set_state(q->ehci, q->async, EST_EXECUTING); } again = 1; } else if (qtd.token & QTD_TOKEN_ACTIVE) { p = ehci_alloc_packet(q); p->qtdaddr = q->qtdaddr; p->qtd = qtd; - ehci_set_state(q->ehci, async, EST_EXECUTE); + ehci_set_state(q->ehci, q->async, EST_EXECUTE); again = 1; } else { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); again = 1; } return again; } -static int ehci_state_horizqh(EHCIQueue *q, int async) +static int ehci_state_horizqh(EHCIQueue *q) { int again = 0; - if (ehci_get_fetch_addr(q->ehci, async) != q->qh.next) { - ehci_set_fetch_addr(q->ehci, async, q->qh.next); - ehci_set_state(q->ehci, async, EST_FETCHENTRY); + if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) { + ehci_set_fetch_addr(q->ehci, q->async, q->qh.next); + ehci_set_state(q->ehci, q->async, EST_FETCHENTRY); again = 1; } else { - ehci_set_state(q->ehci, async, EST_ACTIVE); + ehci_set_state(q->ehci, q->async, EST_ACTIVE); } return again; } -static void ehci_fill_queue(EHCIPacket *p, int async) +static void ehci_fill_queue(EHCIPacket *p) { EHCIQueue *q = p->queue; EHCIqtd qtd = p->qtd; @@ -1928,7 +1930,7 @@ static void ehci_fill_queue(EHCIPacket *p, int async) } } -static int ehci_state_execute(EHCIQueue *q, int async) +static int ehci_state_execute(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; @@ -1944,16 +1946,16 @@ static int ehci_state_execute(EHCIQueue *q, int async) // TODO write back ptr to async list when done or out of time // TODO Windows does not seem to ever set the MULT field - if (!async) { + if (!q->async) { int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); if (!transactCtr) { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); again = 1; goto out; } } - if (async) { + if (q->async) { ehci_set_usbsts(q->ehci, USBSTS_REC); } @@ -1966,20 +1968,20 @@ static int ehci_state_execute(EHCIQueue *q, int async) ehci_flush_qh(q); trace_usb_ehci_packet_action(p->queue, p, "async"); p->async = EHCI_ASYNC_INFLIGHT; - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); again = 1; - ehci_fill_queue(p, async); + ehci_fill_queue(p); goto out; } - ehci_set_state(q->ehci, async, EST_EXECUTING); + ehci_set_state(q->ehci, q->async, EST_EXECUTING); again = 1; out: return again; } -static int ehci_state_executing(EHCIQueue *q, int async) +static int ehci_state_executing(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; @@ -1997,7 +1999,7 @@ static int ehci_state_executing(EHCIQueue *q, int async) } // 4.10.3 - if (!async) { + if (!q->async) { int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); transactCtr--; set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT); @@ -2007,9 +2009,9 @@ static int ehci_state_executing(EHCIQueue *q, int async) /* 4.10.5 */ if (p->usb_status == USB_RET_NAK) { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); } else { - ehci_set_state(q->ehci, async, EST_WRITEBACK); + ehci_set_state(q->ehci, q->async, EST_WRITEBACK); } again = 1; @@ -2020,7 +2022,7 @@ out: } -static int ehci_state_writeback(EHCIQueue *q, int async) +static int ehci_state_writeback(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); int again = 0; @@ -2043,10 +2045,10 @@ static int ehci_state_writeback(EHCIQueue *q, int async) * bit is clear. */ if (q->qh.token & QTD_TOKEN_HALT) { - ehci_set_state(q->ehci, async, EST_HORIZONTALQH); + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); again = 1; } else { - ehci_set_state(q->ehci, async, EST_ADVANCEQUEUE); + ehci_set_state(q->ehci, q->async, EST_ADVANCEQUEUE); again = 1; } return again; @@ -2056,8 +2058,7 @@ static int ehci_state_writeback(EHCIQueue *q, int async) * This is the state machine that is common to both async and periodic */ -static void ehci_advance_state(EHCIState *ehci, - int async) +static void ehci_advance_state(EHCIState *ehci, int async) { EHCIQueue *q = NULL; int again; @@ -2074,7 +2075,12 @@ static void ehci_advance_state(EHCIState *ehci, case EST_FETCHQH: q = ehci_state_fetchqh(ehci, async); - again = q ? 1 : 0; + if (q != NULL) { + assert(q->async == async); + again = 1; + } else { + again = 0; + } break; case EST_FETCHITD: @@ -2086,29 +2092,29 @@ static void ehci_advance_state(EHCIState *ehci, break; case EST_ADVANCEQUEUE: - again = ehci_state_advqueue(q, async); + again = ehci_state_advqueue(q); break; case EST_FETCHQTD: - again = ehci_state_fetchqtd(q, async); + again = ehci_state_fetchqtd(q); break; case EST_HORIZONTALQH: - again = ehci_state_horizqh(q, async); + again = ehci_state_horizqh(q); break; case EST_EXECUTE: - again = ehci_state_execute(q, async); + again = ehci_state_execute(q); break; case EST_EXECUTING: assert(q != NULL); - again = ehci_state_executing(q, async); + again = ehci_state_executing(q); break; case EST_WRITEBACK: assert(q != NULL); - again = ehci_state_writeback(q, async); + again = ehci_state_writeback(q); break; default: From 0fb3e299bdc0f21e0a0ff7d19276a87e0825d651 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 11:19:11 +0200 Subject: [PATCH 27/37] ehci: move async schedule to bottom half This way we can kick the async schedule independant from the periodic frame timer. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f363f1436a..16627d35c0 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -414,6 +414,7 @@ struct EHCIState { * Internal states, shadow registers, etc */ QEMUTimer *frame_timer; + QEMUBH *async_bh; int attach_poll_counter; int astate; // Current state in asynchronous schedule int pstate; // Current state in periodic schedule @@ -959,6 +960,7 @@ static void ehci_reset(void *opaque) ehci_queues_rip_all(s, 0); ehci_queues_rip_all(s, 1); qemu_del_timer(s->frame_timer); + qemu_bh_cancel(s->async_bh); } static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr) @@ -1111,6 +1113,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { qemu_del_timer(s->frame_timer); + qemu_bh_cancel(s->async_bh); ehci_queues_rip_all(s, 0); ehci_queues_rip_all(s, 1); ehci_set_usbsts(s, USBSTS_HALT); @@ -2290,11 +2293,16 @@ static void ehci_frame_timer(void *opaque) /* Async is not inside loop since it executes everything it can once * called */ - ehci_advance_async_state(ehci); + qemu_bh_schedule(ehci->async_bh); qemu_mod_timer(ehci->frame_timer, expire_time); } +static void ehci_async_bh(void *opaque) +{ + EHCIState *ehci = opaque; + ehci_advance_async_state(ehci); +} static const MemoryRegionOps ehci_mem_ops = { .old_mmio = { @@ -2430,6 +2438,7 @@ static int usb_ehci_initfn(PCIDevice *dev) } s->frame_timer = qemu_new_timer_ns(vm_clock, ehci_frame_timer, s); + s->async_bh = qemu_bh_new(ehci_async_bh, s); QTAILQ_INIT(&s->aqueues); QTAILQ_INIT(&s->pqueues); From ae710b99050a347cc7ef93e1873bf08c9a97b6be Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 11 May 2012 11:31:56 +0200 Subject: [PATCH 28/37] ehci: schedule async bh on async packet completion When a packet completes which happens to be part of the async schedule kick the async bottom half for processing, Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 16627d35c0..8b2dfeda56 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1327,6 +1327,10 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet) assert(p->async == EHCI_ASYNC_INFLIGHT); p->async = EHCI_ASYNC_FINISHED; p->usb_status = packet->result; + + if (p->queue->async) { + qemu_bh_schedule(p->queue->ehci->async_bh); + } } static void ehci_execute_complete(EHCIQueue *q) From 0f588df8b3688b00e77aabaa32e26ece5f19bd39 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 22 May 2012 17:24:59 +0200 Subject: [PATCH 29/37] ehci: kick async schedule on wakeup Kick async schedule when we get a wakeup notification from a usb device. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 8b2dfeda56..f8ed80daee 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -852,6 +852,8 @@ static void ehci_wakeup(USBPort *port) USBPort *companion = s->companion_ports[port->index]; if (companion->ops->wakeup) { companion->ops->wakeup(companion); + } else { + qemu_bh_schedule(s->async_bh); } } } From 7046530c36fa3a3f87692bdb54556f5d891a9c03 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 12:04:50 +0200 Subject: [PATCH 30/37] ehci: fix reset Check for the reset bit first when processing USBCMD register writes. Also break out of the switch, there is no need to check the other bits. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f8ed80daee..3b602b04fa 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1107,6 +1107,12 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) /* Do any register specific pre-write processing here. */ switch(addr) { case USBCMD: + if (val & USBCMD_HCRESET) { + ehci_reset(s); + val = s->usbcmd; + break; + } + if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) { qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); SET_LAST_RUN_CLOCK(s); @@ -1121,10 +1127,6 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) ehci_set_usbsts(s, USBSTS_HALT); } - if (val & USBCMD_HCRESET) { - ehci_reset(s); - val = s->usbcmd; - } /* not supporting dynamic frame list size at the moment */ if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { From ec807d12bdd5c740b4266fbd423b4f8b964e597a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 12:31:34 +0200 Subject: [PATCH 31/37] ehci: add ehci_*_enabled() helpers Add helper functions to query whenever the async / periodic schedule is enabled or not. Put them into use too. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 3b602b04fa..d9c0f0fd1a 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -667,6 +667,21 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, (bool)(sitd->results & SITD_RESULTS_ACTIVE)); } +static inline bool ehci_enabled(EHCIState *s) +{ + return s->usbcmd & USBCMD_RUNSTOP; +} + +static inline bool ehci_async_enabled(EHCIState *s) +{ + return ehci_enabled(s) && (s->usbcmd & USBCMD_ASE); +} + +static inline bool ehci_periodic_enabled(EHCIState *s) +{ + return ehci_enabled(s) && (s->usbcmd & USBCMD_PSE); +} + /* packet management */ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) @@ -1160,7 +1175,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) break; case PERIODICLISTBASE: - if ((s->usbcmd & USBCMD_PSE) && (s->usbcmd & USBCMD_RUNSTOP)) { + if (ehci_periodic_enabled(s)) { fprintf(stderr, "ehci: PERIODIC list base register set while periodic schedule\n" " is enabled and HC is enabled\n"); @@ -1168,7 +1183,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) break; case ASYNCLISTADDR: - if ((s->usbcmd & USBCMD_ASE) && (s->usbcmd & USBCMD_RUNSTOP)) { + if (ehci_async_enabled(s)) { fprintf(stderr, "ehci: ASYNC list address register set while async schedule\n" " is enabled and HC is enabled\n"); @@ -2152,7 +2167,7 @@ static void ehci_advance_async_state(EHCIState *ehci) switch(ehci_get_state(ehci, async)) { case EST_INACTIVE: - if (!(ehci->usbcmd & USBCMD_ASE)) { + if (!ehci_async_enabled(ehci)) { break; } ehci_set_usbsts(ehci, USBSTS_ASS); @@ -2160,7 +2175,7 @@ static void ehci_advance_async_state(EHCIState *ehci) // No break, fall through to ACTIVE case EST_ACTIVE: - if ( !(ehci->usbcmd & USBCMD_ASE)) { + if (!ehci_async_enabled(ehci)) { ehci_queues_rip_all(ehci, async); ehci_clear_usbsts(ehci, USBSTS_ASS); ehci_set_state(ehci, async, EST_INACTIVE); @@ -2213,7 +2228,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) switch(ehci_get_state(ehci, async)) { case EST_INACTIVE: - if ( !(ehci->frindex & 7) && (ehci->usbcmd & USBCMD_PSE)) { + if (!(ehci->frindex & 7) && ehci_periodic_enabled(ehci)) { ehci_set_usbsts(ehci, USBSTS_PSS); ehci_set_state(ehci, async, EST_ACTIVE); // No break, fall through to ACTIVE @@ -2221,7 +2236,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) break; case EST_ACTIVE: - if ( !(ehci->frindex & 7) && !(ehci->usbcmd & USBCMD_PSE)) { + if (!(ehci->frindex & 7) && !ehci_periodic_enabled(ehci)) { ehci_queues_rip_all(ehci, async); ehci_clear_usbsts(ehci, USBSTS_PSS); ehci_set_state(ehci, async, EST_INACTIVE); From b53f685d63e1928c0cee669703a8b30f604e6a6e Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 12:34:18 +0200 Subject: [PATCH 32/37] ehci: update status bits in ehci_set_state Update the status register in the ehci_set_state function, to make sure the guest-visible register is in sync with our internal schedule state. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d9c0f0fd1a..0a550f9cbc 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -591,9 +591,19 @@ static void ehci_set_state(EHCIState *s, int async, int state) if (async) { trace_usb_ehci_state("async", state2str(state)); s->astate = state; + if (s->astate == EST_INACTIVE) { + ehci_clear_usbsts(s, USBSTS_ASS); + } else { + ehci_set_usbsts(s, USBSTS_ASS); + } } else { trace_usb_ehci_state("periodic", state2str(state)); s->pstate = state; + if (s->pstate == EST_INACTIVE) { + ehci_clear_usbsts(s, USBSTS_PSS); + } else { + ehci_set_usbsts(s, USBSTS_PSS); + } } } @@ -2170,14 +2180,12 @@ static void ehci_advance_async_state(EHCIState *ehci) if (!ehci_async_enabled(ehci)) { break; } - ehci_set_usbsts(ehci, USBSTS_ASS); ehci_set_state(ehci, async, EST_ACTIVE); // No break, fall through to ACTIVE case EST_ACTIVE: if (!ehci_async_enabled(ehci)) { ehci_queues_rip_all(ehci, async); - ehci_clear_usbsts(ehci, USBSTS_ASS); ehci_set_state(ehci, async, EST_INACTIVE); break; } @@ -2229,7 +2237,6 @@ static void ehci_advance_periodic_state(EHCIState *ehci) switch(ehci_get_state(ehci, async)) { case EST_INACTIVE: if (!(ehci->frindex & 7) && ehci_periodic_enabled(ehci)) { - ehci_set_usbsts(ehci, USBSTS_PSS); ehci_set_state(ehci, async, EST_ACTIVE); // No break, fall through to ACTIVE } else @@ -2238,7 +2245,6 @@ static void ehci_advance_periodic_state(EHCIState *ehci) case EST_ACTIVE: if (!(ehci->frindex & 7) && !ehci_periodic_enabled(ehci)) { ehci_queues_rip_all(ehci, async); - ehci_clear_usbsts(ehci, USBSTS_PSS); ehci_set_state(ehci, async, EST_INACTIVE); break; } From daf25307b4f8c5c13b3a15059fbf9df55823bb72 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 12:53:43 +0200 Subject: [PATCH 33/37] ehci: fix halt status handling When the enable bits for controller / async schedule / periodic schedule change just make sure we kick the frame timer and let ehci_advance_periodic_state and ehci_advance_async_state handle the controller state changes. This will make ehci set USBSTS_HALT when the controller shutdown is actually done, once both schedules are in inactive state and the USBSTS_PSS and USBSTS_ASS bits are clear. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 0a550f9cbc..f77a26d9d9 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -586,6 +586,17 @@ static inline void ehci_commit_interrupt(EHCIState *s) s->usbsts_pending = 0; } +static void ehci_update_halt(EHCIState *s) +{ + if (s->usbcmd & USBCMD_RUNSTOP) { + ehci_clear_usbsts(s, USBSTS_HALT); + } else { + if (s->astate == EST_INACTIVE && s->pstate == EST_INACTIVE) { + ehci_set_usbsts(s, USBSTS_HALT); + } + } +} + static void ehci_set_state(EHCIState *s, int async, int state) { if (async) { @@ -593,6 +604,7 @@ static void ehci_set_state(EHCIState *s, int async, int state) s->astate = state; if (s->astate == EST_INACTIVE) { ehci_clear_usbsts(s, USBSTS_ASS); + ehci_update_halt(s); } else { ehci_set_usbsts(s, USBSTS_ASS); } @@ -601,6 +613,7 @@ static void ehci_set_state(EHCIState *s, int async, int state) s->pstate = state; if (s->pstate == EST_INACTIVE) { ehci_clear_usbsts(s, USBSTS_PSS); + ehci_update_halt(s); } else { ehci_set_usbsts(s, USBSTS_PSS); } @@ -1138,21 +1151,15 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) break; } - if ((val & USBCMD_RUNSTOP) && !(s->usbcmd & USBCMD_RUNSTOP)) { - qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); - SET_LAST_RUN_CLOCK(s); - ehci_clear_usbsts(s, USBSTS_HALT); + if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != + ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) { + if (!ehci_enabled(s)) { + qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); + SET_LAST_RUN_CLOCK(s); + } + ehci_update_halt(s); } - if (!(val & USBCMD_RUNSTOP) && (s->usbcmd & USBCMD_RUNSTOP)) { - qemu_del_timer(s->frame_timer); - qemu_bh_cancel(s->async_bh); - ehci_queues_rip_all(s, 0); - ehci_queues_rip_all(s, 1); - ehci_set_usbsts(s, USBSTS_HALT); - } - - /* not supporting dynamic frame list size at the moment */ if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) { fprintf(stderr, "attempt to set frame list size -- value %d\n", @@ -2291,7 +2298,7 @@ static void ehci_frame_timer(void *opaque) frames = ns_elapsed / FRAME_TIMER_NS; for (i = 0; i < frames; i++) { - if ( !(ehci->usbsts & USBSTS_HALT)) { + if (ehci_enabled(ehci)) { ehci->frindex += 8; if (ehci->frindex == 0x00002000) { @@ -2324,7 +2331,9 @@ static void ehci_frame_timer(void *opaque) */ qemu_bh_schedule(ehci->async_bh); - qemu_mod_timer(ehci->frame_timer, expire_time); + if (ehci_enabled(ehci)) { + qemu_mod_timer(ehci->frame_timer, expire_time); + } } static void ehci_async_bh(void *opaque) From 863b474163085a3df13e2c6bf8e696486f3a0c66 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 16:48:04 +0200 Subject: [PATCH 34/37] ehci: remove unused attach_poll_counter Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index f77a26d9d9..ee7420f0f7 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -415,7 +415,6 @@ struct EHCIState { */ QEMUTimer *frame_timer; QEMUBH *async_bh; - int attach_poll_counter; int astate; // Current state in asynchronous schedule int pstate; // Current state in periodic schedule USBPort ports[NB_PORTS]; @@ -984,7 +983,6 @@ static void ehci_reset(void *opaque) s->astate = EST_INACTIVE; s->pstate = EST_INACTIVE; - s->attach_poll_counter = 0; for(i = 0; i < NB_PORTS; i++) { if (s->companion_ports[i]) { From 6ceced0b2ed52cdfb474d144dce54bf9afa613ce Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 13:28:32 +0200 Subject: [PATCH 35/37] ehci: create ehci_update_frindex Factor out code from ehci_frame_timer. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index ee7420f0f7..c15dbee2c7 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2280,6 +2280,28 @@ static void ehci_advance_periodic_state(EHCIState *ehci) } } +static void ehci_update_frindex(EHCIState *ehci, int frames) +{ + int i; + + if (!ehci_enabled(ehci)) { + return; + } + + for (i = 0; i < frames; i++) { + ehci->frindex += 8; + + if (ehci->frindex == 0x00002000) { + ehci_set_interrupt(ehci, USBSTS_FLR); + } + + if (ehci->frindex == 0x00004000) { + ehci_set_interrupt(ehci, USBSTS_FLR); + ehci->frindex = 0; + } + } +} + static void ehci_frame_timer(void *opaque) { EHCIState *ehci = opaque; @@ -2296,18 +2318,7 @@ static void ehci_frame_timer(void *opaque) frames = ns_elapsed / FRAME_TIMER_NS; for (i = 0; i < frames; i++) { - if (ehci_enabled(ehci)) { - ehci->frindex += 8; - - if (ehci->frindex == 0x00002000) { - ehci_set_interrupt(ehci, USBSTS_FLR); - } - - if (ehci->frindex == 0x00004000) { - ehci_set_interrupt(ehci, USBSTS_FLR); - ehci->frindex = 0; - } - } + ehci_update_frindex(ehci, 1); if (frames - i > ehci->maxframes) { skipped_frames++; From 3a21532626bb5c3ecb0f916843f75ccce6501a9d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 24 May 2012 13:34:02 +0200 Subject: [PATCH 36/37] ehci: adaptive wakeup rate. Adapt the frame timer sleeps according to the actual needs. With the periodic schedule being active we'll have to wakeup 1000 times per second and go check for work. In case only the async schedule is active we can be more lazy though. When idle ehci will increate the sleep time step by step, so qemu has to wake up less frequently. When we'll see transactions on the bus or the guest fiddles with the schedule enable/disable bits we'll return to a 1000 Hz wakeup rate and full speed. With both schedules disabled we stop wakeups altogether. This patch also drops the freq property (configures wakeup rate manually) which is obsoleted by this patch. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 57 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index c15dbee2c7..d97c5396d1 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -386,7 +386,6 @@ struct EHCIState { int companion_count; /* properties */ - uint32_t freq; uint32_t maxframes; /* @@ -430,6 +429,7 @@ struct EHCIState { QEMUSGList isgl; uint64_t last_run_ns; + uint32_t async_stepdown; }; #define SET_LAST_RUN_CLOCK(s) \ @@ -776,6 +776,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; EHCIQueue *q, *tmp; QTAILQ_FOREACH_SAFE(q, head, next, tmp) { @@ -784,8 +785,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) q->ts = ehci->last_run_ns; continue; } - if (!flush && ehci->last_run_ns < q->ts + 250000000) { - /* allow 0.25 sec idle */ + if (!flush && ehci->last_run_ns < q->ts + maxage) { continue; } ehci_free_queue(q); @@ -1151,11 +1151,12 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != ((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & s->usbcmd)) { - if (!ehci_enabled(s)) { - qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); + if (s->pstate == EST_INACTIVE) { SET_LAST_RUN_CLOCK(s); } ehci_update_halt(s); + s->async_stepdown = 0; + qemu_mod_timer(s->frame_timer, qemu_get_clock_ns(vm_clock)); } /* not supporting dynamic frame list size at the moment */ @@ -2146,10 +2147,16 @@ static void ehci_advance_state(EHCIState *ehci, int async) case EST_EXECUTE: again = ehci_state_execute(q); + if (async) { + ehci->async_stepdown = 0; + } break; case EST_EXECUTING: assert(q != NULL); + if (async) { + ehci->async_stepdown = 0; + } again = ehci_state_executing(q); break; @@ -2305,6 +2312,7 @@ static void ehci_update_frindex(EHCIState *ehci, int frames) static void ehci_frame_timer(void *opaque) { EHCIState *ehci = opaque; + int schedules = 0; int64_t expire_time, t_now; uint64_t ns_elapsed; int frames; @@ -2312,21 +2320,32 @@ static void ehci_frame_timer(void *opaque) int skipped_frames = 0; t_now = qemu_get_clock_ns(vm_clock); - expire_time = t_now + (get_ticks_per_sec() / ehci->freq); - ns_elapsed = t_now - ehci->last_run_ns; frames = ns_elapsed / FRAME_TIMER_NS; - for (i = 0; i < frames; i++) { - ehci_update_frindex(ehci, 1); + if (ehci_periodic_enabled(ehci) || ehci->pstate != EST_INACTIVE) { + schedules++; + expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ); - if (frames - i > ehci->maxframes) { - skipped_frames++; - } else { - ehci_advance_periodic_state(ehci); + for (i = 0; i < frames; i++) { + ehci_update_frindex(ehci, 1); + + if (frames - i > ehci->maxframes) { + skipped_frames++; + } else { + ehci_advance_periodic_state(ehci); + } + + ehci->last_run_ns += FRAME_TIMER_NS; } - - ehci->last_run_ns += FRAME_TIMER_NS; + } else { + if (ehci->async_stepdown < ehci->maxframes / 2) { + ehci->async_stepdown++; + } + expire_time = t_now + (get_ticks_per_sec() + * ehci->async_stepdown / FRAME_TIMER_FREQ); + ehci_update_frindex(ehci, frames); + ehci->last_run_ns += FRAME_TIMER_NS * frames; } #if 0 @@ -2338,9 +2357,12 @@ static void ehci_frame_timer(void *opaque) /* Async is not inside loop since it executes everything it can once * called */ - qemu_bh_schedule(ehci->async_bh); + if (ehci_async_enabled(ehci) || ehci->astate != EST_INACTIVE) { + schedules++; + qemu_bh_schedule(ehci->async_bh); + } - if (ehci_enabled(ehci)) { + if (schedules) { qemu_mod_timer(ehci->frame_timer, expire_time); } } @@ -2379,7 +2401,6 @@ static const VMStateDescription vmstate_ehci = { }; 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(), }; From f020ed36fef7780e81a94543ae6388d8dc144ab6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 25 May 2012 08:13:55 +0200 Subject: [PATCH 37/37] ehci: rework frame skipping Move the framecount check out of the loop and use the new ehci_update_frindex function to skip frames if needed. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d97c5396d1..5298204d9d 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2315,9 +2315,8 @@ static void ehci_frame_timer(void *opaque) int schedules = 0; int64_t expire_time, t_now; uint64_t ns_elapsed; - int frames; + int frames, skipped_frames; int i; - int skipped_frames = 0; t_now = qemu_get_clock_ns(vm_clock); ns_elapsed = t_now - ehci->last_run_ns; @@ -2327,15 +2326,17 @@ static void ehci_frame_timer(void *opaque) schedules++; expire_time = t_now + (get_ticks_per_sec() / FRAME_TIMER_FREQ); + if (frames > ehci->maxframes) { + skipped_frames = frames - ehci->maxframes; + ehci_update_frindex(ehci, skipped_frames); + ehci->last_run_ns += FRAME_TIMER_NS * skipped_frames; + frames -= skipped_frames; + DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames); + } + for (i = 0; i < frames; i++) { ehci_update_frindex(ehci, 1); - - if (frames - i > ehci->maxframes) { - skipped_frames++; - } else { - ehci_advance_periodic_state(ehci); - } - + ehci_advance_periodic_state(ehci); ehci->last_run_ns += FRAME_TIMER_NS; } } else { @@ -2348,12 +2349,6 @@ static void ehci_frame_timer(void *opaque) ehci->last_run_ns += FRAME_TIMER_NS * frames; } -#if 0 - if (skipped_frames) { - DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames); - } -#endif - /* Async is not inside loop since it executes everything it can once * called */