📄 lan91c111.c
字号:
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
dev->LAN_state.rpsize = packet_length;
// max 2k packets over Ethernet
packet_length &= 0x07ff; /* mask off top bits */
if ( !(status & RS_ERRORS ) )
{
ADI_ETHER_BUFFER *tmp_q_ele;
#if LAN91C111_LOG_NET_ERRORS
if ( status & RS_MULTICAST ) dev->Stats->cEMAC_RX_CNT_MULTI++;
#endif
// collect the packet
// first two words already read: status and packet size
if((status & 0x1000))
packet_length -= 5; //## used to be 4
else
{
// Get the chip id and fix the hardware anomaly rev-A
_outpw(BSR_REG,3);
chip_id = _inpw(REV_REG);
if( ((chip_id & 0x0F00) == 9) && ((chip_id & 0xF000) == 0) )
packet_length -= 4;
else
packet_length -= 6;
_outpw(BSR_REG, 2);
}
tmp_q_ele = dev->m_RxEnqueuedHead;
if (tmp_q_ele != NULL) {
elnth = (unsigned short *)tmp_q_ele->Data;
*elnth = (packet_length + 4); // add on 4 bytes for the CRC (even if we dont store it)
tmp_q_ele->ProcessedElementCount = (*elnth+2+tmp_q_ele->ElementWidth-1)/tmp_q_ele->ElementWidth;
}
if ((tmp_q_ele)&&(tmp_q_ele->ProcessedElementCount <= tmp_q_ele->ElementCount))
{
//store packet length
// assign pointer
data = (void *)( ((char *)tmp_q_ele->Data) +2);
}
else
{
#if LAN91C111_LOG_NET_ERRORS
if (tmp_q_ele) {
dev->Stats->cEMAC_RX_CNT_LONG++;
} else {
dev->Stats->cEMAC_RX_CNT_LOST++;
}
#endif
// drop packet
// good or bad, delete this packet */
_outpw(MMU_CMD_REG, MC_RELEASE);
return;
}
#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
/* Change the Enqueued head */
dev->m_RxEnqueuedHead = (ADI_ETHER_BUFFER*)tmp_q_ele->pNext;
dev->m_RxEnqueuedCount--;
if (dev->m_RxEnqueuedCount == 0)
dev->m_RxEnqueuedTail = NULL;
/* Add the packet to dequeued list*/
if (dev->m_RxDequeuedCount)
dev->m_RxDequeuedTail->pNext = (ADI_ETHER_BUFFER *)tmp_q_ele;
else
dev->m_RxDequeuedHead = tmp_q_ele;
//No matter what this is also the tail.
dev->m_RxDequeuedTail = tmp_q_ele;
//And tail->next should point to NULL
tmp_q_ele->pNext = NULL;
dev->m_RxDequeuedCount++;
// finally we have to update the status word etc
tmp_q_ele->ProcessedFlag = 1;
tmp_q_ele->StatusWord = 0x3000 + (packet_length+4);
#if LAN91C111_LOG_NET_ERRORS
dev->Stats->cEMAC_RX_CNT_OK++;
#endif
}
else
{
#if LAN91C111_LOG_NET_ERRORS
if ( status & RS_ALGNERR ) dev->Stats->cEMAC_RX_CNT_ALIGN++;
if ( status & (RS_TOOSHORT|RS_TOOLONG)) dev->Stats->cEMAC_RX_CNT_LONG++;
if ( status & RS_BADCRC) dev->Stats->cEMAC_RX_CNT_FCS++;
#endif
}
while ( _inpw(MMU_CMD_REG ) & MC_BUSY );
// 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
*
*****************************************************************************/
static void LAN91C111_tx(ADI_ETHER_LAN91C111_DATA * dev)
{
volatile unsigned char saved_packet;
volatile unsigned char packet_no;
volatile unsigned short 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 );
#if LAN91C111_LOG_NET_ERRORS
dev->Stats->cEMAC_TX_CNT_ABORT++;
if ( tx_status & TS_LOSTCAR ) dev->Stats->cEMAC_TX_CNT_CRS++;
if ( tx_status & TS_LATCOL ) dev->Stats->cEMAC_TX_CNT_LATE++;
#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
dev->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;
}
/******************************************************************************
* IRQ Sub Function: smc_phy_interrupt for the LAN91C111
*
* Purpose:
* Handle interrupts relating to PHY register 18.
*
* Actions:
* Log last Phy18 Interrupt Source
*
*****************************************************************************/
static void LAN91C111_phy_interrupt(ADI_ETHER_LAN91C111_DATA * cptr)
{
volatile unsigned short phy18;
while (1)
{
// Read PHY Register 18, Status Output
phy18 = LAN91C111_read_phy_register(PHY_INT_REG);
// Exit if no more changes
if (phy18 == cptr->LAN_state.lastPhy18)
break;
// Update the last phy 18 variable
cptr->LAN_state.lastPhy18 = phy18;
} // end while
}
/******************************************************************************
* LANC91C111: LAN91C111_hardware_send_packet
*
* Purpose:
* This sends the actual packet to the SMC9xxx chip.
*
* - 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.
*
*****************************************************************************/
static unsigned char LAN91C111_hardware_send_packet(ADI_ETHER_LAN91C111_DATA * cptr)
{
int i;
unsigned char packet_no;
unsigned short length;
#ifndef USE_16_BIT
unsigned int *buf;
#else
unsigned short *buf;
#endif
unsigned short *elnth;
unsigned short lnth_first;
ADI_ETHER_BUFFER *bf = cptr->m_TxEnqueuedHead;
// get pointer
#ifndef USE_16_BIT
buf = (unsigned int *)(((char *)bf->Data)+2);
#else
buf = (unsigned short *)(((char *)bf->Data)+2);
#endif
// the length is held in the first two bytes of the 'frame', the ElementCount says the number of elements in the first buffer
// get length
elnth = (unsigned short *)bf->Data;
length = *elnth;
lnth_first = bf->ElementCount*bf->ElementWidth - 2;
length = ETH_ZLEN < length ? length : ETH_ZLEN;
lnth_first = ETH_ZLEN < lnth_first ? lnth_first : ETH_ZLEN;
// 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 LAN91C111_TX_ERROR;
// 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
// do the first buffer
for (i=0; i < (lnth_first>>1); i++)
_outpw(DATA_REG, *buf++);
// check to see if more to go
if (lnth_first<length) {
buf = bf->PayLoad;
length = length-lnth_first;
// do the second buffer
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 unsigned char
_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);
}
// now set the status in the buffer bf
bf->ProcessedFlag = 1;
bf->ProcessedElementCount = (*elnth+2 + bf->ElementWidth-1)/bf->ElementWidth;
bf->StatusWord = 0x3; // completed and OK
#if 0
///////////////////////////////////////////////////////////////////
// This code is added to handle the case where the source and
// destination MAC address is the same.It just copies the broadcast
// and frames that are destianted to the same MAC.
// The below code could be commented if the client and server runs on
// different boards.
//
if( (!memcmp(cptr->phyAddr,(unsigned char *)cptr->m_TxEnqueuedHead->data_start,6) )
|| (!memcmp((unsigned char *)cptr->m_TxEnqueuedHead->data_start,"\xff\xff\xff\xff\xff\xff",6)))
{
struct buffer_t *tmp_q_ele;
tmp_q_ele = cptr->m_RxEnqueuedHead;
if(tmp_q_ele != NULL)
memcpy(tmp_q_ele->data_start,(unsigned short *)cptr->m_TxEnqueuedHead->data_start,cptr->m_TxEnqueuedHead->num_elements);
/* Change the Enqueued head */
cptr->m_RxEnqueuedHead = (struct buffer_t*)tmp_q_ele->next;
cptr->m_RxEnqueuedCount--;
if (cptr->m_RxEnqueuedCount == 0)
cptr->m_RxEnqueuedTail = (struct buffer_t*)NULL;
/* Add the packet to dequeued list*/
if (cptr->m_RxDequeuedCount)
cptr->m_RxDequeuedTail->next = tmp_q_ele;
else
cptr->m_RxDequeuedHead = tmp_q_ele;
//No matter what this is also the tail.
cptr->m_RxDequeuedTail = tmp_q_ele;
//And tail->next should point to NULL
tmp_q_ele->next = NULL;
cptr->m_RxDequeuedCount++;
}
////////////////////////////////////////////////////////////
#endif
// enable the interrupts
LAN91C111_enable_int( (IM_TX_INT | IM_TX_EMPTY_INT) );
// and let the chipset deal with it
_outpw(MMU_CMD_REG, MC_ENQUEUE);
return LAN91C111_TX_SUCCESS;
}
/******************************************************************************
* LANC91C111: LAN91C111_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.
*****************************************************************************/
static unsigned char LAN91C111_wait_to_send_packet(ADI_ETHER_LAN91C111_DATA * cptr)
{
unsigned short length;
unsigned short numPages;
unsigned short time_out;
unsigned short status;
unsigned short *elnth;
// the length is held in the first two bytes of the 'frame', the ElementCount says the number of elements in the first buffer
// get length
elnth = (unsigned short *)cptr->m_TxEnqueuedHead->Data;
length = *elnth;
// set length
length = ETH_ZLEN < length ? length : ETH_ZLEN;
// clear allocation flag
cptr->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
return LAN91C111_TX_ERROR;
}
// either way, a packet is waiting now
cptr->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.
*
*/
TX_RETRY:
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) )
{
/* The allocation used to fail when ever the RXOVRN was set and
* the packets were not transmitted. This takes cares of the
* above, I have not observed any packet loss, but I am not
* that sure also, MC_RELEASE release the recieve packet,
* At the max we may loose one packet.
*/
// Relase and reset the FIFO, Now the SMSC memory is free
while ( _inpw(MMU_CMD_REG ) & 1 ) // 1 is MC_BUSY
{
//when the last release is completed
ssync();
}
_outpw(MMU_CMD_REG, MC_RELEASE);
_outpw(MMU_CMD_REG, MC_RSTTXFIFO);
goto TX_RETRY;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -