summaryrefslogtreecommitdiff
path: root/hw/net/xilinx_axienet.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/net/xilinx_axienet.c')
-rw-r--r--hw/net/xilinx_axienet.c28
1 files changed, 25 insertions, 3 deletions
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 6d27546b29..544c3ec9c3 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -383,6 +383,9 @@ struct XilinxAXIEnet {
uint8_t *rxmem;
+ uint32_t *rxapp;
+ uint32_t rxsize;
+ uint32_t rxpos;
};
static void axienet_rx_reset(XilinxAXIEnet *s)
@@ -645,7 +648,7 @@ static int eth_can_rx(NetClientState *nc)
XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
/* RX enabled? */
- return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
+ return !s->rxsize && !axienet_rx_resetting(s) && axienet_rx_enabled(s);
}
static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
@@ -663,6 +666,23 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
return match;
}
+static void axienet_eth_rx_notify(void *opaque)
+{
+ XilinxAXIEnet *s = XILINX_AXI_ENET(opaque);
+
+ while (s->rxsize && stream_can_push(s->tx_dev, axienet_eth_rx_notify, s)) {
+ size_t ret = stream_push(s->tx_dev, (void *)s->rxmem + s->rxpos,
+ s->rxsize, s->rxapp);
+ s->rxsize -= ret;
+ s->rxpos += ret;
+ if (!s->rxsize) {
+ s->regs[R_IS] |= IS_RX_COMPLETE;
+ g_free(s->rxapp);
+ }
+ }
+ enet_update_irq(s);
+}
+
static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
{
XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
@@ -800,9 +820,11 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
/* Good frame. */
app[2] |= 1 << 6;
- stream_push(s->tx_dev, (void *)s->rxmem, size, app);
+ s->rxsize = size;
+ s->rxpos = 0;
+ s->rxapp = g_memdup(app, sizeof(app));
+ axienet_eth_rx_notify(s);
- s->regs[R_IS] |= IS_RX_COMPLETE;
enet_update_irq(s);
return size;
}