now with attachment

> -----Original Message-----
> From: Lambrecht Jürgen
> Sent: woensdag 4 juni 2008 0:08
> To: [EMAIL PROTECTED]
> Subject: Re: [ECOS] bugs in AT91 Ethernet driver
> 
> Major bug found in TX part - see below.
> 

--- 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 00:01:51.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,79 @@
    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 */
+   /* 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) //5
    {
-      at91_reset_tbd(priv);
+      debug2_printf("AT91_ETH: Tx COMP.\n");
+      at91_reset_tbd(priv, b_reset_tbd_idx);
       _eth_drv_tx_done(sc,priv->curr_tx_key,0);
       priv->tx_busy = false;
    }
+   else if (b_reset_tbd_idx == true)
+   {
+      at91_reset_tbd(priv, b_reset_tbd_idx);
+   }
 
-   /* 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 +997,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 +1070,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

Reply via email to