mirror of
https://github.com/genodelabs/genode.git
synced 2025-01-22 20:38:09 +00:00
qemu-usb: flush EP improve isochronous handling
- Patch the XHCI model in order to handle frame wrapping correctly. For this adjust 'mfindex_kick' to the correct period (same, before, or after 'mfindex'). - Flush EP when it is stopped, this causes all pending packets for the EP to be acked. Correct counting of packets in flight. - Add BEI patch by Josef. issue #4196
This commit is contained in:
parent
eabda8907f
commit
818f1682ee
@ -1 +1 @@
|
||||
4b74867ae1e9383a53edb67fc3665ed8b305e2e6
|
||||
a716b3ed197d29cb3f059a76de9bb4e0e8c708f0
|
||||
|
@ -10,5 +10,8 @@ TAR_OPT(qemu) := --strip-components=1 --files-from - < <(sed 's/-x.x.x/-$(VERSIO
|
||||
HASH_INPUT += $(REP_DIR)/src/lib/qemu-usb/files.list
|
||||
|
||||
PATCHES := src/lib/qemu-usb/patches/xhci_pci_register.patch \
|
||||
src/lib/qemu-usb/patches/usb_bus_nfree.patch
|
||||
src/lib/qemu-usb/patches/usb_bus_nfree.patch \
|
||||
src/lib/qemu-usb/patches/hcd-xhci-bei.patch \
|
||||
src/lib/qemu-usb/patches/xhci_frame_wrap.patch
|
||||
|
||||
PATCH_OPT:= -p1
|
||||
|
@ -34,7 +34,6 @@ using Packet_alloc_failed = Usb::Session::Tx::Source::Packet_alloc_failed;
|
||||
using Packet_type = Usb::Packet_descriptor::Type;
|
||||
using Packet_error = Usb::Packet_descriptor::Error;
|
||||
|
||||
|
||||
static unsigned endpoint_number(USBEndpoint const *usb_ep)
|
||||
{
|
||||
bool in = usb_ep->pid == USB_TOKEN_IN;
|
||||
@ -342,12 +341,8 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
Usb::Packet_descriptor packet = usb_raw.source()->get_acked_packet();
|
||||
Completion *c = dynamic_cast<Completion *>(packet.completion);
|
||||
|
||||
if ((packet.type == Packet_type::ISOC && !packet.read_transfer())) {
|
||||
free_packet(packet);
|
||||
_isoch_out_pending--;
|
||||
continue;
|
||||
}
|
||||
if ((c && c->state == Completion::CANCELED)) {
|
||||
if ((packet.type == Packet_type::ISOC && !packet.read_transfer()) ||
|
||||
(c && c->state == Completion::CANCELED)) {
|
||||
free_packet(packet);
|
||||
continue;
|
||||
}
|
||||
@ -355,12 +350,11 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
char *content = usb_raw.source()->packet_content(packet);
|
||||
|
||||
if (packet.type != Packet_type::ISOC) {
|
||||
c->complete(packet, content);
|
||||
if (c) c->complete(packet, content);
|
||||
free_packet(packet);
|
||||
} else {
|
||||
/* isochronous in */
|
||||
free_completion(packet);
|
||||
_isoc_in_pending--;
|
||||
Isoc_packet *new_packet = new (_alloc)
|
||||
Isoc_packet(packet, content);
|
||||
isoc_read_queue.enqueue(*new_packet);
|
||||
@ -393,9 +387,7 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
|
||||
bool isoc_new_packet()
|
||||
{
|
||||
unsigned count = 0;
|
||||
isoc_read_queue.for_each([&count] (Isoc_packet&) { count++; });
|
||||
return (count + _isoc_in_pending) < 32 ? true : false;
|
||||
return _isoc_in_pending < 32 ? true : false;
|
||||
}
|
||||
|
||||
void isoc_in_packet(USBPacket *usb_packet)
|
||||
@ -403,8 +395,9 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
enum { NUMBER_OF_PACKETS = 1 };
|
||||
isoc_read(usb_packet);
|
||||
|
||||
if (!isoc_new_packet())
|
||||
if (!isoc_new_packet()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = usb_packet->ep->max_packet_size * NUMBER_OF_PACKETS;
|
||||
try {
|
||||
@ -424,7 +417,6 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
c->endpoint = endpoint_number(usb_packet->ep);
|
||||
|
||||
_isoc_in_pending++;
|
||||
|
||||
submit(packet);
|
||||
} catch (Packet_alloc_failed) {
|
||||
if (verbose_warnings)
|
||||
@ -553,6 +545,11 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
|
||||
void free_packet(Usb::Packet_descriptor &packet)
|
||||
{
|
||||
if (packet.type == Packet_type::ISOC) {
|
||||
if (packet.read_transfer()) _isoc_in_pending--;
|
||||
else _isoch_out_pending--;
|
||||
}
|
||||
|
||||
free_completion(packet);
|
||||
usb_raw.source()->release_packet(packet);
|
||||
}
|
||||
@ -654,6 +651,20 @@ struct Usb_host_device : List<Usb_host_device>::Element
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void flush_transfers(uint8_t ep)
|
||||
{
|
||||
try {
|
||||
Usb::Packet_descriptor packet = alloc_packet(0, false);
|
||||
packet.type = Usb::Packet_descriptor::FLUSH_TRANSFERS;
|
||||
packet.number = ep;
|
||||
submit(packet);
|
||||
} catch (...) {
|
||||
if (verbose_warnings)
|
||||
warning(__func__, " packet allocation failed");
|
||||
}
|
||||
}
|
||||
|
||||
void update_ep(USBDevice *udev)
|
||||
{
|
||||
usb_ep_reset(udev);
|
||||
@ -877,10 +888,14 @@ static void usb_host_ep_stopped(USBDevice *udev, USBEndpoint *usb_ep)
|
||||
bool in = usb_ep->pid == USB_TOKEN_IN;
|
||||
unsigned ep = endpoint_number(usb_ep);
|
||||
|
||||
/* flush pending transfers */
|
||||
dev->flush_transfers(ep);
|
||||
|
||||
switch (usb_ep->type) {
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (in)
|
||||
dev->isoc_in_flush(ep);
|
||||
if (in) {
|
||||
dev->isoc_in_flush(ep, true);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
97
repos/libports/src/lib/qemu-usb/patches/hcd-xhci-bei.patch
Normal file
97
repos/libports/src/lib/qemu-usb/patches/hcd-xhci-bei.patch
Normal file
@ -0,0 +1,97 @@
|
||||
diff --git a/src/lib/qemu/hw/usb/hcd-xhci.c b/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
index 9ce7ca7..0e32df5 100644
|
||||
--- a/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
+++ b/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
@@ -307,7 +307,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid);
|
||||
static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
|
||||
unsigned int epid);
|
||||
static void xhci_xfer_report(XHCITransfer *xfer);
|
||||
-static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
|
||||
+static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v, int bei);
|
||||
static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v);
|
||||
static USBEndpoint *xhci_epid_to_usbep(XHCIEPContext *epctx);
|
||||
|
||||
@@ -458,7 +458,7 @@ static void xhci_mfwrap_timer(void *opaque)
|
||||
XHCIState *xhci = opaque;
|
||||
XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS };
|
||||
|
||||
- xhci_event(xhci, &wrap, 0);
|
||||
+ xhci_event(xhci, &wrap, 0, 0);
|
||||
xhci_mfwrap_update(xhci);
|
||||
}
|
||||
|
||||
@@ -623,7 +623,7 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
}
|
||||
}
|
||||
|
||||
-static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
+static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v, int bei)
|
||||
{
|
||||
XHCIInterrupter *intr;
|
||||
dma_addr_t erdp;
|
||||
@@ -658,7 +658,9 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
|
||||
xhci_write_event(xhci, event, v);
|
||||
}
|
||||
|
||||
- xhci_intr_raise(xhci, v);
|
||||
+ if (!bei) {
|
||||
+ xhci_intr_raise(xhci, v);
|
||||
+ }
|
||||
}
|
||||
|
||||
static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
|
||||
@@ -1395,7 +1397,7 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer)
|
||||
dma_addr_t addr;
|
||||
unsigned int chunk = 0;
|
||||
|
||||
- if (trb->control & TRB_TR_IOC) {
|
||||
+ if ((trb->control & TRB_TR_IOC) && !(trb->control & TRB_TR_BEI)) {
|
||||
xfer->int_req = true;
|
||||
}
|
||||
|
||||
@@ -1499,7 +1501,8 @@ static void xhci_xfer_report(XHCITransfer *xfer)
|
||||
DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
|
||||
edtla = 0;
|
||||
}
|
||||
- xhci_event(xhci, &event, TRB_INTR(*trb));
|
||||
+ xhci_event(xhci, &event, TRB_INTR(*trb),
|
||||
+ (trb->control & TRB_TR_BEI));
|
||||
reported = 1;
|
||||
if (xfer->status != CC_SUCCESS) {
|
||||
return;
|
||||
@@ -1920,7 +1923,7 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid)
|
||||
ev.slotid = epctx->slotid;
|
||||
ev.epid = epctx->epid;
|
||||
ev.ptr = epctx->ring.dequeue;
|
||||
- xhci_event(xhci, &ev, xhci->slots[epctx->slotid-1].intr);
|
||||
+ xhci_event(xhci, &ev, xhci->slots[epctx->slotid-1].intr, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2539,7 +2542,7 @@ static void xhci_process_commands(XHCIState *xhci)
|
||||
break;
|
||||
}
|
||||
event.slotid = slotid;
|
||||
- xhci_event(xhci, &event, 0);
|
||||
+ xhci_event(xhci, &event, 0, 0);
|
||||
|
||||
if (count++ > COMMAND_LIMIT) {
|
||||
trace_usb_xhci_enforced_limit("commands");
|
||||
@@ -2572,7 +2575,7 @@ static void xhci_port_notify(XHCIPort *port, uint32_t bits)
|
||||
if (!xhci_running(port->xhci)) {
|
||||
return;
|
||||
}
|
||||
- xhci_event(port->xhci, &ev, 0);
|
||||
+ xhci_event(port->xhci, &ev, 0, 0);
|
||||
}
|
||||
|
||||
static void xhci_port_update(XHCIPort *port, int is_detach)
|
||||
@@ -2937,7 +2940,7 @@ static void xhci_oper_write(void *ptr, hwaddr reg,
|
||||
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);
|
||||
+ xhci_event(xhci, &event, 0, 0);
|
||||
DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
|
||||
} else {
|
||||
dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
|
@ -0,0 +1,48 @@
|
||||
frame index is the frame at the current time, frame kick is the frame the guest
|
||||
programmed. mfindex is the total of all frames since controller startup. Adjust
|
||||
mfindex_kick (total frames programmed by the guest) to the correct period,
|
||||
because frames wrap every 16364 (14 bit).
|
||||
|
||||
diff --git a/src/lib/qemu/hw/usb/hcd-xhci.c b/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
index 9ce7ca7..6867eca 100644
|
||||
--- a/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
+++ b/src/lib/qemu/hw/usb/hcd-xhci.c
|
||||
@@ -1692,6 +1692,12 @@ static void xhci_calc_intr_kick(XHCIState *xhci, XHCITransfer *xfer,
|
||||
xfer->mfindex_kick = MAX(asap, kick);
|
||||
}
|
||||
|
||||
+/* 0 - 16383 (14 Bit from descriptor) */
|
||||
+#define FRAME_INDEX(frame) (frame & 0x3fff)
|
||||
+#define FRAME_PERIOD(frame) (frame & ~0x3fff)
|
||||
+#define FRAME_PERIOD_NEXT(frame) (FRAME_PERIOD(frame) + 0x4000)
|
||||
+#define FRAME_PERIOD_LAST(frame) (FRAME_PERIOD(frame) - 0x4000)
|
||||
+
|
||||
static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
|
||||
XHCIEPContext *epctx, uint64_t mfindex)
|
||||
{
|
||||
@@ -1705,12 +1711,20 @@ static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
|
||||
xfer->mfindex_kick = asap;
|
||||
}
|
||||
} else {
|
||||
- xfer->mfindex_kick = ((xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT)
|
||||
- & TRB_TR_FRAMEID_MASK) << 3;
|
||||
- xfer->mfindex_kick |= mfindex & ~0x3fff;
|
||||
- if (xfer->mfindex_kick + 0x100 < mfindex) {
|
||||
- xfer->mfindex_kick += 0x4000;
|
||||
+ unsigned frame_kick = ((xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT)
|
||||
+ & TRB_TR_FRAMEID_MASK) << 3;
|
||||
+ unsigned frame_index = FRAME_INDEX(mfindex);
|
||||
+
|
||||
+ /* frame index wrapped, kick is still in previous period */
|
||||
+ if (frame_index < 1000 && frame_kick > 15000) {
|
||||
+ xfer->mfindex_kick = frame_kick | FRAME_PERIOD_LAST(mfindex);
|
||||
+ }
|
||||
+ /* kick index wrapped, kick index is in next period */
|
||||
+ else if (frame_kick < 1000 && frame_index > 15000) {
|
||||
+ xfer->mfindex_kick = frame_kick | FRAME_PERIOD_NEXT(mfindex);
|
||||
}
|
||||
+ else
|
||||
+ xfer->mfindex_kick = frame_kick | FRAME_PERIOD(mfindex);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user