📄 lan91c111.c
字号:
#endif
// bank 2 already opened by main IRQ entry
packet_number = _inpw(FIFO_REG);
if ( packet_number & RXFIFO_REMPTY ) {
// we got called , but nothing was on the FIFO
// so, just leave
return;
}
// start reading from the start of the packet
_outpw(PTR_REG, PTR_READ | PTR_RCV | PTR_AUTOINC);
// First two words are status and packet_length
status = _inpw(DATA_REG);
packet_length = _inpw(DATA_REG);
// store for debug
LAN_state.rpsize = packet_length;
// max 2k packets over Ethernet
packet_length &= 0x07ff; /* mask off top bits */
if ( !(status & RS_ERRORS ) ){
#ifdef log_net_errs
if ( status & RS_MULTICAST ) stats.multicast++;
#endif
// collect the packet
// first two words already read: status and packet size
packet_length -= 4;
if (!LAN_rx_packet.busy) {
//store packet length
LAN_rx_packet.length = packet_length;
// flag buffer in use and pkt avail
LAN_rx_packet.busy = 1;
// assign pointer
data = LAN_rx_packet.data;
// global flag
LAN_pkt_avail = 1;
} else {
#ifdef log_net_errs
stats.rx_dropped++;
#endif
// drop packet
goto done;
}
#ifndef USE_16_BIT
// read in 32bits
for (i=0; i <= (packet_length >> 2); i++)
{
*data++ = _inpd(DATA_REG);
}
#else
for (i=0; i <= (packet_length >> 1); i++)
{
*data++ = _inpw(DATA_REG);
}
#endif // USE_16_BIT
#ifdef log_net_errs
lp->stats.rx_packets++;
#endif
} else {
// error ...
LAN_error = 1;
#ifdef log_net_errs
stats.rx_errors++;
if ( status & RS_ALGNERR ) lp->stats.rx_frame_errors++;
if ( status & (RS_TOOSHORT|RS_TOOLONG)) lp->stats.rx_length_errors++;
if ( status & RS_BADCRC) lp->stats.rx_crc_errors++;
#endif
}
while ( _inpw(MMU_CMD_REG ) & MC_BUSY );
done:
// good or bad, delete this packet */
_outpw(MMU_CMD_REG, MC_RELEASE);
return;
}
/******************************************************************************
* TX IRQ Error Handler Function for SMSC91C111
*
* Purpose: Handle the transmit error message.
* This is only called when an TX error occured because of
* the AUTO_RELEASE mode.
*
* Actions:
* - Save pointer and packet no
* - Get the packet no from the top of the queue
* - check if it's valid ( if not, is this an error??? )
* - read the status word
* - record the error
* - ( resend? Not really, since we don't want old packets around )
* - Restore saved values
*
*****************************************************************************/
void smc_tx()
{
BYTE saved_packet;
BYTE packet_no;
WORD tx_status;
// bank 2 already opened by main IRQ entry
saved_packet = _inp(PN_REG);
packet_no = _inpw(FIFO_REG);
packet_no &= 0x7F;
// If the TX FIFO is empty then nothing to do
if ( packet_no & TXFIFO_TEMPTY )
return;
// select this as the packet to read from
_outp(PN_REG, packet_no);
// read the first word (status word) from this packet
_outpw(PTR_REG, PTR_AUTOINC|PTR_READ);
// read tx status
tx_status = _inpw(DATA_REG );
#ifdef log_net_errs
stats.tx_errors++;
if ( tx_status & TS_LOSTCAR ) stats.tx_carrier_errors++;
if ( tx_status & TS_LATCOL ) stats.tx_window_errors++;
#endif
// re-enable transmit
_outpw(BSR_REG, 0);
_outpw(TCR_REG, _inpw(TCR_REG) | TCR_TXENA);
// kill the packet
_outpw(BSR_REG, 2);
_outpw(MMU_CMD_REG, MC_FREEPKT);
// one less packet waiting for me
LAN_state.packets_waiting--;
// Don't change Packet Number Reg until busy bit is cleared
while ( _inpw(MMU_CMD_REG) & MC_BUSY );
_outp(PN_REG, saved_packet);
return;
}
/******************************************************************************
* LANC91C111: smc_wait_to_send_packet()
*
* Purpose:
* Attempt to allocate memory for a packet, if chip-memory is not
* available, then tell the card to generate an interrupt when it
* is available.
*
* Actions:
* - if the saved_skb is not currently null, then drop this packet
* - on the floor. This should never happen, because of TBUSY.
* - if the saved_skb is null, then replace it with the current packet,
* - See if I can sending it now.
* - (NO): Enable interrupts and let the interrupt handler deal with it.
* - (YES):Send it now.
*
*****************************************************************************/
BYTE smc_wait_to_send_packet()
{
WORD length;
WORD numPages;
WORD time_out;
volatile WORD status;
// set length
length = MAX_TCP_TX_SIZE < LAN_tx_packet.length ? MAX_TCP_TX_SIZE : LAN_tx_packet.length;
/*
* The MMU wants the number of pages to be the number of 256 bytes
* 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
*
* The 91C111 ignores the size bits, but the code is left intact
* for backwards and future compatibility.
*
* Pkt size for allocating is data length +6 (for additional status
* words, length and ctl!)
*
* If odd size then last byte is included in this header.
*/
numPages = ((length & 0xfffe) + 6);
numPages >>= 8; // Divide by 256
if (!numPages) numPages++;
if (numPages > 7 ) {
// way too big packet, this is an error
// free packet
LAN_tx_packet.busy = 0;
return 0;
}
// either way, a packet is waiting now
LAN_state.packets_waiting++;
// now, try to allocate the memory */
_outpw(BSR_REG, 2);
_outpw(MMU_CMD_REG, MC_ALLOC | numPages);
/*
* Performance Hack
*
* wait a short amount of time.. if I can send a packet now, I send
* it now. Otherwise, I enable an interrupt and wait for one to be
* available.
*
* I could have handled this a slightly different way, by checking to
* see if any memory was available in the FREE MEMORY register. However,
* either way, I need to generate an allocation, and the allocation works
* no matter what, so I saw no point in checking free memory.
*/
// time_out = 200; // number of iterations
do {
status = _inp(INT_REG);
if ( status & IM_ALLOC_INT ) {
/* acknowledge the interrupt */
_outp(INT_REG, IM_ALLOC_INT);
break;
}
} while (1);// --time_out );
// too bad, no immediate allocation...
if ( 0){//!time_out ) {
// oh well, wait until the chip finds memory later
smc_enable_int( IM_ALLOC_INT );
/* Check the status bit one more time just in case */
/* it snuk in between the time we last checked it */
/* and when we set the interrupt bit */
status = _inp(INT_REG );
if ( !(status & IM_ALLOC_INT) ) return 0;
// Looks like it did sneak in, so disable
// the interrupt
smc_disable_int( IM_ALLOC_INT );
}
// or YES - I can send the packet now..
smc_hardware_send_packet();
return 1;
}
/******************************************************************************
* LANC91C111: smc_allocate_packet()
*
* Purpose:
* Attempt to allocate memory for a packet, if chip-memory is not
* available, then tell the card to generate an interrupt when it
* is available.
*
* Actions:
*
*****************************************************************************/
BYTE smc_allocate_packet()
{
WORD length;
WORD numPages;
WORD time_out;
WORD status;
// set length
length = MAX_TCP_TX_SIZE < LAN_tx_packet.length ? MAX_TCP_TX_SIZE : LAN_tx_packet.length;
// clear allocation flag
LAN_state.alloc_success = 0;
/*
* The MMU wants the number of pages to be the number of 256 bytes
* 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
*
* Pkt size for allocating is data length +6 (for additional status
* words, length and ctl!)
*
* If odd size then last byte is included in this header.
*/
numPages = ((length & 0xfffe) + 6);
numPages >>= 8; // Divide by 256
if (numPages > 7 ) {
// way too big packet, this is an error
// free packet
LAN_tx_packet.busy = 0;
return 0;
}
// either way, a packet is waiting now
LAN_state.packets_waiting++;
// now, try to allocate the memory */
_outpw(BSR_REG, 2);
_outpw(MMU_CMD_REG, MC_ALLOC | numPages);
/*
* Performance Hack
*
* wait a short amount of time.. if I can allocate a packet now, I set
* the flag now. Otherwise, I enable an interrupt and wait for one to be
* available.
*
*/
time_out = 200; // number of iterations
do {
status = _inp(INT_REG);
if ( status & IM_ALLOC_INT ) {
/* acknowledge the interrupt */
_outp(INT_REG, IM_ALLOC_INT);
break;
}
} while ( --time_out );
// too bad, no immediate allocation...
if ( !time_out )
{
/* Check the status bit one more time just in case */
/* it snuk in between the time we last checked it */
/* and when we set the interrupt bit */
status = _inp(INT_REG );
if ( !(status & IM_ALLOC_INT) ) return 0;
}
// or YES - I got my memory now...
LAN_state.alloc_success = 1;
return 1;
}
/******************************************************************************
* LANC91C111: smc_hardware_send_packet
*
* Purpose:
* This sends the actual packet to the SMC9xxx chip.
*
* Actions:
* - First, see if a pending packet is available.
* this should NOT be called if there is none
* - Find the packet number that the chip allocated
* - Point the data pointers at it in memory
* - Set the length word in the chip's memory
* - Dump the packet to chip memory
* - Check if a last byte is needed ( odd length packet )
* if so, set the control flag right
* - Tell the LAn 91C111 to send it
* - Enable the transmit interrupt, so I know if it failed
* - Free the packet data if I actually sent it.
*
*****************************************************************************/
void smc_hardware_send_packet()
{
BYTE packet_no;
WORD length;
int i;
#ifndef USE_16_BIT
DWORD *buf;
#else
WORD *buf;
#endif
// no data to send...
if ( !LAN_tx_packet.busy ) return;
// get pointer
buf = LAN_tx_packet.data;
// get length
length = LAN_tx_packet.length;
// If I get here, I _know_ there is a packet slot waiting for me
packet_no = _inp(AR_REG);
// or isn't there? BAD CHIP
if ( packet_no & AR_FAILED ) return;
// we have a packet address, so tell the card to use it
_outp(PN_REG, packet_no);
// point to the beginning of the packet
_outpw(PTR_REG, PTR_AUTOINC);
// send the packet length ( +6 for status, length and ctl byte )
// and the status word ( set to zeros ) */
#ifndef USE_16_BIT
_outpd(DATA_REG, (length+6) << 16);
#else
_outpw(DATA_REG, 0);
_outpw(DATA_REG, (length+6));
#endif
// send the actual data
#ifndef USE_16_BIT
for (i=0; i < (length>>2); i++)
_outpd(DATA_REG, *buf++);
// last 16 bit word
if ( length & 0x2 ) _outpw(DATA_REG, *buf);
#else
for (i=0; i < (length>>1); i++)
_outpw(DATA_REG, *buf++);
#endif // USE_32_BIT
/* Send the last byte, if there is one. */
if ((length & 1) == 0)
{
// write 0 as CONTROL BYTE
_outpw(DATA_REG, 0x0000);
}
else
{
#ifndef USE_16_BIT
// last byte to be sent
if (length & 2) *buf = (*buf>>16);
#endif
// last byte to be sent
_outpw(DATA_REG, 0x2000 | (*buf) & 0xff);
}
// enable the interrupts
smc_enable_int( (IM_TX_INT | IM_TX_EMPTY_INT) );
// and let the chipset deal with it
_outpw(MMU_CMD_REG, MC_ENQUEUE);
// we can send another packet
LAN_tx_packet.busy = 0;
return;
}
/******************************************************************************
* LANC91C111: smc_enable_int
*
* Purpose:
* Change the current IRQ mask
*
* Actions:
*
*****************************************************************************/
void smc_enable_int (BYTE IRQ)
{
WORD page;
// save old page
page = _inpw(BSR_REG);
// switch to bank 2
_outpw(BSR_REG, 2);
// update current mask
_outp(IM_REG, _inp(IM_REG) | IRQ);
// restore bank
_outpw(BSR_REG, page);
}
/******************************************************************************
* LANC91C111: smc_disable_int
*
* Purpose:
* Change the current IRQ mask
*
* Actions:
*
*****************************************************************************/
void smc_disable_int (BYTE IRQ)
{
WORD page;
BYTE mask;
// save old page
page = _inpw(BSR_REG);
// switch to bank 2
_outpw(BSR_REG, 2);
// write new mask
_outp(IM_REG, _inp(IM_REG) & ~IRQ);
// restore bank
_outpw(BSR_REG, page);
}
/******************************************************************************
* IRQ Sub Function: smc_phy_interrupt for the LAN91C111
*
* Purpose:
* Handle interrupts relating to PHY register 18.
*
* Actions:
* Log last Phy18 Interrupt Source
*
*****************************************************************************/
void smc_phy_interrupt()
{
WORD phy18;
while (1)
{
// Read PHY Register 18, Status Output
phy18 = smc_read_phy_register(PHY_INT_REG);
// Exit if no more changes
if (phy18 == LAN_state.lastPhy18)
break;
// Update the last phy 18 variable
LAN_state.lastPhy18 = phy18;
} // end while
}
// end-of-file
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -