📄 smc.c
字号:
if (wInts & (RCV_INT | RX_OVRN_INT)) {
dwRet = INTR_TYPE_RX;
}
WriteWord( BANKSEL_REG, wBankSave );
return dwRet;
}
//
// This routine is used by the OAL to configure the debug Ethernet driver. Currently
// the following options are defined:
// OPT_BROADCAST_FILTERING -- If set, filter out all broadcast packets except ARP packets
//
DWORD
SMCSetOptions(DWORD dwOptions)
{
DWORD dwOldOptions = dwConfigOptions;
dwConfigOptions = dwOptions;
return dwOldOptions;
}
static BOOL
SMCReleaseMemory (UINT16 wCmd)
{
DWORD dwStartTime;
// issuem the release command
WriteWord (MMU_CMD_REG, wCmd);
// Wait until release finishes executing
dwStartTime = OEMEthGetSecs();
while (ReadWord (MMU_CMD_REG) & MMU_CMD_BUSY) {
if ((OEMEthGetSecs() - dwStartTime) > 2) {
EdbgOutputDebugString ("!GetFrame: Timed out releasing memory (wCmd = %u)\n", wCmd);
return FALSE;
}
}
return TRUE;
}
static UINT16
SMCRetrieveFrame (BYTE *pbData, UINT16 *pwLength)
{
UINT16 i, wStatusWord;
UINT16 *pwData = (UINT16 *)pbData;
UINT16 wLen;
// Setup pointer address register to point to first byte of Frame
WriteWord( PTR_REG, 0xE000 );
// read status and filter frames if error detected
if (!((wStatusWord = ReadWord (DATA_REG)) & FRAME_FILTER))
{
// The top bits of the byte count are reserved, so mask those (pg 26 of SMC91C94 spec).
// The byte count includes the status word, byte count and control byte so subtract 6
wLen = (0x07FF & ReadWord( DATA_REG )) - 6;
EDBG_DEBUGLED(LED_PKTLEN,*pwLength);
if (*pwLength < wLen) {
EdbgOutputDebugString("SMC9000: received packet larger (%u) than buffer (%u)\n",wLen, *pwLength);
return 0x0800; // frame too long
}
// For broadcast frames, filter out all except for ARPs
i=0;
if ((dwConfigOptions & OPT_BROADCAST_FILTERING) && (wStatusWord & BROADCAST_FILTER_BIT)) {
for (; i< sizeof(EthernetFrameHeader); i++)
*pwData++ = ReadWord( DATA_REG );
if (ntohs(((EthernetFrameHeader UNALIGNED *)pbData)->wFrameType) != 0x0806) {
return BROADCAST_FILTER_BIT; // broadcast, not ARPs
}
}
wStatusWord = 0;
*pwLength = wLen; // set return length
// Read all but the last word, which will contain the control word
wLen >>= 1; // in words
EDBG_DEBUGLED(LED_COPY,0);
for( ; i < wLen; i++ )
*pwData++ = ReadWord( DATA_REG );
EDBG_DEBUGLED(LED_COPY,wLen);
// Now check to see if there were an odd number of bytes sent. If so, I have to extract the
// last one (pg 27 of the SCM91C94 spec).
wLen = ReadWord( DATA_REG );
if (wLen & 0x2000) {
*pwData = (BYTE)wLen;
(*pwLength)++;
}
}
return wStatusWord;
}
#if 1
unsigned char *NetRxPackets[3000];
UINT16
SMCGetFrame( BYTE *pbData, UINT16 *pwLength )
{
int packet_number;
WORD status;
WORD packet_length;
int is_error = 0;
BYTE saved_pnr;
WORD saved_ptr;
SMC_SELECT_BANK(2);
/* save PTR and PTR registers */
saved_pnr = SMC_inb( PN_REG );
saved_ptr = SMC_inw( PTR_REG );
packet_number = SMC_inw( RXFIFO_REG );
if ( packet_number & RXFIFO_REMPTY ) {
return 0;
}
// EdbgOutputDebugString("%s:smc_rcv\n", SMC_DEV_NAME);
/* start reading from the start of the packet */
SMC_outw( PTR_READ | PTR_RCV | PTR_AUTOINC, PTR_REG );
/* First two words are status and packet_length */
status = SMC_inw( DATA_REG );
packet_length = SMC_inw( DATA_REG );
packet_length &= 0x07ff; /* mask off top bits */
if ( !(status & RS_ERRORS ) ){
/* Adjust for having already read the first two words */
packet_length -= 4; /*4; */
/* set odd length for bug in LAN91C111, */
/* which never sets RS_ODDFRAME */
/* TODO ? */
// EdbgOutputDebugString(" Reading %d words and %d byte(s) \n", (packet_length >> 1 ), packet_length & 1 );
SMC_insw(DATA_REG , pbData, packet_length >> 1);
//#if SMC_DEBUG > 2
// EdbgOutputDebugString("Receiving Packet\n");
// DumpEtherFrame(pbData, packet_length);
//#endif
} else {
/* error ... */
/* TODO ? */
is_error = 1;
}
while ( SMC_inw( MMU_CMD_REG ) & MC_BUSY )
udelay(1); /* Wait until not busy */
/* error or good, tell the card to get rid of this packet */
SMC_outw( MC_RELEASE, MMU_CMD_REG );
while ( SMC_inw( MMU_CMD_REG ) & MC_BUSY )
udelay(1); /* Wait until not busy */
/* restore saved registers */
SMC_outb( saved_pnr, PN_REG );
SMC_outw( saved_ptr, PTR_REG );
if (!is_error) {
/* Pass the packet up to the protocol layers. */
*pwLength = packet_length;
return 1;
} else {
return 0;
}
}
#endif
// This routine should be called with a pointer to the ethernet frame data. It is the caller's
// responsibility to fill in all information including the destination and source addresses and
// the frame type. The length parameter gives the number of bytes in the ethernet frame.
// The routine will not return until the frame has been transmitted or an error has occured. If
// the frame transmits successfully, 0 is returned. If an error occured, a message is sent to
// the serial port and the routine returns non-zero.
#define MEMORY_WAIT_TIME 16
static int poll4int (BYTE mask, int timeout)
{
unsigned int tmo = OEMEthGetSecs() + (unsigned int)timeout;
int is_timeout = 0;
WORD old_bank = SMC_inw (BSR_REG);
// EdbgOutputDebugString ("Polling...\n");
SMC_SELECT_BANK (2);
while ((SMC_inw (SMC91111_INT_REG) & mask) == 0) {
if (OEMEthGetSecs() >= tmo) {
is_timeout = 1;
break;
}
}
/* restore old bank selection */
SMC_SELECT_BANK (old_bank);
if (is_timeout)
return 1;
else
return 0;
}
UINT16 SMCSendFrame( BYTE *pbData, DWORD dwLength )
{
BYTE packet_no;
BYTE *buf;
int length;
int numPages;
int trycnt = 0;
int time_out;
BYTE status;
BYTE saved_pnr;
WORD saved_ptr;
/* save PTR and PNR registers before manipulation */
SMC_SELECT_BANK (2);
saved_pnr = SMC_inb( PN_REG );
saved_ptr = SMC_inw( PTR_REG );
// EdbgOutputDebugString ("%s:smc_hardware_send_packet\n", SMC_DEV_NAME);
length = ETH_ZLEN < dwLength ? dwLength : ETH_ZLEN;
/* allocate memory
** 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 > 7) {
EdbgOutputDebugString ("%s: Far too big packet error. \n", SMC_DEV_NAME);
return 0;
}
/* now, try to allocate the memory */
SMC_SELECT_BANK (2);
SMC_outw (MC_ALLOC | numPages, MMU_CMD_REG);
/* FIXME: the ALLOC_INT bit never gets set *
* so the following will always give a *
* memory allocation error. *
* same code works in armboot though *
* -ro
*/
again:
trycnt++;
time_out = MEMORY_WAIT_TIME;
do {
status = SMC_inb (SMC91111_INT_REG);
if (status & IM_ALLOC_INT) {
/* acknowledge the interrupt */
SMC_outb (IM_ALLOC_INT, SMC91111_INT_REG);
break;
}
} while (--time_out);
if (!time_out) {
EdbgOutputDebugString ("%s: memory allocation, try %d failed ...\n",
SMC_DEV_NAME, trycnt);
if (trycnt < SMC_ALLOC_MAX_TRY)
goto again;
else
{
/* Reset the MMU */
SMC_SELECT_BANK (2);
smc_wait_mmu_release_complete ();
SMC_outw (MC_RESET, MMU_CMD_REG);
while (SMC_inw (MMU_CMD_REG) & MC_BUSY)
udelay (1); /* Wait until not busy */
return 0;
}
}
// EdbgOutputDebugString ("%s: memory allocation, try %d succeeded ...\n", SMC_DEV_NAME, trycnt);
/* I can send the packet now.. */
buf = (BYTE *) pbData;
/* If I get here, I _know_ there is a packet slot waiting for me */
packet_no = SMC_inb (AR_REG);
if (packet_no & AR_FAILED) {
/* or isn't there? BAD CHIP! */
EdbgOutputDebugString ("%s: Memory allocation failed. \n", SMC_DEV_NAME);
return 0;
}
/* we have a packet address, so tell the card to use it */
SMC_outb (packet_no, PN_REG);
/* do not write new ptr value if Write data fifo not empty */
while ( saved_ptr & PTR_NOTEMPTY )
EdbgOutputDebugString ("Write data fifo not empty!\n");
/* point to the beginning of the packet */
SMC_outw (PTR_AUTOINC, PTR_REG);
// EdbgOutputDebugString ("%s: Trying to xmit packet of length %x\n", SMC_DEV_NAME, length);
//#if SMC_DEBUG > 2
// EdbgOutputDebugString ("Transmitting Packet\n");
// DumpEtherFrame(pbData, (WORD)dwLength);
//#endif
/* send the packet length ( +6 for status, length and ctl byte )
and the status word ( set to zeros ) */
SMC_outw (0, DATA_REG);
/* send the packet length ( +6 for status words, length, and ctl */
SMC_outw ((length + 6), DATA_REG);
/* send the actual data
. I _think_ it's faster to send the longs first, and then
. mop up by sending the last word. It depends heavily
. on alignment, at least on the 486. Maybe it would be
. a good idea to check which is optimal? But that could take
. almost as much time as is saved?
*/
SMC_outsw (DATA_REG, buf, (length) >> 1);
/* Send the last byte, if there is one. */
if ((length & 1) == 0) {
SMC_outw (0, DATA_REG);
} else {
SMC_outw (buf[length - 1] | 0x2000, DATA_REG);
}
/* and let the chipset deal with it */
SMC_outw (MC_ENQUEUE, MMU_CMD_REG);
/* poll for TX INT */
/* if (poll4int (IM_TX_INT, SMC_TX_TIMEOUT)) { */
/* poll for TX_EMPTY INT - autorelease enabled */
if (poll4int(IM_TX_EMPTY_INT, SMC_TX_TIMEOUT)) {
/* sending failed */
EdbgOutputDebugString ("%s: TX timeout, sending failed...\n", SMC_DEV_NAME);
/* release packet */
/* no need to release, MMU does that now */
/* SMC_outw (MC_FREEPKT, MMU_CMD_REG); */
/* wait for MMU getting ready (low) */
while (SMC_inw (MMU_CMD_REG) & MC_BUSY) {
udelay (10);
}
EdbgOutputDebugString ("MMU ready\n");
return 0;
} else {
/* ack. int */
SMC_outb (IM_TX_EMPTY_INT, SMC91111_INT_REG);
/* SMC_outb (IM_TX_INT, SMC91111_INT_REG); */
// EdbgOutputDebugString ("%s: Sent packet of length %d \n", SMC_DEV_NAME, length);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -