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 + -
显示快捷键?