qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Davicom DM9000 emulation


From: Daniel Silverstone
Subject: [Qemu-devel] [PATCH] Davicom DM9000 emulation
Date: Mon, 13 Oct 2008 11:12:23 +0100

Hi,

Attached is a patch which provides Davicom DM9000E emulation support
including the ability to compile in a pcap-style network trace of the
device.

It is the first step along the way to getting a large number of Simtec
Electronics boards emulated (ARM based) and also therefore a step
towards other Samsung ARM9 SOC based systems. Future patches will bring
in more devices, and also ARM920T support, leading to an S3C2410x
emulation and then later S3C2440.

Regards,

Daniel.

-- 
Daniel Silverstone                              http://www.simtec.co.uk/
PGP mail accepted and encouraged.            Key Id: 2BC8 4016 2068 7895

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 <address@hidden>
Signed-off-by: Vincent Sanders <address@hidden>

 Makefile.target |    3 
 hw/dm9000.c     |  658 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/dm9000.h     |   28 ++
 3 files changed, 689 insertions(+)

=== modified file 'Makefile.target'
--- Makefile.target     2008-10-13 03:14:31 +0000
+++ Makefile.target     2008-10-13 09:53:20 +0000
@@ -540,6 +540,9 @@
 OBJS += rtl8139.o
 OBJS += e1000.o
 
+# MMIO mapped network cards
+OBJS += dm9000.o
+
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
 OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o

=== added file 'hw/dm9000.c'
--- hw/dm9000.c 1970-01-01 00:00:00 +0000
+++ hw/dm9000.c 2008-10-13 10:07:17 +0000
@@ -0,0 +1,658 @@
+/* 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 <string.h>
+#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 */
+
+/* Comment this out if you don't want a packet dump */
+/* #define DM9000_DUMP_FILENAME "/tmp/dm9k_dump" */
+
+#ifdef DM9000_DEBUG
+#define DM9000_DBF(X...) fprintf(stderr, X)
+#else
+#define DM9000_DBF(X...) if(0) fprintf(stderr, X)
+#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 {
+    uint32_t addr; /* address port */
+    uint32_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;
+
+
+#ifdef DM9000_DUMP_FILENAME
+#include <arpa/inet.h>
+static uint8_t pcap_header[24] = {
+    0xA1, 0xB2, 0xC3, 0xD4, /* TCPDUMP Magic */
+    0x00, 0x02, 0x00, 0x04, /* Major 2, Minor 4 */
+    0x00, 0x00, 0x00, 0x00, /* Timezone offset */
+    0x00, 0x00, 0x00, 0x01, /* Accuracy of timestamps */
+    0x00, 0x00, 0x0C, 0x00, /* Snaplen 3KiB */
+    0x00, 0x00, 0x00, 0x01, /* Ethernet frames */
+};
+static uint8_t nulls[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+static void dm9k_dump_packet(uint8_t *buf, uint32_t size)
+{
+    FILE* dm9k_fileh = fopen(DM9000_DUMP_FILENAME, "ab+");
+    unsigned long bsize = htonl(size);
+    DM9000_DBF("Dumping packet at %08x (%d bytes)\n", buf, size);
+    fseek(dm9k_fileh, 0, SEEK_END);
+    if(ftell(dm9k_fileh)==0) fwrite(pcap_header, 1, 24, dm9k_fileh);
+    fwrite(nulls, 1, 8, dm9k_fileh);
+    fwrite(&bsize, 1, 4, dm9k_fileh);
+    fwrite(&bsize, 1, 4, dm9k_fileh);
+    fwrite(buf, 1, size, dm9k_fileh);
+    fclose(dm9k_fileh);
+}
+#else
+#define dm9k_dump_packet(X...) do { } while(0)
+#endif
+
+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;
+    dm9k_dump_packet(state->packet_copy_buffer, state->dm9k_txpl);
+    /* 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;
+    }
+
+    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;
+    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);
+    dm9k_dump_packet(buf, size);
+    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);
+
+}

=== added file 'hw/dm9000.h'
--- hw/dm9000.h 1970-01-01 00:00:00 +0000
+++ hw/dm9000.h 2008-10-13 10:06:18 +0000
@@ -0,0 +1,28 @@
+/* hw/dm9000.h
+ *
+ * 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
+ */
+
+#ifndef QEMU_HW_DM9000_H
+#define QEMU_HW_DM9000_H
+
+void dm9000_init(NICInfo *nd, target_phys_addr_t base_addr, uint32_t 
addr_offset, 
+                 uint32_t data_offset, qemu_irq irq);
+
+#endif


reply via email to

[Prev in Thread] Current Thread [Next in Thread]