if_lan91cxx.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,486 行 · 第 1/4 页
C
1,486 行
db_printf("LAN91CXX - status: %04x\n", val);
#endif
#if 0 < CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA
// Use statically configured ESA from the private data
#if DEBUG & 9
db_printf("LAN91CXX - static ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
cpd->enaddr[0],
cpd->enaddr[1],
cpd->enaddr[2],
cpd->enaddr[3],
cpd->enaddr[4],
cpd->enaddr[5] );
#endif // DEBUG
// Set up hardware address
for (i = 0; i < sizeof(cpd->enaddr); i += 2)
put_reg(sc, LAN91CXX_IA01+i/2,
cpd->enaddr[i] | (cpd->enaddr[i+1] << 8));
#else // not CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA
// Find ESA - check possible sources in sequence and stop when
// one provides the ESA:
// RedBoot option (via provide_esa)
// Compile-time configuration
// EEPROM
if (NULL != cpd->provide_esa) {
esa_configured = cpd->provide_esa(cpd);
# if DEBUG & 8
if (esa_configured)
db_printf("Got ESA from RedBoot option\n");
# endif
}
if (!esa_configured && cpd->hardwired_esa) {
// ESA is already set in cpd->esa[]
esa_configured = true;
# if DEBUG & 8
db_printf("Got ESA from cpd\n");
# endif
}
if (esa_configured) {
// Set up hardware address
for (i = 0; i < sizeof(cpd->enaddr); i += 2)
put_reg(sc, LAN91CXX_IA01+i/2,
cpd->enaddr[i] | (cpd->enaddr[i+1] << 8));
} else {
// Use the address from the serial EEPROM
// Read out hardware address
for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 );
cpd->enaddr[i] = (unsigned char)(0xff & z);
cpd->enaddr[i+1] = (unsigned char)(0xff & (z >> 8));
}
esa_configured = true;
# if DEBUG & 8
db_printf("Got ESA from eeprom\n");
# endif
}
#if DEBUG & 9
db_printf("LAN91CXX - ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
cpd->enaddr[0],
cpd->enaddr[1],
cpd->enaddr[2],
cpd->enaddr[3],
cpd->enaddr[4],
cpd->enaddr[5] );
#endif // DEBUG
#endif // !CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA
// Initialize upper level driver
(sc->funs->eth_drv->init)(sc, cpd->enaddr);
return true;
}
static void
lan91cxx_stop(struct eth_drv_sc *sc)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
DEBUG_FUNCTION();
CYG_ASSERT( cpd->within_send < 10, "stop: Excess send recursions" );
cpd->within_send++;
// Complete any outstanding activity:
if ( cpd->txbusy ) {
cpd->txbusy = 0;
#if DEBUG & 9
db_printf("LAN91CXX - Stopping, cleaning up pending TX\n" );
#endif
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
}
// Reset chip
put_reg(sc, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
put_reg(sc, LAN91CXX_RCR, 0);
cpd->txbusy = cpd->within_send = 0;
}
//
// 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
lan91cxx_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
cyg_uint16 intr;
#ifdef LAN91CXX_IS_LAN91C111
int delay;
#endif
#ifdef CYGPKG_NET
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
#endif
DEBUG_FUNCTION();
#ifdef LAN91CXX_IS_LAN91C111
// 91C111 Errata. Internal PHY comes up disabled. Must enable here.
lan91cxx_write_phy(sc, 0, LAN91CXX_PHY_CTRL, LAN91CXX_PHY_CTRL_RST);
CYGACC_CALL_IF_DELAY_US(500000);
lan91cxx_write_phy(sc, 0, LAN91CXX_PHY_CTRL, LAN91CXX_PHY_CTRL_ANEG_EN |
LAN91CXX_PHY_CTRL_SPEED);
#ifdef LAN91CXX_FORCE_10MHZ
lan91cxx_write_phy( sc, 0, LAN91CXX_PHY_AUTO_AD, 0x0061);
#endif
// Start auto-negotiation
put_reg(sc, LAN91CXX_RPCR,
LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK | LAN91CXX_RPCR_ANEG);
// wait for auto-negotiation to finish.
// give it ~5 seconds before giving up (no cable?)
delay = 50;
while (!(lan91cxx_read_phy(sc, 0, LAN91CXX_PHY_STAT) & 0x20)) {
if (--delay <= 0)
break;
CYGACC_CALL_IF_DELAY_US(100000);
}
#if DEBUG & 1
if (delay <= 0)
diag_printf("auto-negotiation failed.\n");
#endif
#endif
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu);
put_reg(sc, LAN91CXX_INTERRUPT, 0); // disable interrupts
intr = get_reg(sc, LAN91CXX_INTERRUPT);
put_reg(sc, LAN91CXX_INTERRUPT, intr & // ack old interrupts
(LAN91CXX_INTERRUPT_TX_INT | LAN91CXX_INTERRUPT_TX_EMPTY_INT |
LAN91CXX_INTERRUPT_RX_OVRN_INT | LAN91CXX_INTERRUPT_ERCV_INT));
put_reg(sc, LAN91CXX_RCR,
#ifdef RCR_HAS_ABORT_ENB // 91C96 does not - page 46.
LAN91CXX_RCR_ABORT_ENB |
#endif
LAN91CXX_RCR_STRIP_CRC |
LAN91CXX_RCR_RXEN | LAN91CXX_RCR_ALMUL);
put_reg(sc, LAN91CXX_TCR, LAN91CXX_TCR_TXENA | LAN91CXX_TCR_PAD_EN);
put_reg(sc, LAN91CXX_CONTROL, 0);
put_reg(sc, LAN91CXX_INTERRUPT, // enable interrupts
LAN91CXX_INTERRUPT_RCV_INT_M);
#ifdef CYGPKG_NET
if (( 0
#ifdef ETH_DRV_FLAGS_PROMISC_MODE
!= (flags & ETH_DRV_FLAGS_PROMISC_MODE)
#endif
) || (ifp->if_flags & IFF_PROMISC)
) {
// Then we select promiscuous mode.
unsigned short rcr;
rcr = get_reg(sc, LAN91CXX_RCR );
rcr |= LAN91CXX_RCR_PRMS;
put_reg(sc, LAN91CXX_RCR, rcr );
}
#endif
}
//
// This routine is called to perform special "control" opertions
//
static int
lan91cxx_control(struct eth_drv_sc *sc, unsigned long key,
void *data, int data_length)
{
unsigned char *esa = (unsigned char *)data;
int i;
unsigned short reg;
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
DEBUG_FUNCTION();
switch (key) {
case ETH_DRV_SET_MAC_ADDRESS:
#if 9 & DEBUG
db_printf("LAN91CXX - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
esa[0],
esa[1],
esa[2],
esa[3],
esa[4],
esa[5] );
#ifndef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
db_printf("*** PERMANENT EEPROM WRITE NOT ENABLED ***\n");
#endif
#endif // DEBUG
#ifdef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
// Only now can we command the chip to perform EEPROM writes:
// select arbitrary writing to the EEPROM
reg = get_reg(sc, LAN91CXX_CONTROL);
reg |= LAN91CXX_CONTROL_EEPROM_SELECT;
put_reg(sc, LAN91CXX_CONTROL, reg );
for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
int j;
// Set the address register
put_reg(sc, LAN91CXX_POINTER, LAN91CXX_ESA_EEPROM_OFFSET + i/2);
// Poke the data
put_reg(sc, LAN91CXX_GENERAL, esa[i] | (esa[i+1] << 8));
// Command the store
reg = get_reg(sc, LAN91CXX_CONTROL);
reg |= LAN91CXX_CONTROL_STORE;
put_reg(sc, LAN91CXX_CONTROL, reg );
// and poll for completion
for ( j = 1024 * 1024; 0 < j ; j-- ) {
reg = get_reg(sc, LAN91CXX_CONTROL);
if ( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY) )
break;
}
CYG_ASSERT( 0 < j, "EEPROM write timout!" );
}
reg = get_reg(sc, LAN91CXX_CONTROL);
CYG_ASSERT( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY),
"EEPROM still busy!" );
// Clear the EEPROM selection bit
reg &=~LAN91CXX_CONTROL_EEPROM_SELECT;
put_reg(sc, LAN91CXX_CONTROL, reg );
// and check it "took"
reg = get_reg(sc, LAN91CXX_CONTROL);
CYG_ASSERT( 0 == (reg & LAN91CXX_CONTROL_EEPROM_SELECT),
"EEPROM still selected!" );
// and command a complete reload
reg |= LAN91CXX_CONTROL_RELOAD;
put_reg(sc, LAN91CXX_CONTROL, reg );
for ( i = 1024 * 1024; 0 < i ; i-- ) {
reg = get_reg(sc, LAN91CXX_CONTROL);
if ( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY) )
break;
}
CYG_ASSERT( 0 < i, "EEPROM reload timout!" );
// Now extract the MAC address that is in the chip, and tell the
// system about it.
for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 );
cpd->enaddr[i] = (unsigned char)(0xff & z);
cpd->enaddr[i+1] = (unsigned char)(0xff & (z >> 8));
}
#if DEBUG & 9
db_printf("LAN91CXX - eeprom new ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
cpd->enaddr[0],
cpd->enaddr[1],
cpd->enaddr[2],
cpd->enaddr[3],
cpd->enaddr[4],
cpd->enaddr[5] );
#endif // DEBUG
for (i = 0; i < sizeof(cpd->enaddr); i++ ) {
CYG_ASSERT( esa[i] == cpd->enaddr[i], "ESA not written correctly" );
if ( esa[i] != cpd->enaddr[i] )
return 1; // the operation failed.
}
#else // not CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
// Whatever, we can write the MAC address into the interface info,
// and the chip registers no problem.
for ( i = 0; i < sizeof(cpd->enaddr); i++ )
cpd->enaddr[i] = esa[i];
for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
reg = cpd->enaddr[i] | (cpd->enaddr[i+1] << 8);
put_reg(sc, LAN91CXX_IA01+i/2, reg );
}
#endif // !CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
return 0;
#ifdef ETH_DRV_GET_MAC_ADDRESS
case ETH_DRV_GET_MAC_ADDRESS:
// Extract the MAC address that is in the chip, and tell the
// system about it.
for (i = 0; i < sizeof(cpd->enaddr); i += 2) {
unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 );
esa[i] = (unsigned char)(0xff & z);
esa[i+1] = (unsigned char)(0xff & (z >> 8));
}
return 0;
#endif
#ifdef ETH_DRV_GET_IF_STATS_UD
case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE
#endif
// drop through
#ifdef ETH_DRV_GET_IF_STATS
case ETH_DRV_GET_IF_STATS:
#endif
#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
{
struct ether_drv_stats *p = (struct ether_drv_stats *)data;
// Chipset entry is no longer supported; RFC1573.
for ( i = 0; i < SNMP_CHIPSET_LEN; i++ )
p->snmp_chipset[i] = 0;
// This perhaps should be a config opt, so you can make up your own
// description, or supply it from the instantiation.
strcpy( p->description, "SMSC LAN91Cxx" );
// CYG_ASSERT( 48 > strlen(p->description), "Description too long" );
reg = get_reg(sc, LAN91CXX_EPH_STATUS);
if ((reg & LAN91CXX_STATUS_LINK_OK) == 0) {
p->operational = 2; // LINK DOWN
p->duplex = 1; // UNKNOWN
p->speed = 0;
}
else {
p->operational = 3; // LINK UP
p->duplex = 2; // 2 = SIMPLEX, 3 = DUPLEX
p->speed = 10 * 1000000; // it's only a 10Mbit device
}
#ifdef KEEP_STATISTICS
{
struct smsc_lan91cxx_stats *ps = &(cpd->stats);
// Admit to it...
p->supports_dot3 = true;
p->tx_good = ps->tx_good ;
p->tx_max_collisions = ps->tx_max_collisions ;
p->tx_late_collisions = ps->tx_late_collisions ;
p->tx_underrun = ps->tx_underrun ;
p->tx_carrier_loss = ps->tx_carrier_loss ;
p->tx_deferred = ps->tx_deferred ;
p->tx_sqetesterrors = ps->tx_sqetesterrors ;
p->tx_single_collisions = ps->tx_single_collisions;
p->tx_mult_collisions = ps->tx_mult_collisions ;
p->tx_total_collisions = ps->tx_total_collisions ;
p->rx_good = ps->rx_good ;
p->rx_crc_errors = ps->rx_crc_errors ;
p->rx_align_errors = ps->rx_align_errors ;
p->rx_resource_errors = ps->rx_resource_errors ;
p->rx_overrun_errors = ps->rx_overrun_errors ;
p->rx_collisions = ps->rx_collisions ;
p->rx_short_frames = ps->rx_short_frames ;
p->rx_too_long_frames = ps->rx_too_long_frames ;
p->rx_symbol_errors = ps->rx_symbol_errors ;
p->interrupts = ps->interrupts ;
p->rx_count = ps->rx_count ;
p->rx_deliver = ps->rx_deliver ;
p->rx_resource = ps->rx_resource ;
p->rx_restart = ps->rx_restart ;
p->tx_count = ps->tx_count ;
p->tx_complete = ps->tx_complete ;
p->tx_dropped = ps->tx_dropped ;
}
#endif // KEEP_STATISTICS
p->tx_queue_len = 1;
return 0; // OK
}
#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?