This is the mail archive of the ecos-discuss@sourceware.org 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]
Other format: [Raw text]

Re: ping reply not synchronized with the ping request


Solved.
Was a (last?) bug in if_at91.c, cf. http://ecos.sourceware.org/ml/ecos-discuss/2008-06/msg00013.html.
I had forgotten to call '_eth_drv_tx_done' and set 'priv->tx_busy = false' in case of a TX error.
In attach again the patch.


Test result:
500 pings worked, no loss.
For each ping, "AT91_ETH: Tx BEX" is printed (buffers exhausted in mid-frame). I think that is because the last DMA runs out of data but it is ok because there is no data anymore - maybe the length of the DMA is wrong?


Kind regards,
Juergen

Lambrecht Jürgen wrote:
Hello,

Now I've got the Ethernet driver running for my AT91SAM9260-EK based board (if_at91.c - see previous posts of me), I have some strange behavior with the FreeBSD TCP/IP stack:

-> at startup a gratuitous ARP is sent out
<- when a ARP request arrives
-> again a gratuitous ARP is sent out
<- then the ICMP ping request 1 arrives
-> a normal ARP reply is sent
<- ping request 2
-> ping reply 1
<- ping request 3
...
When I stop the ping, and start again after clearing the arp cache, the last
"lost" ping reply is sent as response to the first ARP request.

It is like the TX or RX part holds on to 1 packet, and only releases it when a
new packet arrives.

Why again a gratuitous ARP - is the driver initialized again? Or does the driver normally sends out 2 gratuitous ARPs at startup?

Kind regards,
Jürgen


--- F:\version_ecos\packages\devs\eth\arm\at91\current\src\if_at91.c	2008-03-07 08:35:36.000000000 +-0200
+++ F:\ecos_ronetix\packages\devs\eth\arm\at91\current\src\if_at91.c	2008-06-04 01:02:24.000000000 +-0200
@@ -36,16 +36,18 @@
 // -------------------------------------------
 //####ECOSGPLCOPYRIGHTEND####
 //==========================================================================
 //#####DESCRIPTIONBEGIN####
 //
 // Author(s):    Andrew Lunn, John Eigelaar
-// Contributors:  
+// Contributors: Juergen Lambrecht 
 // Date:         2006-05-10
-// Purpose:
-// Description:
+// Purpose:      BSD compatible network driver
+// Description:  HW network driver for AT91 EMAC block of AT91SAM uC's.
+// Limitations:  Jumbo frames are not supported because of
+//               AT91_EMAC_RBD_SR_LEN_MASK = 0xFFF
 //
 //####DESCRIPTIONEND####
 //
 //========================================================================*/
 
 #include <pkgconf/system.h>
@@ -64,13 +66,15 @@
 #include <cyg/infra/cyg_type.h>
 #include <cyg/infra/cyg_ass.h>
 #include <cyg/infra/diag.h>
 #include <cyg/io/eth/netdev.h>
 #include <cyg/io/eth/eth_drv.h>
 #include <cyg/io/eth/eth_drv_stats.h>
-#include <cyg/io/eth_phy.h>
+#ifdef PHY_PRESENT
+   #include <cyg/io/eth_phy.h>
+#endif
 #include <errno.h>
 #include <string.h>
 
 // Set up the level of debug output
 #if CYGPKG_DEVS_ETH_ARM_AT91_DEBUG_LEVEL > 0
    #define debug1_printf(args...) diag_printf(args)
@@ -165,13 +169,15 @@
 typedef struct at91_eth_priv_s 
 {
    cyg_uint32    intr_vector;
    char *esa_key;      // RedBoot 'key' for device ESA
    cyg_uint8 *enaddr;
    cyg_uint32 base;    // Base address of device
+#ifdef PHY_PRESENT
    eth_phy_access_t *phy;
+#endif
    rbd_t rbd[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS];
    rb_t  rb[CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS];
    tbd_t tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS];
    unsigned long curr_tx_key;
    cyg_bool tx_busy;
    cyg_uint32 last_tbd_idx;
@@ -297,19 +303,20 @@
    {
       CYG_FAIL("Unable to program MII clock");
    }
 
    HAL_WRITE_UINT32(AT91_EMAC + AT91_EMAC_NCFG, cfg);
 }
-
+/* The general PHY driver must know how to access the PHY. */
+#ifdef PHY_PRESENT
 ETH_PHY_REG_LEVEL_ACCESS_FUNS(at91_phy, 
                               at91_init_phy,
                               NULL,
                               at91_write_phy,
                               at91_read_phy);
-
+#endif
 //======================================================================
 // Receiver buffer handling
 
 // Initialize the receiver buffers and descriptors
 static void
 at91_rb_init(at91_eth_priv_t *priv)
@@ -333,13 +340,13 @@
 at91_tb_init(at91_eth_priv_t *priv)
 {
    int i;
    for (i = 0 ; i < CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS; i++)
    {
       priv->tbd[i].addr = 0;
-      priv->tbd[i].sr = AT91_EMAC_TBD_SR_USED;
+      priv->tbd[i].sr = 0; /* datasheet 36.4.1.3 for SAM9260 vG and 37.4.1.3 for SAM7 vG point 2 */
    }
    // Set the wrap bit on the last entry
    priv->tbd[CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1].sr |= AT91_EMAC_TBD_SR_WRAP;
 }
 
 //======================================================================
@@ -482,13 +489,15 @@
 {
    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
    at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
    bool esa_ok = false;
    unsigned char enaddr[6] = { CYGPKG_DEVS_ETH_ARM_AT91_MACADDR};
    unsigned char enzero[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+#ifdef PHY_PRESENT
    unsigned short phy_state = 0;
+#endif
    cyg_uint32 ncfg = 0;
 
    debug1_printf("\nAT91_ETH: Initialising @ %x\n",priv->base);
 
    priv->tx_busy = false;
    priv->curr_tbd_idx = 0;
@@ -518,13 +527,13 @@
                             4,
                             (cyg_addrword_t)sc,
                             at91_eth_isr,
                             eth_drv_dsr,
                             &priv->intr_handle,
                             &priv->intr);
-
+   /* default is level sensitive; */
    cyg_drv_interrupt_attach(priv->intr_handle);
    cyg_drv_interrupt_unmask(priv->intr_vector);
 #endif
 
 #ifdef CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA
    // Get MAC address from RedBoot configuration variables
@@ -557,57 +566,64 @@
    // Setup the transmit descriptors
    at91_tb_init(priv);
 
    // And tell the EMAC where the first transmit buffer descriptor is
    HAL_WRITE_UINT32(priv->base + AT91_EMAC_TBQP, (cyg_uint32)priv->tbd);
 
+#ifdef PHY_PRESENT
    // Setup the PHY
    CYG_ASSERTC(priv->phy);
 
    at91_mdio_enable();
    if (!_eth_phy_init(priv->phy))
    {
       at91_mdio_disable();
+      debug2_printf("_eth_phy_init failed\n");
       return (false);
    }
 
    // Get the current mode and print it
    phy_state = _eth_phy_state(priv->phy);
+#endif
    at91_mdio_disable();
 
    HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);
 
-
+#ifdef PHY_PRESENT
    if ((phy_state & ETH_PHY_STAT_LINK) != 0)
    {
       if (((phy_state & ETH_PHY_STAT_100MB) != 0))
       {
          debug1_printf("AT91_ETH: 100Mbyte/s");
+#endif
          ncfg |= AT91_EMAC_NCFG_SPD_100Mbps;
+#ifdef PHY_PRESENT
       }
       else
       {
          debug1_printf("AT91_ETH: 10Mbyte/s");
          ncfg &= ~(AT91_EMAC_NCFG_SPD_100Mbps);
       }
       if((phy_state & ETH_PHY_STAT_FDX))
       {
          debug1_printf(" Full Duplex\n");
+#endif
          ncfg |= AT91_EMAC_NCFG_FD;
+#ifdef PHY_PRESENT
       }
       else
       {
          debug1_printf(" Half Duplex\n");
          ncfg &= ~(AT91_EMAC_NCFG_FD);
       }
    }
    else
    {
       debug1_printf("AT91_ETH: No Link\n");
    }
-
+#endif
 
    //Setup the network configuration
    ncfg |= (AT91_EMAC_NCFG_RLCE);
 
    HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);
 
@@ -630,31 +646,40 @@
 
 // This function is called to stop the interface.
 static void 
 at91_eth_stop(struct eth_drv_sc *sc)
 {
    at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
+   cyg_uint32 bits;
 
+   bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND |
+           AT91_EMAC_ISR_RTRY | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM);
+
+   HAL_WRITE_UINT32(priv->base + AT91_EMAC_IDR, bits);
+#endif
    at91_disable(priv);
 }
 
 // This function is called to "start up" the interface. It may be called
 // multiple times, even when the hardware is already running.
 static void
 at91_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
 {
    at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
+#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
    cyg_uint32 bits;
 
    // Enable the interrupts we are interested in
    // TODO: We probably need to add at least the RBNA interrupt here
    //       as well in order to do some error handling
-   bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_TCOM);
+   bits = (AT91_EMAC_ISR_RCOM | AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND |
+           AT91_EMAC_ISR_RTRY | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM);
 
    HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, bits);
-
+#endif
    // Enable the receiver and transmitter
    at91_enable(priv);
 }
 
 // This function is called for low level "control" operations
 static int
@@ -697,12 +722,13 @@
 //
 // We allocate one buffer descriptor per scatter/gather entry. We assume that
 // a typical packet will not have more than 3 such entries, and so we say we
 // can send a packet when we have 3 or more buffer descriptors free
 //
 // TODO: Implement what the comment actually says!
+// JL: more difficult with copy to SRAM..
 static int
 at91_eth_can_send(struct eth_drv_sc *sc)
 {
    int can_send;
    at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
    if(priv->tx_busy)
@@ -721,20 +747,27 @@
 at91_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
               int total_len, unsigned long key)
 {
    at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
    int i;
    cyg_uint32 sr;
-
-   priv->tx_busy = true;
+#ifdef SRAM1_ORIGIN /* define it in plf_io.h if present */
+   cyg_uint32 total_bytes = 0; /* position in SRAM1 */
+#endif
+   priv->tx_busy = true; /* for can_send() */
 
-   priv->last_tbd_idx = priv->curr_tbd_idx;
+   priv->last_tbd_idx = priv->curr_tbd_idx; //??
 
    for(i = 0;i<sg_len;i++)
    {
+#ifdef SRAM1_ORIGIN
+      memcpy((cyg_uint8 *)(SRAM1_ORIGIN+total_bytes), (cyg_uint8 *)(sg_list[i].buf), sg_list[i].len);
+      priv->tbd[priv->curr_tbd_idx].addr = SRAM1_ORIGIN+total_bytes;
+#else
       priv->tbd[priv->curr_tbd_idx].addr = sg_list[i].buf;
+#endif
 
       sr = (sg_list[i].len & AT91_EMAC_TBD_SR_LEN_MASK);
       // Set the End Of Frame bit in the last descriptor
       if(i == (sg_len-1))
       {
          sr |= AT91_EMAC_TBD_SR_EOF;
@@ -747,37 +780,45 @@
       }
       else
       {
          priv->tbd[priv->curr_tbd_idx].sr = (sr | AT91_EMAC_TBD_SR_WRAP);
          priv->curr_tbd_idx = 0;
       }
+#ifdef SRAM1_ORIGIN
+      total_bytes += sg_list[i].len;
+#endif
    }
 
    // Store away the key for when the transmit has completed
    // and we need to tell the stack which transmit has completed.
    priv->curr_tx_key = key;
 
    at91_start_transmitter(priv);
 }
 
-static void at91_reset_tbd(at91_eth_priv_t *priv)
+/* datasheet 36.4.1.3 for SAM9260 vG and 37.4.1.3 for SAM7 vG point 2: 
+   set AT91_EMAC_TBD_SR_USED=0 */
+static void at91_reset_tbd(at91_eth_priv_t *priv, bool b_reset_tbd_idx)
 {
      while(priv->curr_tbd_idx != priv->last_tbd_idx)
      {
         if(priv->last_tbd_idx == (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1))
         {
-           priv->tbd[priv->last_tbd_idx].sr = 
-             (AT91_EMAC_TBD_SR_USED|AT91_EMAC_TBD_SR_WRAP);
+           priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_WRAP;
            priv->last_tbd_idx = 0;
         }
         else
         {
-           priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_USED;
+           priv->tbd[priv->last_tbd_idx].sr = 0;
            priv->last_tbd_idx++;
         }
-     }
+     }
+     if (b_reset_tbd_idx)
+     {
+        priv->curr_tbd_idx = 0; //reset, because EMAC also resets its queue 
+     }                          //pointer to the TBQP
 }
 
 
 //======================================================================
 
 #ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
@@ -791,21 +832,23 @@
 
    /* Get the interrupt status */
    HAL_READ_UINT32(priv->base+AT91_EMAC_ISR,isr);
 
    ret = CYG_ISR_HANDLED;
 
-   //TODO: We should probably be handling some of the error interrupts as well
-   if(isr & AT91_EMAC_ISR_TCOM)
+   if(isr & (AT91_EMAC_ISR_TOVR | AT91_EMAC_ISR_TUND | AT91_EMAC_ISR_RTRY | AT91_EMAC_ISR_TBRE | AT91_EMAC_ISR_TCOM))
    {
       ret = CYG_ISR_CALL_DSR;
+      debug2_printf("TX IRQ\n");
    }
 
+   //TODO: We should probably be handling some of the error interrupts as well
    if(isr & AT91_EMAC_ISR_RCOM)
    {
       ret = CYG_ISR_CALL_DSR;
+      debug2_printf("RX IRQ\n");
    }
    cyg_interrupt_acknowledge(vector);
    return(ret);
 }
 #endif
 
@@ -817,56 +860,75 @@
    cyg_uint32 tsr;
    cyg_uint32 rsr;
 
    cyg_uint32 ctr;
    cyg_uint32 cnt;
    cyg_uint32 idx;
+   bool b_reset_tbd_idx = false;
 
-   /* Get the Transmit Status */
+   /* Get the Transmit Status and clear it */
    HAL_READ_UINT32(priv->base+AT91_EMAC_TSR,tsr);
    HAL_WRITE_UINT32(priv->base+AT91_EMAC_TSR,tsr);
 
-   /* Get the Receive Status */
+   /* Get the Receive Status and clear it */
    HAL_READ_UINT32(priv->base+AT91_EMAC_RSR,rsr);
    HAL_WRITE_UINT32(priv->base+AT91_EMAC_RSR,rsr);
 
 
    //TODO: The interrupts other than RCOMP and TCOMP needs to be
    //      handled properly especially stuff like RBNA which could have
    //      serious effects on driver performance
 
-   /* Service the TX buffers */
+   /* Service the TX buffers after IRQ */
+   if (tsr&AT91_EMAC_TSR_OVR)  //0
+   {
+      debug1_printf("AT91_ETH: Tx UBR\n");
+      b_reset_tbd_idx = true;
+   }
+
    if (tsr&AT91_EMAC_TSR_COL)  //1
    {
       debug1_printf("AT91_ETH: Tx COL\n");
    }
 
    if (tsr&AT91_EMAC_TSR_RLE)  //2
    {
       debug1_printf("AT91_ETH: Tx RLE\n");
+      b_reset_tbd_idx = true;
    }
 
+   if ((tsr&AT91_EMAC_TSR_TXIDLE) == 0) //3
+   {
+      debug2_printf("AT91_ETH: Tx IDLE\n");
+   }
+
    if (tsr&AT91_EMAC_TSR_BNQ)  //4
    {
       debug1_printf("AT91_ETH: Tx BEX\n");
+      b_reset_tbd_idx = true;
    }
 
    if (tsr&AT91_EMAC_TSR_UND)  //6
    {
       debug1_printf("AT91_ETH: Tx UND\n");
+      b_reset_tbd_idx = true;
    }
 
    /* Check that the last transmission is completed */
-   if (tsr&AT91_EMAC_TSR_COMP) //5
+   /* After each transmit, it is best to immediately reset tbd */
+   /* If there were errors, tbd_idx must be reset also */
+   if ((tsr&AT91_EMAC_TSR_COMP) || (b_reset_tbd_idx == true)) //5
    {
-      at91_reset_tbd(priv);
+      debug2_printf("AT91_ETH: Tx COMP.\n"); //ok, should add extra if..
+      at91_reset_tbd(priv, b_reset_tbd_idx);
       _eth_drv_tx_done(sc,priv->curr_tx_key,0);
       priv->tx_busy = false;
    }
 
-   /* Service the RX buffers when we get something */
+
+   /* Service the RX buffers when we get something after IRQ */
    if (rsr&AT91_EMAC_RSR_REC)
    {
       /* Do this all until we find the first EMAC Buffer */
       while (priv->rbd[priv->curr_rbd_idx].addr & AT91_EMAC_RBD_ADDR_OWNER_SW)
       {
 
@@ -931,27 +993,30 @@
    cyg_uint32 bytes_in_buffer;
    cyg_uint32 bytes_in_list = 0;
    cyg_uint32 bytes_needed_list = 0;
    cyg_uint32 buffer_pos = 0;
    cyg_uint8 * sg_buf;
    cyg_uint32 total_bytes = 0;
-
+   /* buffer_pos is position in current 128B buffer */
+   /* bytes_in_list is position in current sg_list */
+   /* total_bytes is total no. of packet bytes already copied */
    for(i = 0;i<sg_len;i++)
    {
-      while(bytes_in_list < sg_list[i].len)
-      {
+      bytes_in_list = 0; /* go to next list */
+      while(bytes_in_list < sg_list[i].len) //freebsd - i=0: 14B
+      {                                     //freebsd - i=1: 128B or remainder of packet
          bytes_needed_list = sg_list[i].len - bytes_in_list;
 
          if(priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_EOF)
-         {
-	      bytes_in_buffer = 
+         { /* This 128B buffer contains the End Of the Frame. */
+            bytes_in_buffer =
 		((priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK)
-		 - total_bytes) - buffer_pos;
+		 - total_bytes);
          }
          else
-         {
+         { /* AT91_EMAC_RX_BUFF_SIZE = 128B */
             bytes_in_buffer = AT91_EMAC_RX_BUFF_SIZE - buffer_pos;
          }
 
          sg_buf = (cyg_uint8 *)(sg_list[i].buf);
 
          if(bytes_needed_list < bytes_in_buffer)
@@ -1001,14 +1066,17 @@
    return(CYGNUM_HAL_INTERRUPT_EMAC);
 }
 
 at91_eth_priv_t at91_priv_data =
 {
    .intr_vector = CYGNUM_HAL_INTERRUPT_EMAC,
-   .base = AT91_EMAC,
+   .base = AT91_EMAC
+#ifdef PHY_PRESENT
+   ,
    .phy = &at91_phy
+#endif
 };
 
 ETH_DRV_SC(at91_sc,
            &at91_priv_data,       // Driver specific data
            "eth0",                // Name for this interface
            at91_eth_start,

-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss

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