if_at91.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,030 行 · 第 1/2 页

C
1,030
字号
		 priv->intr_vector);
   cyg_drv_interrupt_create(priv->intr_vector,
                            4,
                            (cyg_addrword_t)sc,
                            at91_eth_isr,
                            eth_drv_dsr,
                            &priv->intr_handle,
                            &priv->intr);

   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
   CYGHWR_DEVS_ETH_ARM_AT91_GET_ESA(&enaddr[0], esa_ok);
   // If this call fails myMacAddr is unchanged and MAC address from
   // CDL is used
#endif

   if (!esa_ok)
   {
      // Can't figure out ESA
      debug1_printf("AT91_ETH - Warning! ESA unknown\n");
   }
   debug1_printf("AT91_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n",
                 enaddr[0],enaddr[1],enaddr[2],
                 enaddr[3],enaddr[4],enaddr[5]);

   // Give the EMAC its address
   at91_set_mac(priv, enaddr, 1);
   at91_set_mac(priv, enzero, 2);
   at91_set_mac(priv, enzero, 3);
   at91_set_mac(priv, enzero, 4);

   // Setup the receiver buffers and descriptors
   at91_rb_init(priv);

   // And tell the EMAC where the first receive buffer descriptor is
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_RBQP, (cyg_uint32)priv->rbd);

   // 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);

   // Setup the PHY
   CYG_ASSERTC(priv->phy);

   at91_mdio_enable();
   if (!_eth_phy_init(priv->phy))
   {
      at91_mdio_disable();
      return (false);
   }

   // Get the current mode and print it
   phy_state = _eth_phy_state(priv->phy);
   at91_mdio_disable();

   HAL_READ_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);


   if ((phy_state & ETH_PHY_STAT_LINK) != 0)
   {
      if (((phy_state & ETH_PHY_STAT_100MB) != 0))
      {
         debug1_printf("AT91_ETH: 100Mbyte/s");
         ncfg |= AT91_EMAC_NCFG_SPD_100Mbps;
      }
      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");
         ncfg |= AT91_EMAC_NCFG_FD;
      }
      else
      {
         debug1_printf(" Half Duplex\n");
         ncfg &= ~(AT91_EMAC_NCFG_FD);
      }
   }
   else
   {
      debug1_printf("AT91_ETH: No Link\n");
   }


   //Setup the network configuration
   ncfg |= (AT91_EMAC_NCFG_RLCE);

   HAL_WRITE_UINT32(priv->base + AT91_EMAC_NCFG,ncfg);

   // Clear the Statistics counters;
   at91_clear_stats(priv);


   /* Clear the status registers */
   HAL_READ_UINT32(priv->base + AT91_EMAC_ISR,ncfg);
   HAL_READ_UINT32(priv->base + AT91_EMAC_RSR,ncfg);
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_RSR,ncfg);
   HAL_READ_UINT32(priv->base + AT91_EMAC_TSR,ncfg);
   HAL_WRITE_UINT32(priv->base + AT91_EMAC_TSR,ncfg);

   // Initialize the upper layer driver
   _eth_drv_init(sc,enaddr);

   return (true);
}

// 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;

   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;
   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);

   HAL_WRITE_UINT32(priv->base + AT91_EMAC_IER, bits);

   // Enable the receiver and transmitter
   at91_enable(priv);
}

// This function is called for low level "control" operations
static int
at91_eth_control(struct eth_drv_sc *sc, unsigned long key,
                 void *data, int length)
{

   switch (key)
   {
      case ETH_DRV_SET_MAC_ADDRESS:
         {
            if(length >= ETHER_ADDR_LEN)
            {
               at91_eth_stop(sc);

               cyg_uint8 * enaddr = (cyg_uint8 *)data;
               debug1_printf("AT91_ETH: %02x:%02x:%02x:%02x:%02x:%02x\n",
                             enaddr[0],enaddr[1],enaddr[2],
                             enaddr[3],enaddr[4],enaddr[5]);

               at91_set_mac((at91_eth_priv_t *)sc->driver_private,enaddr,1);
               at91_eth_start(sc,enaddr,0);
               return 0;
            }
            return 1;
         }
      default:
         {
            diag_printf("%s.%d: key %lx\n", __FUNCTION__, __LINE__, key);
            return (1);
         }
   }

}

// This function is called to see if another packet can be sent.
// It should return the number of packets which can be handled.
// Zero should be returned if the interface is busy and can not send
// any more.
//
// 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!
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)
   {
      can_send = 0;
   }
   else
   {
      can_send = 1;
   }
   return (can_send);
}

// This routine is called to send data to the hardware
static void
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;

   priv->last_tbd_idx = priv->curr_tbd_idx;

   for(i = 0;i<sg_len;i++)
   {
      priv->tbd[priv->curr_tbd_idx].addr = sg_list[i].buf;

      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;
      }
      
      if(priv->curr_tbd_idx < (CYGNUM_DEVS_ETH_ARM_AT91_TX_BUFS-1))
      {
         priv->tbd[priv->curr_tbd_idx].sr = sr;
         priv->curr_tbd_idx++;
      }
      else
      {
         priv->tbd[priv->curr_tbd_idx].sr = (sr | AT91_EMAC_TBD_SR_WRAP);
         priv->curr_tbd_idx = 0;
      }
   }

   // 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)
{
     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->last_tbd_idx = 0;
        }
        else
        {
           priv->tbd[priv->last_tbd_idx].sr = AT91_EMAC_TBD_SR_USED;
           priv->last_tbd_idx++;
        }
     }
}


//======================================================================

#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
static cyg_uint32
at91_eth_isr (cyg_vector_t vector, cyg_addrword_t data)
{
   struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
   at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
   cyg_uint32 ret;
   cyg_uint32 isr;

   /* 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)
   {
      ret = CYG_ISR_CALL_DSR;
   }

   if(isr & AT91_EMAC_ISR_RCOM)
   {
      ret = CYG_ISR_CALL_DSR;
   }
   cyg_interrupt_acknowledge(vector);
   return(ret);
}
#endif

static void 
at91_eth_deliver(struct eth_drv_sc *sc)
{
   at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;

   cyg_uint32 tsr;
   cyg_uint32 rsr;

   cyg_uint32 ctr;
   cyg_uint32 cnt;
   cyg_uint32 idx;

   /* Get the Transmit Status */
   HAL_READ_UINT32(priv->base+AT91_EMAC_TSR,tsr);
   HAL_WRITE_UINT32(priv->base+AT91_EMAC_TSR,tsr);

   /* Get the Receive Status */
   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 */
   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");
   }

   if (tsr&AT91_EMAC_TSR_BNQ)  //4
   {
      debug1_printf("AT91_ETH: Tx BEX\n");
   }

   if (tsr&AT91_EMAC_TSR_UND)  //6
   {
      debug1_printf("AT91_ETH: Tx UND\n");
   }

   /* Check that the last transmission is completed */
   if (tsr&AT91_EMAC_TSR_COMP) //5
   {
      at91_reset_tbd(priv);
      _eth_drv_tx_done(sc,priv->curr_tx_key,0);
      priv->tx_busy = false;
   }

   /* Service the RX buffers when we get something */
   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)
      {

         //Firstly walk through to either the first buffer that belongs 
         // to the controller or the first SOF
         while ((priv->rbd[priv->curr_rbd_idx].addr & 
		 AT91_EMAC_RBD_ADDR_OWNER_SW) && 
                !(priv->rbd[priv->curr_rbd_idx].sr & 
		  AT91_EMAC_RBD_SR_SOF))
         {
            priv->rbd[priv->curr_rbd_idx].addr &= 
	      ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
            priv->curr_rbd_idx++;
            if (priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)
            {
               priv->curr_rbd_idx = 0;
            }
         }

         /* Check that we did find a SOF*/
         if ((priv->rbd[priv->curr_rbd_idx].addr & 
	      AT91_EMAC_RBD_ADDR_OWNER_SW) && 
             (priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_SOF))
         {
            cnt = 0;
            for (ctr=0;ctr<CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS;ctr++)
            {
               idx = (ctr+priv->curr_rbd_idx)%CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS;
               cnt += (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_LEN_MASK);
               if (priv->rbd[idx].sr & AT91_EMAC_RBD_SR_EOF)
               {
                  /* The recv function will adjust the current buffer idx 
                     after the buffer has been cleared
                   */
                  if (cnt)
                     _eth_drv_recv(sc,cnt);
                  break;
               }
            }
         }
      }
   }

   if (rsr&AT91_EMAC_RSR_BNA)
   {
      debug1_printf("AT91_ETH: Rx BNA\n");
   }
   if (rsr&AT91_EMAC_RSR_OVR)
   {
      debug1_printf("AT91_ETH: Rx OVR\n");
   }

}

static void
at91_eth_recv(struct eth_drv_sc *sc,
              struct eth_drv_sg *sg_list,
              int sg_len)
{
   at91_eth_priv_t *priv = (at91_eth_priv_t *)sc->driver_private;
   int i;
   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;

   for(i = 0;i<sg_len;i++)
   {
      while(bytes_in_list < sg_list[i].len)
      {
         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 = 
		((priv->rbd[priv->curr_rbd_idx].sr & AT91_EMAC_RBD_SR_LEN_MASK)
		 - total_bytes) - buffer_pos;
         }
         else
         {
            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)
         {
            if(sg_buf != NULL)
               memcpy(&sg_buf[bytes_in_list],
		      &priv->rb[priv->curr_rbd_idx].rb[buffer_pos],
		      bytes_needed_list);
            bytes_in_list += bytes_needed_list;
            buffer_pos += bytes_needed_list;
            total_bytes += bytes_needed_list;
         }
         else
         {
            if(sg_buf != NULL)
              memcpy(&sg_buf[bytes_in_list],
		     &priv->rb[priv->curr_rbd_idx].rb[buffer_pos],
		     bytes_in_buffer);
            bytes_in_list += bytes_in_buffer;
            total_bytes += bytes_in_buffer;

            /* Step our buffer on one */
            priv->rbd[priv->curr_rbd_idx].addr &= 
	      ~(AT91_EMAC_RBD_ADDR_OWNER_SW);
            priv->curr_rbd_idx++;
            if(priv->curr_rbd_idx >= CYGNUM_DEVS_ETH_ARM_AT91_RX_BUFS)
            {
               priv->curr_rbd_idx = 0;
            }
            buffer_pos = 0;
         }
      }
   }
}

// routine called to handle ethernet controller in polled mode
static void 
at91_eth_poll(struct eth_drv_sc *sc)
{
   /* Service the buffers */
   at91_eth_deliver(sc);
}

static int
at91_eth_int_vector(struct eth_drv_sc *sc)
{
   return(CYGNUM_HAL_INTERRUPT_EMAC);
}

at91_eth_priv_t at91_priv_data =
{
   .intr_vector = CYGNUM_HAL_INTERRUPT_EMAC,
   .base = AT91_EMAC,
   .phy = &at91_phy
};

ETH_DRV_SC(at91_sc,
           &at91_priv_data,       // Driver specific data
           "eth0",                // Name for this interface
           at91_eth_start,
           at91_eth_stop,
           at91_eth_control,
           at91_eth_can_send,
           at91_eth_send,
           at91_eth_recv,
           at91_eth_deliver,
           at91_eth_poll,
           at91_eth_int_vector);

NETDEVTAB_ENTRY(at91_netdev,
                "at91",
                at91_eth_init,
                &at91_sc);

// EOF if_at91.c

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?