AppleUSBCDCEEM.cpp [plain text]
#include <machine/limits.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/network/IOEthernetController.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IOGatedOutputQueue.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/usb/IOUSBBus.h>
#include <IOKit/usb/IOUSBNub.h>
#include <IOKit/usb/IOUSBDevice.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <UserNotification/KUNCUserNotifications.h>
extern "C"
{
#include <sys/param.h>
#include <sys/mbuf.h>
}
#define DEBUG_NAME "AppleUSBCDCEEM"
#include "AppleUSBCDCEEM.h"
#define MIN_BAUD (50 << 1)
#if USE_ELG
com_apple_iokit_XTrace *gXTrace = 0;
#endif
static struct MediumTable
{
UInt32 type;
UInt32 speed;
}
mediumTable[] =
{
{kIOMediumEthernetNone, 0},
{kIOMediumEthernetAuto, 0},
{kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex, 10},
{kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex, 10},
{kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex, 100},
{kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex, 100}
};
#define super IOEthernetController
OSDefineMetaClassAndStructors(AppleUSBCDCEEM, IOEthernetController);
#if USE_ELG
IOReturn findKernelLoggerEED()
{
OSIterator *iterator = NULL;
OSDictionary *matchingDictionary = NULL;
IOReturn error = 0;
matchingDictionary = IOService::serviceMatching("com_apple_iokit_XTrace");
if (!matchingDictionary)
{
error = kIOReturnError;
IOLog(DEBUG_NAME "[findKernelLoggerEED] Couldn't create a matching dictionary.\n");
goto exit;
}
iterator = IOService::getMatchingServices(matchingDictionary);
if (!iterator)
{
error = kIOReturnError;
IOLog(DEBUG_NAME "[findKernelLoggerEED] No XTrace logger found.\n");
goto exit;
}
gXTrace = (com_apple_iokit_XTrace*)iterator->getNextObject();
if (gXTrace)
{
IOLog(DEBUG_NAME "[findKernelLoggerEED] Found XTrace logger at %p.\n", gXTrace);
}
exit:
if (error != kIOReturnSuccess)
{
gXTrace = NULL;
IOLog(DEBUG_NAME "[findKernelLoggerEED] Could not find a logger instance. Error = %X.\n", error);
}
if (matchingDictionary)
matchingDictionary->release();
if (iterator)
iterator->release();
return error;
}
#endif
IOReturn findCDCDriverEED(IOUSBDevice *myDevice, void *dataAddr, UInt8 dataInterfaceNum)
{
AppleUSBCDCEEM *me = (AppleUSBCDCEEM *)dataAddr;
AppleUSBCDC *CDCDriver = NULL;
bool driverOK = false;
OSIterator *iterator = NULL;
OSDictionary *matchingDictionary = NULL;
XTRACE(me, 0, 0, "findCDCDriverEED");
matchingDictionary = IOService::serviceMatching("AppleUSBCDC");
if (!matchingDictionary)
{
XTRACE(me, 0, 0, "findCDCDriverEED - Couldn't create a matching dictionary");
return kIOReturnError;
}
iterator = IOService::getMatchingServices(matchingDictionary);
if (!iterator)
{
XTRACE(me, 0, 0, "findCDCDriverEED - No AppleUSBCDC driver found!");
matchingDictionary->release();
return kIOReturnError;
}
#if 0
CDCDriver = (AppleUSBCDC *)iterator->getNextObject();
if (CDCDriver)
{
driverOK = CDCDriver->confirmDriver(kUSBEthernetEmulationModel, dataInterfaceNum);
}
#endif
CDCDriver = (AppleUSBCDC *)iterator->getNextObject();
while (CDCDriver)
{
XTRACE(me, 0, CDCDriver, "findCDCDriverEED - CDC driver candidate");
if (me->fDataInterface->GetDevice() == CDCDriver->getCDCDevice())
{
XTRACE(me, 0, CDCDriver, "findCDCDriverEED - Found our CDC driver");
driverOK = CDCDriver->confirmDriver(kUSBEthernetEmulationModel, dataInterfaceNum);
break;
}
CDCDriver = (AppleUSBCDC *)iterator->getNextObject();
}
matchingDictionary->release();
iterator->release();
if (!CDCDriver)
{
XTRACE(me, 0, 0, "findCDCDriverEED - CDC driver not found");
return kIOReturnError;
}
if (!driverOK)
{
XTRACE(me, kUSBEthernetEmulationModel, dataInterfaceNum, "findCDCDriverEED - Not my interface");
return kIOReturnError;
}
me->fConfigAttributes = CDCDriver->fbmAttributes;
return kIOReturnSuccess;
}
#if LOG_DATA
#define dumplen 32 // Set this to the number of bytes to dump and the rest should work out correct
#define buflen ((dumplen*2)+dumplen)+3
#define Asciistart (dumplen*2)+3
void AppleUSBCDCEEM::USBLogData(UInt8 Dir, UInt32 Count, char *buf)
{
SInt32 wlen;
UInt8 tDir = Dir;
#if USE_ELG
UInt8 *b;
UInt8 w[8];
#else
UInt32 llen, rlen;
UInt16 i, Aspnt, Hxpnt;
UInt8 wchr;
char LocBuf[buflen+1];
#endif
switch (tDir)
{
case kDataIn:
#if USE_ELG
XTRACE2(this, buf, Count, "USBLogData - Read Complete, address, size");
#else
IOLog("AppleUSBCDCEEM: USBLogData - Read Complete, address = %8x, size = %8d\n", (UInt)buf, (UInt)Count);
#endif
break;
case kDataOut:
#if USE_ELG
XTRACE2(this, buf, Count, "USBLogData - Write, address, size");
#else
IOLog("AppleUSBCDCEEM: USBLogData - Write, address = %8x, size = %8d\n", (UInt)buf, (UInt)Count);
#endif
break;
case kDataOther:
#if USE_ELG
XTRACE2(this, buf, Count, "USBLogData - Other, address, size");
#else
IOLog("AppleUSBCDCEEM: USBLogData - Other, address = %8x, size = %8d\n", (UInt)buf, (UInt)Count);
#endif
break;
case kDataNone:
tDir = kDataOther;
break;
}
#if DUMPALL
wlen = Count;
#else
if (Count > dumplen)
{
wlen = dumplen;
} else {
wlen = Count;
}
#endif
if (wlen == 0)
{
#if USE_ELG
XTRACE2(this, 0, Count, "USBLogData - No data, Count=0");
#else
IOLog("AppleUSBCDCEEM: USBLogData - No data, Count=0\n");
#endif
return;
}
#if (USE_ELG)
b = (UInt8 *)buf;
while (wlen > 0) {
bzero(w, sizeof(w)); bcopy(b, w, min(wlen, 8));
switch (tDir)
{
case kDataIn:
XTRACE2(this, (w[0] << 24 | w[1] << 16 | w[2] << 8 | w[3]), (w[4] << 24 | w[5] << 16 | w[6] << 8 | w[7]), "USBLogData - Rx buffer dump");
break;
case kDataOut:
XTRACE2(this, (w[0] << 24 | w[1] << 16 | w[2] << 8 | w[3]), (w[4] << 24 | w[5] << 16 | w[6] << 8 | w[7]), "USBLogData - Tx buffer dump");
break;
case kDataOther:
XTRACE2(this, (w[0] << 24 | w[1] << 16 | w[2] << 8 | w[3]), (w[4] << 24 | w[5] << 16 | w[6] << 8 | w[7]), "USBLogData - Misc buffer dump");
break;
}
wlen -= 8; b += 8;
}
#else
rlen = 0;
do
{
for (i=0; i<=buflen; i++)
{
LocBuf[i] = 0x20;
}
LocBuf[i] = 0x00;
if (wlen > dumplen)
{
llen = dumplen;
wlen -= dumplen;
} else {
llen = wlen;
wlen = 0;
}
Aspnt = Asciistart;
Hxpnt = 0;
for (i=1; i<=llen; i++)
{
wchr = buf[i-1];
LocBuf[Hxpnt++] = Asciify(wchr >> 4);
LocBuf[Hxpnt++] = Asciify(wchr);
if ((wchr < 0x20) || (wchr > 0x7F)) {
LocBuf[Aspnt++] = 0x2E; } else {
LocBuf[Aspnt++] = wchr;
}
}
LocBuf[(llen + Asciistart) + 1] = 0x00;
IOLog("%s", LocBuf);
IOLog("\n");
IOSleep(Sleep_Time);
rlen += llen;
buf = &buf[rlen];
} while (wlen != 0);
#endif
}
void AppleUSBCDCEEM::dumpData(char *buf, UInt32 size)
{
SInt32 curr, len, dlen;
IOLog("AppleUSBCDCEEM: dumpData - Address = %8x, size = %8d\n", (UInt)buf, (UInt)size);
dlen = 0;
len = size;
for (curr=0; curr<size; curr+=dumplen)
{
if (len > dumplen)
{
dlen = dumplen;
} else {
dlen = len;
}
IOLog("%8x ", (UInt)&buf[curr]);
USBLogData(kDataNone, dlen, &buf[curr]);
len -= dlen;
}
}
#endif
void AppleUSBCDCEEM::dataReadComplete(void *obj, void *param, IOReturn rc, UInt32 remaining)
{
AppleUSBCDCEEM *me = (AppleUSBCDCEEM*)obj;
IOReturn ior;
UInt32 poolIndx = (UInt32)param;
UInt16 EEMHeader;
UInt16 *EEMHeaderAddress = &EEMHeader;
SInt16 actualLen, dataLen, i = 0;
bool done = false;
XTRACE(me, 0, poolIndx, "dataReadComplete");
if (rc == kIOReturnSuccess)
{
dataLen = me->fMax_Block_Size - remaining;
XTRACE(me, 0, dataLen, "dataReadComplete - data length");
while (!done)
{
EEMHeaderAddress[0] = me->fPipeInBuff[poolIndx].pipeInBuffer[i];
EEMHeaderAddress[1] = me->fPipeInBuff[poolIndx].pipeInBuffer[i+1];
if (EEMHeader & bmTypeCommand)
{
me->processEEMCommand(EEMHeader, poolIndx, i+2, &actualLen);
} else {
actualLen = EEMHeader & frameLenMask;
meLogData(kDataIn, actualLen+2, &me->fPipeInBuff[poolIndx].pipeInBuffer[i]);
me->receivePacket(&me->fPipeInBuff[poolIndx].pipeInBuffer[i+2], actualLen);
}
i += actualLen;
if (i >= dataLen)
{
done = true;
}
}
} else {
XTRACE(me, 0, rc, "dataReadComplete - Read completion io err");
if (rc != kIOReturnAborted)
{
rc = me->clearPipeStall(me->fInPipe);
if (rc != kIOReturnSuccess)
{
XTRACE(me, 0, rc, "dataReadComplete - clear stall failed (trying to continue)");
}
}
}
if (rc != kIOReturnAborted)
{
ior = me->fInPipe->Read(me->fPipeInBuff[poolIndx].pipeInMDP, &me->fPipeInBuff[poolIndx].readCompletionInfo, NULL);
if (ior != kIOReturnSuccess)
{
XTRACE(me, 0, ior, "dataReadComplete - Failed to queue read");
me->fPipeInBuff[poolIndx].dead = true;
}
} else {
XTRACE(me, poolIndx, 0, "dataReadComplete - Read terminated");
me->fPipeInBuff[poolIndx].dead = true;
}
return;
}
void AppleUSBCDCEEM::dataWriteComplete(void *obj, void *param, IOReturn rc, UInt32 remaining)
{
AppleUSBCDCEEM *me = (AppleUSBCDCEEM *)obj;
mbuf_t m;
UInt32 pktLen = 0;
UInt32 numbufs = 0;
UInt32 poolIndx;
poolIndx = (UInt32)param;
if (me->fBufferPoolLock)
{
IOLockLock(me->fBufferPoolLock);
}
if (rc == kIOReturnSuccess) {
XTRACE(me, rc, poolIndx, "dataWriteComplete");
if (me->fPipeOutBuff[poolIndx].m != NULL) {
m = me->fPipeOutBuff[poolIndx].m;
while (m)
{
pktLen += mbuf_len(m);
numbufs++;
m = mbuf_next(m);
}
me->freePacket(me->fPipeOutBuff[poolIndx].m); me->fPipeOutBuff[poolIndx].m = NULL;
if ((pktLen % me->fOutPacketSize) == 0) {
XTRACE(me, rc, pktLen, "dataWriteComplete - writing zero length packet");
me->fPipeOutBuff[poolIndx].pipeOutMDP->setLength(0);
me->fPipeOutBuff[poolIndx].writeCompletionInfo.parameter = (void *)poolIndx;
me->fOutPipe->Write(me->fPipeOutBuff[poolIndx].pipeOutMDP, &me->fPipeOutBuff[poolIndx].writeCompletionInfo);
} else {
me->fPipeOutBuff[poolIndx].avail = true;
}
} else {
me->fPipeOutBuff[poolIndx].avail = true; }
} else {
XTRACE(me, rc, poolIndx, "dataWriteComplete - IO err");
if (me->fPipeOutBuff[poolIndx].m != NULL)
{
me->freePacket(me->fPipeOutBuff[poolIndx].m); me->fPipeOutBuff[poolIndx].m = NULL;
me->fPipeOutBuff[poolIndx].avail = true;
}
if (rc != kIOReturnAborted)
{
rc = me->clearPipeStall(me->fOutPipe);
if (rc != kIOReturnSuccess)
{
XTRACE(me, 0, rc, "dataWriteComplete - clear stall failed (trying to continue)");
}
}
}
if (me->fBufferPoolLock)
{
IOLockUnlock(me->fBufferPoolLock);
}
return;
}
IOService* AppleUSBCDCEEM::probe( IOService *provider, SInt32 *score )
{
IOService *res;
OSBoolean *boolObj = OSDynamicCast(OSBoolean, provider->getProperty("kDoNotClassMatchThisInterface"));
if (boolObj && boolObj->isTrue())
{
XTRACE(this, 0, 0, "probe - provider doesn't want us to match");
return NULL;
}
res = super::probe(provider, score);
return res;
}
bool AppleUSBCDCEEM::init(OSDictionary *properties)
{
UInt32 i;
#if USE_ELG
XTraceLogInfo *logInfo;
findKernelLoggerEED();
if (gXTrace)
{
gXTrace->retain(); XTRACE(this, 0, 0xbeefbeef, "Hello from start");
logInfo = gXTrace->LogGetInfo();
IOLog("AppleUSBCDCEEM: init - Log is at %x\n", (unsigned int)logInfo);
} else {
return false;
}
#endif
XTRACE(this, 0, 0, "init");
if (super::init(properties) == false)
{
XTRACE(this, 0, 0, "init - initialize super failed");
return false;
}
for (i=0; i<kMaxOutBufPool; i++)
{
fPipeOutBuff[i].pipeOutMDP = NULL;
fPipeOutBuff[i].pipeOutBuffer = NULL;
fPipeOutBuff[i].m = NULL;
fPipeOutBuff[i].avail = false;
fPipeOutBuff[i].writeCompletionInfo.target = NULL;
fPipeOutBuff[i].writeCompletionInfo.action = NULL;
fPipeOutBuff[i].writeCompletionInfo.parameter = NULL;
}
fOutPoolIndex = 0;
for (i=0; i<kMaxInBufPool; i++)
{
fPipeInBuff[i].pipeInMDP = NULL;
fPipeInBuff[i].pipeInBuffer = NULL;
fPipeInBuff[i].dead = false;
fPipeInBuff[i].readCompletionInfo.target = NULL;
fPipeInBuff[i].readCompletionInfo.action = NULL;
fPipeInBuff[i].readCompletionInfo.parameter = NULL;
}
return true;
}
bool AppleUSBCDCEEM::start(IOService *provider)
{
OSNumber *bufNumber = NULL;
UInt16 bufValue = 0;
XTRACE(this, 0, provider, "start");
if(!super::start(provider))
{
ALERT(0, 0, "start - start super failed");
return false;
}
fDataInterface = OSDynamicCast(IOUSBInterface, provider);
if(!fDataInterface)
{
ALERT(0, 0, "start - provider invalid");
return false;
}
fDataInterfaceNumber = fDataInterface->GetInterfaceNumber();
if (findCDCDriverEED(fDataInterface->GetDevice(), this, fDataInterfaceNumber) != kIOReturnSuccess)
{
XTRACE(this, 0, 0, "start - Find CDC driver failed");
super::stop(provider);
return false;
}
fBufferPoolLock = IOLockAlloc();
if (!fBufferPoolLock)
{
ALERT(0, 0, "start - Buffer pool lock allocate failed");
return false;
}
fWorkLoop = getWorkLoop();
if (!fWorkLoop)
{
ALERT(0, 0, "start - getWorkLoop failed");
return false;
}
if (!configureData())
{
ALERT(0, 0, "start - configureData failed");
return false;
}
fInBufPool = 0;
fOutBufPool = 0;
bufNumber = (OSNumber *)provider->getProperty(inputTag);
if (bufNumber)
{
bufValue = bufNumber->unsigned16BitValue();
XTRACE(this, 0, bufValue, "start - Number of input buffers override value");
if (bufValue <= kMaxInBufPool)
{
fInBufPool = bufValue;
} else {
fInBufPool = kMaxInBufPool;
}
} else {
fInBufPool = 0;
}
if (fInBufPool == 0)
{
bufNumber = NULL;
bufNumber = (OSNumber *)getProperty(inputTag);
if (bufNumber)
{
bufValue = bufNumber->unsigned16BitValue();
XTRACE(this, 0, bufValue, "start - Number of input buffers requested");
if (bufValue <= kMaxInBufPool)
{
fInBufPool = bufValue;
} else {
fInBufPool = kMaxInBufPool;
}
} else {
fInBufPool = kInBufPool;
}
}
bufNumber = NULL;
bufNumber = (OSNumber *)provider->getProperty(outputTag);
if (bufNumber)
{
bufValue = bufNumber->unsigned16BitValue();
XTRACE(this, 0, bufValue, "start - Number of output buffers override value");
if (bufValue <= kMaxInBufPool)
{
fOutBufPool = bufValue;
} else {
fOutBufPool = kMaxOutBufPool;
}
} else {
fOutBufPool = 0;
}
if (fOutBufPool == 0)
{
bufNumber = NULL;
bufNumber = (OSNumber *)getProperty(outputTag);
if (bufNumber)
{
bufValue = bufNumber->unsigned16BitValue();
XTRACE(this, 0, bufValue, "start - Number of output buffers requested");
if (bufValue <= kMaxOutBufPool)
{
fOutBufPool = bufValue;
} else {
fOutBufPool = kMaxOutBufPool;
}
} else {
fOutBufPool = kOutBufPool;
}
}
XTRACE(this, fInBufPool, fOutBufPool, "start - Buffer pools (input, output)");
if (!createNetworkInterface())
{
ALERT(0, 0, "start - createNetworkInterface failed");
return false;
}
fDataInterface->retain();
fWorkLoop->retain();
fTransmitQueue->retain();
fNetworkInterface->registerService();
XTRACE(this, 0, 0, "start - successful");
IOLog(DEBUG_NAME ": Version number - %s, Input buffers %d, Output buffers %d\n", VersionNumber, fInBufPool, fOutBufPool);
return true;
}
void AppleUSBCDCEEM::stop(IOService *provider)
{
XTRACE(this, 0, 0, "stop");
releaseResources();
if (fDataInterface)
{
fDataInterface->close(this);
fDataInterface->release();
fDataInterface = NULL;
}
if (fNetworkInterface)
{
fNetworkInterface->release();
fNetworkInterface = NULL;
}
if (fMediumDict)
{
fMediumDict->release();
fMediumDict = NULL;
}
if (fBufferPoolLock)
{
IOLockFree(fBufferPoolLock);
fBufferPoolLock = NULL;
}
if (fWorkLoop)
{
fWorkLoop->release();
fWorkLoop = NULL;
}
if (fTransmitQueue)
{
fTransmitQueue->release();
fTransmitQueue = NULL;
}
super::stop(provider);
return;
}
bool AppleUSBCDCEEM::configureData()
{
IOUSBFindInterfaceRequest req;
const IOUSBInterfaceDescriptor *altInterfaceDesc;
IOReturn ior = kIOReturnSuccess;
UInt16 numends = 0;
UInt16 alt;
XTRACE(this, 0, 0, "configureData.");
if (!fDataInterface)
{
XTRACE(this, 0, 0, "configureData - Data interface is NULL");
return false;
}
if (!fDataInterface->open(this))
{
XTRACE(this, 0, 0, "configureData - open data interface failed");
fDataInterface->release();
fDataInterface = NULL;
return false;
}
numends = fDataInterface->GetNumEndpoints();
if (numends < 2)
{
req.bInterfaceClass = kUSBDataClass;
req.bInterfaceSubClass = 0;
req.bInterfaceProtocol = 0;
req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
altInterfaceDesc = fDataInterface->FindNextAltInterface(NULL, &req);
if (!altInterfaceDesc)
{
XTRACE(this, 0, 0, "configureData - FindNextAltInterface failed");
return false;
}
while (altInterfaceDesc)
{
numends = altInterfaceDesc->bNumEndpoints;
if (numends > 1)
{
alt = altInterfaceDesc->bAlternateSetting;
XTRACE(this, numends, alt, "configureData - Data Class interface (alternate) found");
ior = fDataInterface->SetAlternateInterface(this, alt);
if (ior == kIOReturnSuccess)
{
XTRACE(this, 0, 0, "configureData - Alternate set");
break;
} else {
XTRACE(this, 0, 0, "configureData - SetAlternateInterface failed");
return false;
}
} else {
XTRACE(this, 0, altInterfaceDesc, "configureData - No endpoints this alternate");
}
altInterfaceDesc = fDataInterface->FindNextAltInterface(altInterfaceDesc, &req);
}
}
if (numends < 2)
{
XTRACE(this, 0, 0, "configureData - Could not find the correct interface");
return false;
}
return true;
}
bool AppleUSBCDCEEM::createNetworkInterface()
{
XTRACE(this, 0, 0, "createNetworkInterface");
fTransmitQueue = (IOGatedOutputQueue *)getOutputQueue();
if (!fTransmitQueue)
{
ALERT(0, 0, "createNetworkInterface - Output queue initialization failed");
return false;
}
XTRACE(this, 0, 0, "createNetworkInterface - attaching and registering interface");
if (!attachInterface((IONetworkInterface **)&fNetworkInterface, false))
{
ALERT(0, 0, "createNetworkInterface - attachInterface failed");
return false;
}
XTRACE(this, 0, 0, "createNetworkInterface - Exiting, successful");
return true;
}
IOReturn AppleUSBCDCEEM::enable(IONetworkInterface *netif)
{
IONetworkMedium *medium;
IOMediumType mediumType = kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex;
XTRACE(this, 0, netif, "enable");
IOSleep(5);
if (fNetifEnabled)
{
XTRACE(this, 0, 0, "enable - already enabled");
return kIOReturnSuccess;
}
if (!fReady)
{
if (!wakeUp())
{
XTRACE(this, 0, fReady, "enable - wakeUp failed");
return kIOReturnIOError;
}
}
fNetifEnabled = true;
fLinkStatus = 1;
medium = IONetworkMedium::getMediumWithType(fMediumDict, mediumType);
XTRACE(this, mediumType, medium, "enable - medium type and pointer");
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, medium, 10 * 1000000);
XTRACE(this, 0, 0, "enable - LinkStatus set");
fTransmitQueue->setCapacity(TRANSMIT_QUEUE_SIZE);
XTRACE(this, 0, TRANSMIT_QUEUE_SIZE, "enable - capicity set");
fTransmitQueue->start();
XTRACE(this, 0, 0, "enable - transmit queue started");
return kIOReturnSuccess;
}
IOReturn AppleUSBCDCEEM::disable(IONetworkInterface *netif)
{
XTRACE(this, 0, 0, "disable");
fTransmitQueue->stop();
fTransmitQueue->setCapacity(0);
fTransmitQueue->flush();
putToSleep();
fNetifEnabled = false;
fReady = false;
return kIOReturnSuccess;
}
IOReturn AppleUSBCDCEEM::setWakeOnMagicPacket(bool active)
{
IOUSBDevRequest devreq;
IOReturn ior = kIOReturnSuccess;
XTRACE(this, 0, active, "setWakeOnMagicPacket");
fWOL = active;
if (fConfigAttributes & kUSBAtrRemoteWakeup)
{
devreq.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBDevice);
if (active)
{
devreq.bRequest = kUSBRqSetFeature;
} else {
devreq.bRequest = kUSBRqClearFeature;
}
devreq.wValue = kUSBFeatureDeviceRemoteWakeup;
devreq.wIndex = 0;
devreq.wLength = 0;
devreq.pData = 0;
ior = fDataInterface->GetDevice()->DeviceRequest(&devreq);
if (ior == kIOReturnSuccess)
{
XTRACE(this, 0, ior, "setWakeOnMagicPacket - Set/Clear remote wake up feature successful");
} else {
XTRACE(this, 0, ior, "setWakeOnMagicPacket - Set/Clear remote wake up feature failed");
}
} else {
XTRACE(this, 0, 0, "setWakeOnMagicPacket - Remote wake up not supported");
}
return kIOReturnSuccess;
}
IOReturn AppleUSBCDCEEM::getPacketFilters(const OSSymbol *group, UInt32 *filters) const
{
IOReturn rtn = kIOReturnSuccess;
XTRACE(this, group, filters, "getPacketFilters");
if (group == gIOEthernetWakeOnLANFilterGroup)
{
if (fConfigAttributes & kUSBAtrRemoteWakeup)
{
*filters = kIOEthernetWakeOnMagicPacket;
} else {
*filters = 0;
}
} else {
if (group == gIONetworkFilterGroup)
{
*filters = kIOPacketFilterUnicast | kIOPacketFilterBroadcast;
} else {
rtn = super::getPacketFilters(group, filters);
}
}
if (rtn != kIOReturnSuccess)
{
XTRACE(this, 0, rtn, "getPacketFilters - failed");
}
return rtn;
}
IOReturn AppleUSBCDCEEM::selectMedium(const IONetworkMedium *medium)
{
XTRACE(this, 0, 0, "selectMedium");
setSelectedMedium(medium);
return kIOReturnSuccess;
}
IOReturn AppleUSBCDCEEM::getHardwareAddress(IOEthernetAddress *ea)
{
UInt32 i;
OSNumber *location;
UInt32 locVal;
UInt8 *rlocVal;
XTRACE(this, 0, 0, "getHardwareAddress");
location = (OSNumber *)fDataInterface->GetDevice()->getProperty(kUSBDevicePropertyLocationID);
if (location)
{
locVal = location->unsigned32BitValue();
rlocVal = (UInt8*)&locVal;
ea->bytes[0] = 0x00;
ea->bytes[1] = 0x03;
for (i=0; i<4; i++)
{
ea->bytes[i+2] = rlocVal[i];
}
} else {
XTRACE(this, 0, 0, "getHardwareAddress - Get location failed");
return kIOReturnError;
}
return kIOReturnSuccess;
}
const OSString* AppleUSBCDCEEM::newVendorString() const
{
XTRACE(this, 0, 0, "newVendorString");
return OSString::withCString((const char *)defaultName);
}
const OSString* AppleUSBCDCEEM::newModelString() const
{
XTRACE(this, 0, 0, "newModelString");
return OSString::withCString("USB");
}
const OSString* AppleUSBCDCEEM::newRevisionString() const
{
XTRACE(this, 0, 0, "newRevisionString");
return OSString::withCString("");
}
IOReturn AppleUSBCDCEEM::setMulticastMode(bool active)
{
XTRACE(this, 0, active, "setMulticastMode");
return kIOReturnIOError;
}
IOReturn AppleUSBCDCEEM::setMulticastList(IOEthernetAddress *addrs, UInt32 count)
{
XTRACE(this, addrs, count, "setMulticastList");
return kIOReturnIOError;
}
IOReturn AppleUSBCDCEEM::setPromiscuousMode(bool active)
{
XTRACE(this, 0, active, "setPromiscuousMode");
return kIOReturnIOError;
}
IOOutputQueue* AppleUSBCDCEEM::createOutputQueue()
{
XTRACE(this, 0, 0, "createOutputQueue");
return IOBasicOutputQueue::withTarget(this, TRANSMIT_QUEUE_SIZE);
}
UInt32 AppleUSBCDCEEM::outputPacket(mbuf_t pkt, void *param)
{
UInt32 ior = kIOReturnSuccess;
XTRACE(this, pkt, 0, "outputPacket");
if (!fLinkStatus)
{
XTRACE(this, pkt, fLinkStatus, "outputPacket - link is down");
fpNetStats->outputErrors++;
freePacket(pkt);
return kIOReturnOutputDropped;
}
ior = USBTransmitPacket(pkt);
if (ior != kIOReturnSuccess)
{
return kIOReturnOutputStall;
}
return kIOReturnOutputSuccess;
}
bool AppleUSBCDCEEM::configureInterface(IONetworkInterface *netif)
{
IONetworkData *nd;
XTRACE(this, IOThreadSelf(), netif, "configureInterface");
if (super::configureInterface(netif) == false)
{
ALERT(0, 0, "configureInterface - super failed");
return false;
}
nd = netif->getNetworkData(kIONetworkStatsKey);
if (!nd || !(fpNetStats = (IONetworkStats *)nd->getBuffer()))
{
ALERT(0, 0, "configureInterface - Invalid network statistics");
return false;
}
nd = netif->getParameter(kIOEthernetStatsKey);
if (!nd || !(fpEtherStats = (IOEthernetStats*)nd->getBuffer()))
{
ALERT(0, 0, "configureInterface - Invalid ethernet statistics\n");
return false;
}
return true;
}
bool AppleUSBCDCEEM::wakeUp()
{
IOReturn rtn = kIOReturnSuccess;
UInt32 i;
bool readOK = false;
XTRACE(this, 0, 0, "wakeUp");
fReady = false;
setLinkStatus(0, 0);
if (!allocateResources())
{
ALERT(0, 0, "wakeUp - allocateResources failed");
return false;
}
for (i=0; i<fInBufPool; i++)
{
if (fPipeInBuff[i].pipeInMDP)
{
fPipeInBuff[i].readCompletionInfo.parameter = (void *)i;
rtn = fInPipe->Read(fPipeInBuff[i].pipeInMDP, &fPipeInBuff[i].readCompletionInfo, NULL);
if (rtn == kIOReturnSuccess)
{
readOK = true;
} else {
XTRACE(this, i, rtn, "wakeUp - Read failed");
}
}
}
if (!readOK)
{
ALERT(0, 0, "wakeUp - Starting the input pipe read(s) failed");
return false;
} else {
if (!fMediumDict)
{
if (!createMediumTables())
{
ALERT(0, 0, "wakeUp - createMediumTables failed");
return false;
}
}
fReady = true;
}
return true;
}
void AppleUSBCDCEEM::putToSleep()
{
XTRACE(this, 0, 0, "putToSleep");
fReady = false;
setLinkStatus(0, 0);
}
bool AppleUSBCDCEEM::createMediumTables()
{
IONetworkMedium *medium;
UInt64 maxSpeed;
UInt32 i;
XTRACE(this, 0, 0, "createMediumTables");
maxSpeed = 100;
fMediumDict = OSDictionary::withCapacity(sizeof(mediumTable) / sizeof(mediumTable[0]));
if (fMediumDict == 0)
{
XTRACE(this, 0, 0, "createMediumTables - create dict. failed");
return false;
}
for (i = 0; i < sizeof(mediumTable) / sizeof(mediumTable[0]); i++)
{
medium = IONetworkMedium::medium(mediumTable[i].type, mediumTable[i].speed);
if (medium && (medium->getSpeed() <= maxSpeed))
{
IONetworkMedium::addMedium(fMediumDict, medium);
medium->release();
}
}
if (publishMediumDictionary(fMediumDict) != true)
{
XTRACE(this, 0, 0, "createMediumTables - publish dict. failed");
return false;
}
medium = IONetworkMedium::getMediumWithType(fMediumDict, kIOMediumEthernetAuto);
setCurrentMedium(medium);
return true;
}
bool AppleUSBCDCEEM::allocateResources()
{
IOUSBFindEndpointRequest epReq;
UInt32 i;
XTRACE(this, 0, 0, "allocateResources.");
epReq.type = kUSBBulk;
epReq.direction = kUSBIn;
epReq.maxPacketSize = 0;
epReq.interval = 0;
fInPipe = fDataInterface->FindNextPipe(0, &epReq);
if (!fInPipe)
{
XTRACE(this, 0, 0, "allocateResources - no bulk input pipe.");
return false;
}
XTRACE(this, epReq.maxPacketSize << 16 |epReq.interval, fInPipe, "allocateResources - bulk input pipe.");
epReq.direction = kUSBOut;
fOutPipe = fDataInterface->FindNextPipe(0, &epReq);
if (!fOutPipe)
{
XTRACE(this, 0, 0, "allocateResources - no bulk output pipe.");
return false;
}
fOutPacketSize = epReq.maxPacketSize;
XTRACE(this, epReq.maxPacketSize << 16 |epReq.interval, fOutPipe, "allocateResources - bulk output pipe.");
for (i=0; i<fInBufPool; i++)
{
fPipeInBuff[i].pipeInMDP = IOBufferMemoryDescriptor::withCapacity(fMax_Block_Size, kIODirectionIn);
if (!fPipeInBuff[i].pipeInMDP)
{
XTRACE(this, 0, i, "allocateResources - Allocate input descriptor failed");
return false;
}
fPipeInBuff[i].pipeInMDP->setLength(fMax_Block_Size);
fPipeInBuff[i].pipeInBuffer = (UInt8*)fPipeInBuff[i].pipeInMDP->getBytesNoCopy();
XTRACE(this, fPipeInBuff[i].pipeInMDP, fPipeInBuff[i].pipeInBuffer, "allocateResources - input buffer");
fPipeInBuff[i].dead = false;
fPipeInBuff[i].readCompletionInfo.target = this;
fPipeInBuff[i].readCompletionInfo.action = dataReadComplete;
fPipeInBuff[i].readCompletionInfo.parameter = NULL;
}
for (i=0; i<fOutBufPool; i++)
{
fPipeOutBuff[i].pipeOutMDP = IOBufferMemoryDescriptor::withCapacity(fMax_Block_Size, kIODirectionOut);
if (!fPipeOutBuff[i].pipeOutMDP)
{
XTRACE(this, 0, i, "allocateResources - Allocate output descriptor failed");
return false;
}
fPipeOutBuff[i].pipeOutMDP->setLength(fMax_Block_Size);
fPipeOutBuff[i].pipeOutBuffer = (UInt8*)fPipeOutBuff[i].pipeOutMDP->getBytesNoCopy();
XTRACE(this, fPipeOutBuff[i].pipeOutMDP, fPipeOutBuff[i].pipeOutBuffer, "allocateResources - output buffer");
fPipeOutBuff[i].avail = true;
fPipeOutBuff[i].writeCompletionInfo.target = this;
fPipeOutBuff[i].writeCompletionInfo.action = dataWriteComplete;
fPipeOutBuff[i].writeCompletionInfo.parameter = NULL; }
return true;
}
void AppleUSBCDCEEM::releaseResources()
{
UInt32 i;
XTRACE(this, 0, 0, "releaseResources");
for (i=0; i<fOutBufPool; i++)
{
if (fPipeOutBuff[i].pipeOutMDP)
{
fPipeOutBuff[i].pipeOutMDP->release();
fPipeOutBuff[i].pipeOutMDP = NULL;
fPipeOutBuff[i].avail = false;
fPipeOutBuff[i].writeCompletionInfo.target = NULL;
fPipeOutBuff[i].writeCompletionInfo.action = NULL;
fPipeOutBuff[i].writeCompletionInfo.parameter = NULL;
}
}
fOutPoolIndex = 0;
for (i=0; i<fInBufPool; i++)
{
if (fPipeInBuff[i].pipeInMDP)
{
fPipeInBuff[i].pipeInMDP->release();
fPipeInBuff[i].pipeInMDP = NULL;
fPipeInBuff[i].dead = false;
fPipeInBuff[i].readCompletionInfo.target = NULL;
fPipeInBuff[i].readCompletionInfo.action = NULL;
fPipeInBuff[i].readCompletionInfo.parameter = NULL;
}
}
}
bool AppleUSBCDCEEM::getOutputBuffer(UInt32 *bufIndx)
{
bool gotBuffer = false;
UInt32 indx;
SInt16 deadMan = 0;
XTRACE(this, 0, 0, "getOutputBuffer");
if (fBufferPoolLock)
{
IOLockLock(fBufferPoolLock);
} else {
return false;
}
while (!gotBuffer)
{
indx = fOutPoolIndex;
if (!fPipeOutBuff[indx].avail)
{
for (indx=0; indx<fOutBufPool; indx++)
{
if (fPipeOutBuff[indx].avail)
{
fOutPoolIndex = indx;
gotBuffer = true;
break;
}
}
} else {
gotBuffer = true;
}
if (gotBuffer)
{
fPipeOutBuff[indx].avail = false;
fOutPoolIndex++;
if (fOutPoolIndex >= fOutBufPool)
{
fOutPoolIndex = 0;
}
break;
}
IOLockUnlock(fBufferPoolLock);
IOSleep(1); IOLockLock(fBufferPoolLock);
if (deadMan++ > 10)
{
ALERT(0, 0, "getOutputBuffer - No buffers available, deadman expired");
break;
}
}
IOLockUnlock(fBufferPoolLock);
*bufIndx = indx;
return gotBuffer;
}
IOReturn AppleUSBCDCEEM::USBTransmitPacket(mbuf_t packet)
{
UInt32 numbufs = 0; mbuf_t m; UInt32 total_pkt_length = 0;
UInt32 rTotal = 0;
IOReturn ior = kIOReturnSuccess;
UInt32 indx;
XTRACE(this, 0, packet, "USBTransmitPacket");
m = packet;
while (m)
{
if (mbuf_len(m) != 0)
{
total_pkt_length += mbuf_len(m);
numbufs++;
}
m = mbuf_next(m);
}
XTRACE(this, total_pkt_length, numbufs, "USBTransmitPacket - Total packet length and Number of mbufs");
if (total_pkt_length > fMax_Block_Size)
{
XTRACE(this, 0, 0, "USBTransmitPacket - Bad packet size"); fpNetStats->outputErrors++;
return kIOReturnInternalError;
}
if (!getOutputBuffer(&indx))
{
ALERT(fOutBufPool, fOutPoolIndex, "USBTransmitPacket - Output buffer unavailable");
fpNetStats->outputErrors++;
return kIOReturnOutputDropped;
}
m = packet; rTotal = 0; do
{
if (mbuf_len(m) == 0) continue;
bcopy(mbuf_data(m), &fPipeOutBuff[indx].pipeOutBuffer[rTotal], mbuf_len(m));
rTotal += mbuf_len(m);
} while ((m = mbuf_next(m)) != 0);
LogData(kDataOut, rTotal, fPipeOutBuff[indx].pipeOutBuffer);
fPipeOutBuff[indx].m = packet;
fPipeOutBuff[indx].writeCompletionInfo.parameter = (void *)indx;
fPipeOutBuff[indx].pipeOutMDP->setLength(rTotal);
ior = fOutPipe->Write(fPipeOutBuff[indx].pipeOutMDP, &fPipeOutBuff[indx].writeCompletionInfo);
if (ior != kIOReturnSuccess)
{
XTRACE(this, 0, ior, "USBTransmitPacket - Write failed");
if (ior == kIOUSBPipeStalled)
{
fOutPipe->Reset();
ior = fOutPipe->Write(fPipeOutBuff[indx].pipeOutMDP, &fPipeOutBuff[indx].writeCompletionInfo);
if (ior != kIOReturnSuccess)
{
XTRACE(this, 0, ior, "USBTransmitPacket - Write really failed");
fpNetStats->outputErrors++;
return ior;
}
}
}
fpNetStats->outputPackets++;
return ior;
}
IOReturn AppleUSBCDCEEM::USBSendCommand(UInt16 command, UInt16 length, UInt8 *anyData)
{
IOReturn ior = kIOReturnSuccess;
UInt32 indx;
bool gotBuffer = false;
UInt16 EEMHeader = bmTypeCommand;
XTRACE(this, command, length, "USBSendCommand");
if ((length > 0) && (anyData == NULL))
{
return kIOReturnBadArgument;
}
if (fBufferPoolLock)
{
IOLockLock(fBufferPoolLock);
}
indx = fOutPoolIndex;
if (!fPipeOutBuff[indx].avail)
{
for (indx=0; indx<fOutBufPool; indx++)
{
if (fPipeOutBuff[indx].avail)
{
fOutPoolIndex = indx;
gotBuffer = true;
break;
}
}
if (!gotBuffer)
{
XTRACE(this, fOutBufPool, fOutPoolIndex, "USBSendCommand - Output buffer unavailable");
fpNetStats->outputErrors++;
if (fBufferPoolLock)
{
IOLockUnlock(fBufferPoolLock);
}
return kIOReturnInternalError;
}
}
fOutPoolIndex++;
if (fOutPoolIndex >= fOutBufPool)
{
fOutPoolIndex = 0;
}
if (fBufferPoolLock)
{
IOLockUnlock(fBufferPoolLock);
}
EEMHeader |= command;
EEMHeader |= length;
bcopy(&EEMHeader, fPipeOutBuff[indx].pipeOutBuffer, sizeof(EEMHeader));
if (length > 0)
{
bcopy(anyData, &fPipeOutBuff[indx].pipeOutBuffer[2], length);
}
LogData(kDataOut, length+2, fPipeOutBuff[indx].pipeOutBuffer);
fPipeOutBuff[indx].m = NULL;
fPipeOutBuff[indx].writeCompletionInfo.parameter = (void *)indx;
fPipeOutBuff[indx].pipeOutMDP->setLength(length+2);
ior = fOutPipe->Write(fPipeOutBuff[indx].pipeOutMDP, &fPipeOutBuff[indx].writeCompletionInfo);
if (ior != kIOReturnSuccess)
{
XTRACE(this, 0, ior, "USBSendCommand - Write failed");
if (ior == kIOUSBPipeStalled)
{
fOutPipe->Reset();
ior = fOutPipe->Write(fPipeOutBuff[indx].pipeOutMDP, &fPipeOutBuff[indx].writeCompletionInfo);
if (ior != kIOReturnSuccess)
{
XTRACE(this, 0, ior, "USBSendCommand - Write really failed");
fpNetStats->outputErrors++;
return ior;
}
}
}
fpNetStats->outputPackets++;
return ior;
}
IOReturn AppleUSBCDCEEM::clearPipeStall(IOUSBPipe *thePipe)
{
IOReturn rtn = kIOReturnSuccess;
XTRACE(this, 0, thePipe, "clearPipeStall");
rtn = thePipe->GetPipeStatus();
if (rtn == kIOUSBPipeStalled)
{
rtn = thePipe->ClearPipeStall(true);
if (rtn == kIOReturnSuccess)
{
XTRACE(this, 0, 0, "clearPipeStall - Successful");
} else {
XTRACE(this, 0, rtn, "clearPipeStall - Failed");
}
} else {
XTRACE(this, 0, 0, "clearPipeStall - Pipe not stalled");
}
return rtn;
}
void AppleUSBCDCEEM::receivePacket(UInt8 *packet, UInt32 size)
{
mbuf_t m;
UInt32 submit;
XTRACE(this, 0, size, "receivePacket");
if (size > fMax_Block_Size)
{
XTRACE(this, 0, 0, "receivePacket - Packet size error, packet dropped");
fpNetStats->inputErrors++;
return;
}
m = allocatePacket(size);
if (m)
{
bcopy(packet, mbuf_data(m), size);
submit = fNetworkInterface->inputPacket(m, size);
XTRACE(this, 0, submit, "receivePacket - Packets submitted");
fpNetStats->inputPackets++;
} else {
XTRACE(this, 0, 0, "receivePacket - Buffer allocation failed, packet dropped");
fpNetStats->inputErrors++;
}
}
void AppleUSBCDCEEM::processEEMCommand(UInt16 EEMHeader, UInt32 poolIndx, SInt16 dataIndx, SInt16 *len)
{
IOReturn rtn = kIOReturnSuccess;
UInt16 EEMCommand;
UInt16 param;
UInt8 *buff = NULL;
XTRACE(this, EEMHeader, poolIndx, "processEEMCommand");
EEMCommand = EEMHeader & bmEEMCmdMask;
param = EEMHeader & bmEEMCmdParamMask;
switch (EEMCommand)
{
case EEMEcho:
XTRACE(this, EEMCommand, param, "processEEMCommand - Echo");
if (param != 0)
{
buff = &fPipeInBuff[poolIndx].pipeInBuffer[2];
}
rtn = USBSendCommand(EEMEchoResponse, param, buff);
if (rtn != kIOReturnSuccess)
{
XTRACE(this, 0, rtn, "processEEMCommand - Failed to send echo response");
}
*len = param + 2;
break;
case EEMEchoResponse:
XTRACE(this, EEMCommand, param, "processEEMCommand - Echo Response");
*len = param + 2;
break;
case EEMSuspendHint:
XTRACE(this, EEMCommand, param, "processEEMCommand - Suspend Hint");
*len = 2;
break;
case EEMResponseHint:
XTRACE(this, EEMCommand, param, "processEEMCommand - Response Hint");
*len = 2;
break;
case EEMResponseCompleteHint:
XTRACE(this, EEMCommand, param, "processEEMCommand - Response Hint Complete");
*len = 2;
break;
case EEMTickle:
XTRACE(this, EEMCommand, param, "processEEMCommand - Tickle");
*len = 2;
break;
default:
XTRACE(this, EEMCommand, param, "processEEMCommand - unknown command");
*len = 2;
break;
}
}
IOReturn AppleUSBCDCEEM::message(UInt32 type, IOService *provider, void *argument)
{
UInt16 i;
IOReturn ior;
XTRACE(this, 0, type, "message");
switch (type)
{
case kIOMessageServiceIsTerminated:
XTRACE(this, fReady, type, "message - kIOMessageServiceIsTerminated");
if (fReady)
{
if (!fTerminate) {
KUNCUserNotificationDisplayNotice(
0, 0, "", "", "/System/Library/Extensions/IOUSBFamily.kext/Contents/PlugIns/AppleUSBCDCEEM.kext", "Unplug Header", "Unplug Notice", "OK");
}
}
releaseResources();
if (fDataInterface)
{
fDataInterface->close(this);
fDataInterface->release();
fDataInterface = NULL;
}
fTerminate = true; fLinkStatus = 0; return kIOReturnSuccess;
case kIOMessageServiceIsSuspended:
XTRACE(this, 0, type, "message - kIOMessageServiceIsSuspended");
break;
case kIOMessageServiceIsResumed:
XTRACE(this, 0, type, "message - kIOMessageServiceIsResumed");
break;
case kIOMessageServiceIsRequestingClose:
XTRACE(this, 0, type, "message - kIOMessageServiceIsRequestingClose");
break;
case kIOMessageServiceWasClosed:
XTRACE(this, 0, type, "message - kIOMessageServiceWasClosed");
break;
case kIOMessageServiceBusyStateChange:
XTRACE(this, 0, type, "message - kIOMessageServiceBusyStateChange");
break;
case kIOUSBMessagePortHasBeenResumed:
XTRACE(this, 0, type, "message - kIOUSBMessagePortHasBeenResumed");
for (i=0; i<fInBufPool; i++)
{
if (fPipeInBuff[i].dead) {
ior = fInPipe->Read(fPipeInBuff[i].pipeInMDP, &fPipeInBuff[i].readCompletionInfo, NULL);
if (ior != kIOReturnSuccess)
{
XTRACE(this, 0, ior, "message - Read io error");
} else {
fPipeInBuff[i].dead = false;
}
}
}
break;
case kIOUSBMessageHubResumePort:
XTRACE(this, 0, type, "message - kIOUSBMessageHubResumePort");
break;
default:
XTRACE(this, 0, type, "message - unknown message");
break;
}
return kIOReturnUnsupported;
}