📄 smc.c
字号:
// Wait until release finishes executing
dwStartTime = OEMEthGetSecs();
while( ReadWord( MMUCOMMAND_REG ) & 1 ) {
if ((OEMEthGetSecs() - dwStartTime) > 2) {
EdbgOutputDebugString("!GetFrame: Timed out releasing memory\n");
break;
}
}
// If the buffer is now empty, re-enable the RCV_INT and clear the fFrameReceived flag
if (!(ReadWord( INTERRUPT_REG ) & RCV_INT)) {
fFrameReceived = 0;
WriteWord( INTERRUPT_REG, wIntMask );
}
// Check the status word for errors (pg 28 of the SMC91C94 spec).
if (wStatusWord & 0x8000)
EdbgOutputDebugString( "GetFrame() - Receive Alignment Error\n" );
if (wStatusWord & 0x2000)
EdbgOutputDebugString( "GetFrame() - CRC Error\n" );
if (wStatusWord & 0x0800)
EdbgOutputDebugString( "GetFrame() - Frame Was Too Long\n" );
if (wStatusWord & 0x0400)
EdbgOutputDebugString( "GetFrame() - Frame Was Too Short\n" );
// if (wStatusWord & 0x4000)
// EdbgOutputDebugString( "GetFrame() - Broadcast\n" );
#ifdef SMC_DUMP_FRAMES
DumpEtherFrame(pbData, *pwLength);
#endif
// If frame isn't filtered, return data to user. Otherwise, get next frame.
if (!fDropFrame) {
EDBG_ADAPTERMSG(ZONE_RECV,("-GetFrame, returning %u bytes\n", *pwLength));
EDBG_DEBUGLED(LED_GETFRAME_EXIT,1);
return 1;
}
}
EDBG_ADAPTERMSG(ZONE_RECV,("-GetFrame\n"));
EDBG_DEBUGLED(LED_GETFRAME_EXIT,0);
return 0;
} // GetFrame()
// 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.
UINT16 SMCSendFrame( BYTE *pbData, DWORD dwLength ) {
UINT16 wFrameHandle;
UINT16 cwBufferSize;
UINT16 wCompletionCode;
UINT16 *pwData;
DWORD dwStartTime;
BOOL bErr = FALSE;
UINT16 wInt;
DWORD i;
EDBG_DEBUGLED(LED_SENDFRAME_ENTRY,0);
EDBG_ADAPTERMSG(ZONE_SEND,("+SendFrame, dwLength: %u\n",dwLength));
#ifdef SMC_DUMP_FRAMES
DumpEtherFrame(pbData, (USHORT)dwLength);
#endif
// Calculate the memory needed = status word + byte count + data area + control byte
// This needs to be rounded up to an even number
cwBufferSize = 2 + 2 + (UINT16)dwLength + 1;
if (cwBufferSize & 1)
cwBufferSize++;
// Allocate memory in the buffer for the Frame
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( MMUCOMMAND_REG, (USHORT)(0x0020 | (UINT16)(cwBufferSize >> 8)) );
// Loop here until the request is satisfied, or we timeout
dwStartTime = OEMEthGetSecs();
while (!(ReadWord( INTERRUPT_REG ) & ALLOC_INT)) {
if (OEMEthGetSecs() - dwStartTime > 2) {
EdbgOutputDebugString("Timed out waiting for ALLOC_INT\n");
// Get the status word so that it can be returned.
WriteWord( POINTER_REG, 0x2000 );
wCompletionCode = ReadWord( DATA_REG );
EdbgOutputDebugString( "SendFrame return with wCompoletionCode=%x\r\n", wCompletionCode);
return wCompletionCode;
}
}
wFrameHandle = ReadWord( PNR_ARR_REG ) >> 8;
// Now write the Frame into the buffer (Pg 39 of the SMC91C94 spec)
WriteWord( PNR_ARR_REG, wFrameHandle );
WriteWord( POINTER_REG, 0x4000 );
// Write the Status Word
WriteWord( DATA_REG, 0 );
// Write the byte count = status word + byte count + data area + control byte
WriteWord( DATA_REG, cwBufferSize );
// Now write all except possibly the last data byte
pwData = (UINT16 *)pbData;
for( i = 0; i < (dwLength >> 1); i++ )
WriteWord( DATA_REG, *pwData++ );
// If the number of data bytes was odd we can put that just before the Control Byte
if (dwLength & 1) {
WriteWord( DATA_REG, (USHORT)((0x3000) | *(pbData + dwLength - 1)));
// Otherwise, the number of bytes is even, and I'll just pad the last byte with 0
}
else
WriteWord( DATA_REG, 0x1000 );
// Enqueue Frame number into TX FIFO
WriteWord( MMUCOMMAND_REG, 0x00C0 );
bErr = TRUE;
dwStartTime = OEMEthGetSecs();
while (OEMEthGetSecs() - dwStartTime < 2) {
wInt = ReadWord (INTERRUPT_REG);
if (wInt & TXEMPTY_INT) {
WriteWord (INTERRUPT_REG, TXEMPTY_INT); // ACK
wInt = ReadWord (INTERRUPT_REG); // read status
WriteWord (INTERRUPT_REG, wIntMask); // re-enable interrupt
bErr = FALSE;
break;
}
}
if (bErr)
EdbgOutputDebugString( "SendFrame timeout waiting for TXEMPTY! INTERRUPT_REG = 0x%x\r\n", wInt);
// If an error occured during transmission, TX_INT will be generated.
// If this happens, I need to delete the untransmitted Frame
// from the buffer. If no error occurred, this will be done by the chip because it's in
// AUTO RELEASE mode. (Page 72, SMC spec)
if (bErr || (TX_INT & wInt)) {
// Get the status word so that it can be returned.
WriteWord( POINTER_REG, 0x2000 );
wCompletionCode = ReadWord( DATA_REG );
SendFrameError("!EDBG error ",wCompletionCode);
// Clear the statistics registers
WriteWord( BANKSEL_REG, BANK0 );
ReadWord( COUNTER_REG );
// Re-enable TXENA
WriteWord( TCR_REG, TCR_REG_INIT );
WriteWord( BANKSEL_REG, BANK2 );
// Release the buffer memory for the Frame that failed to transmit
WriteWord( MMUCOMMAND_REG, 0x00A0 );
// Wait for MMU Release to complete
dwStartTime = OEMEthGetSecs();
while (ReadWord( MMUCOMMAND_REG ) & 1) {
if (OEMEthGetSecs() - dwStartTime > 2) {
EdbgOutputDebugString("Timed out waiting for MMUCOMMAND_REG bit 1\n");
break;
}
}
if (TX_INT & wInt) {
WriteWord (INTERRUPT_REG, TX_INT); // Ack TX_INT
WriteWord (INTERRUPT_REG, wIntMask); // enable interrupt
}
EDBG_DEBUGLED(LED_SENDFRAME_EXIT, wCompletionCode);
return wCompletionCode;
}
else {
// Successful transmission, clear collision count
WriteWord( BANKSEL_REG, BANK0 );
wInt=ReadWord(COUNTER_REG);
wCompletionCode = ReadWord( EPHSTATUS_REG );
// EdbgOutputDebugString("-SendFrame, success(EPSR=0x%x,Count=0x%x)\n",wCompletionCode,wInt);
EDBG_DEBUGLED(LED_SENDFRAME_EXIT,0);
return 0;
}
// The TXEMPTY_INT will be disabled by the ISR.
} // SendFrame()
// This routine is called by a higher level routine if an error was detected by SendFrame()
// returning non-zero. The completion code that is returned is decoded here and error
// message(s) are printed, prefixed by the supplied string.
static void SendFrameError( char *pszPrefix, UINT16 wCompletionCode ) {
// Output any transmit error messages. These correspond to the
// EPH Status Register described on page 37 of the SMC91C94 spec.
if (wCompletionCode & 0x8000)
EdbgOutputDebugString( "%s::SendFrame()::Transmission Underrun\n", pszPrefix );
if (wCompletionCode & 0x4000)
EdbgOutputDebugString( "%s::SendFrame()::Link Test Failed\n", pszPrefix );
if (wCompletionCode & 0x2000)
EdbgOutputDebugString( "%s::SendFrame()::Receive Overrun\n", pszPrefix );
if (wCompletionCode & 0x1000)
EdbgOutputDebugString( "%s::SendFrame()::Statistics Counter Roll Over\n", pszPrefix );
if (wCompletionCode & 0x0800)
EdbgOutputDebugString( "%s::SendFrame()::Excessive Deferral\n", pszPrefix );
if (wCompletionCode & 0x0400)
EdbgOutputDebugString( "%s::SendFrame()::Lost Carrier\n", pszPrefix );
if (wCompletionCode & 0x0200)
EdbgOutputDebugString( "%s::SendFrame()::Late Collision\n", pszPrefix );
if (wCompletionCode & 0x0080)
EdbgOutputDebugString( "%s::SendFrame()::Transmission Deferred\n", pszPrefix );
if (wCompletionCode & 0x0040)
EdbgOutputDebugString( "%s::SendFrame()::Last Frame Was Broadcast\n", pszPrefix );
if (wCompletionCode & 0x0020)
EdbgOutputDebugString( "%s::SendFrame()::Signal Quality Error\n", pszPrefix );
if (wCompletionCode & 0x0010)
EdbgOutputDebugString( "%s::SendFrame()::16 Collisions Reached\n", pszPrefix );
if (wCompletionCode & 0x0008)
EdbgOutputDebugString( "%s::SendFrame()::Last Frame Was Multicast\n", pszPrefix );
if (wCompletionCode & 0x0004)
EdbgOutputDebugString( "%s::SendFrame()::Multiple Collisions Detected\n", pszPrefix );
if (wCompletionCode & 0x0002)
EdbgOutputDebugString( "%s::SendFrame()::Single Collion Detected\n", pszPrefix );
if (wCompletionCode & 0x0001)
EdbgOutputDebugString( "%s::SendFrame()::Transmit Successful\n", pszPrefix );
EdbgOutputDebugString( "%s::SendFrame()::EPH Reg %Hh\n", pszPrefix, wCompletionCode );
}
// ISR routine. Three global flags are used to communicate with GetFrame() and SendFrame().
// They are fFrameReceived, fTXEMPTY_INT and fTX_INT and are described at the top of the file.
static void SMC_ISR( void ) {
UINT16 wInterruptReg;
UINT16 wBankSave;
// I need to save/restore the affected registers in the 91C94
wBankSave = ReadWord( BANKSEL_REG );
WriteWord( BANKSEL_REG, BANK2 );
// Get the currently pending interrupts
wInterruptReg = ReadWord( INTERRUPT_REG );
// Disable any more interrupts
WriteWord( INTERRUPT_REG, 0 );
EDBG_ADAPTERMSG(ZONE_INTR,("+SMC_ISR: Ints pending: %X\n",wInterruptReg));
if (wInterruptReg & RX_OVRN_INT) {
EDBG_DEBUGLED(LED_OVERRUN,wInterruptReg);
EDBG_ADAPTERMSG(ZONE_WARNING,("RX_OVRN_INT\n"));
WriteWord( INTERRUPT_REG, RX_OVRN_INT);
}
// This interrupt indicates that the RX buffer is not empty. It just stays asserted until
// all the Frames are read out of the buffer. I will accept one of these and set the fFrameReceived
// flag to indicate this to the GetFrame() routine, then I'll disable the interrupt from here.
// After GetFrame() gets called it will check the buffer and if it is empty it will re-enable the
// interrupt.
if (wInterruptReg & RCV_INT) {
fFrameReceived = 1;
wInterruptReg &= (~RCV_INTM);
}
// Restore the interrupt mask register, with possible modification
WriteWord( INTERRUPT_REG, (USHORT)(wInterruptReg & 0xFF00 ));
WriteWord( BANKSEL_REG, wBankSave );
EDBG_ADAPTERMSG(ZONE_INTR,("-SMC_ISR: Ints pending: %X, fFrameReceived: %u\n", wInterruptReg, fFrameReceived));
}
#ifdef SMC_DUMP_FRAMES
static void DumpEtherFrame( BYTE *pFrame, WORD cwFrameLength ) {
int i,j;
EdbgOutputDebugString( "Frame Buffer Address: 0x%X\r\n", pFrame );
EdbgOutputDebugString( "To: %B:%B:%B:%B:%B:%B From: %B:%B:%B:%B:%B:%B Type: 0x%H Length: %u\r\n",
pFrame[0], pFrame[1], pFrame[2], pFrame[3], pFrame[4], pFrame[5],
pFrame[6], pFrame[7], pFrame[8], pFrame[9], pFrame[10], pFrame[11],
ntohs(*((UINT16 *)(pFrame + 12))), cwFrameLength );
for( i = 0; i < cwFrameLength / 16; i++ ) {
for( j = 0; j < 16; j++ )
EdbgOutputDebugString( " %B", pFrame[i*16 + j] );
EdbgOutputDebugString( "\r\n" );
}
for( j = 0; j < cwFrameLength % 16; j++ )
EdbgOutputDebugString( " %B", pFrame[i*16 + j] );
EdbgOutputDebugString( "\r\n" );
}
#endif
// The read and write EEPROM routines access the 64 word by 16-bit EEPROM that is attached to the
// 91C94. There are locations within the EEPROM that the chip does not need and can be used to
// store other important information. The memory map of the EEPROM is given in the ethernet.doc
// spec. The procedure used here for accessing these words is discussed on pages 88 and 89 of
// the SMC91C94 spec.
BOOL SMCReadEEPROM( UINT16 EEPROMAddress , UINT16 *pwVal) {
DWORD dwCurTime;
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( POINTER_REG, (USHORT)((EEPROMAddress & 0x003F) | 0x6000 ));
WriteWord( BANKSEL_REG, BANK1 );
WriteWord( CONTROL_REG, (USHORT)(ReadWord(CONTROL_REG) | 0x0006) );
// Loop until EEPROM data is retrieved
dwCurTime = OEMEthGetSecs();
while( ReadWord( CONTROL_REG ) & 0x0003 ) {
if ((OEMEthGetSecs() - dwCurTime) > 2) {
EdbgOutputDebugString("!SMCReadEEPROM read timed out\n");
return FALSE;
}
}
*pwVal = ReadWord( GENERAL_REG );
return TRUE;
}
BOOL SMCWriteEEPROM( UINT16 EEPROMAddress, UINT16 Data ) {
DWORD dwCurTime;
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( POINTER_REG, (USHORT)((EEPROMAddress & 0x003F) | 0x6000 ));
WriteWord( BANKSEL_REG, BANK1 );
WriteWord( GENERAL_REG, Data );
WriteWord( CONTROL_REG, (USHORT)(ReadWord(CONTROL_REG) | 0x0005 ));
// Loop until EEPROM data is written
dwCurTime = OEMEthGetSecs();
while( ReadWord( CONTROL_REG ) & 0x0003 ) {
if ((OEMEthGetSecs() - dwCurTime) > 5) {
EdbgOutputDebugString("!SMCWriteEEPROM read timed out\n");
return FALSE;
}
}
return TRUE;
}
// Read the value on the IOS lines, used to indicate the status of the switches on both the Odo
// Ethernet board, and the SMC eval board.
UINT16 SMCReadSwitches(void) {
WriteWord( BANKSEL_REG, BANK3 );
return ReadWord(MGMT_REG) >> 8;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -