if_lan91cxx.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,486 行 · 第 1/4 页
C
1,486 行
default:
break;
}
return 1;
}
//
// This routine is called to see if it is possible to send another packet.
// It will return non-zero if a transmit is possible, zero otherwise.
//
static int
lan91cxx_can_send(struct eth_drv_sc *sc)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
int tcr;
// DEBUG_FUNCTION();
#ifndef LAN91CXX_IS_LAN91C111
// LINK_OK on 91C111 is just a general purpose input and may not
// have anything to do with the link.
if ((get_reg(sc, LAN91CXX_EPH_STATUS) & LAN91CXX_STATUS_LINK_OK) == 0) {
db_printf("no link\n");
return false; // Link not connected
}
#endif
CYG_ASSERT( cpd->within_send < 10, "can_send: Excess send recursions" );
cpd->within_send++;
tcr = get_reg(sc, LAN91CXX_TCR);
if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {
#if DEBUG & 1
db_printf("%s: ENGINE RESTART: tcr 0x%04x\n", __FUNCTION__, tcr );
#endif
// Complete any outstanding activity:
if ( cpd->txbusy ) {
cpd->txbusy = 0;
#if DEBUG & 9
db_printf("LAN91CXX - can_send, cleaning up pending TX\n" );
#endif
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
}
tcr |= LAN91CXX_TCR_TXENA;
put_reg(sc, LAN91CXX_TCR, tcr);
}
// This helps unstick deadly embraces.
lan91cxx_poll( sc ); // Deal with any outstanding rx state
cpd->within_send--;
return (cpd->txbusy == 0) && (0 == cpd->within_send);
}
//
// This routine is called to send data to the hardware.
static void
lan91cxx_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
int i, len, plen, tcr;
cyg_uint8 *sdata;
cyg_uint16 data = 0;
int dpos = 0;
unsigned short ints, control;
cyg_uint16 packet, status;
DEBUG_FUNCTION();
INCR_STAT( tx_count );
#if DEBUG & 1
ints = get_reg(sc, LAN91CXX_INTERRUPT);
db_printf("%s:START: ints: %04x\n", __FUNCTION__, ints);
#endif
// Worry about the TX engine stopping.
tcr = get_reg(sc, LAN91CXX_TCR);
if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {
#if DEBUG & 1
db_printf("%s: ENGINE RESTART: tcr 0x%04x\n", __FUNCTION__, tcr );
#endif
tcr |= LAN91CXX_TCR_TXENA;
put_reg(sc, LAN91CXX_TCR, tcr);
}
// This helps unstick deadly embraces.
CYG_ASSERT( cpd->within_send < 10, "send: Excess send recursions" );
cpd->within_send++;
lan91cxx_poll( sc ); // Deal with any outstanding rx state
cpd->within_send--;
cpd->txbusy = 1;
cpd->txkey = key;
// Find packet length
plen = 0;
for (i = 0; i < sg_len; i++)
plen += sg_list[i].len;
CYG_ASSERT( plen == total_len, "sg data length mismatch" );
// Alloc new TX packet
do {
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_alloc_for_tx
#ifndef LAN91CXX_IS_LAN91C111
| ((plen >> 8) & 0x07)
#endif
);
i = 1024 * 1024;
do {
status = get_reg(sc, LAN91CXX_INTERRUPT);
} while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT) && (--i > 0) );
if ( i )
packet = get_reg(sc, LAN91CXX_PNR);
else
packet = 0xffff;
#if DEBUG & 1
db_printf("%s: allocated packet %04x\n", __FUNCTION__, packet);
#endif
packet = packet >> 8;
if (packet & 0x80) {
// Hm.. Isn't this a dead end?
#if DEBUG & 1
db_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ );
#endif
// Not if we can make progress with what's filling memory.
lan91cxx_poll( sc ); // Deal with any outstanding state
continue;
}
} while (0);
#if DEBUG & 4
db_printf("#####Tx packet allocated 0x%04x (previous 0x%04x)\n",
packet, cpd->txpacket);
#endif
cpd->txpacket = packet;
put_reg(sc, LAN91CXX_PNR, packet);
// Note: Check FIFO state here before continuing?
put_reg(sc, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000);
// Pointer is now set, and the proper bank is selected for
// data writes.
// Prepare header:
put_data(sc, CYG_CPU_TO_LE16(0)); // reserve space for status word
// packet length (includes status, byte-count and control shorts)
put_data(sc, CYG_CPU_TO_LE16(0x7FE & (plen + 6)) ); // Always even, always < 15xx(dec)
for (i = 0; i < sg_len; i++) {
sdata = (cyg_uint8 *)sg_list[i].buf;
len = sg_list[i].len;
while( len > 0 )
{
data |= *sdata<<((dpos&1)*8);
dpos++, len--, sdata++;
if( (dpos & 1) == 0 )
{
put_data(sc, CYG_CPU_TO_LE16(data));
data = 0;
}
}
}
// Lay down the control short unconditionally at the end.
// (or it might use random memory contents)
control = 0;
if( 1 & plen ) {
// Need to set ODD flag and insert the data
control = data;
control |= LAN91CXX_CONTROLBYTE_ODD;
}
control |= LAN91CXX_CONTROLBYTE_CRC; // Just in case...
put_data(sc, CYG_CPU_TO_LE16(control));
// Enqueue the packet
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet);
// Ack TX empty int and unmask it.
ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00;
put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_ACK);
put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_M);
#if DEBUG & 1
ints = get_reg(sc, LAN91CXX_INTERRUPT);
db_printf("%s:END: ints at TX: %04x\n", __FUNCTION__, ints);
#endif
}
static void
lan91cxx_TxEvent(struct eth_drv_sc *sc, int stat)
{
unsigned short packet, ints, tcr;
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
int success = 1;
DEBUG_FUNCTION();
INCR_STAT( tx_complete );
// Ack and mask TX interrupt set
ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00;
ints |= LAN91CXX_INTERRUPT_TX_SET_ACK;
ints &= ~LAN91CXX_INTERRUPT_TX_SET_M;
put_reg(sc, LAN91CXX_INTERRUPT, ints);
// Get number of completed packet and read the status word
packet = get_reg(sc, LAN91CXX_FIFO_PORTS);
#if DEBUG & 1
db_printf("%s:START: fifo %04x ints %04x\n", __FUNCTION__, packet, ints);
#endif
#ifdef KEEP_STATISTICS
{
unsigned short reg;
reg = get_reg( sc, LAN91CXX_EPH_STATUS );
// Covering each bit in turn...
if ( reg & LAN91CXX_STATUS_TX_UNRN ) INCR_STAT( tx_underrun );
//if ( reg & LAN91CXX_STATUS_LINK_OK ) INCR_STAT( );
//if ( reg & LAN91CXX_STATUS_CTR_ROL ) INCR_STAT( );
//if ( reg & LAN91CXX_STATUS_EXC_DEF ) INCR_STAT( );
if ( reg & LAN91CXX_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss );
if ( reg & LAN91CXX_STATUS_LATCOL ) INCR_STAT( tx_late_collisions );
//if ( reg & LAN91CXX_STATUS_WAKEUP ) INCR_STAT( );
if ( reg & LAN91CXX_STATUS_TX_DEFR ) INCR_STAT( tx_deferred );
//if ( reg & LAN91CXX_STATUS_LTX_BRD ) INCR_STAT( );
if ( reg & LAN91CXX_STATUS_SQET ) INCR_STAT( tx_sqetesterrors );
if ( reg & LAN91CXX_STATUS_16COL ) INCR_STAT( tx_max_collisions );
//if ( reg & LAN91CXX_STATUS_LTX_MULT) INCR_STAT( );
if ( reg & LAN91CXX_STATUS_MUL_COL ) INCR_STAT( tx_mult_collisions );
if ( reg & LAN91CXX_STATUS_SNGL_COL ) INCR_STAT( tx_single_collisions );
if ( reg & LAN91CXX_STATUS_TX_SUC ) INCR_STAT( tx_good );
cpd->stats.tx_total_collisions =
cpd->stats.tx_late_collisions +
cpd->stats.tx_max_collisions +
cpd->stats.tx_mult_collisions +
cpd->stats.tx_single_collisions;
// We do not need to look in the Counter Register (LAN91CXX_COUNTER)
// because it just mimics the info we already have above.
}
#endif // KEEP_STATISTICS
// We do not really care about Tx failure. Ethernet is not a reliable
// medium. But we do care about the TX engine stopping.
tcr = get_reg(sc, LAN91CXX_TCR);
if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {
#if DEBUG & 1
db_printf("%s: ENGINE RESTART: tcr 0x%04x eph 0x%04x ints 0x%04x\n", __FUNCTION__, tcr, get_reg(sc, LAN91CXX_EPH_STATUS), ints);
#endif
tcr |= LAN91CXX_TCR_TXENA;
put_reg(sc, LAN91CXX_TCR, tcr);
success = 0; // And treat this as an error...
}
packet &= 0xff;
// It certainly appears that occasionally the tx fifo tells lies; we
// get the wrong packet number. Freeing the one we allocated seems to
// give correct operation.
#ifdef CYGPKG_INFRA_DEBUG
// Then we log, OOI, the number of times we get a bad packet number
// from the tx done fifo.
if (cpd->txpacket != packet )
lan91cxx_txfifo_bad++;
else
lan91cxx_txfifo_good++;
#endif
#if DEBUG & 4
db_printf("#####Tx packet freed 0x%04x (expected 0x%04x)\n", packet, cpd->txpacket );
#endif
// and then free the packet
put_reg(sc, LAN91CXX_PNR, cpd->txpacket);
put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet);
// Ack the TX int which is supposed to clear the packet from the TX
// completion queue.
ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00;
ints |= LAN91CXX_INTERRUPT_TX_FIFO_ACK;
put_reg(sc, LAN91CXX_INTERRUPT, ints);
#if DEBUG & 1
// Hm... The free doesn't seem to have the desired effect?!?
ints = get_reg(sc, LAN91CXX_INTERRUPT);
packet = get_reg(sc, LAN91CXX_FIFO_PORTS);
db_printf("%s:END: fifo %04x ints %04x\n", __FUNCTION__, packet, ints);
#endif
if ( cpd->txbusy ) {
cpd->txbusy = 0;
(sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success);
}
}
void get_data_init(struct eth_drv_sc *sc)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
cpd->data_buf = 0xa5a5a5a5;
cpd->data_pos = sizeof(rxd_t);
}
cyg_uint8 get_data_byte(struct eth_drv_sc *sc)
{
cyg_uint8 c;
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
if( cpd->data_pos == sizeof(rxd_t) )
{
cpd->data_buf = get_data(sc);
cpd->data_pos = 0;
}
c = (cpd->data_buf>>(cpd->data_pos*8))&0xFF;
cpd->data_pos++;
return c;
}
cyg_uint16 get_data_short(struct eth_drv_sc *sc)
{
cyg_uint16 val;
val = get_data_byte(sc);
val |= get_data_byte(sc)<<8;
return CYG_LE16_TO_CPU(val);
}
//
// This function is called when a packet has been received. Its 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
// 'lan91cxx_recv' will be called to actually fetch it from the hardware.
//
static void
lan91cxx_RxEvent(struct eth_drv_sc *sc)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
unsigned short stat, len;
DEBUG_FUNCTION();
stat = get_reg(sc, LAN91CXX_FIFO_PORTS);
#if DEBUG & 1
db_printf("RxEvent - FIFOs: 0x%04x\n", stat);
#endif
if ( 0x8000 & stat ) {
// Then the Rx FIFO is empty
#if DEBUG & 4
db_printf("#####RxEvent with empty fifo\n");
#endif
return;
}
INCR_STAT( rx_count );
#if DEBUG & 4
db_printf("#####Rx packet allocated 0x%04x (previous 0x%04x)\n",
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?