qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation


From: Daniel Silverstone
Subject: Re: [Qemu-devel] [PATCH] Davicom DM9000 emulation
Date: Mon, 13 Oct 2008 16:13:02 +0100

On Mon, 2008-10-13 at 15:53 +0100, Paul Brook wrote:
> > The implementation only allows for the two registers to be up to a
> > 32-bit address space apart, but it allows for the base of the device to
> > be at any part of the target's physical address space. 
> No it doesn't.
> +typedef struct {
> +    uint32_t addr; /* address port */
> [...]
> +    state->addr = base_addr + addr_offset;
> Will truncate the address to 32 bits.

Argh, missed that bit when I changed it to target_phys_addr_t -- well
spotted. I've fixed up addr and data to be in target_phys_addr_t which
also appeals to my semantic sensibilities.

> On closer inspection I notice that the data member of the state structure is 
> never used,

It was intended for debug, I think I lost the debug at one point. I've
reinstantated the debug which uses it. In theory I could predicate the
presence in the struct on the debug macro if you feel it's worthwhile.

>  and the device will respond to any accesses within the range you 
> reserve, not just the data address.

This was intentional based on the physical implementation of the device.

The chip has a CMD/nDATA pin, which is typically attached to some
address decode logic which results in CMD being high when the address
port is selected, and low when it is not. Then access to the chip is
predicated on the standard nCS type line.

Attached is a patch which correct the struct entries to be
target_phys_addr_t and reinstates the debug on accesses to anywhere
other than the address and data ports.

Thank you for taking the time to go through these patches. I am working
on the others.

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 |    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 <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 */
+
+#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);
+}


reply via email to

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