📄 smc.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
Abstract:
Routines for the SMC9000 ethernet controller used for the Windows CE
debug ethernet services, and bootloaders.
Functions:
Notes:
These routines are called at system init and other times when system calls aren't allowed, so
they can't make any system calls.
--*/
// Note : The Odo SMC implementation sometimes encounters read errors if we
// optimize this routine. So for now I'm just going to turn of all
// optimizations.
#pragma optimize("", off)
#include <windows.h>
#include <halether.h>
#include "smchw.h"
#include "smcsw.h"
#include "udp.h"
void SMCSetMACAddress(USHORT MacAddr[3]);
void SMCGetMACAddress(USHORT MacAddr[3]);
// The base address of the SMC registers. Also, allow for a multiplier, since the Odo
// platform uses 32 bit accesses of the registers.
static BYTE volatile *pbEthernetBase;
static DWORD dwRegisterMultiplier;
#ifdef PLAT_SANDGATE
__inline USHORT ReadWord( USHORT wOffset )
{
return READ_PORT_BYTE((PBYTE)(pbEthernetBase + (wOffset*dwRegisterMultiplier)))+\
(((USHORT)READ_PORT_BYTE((PBYTE)(pbEthernetBase + ((wOffset+1)*dwRegisterMultiplier))))<<8);
};
__inline VOID WriteWord( USHORT wOffset, USHORT Value )
{
WRITE_PORT_BYTE((PBYTE)(pbEthernetBase + (wOffset*dwRegisterMultiplier)),(BYTE)(Value));\
WRITE_PORT_BYTE((PBYTE)(pbEthernetBase + ((wOffset+1)*dwRegisterMultiplier)),(BYTE)(Value>>8));
};
#endif
#ifdef PLAT_LUBBOCK
__inline USHORT ReadWord( USHORT wOffset )
{
return READ_PORT_USHORT((PUSHORT)(pbEthernetBase + (wOffset*dwRegisterMultiplier)));
};
__inline VOID WriteWord( USHORT wOffset, USHORT Value )
{
WRITE_PORT_USHORT((PUSHORT)(pbEthernetBase + (wOffset*dwRegisterMultiplier)),(USHORT)(Value));
};
#endif
// If this is defined, broadcast frames will be discarded. Since we need to process
// ARPs, leave broadcast filter off, and filter out packets as soon as we can. This, since
// we have to take an interrupt anyway, looking at the first few bytes isn't that bad. If
// the SMC chip could be configured so that we didn't even get interrupts for broadcast
// packets, we might look at updating the ARP cache on the desktop and filtering out all
// broadcasts...
#define NO_BROADCAST 0
#define BROADCAST_FILTER_BIT 0x4000
// If any of the following bits are set in the received status word, discard frame
#if NO_BROADCAST
#define FRAME_FILTER (0xAC00 | BROADCAST_FILTER_BIT) // No broadcast
#else
#define FRAME_FILTER 0xAC00 // Allow broadcast
#endif
// For debugging, if defined, this will cause all Rx/Tx data to be dumped to debug serial port
//#undef SMC_DUMP_FRAMES
//#define SMC_DUMP_FRAMES
// This flag indicates when Frames are pending in the receive buffer.
// It is set by the ISR when a RCV_INT is generated, and will be cleared when the GetFrame() routine
// detects that all Frames have been removed from the buffer.
static UINT16 fFrameReceived;
// Mask to use for interrupts
static UINT16 wIntMask;
// Controls whether we filter out broadcast packets (except ARP requests...)
static DWORD dwConfigOptions;
static void SendFrameError( char *pszPrefix, UINT16 wCompletionCode );
static void SMC_ISR( void );
#ifdef SMC_DUMP_FRAMES
static void DumpEtherFrame( BYTE *pFrame, WORD cwFrameLength );
#endif
// This is called to initialze the ethernet low level driver. The base address of the ethernet hardware
// is passed into the routine. The routine will return TRUE for a successful initialization.
BOOL
SMCInit( BYTE *pbBaseAddress, DWORD dwMultiplier, USHORT MacAddr[3]) {
DWORD dwStartTime;
USHORT wBSR, wConfig;
EdbgOutputDebugString("Entering SMCInit\r\n");
if (! pbBaseAddress)
return FALSE;
pbEthernetBase = pbBaseAddress;
dwRegisterMultiplier = dwMultiplier;
// DANGER - May have to wait 750 usec after reset while EEPROM info loads (Pg 50 of the SMC91C94 spec).
// until this occurs, SMC registers are inaccessible.
WriteWord( BANKSEL_REG, BANK1 );
dwStartTime = OEMEthGetSecs();
while( ReadWord( CONTROL_REG ) & 0x0003 && OEMEthGetSecs() - dwStartTime < 2);
// Verify the I/O base. The upper byte of the BSR always reads 0x33, use this.
// For Odo, the bus cycles will time out and garbage values will be returned.
wBSR = ReadWord(BANKSEL_REG);
if ((wBSR & 0xFF00) != 0x3300) {
EdbgOutputDebugString("SMC card not detected, I/O base 0x%X, BSR: 0x%X\n",pbEthernetBase,wBSR);
return FALSE;
}
EdbgOutputDebugString( "SMC Ethernet card detected at I/O base 0x%X\r\n",pbEthernetBase);
// Read the MAC address from the 91C96. This should have been read in from the EEPROM during reset
SMCGetMACAddress(MacAddr);
EdbgOutputDebugString( "SMC Ethernet card Config_Reg=%x\n",ReadWord(CONFIG_REG));
EdbgOutputDebugString( "SMC Ethernet card Config_Base=%x\n",ReadWord(BASE_REG));
EdbgOutputDebugString( "SMC Ethernet card Switch=%x\n",SMCReadSwitches());
// The following code used to initialize the Mac Address in EEPROM.
if (MacAddr[0]==0xffff && MacAddr[1]==0xffff && MacAddr[2]==0xffff )
{
MacAddr[0] = 0x0008;
MacAddr[1] = 0x000F;
MacAddr[2] = 0x0001;
SMCSetMACAddress(MacAddr);
}
// Set SQUELCH bit - this changes the threshold for a valid signal from 300mV to 180mV.
// This is to attempt to fix problems we have seen with some (usually 8 port) hubs.
WriteWord( BANKSEL_REG, BANK1 );
wConfig = ReadWord(CONFIG_REG);
wConfig |= CFG_SETSQLCH|CFG_NO_WAIT;
WriteWord(CONFIG_REG, wConfig);
EdbgOutputDebugString("SMC config reg val: %X\n",wConfig);
// Initialize the control register
WriteWord( BANKSEL_REG, BANK1 );
WriteWord( CONTROL_REG, CONTROL_REG_INIT );
// Initialize memory configuration register
WriteWord( BANKSEL_REG, BANK0 );
WriteWord( MCR_REG, MCR_REG_INIT );
// Initialize transmit control register
WriteWord( BANKSEL_REG, BANK0 );
WriteWord( TCR_REG, TCR_REG_INIT );
// Initialize interrupt mask register (all ints disabled to start)
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( INTERRUPT_REG, 0);
// The receive register should be the last thing initialzed so that we don't start
// getting Frames before we're ready for them.
// Initialize the Receive Control Register (Pg 39 of the SMC91C94 spec):
WriteWord( BANKSEL_REG, BANK0 );
WriteWord( RCR_REG, RCR_REG_INIT );
EdbgOutputDebugString("SMC Reset complete2\r\n");
// Even if the card is present, it likes to have a second to stabilize before the first transmission
while( OEMEthGetSecs() - dwStartTime < 1 );
return TRUE;
}
void SMCSetMACAddress(USHORT MacAddr[3])
{
// Write the MAC address to the registers on the ethernet controller
WriteWord( BANKSEL_REG, BANK1 );
WriteWord(MACADDR_REG0,MacAddr[0]);
WriteWord( BANKSEL_REG, BANK1 );
WriteWord(MACADDR_REG1,MacAddr[1]);
WriteWord( BANKSEL_REG, BANK1 );
WriteWord(MACADDR_REG2,MacAddr[2]);
// Read it back out, and program our local copy
WriteWord( BANKSEL_REG, BANK1 );
MacAddr[0] = ReadWord( MACADDR_REG0 );
WriteWord( BANKSEL_REG, BANK1 );
MacAddr[1] = ReadWord( MACADDR_REG1 );
WriteWord( BANKSEL_REG, BANK1 );
MacAddr[2] = ReadWord( MACADDR_REG2 );
// Print the MAC address using our local copy
EdbgOutputDebugString("SMC MAC Address: %B:%B:%B:%B:%B:%B\r\n",
MacAddr[0] & 0x00FF, MacAddr[0] >> 8,
MacAddr[1] & 0x00FF, MacAddr[1] >> 8,
MacAddr[2] & 0x00FF, MacAddr[2] >> 8);
// Write the MAC address into EEPROM
SMCWriteEEPROM(0x20, MacAddr[0] );
SMCWriteEEPROM(0x21, MacAddr[1] );
SMCWriteEEPROM(0x22, MacAddr[2] );
}
void SMCGetMACAddress(USHORT MacAddr[3])
{
WriteWord( BANKSEL_REG, BANK1 );
MacAddr[0] = ReadWord( MACADDR_REG0 );
MacAddr[1] = ReadWord( MACADDR_REG1 );
MacAddr[2] = ReadWord( MACADDR_REG2 );
EdbgOutputDebugString("SMC MAC Address: %B:%B:%B:%B:%B:%B\r\n",
MacAddr[0] & 0x00FF, MacAddr[0] >> 8,
MacAddr[1] & 0x00FF, MacAddr[1] >> 8,
MacAddr[2] & 0x00FF, MacAddr[2] >> 8);
}
// Interrupts left disabled at init, call this function to turn them on
void
SMCEnableInts()
{
// Only enable receive interrupts (we poll for Tx completion)
wIntMask = RCV_INTM;
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( INTERRUPT_REG, wIntMask );
// The above code came from Microsoft. According to the spec, a write to the interrupt
// register would merely "acknowledge" this interrupt. If this device works as specified,
// then we need to unmask the receive bit in the interrupt mask. Such as:
wIntMask = RCV_INTM;
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( INTERRUPT_MASK_REG, wIntMask );
}
void
SMCDisableInts()
{
wIntMask = 0;
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( INTERRUPT_REG, wIntMask );
// The above code came from Microsoft. According to the spec, a write to the interrupt
// register would merely "acknowledge" this interrupt. If this device works as specified,
// then we need to mask the receive bit in the interrupt mask. Such as:
wIntMask = 0;
WriteWord( BANKSEL_REG, BANK2 );
WriteWord( INTERRUPT_MASK_REG, wIntMask );
}
DWORD
SMCGetPendingInterrupts()
{
WORD wInts;
DWORD dwRet = 0;
UINT16 wBankSave;
// I need to save/restore the affected registers in the 91C94
wBankSave = ReadWord( BANKSEL_REG );
WriteWord( BANKSEL_REG, BANK2 );
wInts = ReadWord(INTERRUPT_REG);
// Just check for Rx int
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;
}
// 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -