if_fec.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 800 行 · 第 1/2 页
C
800 行
bool phy_ok;
unsigned short phy_state = 0;
#endif
// Ensure consistent state between cache and what the FEC sees
HAL_DCACHE_IS_ENABLED(cache_state);
if (cache_state) {
HAL_DCACHE_SYNC();
HAL_DCACHE_DISABLE();
}
qi->fec = fec;
fec_eth_stop(sc); // Make sure it's not running yet
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
#ifdef _FEC_USE_INTS
// Set up to handle interrupts
cyg_drv_interrupt_create(FEC_ETH_INT,
CYGARC_SIU_PRIORITY_HIGH,
(cyg_addrword_t)sc, // Data item passed to interrupt handler
(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 // _FEC_USE_INTS
// Hack - use a thread to simulate interrupts
cyg_thread_create(1, // Priority
fec_fake_int, // entry
(cyg_addrword_t)sc, // entry parameter
"CS8900 int", // Name
&fec_fake_int_stack[0], // Stack
STACK_SIZE, // Size
&fec_fake_int_thread_handle, // Handle
&fec_fake_int_thread_data // Thread data structure
);
cyg_thread_resume(fec_fake_int_thread_handle); // Start it
#endif
#endif
// Set up parallel port for connection to ethernet tranceiver
eppc->pio_pdpar = 0x1FFF;
CYGARC_MFSPR( CYGARC_REG_PVR, proc_rev );
#define PROC_REVB 0x0020
if ((proc_rev & 0x0000FFFF) == PROC_REVB) {
eppc->pio_pddir = 0x1C58;
} else {
eppc->pio_pddir = 0x1FFF;
}
// Get physical device address
#ifdef CYGPKG_REDBOOT
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
esa_ok = flash_get_config("fec_esa", enaddr, CONFIG_ESA);
#else
esa_ok = false;
#endif
#else
esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
"fec_esa", enaddr, CONFIG_ESA);
#endif
if (!esa_ok) {
// Can't figure out ESA
os_printf("FEC_ETH - Warning! ESA unknown\n");
memcpy(&enaddr, &_default_enaddr, sizeof(enaddr));
}
// Configure the device
if (!fec_eth_reset(sc, enaddr, 0)) {
return false;
}
fec->iEvent = 0xFFFFFFFF; // Clear all interrupts
#ifdef CYGSEM_DEVS_ETH_POWERPC_FEC_RESET_PHY
// Reset PHY (transceiver)
FEC_ETH_RESET_PHY();
phy_ok = 0;
if (phy_read(PHY_BMSR, FEC_ETH_PHY, &phy_state)) {
if ((phy_state & PHY_BMSR_LINK) != PHY_BMSR_LINK) {
unsigned short reset_mode;
int i;
phy_write(PHY_BMCR, FEC_ETH_PHY, PHY_BMCR_RESET);
for (i = 0; i < 10; i++) {
phy_ok = phy_read(PHY_BMCR, FEC_ETH_PHY, &phy_state);
if (!phy_ok) break;
if (!(phy_state & PHY_BMCR_RESET)) break;
}
if (!phy_ok || (phy_state & PHY_BMCR_RESET)) {
os_printf("FEC: Can't get PHY unit to soft reset: %x\n", phy_state);
return false;
}
fec->iEvent = 0xFFFFFFFF; // Clear all interrupts
reset_mode = PHY_BMCR_RESTART;
#ifdef CYGNUM_DEVS_ETH_POWERPC_FEC_LINK_MODE_Auto
reset_mode |= PHY_BMCR_AUTO_NEG;
#endif
#ifdef CYGNUM_DEVS_ETH_POWERPC_FEC_LINK_MODE_100Mb
reset_mode |= PHY_BMCR_100MB;
#endif
phy_write(PHY_BMCR, FEC_ETH_PHY, reset_mode);
while (phy_timeout-- >= 0) {
int ev = fec->iEvent;
unsigned short state;
fec->iEvent = ev;
if (ev & iEvent_MII) {
phy_ok = phy_read(PHY_BMSR, FEC_ETH_PHY, &state);
if (phy_ok && (state & PHY_BMSR_LINK)) {
break;
} else {
CYGACC_CALL_IF_DELAY_US(10000); // 10ms
}
}
}
if (phy_timeout <= 0) {
os_printf("** FEC Warning: PHY LINK UP failed\n");
}
}
else {
os_printf("** FEC Info: PHY LINK already UP \n");
}
}
#endif // CYGSEM_DEVS_ETH_POWERPC_FEC_RESET_PHY
// Initialize upper level driver
(sc->funs->eth_drv->init)(sc, (unsigned char *)&enaddr);
if (cache_state)
HAL_DCACHE_ENABLE();
return true;
}
//
// 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!
qi->fec->eControl &= ~eControl_EN;
}
//
// 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)
{
// Enable the device!
fec_eth_reset(sc, enaddr, flags);
}
//
// 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)
{
#ifdef ETH_DRV_SET_MC_ALL
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
volatile struct fec *fec = qi->fec;
#endif
switch (key) {
case ETH_DRV_SET_MAC_ADDRESS:
return 0;
break;
#ifdef ETH_DRV_SET_MC_ALL
case ETH_DRV_SET_MC_ALL:
case ETH_DRV_SET_MC_LIST:
fec->RxControl &= ~RxControl_PROM;
fec->hash[0] = 0xFFFFFFFF;
fec->hash[1] = 0xFFFFFFFF;
return 0;
break;
#endif
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;
return (qi->txactive < CYGNUM_DEVS_ETH_POWERPC_FEC_TxNUM);
}
//
// 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;
volatile struct fec_bd *txbd, *txfirst;
volatile char *bp;
int i, txindex, cache_state;
// 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
}
}
// Set up buffer
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;
}
txbd->length = total_len;
txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
qi->txkey[txindex] = key;
// Note: the MPC8xx does not seem to snoop/invalidate the data cache properly!
HAL_DCACHE_IS_ENABLED(cache_state);
if (cache_state) {
HAL_DCACHE_FLUSH(txbd->buffer, txbd->length); // Make sure no stale data
}
// Send it on it's way
txbd->ctrl |= FEC_BD_Tx_Ready | FEC_BD_Tx_Last | FEC_BD_Tx_TC;
qi->txactive++;
qi->fec->TxUpdate = 0x01000000; // Any write tells machine to look for work
set_led(LED_TxACTIVE);
// Remember the next buffer to try
if (txbd->ctrl & FEC_BD_Tx_Wrap) {
qi->txbd = qi->tbase;
} else {
qi->txbd = txbd+1;
}
}
//
// 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;
volatile struct fec_bd *rxbd, *rxfirst;
rxbd = rxfirst = qi->rnext;
while (true) {
if ((rxbd->ctrl & FEC_BD_Rx_Empty) == 0) {
qi->rxbd = rxbd; // Save for callback
set_led(LED_RxACTIVE); // Remove the CRC
(sc->funs->eth_drv->recv)(sc, rxbd->length - 4);
}
if (rxbd->ctrl & FEC_BD_Rx_Wrap) {
rxbd = qi->rbase;
} else {
rxbd++;
}
if (rxbd == rxfirst) {
break;
}
}
// Remember where we left off
qi->rnext = (struct fec_bd *)rxbd;
qi->fec->RxUpdate = 0x0F0F0F0F; // Any write tells machine to look for work
}
//
// 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, cache_state;
bp = (unsigned char *)qi->rxbd->buffer;
// Note: the MPC8xx does not seem to snoop/invalidate the data cache properly!
HAL_DCACHE_IS_ENABLED(cache_state);
if (cache_state) {
HAL_DCACHE_INVALIDATE(qi->rxbd->buffer, qi->rxbd->length); // Make sure no stale data
}
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;
}
}
qi->rxbd->ctrl |= FEC_BD_Rx_Empty;
clear_led(LED_RxACTIVE);
}
static void
fec_eth_TxEvent(struct eth_drv_sc *sc)
{
struct fec_eth_info *qi = (struct fec_eth_info *)sc->driver_private;
volatile struct fec_bd *txbd;
int key, txindex;
txbd = qi->tnext;
// Note: TC field is used to indicate the buffer has/had data in it
while ((txbd->ctrl & (FEC_BD_Tx_Ready|FEC_BD_Tx_TC)) == FEC_BD_Tx_TC) {
txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
if ((key = qi->txkey[txindex]) != 0) {
qi->txkey[txindex] = 0;
(sc->funs->eth_drv->tx_done)(sc, key, 0);
}
if (--qi->txactive == 0) {
clear_led(LED_TxACTIVE);
}
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;
}
//
// 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 event;
while ((event = qi->fec->iEvent) != 0) {
qi->fec->iEvent = event; // Reset the bits we handled
if ((event & iEvent_TFINT) != 0) {
fec_eth_TxEvent(sc);
}
if ((event & iEvent_RFINT) != 0) {
fec_eth_RxEvent(sc);
}
}
}
//
// Interrupt vector
//
static int
fec_eth_int_vector(struct eth_drv_sc *sc)
{
return (FEC_ETH_INT);
}
#if defined(CYGINT_IO_ETH_INT_SUPPORT_REQUIRED) && ~defined(_FEC_USE_INTS)
void
fec_fake_int(cyg_addrword_t param)
{
struct eth_drv_sc *sc = (struct eth_drv_sc *) param;
int int_state;
while (true) {
cyg_thread_delay(1); // 10ms
HAL_DISABLE_INTERRUPTS(int_state);
fec_eth_int(sc);
HAL_RESTORE_INTERRUPTS(int_state);
}
}
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?