static void dp8393x_watchdog(void *opaque) { dp8393xState *s = opaque; if (s->regs[SONIC_CR] & SONIC_CR_STP) { return; } s->regs[SONIC_WT1] = 0xffff; s->regs[SONIC_WT0] = 0xffff; dp8393x_set_next_tick(s); /* Signal underflow */ s->regs[SONIC_ISR] |= SONIC_ISR_TC; dp8393x_update_irq(s); }
static void do_load_cam(dp8393xState *s) { uint16_t data[8]; int width, size; uint16_t index = 0; width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; while (s->regs[SONIC_CDC] & 0x1f) { /* Fill current entry */ s->memory_rw(s->mem_opaque, (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], (uint8_t *)data, size, 0); s->cam[index][0] = data[1 * width] & 0xff; s->cam[index][1] = data[1 * width] >> 8; s->cam[index][2] = data[2 * width] & 0xff; s->cam[index][3] = data[2 * width] >> 8; s->cam[index][4] = data[3 * width] & 0xff; s->cam[index][5] = data[3 * width] >> 8; DPRINTF("load cam[%d] with %02x%02x%02x%02x%02x%02x\n", index, s->cam[index][0], s->cam[index][1], s->cam[index][2], s->cam[index][3], s->cam[index][4], s->cam[index][5]); /* Move to next entry */ s->regs[SONIC_CDC]--; s->regs[SONIC_CDP] += size; index++; } /* Read CAM enable */ s->memory_rw(s->mem_opaque, (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_CDP], (uint8_t *)data, size, 0); s->regs[SONIC_CE] = data[0 * width]; DPRINTF("load cam done. cam enable mask 0x%04x\n", s->regs[SONIC_CE]); /* Done */ s->regs[SONIC_CR] &= ~SONIC_CR_LCAM; s->regs[SONIC_ISR] |= SONIC_ISR_LCD; dp8393x_update_irq(s); }
static void do_read_rra(dp8393xState *s) { uint16_t data[8]; int width, size; /* Read memory */ width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; size = sizeof(uint16_t) * 4 * width; s->memory_rw(s->mem_opaque, (s->regs[SONIC_URRA] << 16) | s->regs[SONIC_RRP], (uint8_t *)data, size, 0); /* Update SONIC registers */ s->regs[SONIC_CRBA0] = data[0 * width]; s->regs[SONIC_CRBA1] = data[1 * width]; s->regs[SONIC_RBWC0] = data[2 * width]; s->regs[SONIC_RBWC1] = data[3 * width]; DPRINTF("CRBA0/1: 0x%04x/0x%04x, RBWC0/1: 0x%04x/0x%04x\n", s->regs[SONIC_CRBA0], s->regs[SONIC_CRBA1], s->regs[SONIC_RBWC0], s->regs[SONIC_RBWC1]); /* Go to next entry */ s->regs[SONIC_RRP] += size; /* Handle wrap */ if (s->regs[SONIC_RRP] == s->regs[SONIC_REA]) { s->regs[SONIC_RRP] = s->regs[SONIC_RSA]; } /* Check resource exhaustion */ if (s->regs[SONIC_RRP] == s->regs[SONIC_RWP]) { s->regs[SONIC_ISR] |= SONIC_ISR_RBE; dp8393x_update_irq(s); } /* Done */ s->regs[SONIC_CR] &= ~SONIC_CR_RRRA; }
static void nic_reset(void *opaque) { dp8393xState *s = opaque; qemu_del_timer(s->watchdog); s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; s->regs[SONIC_IMR] = 0; s->regs[SONIC_ISR] = 0; s->regs[SONIC_DCR2] = 0; s->regs[SONIC_EOBC] = 0x02F8; s->regs[SONIC_RSC] = 0; s->regs[SONIC_CE] = 0; s->regs[SONIC_RSC] = 0; /* Network cable is connected */ s->regs[SONIC_RCR] |= SONIC_RCR_CRS; dp8393x_update_irq(s); }
static void dp8393x_reset(DeviceState *dev) { dp8393xState *s = DP8393X(dev); timer_del(s->watchdog); memset(s->regs, 0, sizeof(s->regs)); s->regs[SONIC_CR] = SONIC_CR_RST | SONIC_CR_STP | SONIC_CR_RXDIS; s->regs[SONIC_DCR] &= ~(SONIC_DCR_EXBUS | SONIC_DCR_LBR); s->regs[SONIC_RCR] &= ~(SONIC_RCR_LB0 | SONIC_RCR_LB1 | SONIC_RCR_BRD | SONIC_RCR_RNT); s->regs[SONIC_TCR] |= SONIC_TCR_NCRS | SONIC_TCR_PTX; s->regs[SONIC_TCR] &= ~SONIC_TCR_BCM; s->regs[SONIC_IMR] = 0; s->regs[SONIC_ISR] = 0; s->regs[SONIC_DCR2] = 0; s->regs[SONIC_EOBC] = 0x02F8; s->regs[SONIC_RSC] = 0; s->regs[SONIC_CE] = 0; s->regs[SONIC_RSC] = 0; /* Network cable is connected */ s->regs[SONIC_RCR] |= SONIC_RCR_CRS; dp8393x_update_irq(s); }
static ssize_t dp8393x_receive(NetClientState *nc, const uint8_t * buf, size_t size) { dp8393xState *s = qemu_get_nic_opaque(nc); uint16_t data[10]; int packet_type; uint32_t available, address; int width, rx_len = size; uint32_t checksum; width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; s->regs[SONIC_RCR] &= ~(SONIC_RCR_PRX | SONIC_RCR_LBK | SONIC_RCR_FAER | SONIC_RCR_CRCR | SONIC_RCR_LPKT | SONIC_RCR_BC | SONIC_RCR_MC); packet_type = dp8393x_receive_filter(s, buf, size); if (packet_type < 0) { DPRINTF("packet not for netcard\n"); return -1; } /* XXX: Check byte ordering */ /* Check for EOL */ if (s->regs[SONIC_LLFA] & 0x1) { /* Are we still in resource exhaustion? */ size = sizeof(uint16_t) * 1 * width; address = dp8393x_crda(s) + sizeof(uint16_t) * 5 * width; address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); if (data[0 * width] & 0x1) { /* Still EOL ; stop reception */ return -1; } else { s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; } } /* Save current position */ s->regs[SONIC_TRBA1] = s->regs[SONIC_CRBA1]; s->regs[SONIC_TRBA0] = s->regs[SONIC_CRBA0]; /* Calculate the ethernet checksum */ checksum = cpu_to_le32(crc32(0, buf, rx_len)); /* Put packet into RBA */ DPRINTF("Receive packet at %08x\n", dp8393x_crba(s)); address = dp8393x_crba(s); address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, (uint8_t *)buf, rx_len, 1); address += rx_len; address_space_rw(&s->as, address, MEMTXATTRS_UNSPECIFIED, (uint8_t *)&checksum, 4, 1); rx_len += 4; s->regs[SONIC_CRBA1] = address >> 16; s->regs[SONIC_CRBA0] = address & 0xffff; available = dp8393x_rbwc(s); available -= rx_len / 2; s->regs[SONIC_RBWC1] = available >> 16; s->regs[SONIC_RBWC0] = available & 0xffff; /* Update status */ if (dp8393x_rbwc(s) < s->regs[SONIC_EOBC]) { s->regs[SONIC_RCR] |= SONIC_RCR_LPKT; } s->regs[SONIC_RCR] |= packet_type; s->regs[SONIC_RCR] |= SONIC_RCR_PRX; if (s->loopback_packet) { s->regs[SONIC_RCR] |= SONIC_RCR_LBK; s->loopback_packet = 0; } /* Write status to memory */ DPRINTF("Write status at %08x\n", dp8393x_crda(s)); data[0 * width] = s->regs[SONIC_RCR]; /* status */ data[1 * width] = rx_len; /* byte count */ data[2 * width] = s->regs[SONIC_TRBA0]; /* pkt_ptr0 */ data[3 * width] = s->regs[SONIC_TRBA1]; /* pkt_ptr1 */ data[4 * width] = s->regs[SONIC_RSC]; /* seq_no */ size = sizeof(uint16_t) * 5 * width; address_space_rw(&s->as, dp8393x_crda(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); /* Move to next descriptor */ size = sizeof(uint16_t) * width; address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 5 * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_LLFA] = data[0 * width]; if (s->regs[SONIC_LLFA] & 0x1) { /* EOL detected */ s->regs[SONIC_ISR] |= SONIC_ISR_RDE; } else { data[0 * width] = 0; /* in_use */ address_space_rw(&s->as, dp8393x_crda(s) + sizeof(uint16_t) * 6 * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint16_t), 1); s->regs[SONIC_CRDA] = s->regs[SONIC_LLFA]; s->regs[SONIC_ISR] |= SONIC_ISR_PKTRX; s->regs[SONIC_RSC] = (s->regs[SONIC_RSC] & 0xff00) | (((s->regs[SONIC_RSC] & 0x00ff) + 1) & 0x00ff); if (s->regs[SONIC_RCR] & SONIC_RCR_LPKT) { /* Read next RRA */ dp8393x_do_read_rra(s); } } /* Done */ dp8393x_update_irq(s); return size; }
static void dp8393x_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { dp8393xState *s = opaque; int reg = addr >> s->it_shift; DPRINTF("write 0x%04x to reg %s\n", (uint16_t)data, reg_names[reg]); switch (reg) { /* Command register */ case SONIC_CR: dp8393x_do_command(s, data); break; /* Prevent write to read-only registers */ case SONIC_CAP2: case SONIC_CAP1: case SONIC_CAP0: case SONIC_SR: case SONIC_MDT: DPRINTF("writing to reg %d invalid\n", reg); break; /* Accept write to some registers only when in reset mode */ case SONIC_DCR: if (s->regs[SONIC_CR] & SONIC_CR_RST) { s->regs[reg] = data & 0xbfff; } else { DPRINTF("writing to DCR invalid\n"); } break; case SONIC_DCR2: if (s->regs[SONIC_CR] & SONIC_CR_RST) { s->regs[reg] = data & 0xf017; } else { DPRINTF("writing to DCR2 invalid\n"); } break; /* 12 lower bytes are Read Only */ case SONIC_TCR: s->regs[reg] = data & 0xf000; break; /* 9 lower bytes are Read Only */ case SONIC_RCR: s->regs[reg] = data & 0xffe0; break; /* Ignore most significant bit */ case SONIC_IMR: s->regs[reg] = data & 0x7fff; dp8393x_update_irq(s); break; /* Clear bits by writing 1 to them */ case SONIC_ISR: data &= s->regs[reg]; s->regs[reg] &= ~data; if (data & SONIC_ISR_RBE) { dp8393x_do_read_rra(s); } dp8393x_update_irq(s); if (dp8393x_can_receive(s->nic->ncs)) { qemu_flush_queued_packets(qemu_get_queue(s->nic)); } break; /* Ignore least significant bit */ case SONIC_RSA: case SONIC_REA: case SONIC_RRP: case SONIC_RWP: s->regs[reg] = data & 0xfffe; break; /* Invert written value for some registers */ case SONIC_CRCT: case SONIC_FAET: case SONIC_MPT: s->regs[reg] = data ^ 0xffff; break; /* All other registers have no special contrainst */ default: s->regs[reg] = data; } if (reg == SONIC_WT0 || reg == SONIC_WT1) { dp8393x_set_next_tick(s); } }
static void dp8393x_do_transmit_packets(dp8393xState *s) { NetClientState *nc = qemu_get_queue(s->nic); uint16_t data[12]; int width, size; int tx_len, len; uint16_t i; width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1; while (1) { /* Read memory */ size = sizeof(uint16_t) * 6 * width; s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA]; DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s)); address_space_rw(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); tx_len = 0; /* Update registers */ s->regs[SONIC_TCR] = data[0 * width] & 0xf000; s->regs[SONIC_TPS] = data[1 * width]; s->regs[SONIC_TFC] = data[2 * width]; s->regs[SONIC_TSA0] = data[3 * width]; s->regs[SONIC_TSA1] = data[4 * width]; s->regs[SONIC_TFS] = data[5 * width]; /* Handle programmable interrupt */ if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) { s->regs[SONIC_ISR] |= SONIC_ISR_PINT; } else { s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT; } for (i = 0; i < s->regs[SONIC_TFC]; ) { /* Append fragment */ len = s->regs[SONIC_TFS]; if (tx_len + len > sizeof(s->tx_buffer)) { len = sizeof(s->tx_buffer) - tx_len; } address_space_rw(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED, &s->tx_buffer[tx_len], len, 0); tx_len += len; i++; if (i != s->regs[SONIC_TFC]) { /* Read next fragment details */ size = sizeof(uint16_t) * 3 * width; address_space_rw(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * i) * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_TSA0] = data[0 * width]; s->regs[SONIC_TSA1] = data[1 * width]; s->regs[SONIC_TFS] = data[2 * width]; } } /* Handle Ethernet checksum */ if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) { /* Don't append FCS there, to look like slirp packets * which don't have one */ } else { /* Remove existing FCS */ tx_len -= 4; } if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) { /* Loopback */ s->regs[SONIC_TCR] |= SONIC_TCR_CRSL; if (nc->info->can_receive(nc)) { s->loopback_packet = 1; nc->info->receive(nc, s->tx_buffer, tx_len); } } else { /* Transmit packet */ qemu_send_packet(nc, s->tx_buffer, tx_len); } s->regs[SONIC_TCR] |= SONIC_TCR_PTX; /* Write status */ data[0 * width] = s->regs[SONIC_TCR] & 0x0fff; /* status */ size = sizeof(uint16_t) * width; address_space_rw(&s->as, dp8393x_ttda(s), MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 1); if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) { /* Read footer of packet */ size = sizeof(uint16_t) * width; address_space_rw(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * (4 + 3 * s->regs[SONIC_TFC]) * width, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, size, 0); s->regs[SONIC_CTDA] = data[0 * width] & ~0x1; if (data[0 * width] & 0x1) { /* EOL detected */ break; } } } /* Done */ s->regs[SONIC_CR] &= ~SONIC_CR_TXP; s->regs[SONIC_ISR] |= SONIC_ISR_TXDN; dp8393x_update_irq(s); }
static void write_register(dp8393xState *s, int reg, uint16_t val) { DPRINTF("write 0x%04x to reg %s\n", val, reg_names[reg]); switch (reg) { /* Command register */ case SONIC_CR: do_command(s, val);; break; /* Prevent write to read-only registers */ case SONIC_CAP2: case SONIC_CAP1: case SONIC_CAP0: case SONIC_SR: case SONIC_MDT: DPRINTF("writing to reg %d invalid\n", reg); break; /* Accept write to some registers only when in reset mode */ case SONIC_DCR: if (s->regs[SONIC_CR] & SONIC_CR_RST) { s->regs[reg] = val & 0xbfff; } else { DPRINTF("writing to DCR invalid\n"); } break; case SONIC_DCR2: if (s->regs[SONIC_CR] & SONIC_CR_RST) { s->regs[reg] = val & 0xf017; } else { DPRINTF("writing to DCR2 invalid\n"); } break; /* 12 lower bytes are Read Only */ case SONIC_TCR: s->regs[reg] = val & 0xf000; break; /* 9 lower bytes are Read Only */ case SONIC_RCR: s->regs[reg] = val & 0xffe0; break; /* Ignore most significant bit */ case SONIC_IMR: s->regs[reg] = val & 0x7fff; dp8393x_update_irq(s); break; /* Clear bits by writing 1 to them */ case SONIC_ISR: val &= s->regs[reg]; s->regs[reg] &= ~val; if (val & SONIC_ISR_RBE) { do_read_rra(s); } dp8393x_update_irq(s); break; /* Ignore least significant bit */ case SONIC_RSA: case SONIC_REA: case SONIC_RRP: case SONIC_RWP: s->regs[reg] = val & 0xfffe; break; /* Invert written value for some registers */ case SONIC_CRCT: case SONIC_FAET: case SONIC_MPT: s->regs[reg] = val ^ 0xffff; break; /* All other registers have no special contrainst */ default: s->regs[reg] = val; } if (reg == SONIC_WT0 || reg == SONIC_WT1) { set_next_tick(s); } }