This is the mail archive of the ecos-discuss@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

MORE TCP if_quicc.c -- various bug fixes


Hi,

Here are the devs/eth/powerpc/quicc/current/src/if_quicc.c changes.
Anonymous CVS version: June 19 2001.

Fixes are for:
	1. mbuf loss and corruption due to asychronous events not handled
	   correctly.
	2. Transmitter stopping because of unhandled errors on the ethernet
	   line (e.g. someone pulls the plug on the physical line and plugs it
	   back in).

(1) happens in the following situation:
	- quicc_eth_send is called to fill the last available slot in the tx
        descriptors (BDS).
	- some time later the first buffer is sent out and the SCC harware
        clears the "R" (ready) bit in the BD.
	- just after this the ethernet driver sees that the buffer is clear,
	  fills the slot with a new mbuf (callit "B2"), and leaves the old one
        (B1) hanging.
	- The interrupt chain eventually gets to the quicc_eth_TxEvent for the
	  first buffer that got sent and so the now the wrong mbuf (B2) gets passed
	  to the ethernet driver and is placed on the free list
	- some time later the "B2" buffer is sent out and the SCC harware
        clears the "R" (ready) bit in the BD.
	- The interrupt chain eventually gets to the quicc_eth_TxEvent for the
	  "B2"  buffer that got sent and so the the mbuf (B2) gets passed
	  to the ethernet driver and is placed on the free list a second time.
	- meanwhile B1 is lost forever...

(2) happens because we don't even look at Tx errors in the BDs or in the
    event register!

Context diff follows, source attached:

+++ if_quicc.c	Wed Jul  4 11:43:47 2001
@@ -333,11 +334,11 @@ quicc_eth_init(struct cyg_netdevtab_entr

     // Clear any pending interrupt/exceptions
     scc->scc_scce = 0xFFFF;

     // Enable interrupts
-    scc->scc_sccm = QUICC_SCCE_INTS;
+    scc->scc_sccm = QUICC_SCCE_INTS | QUICC_SCCE_GRC | QUICC_SCCE_BSY;

     // Set up SCC1 to run in ethernet mode
     scc->scc_gsmr_h = 0;
     scc->scc_gsmr_l = QUICC_SCC_GSML_TCI | QUICC_SCC_GSML_TPL_48 |
         QUICC_SCC_GSML_TPP_01 | QUICC_SCC_GSML_MODE_ENET;
@@ -420,11 +419,11 @@ static int
 quicc_eth_can_send(struct eth_drv_sc *sc)
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info
*)sc->driver_private;
     volatile struct cp_bufdesc *txbd = qi->txbd;

-    return ((txbd->ctrl & QUICC_BD_CTL_Ready) == 0);
+    return ((txbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int /*jdh*/))
== 0);
 }

 //
 // This routine is called to send data to the hardware.
 static void
@@ -434,27 +433,22 @@ quicc_eth_send(struct eth_drv_sc *sc, st
     struct quicc_eth_info *qi = (struct quicc_eth_info
*)sc->driver_private;
     volatile struct cp_bufdesc *txbd, *txfirst;
     volatile char *bp;
     int i, txindex, cache_state;
     unsigned int ctrl;
-
-    // Find a free buffer
+    // Find a free buffer -- it can only be the next one in the list since
the transmitter
+    // never skips BDS.
     txbd = txfirst = qi->txbd;
-    while (txbd->ctrl & QUICC_BD_CTL_Ready) {
-        // This buffer is busy, move to next one
-        if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
-            txbd = qi->tbase;
-        } else {
-            txbd++;
-        }
-        if (txbd == txfirst) {
+    if ((txbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int /*jdh*/)))
+      {
+
 #ifdef CYGPKG_NET
             panic ("No free xmit buffers");
 #else
             printf("QUICC Ethernet: No free xmit buffers\n");
 #endif
-        }
+
     }
     // Remember the next buffer to try
     if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
         qi->txbd = qi->tbase;
     } else {
@@ -537,32 +530,90 @@ quicc_eth_recv(struct eth_drv_sc *sc, st
             bp += sg_list[i].len;
         }
     }
 }

+
+static void
Command( 
+   struct eth_drv_sc *sc,
+   unsigned long cmd)
+{
+   volatile EPPC *eppc = (volatile EPPC *)eppc_base();
+   quicc_eth_stop(sc);
+   eppc->cp_cr = cmd | QUICC_CPM_CR_BUSY;
+
+   while (eppc->cp_cr & QUICC_CPM_CR_BUSY) ;
+
+   quicc_eth_start(sc,NULL,0);
+
+}
+
+
 static void
 quicc_eth_TxEvent(struct eth_dr
v_sc *sc, int stat)
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info
*)sc->driver_private;
     volatile struct cp_bufdesc *txbd;
     int txindex;

+    //We will restart the transmitter and receiver if the rx bds filled up
+    //because the receiver may have got stuck (undocumented 860 feature?)
+    bool Restart = (stat & QUICC_SCCE_BSY) ? true : false;
+
     txbd = qi->tnext;
+
+    if ((stat & QUICC_SCCE_TXE ) &&
+        ((txbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int)) !=
QUICC_BD_CTL_Int)) {
+       //Tx error and buffer not closed: close buffer and move on
+        txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) /
sizeof(*txbd);
+        (sc->funs->eth_drv->tx_done)(sc, qi->txkey[txindex], 0);
+
+        if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
+            txbd->ctrl &= QUICC_BD_CTL_Wrap;
+            txbd = qi->tbase;
+        } else {
+            txbd->ctrl = 0;
+            txbd++;
+
+        }
+    }
+
     while ((txbd->ctrl & (QUICC_BD_CTL_Ready | QUICC_BD_CTL_Int)) ==
QUICC_BD_CTL_Int) {
         txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) /
sizeof(*txbd);
-        txbd->ctrl &= ~QUICC_BD_CTL_Int;  // Reset int pending bit
+        //txbd->ctrl &= ~QUICC_BD_CTL_Int;  // Reset int pending bit NOT
YET!! -- JDH
         (sc->funs->eth_drv->tx_done)(sc, qi->txkey[txindex], 0);
+
+         //Clear the interrupt bit so that when the client tries to place
+         //another buffer in the BD, it will know that we have ackd the
data.
+         //This also prevents us infinite looping or counting buffers that
have
+         //not been entered let alone sent.
+         txbd->ctrl &= ~QUICC_BD_CTL_Int;
+
+         //Must check for transmission errors that cause the transmitter to
stop, and
+         //for those cases re-enable the transmitter
+         if (txbd->ctrl &   (QUICC_BD_TX_LC | QUICC_BD_TX_RL |
QUICC_BD_TX_UN))
+            Restart = true;
+
         if (txbd->ctrl & QUICC_BD_CTL_Wrap) {
+            txbd->ctrl &= QUICC_BD_CTL_Wrap;
             txbd = qi->tbase;
         } else {
+            txbd->ctrl = 0;
             txbd++;
+
         }
     }
     // Remember where we left off
     qi->tnext = (struct cp_bufdesc *)txbd;
+    if (Restart) {
+        CPMCommand(sc,QUICC_CPM_CR_RESTART_TX);
+        CPMCommand(sc,QUICC_CPM_CR_HUNT_MODE);
+    }
+
 }

+
 //
 // Interrupt processing
 //
 static void
 quicc_eth_int(struct eth_drv_sc *sc)
@@ -569,17 +620,24 @@ quicc_eth_int(struct eth_drv_sc *sc)
 {
     struct quicc_eth_info *qi = (struct quicc_eth_info
*)sc->driver_private;
     volatile struct scc_regs *scc = qi->ctl;
     unsigned short scce;

-    while ((scce = (scc->scc_scce & QUICC_SCCE_INTS)) != 0) {
-        if ((scce & (QUICC_SCCE_TXE | QUICC_SCCE_TX)) != 0) {
+
+    while ((scce = (scc->scc_scce & (QUICC_SCCE_INTS | QUICC_SCCE_GRC |
QUICC_SCCE_BSY))) != 0) {
+        if ((scce & (QUICC_SCCE_TXE | QUICC_SCCE_TX | QUICC_SCCE_BSY)) !=
0) {
             quicc_eth_TxEvent(sc, scce);
         }
-        if ((scce & QUICC_SCCE_RXF) != 0) {
+
+        if ((scce &  (QUICC_SCCE_RXF | QUICC_SCCE_RX | QUICC_SCCE_BSY)) !=
0) {
             quicc_eth_RxEvent(sc);
         }
+        if (scce & QUICC_SCCE_GRC) {
+            CPMCommand(sc,QUICC_CPM_CR_RESTART_TX);
+            CPMCommand(sc,QUICC_CPM_CR_HUNT_MODE);
+        }
+
         scc->scc_scce = scce;  // Reset the bits we handled
     }
 }

 //

Cheers,
   Jon.

if_quicc.c


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]