From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KuPfe-0003lm-G2 for qemu-devel@nongnu.org; Mon, 27 Oct 2008 06:50:02 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KuPfd-0003kX-3B for qemu-devel@nongnu.org; Mon, 27 Oct 2008 06:50:01 -0400 Received: from [199.232.76.173] (port=45194 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KuPfc-0003kE-O1 for qemu-devel@nongnu.org; Mon, 27 Oct 2008 06:50:00 -0400 Received: from batfish.pepperfish.net ([87.237.62.180]:42961 helo=flounder.pepperfish.net) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1KuPfc-0007pb-Cf for qemu-devel@nongnu.org; Mon, 27 Oct 2008 06:50:00 -0400 Received: from cpc2-asht1-0-0-cust815.manc.cable.ntl.com ([80.5.55.48] helo=[10.19.3.101]) by flounder.pepperfish.net with esmtpsa (Exim 4.68 #1 (Debian)) id 1KuPfZ-0007JK-Dl for ; Mon, 27 Oct 2008 10:49:57 +0000 From: Daniel Silverstone Content-Type: multipart/mixed; boundary="=-xjs9wBv6xXGSSU538c6b" Date: Mon, 27 Oct 2008 10:49:56 +0000 Message-Id: <1225104596.24465.3.camel@petitemort> Mime-Version: 1.0 Subject: [Qemu-devel] [PATCH] DM9000 network card driver Reply-To: dsilvers@simtec.co.uk, qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --=-xjs9wBv6xXGSSU538c6b Content-Type: text/plain Content-Transfer-Encoding: 7bit Hi, I think this fell through the cracks, so here it is again. The Davicom DM9000 10/100 Ethernet controller. This driver supplies an MMIO based DM9000 interface which can be instantiated in the emulation's memory map to provide an ethernet interface for ARM "local-bus" type systems. Signed-off-by: Daniel Silverstone Signed-off-by: Vincent Sanders I believe I covered all previously raised objections. Regards, Daniel. -- Daniel Silverstone http://www.simtec.co.uk/ PGP mail accepted and encouraged. Key Id: 2BC8 4016 2068 7895 --=-xjs9wBv6xXGSSU538c6b Content-Disposition: attachment; filename=dm9000.patch Content-Type: text/x-patch; name=dm9000.patch; charset=UTF-8 Content-Transfer-Encoding: 7bit The Davicom DM9000 10/100 Ethernet controller. This driver supplies an MMIO based DM9000 interface which can be instantiated in the emulation's memory map to provide an ethernet interface for ARM "local-bus" type systems. Signed-off-by: Daniel Silverstone Signed-off-by: Vincent Sanders Makefile.target | 2 hw/devices.h | 7 hw/dm9000.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 639 insertions(+), 1 deletion(-) === modified file 'Makefile.target' --- Makefile.target 2008-10-13 03:14:31 +0000 +++ Makefile.target 2008-10-13 14:59:29 +0000 @@ -599,7 +599,7 @@ ifeq ($(TARGET_BASE_ARCH), arm) OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o -OBJS+= versatile_pci.o ptimer.o +OBJS+= versatile_pci.o ptimer.o dm9000.o OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o OBJS+= pl061.o === modified file 'hw/devices.h' --- hw/devices.h 2008-06-09 00:03:13 +0000 +++ hw/devices.h 2008-10-13 14:14:44 +0000 @@ -3,6 +3,13 @@ /* Devices that have nowhere better to go. */ +/* dm9000.c */ +#ifdef NEED_CPU_H +/* This ifdef is present because target_phys_addr_t comes from cpu-defs.h */ +void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, uint32_t addr_offset, + uint32_t data_offset, qemu_irq irq); +#endif + /* smc91c111.c */ void smc91c111_init(NICInfo *, uint32_t, qemu_irq); === added file 'hw/dm9000.c' --- hw/dm9000.c 1970-01-01 00:00:00 +0000 +++ hw/dm9000.c 2008-10-13 15:05:45 +0000 @@ -0,0 +1,631 @@ +/* hw/dm9000.c + * + * DM9000 Ethernet interface + * + * Copyright 2006, 2008 Daniel Silverstone and Vincent Sanders + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include "qemu-common.h" +#include "hw/irq.h" +#include "net.h" + +/* Comment this out if you don't want register debug on stderr */ +/* #define DM9000_DEBUG */ + +#ifdef DM9000_DEBUG +#define DM9000_DBF(X...) do { fprintf(stderr, X); } while(0) +#else +#define DM9000_DBF(X...) do { if (0) fprintf(stderr, X); } while (0) +#endif + +#define DM9000_REG_NCR 0x00 +#define DM9000_REG_NSR 0x01 +#define DM9000_REG_TCR 0x02 +#define DM9000_REG_TSR1 0x03 +#define DM9000_REG_TSR2 0x04 +#define DM9000_REG_RCR 0x05 +#define DM9000_REG_RSR 0x06 +#define DM9000_REG_ROCR 0x07 +#define DM9000_REG_BPTR 0x08 +#define DM9000_REG_FCTR 0x09 +#define DM9000_REG_FCR 0x0A +#define DM9000_REG_EPCR 0x0B +#define DM9000_REG_EPAR 0x0C +#define DM9000_REG_EPDRL 0x0D +#define DM9000_REG_EPDRH 0x0E +#define DM9000_REG_WCR 0x0F +#define DM9000_REG_PAR0 0x10 +#define DM9000_REG_PAR1 0x11 +#define DM9000_REG_PAR2 0x12 +#define DM9000_REG_PAR3 0x13 +#define DM9000_REG_PAR4 0x14 +#define DM9000_REG_PAR5 0x15 +#define DM9000_REG_MAR0 0x16 +#define DM9000_REG_MAR1 0x17 +#define DM9000_REG_MAR2 0x18 +#define DM9000_REG_MAR3 0x19 +#define DM9000_REG_MAR4 0x1A +#define DM9000_REG_MAR5 0x1B +#define DM9000_REG_MAR6 0x1C +#define DM9000_REG_MAR7 0x1D +#define DM9000_REG_GPCR 0x1E +#define DM9000_REG_GPR 0x1F +#define DM9000_REG_TRPAL 0x22 +#define DM9000_REG_TRPAH 0x23 +#define DM9000_REG_RWPAL 0x24 +#define DM9000_REG_RWPAH 0x25 +#define DM9000_REG_VIDL 0x28 +#define DM9000_REG_VIDH 0x29 +#define DM9000_REG_PIDL 0x2A +#define DM9000_REG_PIDH 0x2B +#define DM9000_REG_CHIPR 0x2C +#define DM9000_REG_SMCR 0x2F +#define DM9000_REG_MRCMDX 0xF0 +#define DM9000_REG_MRCMD 0xF2 +#define DM9000_REG_MRRL 0xF4 +#define DM9000_REG_MRRH 0xF5 +#define DM9000_REG_MWCMDX 0xF6 +#define DM9000_REG_MWCMD 0xF8 +#define DM9000_REG_MWRL 0xFA +#define DM9000_REG_MWRH 0xFB +#define DM9000_REG_TXPLL 0xFC +#define DM9000_REG_TXPLH 0xFD +#define DM9000_REG_ISR 0xFE +#define DM9000_REG_IMR 0xFF + +#define DM9000_NCR_RESET 0x01 +#define DM9000_NSR_TX1END 0x04 +#define DM9000_NSR_TX2END 0x08 +#define DM9000_TCR_TXREQ 0x01 + +#define DM9000_IMR_AUTOWRAP 0x80 + +#define DM9000_MII_READ 0x0C +#define DM9000_MII_WRITE 0x0A + +#define DM9000_MII_REG_BMCR 0x00 +#define DM9000_MII_REG_STATUS 0x01 +#define DM9000_MII_REG_PHYID1 0x02 +#define DM9000_MII_REG_PHYID2 0x03 +#define DM9000_MII_REG_ANAR 0x04 +#define DM9000_MII_REG_ANLPAR 0x05 +#define DM9000_MII_REG_ANER 0x06 +#define DM9000_MII_REG_DSCR 0x10 +#define DM9000_MII_REG_DSCSR 0x11 +#define DM9000_MII_REG_10BTCSR 0x12 + + +typedef struct { + target_phys_addr_t addr; /* address port */ + target_phys_addr_t data; /* data port */ + VLANClientState *vc; + qemu_irq irq; + uint8_t macaddr[6]; + uint8_t multihash[8]; /* multicast hash table */ + uint8_t address; /* The internal magial address */ + uint8_t packet_buffer[16 * 1024]; + uint16_t dm9k_mrr, dm9k_mwr; /* Read and write address registers */ + uint16_t dm9k_txpl; /* TX packet length */ + uint16_t dm9k_trpa, dm9k_rwpa; /* TX Read ptr address, RX write ptr address */ + uint8_t dm9k_imr, dm9k_isr; /* Interrupt mask register and status register*/ + uint8_t dm9k_ncr, dm9k_nsr; /* Network control register, network status register */ + uint8_t dm9k_wcr; /* Wakeup control */ + uint8_t dm9k_tcr; /* Transmission control register */ + uint8_t packet_copy_buffer[3 * 1024]; /* packet copy buffer */ + unsigned int packet_index:1; /* 0 == packet I, 1 == packet II */ + + /* Internal MII PHY state */ + uint8_t dm9k_epcr; /* EEPROM/PHY control register */ + uint8_t dm9k_epar; /* EEPROM/PHY address register */ + uint16_t dm9k_epdr; /* EEPROM/PHY data register */ + /* MII Regs */ + uint16_t dm9k_mii_bmcr; + uint16_t dm9k_mii_anar; + uint16_t dm9k_mii_dscr; +} dm9000_state; + +static void dm9000_raise_irq(dm9000_state *state) +{ + int level = ((state->dm9k_isr & state->dm9k_imr) & 0x03) != 0; + DM9000_DBF("DM9000: Set IRQ level %d\n", level); + qemu_set_irq(state->irq, level); +} + +static void dm9000_soft_reset_mii(dm9000_state *state) +{ + state->dm9k_mii_bmcr = 0x3100; /* 100Mbps, AUTONEG, FULL DUPLEX */ + state->dm9k_mii_anar = 0x01E1; + state->dm9k_mii_dscr = 0x0410; +} + +static void dm9000_soft_reset(dm9000_state *state) +{ + DM9000_DBF("DM9000: Soft Reset\n"); + state->dm9k_mrr = state->dm9k_mwr = state->dm9k_txpl = state->dm9k_trpa = 0x0000; + state->dm9k_rwpa = 0x0C04; + state->dm9k_imr = 0; + state->dm9k_isr = 0; /* 16 bit mode, no interrupts asserted */ + state->dm9k_tcr = 0; + state->packet_index = 0; + memset(state->packet_buffer, 0, 16*1024); + memset(state->packet_copy_buffer, 0, 3*1024); + /* These registers have some bits "unaffected by software reset" */ + /* Clear the reset bits */ + state->dm9k_ncr &= 0xA0; + state->dm9k_nsr &= 0xD0; + /* Claim full duplex */ + state->dm9k_ncr |= 1<<3; + /* Set link status to 1 */ + state->dm9k_nsr |= 1<<6; + /* dm9k_wcr is unaffected or reserved, never reset */ + /* MII control regs */ + state->dm9k_epcr = 0x00; + state->dm9k_epar = 0x40; + /* reset the MII */ + dm9000_soft_reset_mii(state); + dm9000_raise_irq(state); /* Clear any potentially pending IRQ */ +} + +static void dm9000_hard_reset(dm9000_state *state) +{ + state->dm9k_ncr = 0x00; + state->dm9k_nsr = 0x00; + state->dm9k_wcr = 0x00; + dm9000_soft_reset(state); +} + +static void dm9000_do_transmit(dm9000_state *state) +{ + uint16_t idx, cnt, tptr; + idx = state->dm9k_trpa; + cnt = state->dm9k_txpl; + tptr = 0; + if( cnt > 3*1024 ) cnt = 3*1024; /* HARD CAP AT 3KiB */ + DM9000_DBF("TX_Packet: %d bytes from %04x\n", cnt, idx); + while(cnt--) { + state->packet_copy_buffer[tptr++] = state->packet_buffer[idx++]; + if( idx == 0x0C00 ) idx = 0; + } + /* DM9KNOTE: Assumes 16bit wiring */ + idx = (idx+1) & ~1; /* Round up to nearest 16bit boundary */ + if( idx == 0x0C00 ) idx = 0; + state->dm9k_trpa = idx; + /* We have the copy buffer, now we do the transmit */ + qemu_send_packet(state->vc, state->packet_copy_buffer, state->dm9k_txpl); + /* Clear the "please xmit" bit */ + state->dm9k_tcr &= ~DM9000_TCR_TXREQ; + /* Set the TXEND bit */ + state->dm9k_nsr |= 1<<(2+state->packet_index); + DM9000_DBF("TX: NSR=%02x PI=%d\n", state->dm9k_nsr, state->packet_index); + /* Claim a TX complete IRQ */ + state->dm9k_isr |= 0x02; /* Packet transmitted latch */ + /* And flip the next-packet bit */ + state->packet_index = !state->packet_index; + dm9000_raise_irq(state); +} + +static void dm9000_mii_read(dm9000_state *state) +{ + int mii_reg = (state->dm9k_epar) & 0x3f; + uint16_t ret = 0; + switch(mii_reg) { + case DM9000_MII_REG_BMCR: + ret = state->dm9k_mii_bmcr; + break; + case DM9000_MII_REG_STATUS: + ret = 0x782D; /* No 100/T4, Can 100/FD, Can 100/HD, Can 10/FD, Can 10/HD, + * No Preamble suppression, Autoneg complete, No remote fault, + * Can autoneg, link up, no jabber, extended capability */ + break; + case DM9000_MII_REG_PHYID1: + ret = 0x0181; + break; + case DM9000_MII_REG_PHYID2: + ret = 0xB8C0; + break; + case DM9000_MII_REG_ANAR: + ret = state->dm9k_mii_anar; + break; + case DM9000_MII_REG_ANLPAR: + ret = 0x0400; + break; + case DM9000_MII_REG_ANER: + ret = 0x0001; + break; + case DM9000_MII_REG_DSCR: + ret = state->dm9k_mii_dscr; + break; + case DM9000_MII_REG_DSCSR: + ret = 0xF008; + break; + case DM9000_MII_REG_10BTCSR: + ret = 0x7800; + } + state->dm9k_epdr = ret; + DM9000_DBF("DM9000:MIIPHY: Read of MII reg %d gives %04x\n", mii_reg, state->dm9k_epdr); +} + +static void dm9000_mii_write(dm9000_state *state) +{ + int mii_reg = (state->dm9k_epar) & 0x3f; + DM9000_DBF("DM9000:MIIPHY: Write of MII reg %d value %04x\n", mii_reg, state->dm9k_epdr); + switch(mii_reg) { + case DM9000_MII_REG_BMCR: + state->dm9k_mii_bmcr = (state->dm9k_epdr &~0x8000); + if( state->dm9k_epdr & 0x8000 ) dm9000_soft_reset_mii(state); + break; + case DM9000_MII_REG_ANAR: + state->dm9k_mii_anar = state->dm9k_epdr; + break; + case DM9000_MII_REG_DSCR: + state->dm9k_mii_dscr = state->dm9k_epdr & ~0x0008; + break; + } +} + +static void dm9000_write(void *opaque, target_phys_addr_t address, + uint32_t value) +{ + dm9000_state *state = (dm9000_state *)opaque; +#ifdef DM9000_DEBUG + int suppress_debug = 0; +#endif + + if (address == state->addr) { + if( (value != DM9000_REG_MRCMD) && + (value != DM9000_REG_MWCMD) ) + DM9000_DBF("DM9000: Address set to 0x%02x\n", value); + state->address = value; + return; + } + + if (address != state->data) { + DM9000_DBF("DM9000: Write to location which is neither data nor address port: " TARGET_FMT_plx "\n", address); + } + + switch(state->address) { + case DM9000_REG_NCR: + state->dm9k_ncr = value & 0xDF; + if (state->dm9k_ncr & DM9000_NCR_RESET) + dm9000_soft_reset(state); + break; + case DM9000_REG_NSR: + state->dm9k_nsr &= ~(value & 0x2C); + break; + case DM9000_REG_TCR: + state->dm9k_tcr = value & 0xFF; + if( value & DM9000_TCR_TXREQ ) dm9000_do_transmit(state); + break; + case DM9000_REG_EPCR: + state->dm9k_epcr = value & 0xFF; + if( value & DM9000_MII_READ ) + dm9000_mii_read(state); + else if( value & DM9000_MII_WRITE ) + dm9000_mii_write(state); + break; + case DM9000_REG_EPAR: + state->dm9k_epar = value & 0xFF; + break; + case DM9000_REG_EPDRL: + state->dm9k_epdr &= 0xFF00; + state->dm9k_epdr |= value & 0xFF; + break; + case DM9000_REG_EPDRH: + state->dm9k_epdr &= 0xFF; + state->dm9k_epdr |= (value & 0xFF) << 8; + break; + case DM9000_REG_PAR0: + case DM9000_REG_PAR1: + case DM9000_REG_PAR2: + case DM9000_REG_PAR3: + case DM9000_REG_PAR4: + case DM9000_REG_PAR5: + state->macaddr[state->address - DM9000_REG_PAR0] = value & 0xFF; + break; + case DM9000_REG_MAR0: + case DM9000_REG_MAR1: + case DM9000_REG_MAR2: + case DM9000_REG_MAR3: + case DM9000_REG_MAR4: + case DM9000_REG_MAR5: + case DM9000_REG_MAR6: + case DM9000_REG_MAR7: + /* multicast hash setup */ + state->multihash[state->address - DM9000_REG_MAR0] = value & 0xFF; + break; + case DM9000_REG_MRRL: + state->dm9k_mrr &= 0xFF00; + state->dm9k_mrr |= value & 0xFF; + break; + case DM9000_REG_MRRH: + state->dm9k_mrr &= 0xFF; + state->dm9k_mrr |= (value & 0xFF) << 8; + break; + case DM9000_REG_MWCMDX: + case DM9000_REG_MWCMD: + /* DM9KNOTE: This assumes a 16bit wide wiring */ + state->packet_buffer[state->dm9k_mwr] = value & 0xFF; + state->packet_buffer[state->dm9k_mwr+1] = (value >> 8) & 0xFF; + if( state->address == DM9000_REG_MWCMD ) { + state->dm9k_mwr += 2; + if( state->dm9k_imr & DM9000_IMR_AUTOWRAP ) + if( state->dm9k_mwr >= 0x0C00 ) + state->dm9k_mwr -= 0x0C00; + } +#ifdef DM9000_DEBUG + suppress_debug = 1; +#endif + break; + case DM9000_REG_MWRL: + state->dm9k_mwr &= 0xFF00; + state->dm9k_mwr |= value & 0xFF; + break; + case DM9000_REG_MWRH: + state->dm9k_mwr &= 0xFF; + state->dm9k_mwr |= (value & 0xFF) << 8; + break; + case DM9000_REG_TXPLL: + state->dm9k_txpl &= 0xFF00; + state->dm9k_txpl |= value & 0xFF; + break; + case DM9000_REG_TXPLH: + state->dm9k_txpl &= 0xFF; + state->dm9k_txpl |= (value & 0xFF) << 8; + break; + case DM9000_REG_ISR: + state->dm9k_isr &= ~(value & 0x0F); + dm9000_raise_irq(state); + break; + case DM9000_REG_IMR: + if( !(state->dm9k_imr & DM9000_IMR_AUTOWRAP) && + (value & DM9000_IMR_AUTOWRAP) ) + state->dm9k_mrr = 0x0C00 | (state->dm9k_mrr & 0xFF); + state->dm9k_imr = value & 0xFF; + dm9000_raise_irq(state); + break; + } +#ifdef DM9000_DEBUG + if(!suppress_debug) DM9000_DBF("DM9000: Write value %04x\n", value); +#endif +} + +static uint32_t dm9000_read(void *opaque, target_phys_addr_t address) +{ + dm9000_state *state = (dm9000_state *)opaque; + uint32_t ret = 0; +#ifdef DM9000_DEBUG + int suppress_debug = 0; +#endif + + if (address == state->addr) + return state->address; + + if (address != state->data) { + DM9000_DBF("DM9000: Read from location which is neither data nor address port: " TARGET_FMT_plx "\n", address); + } + + switch(state->address) { + case DM9000_REG_NCR: + ret = state->dm9k_ncr; + break; + case DM9000_REG_NSR: + ret = state->dm9k_nsr; + /* Note, TX1END and TX2END are *CLEAR ON READ* */ + state->dm9k_nsr &= ~(DM9000_NSR_TX1END | DM9000_NSR_TX2END); + break; + case DM9000_REG_TCR: + ret = state->dm9k_tcr; + break; + case DM9000_REG_TSR1: + case DM9000_REG_TSR2: + ret = 0x00; /* No error, yay! */ + break; + case DM9000_REG_EPCR: + ret = state->dm9k_epcr; + break; + case DM9000_REG_EPAR: + ret = state->dm9k_epar; + break; + case DM9000_REG_EPDRL: + ret = state->dm9k_epdr & 0xFF; + break; + case DM9000_REG_EPDRH: + ret = (state->dm9k_epdr >> 8) & 0xFF; + break; + case DM9000_REG_PAR0: + case DM9000_REG_PAR1: + case DM9000_REG_PAR2: + case DM9000_REG_PAR3: + case DM9000_REG_PAR4: + case DM9000_REG_PAR5: + ret = state->macaddr[state->address - DM9000_REG_PAR0]; + break; + case DM9000_REG_MAR0: + case DM9000_REG_MAR1: + case DM9000_REG_MAR2: + case DM9000_REG_MAR3: + case DM9000_REG_MAR4: + case DM9000_REG_MAR5: + case DM9000_REG_MAR6: + case DM9000_REG_MAR7: + /* multicast hash */ + ret = state->multihash[state->address - DM9000_REG_MAR0]; + break; + case DM9000_REG_TRPAL: + ret = state->dm9k_trpa & 0xFF; + break; + case DM9000_REG_TRPAH: + ret = state->dm9k_trpa >> 8; + break; + case DM9000_REG_RWPAL: + ret = state->dm9k_rwpa & 0xFF; + break; + case DM9000_REG_RWPAH: + ret = state->dm9k_rwpa >> 8; + break; + case DM9000_REG_VIDL: + ret = 0x46; + break; + case DM9000_REG_VIDH: + ret = 0x0A; + break; + case DM9000_REG_PIDL: + ret = 0x00; + break; + case DM9000_REG_PIDH: + ret = 0x90; + break; + case DM9000_REG_CHIPR: + ret = 0x00; + break; + case DM9000_REG_MRCMDX: + case DM9000_REG_MRCMD: + /* DM9KNOTE: This assumes a 16bit wide wiring */ + ret = state->packet_buffer[state->dm9k_mrr]; + ret |= state->packet_buffer[state->dm9k_mrr+1] << 8; + if( state->address == DM9000_REG_MRCMD ) { + state->dm9k_mrr += 2; + if( state->dm9k_mrr >= (16*1024) ) state->dm9k_mrr -= (16*1024); + if( state->dm9k_imr & DM9000_IMR_AUTOWRAP ) + if( state->dm9k_mrr < 0x0C00 ) + state->dm9k_mrr += 0x0C00; + } +#ifdef DM9000_DEBUG + if (state->address==DM9000_REG_MRCMD) + suppress_debug = 1; +#endif + break; + case DM9000_REG_MRRL: + ret = state->dm9k_mrr & 0xFF; + break; + case DM9000_REG_MRRH: + ret = state->dm9k_mrr >> 8; + break; + case DM9000_REG_MWRL: + ret = state->dm9k_mwr & 0xFF; + break; + case DM9000_REG_MWRH: + ret = state->dm9k_mwr >> 8; + break; + case DM9000_REG_TXPLL: + ret = state->dm9k_txpl & 0xFF; + break; + case DM9000_REG_TXPLH: + ret = state->dm9k_txpl >> 8; + break; + case DM9000_REG_ISR: + ret = state->dm9k_isr; + break; + case DM9000_REG_IMR: + ret = state->dm9k_imr; + break; + default: + ret = 0; + } + +#ifdef DM9000_DEBUG + if(!suppress_debug) DM9000_DBF("DM9000: Read gives: %04x\n", ret); +#endif + return ret; +} + + + +static int dm9000_can_receive(void *opaque) +{ + dm9000_state *state = (dm9000_state *)opaque; + uint16_t rx_space; + if( state->dm9k_rwpa < state->dm9k_mrr ) + rx_space = state->dm9k_mrr - state->dm9k_rwpa; + else + rx_space = (13*1024) - (state->dm9k_rwpa - state->dm9k_mrr); + DM9000_DBF("DM9000:RX_Packet: Asked about RX, rwpa=%d mrr=%d => space is %d bytes\n", + state->dm9k_rwpa, state->dm9k_mrr, rx_space); + if (rx_space > 2048) return 1; + return 0; +} + +static void dm9000_receive(void *opaque, const uint8_t *buf, int size) +{ + dm9000_state *state = (dm9000_state *)opaque; + uint16_t rxptr = state->dm9k_rwpa; + uint8_t magic_padding = 4; + if( size > 2048 ) return; /* La La La, I can't hear you */ + /* Fill out the magical header structure */ + DM9000_DBF("DM9000:RX_Packet: %d bytes into buffer at %04x\n", size, rxptr); + if( size < 64 ) magic_padding += (64 - size); + DM9000_DBF("DM9000:RX_Packet: Magical padding is %d bytes\n", magic_padding); + size += magic_padding; /* The magical CRC word */ + state->packet_buffer[state->dm9k_rwpa-4] = 0x01; /* Packet read */ + state->packet_buffer[state->dm9k_rwpa-3] = 0x00; /* Status OK */ + state->packet_buffer[state->dm9k_rwpa-2] = size & 0xFF; /* Size LOW */ + state->packet_buffer[state->dm9k_rwpa-1] = (size & 0xFF00)>>8; /* Size HIGH */ + size += 4; /* The magical next header (which we zero for fun) */ + while(size--) { + if( size > (magic_padding + 3) ) + state->packet_buffer[rxptr++] = *buf++; + else + state->packet_buffer[rxptr++] = 0x00; /* Clear to the next header */ + /* DM9KNOTE: Assumes 16 bit wired config */ + if (size == 4) rxptr = (rxptr+1) & ~1; /* At end of packet, realign */ + if( rxptr >= (16*1024) ) rxptr -= (16*1024); + if( rxptr < 0x0C00 ) rxptr += 0x0C00; + } + state->dm9k_rwpa = rxptr; + state->dm9k_isr |= 0x01; /* RX interrupt, yay */ + dm9000_raise_irq(state); +} + + +static CPUReadMemoryFunc *dm9000_readfn[] = { + dm9000_read, + dm9000_read, + dm9000_read +}; + +static CPUWriteMemoryFunc *dm9000_writefn[] = { + dm9000_write, + dm9000_write, + dm9000_write +}; + +/* initialises a dm9000 ethernet controller + * The dm9k has a single 16bit wide address and data port through which all + * operations are multiplexed, there is a single IRQ + */ +void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, + uint32_t addr_offset, uint32_t data_offset, + qemu_irq irq) +{ + dm9000_state *state; + int iomemtype; + + state = (dm9000_state *)qemu_mallocz(sizeof(dm9000_state)); + iomemtype = cpu_register_io_memory(0, dm9000_readfn, + dm9000_writefn, state); + cpu_register_physical_memory(base_addr, MAX(addr_offset, data_offset) + 4, iomemtype); + state->addr = base_addr + addr_offset; + state->data = base_addr + data_offset; + state->irq = irq; + memcpy(state->macaddr, nd->macaddr, 6); + + dm9000_hard_reset(state); + + state->vc = qemu_new_vlan_client(nd->vlan, dm9000_receive, + dm9000_can_receive, state); +} --=-xjs9wBv6xXGSSU538c6b--