From 45b339b18c660eb85af2ba25bfcaed5469660d77 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 17 Aug 2012 11:39:16 +0200 Subject: usb: controllers do not need to check for babble themselves If an (emulated) usb-device tries to write more data to a packet then its iov len, this will trigger an assert in usb_packet_copy(), and if a driver somehow circumvents that check and writes more data to the iov then there is space, we have a much bigger problem then not correctly reporting babble to the guest. In practice babble will only happen with (real) redirected devices, and there both the usb-host os and the qemu usb-device code already check for it. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 4 ---- hw/usb/hcd-uhci.c | 5 ----- 2 files changed, 9 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 017342b56..95232472a 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1481,10 +1481,6 @@ static void ehci_execute_complete(EHCIQueue *q) assert(0); break; } - } 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_raise_irq(q->ehci, USBSTS_ERRINT); } else { // TODO check 4.12 for splits diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index b0db92145..c7c878635 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -729,11 +729,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_ *int_mask |= 0x01; if (pid == USB_TOKEN_IN) { - if (len > max_len) { - ret = USB_RET_BABBLE; - goto out; - } - if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ -- cgit v1.2.3 From cc40997489260f405aecccd30d4626ceee862502 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 12:33:44 +0200 Subject: usb-core: Don't set packet state to complete on a nak This way the hcd can re-use the same packet to retry without needing to re-init it. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/usb/core.c b/hw/usb/core.c index 2da38e7fd..be6d93688 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -399,8 +399,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) * otherwise packets can complete out of order! */ assert(!p->ep->pipeline); - p->result = ret; - usb_packet_set_state(p, USB_PACKET_COMPLETE); + if (ret != USB_RET_NAK) { + p->result = ret; + usb_packet_set_state(p, USB_PACKET_COMPLETE); + } } } else { ret = USB_RET_ASYNC; -- cgit v1.2.3 From c13a9e61366cc3e28299d8faeb65e65c6e5964cf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Aug 2012 09:43:18 +0200 Subject: usb-core: Add a usb_ep_find_packet_by_id() helper function Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb.h | 2 ++ hw/usb/core.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/hw/usb.h b/hw/usb.h index b8fceec89..684e3f4b5 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -377,6 +377,8 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep, uint16_t raw); int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep); void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled); +USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, + uint64_t id); void usb_attach(USBPort *port); void usb_detach(USBPort *port); diff --git a/hw/usb/core.c b/hw/usb/core.c index be6d93688..fe431d02d 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -726,3 +726,18 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled) struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); uep->pipeline = enabled; } + +USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep, + uint64_t id) +{ + struct USBEndpoint *uep = usb_ep_get(dev, pid, ep); + USBPacket *p; + + while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) { + if (p->id == id) { + return p; + } + } + + return NULL; +} -- cgit v1.2.3 From 9c1f67654ab611553bbfca54a1e0922728c25760 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 12:48:49 +0200 Subject: usb-core: Allow the first packet of a pipelined ep to complete immediately This can happen with usb-redir live-migration when the packet gets re-queued after the migration and the original queuing from the migration source side has already finished. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/core.c b/hw/usb/core.c index fe431d02d..b9f1f7a8b 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -398,7 +398,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p) * When pipelining is enabled usb-devices must always return async, * otherwise packets can complete out of order! */ - assert(!p->ep->pipeline); + assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue)); if (ret != USB_RET_NAK) { p->result = ret; usb_packet_set_state(p, USB_PACKET_COMPLETE); -- cgit v1.2.3 From 66f092d25697e11847b61d761c38ddebedaed8d1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 29 Aug 2012 10:12:52 +0200 Subject: Revert "ehci: don't flush cache on doorbell rings." This reverts commit 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0, which got added to fix an issue where the real, underlying cause was not stopping the ep queue on an error. Now that the underlying cause is fixed by the "usb: Halt ep queue and cancel pending packets on a packet error" patch, the "don't flush" fix is no longer needed. Not only is it not needed, it causes us to see cancellations (unlinks) done by the Linux EHCI driver too late, which in combination with the new usb-core packet-id generation where qtd addresses are used as ids, causes duplicate ids for in flight packets. Signed-off-by: Hans de Goede --- hw/usb/hcd-ehci.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 95232472a..e7c36f415 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -365,7 +365,6 @@ struct EHCIQueue { uint32_t seen; uint64_t ts; int async; - int revalidate; /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) @@ -805,18 +804,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, return NULL; } -static void ehci_queues_tag_unused_async(EHCIState *ehci) -{ - EHCIQueue *q; - - QTAILQ_FOREACH(q, &ehci->aqueues, next) { - if (!q->seen) { - q->revalidate = 1; - } - } -} - -static void ehci_queues_rip_unused(EHCIState *ehci, int async) +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; @@ -828,7 +816,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async) q->ts = ehci->last_run_ns; continue; } - if (ehci->last_run_ns < q->ts + maxage) { + if (!flush && ehci->last_run_ns < q->ts + maxage) { continue; } ehci_free_queue(q); @@ -1684,7 +1672,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) ehci_set_usbsts(ehci, USBSTS_REC); } - ehci_queues_rip_unused(ehci, async); + ehci_queues_rip_unused(ehci, async, 0); /* Find the head of the list (4.9.1.1) */ for(i = 0; i < MAX_QH; i++) { @@ -1769,7 +1757,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) EHCIPacket *p; uint32_t entry, devaddr; EHCIQueue *q; - EHCIqh qh; entry = ehci_get_fetch_addr(ehci, async); q = ehci_find_queue_by_qh(ehci, entry, async); @@ -1787,17 +1774,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2); - if (q->revalidate && (q->qh.epchar != qh.epchar || - q->qh.epcap != qh.epcap || - q->qh.current_qtd != qh.current_qtd)) { - ehci_free_queue(q); - q = ehci_alloc_queue(ehci, entry, async); - q->seen++; - p = NULL; - } - q->qh = qh; - q->revalidate = 0; + (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); @@ -2306,7 +2283,7 @@ static void ehci_advance_async_state(EHCIState *ehci) */ if (ehci->usbcmd & USBCMD_IAAD) { /* Remove all unseen qhs from the async qhs queue */ - ehci_queues_tag_unused_async(ehci); + ehci_queues_rip_unused(ehci, async, 1); DPRINTF("ASYNC: doorbell request acknowledged\n"); ehci->usbcmd &= ~USBCMD_IAAD; ehci_raise_irq(ehci, USBSTS_IAA); @@ -2359,7 +2336,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci) ehci_set_fetch_addr(ehci, async,entry); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); - ehci_queues_rip_unused(ehci, async); + ehci_queues_rip_unused(ehci, async, 0); break; default: -- cgit v1.2.3 From dafe31fc2a8653b535d58f8c7b250c0827b14420 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 29 Aug 2012 10:37:37 +0200 Subject: ehci: Validate qh is not changed unexpectedly by the guest -combine the qh check with the check for devaddr changes -also ensure that p gets set to NULL when the queue gets cancelled on devaddr change, which was not done properly before this patch Signed-off-by: Hans de Goede --- hw/usb/hcd-ehci.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index e7c36f415..35eb441f1 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -780,6 +780,14 @@ static void ehci_cancel_queue(EHCIQueue *q) } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); } +static void ehci_reset_queue(EHCIQueue *q) +{ + trace_usb_ehci_queue_action(q, "reset"); + ehci_cancel_queue(q); + q->dev = NULL; + q->qtdaddr = 0; +} + static void ehci_free_queue(EHCIQueue *q) { EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; @@ -1755,8 +1763,9 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { EHCIPacket *p; - uint32_t entry, devaddr; + uint32_t entry, devaddr, endp; EHCIQueue *q; + EHCIqh qh; entry = ehci_get_fetch_addr(ehci, async); q = ehci_find_queue_by_qh(ehci, entry, async); @@ -1774,17 +1783,25 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2); - ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh); + (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh); + + /* + * The overlay area of the qh should never be changed by the guest, + * except when idle, in which case the reset is a nop. + */ + devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR); + endp = get_field(qh.epchar, QH_EPCHAR_EP); + if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) || + (endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) || + (memcmp(&qh.current_qtd, &q->qh.current_qtd, + 9 * sizeof(uint32_t)) != 0) || + (q->dev != NULL && q->dev->addr != devaddr)) { + ehci_reset_queue(q); + p = NULL; + } + q->qh = 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) */ - ehci_cancel_queue(q); - } - q->dev = NULL; - } if (q->dev == NULL) { q->dev = ehci_find_device(q->ehci, devaddr); } -- cgit v1.2.3 From 522079dd4461c38b9a88bf31a65ea038c5b2be45 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Aug 2012 16:21:12 +0200 Subject: ehci: Update copyright headers to reflect recent work Update copyright headers to reflect all the work Gerd and I have been doing on the EHCI emulation. Signed-off-by: Hans de Goede --- hw/usb/hcd-ehci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 35eb441f1..78a248f08 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2,6 +2,11 @@ * QEMU USB EHCI Emulation * * Copyright(c) 2008 Emutex Ltd. (address@hidden) + * Copyright(c) 2011-2012 Red Hat, Inc. + * + * Red Hat Authors: + * Gerd Hoffmann + * Hans de Goede * * EHCI project was started by Mark Burkley, with contributions by * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, -- cgit v1.2.3 From 0e7953525f52aa6c098dc0c1ce0b4a80ce82da45 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Aug 2012 15:00:33 +0200 Subject: ehci: Properly cleanup packets on cancel Signed-off-by: Hans de Goede --- 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 78a248f08..4fe85c8ef 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -747,6 +747,8 @@ 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); + usb_packet_unmap(&p->packet, &p->sgl); + qemu_sglist_destroy(&p->sgl); } QTAILQ_REMOVE(&p->queue->packets, p, next); usb_packet_cleanup(&p->packet); -- cgit v1.2.3 From 4b63a0df3bda8a2c278e45d9d94d9ba6d5791d8d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 30 Aug 2012 15:18:24 +0200 Subject: ehci: Properly report completed but not yet processed packets to the guest Reported packets which have completed before being cancelled as such to the host. Note that the new code path this patch adds is untested since it I've been unable to actually trigger the race which needs this code path. Signed-off-by: Hans de Goede --- hw/usb/hcd-ehci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 4fe85c8ef..0a6c9ef19 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -489,6 +489,9 @@ static const char *ehci_mmio_names[] = { [CONFIGFLAG] = "CONFIGFLAG", }; +static int ehci_state_executing(EHCIQueue *q); +static int ehci_state_writeback(EHCIQueue *q); + static const char *nr2str(const char **n, size_t len, uint32_t nr) { if (nr < len && n[nr] != NULL) { @@ -750,6 +753,16 @@ static void ehci_free_packet(EHCIPacket *p) usb_packet_unmap(&p->packet, &p->sgl); qemu_sglist_destroy(&p->sgl); } + if (p->async == EHCI_ASYNC_FINISHED) { + int state = ehci_get_state(p->queue->ehci, p->queue->async); + /* This is a normal, but rare condition (cancel racing completion) */ + fprintf(stderr, "EHCI: Warning packet completed but not processed\n"); + ehci_state_executing(p->queue); + ehci_state_writeback(p->queue); + ehci_set_state(p->queue->ehci, p->queue->async, state); + /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */ + return; + } QTAILQ_REMOVE(&p->queue->packets, p, next); usb_packet_cleanup(&p->packet); g_free(p); -- cgit v1.2.3 From 616789cde2a83fad5e634880fd20214f0c984fd5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 31 Aug 2012 10:31:54 +0200 Subject: ehci: check for EHCI_ASYNC_FINISHED first in ehci_free_packet Otherwise we'll see the packet free twice in the trace log even though it actually happens only once. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 0a6c9ef19..23221d0c0 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -747,12 +747,6 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q) 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); - usb_packet_unmap(&p->packet, &p->sgl); - qemu_sglist_destroy(&p->sgl); - } if (p->async == EHCI_ASYNC_FINISHED) { int state = ehci_get_state(p->queue->ehci, p->queue->async); /* This is a normal, but rare condition (cancel racing completion) */ @@ -763,6 +757,12 @@ static void ehci_free_packet(EHCIPacket *p) /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */ return; } + trace_usb_ehci_packet_action(p->queue, p, "free"); + if (p->async == EHCI_ASYNC_INFLIGHT) { + usb_cancel_packet(&p->packet); + usb_packet_unmap(&p->packet, &p->sgl); + qemu_sglist_destroy(&p->sgl); + } QTAILQ_REMOVE(&p->queue->packets, p, next); usb_packet_cleanup(&p->packet); g_free(p); -- cgit v1.2.3 From 5c514681abbb3ae2f61f517c1aa3197f2f3ca93c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 31 Aug 2012 10:44:21 +0200 Subject: ehci: trace guest bugs make qemu_queue_{cancel,reset} return the number of packets released, so the caller can figure whenever there have been active packets even though there shouldn't have been any. Add tracepoint to log this. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 26 ++++++++++++++++++++------ trace-events | 1 + 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 23221d0c0..4564615e0 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -716,6 +716,12 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr, (bool)(sitd->results & SITD_RESULTS_ACTIVE)); } +static void ehci_trace_guest_bug(EHCIState *s, const char *message) +{ + trace_usb_ehci_guest_bug(message); + fprintf(stderr, "ehci warning: %s\n", message); +} + static inline bool ehci_enabled(EHCIState *s) { return s->usbcmd & USBCMD_RUNSTOP; @@ -785,27 +791,33 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) return q; } -static void ehci_cancel_queue(EHCIQueue *q) +static int ehci_cancel_queue(EHCIQueue *q) { EHCIPacket *p; + int packets = 0; p = QTAILQ_FIRST(&q->packets); if (p == NULL) { - return; + return 0; } trace_usb_ehci_queue_action(q, "cancel"); do { ehci_free_packet(p); + packets++; } while ((p = QTAILQ_FIRST(&q->packets)) != NULL); + return packets; } -static void ehci_reset_queue(EHCIQueue *q) +static int ehci_reset_queue(EHCIQueue *q) { + int packets; + trace_usb_ehci_queue_action(q, "reset"); - ehci_cancel_queue(q); + packets = ehci_cancel_queue(q); q->dev = NULL; q->qtdaddr = 0; + return packets; } static void ehci_free_queue(EHCIQueue *q) @@ -1817,7 +1829,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) (memcmp(&qh.current_qtd, &q->qh.current_qtd, 9 * sizeof(uint32_t)) != 0) || (q->dev != NULL && q->dev->addr != devaddr)) { - ehci_reset_queue(q); + if (ehci_reset_queue(q) > 0) { + ehci_trace_guest_bug(ehci, "guest updated active QH"); + } p = NULL; } q->qh = qh; @@ -1979,8 +1993,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q) (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) || (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) || p->qtd.bufptr[0] != qtd.bufptr[0]) { - /* guest bug: guest updated active QH or qTD underneath us */ ehci_cancel_queue(q); + ehci_trace_guest_bug(q->ehci, "guest updated active QH or qTD"); p = NULL; } else { p->qtd = qtd; diff --git a/trace-events b/trace-events index 8fcbc50f9..5112a474e 100644 --- a/trace-events +++ b/trace-events @@ -263,6 +263,7 @@ usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t l 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" usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" +usb_ehci_guest_bug(const char *reason) "%s" # hw/usb/hcd-uhci.c usb_uhci_reset(void) "=== RESET ===" -- cgit v1.2.3 From 1defcbd1e81d67476b6e4e486bcd4d869162900d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 31 Aug 2012 12:41:43 +0200 Subject: ehci: add doorbell trace events Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 3 ++- trace-events | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 4564615e0..398f5e09f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1241,6 +1241,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) */ s->async_stepdown = 0; qemu_bh_schedule(s->async_bh); + trace_usb_ehci_doorbell_ring(); } if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) != @@ -2335,7 +2336,7 @@ static void ehci_advance_async_state(EHCIState *ehci) if (ehci->usbcmd & USBCMD_IAAD) { /* Remove all unseen qhs from the async qhs queue */ ehci_queues_rip_unused(ehci, async, 1); - DPRINTF("ASYNC: doorbell request acknowledged\n"); + trace_usb_ehci_doorbell_ack(); ehci->usbcmd &= ~USBCMD_IAAD; ehci_raise_irq(ehci, USBSTS_IAA); } diff --git a/trace-events b/trace-events index 5112a474e..10bc04eac 100644 --- a/trace-events +++ b/trace-events @@ -264,6 +264,8 @@ 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" usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" usb_ehci_guest_bug(const char *reason) "%s" +usb_ehci_doorbell_ring(void) "" +usb_ehci_doorbell_ack(void) "" # hw/usb/hcd-uhci.c usb_uhci_reset(void) "=== RESET ===" -- cgit v1.2.3 From 3a8ca08e01ea4baafff2a513655008cdd00feebf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 10:22:16 +0200 Subject: ehci: Add some additional ehci_trace_guest_bug() calls Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 398f5e09f..5a882687f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -820,12 +820,16 @@ static int ehci_reset_queue(EHCIQueue *q) return packets; } -static void ehci_free_queue(EHCIQueue *q) +static void ehci_free_queue(EHCIQueue *q, const char *warn) { EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues; + int cancelled; trace_usb_ehci_queue_action(q, "free"); - ehci_cancel_queue(q); + cancelled = ehci_cancel_queue(q); + if (warn && cancelled > 0) { + ehci_trace_guest_bug(q->ehci, warn); + } QTAILQ_REMOVE(head, q, next); g_free(q); } @@ -847,6 +851,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; + const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL; uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4; EHCIQueue *q, *tmp; @@ -859,7 +864,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush) if (!flush && ehci->last_run_ns < q->ts + maxage) { continue; } - ehci_free_queue(q); + ehci_free_queue(q, warn); } } @@ -872,17 +877,18 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async) if (q->dev != dev) { continue; } - ehci_free_queue(q); + ehci_free_queue(q, NULL); } } static void ehci_queues_rip_all(EHCIState *ehci, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; + const char *warn = async ? "guest stopped busy async schedule" : NULL; EHCIQueue *q, *tmp; QTAILQ_FOREACH_SAFE(q, head, next, tmp) { - ehci_free_queue(q); + ehci_free_queue(q, warn); } } @@ -1549,7 +1555,8 @@ static int ehci_execute(EHCIPacket *p, const char *action) 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"); + ehci_trace_guest_bug(p->queue->ehci, + "guest requested more bytes than allowed"); return USB_RET_PROCERR; } -- cgit v1.2.3 From ef5b234477df80700b128f561f5877a0688a70c8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 11:01:13 +0200 Subject: ehci: Fix memory leak in handling of NAK-ed packets Currently each time we try to execute a NAK-ed packet we redo ehci_init_transfer, and usb_packet_map, re-allocing (without freeing) the sg list every time. This patch fixes this, it does this by introducing another async state, so that we also properly cleanup a NAK-ed packet on cancel. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 5a882687f..d87aca833 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -345,6 +345,7 @@ typedef struct EHCIState EHCIState; enum async_state { EHCI_ASYNC_NONE = 0, + EHCI_ASYNC_INITIALIZED, EHCI_ASYNC_INFLIGHT, EHCI_ASYNC_FINISHED, }; @@ -764,6 +765,10 @@ static void ehci_free_packet(EHCIPacket *p) return; } trace_usb_ehci_packet_action(p->queue, p, "free"); + if (p->async == EHCI_ASYNC_INITIALIZED) { + usb_packet_unmap(&p->packet, &p->sgl); + qemu_sglist_destroy(&p->sgl); + } if (p->async == EHCI_ASYNC_INFLIGHT) { usb_cancel_packet(&p->packet); usb_packet_unmap(&p->packet, &p->sgl); @@ -1485,8 +1490,8 @@ static void ehci_execute_complete(EHCIQueue *q) assert(p != NULL); assert(p->qtdaddr == q->qtdaddr); - assert(p->async != EHCI_ASYNC_INFLIGHT); - p->async = EHCI_ASYNC_NONE; + assert(p->async == EHCI_ASYNC_INITIALIZED || + p->async == EHCI_ASYNC_FINISHED); DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n", q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status); @@ -1531,6 +1536,7 @@ static void ehci_execute_complete(EHCIQueue *q) ehci_finish_transfer(q, p->usb_status); usb_packet_unmap(&p->packet, &p->sgl); qemu_sglist_destroy(&p->sgl); + p->async = EHCI_ASYNC_NONE; q->qh.token ^= QTD_TOKEN_DTOGGLE; q->qh.token &= ~QTD_TOKEN_ACTIVE; @@ -1548,6 +1554,9 @@ static int ehci_execute(EHCIPacket *p, const char *action) int ret; int endp; + assert(p->async == EHCI_ASYNC_NONE || + p->async == EHCI_ASYNC_INITIALIZED); + if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) { fprintf(stderr, "Attempting to execute inactive qtd\n"); return USB_RET_PROCERR; @@ -1576,15 +1585,18 @@ static int ehci_execute(EHCIPacket *p, const char *action) break; } - if (ehci_init_transfer(p) != 0) { - return USB_RET_PROCERR; - } - endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP); ep = usb_ep_get(p->queue->dev, p->pid, endp); - usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr); - usb_packet_map(&p->packet, &p->sgl); + if (p->async == EHCI_ASYNC_NONE) { + if (ehci_init_transfer(p) != 0) { + return USB_RET_PROCERR; + } + + usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr); + usb_packet_map(&p->packet, &p->sgl); + p->async = EHCI_ASYNC_INITIALIZED; + } trace_usb_ehci_packet_action(p->queue, p, action); ret = usb_handle_packet(p->queue->dev, &p->packet); @@ -2021,11 +2033,15 @@ static int ehci_state_fetchqtd(EHCIQueue *q) } else if (p != NULL) { switch (p->async) { case EHCI_ASYNC_NONE: + /* Should never happen packet should at least be initialized */ + assert(0); + break; + case EHCI_ASYNC_INITIALIZED: /* Previously nacked packet (likely interrupt ep) */ - ehci_set_state(q->ehci, q->async, EST_EXECUTE); - break; + ehci_set_state(q->ehci, q->async, EST_EXECUTE); + break; case EHCI_ASYNC_INFLIGHT: - /* Unfinyshed async handled packet, go horizontal */ + /* Unfinished async handled packet, go horizontal */ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); break; case EHCI_ASYNC_FINISHED: -- cgit v1.2.3 From eff6dce79bd7ad3c16d75c5e55b5a2a137ba6a60 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 11:35:58 +0200 Subject: ehci: Handle USB_RET_PROCERR in ehci_fill_queue USB_RET_PROCERR can be triggered by the guest (by for example requesting more then BUFFSIZE bytes), so don't assert on it. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index d87aca833..253439420 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2076,7 +2076,7 @@ static int ehci_state_horizqh(EHCIQueue *q) return again; } -static void ehci_fill_queue(EHCIPacket *p) +static int ehci_fill_queue(EHCIPacket *p) { EHCIQueue *q = p->queue; EHCIqtd qtd = p->qtd; @@ -2100,9 +2100,13 @@ static void ehci_fill_queue(EHCIPacket *p) p->qtdaddr = qtdaddr; p->qtd = qtd; p->usb_status = ehci_execute(p, "queue"); + if (p->usb_status == USB_RET_PROCERR) { + break; + } assert(p->usb_status == USB_RET_ASYNC); p->async = EHCI_ASYNC_INFLIGHT; } + return p->usb_status; } static int ehci_state_execute(EHCIQueue *q) @@ -2144,8 +2148,7 @@ static int ehci_state_execute(EHCIQueue *q) trace_usb_ehci_packet_action(p->queue, p, "async"); p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - ehci_fill_queue(p); + again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; goto out; } -- cgit v1.2.3 From cf1f81691d1998fa8fe5bfcb8b498fb3723cf3c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 12:17:48 +0200 Subject: ehci: Correct a comment in fetchqtd packet processing Since my previous comment said "Should never happen", I tried changing the next line to an assert(0), which did not go well, which as the new comments explains is logical if you think about it for a moment. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-ehci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 253439420..2f3e9c03a 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -2045,7 +2045,10 @@ static int ehci_state_fetchqtd(EHCIQueue *q) ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); break; case EHCI_ASYNC_FINISHED: - /* Should never happen, as this case is caught by fetchqh */ + /* + * We get here when advqueue moves to a packet which is already + * finished, which can happen with packets queued up by fill_queue + */ ehci_set_state(q->ehci, q->async, EST_EXECUTING); break; } -- cgit v1.2.3 From 181133404f520fab40a3ad40d935d91cf3cf546c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 17 Aug 2012 17:27:08 +0200 Subject: usb-redir: Never return USB_RET_NAK for async handled packets USB_RET_NAK is not a valid response for async handled packets (and will trigger an assert as such). Also drop the warning when receiving a status of cancelled for packets not cancelled by qemu itself, this can happen when a device gets unredirected by the usbredir-host while transfers are pending. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 10b4fbb3a..7f3719b19 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1028,11 +1028,14 @@ static int usbredir_handle_status(USBRedirDevice *dev, case usb_redir_stall: return USB_RET_STALL; case usb_redir_cancelled: - WARNING("returning cancelled packet to HC?\n"); - return USB_RET_NAK; + /* + * When the usbredir-host unredirects a device, it will report a status + * of cancelled for all pending packets, followed by a disconnect msg. + */ + return USB_RET_IOERROR; case usb_redir_inval: WARNING("got invalid param error from usb-host?\n"); - return USB_RET_NAK; + return USB_RET_IOERROR; case usb_redir_babble: return USB_RET_BABBLE; case usb_redir_ioerror: -- cgit v1.2.3 From ed9873bfbf145c084d039baab08c63b9d67e7bd3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 23 Aug 2012 16:37:19 +0200 Subject: usb-redir: Don't delay handling of open events to a bottom half There is no need for this, and doing so means that a backend trying to write immediately after an open event will see qemu_chr_be_can_write returning 0, which not all backends handle well as there is no wakeup mechanism to detect when the frontend does become writable. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 100 +++++++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 7f3719b19..5cc333490 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -79,8 +79,8 @@ struct USBRedirDevice { /* Data passed from chardev the fd_read cb to the usbredirparser read cb */ const uint8_t *read_buf; int read_buf_size; - /* For async handling of open/close */ - QEMUBH *open_close_bh; + /* For async handling of close */ + QEMUBH *chardev_close_bh; /* To delay the usb attach in case of quick chardev close + open */ QEMUTimer *attach_timer; int64_t next_attach_time; @@ -784,18 +784,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, * from within the USBDevice data / control packet callbacks and doing a * usb_detach from within these callbacks is not a good idea. * - * So we use a bh handler to take care of close events. We also handle - * open events from this callback to make sure that a close directly followed - * by an open gets handled in the right order. + * So we use a bh handler to take care of close events. */ -static void usbredir_open_close_bh(void *opaque) +static void usbredir_chardev_close_bh(void *opaque) { USBRedirDevice *dev = opaque; - uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; - char version[32]; - - strcpy(version, "qemu usb-redir guest "); - pstrcat(version, sizeof(version), qemu_get_version()); usbredir_device_disconnect(dev); @@ -803,36 +796,47 @@ static void usbredir_open_close_bh(void *opaque) usbredirparser_destroy(dev->parser); dev->parser = NULL; } +} - if (dev->cs->opened) { - dev->parser = qemu_oom_check(usbredirparser_create()); - dev->parser->priv = dev; - dev->parser->log_func = usbredir_log; - dev->parser->read_func = usbredir_read; - dev->parser->write_func = usbredir_write; - dev->parser->hello_func = usbredir_hello; - dev->parser->device_connect_func = usbredir_device_connect; - dev->parser->device_disconnect_func = usbredir_device_disconnect; - dev->parser->interface_info_func = usbredir_interface_info; - dev->parser->ep_info_func = usbredir_ep_info; - dev->parser->configuration_status_func = usbredir_configuration_status; - dev->parser->alt_setting_status_func = usbredir_alt_setting_status; - dev->parser->iso_stream_status_func = usbredir_iso_stream_status; - dev->parser->interrupt_receiving_status_func = - usbredir_interrupt_receiving_status; - dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; - dev->parser->control_packet_func = usbredir_control_packet; - dev->parser->bulk_packet_func = usbredir_bulk_packet; - dev->parser->iso_packet_func = usbredir_iso_packet; - dev->parser->interrupt_packet_func = usbredir_interrupt_packet; - dev->read_buf = NULL; - dev->read_buf_size = 0; +static void usbredir_chardev_open(USBRedirDevice *dev) +{ + uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, }; + char version[32]; - usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); - usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); - usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); - usbredirparser_do_write(dev->parser); - } + /* Make sure any pending closes are handled (no-op if none pending) */ + usbredir_chardev_close_bh(dev); + qemu_bh_cancel(dev->chardev_close_bh); + + strcpy(version, "qemu usb-redir guest "); + pstrcat(version, sizeof(version), qemu_get_version()); + + dev->parser = qemu_oom_check(usbredirparser_create()); + dev->parser->priv = dev; + dev->parser->log_func = usbredir_log; + dev->parser->read_func = usbredir_read; + dev->parser->write_func = usbredir_write; + dev->parser->hello_func = usbredir_hello; + dev->parser->device_connect_func = usbredir_device_connect; + dev->parser->device_disconnect_func = usbredir_device_disconnect; + dev->parser->interface_info_func = usbredir_interface_info; + dev->parser->ep_info_func = usbredir_ep_info; + dev->parser->configuration_status_func = usbredir_configuration_status; + dev->parser->alt_setting_status_func = usbredir_alt_setting_status; + dev->parser->iso_stream_status_func = usbredir_iso_stream_status; + dev->parser->interrupt_receiving_status_func = + usbredir_interrupt_receiving_status; + dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status; + dev->parser->control_packet_func = usbredir_control_packet; + dev->parser->bulk_packet_func = usbredir_bulk_packet; + dev->parser->iso_packet_func = usbredir_iso_packet; + dev->parser->interrupt_packet_func = usbredir_interrupt_packet; + dev->read_buf = NULL; + dev->read_buf_size = 0; + + usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); + usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); + usbredirparser_do_write(dev->parser); } static void usbredir_do_attach(void *opaque) @@ -856,13 +860,13 @@ static int usbredir_chardev_can_read(void *opaque) { USBRedirDevice *dev = opaque; - if (dev->parser) { - /* usbredir_parser_do_read will consume *all* data we give it */ - return 1024 * 1024; - } else { - /* usbredir_open_close_bh hasn't handled the open event yet */ + if (!dev->parser) { + WARNING("chardev_can_read called on non open chardev!\n"); return 0; } + + /* usbredir_parser_do_read will consume *all* data we give it */ + return 1024 * 1024; } static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) @@ -886,8 +890,10 @@ static void usbredir_chardev_event(void *opaque, int event) switch (event) { case CHR_EVENT_OPENED: + usbredir_chardev_open(dev); + break; case CHR_EVENT_CLOSED: - qemu_bh_schedule(dev->open_close_bh); + qemu_bh_schedule(dev->chardev_close_bh); break; } } @@ -917,7 +923,7 @@ static int usbredir_initfn(USBDevice *udev) } } - dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev); + dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); QTAILQ_INIT(&dev->asyncq); @@ -957,7 +963,7 @@ static void usbredir_handle_destroy(USBDevice *udev) qemu_chr_fe_close(dev->cs); qemu_chr_delete(dev->cs); /* Note must be done after qemu_chr_close, as that causes a close event */ - qemu_bh_delete(dev->open_close_bh); + qemu_bh_delete(dev->chardev_close_bh); qemu_del_timer(dev->attach_timer); qemu_free_timer(dev->attach_timer); -- cgit v1.2.3 From cb897117cdedd488f19985c8ec5ea05971103a27 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 27 Aug 2012 16:33:08 +0200 Subject: usb-redir: Get rid of async-struct get member This is a preparation patch for completely getting rid of the async-packet struct in usb-redir, instead relying on the (new) per ep queues in the qemu usb core. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 5cc333490..2cae8c5bb 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1,7 +1,7 @@ /* * USB redirector usb-guest * - * Copyright (c) 2011 Red Hat, Inc. + * Copyright (c) 2011-2012 Red Hat, Inc. * * Red Hat Authors: * Hans de Goede @@ -99,7 +99,6 @@ struct AsyncURB { USBRedirDevice *dev; USBPacket *packet; uint32_t packet_id; - int get; union { struct usb_redir_control_packet_header control_packet; struct usb_redir_bulk_packet_header bulk_packet; @@ -672,7 +671,6 @@ static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) DPRINTF("get config id %u\n", aurb->packet_id); - aurb->get = 1; usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; @@ -721,7 +719,6 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); get_alt.interface = interface; - aurb->get = 1; usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, &get_alt); usbredirparser_do_write(dev->parser); @@ -1226,7 +1223,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id, return; } if (aurb->packet) { - if (aurb->get) { + if (dev->dev.setup_buf[0] & USB_DIR_IN) { dev->dev.data_buf[0] = config_status->configuration; len = 1; } @@ -1254,7 +1251,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, return; } if (aurb->packet) { - if (aurb->get) { + if (dev->dev.setup_buf[0] & USB_DIR_IN) { dev->dev.data_buf[0] = alt_setting_status->alt; len = 1; } -- cgit v1.2.3 From 104981d52b63dc3d68f39d4442881c667f44bbb9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Aug 2012 09:05:38 +0200 Subject: usb-redir: Get rid of local shadow copy of packet headers The shadow copy only serves as an extra check (besides the packet-id) to ensure the packet we get back is a reply to the packet we think it is. This check has never triggered in all the time usb-redir is in use now, and since the verified data in the returned packet-header is not used otherwise, removing the check does not open any possibilities for the usbredirhost to confuse us. This is a preparation patch for completely getting rid of the async-packet struct in usb-redir, instead relying on the (new) per ep queues in the qemu usb core. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 2cae8c5bb..e4ef372b1 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -99,11 +99,6 @@ struct AsyncURB { USBRedirDevice *dev; USBPacket *packet; uint32_t packet_id; - union { - struct usb_redir_control_packet_header control_packet; - struct usb_redir_bulk_packet_header bulk_packet; - struct usb_redir_interrupt_packet_header interrupt_packet; - }; QTAILQ_ENTRY(AsyncURB)next; }; @@ -500,7 +495,6 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, bulk_packet.endpoint = ep; bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; - aurb->bulk_packet = bulk_packet; if (ep & USB_DIR_IN) { usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, @@ -581,7 +575,6 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, interrupt_packet.endpoint = ep; interrupt_packet.length = p->iov.size; - aurb->interrupt_packet = interrupt_packet; usb_packet_copy(p, buf, p->iov.size); usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); @@ -762,7 +755,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, control_packet.value = value; control_packet.index = index; control_packet.length = length; - aurb->control_packet = control_packet; if (control_packet.requesttype & USB_DIR_IN) { usbredirparser_send_control_packet(dev->parser, aurb->packet_id, @@ -1326,14 +1318,6 @@ static void usbredir_control_packet(void *priv, uint32_t id, return; } - aurb->control_packet.status = control_packet->status; - aurb->control_packet.length = control_packet->length; - if (memcmp(&aurb->control_packet, control_packet, - sizeof(*control_packet))) { - ERROR("return control packet mismatch, please report this!\n"); - len = USB_RET_NAK; - } - if (aurb->packet) { len = usbredir_handle_status(dev, control_packet->status, len); if (len > 0) { @@ -1371,12 +1355,6 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, return; } - if (aurb->bulk_packet.endpoint != bulk_packet->endpoint || - aurb->bulk_packet.stream_id != bulk_packet->stream_id) { - ERROR("return bulk packet mismatch, please report this!\n"); - len = USB_RET_NAK; - } - if (aurb->packet) { len = usbredir_handle_status(dev, bulk_packet->status, len); if (len > 0) { @@ -1455,11 +1433,6 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, return; } - if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) { - ERROR("return int packet mismatch, please report this!\n"); - len = USB_RET_NAK; - } - if (aurb->packet) { aurb->packet->result = usbredir_handle_status(dev, interrupt_packet->status, len); -- cgit v1.2.3 From 206e7f20fe7b920b362bcc02608680c5d5527f2a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Aug 2012 09:08:45 +0200 Subject: usb-redir: Get rid of unused async-struct dev member This is a preparation patch for completely getting rid of the async-packet struct in usb-redir, instead relying on the (new) per ep queues in the qemu usb core. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index e4ef372b1..6593d506b 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -96,7 +96,6 @@ struct USBRedirDevice { }; struct AsyncURB { - USBRedirDevice *dev; USBPacket *packet; uint32_t packet_id; QTAILQ_ENTRY(AsyncURB)next; @@ -245,7 +244,6 @@ static int usbredir_write(void *priv, uint8_t *data, int count) static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) { AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB)); - aurb->dev = dev; aurb->packet = p; aurb->packet_id = dev->packet_id; QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); -- cgit v1.2.3 From de550a6afb468ed3b8171019e19b63ae8254886d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Aug 2012 11:30:13 +0200 Subject: usb-redir: Move to core packet id and queue handling Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 226 ++++++++++++++++++++++-------------------------------- 1 file changed, 92 insertions(+), 134 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 6593d506b..fd1f8cc51 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -43,7 +43,7 @@ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f)) #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f)) -typedef struct AsyncURB AsyncURB; +typedef struct Cancelled Cancelled; typedef struct USBRedirDevice USBRedirDevice; /* Struct to hold buffered packets (iso or int input packets) */ @@ -86,8 +86,7 @@ struct USBRedirDevice { int64_t next_attach_time; struct usbredirparser *parser; struct endp_data endpoint[MAX_ENDPOINTS]; - uint32_t packet_id; - QTAILQ_HEAD(, AsyncURB) asyncq; + QTAILQ_HEAD(, Cancelled) cancelled; /* Data for device filtering */ struct usb_redir_device_connect_header device_info; struct usb_redir_interface_info_header interface_info; @@ -95,10 +94,9 @@ struct USBRedirDevice { int filter_rules_count; }; -struct AsyncURB { - USBPacket *packet; - uint32_t packet_id; - QTAILQ_ENTRY(AsyncURB)next; +struct Cancelled { + uint64_t id; + QTAILQ_ENTRY(Cancelled)next; }; static void usbredir_hello(void *priv, struct usb_redir_hello_header *h); @@ -238,57 +236,58 @@ static int usbredir_write(void *priv, uint8_t *data, int count) } /* - * Async and buffered packets helpers + * Cancelled and buffered packets helpers */ -static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p) +static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) { - AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB)); - aurb->packet = p; - aurb->packet_id = dev->packet_id; - QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next); - dev->packet_id++; + USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); + Cancelled *c; - return aurb; -} + DPRINTF("cancel packet id %"PRIu64"\n", p->id); -static void async_free(USBRedirDevice *dev, AsyncURB *aurb) -{ - QTAILQ_REMOVE(&dev->asyncq, aurb, next); - g_free(aurb); + c = g_malloc0(sizeof(Cancelled)); + c->id = p->id; + QTAILQ_INSERT_TAIL(&dev->cancelled, c, next); + + usbredirparser_send_cancel_data_packet(dev->parser, p->id); + usbredirparser_do_write(dev->parser); } -static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id) +static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id) { - AsyncURB *aurb; + Cancelled *c; + + if (!dev->dev.attached) { + return 1; /* Treat everything as cancelled after a disconnect */ + } - QTAILQ_FOREACH(aurb, &dev->asyncq, next) { - if (aurb->packet_id == packet_id) { - return aurb; + QTAILQ_FOREACH(c, &dev->cancelled, next) { + if (c->id == id) { + QTAILQ_REMOVE(&dev->cancelled, c, next); + g_free(c); + return 1; } } - DPRINTF("could not find async urb for packet_id %u\n", packet_id); - return NULL; + return 0; } -static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p) +static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev, + uint8_t ep, uint64_t id) { - USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); - AsyncURB *aurb; - - QTAILQ_FOREACH(aurb, &dev->asyncq, next) { - if (p != aurb->packet) { - continue; - } + USBPacket *p; - DPRINTF("async cancel id %u\n", aurb->packet_id); - usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id); - usbredirparser_do_write(dev->parser); + if (usbredir_is_cancelled(dev, id)) { + return NULL; + } - /* Mark it as dead */ - aurb->packet = NULL; - break; + p = usb_ep_find_packet_by_id(&dev->dev, + (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT, + ep & 0x0f, id); + if (p == NULL) { + ERROR("could not find packet with id %"PRIu64"\n", id); } + return p; } static void bufp_alloc(USBRedirDevice *dev, @@ -484,24 +483,22 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep) static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, uint8_t ep) { - AsyncURB *aurb = async_alloc(dev, p); struct usb_redir_bulk_packet_header bulk_packet; - DPRINTF("bulk-out ep %02X len %zd id %u\n", ep, - p->iov.size, aurb->packet_id); + DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id); bulk_packet.endpoint = ep; bulk_packet.length = p->iov.size; bulk_packet.stream_id = 0; if (ep & USB_DIR_IN) { - usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, NULL, 0); } else { uint8_t buf[p->iov.size]; usb_packet_copy(p, buf, p->iov.size); usbredir_log_data(dev, "bulk data out:", buf, p->iov.size); - usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id, + usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, buf, p->iov.size); } usbredirparser_do_write(dev->parser); @@ -564,19 +561,18 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev, return len; } else { /* Output interrupt endpoint, normal async operation */ - AsyncURB *aurb = async_alloc(dev, p); struct usb_redir_interrupt_packet_header interrupt_packet; uint8_t buf[p->iov.size]; - DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size, - aurb->packet_id); + DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep, + p->iov.size, p->id); interrupt_packet.endpoint = ep; interrupt_packet.length = p->iov.size; usb_packet_copy(p, buf, p->iov.size); usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size); - usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id, + usbredirparser_send_interrupt_packet(dev->parser, p->id, &interrupt_packet, buf, p->iov.size); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; @@ -630,10 +626,9 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, int config) { struct usb_redir_set_configuration_header set_config; - AsyncURB *aurb = async_alloc(dev, p); int i; - DPRINTF("set config %d id %u\n", config, aurb->packet_id); + DPRINTF("set config %d id %"PRIu64"\n", config, p->id); for (i = 0; i < MAX_ENDPOINTS; i++) { switch (dev->endpoint[i].type) { @@ -650,19 +645,16 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p, } set_config.configuration = config; - usbredirparser_send_set_configuration(dev->parser, aurb->packet_id, - &set_config); + usbredirparser_send_set_configuration(dev->parser, p->id, &set_config); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; } static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p) { - AsyncURB *aurb = async_alloc(dev, p); - - DPRINTF("get config id %u\n", aurb->packet_id); + DPRINTF("get config id %"PRIu64"\n", p->id); - usbredirparser_send_get_configuration(dev->parser, aurb->packet_id); + usbredirparser_send_get_configuration(dev->parser, p->id); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; } @@ -671,11 +663,9 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, int interface, int alt) { struct usb_redir_set_alt_setting_header set_alt; - AsyncURB *aurb = async_alloc(dev, p); int i; - DPRINTF("set interface %d alt %d id %u\n", interface, alt, - aurb->packet_id); + DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id); for (i = 0; i < MAX_ENDPOINTS; i++) { if (dev->endpoint[i].interface == interface) { @@ -695,8 +685,7 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p, set_alt.interface = interface; set_alt.alt = alt; - usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id, - &set_alt); + usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; } @@ -705,13 +694,11 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p, int interface) { struct usb_redir_get_alt_setting_header get_alt; - AsyncURB *aurb = async_alloc(dev, p); - DPRINTF("get interface %d id %u\n", interface, aurb->packet_id); + DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id); get_alt.interface = interface; - usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id, - &get_alt); + usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt); usbredirparser_do_write(dev->parser); return USB_RET_ASYNC; } @@ -721,7 +708,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, { USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev); struct usb_redir_control_packet_header control_packet; - AsyncURB *aurb; /* Special cases for certain standard device requests */ switch (request) { @@ -739,13 +725,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, return usbredir_get_interface(dev, p, index); } - /* "Normal" ctrl requests */ - aurb = async_alloc(dev, p); - - /* Note request is (bRequestType << 8) | bRequest */ - DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n", - request >> 8, request & 0xff, value, index, length, - aurb->packet_id); + /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */ + DPRINTF( + "ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n", + request >> 8, request & 0xff, value, index, length, p->id); control_packet.request = request & 0xFF; control_packet.requesttype = request >> 8; @@ -755,11 +738,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p, control_packet.length = length; if (control_packet.requesttype & USB_DIR_IN) { - usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + usbredirparser_send_control_packet(dev->parser, p->id, &control_packet, NULL, 0); } else { usbredir_log_data(dev, "ctrl data out:", data, length); - usbredirparser_send_control_packet(dev->parser, aurb->packet_id, + usbredirparser_send_control_packet(dev->parser, p->id, &control_packet, data, length); } usbredirparser_do_write(dev->parser); @@ -913,7 +896,7 @@ static int usbredir_initfn(USBDevice *udev) dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev); dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev); - QTAILQ_INIT(&dev->asyncq); + QTAILQ_INIT(&dev->cancelled); for (i = 0; i < MAX_ENDPOINTS; i++) { QTAILQ_INIT(&dev->endpoint[i].bufpq); } @@ -932,11 +915,12 @@ static int usbredir_initfn(USBDevice *udev) static void usbredir_cleanup_device_queues(USBRedirDevice *dev) { - AsyncURB *aurb, *next_aurb; + Cancelled *c, *next_c; int i; - QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) { - async_free(dev, aurb); + QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) { + QTAILQ_REMOVE(&dev->cancelled, c, next); + g_free(c); } for (i = 0; i < MAX_ENDPOINTS; i++) { usbredir_free_bufpq(dev, I2EP(i)); @@ -1202,33 +1186,28 @@ static void usbredir_configuration_status(void *priv, uint32_t id, struct usb_redir_configuration_status_header *config_status) { USBRedirDevice *dev = priv; - AsyncURB *aurb; + USBPacket *p; int len = 0; DPRINTF("set config status %d config %d id %u\n", config_status->status, config_status->configuration, id); - aurb = async_find(dev, id); - if (!aurb) { - return; - } - if (aurb->packet) { + p = usbredir_find_packet_by_id(dev, 0, id); + if (p) { if (dev->dev.setup_buf[0] & USB_DIR_IN) { dev->dev.data_buf[0] = config_status->configuration; len = 1; } - aurb->packet->result = - usbredir_handle_status(dev, config_status->status, len); - usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + p->result = usbredir_handle_status(dev, config_status->status, len); + usb_generic_async_ctrl_complete(&dev->dev, p); } - async_free(dev, aurb); } static void usbredir_alt_setting_status(void *priv, uint32_t id, struct usb_redir_alt_setting_status_header *alt_setting_status) { USBRedirDevice *dev = priv; - AsyncURB *aurb; + USBPacket *p; int len = 0; DPRINTF("alt status %d intf %d alt %d id: %u\n", @@ -1236,20 +1215,16 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, alt_setting_status->interface, alt_setting_status->alt, id); - aurb = async_find(dev, id); - if (!aurb) { - return; - } - if (aurb->packet) { + p = usbredir_find_packet_by_id(dev, 0, id); + if (p) { if (dev->dev.setup_buf[0] & USB_DIR_IN) { dev->dev.data_buf[0] = alt_setting_status->alt; len = 1; } - aurb->packet->result = + p->result = usbredir_handle_status(dev, alt_setting_status->status, len); - usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + usb_generic_async_ctrl_complete(&dev->dev, p); } - async_free(dev, aurb); } static void usbredir_iso_stream_status(void *priv, uint32_t id, @@ -1304,19 +1279,14 @@ static void usbredir_control_packet(void *priv, uint32_t id, uint8_t *data, int data_len) { USBRedirDevice *dev = priv; + USBPacket *p; int len = control_packet->length; - AsyncURB *aurb; DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, len, id); - aurb = async_find(dev, id); - if (!aurb) { - free(data); - return; - } - - if (aurb->packet) { + p = usbredir_find_packet_by_id(dev, 0, id); + if (p) { len = usbredir_handle_status(dev, control_packet->status, len); if (len > 0) { usbredir_log_data(dev, "ctrl data in:", data, data_len); @@ -1328,10 +1298,9 @@ static void usbredir_control_packet(void *priv, uint32_t id, len = USB_RET_STALL; } } - aurb->packet->result = len; - usb_generic_async_ctrl_complete(&dev->dev, aurb->packet); + p->result = len; + usb_generic_async_ctrl_complete(&dev->dev, p); } - async_free(dev, aurb); free(data); } @@ -1342,33 +1311,27 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, USBRedirDevice *dev = priv; uint8_t ep = bulk_packet->endpoint; int len = bulk_packet->length; - AsyncURB *aurb; + USBPacket *p; DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, ep, len, id); - aurb = async_find(dev, id); - if (!aurb) { - free(data); - return; - } - - if (aurb->packet) { + p = usbredir_find_packet_by_id(dev, ep, id); + if (p) { len = usbredir_handle_status(dev, bulk_packet->status, len); if (len > 0) { usbredir_log_data(dev, "bulk data in:", data, data_len); - if (data_len <= aurb->packet->iov.size) { - usb_packet_copy(aurb->packet, data, data_len); + if (data_len <= p->iov.size) { + usb_packet_copy(p, data, data_len); } else { ERROR("bulk buffer too small (%d > %zd)\n", data_len, - aurb->packet->iov.size); + p->iov.size); len = USB_RET_STALL; } } - aurb->packet->result = len; - usb_packet_complete(&dev->dev, aurb->packet); + p->result = len; + usb_packet_complete(&dev->dev, p); } - async_free(dev, aurb); free(data); } @@ -1426,17 +1389,12 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id, } else { int len = interrupt_packet->length; - AsyncURB *aurb = async_find(dev, id); - if (!aurb) { - return; - } - - if (aurb->packet) { - aurb->packet->result = usbredir_handle_status(dev, + USBPacket *p = usbredir_find_packet_by_id(dev, ep, id); + if (p) { + p->result = usbredir_handle_status(dev, interrupt_packet->status, len); - usb_packet_complete(&dev->dev, aurb->packet); + usb_packet_complete(&dev->dev, p); } - async_free(dev, aurb); } } -- cgit v1.2.3 From 2979a36183a3902cd75665e7c6bbc8668668fd17 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 28 Aug 2012 11:33:47 +0200 Subject: usb-redir: Return babble when getting more bulk data then requested Babble is the appropriate error in this case (rather then signalling a stall). Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index fd1f8cc51..ee7521760 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1324,9 +1324,9 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, if (data_len <= p->iov.size) { usb_packet_copy(p, data, data_len); } else { - ERROR("bulk buffer too small (%d > %zd)\n", data_len, - p->iov.size); - len = USB_RET_STALL; + ERROR("bulk got more data then requested (%d > %zd)\n", + data_len, p->iov.size); + len = USB_RET_BABBLE; } } p->result = len; -- cgit v1.2.3 From be4a892846651e06dbbd9a48aa877f4e0397d01e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 31 Aug 2012 13:41:38 +0200 Subject: usb-redir: Convert to new libusbredirparser 0.5 API This gives us support for 64 bit ids which is needed for using XHCI with the new hcd generated ids. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- configure | 2 +- hw/usb/redirect.c | 62 +++++++++++++++++++++++++++---------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/configure b/configure index 30be78488..7656c32b2 100755 --- a/configure +++ b/configure @@ -2758,7 +2758,7 @@ fi # check for usbredirparser for usb network redirection support if test "$usb_redir" != "no" ; then - if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then + if $pkg_config --atleast-version=0.5 libusbredirparser >/dev/null 2>&1 ; then usb_redir="yes" usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index ee7521760..eeeb003c5 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -107,27 +107,27 @@ static void usbredir_interface_info(void *priv, struct usb_redir_interface_info_header *interface_info); static void usbredir_ep_info(void *priv, struct usb_redir_ep_info_header *ep_info); -static void usbredir_configuration_status(void *priv, uint32_t id, +static void usbredir_configuration_status(void *priv, uint64_t id, struct usb_redir_configuration_status_header *configuration_status); -static void usbredir_alt_setting_status(void *priv, uint32_t id, +static void usbredir_alt_setting_status(void *priv, uint64_t id, struct usb_redir_alt_setting_status_header *alt_setting_status); -static void usbredir_iso_stream_status(void *priv, uint32_t id, +static void usbredir_iso_stream_status(void *priv, uint64_t id, struct usb_redir_iso_stream_status_header *iso_stream_status); -static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, +static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status); -static void usbredir_bulk_streams_status(void *priv, uint32_t id, +static void usbredir_bulk_streams_status(void *priv, uint64_t id, struct usb_redir_bulk_streams_status_header *bulk_streams_status); -static void usbredir_control_packet(void *priv, uint32_t id, +static void usbredir_control_packet(void *priv, uint64_t id, struct usb_redir_control_packet_header *control_packet, uint8_t *data, int data_len); -static void usbredir_bulk_packet(void *priv, uint32_t id, +static void usbredir_bulk_packet(void *priv, uint64_t id, struct usb_redir_bulk_packet_header *bulk_packet, uint8_t *data, int data_len); -static void usbredir_iso_packet(void *priv, uint32_t id, +static void usbredir_iso_packet(void *priv, uint64_t id, struct usb_redir_iso_packet_header *iso_packet, uint8_t *data, int data_len); -static void usbredir_interrupt_packet(void *priv, uint32_t id, +static void usbredir_interrupt_packet(void *priv, uint64_t id, struct usb_redir_interrupt_packet_header *interrupt_header, uint8_t *data, int data_len); @@ -805,6 +805,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); usbredirparser_do_write(dev->parser); } @@ -1182,15 +1183,15 @@ static void usbredir_ep_info(void *priv, } } -static void usbredir_configuration_status(void *priv, uint32_t id, +static void usbredir_configuration_status(void *priv, uint64_t id, struct usb_redir_configuration_status_header *config_status) { USBRedirDevice *dev = priv; USBPacket *p; int len = 0; - DPRINTF("set config status %d config %d id %u\n", config_status->status, - config_status->configuration, id); + DPRINTF("set config status %d config %d id %"PRIu64"\n", + config_status->status, config_status->configuration, id); p = usbredir_find_packet_by_id(dev, 0, id); if (p) { @@ -1203,16 +1204,15 @@ static void usbredir_configuration_status(void *priv, uint32_t id, } } -static void usbredir_alt_setting_status(void *priv, uint32_t id, +static void usbredir_alt_setting_status(void *priv, uint64_t id, struct usb_redir_alt_setting_status_header *alt_setting_status) { USBRedirDevice *dev = priv; USBPacket *p; int len = 0; - DPRINTF("alt status %d intf %d alt %d id: %u\n", - alt_setting_status->status, - alt_setting_status->interface, + DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n", + alt_setting_status->status, alt_setting_status->interface, alt_setting_status->alt, id); p = usbredir_find_packet_by_id(dev, 0, id); @@ -1227,13 +1227,13 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id, } } -static void usbredir_iso_stream_status(void *priv, uint32_t id, +static void usbredir_iso_stream_status(void *priv, uint64_t id, struct usb_redir_iso_stream_status_header *iso_stream_status) { USBRedirDevice *dev = priv; uint8_t ep = iso_stream_status->endpoint; - DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status, + DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status, ep, id); if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) { @@ -1247,14 +1247,14 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id, } } -static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, +static void usbredir_interrupt_receiving_status(void *priv, uint64_t id, struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status) { USBRedirDevice *dev = priv; uint8_t ep = interrupt_receiving_status->endpoint; - DPRINTF("interrupt recv status %d ep %02X id %u\n", + DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n", interrupt_receiving_status->status, ep, id); if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) { @@ -1269,12 +1269,12 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id, } } -static void usbredir_bulk_streams_status(void *priv, uint32_t id, +static void usbredir_bulk_streams_status(void *priv, uint64_t id, struct usb_redir_bulk_streams_status_header *bulk_streams_status) { } -static void usbredir_control_packet(void *priv, uint32_t id, +static void usbredir_control_packet(void *priv, uint64_t id, struct usb_redir_control_packet_header *control_packet, uint8_t *data, int data_len) { @@ -1282,7 +1282,7 @@ static void usbredir_control_packet(void *priv, uint32_t id, USBPacket *p; int len = control_packet->length; - DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status, + DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status, len, id); p = usbredir_find_packet_by_id(dev, 0, id); @@ -1304,7 +1304,7 @@ static void usbredir_control_packet(void *priv, uint32_t id, free(data); } -static void usbredir_bulk_packet(void *priv, uint32_t id, +static void usbredir_bulk_packet(void *priv, uint64_t id, struct usb_redir_bulk_packet_header *bulk_packet, uint8_t *data, int data_len) { @@ -1313,8 +1313,8 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, int len = bulk_packet->length; USBPacket *p; - DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status, - ep, len, id); + DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n", + bulk_packet->status, ep, len, id); p = usbredir_find_packet_by_id(dev, ep, id); if (p) { @@ -1335,15 +1335,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id, free(data); } -static void usbredir_iso_packet(void *priv, uint32_t id, +static void usbredir_iso_packet(void *priv, uint64_t id, struct usb_redir_iso_packet_header *iso_packet, uint8_t *data, int data_len) { USBRedirDevice *dev = priv; uint8_t ep = iso_packet->endpoint; - DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep, - data_len, id); + DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n", + iso_packet->status, ep, data_len, id); if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) { ERROR("received iso packet for non iso endpoint %02X\n", ep); @@ -1361,14 +1361,14 @@ static void usbredir_iso_packet(void *priv, uint32_t id, bufp_alloc(dev, data, data_len, iso_packet->status, ep); } -static void usbredir_interrupt_packet(void *priv, uint32_t id, +static void usbredir_interrupt_packet(void *priv, uint64_t id, struct usb_redir_interrupt_packet_header *interrupt_packet, uint8_t *data, int data_len) { USBRedirDevice *dev = priv; uint8_t ep = interrupt_packet->endpoint; - DPRINTF("interrupt-in status %d ep %02X len %d id %u\n", + DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n", interrupt_packet->status, ep, data_len, id); if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) { -- cgit v1.2.3 From 0fde3b7a826aa654612126cdea9832319a34172c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 11:49:07 +0200 Subject: usb-redir: Set ep max_packet_size if available This is needed for usb-redir to work properly with the xhci emulation. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index eeeb003c5..a91e073aa 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -805,6 +805,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev) usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version); usbredirparser_caps_set_cap(caps, usb_redir_cap_filter); + usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size); usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids); usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0); usbredirparser_do_write(dev->parser); @@ -1180,6 +1181,10 @@ static void usbredir_ep_info(void *priv, i & 0x0f); usb_ep->type = dev->endpoint[i].type; usb_ep->ifnum = dev->endpoint[i].interface; + if (usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size)) { + usb_ep->max_packet_size = ep_info->max_packet_size[i]; + } } } -- cgit v1.2.3 From 910c1e6b14e4abb188ff7ef584a629187479f82d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 11:53:28 +0200 Subject: usb-redir: Add a usbredir_reject_device helper function Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index a91e073aa..c5182012f 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -811,16 +811,21 @@ static void usbredir_chardev_open(USBRedirDevice *dev) usbredirparser_do_write(dev->parser); } +static void usbredir_reject_device(USBRedirDevice *dev) +{ + usbredir_device_disconnect(dev); + if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { + usbredirparser_send_filter_reject(dev->parser); + usbredirparser_do_write(dev->parser); + } +} + static void usbredir_do_attach(void *opaque) { USBRedirDevice *dev = opaque; if (usb_device_attach(&dev->dev) != 0) { - usbredir_device_disconnect(dev); - if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { - usbredirparser_send_filter_reject(dev->parser); - usbredirparser_do_write(dev->parser); - } + usbredir_reject_device(dev); } } @@ -986,11 +991,7 @@ static int usbredir_check_filter(USBRedirDevice *dev) return 0; error: - usbredir_device_disconnect(dev); - if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) { - usbredirparser_send_filter_reject(dev->parser); - usbredirparser_do_write(dev->parser); - } + usbredir_reject_device(dev); return -1; } -- cgit v1.2.3 From a508cc42e22a2ac3878b964290780f1d81ec65c1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 12:04:49 +0200 Subject: usb-redir: Ensure our peer has the necessary caps when redirecting to XHCI In order for redirection to work properly when redirecting to an emulated XHCI controller, the usb-redir-host must support both usb_redir_cap_ep_info_max_packet_size and usb_redir_cap_64bits_ids, reject any devices redirected to an XHCI controller when these are not supported. Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index c5182012f..7fb0fb3de 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -824,6 +824,17 @@ static void usbredir_do_attach(void *opaque) { USBRedirDevice *dev = opaque; + /* In order to work properly with XHCI controllers we need these caps */ + if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !( + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_ep_info_max_packet_size) && + usbredirparser_peer_has_cap(dev->parser, + usb_redir_cap_64bits_ids))) { + ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n"); + usbredir_reject_device(dev); + return; + } + if (usb_device_attach(&dev->dev) != 0) { usbredir_reject_device(dev); } -- cgit v1.2.3 From 6c67446a427b50a706e628f810116353f5d128cf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 3 Sep 2012 13:44:04 +0200 Subject: usb-redir: Enable pipelining for bulk endpoints Signed-off-by: Hans de Goede Signed-off-by: Gerd Hoffmann --- hw/usb/redirect.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 7fb0fb3de..5301a69c4 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1197,6 +1197,9 @@ static void usbredir_ep_info(void *priv, usb_redir_cap_ep_info_max_packet_size)) { usb_ep->max_packet_size = ep_info->max_packet_size[i]; } + if (ep_info->type[i] == usb_redir_type_bulk) { + usb_ep->pipeline = true; + } } } -- cgit v1.2.3 From 2964cd9bfa5100e433471d3e3fedcc9d62891894 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Thu, 23 Aug 2012 09:59:27 +0200 Subject: Better name usb braille device Windows users need to know that they have to use the Baum driver to make the qemu braille device work. Signed-off-by: Samuel Thibault Signed-off-by: Gerd Hoffmann --- hw/usb/dev-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 8aa655286..69b6e48d5 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -113,7 +113,7 @@ enum { static const USBDescStrings desc_strings = { [STR_MANUFACTURER] = "QEMU", [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", - [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE", + [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE", [STR_SERIALNUMBER] = "1", }; -- cgit v1.2.3 From 2bbd086c41a00dc4384727ec895a94890c688eb5 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 Aug 2012 16:43:34 +0200 Subject: usb-audio: fix usb version usb-audio is a full speed (1.1) device, but bcdUSB claims it is usb 2.0. Fix it. Signed-off-by: Gerd Hoffmann --- hw/usb/dev-audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 79b75fb62..2594c7810 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -217,7 +217,7 @@ static const USBDescIface desc_iface[] = { }; static const USBDescDevice desc_device = { - .bcdUSB = 0x0200, + .bcdUSB = 0x0100, .bMaxPacketSize0 = 64, .bNumConfigurations = 1, .confs = (USBDescConfig[]) { -- cgit v1.2.3 From 331e9406f152b6bae6859a153d36e5076c58901d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 17 Aug 2012 14:05:21 +0200 Subject: xhci: rip out background transfer code original xhci code (the one which used libusb directly) used to use 'background transfers' for iso streams. In upstream qemu the iso stream buffering is handled by usb-host & usb-redir, so we will never ever need this. It has been left in as reference, but is dead code anyway. Rip it out. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 223 +----------------------------------------------------- 1 file changed, 4 insertions(+), 219 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 3eb27fadb..c0a2476f0 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -45,8 +45,6 @@ #define MAXPORTS (USB2_PORTS+USB3_PORTS) #define TD_QUEUE 24 -#define BG_XFERS 8 -#define BG_PKTS 8 /* Very pessimistic, let's hope it's enough for all cases */ #define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS) @@ -311,13 +309,11 @@ typedef struct XHCITransfer { bool running_retry; bool cancelled; bool complete; - bool backgrounded; unsigned int iso_pkts; unsigned int slotid; unsigned int epid; bool in_xfer; bool iso_xfer; - bool bg_xfer; unsigned int trb_count; unsigned int trb_alloced; @@ -340,14 +336,9 @@ typedef struct XHCIEPContext { unsigned int comp_xfer; XHCITransfer transfers[TD_QUEUE]; XHCITransfer *retry; - bool bg_running; - bool bg_updating; - unsigned int next_bg; - XHCITransfer bg_transfers[BG_XFERS]; EPType type; dma_addr_t pctx; unsigned int max_psize; - bool has_bg; uint32_t state; } XHCIEPContext; @@ -866,10 +857,6 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, epctx->pctx = pctx; epctx->max_psize = ctx[1]>>16; epctx->max_psize *= 1+((ctx[1]>>8)&0xff); - epctx->has_bg = false; - if (epctx->type == ET_ISO_IN) { - epctx->has_bg = true; - } DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n", epid/2, epid%2, epctx->max_psize); for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) { @@ -916,9 +903,6 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, t->running_retry = 0; epctx->retry = NULL; } - if (t->backgrounded) { - t->backgrounded = 0; - } if (t->trbs) { g_free(t->trbs); } @@ -932,25 +916,6 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, t->data_length = t->data_alloced = 0; xferi = (xferi + 1) % TD_QUEUE; } - if (epctx->has_bg) { - xferi = epctx->next_bg; - for (i = 0; i < BG_XFERS; i++) { - XHCITransfer *t = &epctx->bg_transfers[xferi]; - if (t->running_async) { - usb_cancel_packet(&t->packet); - t->running_async = 0; - t->cancelled = 1; - DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i); - killed++; - } - if (t->data) { - g_free(t->data); - } - - t->data = NULL; - xferi = (xferi + 1) % BG_XFERS; - } - } return killed; } @@ -1231,160 +1196,6 @@ static void xhci_stall_ep(XHCITransfer *xfer) static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx); -static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx) -{ - if (epctx->bg_updating) { - return; - } - DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx); - assert(epctx->has_bg); - DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg); - epctx->bg_updating = 1; - while (epctx->transfers[epctx->comp_xfer].backgrounded && - epctx->bg_transfers[epctx->next_bg].complete) { - XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer]; - XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg]; -#if 0 - DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n", - epctx->comp_xfer, epctx->next_bg, bg->cur_pkt, - bg->usbxfer->iso_packet_desc[bg->cur_pkt].status - ); -#endif - assert(epctx->type == ET_ISO_IN); - assert(bg->iso_xfer); - assert(bg->in_xfer); - uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize; -#if 0 - int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length; - fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status); -#else - int len = 0; - FIXME(); -#endif - fg->complete = 1; - fg->backgrounded = 0; - - if (fg->status == CC_STALL_ERROR) { - xhci_stall_ep(fg); - } - - xhci_xfer_data(fg, p, len, 1, 0, 1); - - epctx->comp_xfer++; - if (epctx->comp_xfer == TD_QUEUE) { - epctx->comp_xfer = 0; - } - DPRINTF("next fg xfer: %d\n", epctx->comp_xfer); - bg->cur_pkt++; - if (bg->cur_pkt == bg->pkts) { - bg->complete = 0; - if (xhci_submit(xhci, bg, epctx) < 0) { - fprintf(stderr, "xhci: bg resubmit failed\n"); - } - epctx->next_bg++; - if (epctx->next_bg == BG_XFERS) { - epctx->next_bg = 0; - } - DPRINTF("next bg xfer: %d\n", epctx->next_bg); - - xhci_kick_ep(xhci, fg->slotid, fg->epid); - } - } - epctx->bg_updating = 0; -} - -#if 0 -static void xhci_xfer_cb(struct libusb_transfer *transfer) -{ - XHCIState *xhci; - XHCITransfer *xfer; - - xfer = (XHCITransfer *)transfer->user_data; - xhci = xfer->xhci; - - DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid, - xfer->epid, transfer->status); - - assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS); - assert(xfer->epid >= 1 && xfer->epid <= 31); - - if (xfer->cancelled) { - DPRINTF("xhci: transfer cancelled, not reporting anything\n"); - xfer->running = 0; - return; - } - - XHCIEPContext *epctx; - XHCISlot *slot; - slot = &xhci->slots[xfer->slotid-1]; - assert(slot->eps[xfer->epid-1]); - epctx = slot->eps[xfer->epid-1]; - - if (xfer->bg_xfer) { - DPRINTF("xhci: background transfer, updating\n"); - xfer->complete = 1; - xfer->running = 0; - xhci_bg_update(xhci, epctx); - return; - } - - if (xfer->iso_xfer) { - transfer->status = transfer->iso_packet_desc[0].status; - transfer->actual_length = transfer->iso_packet_desc[0].actual_length; - } - - xfer->status = libusb_to_ccode(transfer->status); - - xfer->complete = 1; - xfer->running = 0; - - if (transfer->status == LIBUSB_TRANSFER_STALL) - xhci_stall_ep(xhci, epctx, xfer); - - DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length); - - if (xfer->in_xfer) { - if (xfer->epid == 1) { - xhci_xfer_data(xhci, xfer, xfer->data + 8, - transfer->actual_length, 1, 0, 1); - } else { - xhci_xfer_data(xhci, xfer, xfer->data, - transfer->actual_length, 1, 0, 1); - } - } else { - xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1); - } - - xhci_kick_ep(xhci, xfer->slotid, xfer->epid); -} - -static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer, - uint8_t bmRequestType, uint8_t bRequest, - uint16_t wValue, uint16_t wIndex, uint16_t wLength) -{ - uint16_t type_req = (bmRequestType << 8) | bRequest; - - switch (type_req) { - case 0x0000 | USB_REQ_SET_CONFIGURATION: - DPRINTF("xhci: HLE switch configuration\n"); - return xhci_switch_config(xhci, xfer->slotid, wValue) == 0; - case 0x0100 | USB_REQ_SET_INTERFACE: - DPRINTF("xhci: HLE set interface altsetting\n"); - return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0; - case 0x0200 | USB_REQ_CLEAR_FEATURE: - if (wValue == 0) { // endpoint halt - DPRINTF("xhci: HLE clear halt\n"); - return xhci_clear_halt(xhci, xfer->slotid, wIndex); - } - case 0x0000 | USB_REQ_SET_ADDRESS: - fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n"); - return 0; - default: - return 0; - } -} -#endif - static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) { USBEndpoint *ep; @@ -1559,9 +1370,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx xfer->data_alloced = xfer->data_length; } if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { - if (!xfer->bg_xfer) { - xfer->pkts = 1; - } + xfer->pkts = 1; } else { xfer->pkts = 0; } @@ -1620,32 +1429,8 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length); - if (!epctx->has_bg) { - xfer->data_length = length; - xfer->backgrounded = 0; - return xhci_submit(xhci, xfer, epctx); - } else { - if (!epctx->bg_running) { - for (i = 0; i < BG_XFERS; i++) { - XHCITransfer *t = &epctx->bg_transfers[i]; - t->xhci = xhci; - t->epid = xfer->epid; - t->slotid = xfer->slotid; - t->pkts = BG_PKTS; - t->pktsize = epctx->max_psize; - t->data_length = t->pkts * t->pktsize; - t->bg_xfer = 1; - if (xhci_submit(xhci, t, epctx) < 0) { - fprintf(stderr, "xhci: bg submit failed\n"); - return -1; - } - } - epctx->bg_running = 1; - } - xfer->backgrounded = 1; - xhci_bg_update(xhci, epctx); - return 0; - } + xfer->data_length = length; + return xhci_submit(xhci, xfer, epctx); } static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid) @@ -1695,7 +1480,7 @@ 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) { + if (xfer->running_async || xfer->running_retry) { break; } length = xhci_ring_chain_length(xhci, &epctx->ring); -- cgit v1.2.3 From d5a15814b413869667b2a3215772986885be574a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 17 Aug 2012 11:04:36 +0200 Subject: xhci: drop buffering This patch splits the xhci_xfer_data function into three. The xhci_xfer_data function used to do does two things: (1) copy transfer data between guest memory and a temporary buffer. (2) report transfer results to the guest using events. Now we three functions to handle this: (1) xhci_xfer_map creates a scatter list for the transfer and uses that (instead of the temporary buffer) to build a USBPacket. (2) xhci_xfer_unmap undoes the mapping. (3) xhci_xfer_report sends out events. The patch also fixes reporting of transaction errors which must be reported unconditinally, not only in case the guest asks for it using the ISP flag. [ v2: fix warning ] Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 185 +++++++++++++++++++++--------------------------------- trace-events | 2 +- 2 files changed, 72 insertions(+), 115 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index c0a2476f0..446d692ea 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -305,6 +305,7 @@ typedef struct XHCIState XHCIState; typedef struct XHCITransfer { XHCIState *xhci; USBPacket packet; + QEMUSGList sgl; bool running_async; bool running_retry; bool cancelled; @@ -319,10 +320,6 @@ typedef struct XHCITransfer { unsigned int trb_alloced; XHCITRB *trbs; - unsigned int data_length; - unsigned int data_alloced; - uint8_t *data; - TRBCCode status; unsigned int pkts; @@ -906,14 +903,9 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, if (t->trbs) { g_free(t->trbs); } - if (t->data) { - g_free(t->data); - } t->trbs = NULL; - t->data = NULL; t->trb_count = t->trb_alloced = 0; - t->data_length = t->data_alloced = 0; xferi = (xferi + 1) % TD_QUEUE; } return killed; @@ -1072,24 +1064,13 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, return CC_SUCCESS; } -static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, - unsigned int length, bool in_xfer, bool out_xfer, - bool report) +static int xhci_xfer_map(XHCITransfer *xfer) { - int i; - uint32_t edtla = 0; - unsigned int transferred = 0; - unsigned int left = length; - bool reported = 0; - bool shortpkt = 0; - XHCIEvent event = {ER_TRANSFER, CC_SUCCESS}; + int in_xfer = (xfer->packet.pid == USB_TOKEN_IN); XHCIState *xhci = xfer->xhci; + int i; - DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n", - length, in_xfer, out_xfer, report); - - assert(!(in_xfer && out_xfer)); - + pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count); for (i = 0; i < xfer->trb_count; i++) { XHCITRB *trb = &xfer->trbs[i]; dma_addr_t addr; @@ -1099,54 +1080,70 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, case TR_DATA: if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n"); - xhci_die(xhci); - return transferred; + goto err; } /* fallthrough */ case TR_NORMAL: case TR_ISOCH: addr = xhci_mask64(trb->parameter); + chunk = trb->status & 0x1ffff; + if (trb->control & TRB_TR_IDT) { + if (chunk > 8 || in_xfer) { + fprintf(stderr, "xhci: invalid immediate data TRB\n"); + goto err; + } + qemu_sglist_add(&xfer->sgl, trb->addr, chunk); + } else { + qemu_sglist_add(&xfer->sgl, addr, chunk); + } + break; + } + } + + usb_packet_map(&xfer->packet, &xfer->sgl); + return 0; + +err: + qemu_sglist_destroy(&xfer->sgl); + xhci_die(xhci); + return -1; +} + +static void xhci_xfer_unmap(XHCITransfer *xfer) +{ + usb_packet_unmap(&xfer->packet, &xfer->sgl); + qemu_sglist_destroy(&xfer->sgl); +} + +static void xhci_xfer_report(XHCITransfer *xfer) +{ + uint32_t edtla = 0; + unsigned int left; + bool reported = 0; + bool shortpkt = 0; + XHCIEvent event = {ER_TRANSFER, CC_SUCCESS}; + XHCIState *xhci = xfer->xhci; + int i; + + left = xfer->packet.result < 0 ? 0 : xfer->packet.result; + + for (i = 0; i < xfer->trb_count; i++) { + XHCITRB *trb = &xfer->trbs[i]; + unsigned int chunk = 0; + + switch (TRB_TYPE(*trb)) { + case TR_DATA: + case TR_NORMAL: + case TR_ISOCH: chunk = trb->status & 0x1ffff; if (chunk > left) { chunk = left; - shortpkt = 1; - } - if (in_xfer || out_xfer) { - if (trb->control & TRB_TR_IDT) { - uint64_t idata; - if (chunk > 8 || in_xfer) { - fprintf(stderr, "xhci: invalid immediate data TRB\n"); - xhci_die(xhci); - return transferred; - } - idata = le64_to_cpu(trb->parameter); - memcpy(data, &idata, chunk); - } else { - DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at " - DMA_ADDR_FMT "\n", in_xfer, chunk, addr); - if (in_xfer) { - pci_dma_write(&xhci->pci_dev, addr, data, chunk); - } else { - pci_dma_read(&xhci->pci_dev, addr, data, chunk); - } -#ifdef DEBUG_DATA - unsigned int count = chunk; - int i; - if (count > 16) { - count = 16; - } - DPRINTF(" ::"); - for (i = 0; i < count; i++) { - DPRINTF(" %02x", data[i]); - } - DPRINTF("\n"); -#endif + if (xfer->status == CC_SUCCESS) { + shortpkt = 1; } } left -= chunk; - data += chunk; edtla += chunk; - transferred += chunk; break; case TR_STATUS: reported = 0; @@ -1154,8 +1151,9 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, break; } - if (report && !reported && (trb->control & TRB_TR_IOC || - (shortpkt && (trb->control & TRB_TR_ISP)))) { + if (!reported && ((trb->control & TRB_TR_IOC) || + (shortpkt && (trb->control & TRB_TR_ISP)) || + (xfer->status != CC_SUCCESS))) { event.slotid = xfer->slotid; event.epid = xfer->epid; event.length = (trb->status & 0x1ffff) - chunk; @@ -1175,9 +1173,11 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data, } xhci_event(xhci, &event); reported = 1; + if (xfer->status != CC_SUCCESS) { + return; + } } } - return transferred; } static void xhci_stall_ep(XHCITransfer *xfer) @@ -1204,7 +1204,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; ep = usb_ep_get(dev, dir, xfer->epid >> 1); usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr); - usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length); + xhci_xfer_map(xfer); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", xfer->packet.pid, dev->addr, ep->nr); return 0; @@ -1230,12 +1230,13 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) xfer->running_async = 0; xfer->running_retry = 0; xfer->complete = 1; + xhci_xfer_unmap(xfer); } 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); + xfer->status = CC_SUCCESS; + xhci_xfer_report(xfer); return 0; } @@ -1244,12 +1245,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) switch (ret) { case USB_RET_NODEV: xfer->status = CC_USB_TRANSACTION_ERROR; - xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1); + xhci_xfer_report(xfer); xhci_stall_ep(xfer); break; case USB_RET_STALL: xfer->status = CC_STALL_ERROR; - xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1); + xhci_xfer_report(xfer); xhci_stall_ep(xfer); break; default: @@ -1271,7 +1272,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) { XHCITRB *trb_setup, *trb_status; uint8_t bmRequestType; - uint16_t wLength; XHCIPort *port; USBDevice *dev; int ret; @@ -1279,8 +1279,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) 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); + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); /* at most one Event Data TRB allowed after STATUS */ if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) { @@ -1309,19 +1308,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) } bmRequestType = trb_setup->parameter; - wLength = trb_setup->parameter >> 48; - - if (xfer->data && xfer->data_alloced < wLength) { - xfer->data_alloced = 0; - g_free(xfer->data); - xfer->data = NULL; - } - if (!xfer->data) { - DPRINTF("xhci: alloc %d bytes data\n", wLength); - xfer->data = g_malloc(wLength+1); - xfer->data_alloced = wLength; - } - xfer->data_length = wLength; port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); @@ -1336,9 +1322,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) xhci_setup_packet(xfer, dev); xfer->packet.parameter = trb_setup->parameter; - if (!xfer->in_xfer) { - xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0); - } ret = usb_handle_packet(dev, &xfer->packet); @@ -1359,16 +1342,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx xfer->in_xfer = epctx->type>>2; - if (xfer->data && xfer->data_alloced < xfer->data_length) { - xfer->data_alloced = 0; - g_free(xfer->data); - xfer->data = NULL; - } - if (!xfer->data && xfer->data_length) { - DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length); - xfer->data = g_malloc(xfer->data_length); - xfer->data_alloced = xfer->data_length; - } if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { xfer->pkts = 1; } else { @@ -1402,9 +1375,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx return -1; } - if (!xfer->in_xfer) { - xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0); - } ret = usb_handle_packet(dev, &xfer->packet); xhci_complete_packet(xfer, ret); @@ -1416,20 +1386,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) { - int i; - unsigned int length = 0; - XHCITRB *trb; - - 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; - } - } - - trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length); - - xfer->data_length = length; + trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid); return xhci_submit(xhci, xfer, epctx); } diff --git a/trace-events b/trace-events index 10bc04eac..c83d65ede 100644 --- a/trace-events +++ b/trace-events @@ -326,7 +326,7 @@ 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_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d" usb_xhci_xfer_async(void *xfer) "%p" usb_xhci_xfer_nak(void *xfer) "%p" usb_xhci_xfer_retry(void *xfer) "%p" -- cgit v1.2.3 From 5c08106ff65c904c297dab17cd0b0a3a28b63527 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 Aug 2012 14:21:39 +0200 Subject: xhci: move device lookup into xhci_setup_packet Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 74 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 446d692ea..c108c9d47 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1196,13 +1196,38 @@ static void xhci_stall_ep(XHCITransfer *xfer) static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx); -static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev) +static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) { + if (!(port->portsc & PORTSC_PED)) { + return NULL; + } + return usb_find_device(&port->port, addr); +} + +static int xhci_setup_packet(XHCITransfer *xfer) +{ + XHCIState *xhci = xfer->xhci; + XHCIPort *port; + USBDevice *dev; USBEndpoint *ep; int dir; dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT; - ep = usb_ep_get(dev, dir, xfer->epid >> 1); + + if (xfer->packet.ep) { + ep = xfer->packet.ep; + dev = ep->dev; + } else { + port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; + dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); + if (!dev) { + fprintf(stderr, "xhci: slot %d port %d has no device\n", + xfer->slotid, xhci->slots[xfer->slotid-1].port); + return -1; + } + ep = usb_ep_get(dev, dir, xfer->epid >> 1); + } + usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr); xhci_xfer_map(xfer); DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n", @@ -1260,20 +1285,10 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret) return 0; } -static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) -{ - if (!(port->portsc & PORTSC_PED)) { - return NULL; - } - return usb_find_device(&port->port, addr); -} - static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) { XHCITRB *trb_setup, *trb_status; uint8_t bmRequestType; - XHCIPort *port; - USBDevice *dev; int ret; trb_setup = &xfer->trbs[0]; @@ -1309,21 +1324,15 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) bmRequestType = trb_setup->parameter; - port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); - if (!dev) { - fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, - xhci->slots[xfer->slotid-1].port); - return -1; - } - xfer->in_xfer = bmRequestType & USB_DIR_IN; xfer->iso_xfer = false; - xhci_setup_packet(xfer, dev); + if (xhci_setup_packet(xfer) < 0) { + return -1; + } xfer->packet.parameter = trb_setup->parameter; - ret = usb_handle_packet(dev, &xfer->packet); + ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); xhci_complete_packet(xfer, ret); if (!xfer->running_async && !xfer->running_retry) { @@ -1334,8 +1343,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) { - XHCIPort *port; - USBDevice *dev; int ret; DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); @@ -1348,16 +1355,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx xfer->pkts = 0; } - port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); - if (!dev) { - fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid, - xhci->slots[xfer->slotid-1].port); - return -1; - } - - xhci_setup_packet(xfer, dev); - switch(epctx->type) { case ET_INTR_OUT: case ET_INTR_IN: @@ -1375,7 +1372,10 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx return -1; } - ret = usb_handle_packet(dev, &xfer->packet); + if (xhci_setup_packet(xfer) < 0) { + return -1; + } + ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); xhci_complete_packet(xfer, ret); if (!xfer->running_async && !xfer->running_retry) { @@ -1418,7 +1418,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid trace_usb_xhci_xfer_retry(xfer); assert(xfer->running_retry); - xhci_setup_packet(xfer, xfer->packet.ep->dev); + if (xhci_setup_packet(xfer) < 0) { + return; + } result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); if (result == USB_RET_NAK) { return; -- cgit v1.2.3 From 01546fa6624ad0d1068423c58fa31bdfc44da2bf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 21 Aug 2012 12:32:58 +0200 Subject: xhci: implement mfindex Implement mfindex register and mfindex wrap event. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index c108c9d47..1d9940dcc 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -380,8 +380,6 @@ struct XHCIState { XHCISlot slots[MAXSLOTS]; /* Runtime Registers */ - uint32_t mfindex; - /* note: we only support one interrupter */ uint32_t iman; uint32_t imod; uint32_t erstsz; @@ -390,6 +388,9 @@ struct XHCIState { uint32_t erdp_low; uint32_t erdp_high; + int64_t mfindex_start; + QEMUTimer *mfwrap_timer; + dma_addr_t er_start; uint32_t er_size; bool er_pcs; @@ -410,6 +411,11 @@ typedef struct XHCIEvRingSeg { uint32_t rsvd; } XHCIEvRingSeg; +static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, + unsigned int epid); +static void xhci_event(XHCIState *xhci, XHCIEvent *event); +static void xhci_write_event(XHCIState *xhci, XHCIEvent *event); + static const char *TRBType_names[] = { [TRB_RESERVED] = "TRB_RESERVED", [TR_NORMAL] = "TR_NORMAL", @@ -462,8 +468,36 @@ static const char *trb_name(XHCITRB *trb) ARRAY_SIZE(TRBType_names)); } -static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, - unsigned int epid); +static uint64_t xhci_mfindex_get(XHCIState *xhci) +{ + int64_t now = qemu_get_clock_ns(vm_clock); + return (now - xhci->mfindex_start) / 125000; +} + +static void xhci_mfwrap_update(XHCIState *xhci) +{ + const uint32_t bits = USBCMD_RS | USBCMD_EWE; + uint32_t mfindex, left; + int64_t now; + + if ((xhci->usbcmd & bits) == bits) { + now = qemu_get_clock_ns(vm_clock); + mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff; + left = 0x4000 - mfindex; + qemu_mod_timer(xhci->mfwrap_timer, now + left * 125000); + } else { + qemu_del_timer(xhci->mfwrap_timer); + } +} + +static void xhci_mfwrap_timer(void *opaque) +{ + XHCIState *xhci = opaque; + XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS }; + + xhci_event(xhci, &wrap); + xhci_mfwrap_update(xhci); +} static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high) { @@ -793,6 +827,7 @@ static void xhci_run(XHCIState *xhci) { trace_usb_xhci_run(); xhci->usbsts &= ~USBSTS_HCH; + xhci->mfindex_start = qemu_get_clock_ns(vm_clock); } static void xhci_stop(XHCIState *xhci) @@ -2048,7 +2083,6 @@ static void xhci_reset(DeviceState *dev) xhci_update_port(xhci, xhci->ports + i, 0); } - xhci->mfindex = 0; xhci->iman = 0; xhci->imod = 0; xhci->erstsz = 0; @@ -2062,6 +2096,9 @@ static void xhci_reset(DeviceState *dev) xhci->er_full = 0; xhci->ev_buffer_put = 0; xhci->ev_buffer_get = 0; + + xhci->mfindex_start = qemu_get_clock_ns(vm_clock); + xhci_mfwrap_update(xhci); } static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) @@ -2264,6 +2301,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) xhci_stop(xhci); } xhci->usbcmd = val & 0xc0f; + xhci_mfwrap_update(xhci); if (val & USBCMD_HCRST) { xhci_reset(&xhci->pci_dev.qdev); } @@ -2315,8 +2353,7 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) switch (reg) { case 0x00: /* MFINDEX */ - fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n"); - ret = xhci->mfindex; + ret = xhci_mfindex_get(xhci) & 0x3fff; break; case 0x20: /* IMAN */ ret = xhci->iman; @@ -2616,6 +2653,8 @@ static int usb_xhci_initfn(struct PCIDevice *dev) usb_xhci_init(xhci, &dev->qdev); + xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci); + xhci->irq = xhci->pci_dev.irq[0]; memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci, -- cgit v1.2.3 From 3d1396842dc8acd691d3e5d1d3c59ade39776e5a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 24 Aug 2012 14:13:08 +0200 Subject: xhci: iso xfer support Add support for iso transfers. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 16 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 1d9940dcc..2dc8699f7 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -325,9 +325,15 @@ typedef struct XHCITransfer { unsigned int pkts; unsigned int pktsize; unsigned int cur_pkt; + + uint64_t mfindex_kick; } XHCITransfer; typedef struct XHCIEPContext { + XHCIState *xhci; + unsigned int slotid; + unsigned int epid; + XHCIRing ring; unsigned int next_xfer; unsigned int comp_xfer; @@ -337,6 +343,11 @@ typedef struct XHCIEPContext { dma_addr_t pctx; unsigned int max_psize; uint32_t state; + + /* iso xfer scheduling */ + unsigned int interval; + int64_t mfindex_last; + QEMUTimer *kick_timer; } XHCIEPContext; typedef struct XHCISlot { @@ -856,6 +867,12 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx, epctx->state = state; } +static void xhci_ep_kick_timer(void *opaque) +{ + XHCIEPContext *epctx = opaque; + xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid); +} + static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid, dma_addr_t pctx, uint32_t *ctx) @@ -877,6 +894,9 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, epctx = g_malloc(sizeof(XHCIEPContext)); memset(epctx, 0, sizeof(XHCIEPContext)); + epctx->xhci = xhci; + epctx->slotid = slotid; + epctx->epid = epid; slot->eps[epid-1] = epctx; @@ -895,6 +915,10 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, usb_packet_init(&epctx->transfers[i].packet); } + epctx->interval = 1 << (ctx[0] >> 16) & 0xff; + epctx->mfindex_last = 0; + epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx); + epctx->state = EP_RUNNING; ctx[0] &= ~EP_STATE_MASK; ctx[0] |= EP_RUNNING; @@ -934,6 +958,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid, if (t->running_retry) { t->running_retry = 0; epctx->retry = NULL; + qemu_del_timer(epctx->kick_timer); } if (t->trbs) { g_free(t->trbs); @@ -969,6 +994,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid, xhci_set_ep_state(xhci, epctx, EP_DISABLED); + qemu_free_timer(epctx->kick_timer); g_free(epctx); slot->eps[epid-1] = NULL; @@ -1376,29 +1402,70 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer) return 0; } +static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer, + XHCIEPContext *epctx, uint64_t mfindex) +{ + if (xfer->trbs[0].control & TRB_TR_SIA) { + uint64_t asap = ((mfindex + epctx->interval - 1) & + ~(epctx->interval-1)); + if (asap >= epctx->mfindex_last && + asap <= epctx->mfindex_last + epctx->interval * 4) { + xfer->mfindex_kick = epctx->mfindex_last + epctx->interval; + } else { + xfer->mfindex_kick = asap; + } + } else { + xfer->mfindex_kick = (xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT) + & TRB_TR_FRAMEID_MASK; + xfer->mfindex_kick |= mfindex & ~0x3fff; + if (xfer->mfindex_kick < mfindex) { + xfer->mfindex_kick += 0x4000; + } + } +} + +static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer, + XHCIEPContext *epctx, uint64_t mfindex) +{ + if (xfer->mfindex_kick > mfindex) { + qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock) + + (xfer->mfindex_kick - mfindex) * 125000); + xfer->running_retry = 1; + } else { + epctx->mfindex_last = xfer->mfindex_kick; + qemu_del_timer(epctx->kick_timer); + xfer->running_retry = 0; + } +} + + static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx) { + uint64_t mfindex; int ret; DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid); xfer->in_xfer = epctx->type>>2; - if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { - xfer->pkts = 1; - } else { - xfer->pkts = 0; - } - switch(epctx->type) { case ET_INTR_OUT: case ET_INTR_IN: case ET_BULK_OUT: case ET_BULK_IN: + xfer->pkts = 0; + xfer->iso_xfer = false; break; case ET_ISO_OUT: case ET_ISO_IN: - FIXME(); + xfer->pkts = 1; + xfer->iso_xfer = true; + mfindex = xhci_mfindex_get(xhci); + xhci_calc_iso_kick(xhci, xfer, epctx, mfindex); + xhci_check_iso_kick(xhci, xfer, epctx, mfindex); + if (xfer->running_retry) { + return -1; + } break; default: fprintf(stderr, "xhci: unknown or unhandled EP " @@ -1428,6 +1495,7 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid) { XHCIEPContext *epctx; + uint64_t mfindex; int length; int i; @@ -1447,20 +1515,35 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid } if (epctx->retry) { - /* retry nak'ed transfer */ XHCITransfer *xfer = epctx->retry; int result; trace_usb_xhci_xfer_retry(xfer); assert(xfer->running_retry); - if (xhci_setup_packet(xfer) < 0) { - return; - } - result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); - if (result == USB_RET_NAK) { - return; + if (xfer->iso_xfer) { + /* retry delayed iso transfer */ + mfindex = xhci_mfindex_get(xhci); + xhci_check_iso_kick(xhci, xfer, epctx, mfindex); + if (xfer->running_retry) { + return; + } + if (xhci_setup_packet(xfer) < 0) { + return; + } + result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + assert(result != USB_RET_NAK); + xhci_complete_packet(xfer, result); + } else { + /* retry nak'ed transfer */ + if (xhci_setup_packet(xfer) < 0) { + return; + } + result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet); + if (result == USB_RET_NAK) { + return; + } + xhci_complete_packet(xfer, result); } - xhci_complete_packet(xfer, result); assert(!xfer->running_retry); epctx->retry = NULL; } @@ -1512,7 +1595,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) { epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE; } else { - fprintf(stderr, "xhci: error firing data transfer\n"); + if (!xfer->iso_xfer) { + fprintf(stderr, "xhci: error firing data transfer\n"); + } } } -- cgit v1.2.3 From 873123fe094546ba99ff96d089e8eb02f307043d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 27 Aug 2012 16:09:20 +0200 Subject: xhci: trace cc codes in cleartext Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- trace-events | 2 +- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 2dc8699f7..0fd6a029c 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -465,6 +465,45 @@ static const char *TRBType_names[] = { [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE", }; +static const char *TRBCCode_names[] = { + [CC_INVALID] = "CC_INVALID", + [CC_SUCCESS] = "CC_SUCCESS", + [CC_DATA_BUFFER_ERROR] = "CC_DATA_BUFFER_ERROR", + [CC_BABBLE_DETECTED] = "CC_BABBLE_DETECTED", + [CC_USB_TRANSACTION_ERROR] = "CC_USB_TRANSACTION_ERROR", + [CC_TRB_ERROR] = "CC_TRB_ERROR", + [CC_STALL_ERROR] = "CC_STALL_ERROR", + [CC_RESOURCE_ERROR] = "CC_RESOURCE_ERROR", + [CC_BANDWIDTH_ERROR] = "CC_BANDWIDTH_ERROR", + [CC_NO_SLOTS_ERROR] = "CC_NO_SLOTS_ERROR", + [CC_INVALID_STREAM_TYPE_ERROR] = "CC_INVALID_STREAM_TYPE_ERROR", + [CC_SLOT_NOT_ENABLED_ERROR] = "CC_SLOT_NOT_ENABLED_ERROR", + [CC_EP_NOT_ENABLED_ERROR] = "CC_EP_NOT_ENABLED_ERROR", + [CC_SHORT_PACKET] = "CC_SHORT_PACKET", + [CC_RING_UNDERRUN] = "CC_RING_UNDERRUN", + [CC_RING_OVERRUN] = "CC_RING_OVERRUN", + [CC_VF_ER_FULL] = "CC_VF_ER_FULL", + [CC_PARAMETER_ERROR] = "CC_PARAMETER_ERROR", + [CC_BANDWIDTH_OVERRUN] = "CC_BANDWIDTH_OVERRUN", + [CC_CONTEXT_STATE_ERROR] = "CC_CONTEXT_STATE_ERROR", + [CC_NO_PING_RESPONSE_ERROR] = "CC_NO_PING_RESPONSE_ERROR", + [CC_EVENT_RING_FULL_ERROR] = "CC_EVENT_RING_FULL_ERROR", + [CC_INCOMPATIBLE_DEVICE_ERROR] = "CC_INCOMPATIBLE_DEVICE_ERROR", + [CC_MISSED_SERVICE_ERROR] = "CC_MISSED_SERVICE_ERROR", + [CC_COMMAND_RING_STOPPED] = "CC_COMMAND_RING_STOPPED", + [CC_COMMAND_ABORTED] = "CC_COMMAND_ABORTED", + [CC_STOPPED] = "CC_STOPPED", + [CC_STOPPED_LENGTH_INVALID] = "CC_STOPPED_LENGTH_INVALID", + [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR] + = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR", + [CC_ISOCH_BUFFER_OVERRUN] = "CC_ISOCH_BUFFER_OVERRUN", + [CC_EVENT_LOST_ERROR] = "CC_EVENT_LOST_ERROR", + [CC_UNDEFINED_ERROR] = "CC_UNDEFINED_ERROR", + [CC_INVALID_STREAM_ID_ERROR] = "CC_INVALID_STREAM_ID_ERROR", + [CC_SECONDARY_BANDWIDTH_ERROR] = "CC_SECONDARY_BANDWIDTH_ERROR", + [CC_SPLIT_TRANSACTION_ERROR] = "CC_SPLIT_TRANSACTION_ERROR", +}; + static const char *lookup_name(uint32_t index, const char **list, uint32_t llen) { if (index >= llen || list[index] == NULL) { @@ -479,6 +518,12 @@ static const char *trb_name(XHCITRB *trb) ARRAY_SIZE(TRBType_names)); } +static const char *event_name(XHCIEvent *event) +{ + return lookup_name(event->ccode, TRBCCode_names, + ARRAY_SIZE(TRBCCode_names)); +} + static uint64_t xhci_mfindex_get(XHCIState *xhci) { int64_t now = qemu_get_clock_ns(vm_clock); @@ -574,7 +619,8 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) ev_trb.control = cpu_to_le32(ev_trb.control); trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb), - ev_trb.parameter, ev_trb.status, ev_trb.control); + event_name(event), 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 c83d65ede..27d59cdad 100644 --- a/trace-events +++ b/trace-events @@ -313,7 +313,7 @@ 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" +usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %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" -- cgit v1.2.3 From d829fde97d25bfa5ec62d21d28ed32991e56ffc7 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 29 Aug 2012 12:54:59 +0200 Subject: xhci: add trace_usb_xhci_ep_set_dequeue Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 2 +- trace-events | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 0fd6a029c..952112606 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1145,7 +1145,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid, return CC_TRB_ERROR; } - DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue); + trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue); dequeue = xhci_mask64(pdequeue); slot = &xhci->slots[slotid-1]; diff --git a/trace-events b/trace-events index 27d59cdad..a894689ca 100644 --- a/trace-events +++ b/trace-events @@ -323,6 +323,7 @@ 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_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64 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" -- cgit v1.2.3 From 8e9f18b6db1cd67f0a7efd7d0285bee489445197 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 12:42:32 +0200 Subject: xhci: fix runtime write tracepoint Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 952112606..1920edae7 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2518,7 +2518,7 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) { - trace_usb_xhci_runtime_read(reg, val); + trace_usb_xhci_runtime_write(reg, val); switch (reg) { case 0x20: /* IMAN */ -- cgit v1.2.3 From 106b214c4fbba80699b32b63020432cbd1cf95db Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 23 Aug 2012 13:26:25 +0200 Subject: xhci: update register layout Change the register layout to be a bit more sparse and also not depend on the number of ports. Useful when for making the number of ports runtime-configurable. --- hw/usb/hcd-xhci.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 1920edae7..92ee62943 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -36,13 +36,12 @@ #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ __func__, __LINE__); abort(); } while (0) -#define MAXSLOTS 8 -#define MAXINTRS 1 - #define USB2_PORTS 4 #define USB3_PORTS 4 #define MAXPORTS (USB2_PORTS+USB3_PORTS) +#define MAXSLOTS MAXPORTS +#define MAXINTRS 1 /* MAXPORTS */ #define TD_QUEUE 24 @@ -53,16 +52,22 @@ #define ER_FULL_HACK #define LEN_CAP 0x40 -#define OFF_OPER LEN_CAP #define LEN_OPER (0x400 + 0x10 * MAXPORTS) -#define OFF_RUNTIME ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f) -#define LEN_RUNTIME (0x20 + MAXINTRS * 0x20) -#define OFF_DOORBELL (OFF_RUNTIME + LEN_RUNTIME) +#define LEN_RUNTIME ((MAXINTRS + 1) * 0x20) #define LEN_DOORBELL ((MAXSLOTS + 1) * 0x20) +#define OFF_OPER LEN_CAP +#define OFF_RUNTIME 0x1000 +#define OFF_DOORBELL 0x2000 /* must be power of 2 */ -#define LEN_REGS 0x2000 +#define LEN_REGS 0x4000 +#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME +#error Increase OFF_RUNTIME +#endif +#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL +#error Increase OFF_DOORBELL +#endif #if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS # error Increase LEN_REGS #endif -- cgit v1.2.3 From 0846e6359c407e372f446723b8b7b09ac20d0f03 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 Aug 2012 13:38:01 +0200 Subject: xhci: update port handling This patch changes the way xhci ports are linked to USBPorts. The fixed 1:1 relationship between xhci ports and USBPorts is gone. Now each USBPort represents a physical plug which has usually two xhci ports assigned: one usb2 and ond usb3 port. usb devices show up at one or the other, depending on whenever they support superspeed or not. This patch also makes the number of usb2 and usb3 ports runtime configurable by adding 'p2' and 'p3' properties. It is allowed to have different numbers of usb2 and usb3 ports. Specifying p2=4,p3=2 will give you an xhci adapter which supports all speeds on physical ports 1+2 and usb2 only on ports 3+4. --- hw/usb/hcd-xhci.c | 137 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 97 insertions(+), 40 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 92ee62943..f0c185986 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -36,10 +36,10 @@ #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ __func__, __LINE__); abort(); } while (0) -#define USB2_PORTS 4 -#define USB3_PORTS 4 +#define MAXPORTS_2 8 +#define MAXPORTS_3 8 -#define MAXPORTS (USB2_PORTS+USB3_PORTS) +#define MAXPORTS (MAXPORTS_2+MAXPORTS_3) #define MAXSLOTS MAXPORTS #define MAXINTRS 1 /* MAXPORTS */ @@ -300,8 +300,10 @@ typedef struct XHCIRing { } XHCIRing; typedef struct XHCIPort { - USBPort port; uint32_t portsc; + uint32_t portnr; + USBPort *uport; + uint32_t speedmask; } XHCIPort; struct XHCIState; @@ -379,9 +381,13 @@ struct XHCIState { qemu_irq irq; MemoryRegion mem; const char *name; - uint32_t msi; unsigned int devaddr; + /* properties */ + uint32_t numports_2; + uint32_t numports_3; + uint32_t msi; + /* Operational Registers */ uint32_t usbcmd; uint32_t usbsts; @@ -392,8 +398,10 @@ struct XHCIState { uint32_t dcbaap_high; uint32_t config; + USBPort uports[MAX(MAXPORTS_2, MAXPORTS_3)]; XHCIPort ports[MAXPORTS]; XHCISlot slots[MAXSLOTS]; + uint32_t numports; /* Runtime Registers */ uint32_t iman; @@ -578,6 +586,28 @@ static inline dma_addr_t xhci_mask64(uint64_t addr) } } +static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) +{ + int index; + + if (!uport->dev) { + return NULL; + } + switch (uport->dev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + index = uport->index; + break; + case USB_SPEED_SUPER: + index = uport->index + xhci->numports_2; + break; + default: + return NULL; + } + return &xhci->ports[index]; +} + static void xhci_irq_update(XHCIState *xhci) { int level = 0; @@ -1126,7 +1156,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, ep |= 0x80; } - dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev; + dev = xhci->ports[xhci->slots[slotid-1].port-1].uport->dev; if (!dev) { return CC_USB_TRANSACTION_ERROR; } @@ -1313,7 +1343,7 @@ static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) if (!(port->portsc & PORTSC_PED)) { return NULL; } - return usb_find_device(&port->port, addr); + return usb_find_device(port->uport, addr); } static int xhci_setup_packet(XHCITransfer *xfer) @@ -1734,9 +1764,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); port = (slot_ctx[1]>>16) & 0xFF; - dev = xhci->ports[port-1].port.dev; + dev = xhci->ports[port-1].uport->dev; - if (port < 1 || port > MAXPORTS) { + if (port < 1 || port > xhci->numports) { fprintf(stderr, "xhci: bad port %d\n", port); return CC_TRB_ERROR; } else if (!dev) { @@ -1985,7 +2015,7 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) { dma_addr_t ctx; - uint8_t bw_ctx[MAXPORTS+1]; + uint8_t bw_ctx[xhci->numports+1]; DPRINTF("xhci_get_port_bandwidth()\n"); @@ -1995,7 +2025,7 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx) /* TODO: actually implement real values here */ bw_ctx[0] = 0; - memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */ + memset(&bw_ctx[1], 80, xhci->numports); /* 80% */ pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx)); return CC_SUCCESS; @@ -2165,12 +2195,11 @@ static void xhci_process_commands(XHCIState *xhci) static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) { - int nr = port->port.index + 1; - port->portsc = PORTSC_PP; - if (port->port.dev && port->port.dev->attached && !is_detach) { + if (port->uport->dev && port->uport->dev->attached && !is_detach && + (1 << port->uport->dev->speed) & port->speedmask) { port->portsc |= PORTSC_CCS; - switch (port->port.dev->speed) { + switch (port->uport->dev->speed) { case USB_SPEED_LOW: port->portsc |= PORTSC_SPEED_LOW; break; @@ -2180,14 +2209,18 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) case USB_SPEED_HIGH: port->portsc |= PORTSC_SPEED_HIGH; break; + case USB_SPEED_SUPER: + port->portsc |= PORTSC_SPEED_SUPER; + break; } } if (xhci_running(xhci)) { port->portsc |= PORTSC_CSC; - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24}; + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, + port->portnr << 24}; xhci_event(xhci, &ev); - DPRINTF("xhci: port change event for port %d\n", nr); + DPRINTF("xhci: port change event for port %d\n", port->portnr); } } @@ -2215,7 +2248,7 @@ static void xhci_reset(DeviceState *dev) xhci_disable_slot(xhci, i+1); } - for (i = 0; i < MAXPORTS; i++) { + for (i = 0; i < xhci->numports; i++) { xhci_update_port(xhci, xhci->ports + i, 0); } @@ -2246,7 +2279,8 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) ret = 0x01000000 | LEN_CAP; break; case 0x04: /* HCSPARAMS 1 */ - ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS; + ret = ((xhci->numports_2+xhci->numports_3)<<24) + | (MAXINTRS<<8) | MAXSLOTS; break; case 0x08: /* HCSPARAMS 2 */ ret = 0x0000000f; @@ -2276,7 +2310,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) ret = 0x20425455; /* "USB " */ break; case 0x28: /* Supported Protocol:08 */ - ret = 0x00000001 | (USB2_PORTS<<8); + ret = 0x00000001 | (xhci->numports_2<<8); break; case 0x2c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -2288,7 +2322,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) ret = 0x20425455; /* "USB " */ break; case 0x38: /* Supported Protocol:08 */ - ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8); + ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8); break; case 0x3c: /* Supported Protocol:0c */ ret = 0x00000000; /* reserved */ @@ -2307,7 +2341,7 @@ static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg) uint32_t port = reg >> 4; uint32_t ret; - if (port >= MAXPORTS) { + if (port >= xhci->numports) { fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); ret = 0; goto out; @@ -2340,7 +2374,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) trace_usb_xhci_port_write(port, reg & 0x0f, val); - if (port >= MAXPORTS) { + if (port >= xhci->numports) { fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); return; } @@ -2362,7 +2396,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) /* write-1-to-start bits */ if (val & PORTSC_PR) { DPRINTF("xhci: port %d reset\n", port); - usb_device_reset(xhci->ports[port].port.dev); + usb_device_reset(xhci->ports[port].uport->dev); portsc |= PORTSC_PRC | PORTSC_PED; } xhci->ports[port].portsc = portsc; @@ -2657,7 +2691,7 @@ static const MemoryRegionOps xhci_mem_ops = { static void xhci_attach(USBPort *usbport) { XHCIState *xhci = usbport->opaque; - XHCIPort *port = &xhci->ports[usbport->index]; + XHCIPort *port = xhci_lookup_port(xhci, usbport); xhci_update_port(xhci, port, 0); } @@ -2665,7 +2699,7 @@ static void xhci_attach(USBPort *usbport) static void xhci_detach(USBPort *usbport) { XHCIState *xhci = usbport->opaque; - XHCIPort *port = &xhci->ports[usbport->index]; + XHCIPort *port = xhci_lookup_port(xhci, usbport); xhci_update_port(xhci, port, 1); } @@ -2673,9 +2707,9 @@ static void xhci_detach(USBPort *usbport) static void xhci_wakeup(USBPort *usbport) { XHCIState *xhci = usbport->opaque; - XHCIPort *port = &xhci->ports[usbport->index]; - int nr = port->port.index + 1; - XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24}; + XHCIPort *port = xhci_lookup_port(xhci, usbport); + XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, + port->portnr << 24}; uint32_t pls; pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK; @@ -2757,22 +2791,43 @@ static USBBusOps xhci_bus_ops = { static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) { - int i; + XHCIPort *port; + int i, usbports, speedmask; xhci->usbsts = USBSTS_HCH; + if (xhci->numports_2 > MAXPORTS_2) { + xhci->numports_2 = MAXPORTS_2; + } + if (xhci->numports_3 > MAXPORTS_3) { + xhci->numports_3 = MAXPORTS_3; + } + usbports = MAX(xhci->numports_2, xhci->numports_3); + xhci->numports = xhci->numports_2 + xhci->numports_3; + usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev); - for (i = 0; i < MAXPORTS; i++) { - memset(&xhci->ports[i], 0, sizeof(xhci->ports[i])); - usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i, - &xhci_port_ops, - USB_SPEED_MASK_LOW | - USB_SPEED_MASK_FULL | - USB_SPEED_MASK_HIGH); - } - for (i = 0; i < MAXSLOTS; i++) { - xhci->slots[i].enabled = 0; + for (i = 0; i < usbports; i++) { + speedmask = 0; + if (i < xhci->numports_2) { + port = &xhci->ports[i]; + port->portnr = i + 1; + port->uport = &xhci->uports[i]; + port->speedmask = + USB_SPEED_MASK_LOW | + USB_SPEED_MASK_FULL | + USB_SPEED_MASK_HIGH; + speedmask |= port->speedmask; + } + if (i < xhci->numports_3) { + port = &xhci->ports[i + xhci->numports_2]; + port->portnr = i + 1 + xhci->numports_2; + port->uport = &xhci->uports[i]; + port->speedmask = USB_SPEED_MASK_SUPER; + speedmask |= port->speedmask; + } + usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i, + &xhci_port_ops, speedmask); } } @@ -2828,6 +2883,8 @@ static const VMStateDescription vmstate_xhci = { static Property xhci_properties[] = { DEFINE_PROP_UINT32("msi", XHCIState, msi, 0), + DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), + DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), DEFINE_PROP_END_OF_LIST(), }; -- cgit v1.2.3 From 6d51b2bb07030c38e5f2d9048c6c474ca486fe9b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 Aug 2012 17:28:50 +0200 Subject: usb3: superspeed descriptors Add superspeed descriptor entry to USBDesc, advertise superspeed support when present. Signed-off-by: Gerd Hoffmann --- hw/usb/desc.c | 10 +++++++++- hw/usb/desc.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 0a9d3c9f6..3e8c6cb50 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -359,6 +359,9 @@ static void usb_desc_setdefaults(USBDevice *dev) case USB_SPEED_HIGH: dev->device = desc->high; break; + case USB_SPEED_SUPER: + dev->device = desc->super; + break; } usb_desc_set_config(dev, 0); } @@ -376,6 +379,9 @@ void usb_desc_init(USBDevice *dev) if (desc->high) { dev->speedmask |= USB_SPEED_MASK_HIGH; } + if (desc->super) { + dev->speedmask |= USB_SPEED_MASK_SUPER; + } usb_desc_setdefaults(dev); } @@ -384,7 +390,9 @@ void usb_desc_attach(USBDevice *dev) const USBDesc *desc = usb_device_get_usb_desc(dev); assert(desc != NULL); - if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { + if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) { + dev->speed = USB_SPEED_SUPER; + } else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { dev->speed = USB_SPEED_HIGH; } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) { dev->speed = USB_SPEED_FULL; diff --git a/hw/usb/desc.h b/hw/usb/desc.h index 7cf544294..d89fa4166 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -152,6 +152,7 @@ struct USBDesc { USBDescID id; const USBDescDevice *full; const USBDescDevice *high; + const USBDescDevice *super; const char* const *str; }; -- cgit v1.2.3 From b43a28517620c4eba8ab8b96b08e5ec85aedeeaf Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 Aug 2012 17:28:03 +0200 Subject: usb3: superspeed endpoint companion Add support for building superspeed endpoint companion descriptors, create them for superspeed usb devices. Signed-off-by: Gerd Hoffmann --- hw/usb.h | 1 + hw/usb/desc.c | 55 ++++++++++++++++++++++++++++++++++++++++--------------- hw/usb/desc.h | 26 +++++++++++++++++++++----- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/hw/usb.h b/hw/usb.h index 684e3f4b5..78ffdf4ef 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -137,6 +137,7 @@ #define USB_DT_INTERFACE_ASSOC 0x0B #define USB_DT_CS_INTERFACE 0x24 #define USB_DT_CS_ENDPOINT 0x25 +#define USB_DT_ENDPOINT_COMPANION 0x30 #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 3e8c6cb50..8f5a8e519 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -76,7 +76,8 @@ int usb_desc_device_qualifier(const USBDescDevice *dev, return bLength; } -int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) +int usb_desc_config(const USBDescConfig *conf, int flags, + uint8_t *dest, size_t len) { uint8_t bLength = 0x09; uint16_t wTotalLength = 0; @@ -99,7 +100,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) /* handle grouped interfaces if any */ for (i = 0; i < conf->nif_groups; i++) { - rc = usb_desc_iface_group(&(conf->if_groups[i]), + rc = usb_desc_iface_group(&(conf->if_groups[i]), flags, dest + wTotalLength, len - wTotalLength); if (rc < 0) { @@ -110,7 +111,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) /* handle normal (ungrouped / no IAD) interfaces if any */ for (i = 0; i < conf->nif; i++) { - rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength); + rc = usb_desc_iface(conf->ifs + i, flags, + dest + wTotalLength, len - wTotalLength); if (rc < 0) { return rc; } @@ -122,8 +124,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) return wTotalLength; } -int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, - size_t len) +int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, + uint8_t *dest, size_t len) { int pos = 0; int i = 0; @@ -147,7 +149,7 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, /* handle associated interfaces in this group */ for (i = 0; i < iad->nif; i++) { - int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos); + int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos); if (rc < 0) { return rc; } @@ -157,7 +159,8 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, return pos; } -int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) +int usb_desc_iface(const USBDescIface *iface, int flags, + uint8_t *dest, size_t len) { uint8_t bLength = 0x09; int i, rc, pos = 0; @@ -188,7 +191,7 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) } for (i = 0; i < iface->bNumEndpoints; i++) { - rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos); + rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos); if (rc < 0) { return rc; } @@ -198,13 +201,15 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) return pos; } -int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len) +int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, + uint8_t *dest, size_t len) { uint8_t bLength = ep->is_audio ? 0x09 : 0x07; uint8_t extralen = ep->extra ? ep->extra[0] : 0; + uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0; USBDescriptor *d = (void *)dest; - if (len < bLength + extralen) { + if (len < bLength + extralen + superlen) { return -1; } @@ -224,7 +229,21 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len) memcpy(dest + bLength, ep->extra, extralen); } - return bLength + extralen; + if (superlen) { + USBDescriptor *d = (void *)(dest + bLength + extralen); + + d->bLength = 0x06; + d->bDescriptorType = USB_DT_ENDPOINT_COMPANION; + + d->u.super_endpoint.bMaxBurst = ep->bMaxBurst; + d->u.super_endpoint.bmAttributes = ep->bmAttributes_super; + d->u.super_endpoint.wBytesPerInterval_lo = + usb_lo(ep->wBytesPerInterval); + d->u.super_endpoint.wBytesPerInterval_hi = + usb_hi(ep->wBytesPerInterval); + } + + return bLength + extralen + superlen; } int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) @@ -509,7 +528,7 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len uint8_t buf[256]; uint8_t type = value >> 8; uint8_t index = value & 0xff; - int ret = -1; + int flags, ret = -1; if (dev->speed == USB_SPEED_HIGH) { other_dev = usb_device_get_usb_desc(dev)->full; @@ -517,6 +536,11 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len other_dev = usb_device_get_usb_desc(dev)->high; } + flags = 0; + if (dev->device->bcdUSB >= 0x0300) { + flags |= USB_DESC_FLAG_SUPER; + } + switch(type) { case USB_DT_DEVICE: ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf)); @@ -524,7 +548,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len break; case USB_DT_CONFIG: if (index < dev->device->bNumConfigurations) { - ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf)); + ret = usb_desc_config(dev->device->confs + index, flags, + buf, sizeof(buf)); } trace_usb_desc_config(dev->addr, index, len, ret); break; @@ -532,7 +557,6 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len ret = usb_desc_string(dev, index, buf, sizeof(buf)); trace_usb_desc_string(dev->addr, index, len, ret); break; - case USB_DT_DEVICE_QUALIFIER: if (other_dev != NULL) { ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf)); @@ -541,7 +565,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len break; case USB_DT_OTHER_SPEED_CONFIG: if (other_dev != NULL && index < other_dev->bNumConfigurations) { - ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf)); + ret = usb_desc_config(other_dev->confs + index, flags, + buf, sizeof(buf)); buf[0x01] = USB_DT_OTHER_SPEED_CONFIG; } trace_usb_desc_other_speed_config(dev->addr, index, len, ret); diff --git a/hw/usb/desc.h b/hw/usb/desc.h index d89fa4166..4b5e88d81 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -63,6 +63,12 @@ typedef struct USBDescriptor { uint8_t bRefresh; /* only audio ep */ uint8_t bSynchAddress; /* only audio ep */ } endpoint; + struct { + uint8_t bMaxBurst; + uint8_t bmAttributes; + uint8_t wBytesPerInterval_lo; + uint8_t wBytesPerInterval_hi; + } super_endpoint; } u; } QEMU_PACKED USBDescriptor; @@ -139,6 +145,11 @@ struct USBDescEndpoint { uint8_t is_audio; /* has bRefresh + bSynchAddress */ uint8_t *extra; + + /* superspeed endpoint companion */ + uint8_t bMaxBurst; + uint8_t bmAttributes_super; + uint16_t wBytesPerInterval; }; struct USBDescOther { @@ -156,16 +167,21 @@ struct USBDesc { const char* const *str; }; +#define USB_DESC_FLAG_SUPER (1 << 1) + /* generate usb packages from structs */ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, uint8_t *dest, size_t len); int usb_desc_device_qualifier(const USBDescDevice *dev, uint8_t *dest, size_t len); -int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len); -int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest, - size_t len); -int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len); -int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len); +int usb_desc_config(const USBDescConfig *conf, int flags, + uint8_t *dest, size_t len); +int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags, + uint8_t *dest, size_t len); +int usb_desc_iface(const USBDescIface *iface, int flags, + uint8_t *dest, size_t len); +int usb_desc_endpoint(const USBDescEndpoint *ep, int flags, + uint8_t *dest, size_t len); int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len); /* control message emulation helpers */ -- cgit v1.2.3 From 2077469b58066da3cdac9e5b81d3c60178e6d300 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 Aug 2012 17:46:29 +0200 Subject: usb3: bos decriptor Add support for creating BOS descriptor and device cappability descriptors. Signed-off-by: Gerd Hoffmann --- hw/usb.h | 6 ++++ hw/usb/desc.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb/desc.h | 25 ++++++++++++++ trace-events | 1 + 4 files changed, 141 insertions(+) diff --git a/hw/usb.h b/hw/usb.h index 78ffdf4ef..48c8926a7 100644 --- a/hw/usb.h +++ b/hw/usb.h @@ -135,10 +135,16 @@ #define USB_DT_OTHER_SPEED_CONFIG 0x07 #define USB_DT_DEBUG 0x0A #define USB_DT_INTERFACE_ASSOC 0x0B +#define USB_DT_BOS 0x0F +#define USB_DT_DEVICE_CAPABILITY 0x10 #define USB_DT_CS_INTERFACE 0x24 #define USB_DT_CS_ENDPOINT 0x25 #define USB_DT_ENDPOINT_COMPANION 0x30 +#define USB_DEV_CAP_WIRELESS 0x01 +#define USB_DEV_CAP_USB2_EXT 0x02 +#define USB_DEV_CAP_SUPERSPEED 0x03 + #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 #define USB_ENDPOINT_XFER_BULK 2 diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 8f5a8e519..1f12eaecb 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -258,6 +258,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) return bLength; } +static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x07; + USBDescriptor *d = (void *)dest; + + if (len < bLength) { + return -1; + } + + d->bLength = bLength; + d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT; + + d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */ + d->u.cap.u.usb2_ext.bmAttributes_2 = 0; + d->u.cap.u.usb2_ext.bmAttributes_3 = 0; + d->u.cap.u.usb2_ext.bmAttributes_4 = 0; + + return bLength; +} + +static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x0a; + USBDescriptor *d = (void *)dest; + + if (len < bLength) { + return -1; + } + + d->bLength = bLength; + d->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED; + + d->u.cap.u.super.bmAttributes = 0; + d->u.cap.u.super.wSpeedsSupported_lo = 0; + d->u.cap.u.super.wSpeedsSupported_hi = 0; + d->u.cap.u.super.bFunctionalitySupport = 0; + d->u.cap.u.super.bU1DevExitLat = 0x0a; + d->u.cap.u.super.wU2DevExitLat_lo = 0x20; + d->u.cap.u.super.wU2DevExitLat_hi = 0; + + if (desc->full) { + d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1); + d->u.cap.u.super.bFunctionalitySupport = 1; + } + if (desc->high) { + d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2); + if (!d->u.cap.u.super.bFunctionalitySupport) { + d->u.cap.u.super.bFunctionalitySupport = 2; + } + } + if (desc->super) { + d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3); + if (!d->u.cap.u.super.bFunctionalitySupport) { + d->u.cap.u.super.bFunctionalitySupport = 3; + } + } + + return bLength; +} + +static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x05; + uint16_t wTotalLength = 0; + uint8_t bNumDeviceCaps = 0; + USBDescriptor *d = (void *)dest; + int rc; + + if (len < bLength) { + return -1; + } + + d->bLength = bLength; + d->bDescriptorType = USB_DT_BOS; + + wTotalLength += bLength; + + if (desc->high != NULL) { + rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + bNumDeviceCaps++; + } + + if (desc->super != NULL) { + rc = usb_desc_cap_super(desc, dest + wTotalLength, + len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + bNumDeviceCaps++; + } + + d->u.bos.wTotalLength_lo = usb_lo(wTotalLength); + d->u.bos.wTotalLength_hi = usb_hi(wTotalLength); + d->u.bos.bNumDeviceCaps = bNumDeviceCaps; + return wTotalLength; +} + /* ------------------------------------------------------------------ */ static void usb_desc_ep_init(USBDevice *dev) @@ -571,6 +676,10 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len } trace_usb_desc_other_speed_config(dev->addr, index, len, ret); break; + case USB_DT_BOS: + ret = usb_desc_bos(desc, buf, sizeof(buf)); + trace_usb_desc_bos(dev->addr, len, ret); + break; case USB_DT_DEBUG: /* ignore silently */ diff --git a/hw/usb/desc.h b/hw/usb/desc.h index 4b5e88d81..68bb57067 100644 --- a/hw/usb/desc.h +++ b/hw/usb/desc.h @@ -69,6 +69,31 @@ typedef struct USBDescriptor { uint8_t wBytesPerInterval_lo; uint8_t wBytesPerInterval_hi; } super_endpoint; + struct { + uint8_t wTotalLength_lo; + uint8_t wTotalLength_hi; + uint8_t bNumDeviceCaps; + } bos; + struct { + uint8_t bDevCapabilityType; + union { + struct { + uint8_t bmAttributes_1; + uint8_t bmAttributes_2; + uint8_t bmAttributes_3; + uint8_t bmAttributes_4; + } usb2_ext; + struct { + uint8_t bmAttributes; + uint8_t wSpeedsSupported_lo; + uint8_t wSpeedsSupported_hi; + uint8_t bFunctionalitySupport; + uint8_t bU1DevExitLat; + uint8_t wU2DevExitLat_lo; + uint8_t wU2DevExitLat_hi; + } super; + } u; + } cap; } u; } QEMU_PACKED USBDescriptor; diff --git a/trace-events b/trace-events index a894689ca..5bc591a72 100644 --- a/trace-events +++ b/trace-events @@ -340,6 +340,7 @@ usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device quali usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" +usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d" usb_set_addr(int addr) "dev %d" usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d" -- cgit v1.2.3 From 79b40459ba361ac0946bb54fee4a2389d1307c68 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 28 Aug 2012 17:29:15 +0200 Subject: usb-storage: usb3 support Add usb3 descriptors to usb-storage, so it shows up as superspeed device when connected to xhci. Signed-off-by: Gerd Hoffmann --- hw/usb/dev-storage.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index ff48d9104..e732191a2 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -78,6 +78,7 @@ enum { STR_SERIALNUMBER, STR_CONFIG_FULL, STR_CONFIG_HIGH, + STR_CONFIG_SUPER, }; static const USBDescStrings desc_strings = { @@ -86,6 +87,7 @@ static const USBDescStrings desc_strings = { [STR_SERIALNUMBER] = "1", [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", + [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)", }; static const USBDescIface desc_iface_full = { @@ -158,6 +160,43 @@ static const USBDescDevice desc_device_high = { }, }; +static const USBDescIface desc_iface_super = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, /* SCSI */ + .bInterfaceProtocol = 0x50, /* Bulk */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 1024, + .bMaxBurst = 15, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 1024, + .bMaxBurst = 15, + }, + } +}; + +static const USBDescDevice desc_device_super = { + .bcdUSB = 0x0300, + .bMaxPacketSize0 = 9, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_SUPER, + .bmAttributes = 0xc0, + .nif = 1, + .ifs = &desc_iface_super, + }, + }, +}; + static const USBDesc desc = { .id = { .idVendor = 0x46f4, /* CRC16() of "QEMU" */ @@ -167,9 +206,10 @@ static const USBDesc desc = { .iProduct = STR_PRODUCT, .iSerialNumber = STR_SERIALNUMBER, }, - .full = &desc_device_full, - .high = &desc_device_high, - .str = desc_strings, + .full = &desc_device_full, + .high = &desc_device_high, + .super = &desc_device_super, + .str = desc_strings, }; static void usb_msd_copy_data(MSDState *s, USBPacket *p) -- cgit v1.2.3 From c5e9b02dee4a19f7b047fb75399012e1db759190 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 10:57:12 +0200 Subject: xhci: fix & cleanup msi. Drop custom write_config function which isn't needed any more. Make the msi property a bit property so it accepts 'on' & 'off'. Enable MSI by default. TODO: add compat property to disable on old machine types. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index f0c185986..e3de242a4 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -386,7 +386,7 @@ struct XHCIState { /* properties */ uint32_t numports_2; uint32_t numports_3; - uint32_t msi; + uint32_t flags; /* Operational Registers */ uint32_t usbcmd; @@ -435,6 +435,10 @@ typedef struct XHCIEvRingSeg { uint32_t rsvd; } XHCIEvRingSeg; +enum xhci_flags { + XHCI_FLAG_USE_MSI = 1, +}; + static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); static void xhci_event(XHCIState *xhci, XHCIEvent *event); @@ -617,7 +621,7 @@ static void xhci_irq_update(XHCIState *xhci) level = 1; } - if (xhci->msi && msi_enabled(&xhci->pci_dev)) { + if (msi_enabled(&xhci->pci_dev)) { if (level) { trace_usb_xhci_irq_msi(0); msi_notify(&xhci->pci_dev, 0); @@ -2857,32 +2861,20 @@ static int usb_xhci_initfn(struct PCIDevice *dev) ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0); assert(ret >= 0); - if (xhci->msi) { - ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false); - assert(ret >= 0); + if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) { + msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false); } return 0; } -static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, - int len) -{ - XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev); - - pci_default_write_config(dev, addr, val, len); - if (xhci->msi) { - msi_write_config(dev, addr, val, len); - } -} - static const VMStateDescription vmstate_xhci = { .name = "xhci", .unmigratable = 1, }; static Property xhci_properties[] = { - DEFINE_PROP_UINT32("msi", XHCIState, msi, 0), + DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), DEFINE_PROP_END_OF_LIST(), @@ -2902,7 +2894,6 @@ static void xhci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; k->revision = 0x03; k->is_express = 1; - k->config_write = xhci_write_config; } static TypeInfo xhci_info = { -- cgit v1.2.3 From 4c4abe7cc903e057d343cd445eca2e5227783579 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 13:05:10 +0200 Subject: xhci: rework interrupt handling Split xhci_irq_update into a function which handles intx updates (including lowering the irq line once the guests acks the interrupt) and one which is used for raising an irq only. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index e3de242a4..06c1f5178 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -612,24 +612,43 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport) return &xhci->ports[index]; } -static void xhci_irq_update(XHCIState *xhci) +static void xhci_intx_update(XHCIState *xhci) { int level = 0; - if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE && + if (msi_enabled(&xhci->pci_dev)) { + return; + } + + if (xhci->iman & IMAN_IP && + xhci->iman & IMAN_IE && xhci->usbcmd & USBCMD_INTE) { level = 1; } + trace_usb_xhci_irq_intx(level); + qemu_set_irq(xhci->irq, level); +} + +static void xhci_intr_raise(XHCIState *xhci) +{ + if (!(xhci->iman & IMAN_IP) || + !(xhci->iman & IMAN_IE)) { + return; + } + + if (!(xhci->usbcmd & USBCMD_INTE)) { + return; + } + if (msi_enabled(&xhci->pci_dev)) { - if (level) { - 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); + trace_usb_xhci_irq_msi(0); + msi_notify(&xhci->pci_dev, 0); + return; } + + trace_usb_xhci_irq_intx(1); + qemu_set_irq(xhci->irq, 1); } static inline int xhci_running(XHCIState *xhci) @@ -732,7 +751,7 @@ static void xhci_events_update(XHCIState *xhci) xhci->erdp_low |= ERDP_EHB; xhci->iman |= IMAN_IP; xhci->usbsts |= USBSTS_EINT; - xhci_irq_update(xhci); + xhci_intr_raise(xhci); } if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) { @@ -796,7 +815,7 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event) xhci->iman |= IMAN_IP; xhci->usbsts |= USBSTS_EINT; - xhci_irq_update(xhci); + xhci_intr_raise(xhci); } static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring, @@ -2479,13 +2498,13 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) if (val & USBCMD_HCRST) { xhci_reset(&xhci->pci_dev.qdev); } - xhci_irq_update(xhci); + xhci_intx_update(xhci); break; case 0x04: /* USBSTS */ /* these bits are write-1-to-clear */ xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE)); - xhci_irq_update(xhci); + xhci_intx_update(xhci); break; case 0x14: /* DNCTRL */ @@ -2570,7 +2589,7 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) } xhci->iman &= ~IMAN_IE; xhci->iman |= val & IMAN_IE; - xhci_irq_update(xhci); + xhci_intx_update(xhci); break; case 0x24: /* IMOD */ xhci->imod = val; -- cgit v1.2.3 From 4c47f800631a14c8cb7970ba3a47d4a4ab0ee088 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 12:06:59 +0200 Subject: xhci: add msix support Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++- trace-events | 3 +++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 06c1f5178..19bbb164e 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 "hw/msix.h" #include "trace.h" //#define DEBUG_XHCI @@ -59,6 +60,8 @@ #define OFF_OPER LEN_CAP #define OFF_RUNTIME 0x1000 #define OFF_DOORBELL 0x2000 +#define OFF_MSIX_TABLE 0x3000 +#define OFF_MSIX_PBA 0x3800 /* must be power of 2 */ #define LEN_REGS 0x4000 @@ -411,6 +414,7 @@ struct XHCIState { uint32_t erstba_high; uint32_t erdp_low; uint32_t erdp_high; + bool msix_used; int64_t mfindex_start; QEMUTimer *mfwrap_timer; @@ -437,6 +441,7 @@ typedef struct XHCIEvRingSeg { enum xhci_flags { XHCI_FLAG_USE_MSI = 1, + XHCI_FLAG_USE_MSI_X, }; static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, @@ -616,7 +621,8 @@ static void xhci_intx_update(XHCIState *xhci) { int level = 0; - if (msi_enabled(&xhci->pci_dev)) { + if (msix_enabled(&xhci->pci_dev) || + msi_enabled(&xhci->pci_dev)) { return; } @@ -630,6 +636,30 @@ static void xhci_intx_update(XHCIState *xhci) qemu_set_irq(xhci->irq, level); } +static void xhci_msix_update(XHCIState *xhci) +{ + bool enabled; + + if (!msix_enabled(&xhci->pci_dev)) { + return; + } + + enabled = xhci->iman & IMAN_IE; + if (enabled == xhci->msix_used) { + return; + } + + if (enabled) { + trace_usb_xhci_irq_msix_use(0); + msix_vector_use(&xhci->pci_dev, 0); + xhci->msix_used = true; + } else { + trace_usb_xhci_irq_msix_unuse(0); + msix_vector_unuse(&xhci->pci_dev, 0); + xhci->msix_used = false; + } +} + static void xhci_intr_raise(XHCIState *xhci) { if (!(xhci->iman & IMAN_IP) || @@ -641,6 +671,12 @@ static void xhci_intr_raise(XHCIState *xhci) return; } + if (msix_enabled(&xhci->pci_dev)) { + trace_usb_xhci_irq_msix(0); + msix_notify(&xhci->pci_dev, 0); + return; + } + if (msi_enabled(&xhci->pci_dev)) { trace_usb_xhci_irq_msi(0); msi_notify(&xhci->pci_dev, 0); @@ -2282,6 +2318,7 @@ static void xhci_reset(DeviceState *dev) xhci->erstba_high = 0; xhci->erdp_low = 0; xhci->erdp_high = 0; + xhci->msix_used = 0; xhci->er_ep_idx = 0; xhci->er_pcs = 1; @@ -2590,6 +2627,7 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) xhci->iman &= ~IMAN_IE; xhci->iman |= val & IMAN_IE; xhci_intx_update(xhci); + xhci_msix_update(xhci); break; case 0x24: /* IMOD */ xhci->imod = val; @@ -2883,6 +2921,12 @@ static int usb_xhci_initfn(struct PCIDevice *dev) if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) { msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false); } + if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) { + msix_init(&xhci->pci_dev, MAXINTRS, + &xhci->mem, 0, OFF_MSIX_TABLE, + &xhci->mem, 0, OFF_MSIX_PBA, + 0x90); + } return 0; } @@ -2894,6 +2938,7 @@ static const VMStateDescription vmstate_xhci = { static Property xhci_properties[] = { DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true), + DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true), DEFINE_PROP_UINT32("p2", XHCIState, numports_2, 4), DEFINE_PROP_UINT32("p3", XHCIState, numports_3, 4), DEFINE_PROP_END_OF_LIST(), diff --git a/trace-events b/trace-events index 5bc591a72..8589ca4ea 100644 --- a/trace-events +++ b/trace-events @@ -313,6 +313,9 @@ 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_irq_msix(uint32_t nr) "nr %d" +usb_xhci_irq_msix_use(uint32_t nr) "nr %d" +usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d" usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %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" -- cgit v1.2.3 From 2cae41195b5b95129d92a189a0bfce3e5d0d8707 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 14:04:04 +0200 Subject: xhci: move register update into xhci_intr_raise Now that we have a separate function to raise an IRQ we can move some comon code into the function. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 19bbb164e..8dc9986e5 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -662,8 +662,11 @@ static void xhci_msix_update(XHCIState *xhci) static void xhci_intr_raise(XHCIState *xhci) { - if (!(xhci->iman & IMAN_IP) || - !(xhci->iman & IMAN_IE)) { + xhci->erdp_low |= ERDP_EHB; + xhci->iman |= IMAN_IP; + xhci->usbsts |= USBSTS_EINT; + + if (!(xhci->iman & IMAN_IE)) { return; } @@ -784,9 +787,6 @@ static void xhci_events_update(XHCIState *xhci) } if (do_irq) { - xhci->erdp_low |= ERDP_EHB; - xhci->iman |= IMAN_IP; - xhci->usbsts |= USBSTS_EINT; xhci_intr_raise(xhci); } @@ -847,10 +847,6 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event) xhci_write_event(xhci, event); } - xhci->erdp_low |= ERDP_EHB; - xhci->iman |= IMAN_IP; - xhci->usbsts |= USBSTS_EINT; - xhci_intr_raise(xhci); } -- cgit v1.2.3 From 962d11e17264af0239f259aad1386fcc7ff471aa Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 15:49:03 +0200 Subject: xhci: add XHCIInterrupter Move all state belonging to the (single) interrupter into a separate struct. First step in adding support for multiple interrupters. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 307 ++++++++++++++++++++++++++++-------------------------- trace-events | 2 +- 2 files changed, 161 insertions(+), 148 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 8dc9986e5..0a03053c0 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -378,6 +378,27 @@ typedef struct XHCIEvent { uint8_t epid; } XHCIEvent; +typedef struct XHCIInterrupter { + uint32_t iman; + uint32_t imod; + uint32_t erstsz; + uint32_t erstba_low; + uint32_t erstba_high; + uint32_t erdp_low; + uint32_t erdp_high; + + bool msix_used, er_pcs, er_full; + + dma_addr_t er_start; + uint32_t er_size; + unsigned int er_ep_idx; + + XHCIEvent ev_buffer[EV_QUEUE]; + unsigned int ev_buffer_put; + unsigned int ev_buffer_get; + +} XHCIInterrupter; + struct XHCIState { PCIDevice pci_dev; USBBus bus; @@ -407,27 +428,9 @@ struct XHCIState { uint32_t numports; /* Runtime Registers */ - uint32_t iman; - uint32_t imod; - uint32_t erstsz; - uint32_t erstba_low; - uint32_t erstba_high; - uint32_t erdp_low; - uint32_t erdp_high; - bool msix_used; - int64_t mfindex_start; QEMUTimer *mfwrap_timer; - - dma_addr_t er_start; - uint32_t er_size; - bool er_pcs; - unsigned int er_ep_idx; - bool er_full; - - XHCIEvent ev_buffer[EV_QUEUE]; - unsigned int ev_buffer_put; - unsigned int ev_buffer_get; + XHCIInterrupter intr[MAXINTRS]; XHCIRing cmd_ring; }; @@ -446,8 +449,8 @@ enum xhci_flags { static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid); -static void xhci_event(XHCIState *xhci, XHCIEvent *event); -static void xhci_write_event(XHCIState *xhci, XHCIEvent *event); +static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v); +static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v); static const char *TRBType_names[] = { [TRB_RESERVED] = "TRB_RESERVED", @@ -573,7 +576,7 @@ static void xhci_mfwrap_timer(void *opaque) XHCIState *xhci = opaque; XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS }; - xhci_event(xhci, &wrap); + xhci_event(xhci, &wrap, 0); xhci_mfwrap_update(xhci); } @@ -626,8 +629,8 @@ static void xhci_intx_update(XHCIState *xhci) return; } - if (xhci->iman & IMAN_IP && - xhci->iman & IMAN_IE && + if (xhci->intr[0].iman & IMAN_IP && + xhci->intr[0].iman & IMAN_IE && xhci->usbcmd & USBCMD_INTE) { level = 1; } @@ -636,7 +639,7 @@ static void xhci_intx_update(XHCIState *xhci) qemu_set_irq(xhci->irq, level); } -static void xhci_msix_update(XHCIState *xhci) +static void xhci_msix_update(XHCIState *xhci, int v) { bool enabled; @@ -644,29 +647,29 @@ static void xhci_msix_update(XHCIState *xhci) return; } - enabled = xhci->iman & IMAN_IE; - if (enabled == xhci->msix_used) { + enabled = xhci->intr[v].iman & IMAN_IE; + if (enabled == xhci->intr[v].msix_used) { return; } if (enabled) { - trace_usb_xhci_irq_msix_use(0); - msix_vector_use(&xhci->pci_dev, 0); - xhci->msix_used = true; + trace_usb_xhci_irq_msix_use(v); + msix_vector_use(&xhci->pci_dev, v); + xhci->intr[v].msix_used = true; } else { - trace_usb_xhci_irq_msix_unuse(0); - msix_vector_unuse(&xhci->pci_dev, 0); - xhci->msix_used = false; + trace_usb_xhci_irq_msix_unuse(v); + msix_vector_unuse(&xhci->pci_dev, v); + xhci->intr[v].msix_used = false; } } -static void xhci_intr_raise(XHCIState *xhci) +static void xhci_intr_raise(XHCIState *xhci, int v) { - xhci->erdp_low |= ERDP_EHB; - xhci->iman |= IMAN_IP; + xhci->intr[v].erdp_low |= ERDP_EHB; + xhci->intr[v].iman |= IMAN_IP; xhci->usbsts |= USBSTS_EINT; - if (!(xhci->iman & IMAN_IE)) { + if (!(xhci->intr[v].iman & IMAN_IE)) { return; } @@ -675,24 +678,26 @@ static void xhci_intr_raise(XHCIState *xhci) } if (msix_enabled(&xhci->pci_dev)) { - trace_usb_xhci_irq_msix(0); - msix_notify(&xhci->pci_dev, 0); + trace_usb_xhci_irq_msix(v); + msix_notify(&xhci->pci_dev, v); return; } if (msi_enabled(&xhci->pci_dev)) { - trace_usb_xhci_irq_msi(0); - msi_notify(&xhci->pci_dev, 0); + trace_usb_xhci_irq_msi(v); + msi_notify(&xhci->pci_dev, v); return; } - trace_usb_xhci_irq_intx(1); - qemu_set_irq(xhci->irq, 1); + if (v == 0) { + trace_usb_xhci_irq_intx(1); + qemu_set_irq(xhci->irq, 1); + } } static inline int xhci_running(XHCIState *xhci) { - return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full; + return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full; } static void xhci_die(XHCIState *xhci) @@ -701,8 +706,9 @@ static void xhci_die(XHCIState *xhci) fprintf(stderr, "xhci: asserted controller error\n"); } -static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) +static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v) { + XHCIInterrupter *intr = &xhci->intr[v]; XHCITRB ev_trb; dma_addr_t addr; @@ -710,27 +716,28 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event) ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24)); ev_trb.control = (event->slotid << 24) | (event->epid << 16) | event->flags | (event->type << TRB_TYPE_SHIFT); - if (xhci->er_pcs) { + if (intr->er_pcs) { ev_trb.control |= TRB_C; } ev_trb.control = cpu_to_le32(ev_trb.control); - trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb), + trace_usb_xhci_queue_event(v, intr->er_ep_idx, trb_name(&ev_trb), event_name(event), ev_trb.parameter, ev_trb.status, ev_trb.control); - addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx; + addr = intr->er_start + TRB_SIZE*intr->er_ep_idx; pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE); - xhci->er_ep_idx++; - if (xhci->er_ep_idx >= xhci->er_size) { - xhci->er_ep_idx = 0; - xhci->er_pcs = !xhci->er_pcs; + intr->er_ep_idx++; + if (intr->er_ep_idx >= intr->er_size) { + intr->er_ep_idx = 0; + intr->er_pcs = !intr->er_pcs; } } -static void xhci_events_update(XHCIState *xhci) +static void xhci_events_update(XHCIState *xhci, int v) { + XHCIInterrupter *intr = &xhci->intr[v]; dma_addr_t erdp; unsigned int dp_idx; bool do_irq = 0; @@ -739,115 +746,116 @@ static void xhci_events_update(XHCIState *xhci) return; } - erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high); - if (erdp < xhci->er_start || - erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) { + erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); + if (erdp < intr->er_start || + erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n", - xhci->er_start, xhci->er_size); + fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", + v, intr->er_start, intr->er_size); xhci_die(xhci); return; } - dp_idx = (erdp - xhci->er_start) / TRB_SIZE; - assert(dp_idx < xhci->er_size); + dp_idx = (erdp - intr->er_start) / TRB_SIZE; + assert(dp_idx < intr->er_size); /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus * deadlocks when the ER is full. Hack it by holding off events until * the driver decides to free at least half of the ring */ - if (xhci->er_full) { - int er_free = dp_idx - xhci->er_ep_idx; + if (intr->er_full) { + int er_free = dp_idx - intr->er_ep_idx; if (er_free <= 0) { - er_free += xhci->er_size; + er_free += intr->er_size; } - if (er_free < (xhci->er_size/2)) { + if (er_free < (intr->er_size/2)) { DPRINTF("xhci_events_update(): event ring still " "more than half full (hack)\n"); return; } } - while (xhci->ev_buffer_put != xhci->ev_buffer_get) { - assert(xhci->er_full); - if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) { + while (intr->ev_buffer_put != intr->ev_buffer_get) { + assert(intr->er_full); + if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) { DPRINTF("xhci_events_update(): event ring full again\n"); #ifndef ER_FULL_HACK XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; - xhci_write_event(xhci, &full); + xhci_write_event(xhci, &full, v); #endif do_irq = 1; break; } - XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get]; - xhci_write_event(xhci, event); - xhci->ev_buffer_get++; + XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get]; + xhci_write_event(xhci, event, v); + intr->ev_buffer_get++; do_irq = 1; - if (xhci->ev_buffer_get == EV_QUEUE) { - xhci->ev_buffer_get = 0; + if (intr->ev_buffer_get == EV_QUEUE) { + intr->ev_buffer_get = 0; } } if (do_irq) { - xhci_intr_raise(xhci); + xhci_intr_raise(xhci, v); } - if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) { + if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) { DPRINTF("xhci_events_update(): event ring no longer full\n"); - xhci->er_full = 0; + intr->er_full = 0; } return; } -static void xhci_event(XHCIState *xhci, XHCIEvent *event) +static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) { + XHCIInterrupter *intr = &xhci->intr[v]; dma_addr_t erdp; unsigned int dp_idx; - if (xhci->er_full) { + if (intr->er_full) { DPRINTF("xhci_event(): ER full, queueing\n"); - if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) { + if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { fprintf(stderr, "xhci: event queue full, dropping event!\n"); return; } - xhci->ev_buffer[xhci->ev_buffer_put++] = *event; - if (xhci->ev_buffer_put == EV_QUEUE) { - xhci->ev_buffer_put = 0; + intr->ev_buffer[intr->ev_buffer_put++] = *event; + if (intr->ev_buffer_put == EV_QUEUE) { + intr->ev_buffer_put = 0; } return; } - erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high); - if (erdp < xhci->er_start || - erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) { + erdp = xhci_addr64(intr->erdp_low, intr->erdp_high); + if (erdp < intr->er_start || + erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) { fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp); - fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n", - xhci->er_start, xhci->er_size); + fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n", + v, intr->er_start, intr->er_size); xhci_die(xhci); return; } - dp_idx = (erdp - xhci->er_start) / TRB_SIZE; - assert(dp_idx < xhci->er_size); + dp_idx = (erdp - intr->er_start) / TRB_SIZE; + assert(dp_idx < intr->er_size); - if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) { + if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) { DPRINTF("xhci_event(): ER full, queueing\n"); #ifndef ER_FULL_HACK XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR}; xhci_write_event(xhci, &full); #endif - xhci->er_full = 1; - if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) { + intr->er_full = 1; + if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { fprintf(stderr, "xhci: event queue full, dropping event!\n"); return; } - xhci->ev_buffer[xhci->ev_buffer_put++] = *event; - if (xhci->ev_buffer_put == EV_QUEUE) { - xhci->ev_buffer_put = 0; + intr->ev_buffer[intr->ev_buffer_put++] = *event; + if (intr->ev_buffer_put == EV_QUEUE) { + intr->ev_buffer_put = 0; } } else { - xhci_write_event(xhci, event); + xhci_write_event(xhci, event, v); } - xhci_intr_raise(xhci); + xhci_intr_raise(xhci, v); } static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring, @@ -939,17 +947,18 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring) } } -static void xhci_er_reset(XHCIState *xhci) +static void xhci_er_reset(XHCIState *xhci, int v) { + XHCIInterrupter *intr = &xhci->intr[v]; XHCIEvRingSeg seg; /* cache the (sole) event ring segment location */ - if (xhci->erstsz != 1) { - fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz); + if (intr->erstsz != 1) { + fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz); xhci_die(xhci); return; } - dma_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high); + dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high); pci_dma_read(&xhci->pci_dev, erstba, &seg, sizeof(seg)); le32_to_cpus(&seg.addr_low); le32_to_cpus(&seg.addr_high); @@ -959,15 +968,15 @@ static void xhci_er_reset(XHCIState *xhci) xhci_die(xhci); return; } - xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high); - xhci->er_size = seg.size; + intr->er_start = xhci_addr64(seg.addr_low, seg.addr_high); + intr->er_size = seg.size; - xhci->er_ep_idx = 0; - xhci->er_pcs = 1; - xhci->er_full = 0; + intr->er_ep_idx = 0; + intr->er_pcs = 1; + intr->er_full = 0; - DPRINTF("xhci: event ring:" DMA_ADDR_FMT " [%d]\n", - xhci->er_start, xhci->er_size); + DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n", + v, intr->er_start, intr->er_size); } static void xhci_run(XHCIState *xhci) @@ -1368,7 +1377,7 @@ static void xhci_xfer_report(XHCITransfer *xfer) DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length); edtla = 0; } - xhci_event(xhci, &event); + xhci_event(xhci, &event, 0 /* FIXME */); reported = 1; if (xfer->status != CC_SUCCESS) { return; @@ -2244,7 +2253,7 @@ static void xhci_process_commands(XHCIState *xhci) break; } event.slotid = slotid; - xhci_event(xhci, &event); + xhci_event(xhci, &event, 0 /* FIXME */); } } @@ -2274,7 +2283,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) port->portsc |= PORTSC_CSC; XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, port->portnr << 24}; - xhci_event(xhci, &ev); + xhci_event(xhci, &ev, 0 /* FIXME */); DPRINTF("xhci: port change event for port %d\n", port->portnr); } } @@ -2307,20 +2316,22 @@ static void xhci_reset(DeviceState *dev) xhci_update_port(xhci, xhci->ports + i, 0); } - xhci->iman = 0; - xhci->imod = 0; - xhci->erstsz = 0; - xhci->erstba_low = 0; - xhci->erstba_high = 0; - xhci->erdp_low = 0; - xhci->erdp_high = 0; - xhci->msix_used = 0; + for (i = 0; i < MAXINTRS; i++) { + xhci->intr[i].iman = 0; + xhci->intr[i].imod = 0; + xhci->intr[i].erstsz = 0; + xhci->intr[i].erstba_low = 0; + xhci->intr[i].erstba_high = 0; + xhci->intr[i].erdp_low = 0; + xhci->intr[i].erdp_high = 0; + xhci->intr[i].msix_used = 0; - xhci->er_ep_idx = 0; - xhci->er_pcs = 1; - xhci->er_full = 0; - xhci->ev_buffer_put = 0; - xhci->ev_buffer_get = 0; + xhci->intr[i].er_ep_idx = 0; + xhci->intr[i].er_pcs = 1; + xhci->intr[i].er_full = 0; + xhci->intr[i].ev_buffer_put = 0; + xhci->intr[i].ev_buffer_get = 0; + } xhci->mfindex_start = qemu_get_clock_ns(vm_clock); xhci_mfwrap_update(xhci); @@ -2551,7 +2562,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) { XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED}; xhci->crcr_low &= ~CRCR_CRR; - xhci_event(xhci, &event); + xhci_event(xhci, &event, 0 /* FIXME */); DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low); } else { dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val); @@ -2575,6 +2586,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) { + XHCIInterrupter *intr = &xhci->intr[0]; uint32_t ret; switch (reg) { @@ -2582,25 +2594,25 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) ret = xhci_mfindex_get(xhci) & 0x3fff; break; case 0x20: /* IMAN */ - ret = xhci->iman; + ret = intr->iman; break; case 0x24: /* IMOD */ - ret = xhci->imod; + ret = intr->imod; break; case 0x28: /* ERSTSZ */ - ret = xhci->erstsz; + ret = intr->erstsz; break; case 0x30: /* ERSTBA low */ - ret = xhci->erstba_low; + ret = intr->erstba_low; break; case 0x34: /* ERSTBA high */ - ret = xhci->erstba_high; + ret = intr->erstba_high; break; case 0x38: /* ERDP low */ - ret = xhci->erdp_low; + ret = intr->erdp_low; break; case 0x3c: /* ERDP high */ - ret = xhci->erdp_high; + ret = intr->erdp_high; break; default: fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); @@ -2613,42 +2625,43 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) { + XHCIInterrupter *intr = &xhci->intr[0]; trace_usb_xhci_runtime_write(reg, val); switch (reg) { case 0x20: /* IMAN */ if (val & IMAN_IP) { - xhci->iman &= ~IMAN_IP; + intr->iman &= ~IMAN_IP; } - xhci->iman &= ~IMAN_IE; - xhci->iman |= val & IMAN_IE; + intr->iman &= ~IMAN_IE; + intr->iman |= val & IMAN_IE; xhci_intx_update(xhci); - xhci_msix_update(xhci); + xhci_msix_update(xhci, 0); break; case 0x24: /* IMOD */ - xhci->imod = val; + intr->imod = val; break; case 0x28: /* ERSTSZ */ - xhci->erstsz = val & 0xffff; + intr->erstsz = val & 0xffff; break; case 0x30: /* ERSTBA low */ /* XXX NEC driver bug: it doesn't align this to 64 bytes - xhci->erstba_low = val & 0xffffffc0; */ - xhci->erstba_low = val & 0xfffffff0; + intr->erstba_low = val & 0xffffffc0; */ + intr->erstba_low = val & 0xfffffff0; break; case 0x34: /* ERSTBA high */ - xhci->erstba_high = val; - xhci_er_reset(xhci); + intr->erstba_high = val; + xhci_er_reset(xhci, 0); break; case 0x38: /* ERDP low */ if (val & ERDP_EHB) { - xhci->erdp_low &= ~ERDP_EHB; + intr->erdp_low &= ~ERDP_EHB; } - xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB); + intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB); break; case 0x3c: /* ERDP high */ - xhci->erdp_high = val; - xhci_events_update(xhci); + intr->erdp_high = val; + xhci_events_update(xhci, 0); break; default: fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); @@ -2778,7 +2791,7 @@ static void xhci_wakeup(USBPort *usbport) return; } port->portsc |= PORTSC_PLC; - xhci_event(xhci, &ev); + xhci_event(xhci, &ev, 0 /* FIXME */); } static void xhci_complete(USBPort *port, USBPacket *packet) diff --git a/trace-events b/trace-events index 8589ca4ea..b25ae1c43 100644 --- a/trace-events +++ b/trace-events @@ -316,7 +316,7 @@ usb_xhci_irq_msi(uint32_t nr) "nr %d" usb_xhci_irq_msix(uint32_t nr) "nr %d" usb_xhci_irq_msix_use(uint32_t nr) "nr %d" usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d" -usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x" +usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %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" -- cgit v1.2.3 From 43d9d6047e0f12b96bfc680982a630f0af78611a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 30 Aug 2012 17:15:12 +0200 Subject: xhci: prepare xhci_runtime_{read,write} for multiple interrupters Prepare xhci runtime register access function for multiple interrupters. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 100 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 0a03053c0..72f4eebb6 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2586,37 +2586,43 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) { - XHCIInterrupter *intr = &xhci->intr[0]; - uint32_t ret; + uint32_t ret = 0; - switch (reg) { - case 0x00: /* MFINDEX */ - ret = xhci_mfindex_get(xhci) & 0x3fff; - break; - case 0x20: /* IMAN */ - ret = intr->iman; - break; - case 0x24: /* IMOD */ - ret = intr->imod; - break; - case 0x28: /* ERSTSZ */ - ret = intr->erstsz; - break; - case 0x30: /* ERSTBA low */ - ret = intr->erstba_low; - break; - case 0x34: /* ERSTBA high */ - ret = intr->erstba_high; - break; - case 0x38: /* ERDP low */ - ret = intr->erdp_low; - break; - case 0x3c: /* ERDP high */ - ret = intr->erdp_high; - break; - default: - fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); - ret = 0; + if (reg < 0x20) { + switch (reg) { + case 0x00: /* MFINDEX */ + ret = xhci_mfindex_get(xhci) & 0x3fff; + break; + default: + fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); + break; + } + } else { + int v = (reg - 0x20) / 0x20; + XHCIInterrupter *intr = &xhci->intr[v]; + switch (reg & 0x1f) { + case 0x00: /* IMAN */ + ret = intr->iman; + break; + case 0x04: /* IMOD */ + ret = intr->imod; + break; + case 0x08: /* ERSTSZ */ + ret = intr->erstsz; + break; + case 0x10: /* ERSTBA low */ + ret = intr->erstba_low; + break; + case 0x14: /* ERSTBA high */ + ret = intr->erstba_high; + break; + case 0x18: /* ERDP low */ + ret = intr->erdp_low; + break; + case 0x1c: /* ERDP high */ + ret = intr->erdp_high; + break; + } } trace_usb_xhci_runtime_read(reg, ret); @@ -2625,43 +2631,51 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) { - XHCIInterrupter *intr = &xhci->intr[0]; + int v = (reg - 0x20) / 0x20; + XHCIInterrupter *intr = &xhci->intr[v]; trace_usb_xhci_runtime_write(reg, val); - switch (reg) { - case 0x20: /* IMAN */ + if (reg < 0x20) { + fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); + return; + } + + switch (reg & 0x1f) { + case 0x00: /* IMAN */ if (val & IMAN_IP) { intr->iman &= ~IMAN_IP; } intr->iman &= ~IMAN_IE; intr->iman |= val & IMAN_IE; - xhci_intx_update(xhci); - xhci_msix_update(xhci, 0); + if (v == 0) { + xhci_intx_update(xhci); + } + xhci_msix_update(xhci, v); break; - case 0x24: /* IMOD */ + case 0x04: /* IMOD */ intr->imod = val; break; - case 0x28: /* ERSTSZ */ + case 0x08: /* ERSTSZ */ intr->erstsz = val & 0xffff; break; - case 0x30: /* ERSTBA low */ + case 0x10: /* ERSTBA low */ /* XXX NEC driver bug: it doesn't align this to 64 bytes intr->erstba_low = val & 0xffffffc0; */ intr->erstba_low = val & 0xfffffff0; break; - case 0x34: /* ERSTBA high */ + case 0x14: /* ERSTBA high */ intr->erstba_high = val; - xhci_er_reset(xhci, 0); + xhci_er_reset(xhci, v); break; - case 0x38: /* ERDP low */ + case 0x18: /* ERDP low */ if (val & ERDP_EHB) { intr->erdp_low &= ~ERDP_EHB; } intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB); break; - case 0x3c: /* ERDP high */ + case 0x1c: /* ERDP high */ intr->erdp_high = val; - xhci_events_update(xhci, 0); + xhci_events_update(xhci, v); break; default: fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); -- cgit v1.2.3 From 2d1de8508fed1bddb1946d7d57256c96e02c1dd4 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 31 Aug 2012 15:30:51 +0200 Subject: xhci: pick target interrupter Pick the correct interrupter when queuing an event. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 72f4eebb6..157985133 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -264,6 +264,10 @@ typedef enum TRBCCode { #define TRB_LK_TC (1<<1) +#define TRB_INTR_SHIFT 22 +#define TRB_INTR_MASK 0x3ff +#define TRB_INTR(t) (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK) + #define EP_TYPE_MASK 0x7 #define EP_TYPE_SHIFT 3 @@ -806,10 +810,16 @@ static void xhci_events_update(XHCIState *xhci, int v) static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v) { - XHCIInterrupter *intr = &xhci->intr[v]; + XHCIInterrupter *intr; dma_addr_t erdp; unsigned int dp_idx; + if (v >= MAXINTRS) { + DPRINTF("intr nr out of range (%d >= %d)\n", v, MAXINTRS); + return; + } + intr = &xhci->intr[v]; + if (intr->er_full) { DPRINTF("xhci_event(): ER full, queueing\n"); if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) { @@ -1377,7 +1387,7 @@ static void xhci_xfer_report(XHCITransfer *xfer) DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length); edtla = 0; } - xhci_event(xhci, &event, 0 /* FIXME */); + xhci_event(xhci, &event, TRB_INTR(*trb)); reported = 1; if (xfer->status != CC_SUCCESS) { return; @@ -2253,7 +2263,7 @@ static void xhci_process_commands(XHCIState *xhci) break; } event.slotid = slotid; - xhci_event(xhci, &event, 0 /* FIXME */); + xhci_event(xhci, &event, 0); } } @@ -2283,7 +2293,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach) port->portsc |= PORTSC_CSC; XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, port->portnr << 24}; - xhci_event(xhci, &ev, 0 /* FIXME */); + xhci_event(xhci, &ev, 0); DPRINTF("xhci: port change event for port %d\n", port->portnr); } } @@ -2562,7 +2572,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) { XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED}; xhci->crcr_low &= ~CRCR_CRR; - xhci_event(xhci, &event, 0 /* FIXME */); + xhci_event(xhci, &event, 0); DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low); } else { dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val); @@ -2805,7 +2815,7 @@ static void xhci_wakeup(USBPort *usbport) return; } port->portsc |= PORTSC_PLC; - xhci_event(xhci, &ev, 0 /* FIXME */); + xhci_event(xhci, &ev, 0); } static void xhci_complete(USBPort *port, USBPacket *packet) -- cgit v1.2.3 From fa8ee89e8b0a075e82ca54faa6135137abccfa48 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 4 Sep 2012 12:56:55 +0200 Subject: xhci: support multiple interrupters Everything is in place, flip the big switch now and enable support for multiple interrupters. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 157985133..2e3a6209c 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -42,7 +42,7 @@ #define MAXPORTS (MAXPORTS_2+MAXPORTS_3) #define MAXSLOTS MAXPORTS -#define MAXINTRS 1 /* MAXPORTS */ +#define MAXINTRS MAXPORTS #define TD_QUEUE 24 @@ -75,10 +75,6 @@ # error Increase LEN_REGS #endif -#if MAXINTRS > 1 -# error TODO: only one interrupter supported -#endif - /* bit definitions */ #define USBCMD_RS (1<<0) #define USBCMD_HCRST (1<<1) -- cgit v1.2.3 From 1b067564ce5db9a144eb15239abbd36c31dd65c8 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 4 Sep 2012 14:42:20 +0200 Subject: xhci: kill xhci_mem_{read,write} dispatcher functions ... and register subregions instead, so we offload the dispatching to the the memory subsystem which is designed to handle it. Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 140 +++++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 65 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 2e3a6209c..567ffb159 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -404,6 +404,10 @@ struct XHCIState { USBBus bus; qemu_irq irq; MemoryRegion mem; + MemoryRegion mem_cap; + MemoryRegion mem_oper; + MemoryRegion mem_runtime; + MemoryRegion mem_doorbell; const char *name; unsigned int devaddr; @@ -2343,8 +2347,9 @@ static void xhci_reset(DeviceState *dev) xhci_mfwrap_update(xhci); } -static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) +static uint64_t xhci_cap_read(void *ptr, target_phys_addr_t reg, unsigned size) { + XHCIState *xhci = ptr; uint32_t ret; switch (reg) { @@ -2401,7 +2406,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg) ret = 0x00000000; /* reserved */ break; default: - fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg); + fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg); ret = 0; } @@ -2482,8 +2487,9 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) } } -static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) +static uint64_t xhci_oper_read(void *ptr, target_phys_addr_t reg, unsigned size) { + XHCIState *xhci = ptr; uint32_t ret; if (reg >= 0x400) { @@ -2519,7 +2525,7 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) ret = xhci->config; break; default: - fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg); + fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg); ret = 0; } @@ -2527,8 +2533,11 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg) return ret; } -static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) +static void xhci_oper_write(void *ptr, target_phys_addr_t reg, + uint64_t val, unsigned size) { + XHCIState *xhci = ptr; + if (reg >= 0x400) { xhci_port_write(xhci, reg - 0x400, val); return; @@ -2586,12 +2595,14 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val) xhci->config = val & 0xff; break; default: - fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); + fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg); } } -static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) +static uint64_t xhci_runtime_read(void *ptr, target_phys_addr_t reg, + unsigned size) { + XHCIState *xhci = ptr; uint32_t ret = 0; if (reg < 0x20) { @@ -2600,7 +2611,8 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) ret = xhci_mfindex_get(xhci) & 0x3fff; break; default: - fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg); + fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", + (int)reg); break; } } else { @@ -2635,14 +2647,16 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg) return ret; } -static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) +static void xhci_runtime_write(void *ptr, target_phys_addr_t reg, + uint64_t val, unsigned size) { + XHCIState *xhci = ptr; int v = (reg - 0x20) / 0x20; XHCIInterrupter *intr = &xhci->intr[v]; trace_usb_xhci_runtime_write(reg, val); if (reg < 0x20) { - fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); + fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg); return; } @@ -2684,19 +2698,24 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val) xhci_events_update(xhci, v); break; default: - fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg); + fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", + (int)reg); } } -static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg) +static uint64_t xhci_doorbell_read(void *ptr, target_phys_addr_t reg, + unsigned size) { /* 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) +static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg, + uint64_t val, unsigned size) { + XHCIState *xhci = ptr; + trace_usb_xhci_doorbell_write(reg, val); if (!xhci_running(xhci)) { @@ -2710,69 +2729,47 @@ static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val) if (val == 0) { xhci_process_commands(xhci); } else { - fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val); + fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", + (uint32_t)val); } } else { if (reg > MAXSLOTS) { - fprintf(stderr, "xhci: bad doorbell %d\n", reg); + fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg); } else if (val > 31) { - fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val); + fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", + (int)reg, (uint32_t)val); } else { xhci_kick_ep(xhci, reg, val); } } } -static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr, - unsigned size) -{ - XHCIState *xhci = ptr; - - /* Only aligned reads are allowed on xHCI */ - if (addr & 3) { - fprintf(stderr, "xhci_mem_read: Mis-aligned read\n"); - return 0; - } - - if (addr < LEN_CAP) { - return xhci_cap_read(xhci, addr); - } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) { - return xhci_oper_read(xhci, addr - OFF_OPER); - } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) { - return xhci_runtime_read(xhci, addr - OFF_RUNTIME); - } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) { - return xhci_doorbell_read(xhci, addr - OFF_DOORBELL); - } else { - fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr); - return 0; - } -} - -static void xhci_mem_write(void *ptr, target_phys_addr_t addr, - uint64_t val, unsigned size) -{ - XHCIState *xhci = ptr; +static const MemoryRegionOps xhci_cap_ops = { + .read = xhci_cap_read, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; - /* Only aligned writes are allowed on xHCI */ - if (addr & 3) { - fprintf(stderr, "xhci_mem_write: Mis-aligned write\n"); - return; - } +static const MemoryRegionOps xhci_oper_ops = { + .read = xhci_oper_read, + .write = xhci_oper_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; - if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) { - xhci_oper_write(xhci, addr - OFF_OPER, val); - } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) { - xhci_runtime_write(xhci, addr - OFF_RUNTIME, val); - } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) { - xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val); - } else { - fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr); - } -} +static const MemoryRegionOps xhci_runtime_ops = { + .read = xhci_runtime_read, + .write = xhci_runtime_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; -static const MemoryRegionOps xhci_mem_ops = { - .read = xhci_mem_read, - .write = xhci_mem_write, +static const MemoryRegionOps xhci_doorbell_ops = { + .read = xhci_doorbell_read, + .write = xhci_doorbell_write, .valid.min_access_size = 4, .valid.max_access_size = 4, .endianness = DEVICE_LITTLE_ENDIAN, @@ -2938,8 +2935,21 @@ static int usb_xhci_initfn(struct PCIDevice *dev) xhci->irq = xhci->pci_dev.irq[0]; - memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci, - "xhci", LEN_REGS); + memory_region_init(&xhci->mem, "xhci", LEN_REGS); + memory_region_init_io(&xhci->mem_cap, &xhci_cap_ops, xhci, + "capabilities", LEN_CAP); + memory_region_init_io(&xhci->mem_oper, &xhci_oper_ops, xhci, + "operational", 0x400 + 0x10 * xhci->numports); + memory_region_init_io(&xhci->mem_runtime, &xhci_runtime_ops, xhci, + "runtime", LEN_RUNTIME); + memory_region_init_io(&xhci->mem_doorbell, &xhci_doorbell_ops, xhci, + "doorbell", LEN_DOORBELL); + + memory_region_add_subregion(&xhci->mem, 0, &xhci->mem_cap); + memory_region_add_subregion(&xhci->mem, OFF_OPER, &xhci->mem_oper); + memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime); + memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell); + pci_register_bar(&xhci->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, &xhci->mem); -- cgit v1.2.3 From 6ee021d41078844df60a3a466e3829a3e82776f3 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Tue, 4 Sep 2012 14:48:03 +0200 Subject: xhci: allow bytewise capability register reads Some guests need this according to Alejandro Martinez Ruiz Signed-off-by: Gerd Hoffmann --- hw/usb/hcd-xhci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 567ffb159..e0ca69044 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -2746,8 +2746,10 @@ static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg, static const MemoryRegionOps xhci_cap_ops = { .read = xhci_cap_read, - .valid.min_access_size = 4, + .valid.min_access_size = 1, .valid.max_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, .endianness = DEVICE_LITTLE_ENDIAN, }; -- cgit v1.2.3