]> Git Repo - qemu.git/blobdiff - hw/lance.c
openpic: Unfold write_IRQreg
[qemu.git] / hw / lance.c
index 25ad8c45b2a973607c7813dc6556934413f5e70c..d83e7f57a9161741165043491d43c2f4ef8cb8a3 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * QEMU Lance emulation
- * 
- * Copyright (c) 2003-2004 Fabrice Bellard
- * 
+ * QEMU AMD PC-Net II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include "vl.h"
-
-/* debug LANCE card */
-//#define DEBUG_LANCE
-
-#ifndef LANCE_LOG_TX_BUFFERS
-#define LANCE_LOG_TX_BUFFERS 4
-#define LANCE_LOG_RX_BUFFERS 4
-#endif
-
-#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */
-#define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
-
-
-#define LE_CSR0 0
-#define LE_CSR1 1
-#define LE_CSR2 2
-#define LE_CSR3 3
-#define LE_MAXREG (LE_CSR3 + 1)
-
-#define LE_RDP  0
-#define LE_RAP  1
-
-#define LE_MO_PROM      0x8000  /* Enable promiscuous mode */
-
-#define        LE_C0_ERR       0x8000  /* Error: set if BAB, SQE, MISS or ME is set */
-#define        LE_C0_BABL      0x4000  /* BAB:  Babble: tx timeout. */
-#define        LE_C0_CERR      0x2000  /* SQE:  Signal quality error */
-#define        LE_C0_MISS      0x1000  /* MISS: Missed a packet */
-#define        LE_C0_MERR      0x0800  /* ME:   Memory error */
-#define        LE_C0_RINT      0x0400  /* Received interrupt */
-#define        LE_C0_TINT      0x0200  /* Transmitter Interrupt */
-#define        LE_C0_IDON      0x0100  /* IFIN: Init finished. */
-#define        LE_C0_INTR      0x0080  /* Interrupt or error */
-#define        LE_C0_INEA      0x0040  /* Interrupt enable */
-#define        LE_C0_RXON      0x0020  /* Receiver on */
-#define        LE_C0_TXON      0x0010  /* Transmitter on */
-#define        LE_C0_TDMD      0x0008  /* Transmitter demand */
-#define        LE_C0_STOP      0x0004  /* Stop the card */
-#define        LE_C0_STRT      0x0002  /* Start the card */
-#define        LE_C0_INIT      0x0001  /* Init the card */
-
-#define        LE_C3_BSWP      0x4     /* SWAP */
-#define        LE_C3_ACON      0x2     /* ALE Control */
-#define        LE_C3_BCON      0x1     /* Byte control */
-
-/* Receive message descriptor 1 */
-#define LE_R1_OWN       0x80    /* Who owns the entry */
-#define LE_R1_ERR       0x40    /* Error: if FRA, OFL, CRC or BUF is set */
-#define LE_R1_FRA       0x20    /* FRA: Frame error */
-#define LE_R1_OFL       0x10    /* OFL: Frame overflow */
-#define LE_R1_CRC       0x08    /* CRC error */
-#define LE_R1_BUF       0x04    /* BUF: Buffer error */
-#define LE_R1_SOP       0x02    /* Start of packet */
-#define LE_R1_EOP       0x01    /* End of packet */
-#define LE_R1_POK       0x03    /* Packet is complete: SOP + EOP */
-
-#define LE_T1_OWN       0x80    /* Lance owns the packet */
-#define LE_T1_ERR       0x40    /* Error summary */
-#define LE_T1_EMORE     0x10    /* Error: more than one retry needed */
-#define LE_T1_EONE      0x08    /* Error: one retry needed */
-#define LE_T1_EDEF      0x04    /* Error: deferred */
-#define LE_T1_SOP       0x02    /* Start of packet */
-#define LE_T1_EOP       0x01    /* End of packet */
-#define LE_T1_POK      0x03    /* Packet is complete: SOP + EOP */
-
-#define LE_T3_BUF       0x8000  /* Buffer error */
-#define LE_T3_UFL       0x4000  /* Error underflow */
-#define LE_T3_LCOL      0x1000  /* Error late collision */
-#define LE_T3_CLOS      0x0800  /* Error carrier loss */
-#define LE_T3_RTY       0x0400  /* Error retry */
-#define LE_T3_TDR       0x03ff  /* Time Domain Reflectometry counter */
-
-#define TX_RING_SIZE                   (1 << (LANCE_LOG_TX_BUFFERS))
-#define TX_RING_MOD_MASK               (TX_RING_SIZE - 1)
-#define TX_RING_LEN_BITS               ((LANCE_LOG_TX_BUFFERS) << 29)
-
-#define RX_RING_SIZE                   (1 << (LANCE_LOG_RX_BUFFERS))
-#define RX_RING_MOD_MASK               (RX_RING_SIZE - 1)
-#define RX_RING_LEN_BITS               ((LANCE_LOG_RX_BUFFERS) << 29)
-
-#define PKT_BUF_SZ             1544
-#define RX_BUFF_SIZE            PKT_BUF_SZ
-#define TX_BUFF_SIZE            PKT_BUF_SZ
 
-struct lance_rx_desc {
-       unsigned short rmd0;        /* low address of packet */
-       unsigned char  rmd1_bits;   /* descriptor bits */
-       unsigned char  rmd1_hadr;   /* high address of packet */
-       short    length;            /* This length is 2s complement (negative)!
-                                    * Buffer length
-                                    */
-       unsigned short mblength;    /* This is the actual number of bytes received */
-};
-
-struct lance_tx_desc {
-       unsigned short tmd0;        /* low address of packet */
-       unsigned char  tmd1_bits;   /* descriptor bits */
-       unsigned char  tmd1_hadr;   /* high address of packet */
-       short length;               /* Length is 2s complement (negative)! */
-       unsigned short misc;
-};
-
-/* The LANCE initialization block, described in databook. */
-/* On the Sparc, this block should be on a DMA region     */
-struct lance_init_block {
-       unsigned short mode;            /* Pre-set mode (reg. 15) */
-       unsigned char phys_addr[6];     /* Physical ethernet address */
-       unsigned filter[2];             /* Multicast filter. */
-
-       /* Receive and transmit ring base, along with extra bits. */
-       unsigned short rx_ptr;          /* receive descriptor addr */
-       unsigned short rx_len;          /* receive len and high addr */
-       unsigned short tx_ptr;          /* transmit descriptor addr */
-       unsigned short tx_len;          /* transmit len and high addr */
-    
-       /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */
-       struct lance_rx_desc brx_ring[RX_RING_SIZE];
-       struct lance_tx_desc btx_ring[TX_RING_SIZE];
-    
-       char   tx_buf [TX_RING_SIZE][TX_BUFF_SIZE];
-       char   pad[2];                  /* align rx_buf for copy_and_sum(). */
-       char   rx_buf [RX_RING_SIZE][RX_BUFF_SIZE];
-};
-
-#define LEDMA_REGS 4
-#if 0
-/* Structure to describe the current status of DMA registers on the Sparc */
-struct sparc_dma_registers {
-    uint32_t cond_reg; /* DMA condition register */
-    uint32_t st_addr;  /* Start address of this transfer */
-    uint32_t cnt;      /* How many bytes to transfer */
-    uint32_t dma_test; /* DMA test register */
-};
-#endif
-
-typedef struct LEDMAState {
-    uint32_t addr;
-    uint32_t regs[LEDMA_REGS];
-} LEDMAState;
+/* This software was written to be compatible with the specification:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436  Rev:E  Amendment/0  Issue Date: June 2000
+ */
 
-typedef struct LANCEState {
-    uint32_t paddr;
-    NetDriverState *nd;
-    uint32_t leptr;
-    uint16_t addr;
-    uint16_t regs[LE_MAXREG];
-    uint8_t phys[6]; /* mac address */
-    int irq;
-    LEDMAState *ledma;
-} LANCEState;
+/*
+ * On Sparc32, this is the Lance (Am7990) part of chip STP2000 (Master I/O), also
+ * produced as NCR89C100. See
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C100.txt
+ * and
+ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt
+ */
 
-static unsigned int rxptr, txptr;
+#include "sysbus.h"
+#include "net.h"
+#include "qemu-timer.h"
+#include "qemu_socket.h"
+#include "sun4m.h"
+#include "pcnet.h"
+#include "trace.h"
 
-static void lance_send(void *opaque);
+typedef struct {
+    SysBusDevice busdev;
+    PCNetState state;
+} SysBusPCNetState;
 
-static void lance_reset(LANCEState *s)
+static void parent_lance_reset(void *opaque, int irq, int level)
 {
-    memcpy(s->phys, s->nd->macaddr, 6);
-    rxptr = 0;
-    txptr = 0;
-    s->regs[LE_CSR0] = LE_C0_STOP;
+    SysBusPCNetState *d = opaque;
+    if (level)
+        pcnet_h_reset(&d->state);
 }
 
-static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
+static void lance_mem_write(void *opaque, target_phys_addr_t addr,
+                            uint64_t val, unsigned size)
 {
-    LANCEState *s = opaque;
-    uint32_t saddr;
+    SysBusPCNetState *d = opaque;
 
-    saddr = addr - s->paddr;
-    switch (saddr >> 1) {
-    case LE_RDP:
-       return s->regs[s->addr];
-    case LE_RAP:
-       return s->addr;
-    default:
-       break;
-    }
-    return 0;
+    trace_lance_mem_writew(addr, val & 0xffff);
+    pcnet_ioport_writew(&d->state, addr, val & 0xffff);
 }
 
-static void lance_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+static uint64_t lance_mem_read(void *opaque, target_phys_addr_t addr,
+                               unsigned size)
 {
-    LANCEState *s = opaque;
-    uint32_t saddr;
-    uint16_t reg;
-
-    saddr = addr - s->paddr;
-    switch (saddr >> 1) {
-    case LE_RDP:
-       switch(s->addr) {
-       case LE_CSR0:
-           if (val & LE_C0_STOP) {
-               s->regs[LE_CSR0] = LE_C0_STOP;
-               break;
-           }
-
-           reg = s->regs[LE_CSR0];
-
-           // 1 = clear for some bits
-           reg &= ~(val & 0x7f00);
+    SysBusPCNetState *d = opaque;
+    uint32_t val;
 
-           // generated bits
-           reg &= ~(LE_C0_ERR | LE_C0_INTR);
-           if (reg & 0x7100)
-               reg |= LE_C0_ERR;
-           if (reg & 0x7f00)
-               reg |= LE_C0_INTR;
-
-           // direct bit
-           reg &= ~LE_C0_INEA;
-           reg |= val & LE_C0_INEA;
-
-           // exclusive bits
-           if (val & LE_C0_INIT) {
-               reg |= LE_C0_IDON | LE_C0_INIT;
-               reg &= ~LE_C0_STOP;
-           }
-           else if (val & LE_C0_STRT) {
-               reg |= LE_C0_STRT | LE_C0_RXON | LE_C0_TXON;
-               reg &= ~LE_C0_STOP;
-           }
-
-           s->regs[LE_CSR0] = reg;
-
-           // trigger bits
-           //if (val & LE_C0_TDMD)
-
-           if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
-               pic_set_irq(s->irq, 1);
-           break;
-       case LE_CSR1:
-           s->leptr = (s->leptr & 0xffff0000) | (val & 0xffff);
-           s->regs[s->addr] = val;
-           break;
-       case LE_CSR2:
-           s->leptr = (s->leptr & 0xffff) | ((val & 0xffff) << 16);
-           s->regs[s->addr] = val;
-           break;
-       case LE_CSR3:
-           s->regs[s->addr] = val;
-           break;
-       }
-       break;
-    case LE_RAP:
-       if (val < LE_MAXREG)
-           s->addr = val;
-       break;
-    default:
-       break;
-    }
-    lance_send(s);
+    val = pcnet_ioport_readw(&d->state, addr);
+    trace_lance_mem_readw(addr, val & 0xffff);
+    return val & 0xffff;
 }
 
-static CPUReadMemoryFunc *lance_mem_read[3] = {
-    lance_mem_readw,
-    lance_mem_readw,
-    lance_mem_readw,
-};
-
-static CPUWriteMemoryFunc *lance_mem_write[3] = {
-    lance_mem_writew,
-    lance_mem_writew,
-    lance_mem_writew,
+static const MemoryRegionOps lance_mem_ops = {
+    .read = lance_mem_read,
+    .write = lance_mem_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 2,
+        .max_access_size = 2,
+    },
 };
 
-
-/* return the max buffer size if the LANCE can receive more data */
-static int lance_can_receive(void *opaque)
+static void lance_cleanup(VLANClientState *nc)
 {
-    LANCEState *s = opaque;
-    void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]);
-    struct lance_init_block *ib;
-    int i;
-    uint16_t temp;
+    PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque;
 
-    if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
-       return 0;
-
-    ib = (void *) iommu_translate(dmaptr);
-
-    for (i = 0; i < RX_RING_SIZE; i++) {
-       cpu_physical_memory_read(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
-       temp &= 0xff;
-       if (temp == (LE_R1_OWN)) {
-#ifdef DEBUG_LANCE
-           fprintf(stderr, "lance: can receive %d\n", RX_BUFF_SIZE);
-#endif
-           return RX_BUFF_SIZE;
-       }
-    }
-#ifdef DEBUG_LANCE
-    fprintf(stderr, "lance: cannot receive\n");
-#endif
-    return 0;
+    pcnet_common_cleanup(d);
 }
 
-#define MIN_BUF_SIZE 60
-
-static void lance_receive(void *opaque, const uint8_t *buf, int size)
-{
-    LANCEState *s = opaque;
-    void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]);
-    struct lance_init_block *ib;
-    unsigned int i, old_rxptr, j;
-    uint16_t temp;
-
-    if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
-       return;
-
-    ib = (void *) iommu_translate(dmaptr);
+static NetClientInfo net_lance_info = {
+    .type = NET_CLIENT_TYPE_NIC,
+    .size = sizeof(NICState),
+    .can_receive = pcnet_can_receive,
+    .receive = pcnet_receive,
+    .cleanup = lance_cleanup,
+};
 
-    old_rxptr = rxptr;
-    for (i = rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) {
-       cpu_physical_memory_read(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
-       if (temp == (LE_R1_OWN)) {
-           rxptr = (rxptr + 1) & RX_RING_MOD_MASK;
-           temp = size;
-           bswap16s(&temp);
-           cpu_physical_memory_write(&ib->brx_ring[i].mblength, (void *) &temp, 2);
-#if 0
-           cpu_physical_memory_write(&ib->rx_buf[i], buf, size);
-#else
-           for (j = 0; j < size; j++) {
-               cpu_physical_memory_write(((void *)&ib->rx_buf[i]) + j, &buf[j], 1);
-           }
-#endif
-           temp = LE_R1_POK;
-           cpu_physical_memory_write(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1);
-           s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR;
-           if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
-               pic_set_irq(s->irq, 1);
-#ifdef DEBUG_LANCE
-           fprintf(stderr, "lance: got packet, len %d\n", size);
-#endif
-           return;
-       }
+static const VMStateDescription vmstate_lance = {
+    .name = "pcnet",
+    .version_id = 3,
+    .minimum_version_id = 2,
+    .minimum_version_id_old = 2,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT(state, SysBusPCNetState, 0, vmstate_pcnet, PCNetState),
+        VMSTATE_END_OF_LIST()
     }
-}
+};
 
-static void lance_send(void *opaque)
+static int lance_init(SysBusDevice *dev)
 {
-    LANCEState *s = opaque;
-    void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]);
-    struct lance_init_block *ib;
-    unsigned int i, old_txptr, j;
-    uint16_t temp;
-    char pkt_buf[PKT_BUF_SZ];
+    SysBusPCNetState *d = FROM_SYSBUS(SysBusPCNetState, dev);
+    PCNetState *s = &d->state;
 
-    if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
-       return;
+    memory_region_init_io(&s->mmio, &lance_mem_ops, d, "lance-mmio", 4);
 
-    ib = (void *) iommu_translate(dmaptr);
+    qdev_init_gpio_in(&dev->qdev, parent_lance_reset, 1);
 
-    old_txptr = txptr;
-    for (i = txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) {
-       cpu_physical_memory_read(&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1);
-       if (temp == (LE_T1_POK|LE_T1_OWN)) {
-           cpu_physical_memory_read(&ib->btx_ring[i].length, (void *) &temp, 2);
-           bswap16s(&temp);
-           temp = (~temp) + 1;
-#if 0
-           cpu_physical_memory_read(&ib->tx_buf[i], pkt_buf, temp);
-#else
-           for (j = 0; j < temp; j++) {
-               cpu_physical_memory_read(((void *)&ib->tx_buf[i]) + j, &pkt_buf[j], 1);
-           }
-#endif
-
-#ifdef DEBUG_LANCE
-           fprintf(stderr, "lance: sending packet, len %d\n", temp);
-#endif
-           qemu_send_packet(s->nd, pkt_buf, temp);
-           temp = LE_T1_POK;
-           cpu_physical_memory_write(&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1);
-           txptr = (txptr + 1) & TX_RING_MOD_MASK;
-           s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR;
-       }
-    }
-}
+    sysbus_init_mmio_region(dev, &s->mmio);
 
-static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr)
-{
-    LEDMAState *s = opaque;
-    uint32_t saddr;
+    sysbus_init_irq(dev, &s->irq);
 
-    saddr = (addr - s->addr) >> 2;
-    if (saddr < LEDMA_REGS)
-       return s->regs[saddr];
-    else
-       return 0;
+    s->phys_mem_read = ledma_memory_read;
+    s->phys_mem_write = ledma_memory_write;
+    return pcnet_common_init(&dev->qdev, s, &net_lance_info);
 }
 
-static void ledma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+static void lance_reset(DeviceState *dev)
 {
-    LEDMAState *s = opaque;
-    uint32_t saddr;
+    SysBusPCNetState *d = DO_UPCAST(SysBusPCNetState, busdev.qdev, dev);
 
-    saddr = (addr - s->addr) >> 2;
-    if (saddr < LEDMA_REGS)
-       s->regs[saddr] = val;
+    pcnet_h_reset(&d->state);
 }
 
-static CPUReadMemoryFunc *ledma_mem_read[3] = {
-    ledma_mem_readl,
-    ledma_mem_readl,
-    ledma_mem_readl,
-};
-
-static CPUWriteMemoryFunc *ledma_mem_write[3] = {
-    ledma_mem_writel,
-    ledma_mem_writel,
-    ledma_mem_writel,
+static SysBusDeviceInfo lance_info = {
+    .init       = lance_init,
+    .qdev.name  = "lance",
+    .qdev.fw_name  = "ethernet",
+    .qdev.size  = sizeof(SysBusPCNetState),
+    .qdev.reset = lance_reset,
+    .qdev.vmsd  = &vmstate_lance,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_PTR("dma", SysBusPCNetState, state.dma_opaque),
+        DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf),
+        DEFINE_PROP_END_OF_LIST(),
+    }
 };
 
-void lance_init(NetDriverState *nd, int irq, uint32_t leaddr, uint32_t ledaddr)
+static void lance_register_devices(void)
 {
-    LANCEState *s;
-    LEDMAState *led;
-    int lance_io_memory, ledma_io_memory;
-
-    s = qemu_mallocz(sizeof(LANCEState));
-    if (!s)
-        return;
-
-    s->paddr = leaddr;
-    s->nd = nd;
-    s->irq = irq;
-
-    lance_io_memory = cpu_register_io_memory(0, lance_mem_read, lance_mem_write, s);
-    cpu_register_physical_memory(leaddr, 8, lance_io_memory);
-
-    led = qemu_mallocz(sizeof(LEDMAState));
-    if (!led)
-        return;
-
-    s->ledma = led;
-    led->addr = ledaddr;
-    ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, led);
-    cpu_register_physical_memory(ledaddr, 16, ledma_io_memory);
-
-    lance_reset(s);
-    qemu_add_read_packet(nd, lance_can_receive, lance_receive, s);
+    sysbus_register_withprop(&lance_info);
 }
-
+device_init(lance_register_devices)
This page took 0.041028 seconds and 4 git commands to generate.