Hi!

lance.c emulation implements incorrect algorithm to scan receive/transmit descriptors:
 - it scans a whole ring of descriptors instead of stopping at first owned by host
 - it skips buffers in corner cases
 - card is not reset, current rx/tx descriptor number is not reset when card is stopped by driver

Attached patch should fix these problems.
On-demand packet transmission is also implemented.

Without this patch I had ~500 byte/second transfers with qemu-system-sparc,
now it network speed is much better :)

Looking at 7990 chip docs (main chip on lance card) it seems like there are more features
not implemented; e.g. 1.6ms timer is needed to implement "normal" transmission.

Index: hw/lance.c
===================================================================
RCS file: /cvsroot/qemu/qemu/hw/lance.c,v
retrieving revision 1.9
diff -u -r1.9 lance.c
--- hw/lance.c	9 Aug 2006 22:38:19 -0000	1.9
+++ hw/lance.c	9 Aug 2006 23:14:37 -0000
@@ -21,6 +21,20 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+
+/*
+ * Modifications:
+ *  2006-Aug-10  Igor Kovalenko :   Corrected STOP procedure to reset chip
+ *                                  Corrected transmission routine to stop ring polling
+ *                                  if OWN bit is not set in current descriptor
+ *                                  Implemented TDMD bit of CSR0 for transmit on demand
+ *                                  More debug statements in routines
+ *
+ * TODO list:
+ *      Implement block chaining for send and receive rings.
+ *      lance_can_receive() : implement checking if card owns current rx ring descriptor
+ *
+ */
 #include "vl.h"
 
 /* debug LANCE card */
@@ -176,6 +190,8 @@
     memset(s->regs, 0, LE_NREGS * 2);
     s->regs[LE_CSR0] = LE_C0_STOP;
     memset(s->ledmaregs, 0, LEDMA_REGS * 4);
+
+    DPRINTF("done reset\n");
 }
 
 static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr)
@@ -204,6 +220,7 @@
     LANCEState *s = opaque;
     uint32_t saddr;
     uint16_t reg;
+    int lance_transmit_on_demand = 0;
 
     saddr = addr & LE_MAXREG;
     switch (saddr >> 1) {
@@ -212,13 +229,23 @@
         switch (s->addr) {
         case LE_CSR0:
             if (val & LE_C0_STOP) {
-                s->regs[LE_CSR0] = LE_C0_STOP;
+                DPRINTF("CSR0 write STOP: chip reset\n");
+                lance_reset(s);
                 break;
             }
 
+            // Transmit demand bit
+            if (val & LE_C0_TDMD)
+            {
+                DPRINTF("CSR0 write %08x : TDMD transmit demand\n", val);
+                lance_transmit_on_demand = 1;
+
+                // this bit is automatically cleared, see below
+            }
+
             reg = s->regs[LE_CSR0];
 
-            // 1 = clear for some bits
+            // 1 = clear for some bits, including TDMD
             reg &= ~(val & 0x7f00);
 
             // generated bits
@@ -265,7 +292,12 @@
         DPRINTF("write unknown(%d) = %4.4x\n", saddr >> 1, val);
         break;
     }
-    lance_send(s);
+
+    if (lance_transmit_on_demand)
+    {
+        DPRINTF("=== Transmit on demand\n");
+        lance_send(s);
+    }
 }
 
 static CPUReadMemoryFunc *lance_mem_read[3] = {
@@ -285,6 +317,16 @@
 
 static int lance_can_receive(void *opaque)
 {
+    LANCEState *s = opaque;
+
+    if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
+    {
+        DPRINTF("lance_can_receive() : STOP\n");
+        return 1;
+    }
+
+    // TODO: Need to check if card owns current rx ring descriptor
+
     return 1;
 }
 
@@ -299,33 +341,47 @@
 
     DPRINTF("receive size %d\n", size);
     if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
+    {
+        DPRINTF("lance_receive() : STOP\n");
         return;
+    }
 
     ib = (void *) iommu_translate(dmaptr);
 
+    DPRINTF("--- lance_receive --- scanning...\n");
+
     old_rxptr = s->rxptr;
     for (i = s->rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK);
          i = (i + 1) & RX_RING_MOD_MASK) {
         cpu_physical_memory_read((uint32_t) & ib->brx_ring[i].rmd1_bits,
                                  (void *) &temp8, 1);
-        if (temp8 == (LE_R1_OWN)) {
-            s->rxptr = (s->rxptr + 1) & RX_RING_MOD_MASK;
-            temp16 = size + 4;
-            bswap16s(&temp16);
-            cpu_physical_memory_write((uint32_t) & ib->brx_ring[i].
-                                      mblength, (void *) &temp16, 2);
-            cpu_physical_memory_write((uint32_t) & ib->rx_buf[i], buf,
-                                      size);
-            temp8 = LE_R1_POK;
-            cpu_physical_memory_write((uint32_t) & ib->brx_ring[i].
-                                      rmd1_bits, (void *) &temp8, 1);
-            s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR;
-            if (s->regs[LE_CSR0] & LE_C0_INEA)
-                pic_set_irq(s->irq, 1);
-            DPRINTF("got packet, len %d\n", size);
-            return;
+
+        if (!(temp8 & LE_R1_OWN))
+        {
+            DPRINTF("### lance_receive() : OWN not set RMD1 bits=%02x in rx descriptor %u\n", temp8, i);
+            break;
         }
+
+        // TODO: implement receive buffer chaining
+
+        s->rxptr = (s->rxptr + 1) & RX_RING_MOD_MASK;
+        temp16 = size + 4;
+        bswap16s(&temp16);
+        cpu_physical_memory_write((uint32_t) & ib->brx_ring[i].
+                                  mblength, (void *) &temp16, 2);
+        cpu_physical_memory_write((uint32_t) & ib->rx_buf[i], buf,
+                                  size);
+        temp8 = LE_R1_POK;
+        cpu_physical_memory_write((uint32_t) & ib->brx_ring[i].
+                                  rmd1_bits, (void *) &temp8, 1);
+        s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR;
+        DPRINTF("got packet, len %d\n", size);
+        if (s->regs[LE_CSR0] & LE_C0_INEA)
+            pic_set_irq(s->irq, 1);
+        break;
     }
+
+    DPRINTF("lance_receive() : leave\n");
 }
 
 static void lance_send(void *opaque)
@@ -340,35 +396,82 @@
 
     DPRINTF("sending packet? (csr0 %4.4x)\n", s->regs[LE_CSR0]);
     if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP)
+    {
+        DPRINTF("lance_send() : STOP\n");
         return;
+    }
 
     ib = (void *) iommu_translate(dmaptr);
 
     DPRINTF("sending packet? (dmaptr %8.8x) (ib %p) (btx_ring %p)\n",
             dmaptr, ib, &ib->btx_ring);
     old_txptr = s->txptr;
-    for (i = s->txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK);
-         i = (i + 1) & TX_RING_MOD_MASK) {
+
+    // transmit descriptor polling loop
+    // terminates ONLY if lance does not own current transmit descriptor
+    while (1)
+    {
+        i = s->txptr;
+
         cpu_physical_memory_read((uint32_t) & ib->btx_ring[i].tmd1_bits,
                                  (void *) &temp8, 1);
-        if (temp8 == (LE_T1_POK | LE_T1_OWN)) {
+
+        if (!(temp8 & LE_T1_OWN))
+        {
+            DPRINTF("### OWN bit not set in tx descriptor %u : stop polling\n", i);
+            break;
+        }
+        else if (temp8 == (LE_T1_POK|LE_T1_OWN))
+        {
+            // TODO: NODE this is a probe for complete packet in single tx buffer
+
+            DPRINTF("+++ OWN bit set in tx descriptor %u\n", i);
             cpu_physical_memory_read((uint32_t) & ib->btx_ring[i].length,
                                      (void *) &temp16, 2);
             bswap16s(&temp16);
             temp16 = (~temp16) + 1;
             cpu_physical_memory_read((uint32_t) & ib->tx_buf[i], pkt_buf,
                                      temp16);
-            DPRINTF("sending packet, len %d\n", temp16);
-            qemu_send_packet(s->vc, pkt_buf, temp16);
             temp8 = LE_T1_POK;
             cpu_physical_memory_write((uint32_t) & ib->btx_ring[i].
                                       tmd1_bits, (void *) &temp8, 1);
+
+            // advance to next transmit descriptor
             s->txptr = (s->txptr + 1) & TX_RING_MOD_MASK;
             s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR;
+
+            // notify about complete packet transmission
+            if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
+            {
+                DPRINTF("--- lance_send +++ raising interrupt for TINT\n");
+                pic_set_irq(s->irq, 1);
+            }
+
+            // actually send packet a bit later, to prevent possible recursion (tx->rx->tx)
+            DPRINTF("--- lance_send +++ sending packet, len %d\n", temp16);
+            qemu_send_packet(s->vc, pkt_buf, temp16);
+        }
+        else
+        {
+            DPRINTF("### not implemented buffer chaining, flags %02x in tx descriptor %u\n", temp8, i);
+
+            // TODO: must handle buffer chaining
+
+            temp8 = 0;
+
+            cpu_physical_memory_write((uint32_t)&ib->btx_ring[i].tmd1_bits, (void *) &temp8, 1);
+            s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR;
+
+            // notify about complete packet transmission
+            if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
+            {
+                DPRINTF("--- lance_send +++ raising interrupt for TINT\n");
+                pic_set_irq(s->irq, 1);
+            }
         }
     }
-    if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA))
-        pic_set_irq(s->irq, 1);
+
+    DPRINTF("lance_send() leave\n");
 }
 
 static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr)
_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to