📄 smc.c
字号:
{
DWORD dwStartTime;
// issuem the release command
LAN91C96_Write (MMUCOMMAND_REG, wCmd);
// Wait until release finishes executing
dwStartTime = OEMEthGetSecs();
while (LAN91C96_Read (MMUCOMMAND_REG) & MMU_CMD_BUSY) {
if ((OEMEthGetSecs() - dwStartTime) > 2) {
EdbgOutputDebugString ("!GetFrame: Timed out releasing memory (wCmd = %u)\n", wCmd);
return FALSE;
}
}
return TRUE;
}
// This routine is polled to find out if a frame has been received. If there are no frames
// in the RX FIFO, the routine will return 0. If there was a frame that was received correctly,
// it will be stored in pwData, otherwise it will be discarded.
UINT16
SMCGetFrame( BYTE *pbData, UINT16 *pwLength ) {
UINT16 wStatusWord, wControlWord, i;
UINT16 *pwData;
DWORD dwStartTime;
BOOL fDropFrame;
WORD wBufLen = *pwLength;
// For the bootloader we don't want to deal with interrupts, so I'll poll the ISR routine
SMC_ISR();
while (fFrameReceived) {
// Setup pointer address register to point to first byte of Frame
LAN91C96_Write( BANKSEL_REG, BANK2 );
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_RCV | PTR_READ |0x0));
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_AUTO));
// Check for an error. If no error, then get the Frame out
wStatusWord = LAN91C96_Read( DATA_REG );
if (wStatusWord & FRAME_FILTER) {
// Don't deliver this frame to the user
fDropFrame = TRUE;
} else {
fDropFrame = FALSE;
pwData = (UINT16 *)pbData;
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_RCV | PTR_READ |0x2));
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_AUTO));
// 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
*pwLength = (0x07FF & LAN91C96_Read( DATA_REG )) - 6;
// EdbgOutputDebugString("SMC9000: received packet largth 0x%x\r\n",TMP);
if (*pwLength > wBufLen) {
EdbgOutputDebugString("SMC9000: received packet larger (%u) than buffer (%u)\n",*pwLength,wBufLen);
fDropFrame = TRUE;
goto ReleaseFrame;
}
// 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++)
{
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_RCV | PTR_READ |(i*2+0x4)));
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_AUTO));
*pwData++ = LAN91C96_Read( DATA_REG );
}
if (ntohs(((EthernetFrameHeader *)pbData)->wFrameType) != 0x0806) {
fDropFrame = TRUE;
goto ReleaseFrame;
}
}
// Read all but the last word, which will contain the control word
for( ; i < (*pwLength >> 1); i++ )
{
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_RCV | PTR_READ |(i*2+0x4)));
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_AUTO));
*pwData++ = LAN91C96_Read( DATA_REG );
}
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_RCV | PTR_READ |(i*2+0x4)));
LAN91C96_Write(POINTER_REG, (USHORT) (PTR_AUTO));
wControlWord = LAN91C96_Read( DATA_REG );
if (wControlWord & 0x2000) {
*pwData = (BYTE)wControlWord;
(*pwLength)++;
}
}
// Release the memory for the received Frame
ReleaseFrame:
LAN91C96_Write( MMUCOMMAND_REG, 0x0080 );
// Wait until release finishes executing
dwStartTime = OEMEthGetSecs();
while( LAN91C96_Read( 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 (!(LAN91C96_Read( INTERRUPT_REG ) & RCV_INT)) {
fFrameReceived = 0;
LAN91C96_Write( 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) {
return 1;
}
}
return 0;
} // GetFrame()
// 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;
//clear GPIO int
// *(volatile long *)(VA_GPIO8_BASE + GPIOIC) |= 0x2; //level int
// I need to save/restore the affected registers in the 91C94
wBankSave = LAN91C96_Read( BANKSEL_REG );
LAN91C96_Write( BANKSEL_REG, BANK2 );
// Get the currently pending interrupts
wInterruptReg = LAN91C96_Read( INTERRUPT_REG );
// Disable any more interrupts
LAN91C96_Write( INTERRUPT_REG, 0 );
if (wInterruptReg & RX_OVRN_INT) {
LAN91C96_Write( 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
LAN91C96_Write( INTERRUPT_REG, (USHORT)(wInterruptReg & 0xFF00 ));
LAN91C96_Write( BANKSEL_REG, wBankSave );
}
// 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 = 0;
UINT16 *pwData;
DWORD dwStartTime;
BOOL bErr = FALSE;
UINT16 wInt;
DWORD i;
// USHORT TMP;
// EdbgOutputDebugString( "+SMCSendFrame\r\n");
if ((DWORD) pbData & 1) {
EdbgOutputDebugString ("!SMCSendFrame: Data MUST be 16bit aligned\r\n");
return FALSE;
}
LAN91C96_Write( BANKSEL_REG, BANK2 );
// EdbgOutputDebugString( "SMCSendFrame M68K_DATA 0x%X\r\n",LAN91C96_Read(BANKSEL_REG));
LAN91C96_Write (INTERRUPT_REG, 0);
// EdbgOutputDebugString( "+SendFrame dwLength: %u\r\n",dwLength);
#ifdef SMC_DUMP_FRAMES
DumpEtherFrame(pbData, (USHORT)dwLength);
#endif
// Calculate the amount of memory needed. 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
LAN91C96_Write( MMUCOMMAND_REG, CMD_ALLOC | (UINT16)((cwBufferSize >> 8)&0x0007 ));
// Loop here until the request is satisfied, or we timeout
dwStartTime = OEMEthGetSecs();
while (!LAN91C96_Read( 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.
LAN91C96_Write( POINTER_REG, 0x2000 );
wCompletionCode = LAN91C96_Read( DATA_REG );
LAN91C96_Write (INTERRUPT_REG, wIntMask);
return wCompletionCode;
}
}
wFrameHandle = (0x3f00 & LAN91C96_Read( PNR_ARR_REG )) >> 8;
// Now write the Frame into the buffer (Pg 39 of the SMC91C94 spec)
LAN91C96_Write( PNR_ARR_REG, wFrameHandle );
// LAN91C96_Write( POINTER_REG, 0x4000 );
LAN91C96_Write( POINTER_REG, 0x0000 );
// Write the Status Word
LAN91C96_Write( DATA_REG, 0 );
// Write the byte count = status word + byte count + data area + control byte
LAN91C96_Write( POINTER_REG, 0x0002);
LAN91C96_Write( DATA_REG, cwBufferSize );
// Now write all except possibly the last data byte
pwData = (UINT16 *)pbData;
for( i = 0; i < (dwLength >> 1); i++ )
{
LAN91C96_Write( POINTER_REG, (USHORT)( 0x0004 + 2*i ));
LAN91C96_Write( DATA_REG, *pwData++ );
}
// If the number of data bytes was odd we can put that just before the Control Byte
if (dwLength & 1)
{
LAN91C96_Write( POINTER_REG, (USHORT)( 0x0004 + 2*i ));
LAN91C96_Write( DATA_REG, (0x3000) | *(pbData + dwLength - 1));
}
// Otherwise, the number of bytes is even, and I'll just pad the last byte with 0
else
{
LAN91C96_Write( POINTER_REG, (USHORT)( 0x0004 + 2*i ));
LAN91C96_Write( DATA_REG, 0x1000 );
}
// Enqueue Frame number into TX FIFO
LAN91C96_Write( MMUCOMMAND_REG, CMD_ENQ_TX );
bErr = TRUE;
dwStartTime = OEMEthGetSecs();
do {
wInt = LAN91C96_Read (INTERRUPT_REG);
if (wInt & TXEMPTY_INT) {
LAN91C96_Write (INTERRUPT_REG, TXEMPTY_INT); // ACK
wInt = LAN91C96_Read (INTERRUPT_REG); // read status
bErr = FALSE;
break;
}
} while (OEMEthGetSecs() - dwStartTime < 2);
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.
LAN91C96_Write( POINTER_REG, 0x2000 );
wCompletionCode = LAN91C96_Read( DATA_REG );
// SendFrameError("!EDBG error 0x%x *******************************",wCompletionCode);
// Clear the statistics registers
LAN91C96_Write( BANKSEL_REG, BANK0 );
LAN91C96_Read( COUNTER_REG );
// Re-enable TXENA
LAN91C96_Write( TCR_REG, TCR_REG_INIT );
LAN91C96_Write( BANKSEL_REG, BANK2 );
// Release the buffer memory for the Frame that failed to transmit
SMCReleaseMemory (CMD_REL_SPEC);
if (TX_INT & wInt) {
LAN91C96_Write (INTERRUPT_REG, TX_INT); // Ack TX_INT
}
} else {
// Successful transmission, clear collision count
LAN91C96_Write( BANKSEL_REG, BANK0 );
wInt=LAN91C96_Read(COUNTER_REG);
wCompletionCode = LAN91C96_Read( EPHSTATUS_REG );
SMCReleaseMemory (CMD_REL_SPEC);
return 0;
}
LAN91C96_Write (BANKSEL_REG, BANK2);
LAN91C96_Write (INTERRUPT_REG, wIntMask);
return wCompletionCode;
// 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 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -