--- trunk/src/devices/dev_dec21143.c 2007/10/08 16:19:28 21 +++ trunk/src/devices/dev_dec21143.c 2007/10/08 16:19:37 22 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2006 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,16 +25,29 @@ * SUCH DAMAGE. * * - * $Id: dev_dec21143.c,v 1.16 2005/11/25 03:52:55 debug Exp $ + * $Id: dev_dec21143.c,v 1.24 2006/02/09 20:02:59 debug Exp $ * * DEC 21143 ("Tulip") ethernet controller. Implemented from Intel document * 278074-001 ("21143 PC/CardBus 10/100Mb/s Ethernet LAN Controller") and by * reverse-engineering OpenBSD and NetBSD sources. * + * This device emulates several sub-components: + * + * 21143: This is the actual ethernet controller. + * + * MII: The "physical" network interface. + * + * SROM: A ROM area containing setting such as which MAC address to + * use, and info about the MII. * - * TODO: * - * o) Endianness for descriptors... + * TODO: + * o) Handle _writes_ to MII registers. + * o) Make it work with modern Linux kernels (as a guest OS). + * o) Endianness for descriptors? If necessary. + * o) Actually handle the "Setup" packet. + * o) MAC filtering on incoming packets. + * o) Don't hardcode as many values. */ #include @@ -117,6 +130,10 @@ /* * dec21143_rx(): + * + * Receive a packet. (If there is no current packet, then check for newly + * arrived ones. If the current packet couldn't be fully transfered the + * last time, then continue on that packet.) */ int dec21143_rx(struct cpu *cpu, struct dec21143_data *d) { @@ -135,10 +152,21 @@ net_ethernet_rx(d->net, d, &d->cur_rx_buf, &d->cur_rx_buf_len); + /* Append a 4 byte CRC: */ + d->cur_rx_buf_len += 4; + d->cur_rx_buf = realloc(d->cur_rx_buf, d->cur_rx_buf_len); + if (d->cur_rx_buf == NULL) { + fatal("dec21143_rx(): out of memory\n"); + exit(1); + } + /* Well... the CRC is just zeros, for now. */ + memset(d->cur_rx_buf + d->cur_rx_buf_len - 4, 0, 4); + d->cur_rx_offset = 0; } /* fatal("{ dec21143_rx: base = 0x%08x }\n", (int)addr); */ + addr &= 0x7fffffff; if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t), MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { @@ -181,9 +209,9 @@ d->cur_rx_addr += 4 * sizeof(uint32_t); } - /* fatal("{ RX (%llx): %08x %08x %x %x: buf %i bytes at 0x%x }\n", - (long long)addr, rdes0, rdes1, rdes2, rdes3, bufsize, - (int)bufaddr); */ + debug("{ RX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n", + (long long)addr, rdes0, rdes1, rdes2, rdes3, bufsize, (int)bufaddr); + bufaddr &= 0x7fffffff; /* Turn off all status bits, and give up ownership: */ rdes0 = 0x00000000; @@ -210,8 +238,8 @@ if (d->cur_rx_offset >= d->cur_rx_buf_len) { rdes0 |= TDSTAT_Rx_LS; - /* Frame len, which includes the size of a 4-byte CRC: */ - rdes0 |= ((d->cur_rx_buf_len + 4) << 16) & TDSTAT_Rx_FL; + /* Set the frame length: */ + rdes0 |= (d->cur_rx_buf_len << 16) & TDSTAT_Rx_FL; /* Frame too long? (1518 is max ethernet frame length) */ if (d->cur_rx_buf_len > 1518) @@ -249,6 +277,9 @@ /* * dec21143_tx(): + * + * Transmit a packet, if the guest OS has marked a descriptor as containing + * data to transmit. */ int dec21143_tx(struct cpu *cpu, struct dec21143_data *d) { @@ -257,7 +288,7 @@ uint32_t tdes0, tdes1, tdes2, tdes3; int bufsize, buf1_size, buf2_size, i, writeback_len = 4; - /* fatal("{ dec21143_tx: base = 0x%08x }\n", (int)addr); */ + addr &= 0x7fffffff; if (!cpu->memory_rw(cpu, cpu->mem, addr, descr, sizeof(uint32_t), MEM_READ, PHYSICAL | NO_EXCEPTIONS)) { @@ -267,6 +298,9 @@ tdes0 = descr[0] + (descr[1]<<8) + (descr[2]<<16) + (descr[3]<<24); + /* fatal("{ dec21143_tx: base=0x%08x, tdes0=0x%08x }\n", + (int)addr, (int)tdes0); */ + /* Only process packets owned by the 21143: */ if (!(tdes0 & TDSTAT_OWN)) { if (d->tx_idling > d->tx_idling_threshold) { @@ -304,9 +338,9 @@ d->cur_tx_addr += 4 * sizeof(uint32_t); } - /* fatal("{ TX (%llx): %08x %08x %x %x: buf %i bytes at 0x%x }\n", - (long long)addr, tdes0, tdes1, tdes2, tdes3, bufsize, - (int)bufaddr); */ + fatal("{ TX (%llx): 0x%08x 0x%08x 0x%x 0x%x: buf %i bytes at 0x%x }\n", + (long long)addr, tdes0, tdes1, tdes2, tdes3, bufsize, (int)bufaddr); + bufaddr &= 0x7fffffff; /* Assume no error: */ tdes0 &= ~ (TDSTAT_Tx_UF | TDSTAT_Tx_EC | TDSTAT_Tx_LC @@ -379,6 +413,9 @@ d->cur_tx_buf = NULL; d->cur_tx_buf_len = 0; + /* TODO: Shouldn't the OWN bit be cleared on all + kinds of segments, not just the Last? */ + /* We are done. */ tdes0 &= ~TDSTAT_OWN; writeback_len = 1; @@ -621,7 +658,7 @@ * * This function handles reads from the Ethernet Address ROM. This is not a * 100% correct implementation, as it was reverse-engineered from OpenBSD - * sources; it seems to work with OpenBSD, NetBSD, and Linux. + * sources; it seems to work with OpenBSD, NetBSD, and Linux, though. * * Each transfer (if I understood this correctly) is of the following format: * @@ -785,9 +822,7 @@ /* * dev_dec21143_access(): */ -int dev_dec21143_access(struct cpu *cpu, struct memory *mem, - uint64_t relative_addr, unsigned char *data, size_t len, - int writeflag, void *extra) +DEVICE_ACCESS(dec21143) { struct dec21143_data *d = extra; uint64_t idata = 0, odata = 0; @@ -893,7 +928,7 @@ d->reg[CSR_STATUS/8] &= ~STATUS_RS; } idata &= ~(OPMODE_HBD | OPMODE_SCR | OPMODE_PCS - | OPMODE_PS | OPMODE_SF | OPMODE_TTM); + | OPMODE_PS | OPMODE_SF | OPMODE_TTM | OPMODE_FD); if (idata & OPMODE_PNIC_IT) { idata &= ~OPMODE_PNIC_IT; d->tx_idling = d->tx_idling_threshold; @@ -948,10 +983,7 @@ } -/* - * devinit_dec21143(): - */ -int devinit_dec21143(struct devinit *devinit) +DEVINIT(dec21143) { struct dec21143_data *d; char name2[100];