if_upd985xx.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,622 行 · 第 1/4 页
C
1,622 行
bp->attr =
ETH_BUF_LAST |
ETH_BUF_D_L_DATA |
ETH_BUF_OWN_CPU |
(ETH_BUF_SIZE & 0);
}
}
// ------------------------------------------------------------------------
//
// Function : TxDone (Called from delivery thread)
//
// This returns Tx's from the Tx Machine to the stack (ie. reports
// completion) - allowing for missed interrupts, and so on.
// ------------------------------------------------------------------------
STATIC void TxDone(struct eth_upd985xx* p_eth_upd985xx)
{
struct cyg_netdevtab_entry *ndp;
struct eth_drv_sc *sc;
ndp = (struct cyg_netdevtab_entry *)(p_eth_upd985xx->ndp);
sc = (struct eth_drv_sc *)(ndp->device_instance);
CHECK_NDP_SC_LINK();
if ( p_eth_upd985xx->tx_busy ) {
cyg_uint32 ss;
ss = INL( ETH_TXSR ); // Get tx status
if ( ss & (ETH_TXSR_CSE |
ETH_TXSR_TUDR |
ETH_TXSR_TGNT |
ETH_TXSR_LCOL |
ETH_TXSR_ECOL |
ETH_TXSR_TEDFR |
ETH_TXSR_TDFR |
ETH_TXSR_TBRO |
ETH_TXSR_TMUL |
ETH_TXSR_TDONE ) ) {
// Then it finished; somehow...
cyg_uint32 key = p_eth_upd985xx->tx_keys[ 0 ];
// Turn off the transmitter (before the callback to the stack).
OUTL( ETH_TXCR, 0 );
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E8
// Must take action after certain types of tx failure:
if ( ss & (ETH_TXSR_TUDR |
ETH_TXSR_LCOL |
ETH_TXSR_ECOL) ) {
p_eth_upd985xx->count_bad_tx_completion++;
CYG_ASSERT ( p_eth_upd985xx->active, "Device not active!" );
eth_upd985xx_stop( sc );
eth_upd985xx_start( sc, NULL, 0 );
key = 0; // Important! Stop above already fed it back.
}
#endif
#ifdef DEBUG_TRAFFIC
os_printf("TxDone %d %x: KEY %x\n",
p_eth_upd985xx->index, (int)p_eth_upd985xx, key );
#endif
// Finished, ready for the next one
p_eth_upd985xx->tx_keys[ 0 ] = 0;
p_eth_upd985xx->tx_busy = 0;
// Then tell the stack we are done:
if (key) {
(sc->funs->eth_drv->tx_done)( sc, key,
0 == (p_eth_upd985xx->intrs & ETH_ISR_TABR) );
}
}
}
}
// ------------------------------------------------------------------------
//
// Function : eth_upd985xx_can_send
//
// ------------------------------------------------------------------------
STATIC int
eth_upd985xx_can_send(struct eth_drv_sc *sc)
{
struct eth_upd985xx *p_eth_upd985xx;
p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;
// Guard possible external entry points
if ( ! p_eth_upd985xx->active )
return 0;
return ! p_eth_upd985xx->tx_busy;
}
// ------------------------------------------------------------------------
//
// Function : eth_upd985xx_send
//
// ------------------------------------------------------------------------
STATIC void
eth_upd985xx_send(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list, int sg_len, int total_len,
unsigned long key)
{
struct eth_upd985xx *p_eth_upd985xx;
struct eth_drv_sg *last_sg;
volatile struct bufdesc *bp;
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3
struct eth_drv_sg local_sg[2];
#endif
p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;
#ifdef DEBUG_TRAFFIC
os_printf("Tx %d %x: %d sg's, %d bytes, KEY %x\n",
p_eth_upd985xx->index, (int)p_eth_upd985xx, sg_len, total_len, key );
#endif
if ( ! p_eth_upd985xx->active )
return; // device inactive, no return
CYG_ASSERT( ! p_eth_upd985xx->tx_busy, "Can't send when busy!" );
p_eth_upd985xx->tx_busy++;
p_eth_upd985xx->tx_keys[0] = key;
bp = &p_eth_upd985xx->txring[0]; // Current free tx
CYG_ASSERT( 0 < sg_len, "sg_len underflow" );
CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" );
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3
// We must copy any Tx that is more than two SGs into just one buffer.
if ( sg_len > 2 ) {
cyg_uint8 *from_p, *to_p;
to_p = &tx_databuf[0]; // normal cached address
if ( sizeof( tx_databuf ) < total_len )
total_len = sizeof( tx_databuf );
for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
int l;
from_p = (cyg_uint8 *)(sg_list->buf); // normal cached address
l = sg_list->len;
if ( l > total_len )
l = total_len;
memcpy( to_p, from_p, l ); // All in cached memory
to_p += l;
total_len -= l;
if ( 0 > total_len )
break; // Should exit via sg_last normally
}
// Set up SGs describing the single tx buffer
total_len = to_p - &tx_databuf[0];
local_sg[0].buf = (CYG_ADDRESS)&tx_databuf[0];
local_sg[0].len = (CYG_ADDRWORD)total_len;
local_sg[1].buf = (CYG_ADDRESS)0;
local_sg[1].len = (CYG_ADDRWORD)0;
// And make the subsequent code use it.
sg_len = 1;
sg_list = &local_sg[0];
}
#endif
for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
cyg_uint8 *from_p;
int l;
from_p = (cyg_uint8 *)(sg_list->buf); // normal cached address
l = sg_list->len;
if ( l > total_len )
l = total_len;
// Ensure the mbuf contents really is in RAM where DMA can see it.
// (Must round to cache lines apparantly for 4120)
HAL_DCACHE_STORE( ((CYG_ADDRESS)from_p) &~(HAL_DCACHE_LINE_SIZE-1),
l + HAL_DCACHE_LINE_SIZE );
bp->ptr = VIRT_TO_BUS( from_p ); // uncached real RAM address
bp->attr &=~(ETH_BUF_LAST | ETH_BUF_SIZE);
bp->attr |= ETH_BUF_SIZE & l;
bp->attr |= ETH_BUF_D_L_DATA;
total_len -= l;
bp++;
if ( 0 > total_len )
break; // Should exit via sg_last normally
}
CYG_ASSERT( bp > &p_eth_upd985xx->txring[0], "bp underflow" );
CYG_ASSERT( bp < &p_eth_upd985xx->txring[
NUM_ELEMENTS(p_eth_upd985xx->tx_bufdesc)
], "bp underflow" );
bp--;
bp->attr |= ETH_BUF_LAST;
// Make the rest be null links
for ( bp++; bp <
&p_eth_upd985xx->txring[NUM_ELEMENTS(p_eth_upd985xx->tx_bufdesc)];
bp++ ) {
bp->attr = ETH_BUF_D_L_LINK;
bp->ptr = NULL;
}
CYG_ASSERT( 0 == total_len, "length mismatch in tx" );
CYG_ASSERT( last_sg == sg_list, "sg count mismatch in tx" );
// And start off the tx system
// Point the hardware at the list of buffers
OUTL( ETH_TXDPR, (cyg_uint32)p_eth_upd985xx->txring );
// and start the tx.
// Fault E4 - use only 8 for DTBS, not the previously recommended 16.
// Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E4
// but no config opt is provided.
// Fault E7: ETH_TXCR_AFCE must not be used.
// Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E7
// but no config opt is provided.
OUTL( ETH_TXCR, ETH_TXCR_TXE | ETH_TXCR_DTBS_8 /* | ETH_TXCR_AFCE */ );
}
#ifdef CYGPKG_NET
// ------------------------------------------------------------------------
//
// Function : eth_upd985xx_configure
//
// Return : 0 = It worked.
// non0 = It failed.
// ------------------------------------------------------------------------
STATIC int
eth_upd985xx_configure(struct eth_upd985xx* p_eth_upd985xx, int promisc, int oversized)
{
int ss;
// We implement permission of oversize packets by changing LMAX (rather
// than enabling HUGEN in ETH_MACC1) because we rely on only one
// reception per rx descriptor. General oversize packets could eat
// many rx descriptors and we would become ...confused.
// Sanity check the numbers we're about to use.
CYG_ASSERT( sizeof( rx_databuf[0] ) >= MAX_OVERSIZE_PACKET_SIZE,
"Oversize packet would overflow rx buffer" );
CYG_ASSERT( sizeof( rx_databuf[0] ) >= MAX_ETHERNET_PACKET_SIZE,
"Ethernet packet would overflow rx buffer" );
if ( oversized )
OUTL( ETH_LMAX, MAX_OVERSIZE_PACKET_SIZE );
else
OUTL( ETH_LMAX, MAX_ETHERNET_PACKET_SIZE );
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2
ss = promisc ? 1 : 0; // avoid unused var warning
p_eth_upd985xx->promisc = ss;
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY
// Then we must also set the mode in the chip
ss = INL( ETH_AFR );
if ( promisc )
ss |= ETH_AFR_PRO;
else
ss &=~ETH_AFR_PRO;
OUTL( ETH_AFR, ss );
#endif // CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY
#else
ss = INL( ETH_AFR );
if ( promisc )
ss |= ETH_AFR_PRO;
else
ss &=~ETH_AFR_PRO;
OUTL( ETH_AFR, ss );
#endif
return 0; // OK
}
#endif
// ------------------------------------------------------------------------
//
// Function : eth_upd985xx_ioctl
//
// ------------------------------------------------------------------------
STATIC int
eth_upd985xx_ioctl(struct eth_drv_sc *sc, unsigned long key,
void *data, int data_length)
{
struct eth_upd985xx *p_eth_upd985xx;
p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;
#ifdef DEBUG_IOCTL
db_printf( "eth_upd985xx_ioctl: device eth%d at %x; key is 0x%x, data at %x[%d]\n",
p_eth_upd985xx->index, p_eth_upd985xx, key, data, data_length );
#endif
// DO NOT guard possible external entry points - want to be able eg. to
// set a mac address of a down interface before bringing it up!
switch ( key ) {
#ifdef ETH_DRV_SET_MAC_ADDRESS
case ETH_DRV_SET_MAC_ADDRESS:
if ( 6 != data_length )
return -2;
return eth_set_mac_address( p_eth_upd985xx, data );
#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;
int i;
// 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, "NEC uPD985xx on-chip ethernet (CANDY)" );
// CYG_ASSERT( 48 > strlen(p->description), "Description too long" );
i = eth_upd985xx_status( p_eth_upd985xx );
if ( !( i & PHY_STATUS_LINK) ) {
p->operational = 2; // LINK DOWN
p->duplex = 1; // UNKNOWN
p->speed = 0;
}
else {
p->operational = 3; // LINK UP
p->duplex = (i & PHY_STATUS_FDX) ? 3 : 2; // 2 = SIMPLEX, 3 = DUPLEX
p->speed = ((i & PHY_STATUS_100MBPS) ? 100 : 10) * 1000000;
}
// Admit to it...
p->supports_dot3 = true;
// Those commented out are not available on this chip.
p->tx_good = INL( ETH_TPCT ) ;
p->tx_max_collisions = INL( ETH_TXCL ) ;
p->tx_late_collisions = INL( ETH_TLCL ) ;
//p->tx_underrun = INL( ) ;
p->tx_carrier_loss = INL( ETH_TCSE ) ;
p->tx_deferred = INL( ETH_TDFR ) +
INL( ETH_TXDF ) ;
//p->tx_sqetesterrors = INL( ) ;
p->tx_single_collisions = INL( ETH_TSCL ) ;
p->tx_mult_collisions = INL( ETH_TMCL ) ;
p->tx_total_collisions = INL( ETH_TSCL ) +
INL( ETH_TMCL ) +
INL( ETH_TLCL ) +
INL( ETH_TXCL ) ;
p->rx_good = INL( ETH_RPKT ) ;
p->rx_crc_errors = INL( ETH_RFCS ) ;
p->rx_align_errors = INL( ETH_RALN ) ;
p->rx_resource_errors = p_eth_upd985xx->count_rx_resource;
//p->rx_overrun_errors = INL( ) ;
//p->rx_collisions = INL( ) ;
p->rx_short_frames = INL( ETH_RUND ) ;
p->rx_too_long_frames = INL( ETH_ROVR ) ;
p->rx_symbol_errors = INL( ETH_RXUO ) ;
p->interrupts = p_eth_upd985xx->count_interrupts;
p->rx_count = INL( ETH_RBYT ) ;
p->rx_deliver = INL( ETH_RPKT ) ;
p->rx_resource = p_eth_upd985xx->count_rx_resource;
p->rx_restart = p_eth_upd985xx->count_rx_resource +
p_eth_upd985xx->count_rx_restart;
p->tx_count = INL( ETH_TBYT ) ;
p->tx_complete = INL( ETH_TPCT ) ;
p->tx_dropped = INL( ETH_TNCL ) ;
p->tx_queue_len = 1;
return 0; // OK
}
#endif
default:
break;
}
return -1;
}
// ------------------------------------------------------------------------
// EOF if_upd985xx.c
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?