if_fec.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 711 行 · 第 1/2 页
C
711 行
E_fcc->iaddr_l = 0;
E_fcc->minflr = FEC_MIN_FLR;
E_fcc->taddr_h = 0;
E_fcc->taddr_m = 0;
E_fcc->taddr_l = 0;
E_fcc->pad_ptr = FEC_PRAM_TIPTR; // No special padding char ...
E_fcc->cf_type = 0;
E_fcc->maxd1 = FEC_PRAM_MAXD;
E_fcc->maxd2 = FEC_PRAM_MAXD;
// FCC register initialization
IMM->fcc_regs[FCC2].fcc_gfmr = FEC_GFMR_INIT;
IMM->fcc_regs[FCC2].fcc_psmr = FEC_PSMR_INIT;
IMM->fcc_regs[FCC2].fcc_dsr = FEC_DSR_INIT;
#ifdef CYGPKG_NET
// clear the events of FCC2
IMM->fcc_regs[FCC2].fcc_fcce = 0xFFFF0000;
IMM->fcc_regs[FCC2].fcc_fccm = FEC_EV_TXE | FEC_EV_TXB | FEC_EV_RXF;
// Set up to handle interrupts
cyg_drv_interrupt_create(FEC_ETH_INT,
0, // Highest //CYGARC_SIU_PRIORITY_HIGH,
(cyg_addrword_t)sc, // Data passed to ISR
(cyg_ISR_t *)fec_eth_isr,
(cyg_DSR_t *)eth_drv_dsr,
&fec_eth_interrupt_handle,
&fec_eth_interrupt);
cyg_drv_interrupt_attach(fec_eth_interrupt_handle);
cyg_drv_interrupt_acknowledge(FEC_ETH_INT);
cyg_drv_interrupt_unmask(FEC_ETH_INT);
#else
// Mask the interrupts
IMM->fcc_regs[FCC2].fcc_fccm = 0;
#endif
// Issue Init RX & TX Parameters Command for FCC2
while ((IMM->cpm_cpcr & CPCR_FLG) != CPCR_READY_TO_RX_CMD);
IMM->cpm_cpcr = CPCR_INIT_TX_RX_PARAMS |
CPCR_FCC2_CH |
CPCR_MCN_FEC |
CPCR_FLG; /* ISSUE COMMAND */
while ((IMM->cpm_cpcr & CPCR_FLG) != CPCR_READY_TO_RX_CMD);
// Initialize upper level driver for ecos
(sc->funs->eth_drv->init)(sc, (unsigned char *)&enaddr);
return true;
}
//
// This function is called to "start up" the interface. It may be called
// multiple times, even when the hardware is already running. It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
//
static void
fec_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
// Enable the device :
// Set the ENT/ENR bits in the GFMR -- Enable Transmit/Receive
qi->fcc_reg->fcc_gfmr |= (FEC_GFMR_EN_Rx | FEC_GFMR_EN_Tx);
}
//
// This function is called to shut down the interface.
//
static void
fec_eth_stop(struct eth_drv_sc *sc)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
// Disable the device :
// Clear the ENT/ENR bits in the GFMR -- Disable Transmit/Receive
qi->fcc_reg->fcc_gfmr &= ~(FEC_GFMR_EN_Rx | FEC_GFMR_EN_Tx);
}
//
// This function is called for low level "control" operations
//
static int
fec_eth_control(struct eth_drv_sc *sc, unsigned long key,
void *data, int length)
{
switch (key) {
case ETH_DRV_SET_MAC_ADDRESS:
return 0;
break;
default:
return 1;
break;
}
}
//
// 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.
//
static int
fec_eth_can_send(struct eth_drv_sc *sc)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
volatile struct fec_bd *txbd = qi->txbd;
int cache_state;
HAL_DCACHE_IS_ENABLED(cache_state);
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_INVALIDATE(fec_eth_txring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_TxNUM);
}
#endif
return ((txbd->ctrl & (FCC_BD_Tx_TC | FCC_BD_Tx_Ready)) == 0);
}
//
// This routine is called to send data to the hardware.
static void
fec_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
struct fec_bd *txbd, *txfirst;
volatile char *bp;
int i, txindex, cache_state;
HAL_DCACHE_IS_ENABLED(cache_state);
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_INVALIDATE(fec_eth_txring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_TxNUM);
}
#endif
// Find a free buffer
txbd = txfirst = qi->txbd;
while (txbd->ctrl & FEC_BD_Tx_Ready) {
// This buffer is busy, move to next one
if (txbd->ctrl & FEC_BD_Tx_Wrap) {
txbd = qi->tbase;
} else {
txbd++;
}
if (txbd == txfirst) {
#ifdef CYGPKG_NET
panic ("No free xmit buffers");
#else
os_printf("FEC Ethernet: No free xmit buffers\n");
#endif
}
}
// Remember the next buffer to try
if (txbd->ctrl & FEC_BD_Tx_Wrap) {
qi->txbd = qi->tbase;
} else {
qi->txbd = txbd+1;
}
txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
qi->txkey[txindex] = key;
// Set up buffer
txbd->length = total_len;
bp = txbd->buffer;
for (i = 0; i < sg_len; i++) {
memcpy((void *)bp, (void *)sg_list[i].buf, sg_list[i].len);
bp += sg_list[i].len;
}
// Make sure no stale data buffer ...
if (cache_state) {
HAL_DCACHE_FLUSH(txbd->buffer, txbd->length);
}
// Send it on it's way
txbd->ctrl |= FEC_BD_Tx_Ready | FEC_BD_Tx_Last | FEC_BD_Tx_TC;
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_FLUSH(fec_eth_txring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_TxNUM);
}
#endif
}
//
// This function is called when a packet has been received. It's job is
// to prepare to unload the packet from the hardware. Once the length of
// the packet is known, the upper layer of the driver can be told. When
// the upper layer is ready to unload the packet, the internal function
// 'fec_eth_recv' will be called to actually fetch it from the hardware.
//
static void
fec_eth_RxEvent(struct eth_drv_sc *sc)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
struct fec_bd *rxbd;
int cache_state;
HAL_DCACHE_IS_ENABLED(cache_state);
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_INVALIDATE(fec_eth_rxring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_RxNUM);
}
#endif
rxbd = qi->rnext;
while ((rxbd->ctrl & FEC_BD_Rx_Empty) == 0) {
qi->rxbd = rxbd; // Save for callback
// This is the right way of doing it, but dcbi has a bug ...
// if (cache_state) {
// HAL_DCACHE_INVALIDATE(rxbd->buffer, rxbd->length);
// }
(sc->funs->eth_drv->recv)(sc, rxbd->length);
if (cache_state) {
HAL_DCACHE_FLUSH(rxbd->buffer, rxbd->length);
}
rxbd->ctrl |= FEC_BD_Rx_Empty;
if (rxbd->ctrl & FEC_BD_Rx_Wrap) {
rxbd = qi->rbase;
} else {
rxbd++;
}
}
// Remember where we left off
qi->rnext = (struct fec_bd *)rxbd;
// Make sure no stale data
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_FLUSH(fec_eth_rxring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_RxNUM);
}
#endif
}
//
// This function is called as a result of the "eth_drv_recv()" call above.
// It's job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet. Note that the buffers
// may come in pieces, using a scatter-gather list. This allows for more
// efficient processing in the upper layers of the stack.
//
static void
fec_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
unsigned char *bp;
int i;
bp = (unsigned char *)qi->rxbd->buffer;
for (i = 0; i < sg_len; i++) {
if (sg_list[i].buf != 0) {
memcpy((void *)sg_list[i].buf, bp, sg_list[i].len);
bp += sg_list[i].len;
}
}
}
static void
fec_eth_TxEvent(struct eth_drv_sc *sc, int stat)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
struct fec_bd *txbd;
int txindex, cache_state;
// Make sure no stale data
HAL_DCACHE_IS_ENABLED(cache_state);
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_INVALIDATE(fec_eth_txring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_TxNUM);
}
#endif
txbd = qi->tnext;
// Note: TC field is used to indicate the buffer has/had data in it
while ( (txbd->ctrl & (FEC_BD_Tx_TC | FEC_BD_Tx_Ready)) == FEC_BD_Tx_TC ) {
txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
(sc->funs->eth_drv->tx_done)(sc, qi->txkey[txindex], 0);
txbd->ctrl &= ~FEC_BD_Tx_TC;
if (txbd->ctrl & FEC_BD_Tx_Wrap) {
txbd = qi->tbase;
} else {
txbd++;
}
}
// Remember where we left off
qi->tnext = (struct fec_bd *)txbd;
// Make sure no stale data
#ifndef FEC_BDs_NONCACHED
if (cache_state) {
HAL_DCACHE_FLUSH(fec_eth_txring,
8*CYGNUM_DEVS_ETH_POWERPC_QUICC2_TxNUM);
}
#endif
}
//
// Interrupt processing
//
static void
fec_eth_int(struct eth_drv_sc *sc)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
unsigned long iEvent;
while ((iEvent = qi->fcc_reg->fcc_fcce) != 0){
// Writing 1's clear fcce, Writing 0's have no effect
qi->fcc_reg->fcc_fcce = iEvent;
// Tx Done or Tx Error
if ( iEvent & (FEC_EV_TXB | FEC_EV_TXE) ) {
fec_eth_TxEvent(sc, iEvent);
}
// Complete or non-complete frame receive
if (iEvent & (FEC_EV_RXF | FEC_EV_RXB) ) {
fec_eth_RxEvent(sc);
}
}
}
//
// Interrupt vector
//
static int
fec_eth_int_vector(struct eth_drv_sc *sc)
{
return (FEC_ETH_INT);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?