#include "BCM440X.h"
#define super IOEthernetController
OSDefineMetaClassAndStructors( AppleBCM440XEthernet, IOEthernetController )
bool BCM440X::start( IOService * provider )
{
bool success = false;
bool started = false;
union {
UInt32 dword[32];
UInt8 byte[];
} eeprom;
do {
if (super::start(provider) != true)
break;
started = true;
sc = IONew(struct bfe_softc, 1);
if (sc == 0)
{
ERROR_LOG("%s: No memory for bfe_softc\n", getName());
break;
}
memset(sc, 0, sizeof(*sc));
if (allocateTxMemory() == false)
{
ERROR_LOG("%s: allocateTxMemory failed\n", getName());
break;
}
if (allocateRxMemory() == false)
{
ERROR_LOG("%s: allocateRxMemory failed\n", getName());
break;
}
fPCIDevice = OSDynamicCast(IOPCIDevice, provider);
if (fPCIDevice == 0) break;
fPCIDevice->retain();
if (fPCIDevice->open(this) == false)
break;
if (initDriverObjects(provider) == false)
{
ERROR_LOG("%s: initDriverObjects failed\n", getName());
break;
}
initPCIConfigSpace(fPCIDevice);
fRegMap = fPCIDevice->mapDeviceMemoryWithRegister(
BFE_PCI_MEMLO, kIOMapInhibitCache );
if (fRegMap == 0)
{
ERROR_LOG("%s: PCI BAR@%x (%08lx) mapping error\n",
getName(), BFE_PCI_MEMLO,
fPCIDevice->configRead32(BFE_PCI_MEMLO));
break;
}
sc->bfe_dev = this;
sc->bfe_unit = 0;
sc->bfe_bhandle = (void *) fRegMap->getVirtualAddress();
sc->bfe_phyaddr = (eeprom.byte[90] & 0x1f);
sc->bfe_mdc_port = (eeprom.byte[90] >> 14) & 0x1;
sc->bfe_tx_dma = fTxDescPhysAddr;
sc->bfe_rx_dma = fRxDescPhysAddr;
for (int i = 0; i < 32; i++)
eeprom.dword[i] = CSR_READ_4(sc, 0x1000 + 4*i);
fEnetAddr.bytes[0] = eeprom.byte[79];
fEnetAddr.bytes[1] = eeprom.byte[78];
fEnetAddr.bytes[2] = eeprom.byte[81];
fEnetAddr.bytes[3] = eeprom.byte[80];
fEnetAddr.bytes[4] = eeprom.byte[83];
fEnetAddr.bytes[5] = eeprom.byte[82];
DEBUG_LOG("Enet address = %02x:%02x:%02x:%02x:%02x:%02x\n",
fEnetAddr.bytes[0], fEnetAddr.bytes[1],
fEnetAddr.bytes[2], fEnetAddr.bytes[3],
fEnetAddr.bytes[4], fEnetAddr.bytes[5]);
bfe_chip_reset(sc);
if (fPHY->probePHY() != kIOReturnSuccess)
{
ERROR_LOG("%s: PHY not detected\n", getName());
break;
}
publishMediaSupport();
success = true;
} while (0);
if (fPCIDevice) fPCIDevice->close(this);
do {
if (success == false) break;
success = false;
if (attachInterface((IONetworkInterface **) &fNetif, false) == false)
break;
fKDPMbuf = allocatePacket(BFE_BUFFER_SIZE);
if (fKDPMbuf &&
fTxMbufCursor->getPhysicalSegments(fKDPMbuf, &fKDPMbufSeg) == 1)
{
attachDebuggerClient(&fKDPNub);
}
fNetif->registerService();
success = true;
} while (0);
if (started && !success)
{
super::stop(provider);
}
return success;
}
void BCM440X::free( void )
{
#define RELEASE(x) do { if(x) { (x)->release(); (x) = 0; } } while(0)
DEBUG_LOG("BCM440X::free\n");
assert(fEnabledForKDP == false);
assert(fEnabledForBSD == false);
if (fInterruptSrc && fWorkLoop)
{
fWorkLoop->removeEventSource(fInterruptSrc);
}
RELEASE( fInterruptSrc );
RELEASE( fKDPNub );
RELEASE( fNetif );
RELEASE( fTxMbufCursor );
RELEASE( fPCIDevice );
RELEASE( fWorkLoop );
RELEASE( fRegMap );
RELEASE( fWatchdogTimer );
RELEASE( fGarbageQueue );
RELEASE( fMediaDict );
RELEASE( fPHY );
RELEASE( fKDPNub );
releaseTxMemory();
releaseRxMemory();
if (fKDPMbuf)
{
freePacket(fKDPMbuf);
fKDPMbuf = 0;
}
if (sc)
{
IODelete(sc, struct bfe_softc, 1);
sc = 0;
}
return super::free();
}
bool BCM440X::initDriverObjects( IOService * provider )
{
fPHY = AppleBCM440XPHY::BCM440XPHY(this, mdiRead, mdiWrite);
if (fPHY == 0)
return false;
fTransmitQueue = getOutputQueue();
if (fTransmitQueue == 0)
return false;
fGarbageQueue = IOPacketQueue::withCapacity(~0);
if (fGarbageQueue == 0)
return false;
IOWorkLoop * workLoop = (IOWorkLoop *) getWorkLoop();
if (!workLoop)
{
DEBUG_LOG("No work loop\n");
return false;
}
fTxMbufCursor = IOMbufNaturalMemoryCursor::withSpecification(
BFE_BUFFER_SIZE, 1);
if (!fTxMbufCursor)
{
DEBUG_LOG("TX mbuf cursor allocation error\n");
return false;
}
fInterruptSrc = IOInterruptEventSource::interruptEventSource( this,
(IOInterruptEventAction) &BCM440X::interruptOccurred,
provider);
if (!fInterruptSrc ||
(workLoop->addEventSource(fInterruptSrc) != kIOReturnSuccess))
{
DEBUG_LOG("IOInterruptEventSource error\n");
return false;
}
fWatchdogTimer = IOTimerEventSource::timerEventSource(this,
(IOTimerEventSource::Action) &BCM440X::watchdogTimeout);
if (!fWatchdogTimer ||
(workLoop->addEventSource(fWatchdogTimer) != kIOReturnSuccess))
{
DEBUG_LOG("IOTimerEventSource error\n");
return false;
}
fInterruptSrc->enable();
return true;
}
void BCM440X::initPCIConfigSpace( IOPCIDevice * pci )
{
UInt16 cmd = pci->configRead16(kIOPCIConfigCommand);
cmd |= ( kIOPCICommandBusMaster |
kIOPCICommandMemorySpace |
kIOPCICommandMemWrInvalidate );
cmd &= ~kIOPCICommandIOSpace;
pci->configWrite16(kIOPCIConfigCommand, cmd);
DEBUG_LOG("PCI CMD = %04x\n", pci->configRead16(kIOPCIConfigCommand));
}
IOReturn BCM440X::getHardwareAddress( IOEthernetAddress * addr )
{
memcpy(addr, &fEnetAddr, sizeof(*addr));
return kIOReturnSuccess;
}
bool BCM440X::createWorkLoop( void )
{
fWorkLoop = IOWorkLoop::workLoop();
return (fWorkLoop != 0);
}
IOWorkLoop * BCM440X::getWorkLoop( void ) const
{
return fWorkLoop;
}
const OSString * BCM440X::newVendorString( void ) const
{
return OSString::withCString("Broadcom");
}
const OSString * BCM440X::newModelString( void ) const
{
return OSString::withCString("4401");
}
bool BCM440X::configureInterface( IONetworkInterface * netif )
{
IONetworkData * data;
if (super::configureInterface(netif) == false)
return false;
data = netif->getParameter(kIONetworkStatsKey);
if (!data || !(fNetStats = (IONetworkStats *) data->getBuffer()))
{
DEBUG_LOG("No network statistics\n");
return false;
}
data = netif->getParameter(kIOEthernetStatsKey);
if (!data || !(fEtherStats = (IOEthernetStats *) data->getBuffer()))
{
DEBUG_LOG("No Ethernet statistics\n");
return false;
}
return true;
}
#pragma mark -
#pragma mark ••• Enable and Disable •••
#pragma mark -
bool BCM440X::increaseActivationLevel( UInt32 newLevel )
{
bool success = false;
switch (newLevel)
{
case kActivationLevelKDP:
if ((fPCIDevice == 0) || (fPCIDevice->open(this) == false))
break;
bfe_chip_reset(sc);
if (initTxRing() == false || initRxRing() == false)
{
ERROR_LOG("%s: TX/RX ring init failed\n", getName());
break;
}
fSelectMediumOverride = true;
selectMedium(getSelectedMedium());
fSelectMediumOverride = false;
initRxFilter();
bfe_chip_enable(sc);
fWatchdogTimer->setTimeoutMS(kWatchdogTimerPeriodMS);
success = true;
break;
case kActivationLevelBSD:
bfe_enable_interrupts(sc);
fInterruptEnabled = true;
fTransmitQueue->setCapacity(1024);
fTransmitQueue->start();
success = true;
break;
}
return success;
}
bool BCM440X::decreaseActivationLevel( UInt32 newLevel )
{
bool success = true;
switch (newLevel)
{
case kActivationLevelKDP:
bfe_chip_halt(sc);
IOSleep(10);
bfe_chip_reset(sc);
fWatchdogTimer->cancelTimeout();
setLinkStatus( kIONetworkLinkValid );
IOSleep(20);
freeTxRingPackets();
freeRxRingPackets();
if (fPCIDevice)
fPCIDevice->close(this);
break;
case kActivationLevelBSD:
bfe_disable_interrupts(sc);
fInterruptEnabled = false;
fTransmitQueue->stop();
fTransmitQueue->setCapacity(0);
fTransmitQueue->flush();
break;
}
return success;
}
bool BCM440X::resetCurrentActivationLevel( void )
{
bool success = false;
do {
if (fActivationLevel != kActivationLevelKDP &&
fActivationLevel != kActivationLevelBSD)
{
success = true;
break;
}
if (fActivationLevel == kActivationLevelBSD)
fTransmitQueue->stop();
IODebuggerLockState state = IOKernelDebugger::lock(this);
bfe_chip_halt(sc);
IOSleep(10);
bfe_chip_reset(sc);
fInterruptEnabled = false;
if (initTxRing() == false || initRxRing() == false)
{
ERROR_LOG("%s: TX/RX ring init failed\n", getName());
IOKernelDebugger::unlock(state);
break;
}
initRxFilter();
bfe_chip_enable(sc);
if (fActivationLevel == kActivationLevelBSD)
{
bfe_enable_interrupts(sc);
fInterruptEnabled = true;
IOKernelDebugger::unlock(state);
fTransmitQueue->start();
}
else
{
IOKernelDebugger::unlock(state);
}
success = true;
} while (0);
return success;
}
bool BCM440X::setActivationLevel( UInt32 inLevel )
{
bool success = false;
DEBUG_LOG("setActivationLevel %ld\n", inLevel);
if (fActivationLevel == inLevel) return true;
for ( ; fActivationLevel > inLevel; fActivationLevel--)
{
if ((success = decreaseActivationLevel(fActivationLevel)) == false)
break;
}
for ( ; fActivationLevel < inLevel; fActivationLevel++ )
{
if ((success = increaseActivationLevel(fActivationLevel+1)) == false)
break;
}
return success;
}
IOReturn BCM440X::enable( IONetworkInterface * netif )
{
if (fEnabledForBSD) return kIOReturnSuccess;
fEnabledForBSD = setActivationLevel(kActivationLevelBSD);
return fEnabledForBSD ? kIOReturnSuccess : kIOReturnIOError;
}
IOReturn BCM440X::disable( IONetworkInterface * netif )
{
fEnabledForBSD = false;
setActivationLevel(fEnabledForKDP ?
kActivationLevelKDP :
kActivationLevelNone);
return kIOReturnSuccess;
}
IOReturn BCM440X::enable( IOKernelDebugger * debugger )
{
if (fEnabledForKDP || fEnabledForBSD)
{
fEnabledForKDP = true;
return kIOReturnSuccess;
}
fEnabledForKDP = setActivationLevel(kActivationLevelKDP);
return (fEnabledForKDP ? kIOReturnSuccess : kIOReturnIOError);
}
IOReturn BCM440X::disable( IOKernelDebugger * debugger )
{
fEnabledForKDP = false;
if (fEnabledForBSD == false)
setActivationLevel(kActivationLevelNone);
return kIOReturnSuccess;
}
#pragma mark -
#pragma mark ••• Transmit •••
#pragma mark -
bool BCM440X::allocateTxMemory( void )
{
fTxDescMemory = IOBufferMemoryDescriptor::withOptions(
kIOMemoryPhysicallyContiguous,
BFE_TX_LIST_SIZE, PAGE_SIZE);
if (fTxDescMemory == 0)
{
ERROR_LOG("%s: No memory for transmit descriptors\n", getName());
return false;
}
if (fTxDescMemory->prepare() != kIOReturnSuccess)
{
ERROR_LOG("%s: TX memory prepare failed\n", getName());
fTxDescMemory->release();
fTxDescMemory = 0;
return false;
}
fTxDescPhysAddr = fTxDescMemory->getPhysicalSegment(0, 0);
if (fTxDescPhysAddr == 0)
{
ERROR_LOG("%s: TX memory getPhysicalSegment failed\n", getName());
return false;
}
fTxDescBase = (bfe_desc *) fTxDescMemory->getBytesNoCopy();
DEBUG_LOG("TX DESC Base V=%p P=0x%lx\n", fTxDescBase, fTxDescPhysAddr);
memset(fTxDescBase, 0, BFE_TX_LIST_SIZE);
fTxPacketArray = IONew(mbuf_t, BFE_TX_LIST_CNT);
if (fTxPacketArray == 0)
{
ERROR_LOG("%s: No memory for transmit packet list\n", getName());
return false;
}
memset(fTxPacketArray, 0, BFE_TX_LIST_CNT * sizeof(mbuf_t));
return true;
}
void BCM440X::releaseTxMemory( void )
{
if (fTxDescMemory)
{
fTxDescMemory->complete();
fTxDescMemory->release();
fTxDescMemory = 0;
fTxDescBase = 0;
}
if (fTxPacketArray)
{
freeTxRingPackets();
IODelete(fTxPacketArray, mbuf_t, BFE_TX_LIST_CNT);
fTxPacketArray = 0;
}
}
bool BCM440X::initTxRing( void )
{
freeTxRingPackets();
memset(fTxDescBase, 0, BFE_TX_LIST_SIZE);
memset(fTxPacketArray, 0, BFE_TX_LIST_CNT * sizeof(mbuf_t));
fTxHeadIndex = 0;
fTxTailIndex = 0;
fTxDescBusy = 0;
fTxRingDelayIOC = 0;
return true;
}
void BCM440X::freeTxRingPackets( void )
{
if (fTxPacketArray)
{
for (int i = 0; i < BFE_TX_LIST_CNT; i++)
{
if (fTxPacketArray[i])
{
freePacket(fTxPacketArray[i]);
fTxPacketArray[i] = 0;
}
}
}
}
UInt32 BCM440X::outputPacket( mbuf_t packet, void * param )
{
enum {
kTxMaxSegmentCount = 8,
kTxDescLowWater = kTxMaxSegmentCount + 2
};
UInt32 ctrl = 0;
int curFrag, lastFrag, fragCount;
IOPhysicalSegment vectors[kTxMaxSegmentCount];
IODebuggerLockState state = IOKernelDebugger::lock(this);
if (BFE_TX_LIST_CNT - fTxDescBusy < kTxDescLowWater)
{
IOKernelDebugger::unlock(state);
return kIOReturnOutputStall;
}
fragCount = fTxMbufCursor->getPhysicalSegmentsWithCoalesce(
packet, vectors,
kTxMaxSegmentCount);
if (fragCount == 0)
{
DEBUG_LOG("TX Cursor returned 0 segments\n");
goto drop_packet;
}
curFrag = lastFrag = fTxTailIndex;
for (int i = 0; i < fragCount; i++)
{
ctrl = vectors[i].length & BFE_DESC_LEN;
if (i == 0)
ctrl |= BFE_DESC_SOF;
if (curFrag == BFE_TX_LIST_CNT - 1)
ctrl |= BFE_DESC_EOT;
OSWriteLittleInt32(&fTxDescBase[curFrag].bfe_ctrl, 0, ctrl);
OSWriteLittleInt32(&fTxDescBase[curFrag].bfe_addr, 0,
vectors[i].location + BFE_PCI_DMA);
lastFrag = curFrag;
BFE_INC(curFrag, BFE_TX_LIST_CNT);
}
ctrl |= BFE_DESC_EOF;
fTxRingDelayIOC += fragCount;
if (fTxRingDelayIOC >= min(32, BFE_TX_LIST_CNT/4))
{
fTxRingDelayIOC = 0;
ctrl |= BFE_DESC_IOC;
}
OSWriteLittleInt32(&fTxDescBase[lastFrag].bfe_ctrl, 0, ctrl);
fTxPacketArray[lastFrag] = packet;
fTxDescBusy += fragCount;
fTxTailIndex = curFrag;
CSR_WRITE_4(sc, BFE_DMATX_PTR, curFrag * sizeof(struct bfe_desc));
IOKernelDebugger::unlock(state);
NET_STAT(outputPackets, 1);
RX_TX_LOG("TX index %d\n", lastFrag);
return kIOReturnOutputSuccess;
drop_packet:
IOKernelDebugger::unlock(state);
freePacket(packet);
NET_STAT(outputErrors, 1);
return kIOReturnOutputDropped;
}
void BCM440X::serviceTxInterrupt( void )
{
UInt32 index;
UInt32 chipIndex;
chipIndex = CSR_READ_4(sc, BFE_DMATX_STAT) & BFE_STAT_CDMASK;
chipIndex /= sizeof(struct bfe_desc);
for ( index = fTxHeadIndex; fTxDescBusy > 0; )
{
RX_TX_LOG("TX ISR index %ld chipIndex %ld\n", index, chipIndex);
if (index == chipIndex)
break;
if (fTxPacketArray[index] != 0)
{
freePacket(fTxPacketArray[index]);
fTxPacketArray[index] = 0;
}
fTxDescBusy--;
BFE_INC(index, BFE_TX_LIST_CNT);
}
fTxHeadIndex = index;
fTransmitQueue->service();
}
IOOutputQueue * BCM440X::createOutputQueue( void )
{
return IOGatedOutputQueue::withTarget(this, getWorkLoop());
}
#pragma mark -
#pragma mark ••• Receive •••
#pragma mark -
bool BCM440X::allocateRxMemory( void )
{
fRxDescMemory = IOBufferMemoryDescriptor::withOptions(
kIOMemoryPhysicallyContiguous,
BFE_RX_LIST_SIZE, PAGE_SIZE);
if (fRxDescMemory == 0)
{
ERROR_LOG("%s: No memory for receive descriptors\n", getName());
return false;
}
if (fRxDescMemory->prepare() != kIOReturnSuccess)
{
ERROR_LOG("%s: RX memory prepare failed\n", getName());
fRxDescMemory->release();
fRxDescMemory = 0;
return false;
}
fRxDescPhysAddr = fRxDescMemory->getPhysicalSegment(0, 0);
if (fRxDescPhysAddr == 0)
{
ERROR_LOG("%s: RX memory getPhysicalSegment failed\n", getName());
return false;
}
fRxDescBase = (bfe_desc *) fRxDescMemory->getBytesNoCopy();
DEBUG_LOG("RX DESC Base V=%p P=0x%lx\n", fRxDescBase, fRxDescPhysAddr);
memset(fRxDescBase, 0, BFE_RX_LIST_SIZE);
fRxPacketArray = IONew(mbuf_t, BFE_RX_LIST_CNT);
if (fRxPacketArray == 0)
{
ERROR_LOG("%s: No memory for receive packet list\n", getName());
return false;
}
memset(fRxPacketArray, 0, BFE_RX_LIST_CNT * sizeof(mbuf_t));
return true;
}
void BCM440X::releaseRxMemory( void )
{
if (fRxDescMemory)
{
fRxDescMemory->complete();
fRxDescMemory->release();
fRxDescMemory = 0;
fRxDescBase = 0;
}
if (fRxPacketArray)
{
freeRxRingPackets();
IODelete(fRxPacketArray, mbuf_t, BFE_RX_LIST_CNT);
fRxPacketArray = 0;
}
}
bool BCM440X::initRxRing( void )
{
int i;
memset(fRxDescBase, 0, BFE_RX_LIST_SIZE);
for (i = 0; i < BFE_RX_LIST_CNT; i++)
{
if (fRxPacketArray[i] == 0)
fRxPacketArray[i] = allocatePacket(BFE_BUFFER_SIZE);
if (fRxPacketArray[i] == 0)
return false;
if (updateRxDescriptor(i) == false)
return false;
}
CSR_WRITE_4(sc, BFE_DMARX_PTR, i * sizeof(bfe_desc));
fRxHeadIndex = 0;
return true;
}
void BCM440X::freeRxRingPackets( void )
{
if (fRxPacketArray)
{
for (int i = 0; i < BFE_RX_LIST_CNT; i++)
{
if (fRxPacketArray[i])
{
freePacket(fRxPacketArray[i]);
fRxPacketArray[i] = 0;
}
}
}
}
#define MBUF_PHYS_ADDR(m) \
((IOPhysicalAddress)mbuf_data_to_physical(mbuf_data(m)))
bool BCM440X::updateRxDescriptor( UInt32 index )
{
bfe_rxheader * rx_header;
UInt32 ctrl;
mbuf_t packet;
assert(index <= BFE_RX_LIST_CNT);
packet = fRxPacketArray[index];
rx_header = (struct bfe_rxheader *) mbuf_data(packet);
rx_header->len = 0;
rx_header->flags = 0;
ctrl = BFE_BUFFER_SIZE;
if (index == BFE_RX_LIST_CNT - 1)
ctrl |= BFE_DESC_EOT;
OSWriteLittleInt32(&fRxDescBase[index].bfe_ctrl, 0, ctrl);
OSWriteLittleInt32(&fRxDescBase[index].bfe_addr, 0,
MBUF_PHYS_ADDR(packet) + BFE_PCI_DMA);
return true;
}
void BCM440X::serviceRxInterrupt( void )
{
UInt32 index;
UInt32 chipIndex;
bool replaced;
bfe_rxheader * rxh;
UInt16 flags, len;
mbuf_t inputPkt;
UInt8 * inputData;
chipIndex = (CSR_READ_4(sc, BFE_DMARX_STAT) & BFE_STAT_CDMASK) /
sizeof(struct bfe_desc);
for (index = fRxHeadIndex; index != chipIndex; )
{
rxh = (bfe_rxheader *) mbuf_data(fRxPacketArray[index]);
len = OSSwapLittleToHostInt16(rxh->len);
if (len == 0)
{
for (int retry = 0; retry < 10; retry++)
{
len = OSSwapLittleToHostInt16(rxh->len);
if (len) break;
DEBUG_LOG("RX zero header len: chip %ld index %ld retry %d\n",
chipIndex, index, retry);
IODelay(1);
}
if (len == 0)
{
resetCurrentActivationLevel();
ETH_STAT(dot3RxExtraEntry.resets, 1);
return;
}
}
flags = OSSwapLittleToHostInt16(rxh->flags);
if ((len > kIOEthernetMaxPacketSize) ||
(len < kIOEthernetMinPacketSize) ||
((flags & (BFE_RX_FLAG_ERRORS|BFE_RX_FLAG_LAST)) != BFE_RX_FLAG_LAST))
{
DEBUG_LOG("RX error len %d flags 0x%x\n", len, flags);
NET_STAT(inputErrors, 1);
goto next;
}
inputPkt = replaceOrCopyPacket(&fRxPacketArray[index],
len + BFE_RX_OFFSET,
&replaced);
if (inputPkt == 0)
{
DEBUG_LOG("RX replaceOrCopy error, len %d flags 0x%x\n",
len + BFE_RX_OFFSET, flags);
NET_STAT(inputErrors, 1);
goto next;
}
inputData = (UInt8 *) mbuf_data(inputPkt);
inputData += BFE_RX_OFFSET;
mbuf_setdata(inputPkt, inputData, len);
fNetif->inputPacket( inputPkt, len,
IONetworkInterface::kInputOptionQueuePacket );
NET_STAT(inputPackets, 1);
RX_TX_LOG("RX %d bytes at index %ld\n", len, index);
next:
updateRxDescriptor(index);
BFE_INC(index, BFE_RX_LIST_CNT);
}
fRxHeadIndex = index;
fNetif->flushInputQueue();
}
IOReturn BCM440X::setPromiscuousMode( bool active )
{
UInt32 rxc = CSR_READ_4(sc, BFE_RXCONF);
DEBUG_LOG("setPromiscuousMode %d\n", active);
if (active)
rxc |= BFE_RXCONF_PROMISC;
else
rxc &= ~BFE_RXCONF_PROMISC;
CSR_WRITE_4(sc, BFE_RXCONF, rxc);
return kIOReturnSuccess;
}
IOReturn BCM440X::setMulticastMode( bool active )
{
return kIOReturnSuccess;
}
IOReturn BCM440X::setMulticastList( IOEthernetAddress * mcAddrList,
UInt32 mcAddrCount )
{
UInt32 index = 0;
DEBUG_LOG("setMulticastList cnt=%ld\n", mcAddrCount);
CSR_WRITE_4(sc, BFE_CAM_CTRL, 0);
bfe_cam_write(sc, fEnetAddr.bytes, index++);
for ( UInt32 i = 0; i < mcAddrCount; i++ )
{
bfe_cam_write(sc, mcAddrList[i].bytes, index++);
}
BFE_OR(sc, BFE_CAM_CTRL, BFE_CAM_ENABLE);
return kIOReturnSuccess;
}
void BCM440X::initRxFilter( void )
{
UInt32 rxc = CSR_READ_4(sc, BFE_RXCONF);
DEBUG_LOG("initRxFilter: rxconfig was 0x%lx\n", rxc);
rxc &= ~(BFE_RXCONF_DBCAST |
BFE_RXCONF_ALLMULTI |
BFE_RXCONF_PROMISC |
BFE_RXCONF_LPBACK |
BFE_RXCONF_FLOW |
BFE_RXCONF_ACCEPT |
BFE_RXCONF_RFILT );
CSR_WRITE_4(sc, BFE_CAM_CTRL, 0);
bfe_cam_write(sc, fEnetAddr.bytes, 0);
CSR_WRITE_4(sc, BFE_RXCONF, rxc);
BFE_OR(sc, BFE_CAM_CTRL, BFE_CAM_ENABLE);
}
#pragma mark -
#pragma mark ••• Interrupt and Timer •••
#pragma mark -
void BCM440X::interruptOccurred( IOInterruptEventSource * src, int count )
{
IODebuggerLockState state;
UInt32 istat;
if (fInterruptEnabled == false)
return;
state = IOKernelDebugger::lock(this);
while (1)
{
istat = CSR_READ_4(sc, BFE_ISTAT) & BFE_IMASK_DEF;
if (istat == 0)
break;
CSR_WRITE_4(sc, BFE_ISTAT, istat);
if (istat & BFE_ISTAT_ERRORS)
{
UInt32 flag = CSR_READ_4(sc, BFE_DMATX_STAT);
if (flag & BFE_STAT_EMASK)
NET_STAT(inputErrors, 1);
flag = CSR_READ_4(sc, BFE_DMARX_STAT);
if (flag & BFE_RX_FLAG_ERRORS)
NET_STAT(inputErrors, 1);
resetCurrentActivationLevel();
ETH_STAT(dot3RxExtraEntry.resets, 1);
}
if (istat & BFE_ISTAT_RX)
{
serviceRxInterrupt();
ETH_STAT(dot3RxExtraEntry.interrupts, 1);
}
if (istat & BFE_ISTAT_TX)
{
serviceTxInterrupt();
ETH_STAT(dot3TxExtraEntry.interrupts, 1);
}
}
IOKernelDebugger::unlock(state);
}
void BCM440X::watchdogTimeout( OSObject * owner,
IOTimerEventSource * timer )
{
BCM440X * me = (BCM440X *) owner;
if (me->fActivationLevel >= kActivationLevelKDP)
{
bool linkChanged;
me->fPHY->checkForLinkChange(&linkChanged, kPHYLinkChangePoll);
if (linkChanged)
me->reportLinkStatus();
if (me->fActivationLevel >= kActivationLevelBSD)
me->fNetStats->collisions += CSR_READ_4(me->sc, BFE_TX_TCOLS);
}
if (me->fGarbageQueue->getSize())
{
IODebuggerLockState state = IOKernelDebugger::lock(me);
me->fGarbageQueue->flush();
IOKernelDebugger::unlock(state);
}
timer->setTimeoutMS(kWatchdogTimerPeriodMS);
}
#pragma mark -
#pragma mark ••• MDIO Interface •••
#pragma mark -
bool BCM440X::mdiRead( void * owner, UInt32 phyAddr, UInt16 phyReg,
UInt16 * phyData )
{
u_int32_t value;
struct bfe_softc * sc = ((BCM440X *)owner)->sc;
sc->bfe_phyaddr = phyAddr;
if (bfe_readphy(sc, phyReg, &value) == 0)
{
*phyData = (PHYWord) value;
return true;
}
else
{
*phyData = 0;
return false;
}
}
bool BCM440X::mdiWrite( void * owner, UInt32 phyAddr, UInt16 phyReg,
UInt16 phyData )
{
struct bfe_softc * sc = ((BCM440X *)owner)->sc;
sc->bfe_phyaddr = phyAddr;
return (bfe_writephy(sc, phyReg, phyData) == 0);
}
#pragma mark -
#pragma mark ••• Media •••
#pragma mark -
static bool addMedium( OSDictionary * table,
UInt32 type, UInt32 speed, UInt32 refcon )
{
IONetworkMedium * medium;
bool success = false;
medium = IONetworkMedium::medium(type, speed, 0, refcon);
if (medium)
{
success = IONetworkMedium::addMedium(table, medium);
medium->release();
}
return success;
}
bool BCM440X::publishMediaSupport( void )
{
UInt32 supportMask;
if (fPHY == 0) return false;
if (fMediaDict) return true;
fMediaDict = OSDictionary::withCapacity(5);
if (!fMediaDict)
return false;
supportMask = fPHY->getLocalLinkSupportMask();
addMedium(fMediaDict, kIOMediumEthernetAuto, 0, kMIILinkAutoNeg);
if (supportMask & kMIILink10BASET)
addMedium(fMediaDict,
kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex,
10, kMIILink10BASET);
if (supportMask & kMIILink10BASET_FD)
addMedium(fMediaDict,
kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex,
10, kMIILink10BASET_FD);
if (supportMask & kMIILink100BASETX)
addMedium(fMediaDict,
kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex,
100, kMIILink100BASETX);
if (supportMask & kMIILink100BASETX_FD)
addMedium(fMediaDict,
kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex,
100, kMIILink100BASETX_FD);
return publishMediumDictionary(fMediaDict);
}
void BCM440X::reportLinkStatus( void )
{
IONetworkMedium * medium;
UInt32 miiLink;
miiLink = fPHY->getActiveLink();
if (miiLink == kMIILinkNone)
{
setLinkStatus(kIONetworkLinkValid, getSelectedMedium());
}
else
{
medium = IONetworkMedium::getMediumWithIndex(fMediaDict, miiLink);
setLinkStatus( kIONetworkLinkValid | kIONetworkLinkActive, medium);
}
}
IOReturn BCM440X::selectMedium( const IONetworkMedium * medium )
{
IOReturn ior = kIOReturnSuccess;
UInt32 miiLink;
if (OSDynamicCast(IONetworkMedium, medium) == 0)
{
medium = IONetworkMedium::getMediumWithIndex(fMediaDict, kMIILinkAutoNeg);
if (medium == 0) return kIOReturnBadArgument;
}
setSelectedMedium(medium);
if (fSelectMediumOverride || fActivationLevel > kActivationLevelNone)
{
miiLink = medium->getIndex();
ior = fPHY->programLink(miiLink, 5000);
bfe_setupphy(sc);
fPHY->checkForLinkChange();
reportLinkStatus();
}
return ior;
}
#pragma mark -
#pragma mark ••• Power Management •••
#pragma mark -
enum {
kPowerStateOff = 0,
kPowerStateOn,
kPowerStateCount
};
IOReturn BCM440X::registerWithPolicyMaker( IOService * policyMaker )
{
static IOPMPowerState powerStateArray[ kPowerStateCount ] =
{
{ 1,0,0,0,0,0,0,0,0,0,0,0 },
{ 1,kIOPMDeviceUsable,kIOPMPowerOn,kIOPMPowerOn,0,0,0,0,0,0,0,0 }
};
return policyMaker->registerPowerDriver( this, powerStateArray,
kPowerStateCount );
}
IOReturn BCM440X::setPowerState( unsigned long powerStateOrdinal,
IOService * policyMaker )
{
return IOPMAckImplied;
}
#pragma mark -
#pragma mark ••• KDP (Polled Mode) Interface •••
#pragma mark -
enum { kDebuggerPollDelayUS = 10 };
void BCM440X::receivePacket( void * pkt_data,
UInt32 * pkt_size,
UInt32 timeoutMS )
{
bfe_rxheader * rxh;
UInt32 chipIndex;
UInt16 flags, len;
int timeoutUS = timeoutMS * 1000;
*pkt_size = 0;
while (1)
{
chipIndex = (CSR_READ_4(sc, BFE_DMARX_STAT) & BFE_STAT_CDMASK) /
sizeof(struct bfe_desc);
if (chipIndex != fRxHeadIndex)
{
rxh = (bfe_rxheader *) mbuf_data(fRxPacketArray[fRxHeadIndex]);
len = OSSwapLittleToHostInt16(rxh->len);
flags = OSSwapLittleToHostInt16(rxh->flags);
if (len) break; }
if (timeoutUS <= 0)
return;
IODelay(kDebuggerPollDelayUS);
timeoutUS -= kDebuggerPollDelayUS;
}
if ((len >= kIOEthernetMinPacketSize) &&
(len <= kIOEthernetMaxPacketSize) &&
((flags & (BFE_RX_FLAG_ERRORS|BFE_RX_FLAG_LAST)) == BFE_RX_FLAG_LAST))
{
const UInt8 * frameData;
frameData = (const UInt8 *) mbuf_data(fRxPacketArray[fRxHeadIndex]) +
BFE_RX_OFFSET;
memcpy(pkt_data, frameData, len);
*pkt_size = len;
}
updateRxDescriptor(fRxHeadIndex);
BFE_INC(fRxHeadIndex, BFE_RX_LIST_CNT);
}
void BCM440X::sendPacket( void * pkt_data, UInt32 pkt_size )
{
UInt32 ctrl;
bfe_desc * desc;
if (!pkt_data || pkt_size > kIOEthernetMaxPacketSize) return;
if (fTxDescBusy >= BFE_TX_LIST_CNT)
{
DEBUG_LOG("sendPacket: no TX descriptor\n");
return;
}
desc = &fTxDescBase[fTxTailIndex];
memcpy(mbuf_data(fKDPMbuf), pkt_data, pkt_size);
ctrl = pkt_size & BFE_DESC_LEN;
ctrl |= BFE_DESC_SOF | BFE_DESC_EOF | BFE_DESC_IOC;
if (fTxTailIndex == BFE_TX_LIST_CNT - 1)
ctrl |= BFE_DESC_EOT;
OSWriteLittleInt32(&desc->bfe_ctrl, 0, ctrl);
OSWriteLittleInt32(&desc->bfe_addr, 0,
fKDPMbufSeg.location + BFE_PCI_DMA);
fTxDescBusy++;
BFE_INC(fTxTailIndex, BFE_TX_LIST_CNT);
CSR_WRITE_4(sc, BFE_DMATX_PTR, fTxTailIndex * sizeof(struct bfe_desc));
do {
UInt32 chipIndex;
chipIndex = CSR_READ_4(sc, BFE_DMATX_STAT) & BFE_STAT_CDMASK;
chipIndex /= sizeof(struct bfe_desc);
while ((fTxHeadIndex != chipIndex) && (fTxDescBusy > 0))
{
if (fTxPacketArray[fTxHeadIndex] != 0)
{
fGarbageQueue->enqueue(fTxPacketArray[fTxHeadIndex]);
fTxPacketArray[fTxHeadIndex] = 0;
}
fTxDescBusy--;
BFE_INC(fTxHeadIndex, BFE_TX_LIST_CNT);
}
IODelay(kDebuggerPollDelayUS);
} while (fTxDescBusy > 0);
}
#pragma mark -
#pragma mark ••• BFE Driver Shim •••
#pragma mark -
extern "C" {
__private_extern__
int pci_read_config( void * device, unsigned offset, unsigned size )
{
assert(size == 4);
BCM440X * me = (BCM440X *) device;
return me->fPCIDevice->configRead32( offset );
}
__private_extern__
void pci_write_config( void * device, unsigned offset, unsigned value,
unsigned size )
{
assert(size == 4);
BCM440X * me = (BCM440X *) device;
return me->fPCIDevice->configWrite32( offset, value );
}
}