static void stellaris_enet_write(void *opaque, target_phys_addr_t offset, uint32_t value) { stellaris_enet_state *s = (stellaris_enet_state *)opaque; switch (offset) { case 0x00: /* IACK */ s->ris &= ~value; DPRINTF("IRQ ack %02x/%02x\n", value, s->ris); stellaris_enet_update(s); /* Clearing TXER also resets the TX fifo. */ if (value & SE_INT_TXER) s->tx_frame_len = -1; break; case 0x04: /* IM */ DPRINTF("IRQ mask %02x/%02x\n", value, s->ris); s->im = value; stellaris_enet_update(s); break; case 0x08: /* RCTL */ s->rctl = value; if (value & SE_RCTL_RSTFIFO) { s->rx_fifo_len = 0; s->np = 0; stellaris_enet_update(s); } break; case 0x0c: /* TCTL */ s->tctl = value; break; case 0x10: /* DATA */ if (s->tx_frame_len == -1) { s->tx_frame_len = value & 0xffff; if (s->tx_frame_len > 2032) { DPRINTF("TX frame too long (%d)\n", s->tx_frame_len); s->tx_frame_len = 0; s->ris |= SE_INT_TXER; stellaris_enet_update(s); } else { DPRINTF("Start TX frame len=%d\n", s->tx_frame_len); /* The value written does not include the ethernet header. */ s->tx_frame_len += 14; if ((s->tctl & SE_TCTL_CRC) == 0) s->tx_frame_len += 4; s->tx_fifo_len = 0; s->tx_fifo[s->tx_fifo_len++] = value >> 16; s->tx_fifo[s->tx_fifo_len++] = value >> 24; } } else {
/* Send the packet currently in the TX FIFO */ static void stellaris_enet_send(stellaris_enet_state *s) { int framelen = stellaris_txpacket_datalen(s); /* Ethernet header is in the FIFO but not in the datacount. * We don't implement explicit CRC, so just ignore any * CRC value in the FIFO. */ framelen += 14; if ((s->tctl & SE_TCTL_PADEN) && framelen < 60) { memset(&s->tx_fifo[framelen + 2], 0, 60 - framelen); framelen = 60; } /* This MIN will have no effect unless the FIFO data is corrupt * (eg bad data from an incoming migration); otherwise the check * on the datalen at the start of writing the data into the FIFO * will have caught this. Silently write a corrupt half-packet, * which is what the hardware does in FIFO underrun situations. */ framelen = MIN(framelen, ARRAY_SIZE(s->tx_fifo) - 2); qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo + 2, framelen); s->tx_fifo_len = 0; s->ris |= SE_INT_TXEMP; stellaris_enet_update(s); DPRINTF("Done TX\n"); }
/* TODO: Implement MAC address filtering. */ static ssize_t stellaris_enet_receive(VLANClientState *vc, const uint8_t *buf, size_t size) { stellaris_enet_state *s = vc->opaque; int n; uint8_t *p; uint32_t crc; if ((s->rctl & SE_RCTL_RXEN) == 0) return -1; if (s->np >= 31) { DPRINTF("Packet dropped\n"); return -1; } DPRINTF("Received packet len=%d\n", size); n = s->next_packet + s->np; if (n >= 31) n -= 31; s->np++; s->rx[n].len = size + 6; p = s->rx[n].data; *(p++) = (size + 6); *(p++) = (size + 6) >> 8; memcpy (p, buf, size); p += size; crc = crc32(~0, buf, size); *(p++) = crc; *(p++) = crc >> 8; *(p++) = crc >> 16; *(p++) = crc >> 24; /* Clear the remaining bytes in the last word. */ if ((size & 3) != 2) { memset(p, 0, (6 - size) & 3); } s->ris |= SE_INT_RX; stellaris_enet_update(s); return size; }
static void stellaris_enet_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { stellaris_enet_state *s = (stellaris_enet_state *)opaque; switch (offset) { case 0x00: /* IACK */ s->ris &= ~value; DPRINTF("IRQ ack %02" PRIx64 "/%02x\n", value, s->ris); stellaris_enet_update(s); /* Clearing TXER also resets the TX fifo. */ if (value & SE_INT_TXER) { s->tx_fifo_len = 0; } break; case 0x04: /* IM */ DPRINTF("IRQ mask %02" PRIx64 "/%02x\n", value, s->ris); s->im = value; stellaris_enet_update(s); break; case 0x08: /* RCTL */ s->rctl = value; if (value & SE_RCTL_RSTFIFO) { s->np = 0; s->rx_fifo_offset = 0; stellaris_enet_update(s); } break; case 0x0c: /* TCTL */ s->tctl = value; break; case 0x10: /* DATA */ if (s->tx_fifo_len == 0) { /* The first word is special, it contains the data length */ int framelen = value & 0xffff; if (framelen > 2032) { DPRINTF("TX frame too long (%d)\n", framelen); s->ris |= SE_INT_TXER; stellaris_enet_update(s); break; } } if (s->tx_fifo_len + 4 <= ARRAY_SIZE(s->tx_fifo)) { s->tx_fifo[s->tx_fifo_len++] = value; s->tx_fifo[s->tx_fifo_len++] = value >> 8; s->tx_fifo[s->tx_fifo_len++] = value >> 16; s->tx_fifo[s->tx_fifo_len++] = value >> 24; } if (stellaris_tx_thr_reached(s) && stellaris_txpacket_complete(s)) { stellaris_enet_send(s); } break; case 0x14: /* IA0 */ s->conf.macaddr.a[0] = value; s->conf.macaddr.a[1] = value >> 8; s->conf.macaddr.a[2] = value >> 16; s->conf.macaddr.a[3] = value >> 24; break; case 0x18: /* IA1 */ s->conf.macaddr.a[4] = value; s->conf.macaddr.a[5] = value >> 8; break; case 0x1c: /* THR */ s->thr = value; break; case 0x20: /* MCTL */ s->mctl = value; break; case 0x24: /* MDV */ s->mdv = value; break; case 0x28: /* MADD */ /* ignored. */ break; case 0x2c: /* MTXD */ s->mtxd = value & 0xff; break; case 0x38: /* TR */ if (value & 1) { stellaris_enet_send(s); } break; case 0x30: /* MRXD */ case 0x34: /* NP */ /* Ignored. */ case 0x3c: /* Undocuented: Timestamp? */ /* Ignored. */ break; default: hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); }