#include <IOKit/assert.h>
#include <IOKit/platform/AppleMacIODevice.h>
#include "MaceEnetPrivate.h"
#define super IOEthernetController
OSDefineMetaClassAndStructors( MaceEnet, IOEthernetController )
#define PROVIDER_DEV 0
#define PROVIDER_DMA_TX 1
#define PROVIDER_DMA_RX 2
bool MaceEnet::init(OSDictionary * properties)
{
if (!super::init(properties))
return false;
isPromiscuous = false;
multicastEnabled = false;
ready = false;
debugClient = false;
debugTxPoll = false;
netifClient = false;
return true;
}
MaceEnet * MaceEnet::probe(IOService * ,
unsigned int * ,
unsigned int * )
{
#ifdef OLD_CODE
extern int kdp_flag;
if( kdp_flag & 1)
{
return 0;
}
#endif
return this;
}
bool MaceEnet::start(IOService * provider)
{
AppleMacIODevice *nub = OSDynamicCast(AppleMacIODevice, provider);
if (!nub || !super::start(provider))
return false;
transmitQueue = OSDynamicCast(IOGatedOutputQueue, getOutputQueue());
if (!transmitQueue)
{
IOLog("Mace: output queue initialization failed\n");
return false;
}
transmitQueue->retain();
debugQueue = IOPacketQueue::withCapacity((UInt) -1);
if (!debugQueue)
return false;
mbufCursor = IOMbufBigMemoryCursor::withSpecification(NETWORK_BUFSIZE, 2);
if (!mbufCursor)
{
IOLog("Mace: IOMbufMemoryCursor allocation failed\n");
return false;
}
for (int i = 0; i < MEMORY_MAP_COUNT; i++) {
IOMemoryMap * map;
map = provider->mapDeviceMemoryWithIndex(i);
if (!map)
return false;
#ifdef DEBUG_XXX
IOLog("map %d: Phys:%08x Virt:%08x len:%d\n",
i,
(UInt) map->getPhysicalAddress(),
(UInt) map->getVirtualAddress(),
(UInt) map->getLength());
#endif
switch (i) {
case MEMORY_MAP_ENET_INDEX:
ioBaseEnet = (IOPPCAddress) map->getVirtualAddress();
ioBaseEnetROM = (IOPPCAddress) ((map->getPhysicalAddress() &
~0xffff) | kControllerROMOffset);
break;
case MEMORY_MAP_TXDMA_INDEX:
ioBaseEnetTxDMA = (IODBDMAChannelRegisters *)
map->getVirtualAddress();
break;
case MEMORY_MAP_RXDMA_INDEX:
ioBaseEnetRxDMA = (IODBDMAChannelRegisters *)
map->getVirtualAddress();
break;
}
maps[i] = map;
}
IODeviceMemory * romMemory = IODeviceMemory::withRange(
(UInt) ioBaseEnetROM, 0x1000);
if (!romMemory) {
IOLog("Mace: can't create ROM memory object\n");
return false;
}
romMap = romMemory->map();
romMemory->release();
if (!romMap)
return false;
ioBaseEnetROM = (IOPPCAddress) romMap->getVirtualAddress();
#ifdef DEBUG_XXX
IOLog("Mace: ioBaseEnet : %08x\n", (UInt) ioBaseEnet);
IOLog("Mace: ioBaseEnetTxDMA : %08x\n", (UInt) ioBaseEnetTxDMA);
IOLog("Mace: ioBaseEnetRxDMA : %08x\n", (UInt) ioBaseEnetRxDMA);
IOLog("Mace: ioBaseEnetROM : %08x\n", (UInt) ioBaseEnetROM);
#endif
IOWorkLoop * myWorkLoop = (IOWorkLoop *) getWorkLoop();
assert(myWorkLoop);
txIntSrc = IOInterruptEventSource::interruptEventSource
(this,
(IOInterruptEventAction) &MaceEnet::interruptOccurredForSource,
provider, PROVIDER_DMA_TX);
if (!txIntSrc
|| (myWorkLoop->addEventSource(txIntSrc) != kIOReturnSuccess)) {
IOLog("Mace: txIntSrc init failure\n");
return false;
}
rxIntSrc = IOInterruptEventSource::interruptEventSource
(this,
(IOInterruptEventAction) &MaceEnet::interruptOccurredForSource,
provider, PROVIDER_DMA_RX);
if (!rxIntSrc
|| (myWorkLoop->addEventSource(rxIntSrc) != kIOReturnSuccess)) {
IOLog("Mace: rxIntSrc init failure\n");
return false;
}
timerSrc = IOTimerEventSource::timerEventSource
(this, (IOTimerEventSource::Action) &MaceEnet::timeoutOccurred);
if (!timerSrc
|| (myWorkLoop->addEventSource(timerSrc) != kIOReturnSuccess)) {
IOLog("Mace: timerSrc init failure\n");
return false;
}
MGETHDR(txDebuggerPkt, M_DONTWAIT, MT_DATA);
if (!txDebuggerPkt)
{
IOLog("Mace: Can't allocate KDB buffer\n");
return false;
}
#if 0
myWorkLoop->enableAllInterrupts();
#endif
#if 0
if ( !resetAndEnable(false) )
{
return false;
}
#endif
getHardwareAddress(&myAddress);
if (_allocateMemory() == false)
{
return false;
}
attachDebuggerClient(&debugger);
if (!attachInterface((IONetworkInterface **) &networkInterface))
return false;
return true;
}
void MaceEnet::free()
{
UInt i;
timerSrc->cancelTimeout();
_resetChip();
if (debugger)
debugger->release();
if (timerSrc)
timerSrc->release();
if (rxIntSrc)
rxIntSrc->release();
if (txIntSrc)
txIntSrc->release();
if (transmitQueue)
transmitQueue->release();
if (debugQueue)
debugQueue->release();
if (networkInterface)
networkInterface->release();
if (mbufCursor)
mbufCursor->release();
if (txDebuggerPkt)
freePacket(txDebuggerPkt);
for (i = 0; i < rxMaxCommand; i++)
if (rxMbuf[i]) freePacket(rxMbuf[i]);
for (i = 0; i < txMaxCommand; i++)
if (txMbuf[i]) freePacket(txMbuf[i]);
if (romMap) romMap->release();
for (i = 0; i < MEMORY_MAP_COUNT; i++)
if (maps[i]) maps[i]->release();
if (dmaMemory.ptr)
{
IOFree(dmaMemory.ptrReal, dmaMemory.sizeReal);
dmaMemory.ptr = 0;
}
if ( workLoop )
{
workLoop->release();
workLoop = 0;
}
super::free();
}
bool MaceEnet::createWorkLoop()
{
workLoop = IOWorkLoop::workLoop();
return ( workLoop != 0 );
}
IOWorkLoop * MaceEnet::getWorkLoop() const
{
return workLoop;
}
void MaceEnet::interruptOccurredForSource(IOInterruptEventSource *src,
int )
{
bool doFlushQueue = false;
bool doService = false;
if (!ready) {
return;
}
reserveDebuggerLock();
if (src == txIntSrc) {
txWDInterrupts++;
KERNEL_DEBUG(DBG_MACE_TXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
doService = _transmitInterruptOccurred();
KERNEL_DEBUG(DBG_MACE_TXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 );
}
else {
KERNEL_DEBUG(DBG_MACE_RXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
doFlushQueue = _receiveInterruptOccurred();
KERNEL_DEBUG(DBG_MACE_RXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 );
}
releaseDebuggerLock();
if (doFlushQueue)
networkInterface->flushInputQueue();
if (doService && netifClient)
transmitQueue->service();
}
UInt32 MaceEnet::outputPacket(struct mbuf *pkt, void *param)
{
u_int32_t i;
u_int8_t regValue;
UInt32 ret = kIOReturnOutputSuccess;
KERNEL_DEBUG(DBG_MACE_TXQUEUE | DBG_FUNC_NONE, (int) pkt,
(int) pkt->m_pkthdr.len, 0, 0, 0 );
reserveDebuggerLock();
do
{
regValue = ReadMaceRegister( ioBaseEnet, kMacCC );
regValue |= kMacCCEnRcv;
WriteMaceRegister( ioBaseEnet, kMacCC, regValue );
assert(pkt && netifClient);
_transmitInterruptOccurred();
i = txCommandTail + 1;
if ( i >= txMaxCommand ) i = 0;
if ( i == txCommandHead )
{
ret = kIOReturnOutputStall;
continue;
}
_transmitPacket(pkt);
}
while ( 0 );
releaseDebuggerLock();
return ret;
}
IOReturn MaceEnet::enable(IONetworkInterface * netif)
{
IONetworkParameter * param;
if (netifClient) {
IOLog("Mace: already enabled\n");
return kIOReturnSuccess;
}
param = netif->getParameter(kIONetworkStatsKey);
if (!param || !(netStats = (IONetworkStats *) param->getBuffer()))
{
IOLog("Mace: invalid network statistics\n");
return kIOReturnError;
}
if ((ready == false) && !resetAndEnable(true))
return kIOReturnIOError;
netifClient = true;
transmitQueue->setCapacity(TRANSMIT_QUEUE_SIZE);
transmitQueue->start();
return kIOReturnSuccess;
}
IOReturn MaceEnet::disable(IONetworkInterface * )
{
if (debugClient == false)
resetAndEnable(false);
transmitQueue->stop();
transmitQueue->setCapacity(0);
transmitQueue->flush();
netifClient = false;
return kIOReturnSuccess;
}
IOReturn MaceEnet::enable(IOKernelDebugger * )
{
if ((ready == false) && !resetAndEnable(true))
return kIOReturnIOError;
debugClient = true;
return kIOReturnSuccess;
}
IOReturn MaceEnet::disable(IOKernelDebugger * )
{
debugClient = false;
if (netifClient == false)
resetAndEnable(false);
return kIOReturnSuccess;
}
bool MaceEnet::resetAndEnable(bool enable)
{
bool ret = true;
if (timerSrc)
timerSrc->cancelTimeout();
_disableAdapterInterrupts();
if (getWorkLoop()) getWorkLoop()->disableAllInterrupts();
reserveDebuggerLock();
ready = false;
_resetChip();
do {
if (!enable) break;
if ( !_initRxRing() || !_initTxRing() || !_initChip() )
{
ret = false;
break;
}
_startChip();
ready = true;
releaseDebuggerLock();
timerSrc->setTimeoutMS(WATCHDOG_TIMER_MS);
if (getWorkLoop()) getWorkLoop()->enableAllInterrupts();
_enableAdapterInterrupts();
return true;
}
while (0);
releaseDebuggerLock();
return ret;
}
void MaceEnet::_sendTestPacket()
{
unsigned char * buf;
const unsigned int size = 64;
struct mbuf * m = allocatePacket(size);
if (!m) {
IOLog("Mace: _sendTestpacket: allocatePacket() failed\n");
return;
}
buf = mtod(m, unsigned char *);
bcopy(&myAddress, buf, NUM_EN_ADDR_BYTES);
buf += NUM_EN_ADDR_BYTES;
bcopy(&myAddress, buf, NUM_EN_ADDR_BYTES);
buf += NUM_EN_ADDR_BYTES;
*buf++ = 0;
*buf++ = 0;
outputPacket(m, 0);
}
void MaceEnet::timeoutOccurred(IOTimerEventSource * )
{
u_int32_t dmaStatus;
bool doFlushQueue = false;
bool doService = false;
reserveDebuggerLock();
dmaStatus = IOGetDBDMAChannelStatus( ioBaseEnetRxDMA );
if ( !(dmaStatus & kdbdmaActive) )
{
#if 0
IOLog("Mace: Timeout check - RxHead = %d RxTail = %d\n",
rxCommandHead, rxCommandTail);
#endif
#if 0
IOLog( "Mace: Rx Commands = %08x(p) Rx DMA Ptr = %08x(p)\n\r", rxDMACommandsPhys, IOGetDBDMACommandPtr(ioBaseEnetRxDMA) );
[self _dumpDesc:(void *)rxDMACommands Size:rxMaxCommand * sizeof(enet_dma_cmd_t)];
#endif
doFlushQueue = _receiveInterruptOccurred();
}
if ( txCommandHead != txCommandTail )
{
if ( txWDInterrupts == 0 )
{
if ( ++txWDTimeouts > 1 ) txWDForceReset = true;
#if 0
IOLog( "Mace: Checking for timeout - TxHead = %d TxTail = %d\n",
txCommandHead, txCommandTail);
#endif
doService = _transmitInterruptOccurred();
}
else
{
txWDTimeouts = 0;
txWDInterrupts = 0;
}
}
else
{
txWDTimeouts = 0;
txWDInterrupts = 0;
}
if (debugTxPoll)
{
debugQueue->flush();
debugTxPoll = false;
releaseDebuggerLock();
doService = true;
}
else
{
releaseDebuggerLock();
}
if (doFlushQueue)
{
networkInterface->flushInputQueue();
}
if (doService && netifClient)
{
transmitQueue->service();
}
timerSrc->setTimeoutMS(WATCHDOG_TIMER_MS);
}
const OSString * MaceEnet::newVendorString() const
{
return OSString::withCString("Apple");
}
const OSString * MaceEnet::newModelString() const
{
return OSString::withCString("Mace");
}
const OSString * MaceEnet::newRevisionString() const
{
return OSString::withCString("");
}
IOReturn MaceEnet::_setPromiscuousMode(IOEnetPromiscuousMode mode)
{
u_int8_t regVal;
regVal = ReadMaceRegister( ioBaseEnet, kMacCC );
WriteMaceRegister( ioBaseEnet, kMacCC, regVal & ~kMacCCEnRcv );
if (mode == kIOEnetPromiscuousModeOff) {
regVal &= ~kMacCCProm;
isPromiscuous = false;
}
else {
regVal |= kMacCCProm;
isPromiscuous = true;
}
WriteMaceRegister( ioBaseEnet, kMacCC, regVal );
return kIOReturnSuccess;
}
IOReturn MaceEnet::setPromiscuousMode(IOEnetPromiscuousMode mode)
{
IOReturn ret;
reserveDebuggerLock();
ret = _setPromiscuousMode(mode);
releaseDebuggerLock();
return ret;
}
IOReturn MaceEnet::setMulticastMode(IOEnetMulticastMode mode)
{
multicastEnabled = (mode == kIOEnetMulticastModeOff) ? false : true;
return kIOReturnSuccess;
}
IOReturn MaceEnet::setMulticastList(IOEthernetAddress *addrs, UInt32 count)
{
reserveDebuggerLock();
_resetHashTableMask();
for (UInt32 i = 0; i < count; i++) {
_addToHashTableMask(addrs->bytes);
addrs++;
}
_updateHashTableMask();
releaseDebuggerLock();
return kIOReturnSuccess;
}
IOOutputQueue * MaceEnet::createOutputQueue()
{
return IOGatedOutputQueue::withTarget( this, getWorkLoop() );
}
void MaceEnet::sendPacket(void *pkt, UInt32 pkt_len)
{
_sendPacket(pkt, pkt_len);
}
void MaceEnet::receivePacket(void *pkt, UInt32 *pkt_len, UInt32 timeout)
{
_receivePacket(pkt, (UInt *) pkt_len, timeout);
}
#if 0 // no power management stuff in IOKit yet.
- (IOReturn)getPowerState:(PMPowerState *)state_p
{
return kIOReturnUnsupported;
}
- (IOReturn)setPowerState:(PMPowerState)state
{
if (state == PM_OFF) {
resetAndEnabled = NO;
[self _resetChip];
return kIOReturnSuccess;
}
return kIOReturnUnsupported;
}
- (IOReturn)getPowerManagement:(PMPowerManagementState *)state_p
{
return kIOReturnUnsupported;
}
- (IOReturn)setPowerManagement:(PMPowerManagementState)state
{
return kIOReturnUnsupported;
}
#endif