#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSDictionary.h>
#include <libkern/c++/OSNumber.h>
#include <libkern/c++/OSData.h>
#include <IOKit/network/IONetworkData.h>
#define super OSObject
OSDefineMetaClassAndStructors( IONetworkData, OSObject )
OSMetaClassDefineReservedUnused( IONetworkData, 0);
OSMetaClassDefineReservedUnused( IONetworkData, 1);
OSMetaClassDefineReservedUnused( IONetworkData, 2);
OSMetaClassDefineReservedUnused( IONetworkData, 3);
#define TAP_IS_VALID (_tapAction)
#define RELEASE(x) do { if (x) { (x)->release(); (x) = 0; } } while (0)
static IOLock * gIONDLock = 0;
#define LOCK IOTakeLock(gIONDLock)
#define UNLOCK IOUnlock(gIONDLock)
static const OSSymbol * gIONDDataKey;
static const OSSymbol * gIONDAccessKey;
static const OSSymbol * gIONDSizeKey;
class IONetworkDataGlobals
{
public:
IONetworkDataGlobals();
~IONetworkDataGlobals();
inline bool isValid() const;
};
static IONetworkDataGlobals gIONetworkDataGlobals;
IONetworkDataGlobals::IONetworkDataGlobals()
{
gIONDLock = IOLockAlloc();
if ( gIONDLock )
IOLockInitWithState( gIONDLock, kIOLockStateUnlocked );
gIONDDataKey = OSSymbol::withCStringNoCopy( kIONetworkDataBytes );
gIONDAccessKey = OSSymbol::withCStringNoCopy( kIONetworkDataAccessTypes );
gIONDSizeKey = OSSymbol::withCStringNoCopy( kIONetworkDataSize );
}
IONetworkDataGlobals::~IONetworkDataGlobals()
{
RELEASE( gIONDDataKey );
RELEASE( gIONDAccessKey );
RELEASE( gIONDSizeKey );
if ( gIONDLock )
{
IOLockFree( gIONDLock );
gIONDLock = 0;
}
}
bool IONetworkDataGlobals::isValid() const
{
return ( gIONDLock && gIONDDataKey && gIONDAccessKey && gIONDSizeKey );
}
bool
IONetworkData::init(const char * name,
UInt32 bufferType,
UInt32 bufferSize,
void * extBuffer,
UInt32 accessTypes,
void * target,
Action action,
void * param)
{
if ( super::init() == false )
return false;
if ( gIONetworkDataGlobals.isValid() == false )
return false;
if ((bufferType == kIONetworkDataBufferTypeInternal) ||
(bufferType == kIONetworkDataBufferTypeExternal))
{
_buffer = (bufferType == kIONetworkDataBufferTypeInternal) ?
(void *) IOMalloc(bufferSize) : extBuffer;
if (_buffer == 0)
return false;
if (bufferType == kIONetworkDataBufferTypeInternal)
bzero(_buffer, bufferSize);
}
_bufType = bufferType;
_access = accessTypes;
_tapTarget = target;
_tapAction = action;
_tapParam = param;
_size = bufferSize;
if ((_key = OSSymbol::withCString(name)) == 0)
return false;
return true;
}
IONetworkData *
IONetworkData::withInternalBuffer(
const char * name,
UInt32 bufferSize,
UInt32 accessTypes,
void * target,
Action action,
void * param)
{
IONetworkData * aData = new IONetworkData;
if (aData && !aData->init(name,
kIONetworkDataBufferTypeInternal,
bufferSize,
0,
accessTypes,
target,
action,
param))
{
aData->release();
aData = 0;
}
return aData;
}
IONetworkData *
IONetworkData::withExternalBuffer(
const char * name,
UInt32 bufferSize,
void * buffer,
UInt32 accessTypes,
void * target,
Action action,
void * param)
{
IONetworkData * aData = new IONetworkData;
if (aData && !aData->init(name,
kIONetworkDataBufferTypeExternal,
bufferSize,
buffer,
accessTypes,
target,
action,
param))
{
aData->release();
aData = 0;
}
return aData;
}
IONetworkData *
IONetworkData::withNoBuffer(const char * name,
UInt32 bufferSize,
UInt32 accessTypes,
void * target,
Action action,
void * param)
{
IONetworkData * aData = new IONetworkData;
if (aData && !aData->init(name,
kIONetworkDataBufferTypeNone,
bufferSize,
0,
accessTypes,
target,
action,
param))
{
aData->release();
aData = 0;
}
return aData;
}
void IONetworkData::free()
{
if (_key)
_key->release();
if (_buffer && (_bufType == kIONetworkDataBufferTypeInternal))
IOFree( _buffer, _size);
super::free();
}
UInt32 IONetworkData::getBufferType() const
{
return _bufType;
}
#define kIONetworkDataImmutableAccessTypes 0
void IONetworkData::setAccessTypes(UInt32 types)
{
LOCK;
_access = (_access & kIONetworkDataImmutableAccessTypes) |
(types & ~kIONetworkDataImmutableAccessTypes);
UNLOCK;
}
void IONetworkData::setNotificationTarget(void * target,
Action action,
void * param)
{
LOCK;
_tapTarget = target;
_tapAction = action;
_tapParam = param;
UNLOCK;
}
UInt32 IONetworkData::getAccessTypes() const
{
return _access;
}
void * IONetworkData::getNotificationTarget() const
{
return _tapTarget;
}
IONetworkData::Action IONetworkData::getNotificationAction() const
{
return _tapAction;
}
void * IONetworkData::getNotificationParameter() const
{
return _tapParam;
}
const OSSymbol * IONetworkData::getKey() const
{
return _key;
}
UInt32 IONetworkData::getSize() const
{
return _size;
}
bool IONetworkData::writeBytes(const void * srcBuffer,
UInt32 srcBufferSize,
UInt32 writeOffset)
{
if ( _buffer == 0 ) return false;
if ( srcBufferSize &&
(writeOffset < _size) &&
((writeOffset + srcBufferSize) <= _size) )
{
bcopy(srcBuffer, (char *) _buffer + writeOffset, srcBufferSize);
return true;
}
return false;
}
const void * IONetworkData::getBuffer() const
{
return (_buffer) ? _buffer : 0;
}
bool IONetworkData::readBytes(void * dstBuffer,
UInt32 * dstBufferSize,
UInt32 readOffset) const
{
if ( _buffer == 0 ) return false;
if ( *dstBufferSize && (readOffset < _size) )
{
UInt32 bytesCopied = min((_size - readOffset), *dstBufferSize);
bcopy((char *) _buffer + readOffset, dstBuffer, bytesCopied);
*dstBufferSize = bytesCopied;
return true;
}
return false;
}
bool IONetworkData::clearBuffer()
{
if ( _buffer )
{
bzero((void *) _buffer, _size);
return true;
}
return false;
}
IOReturn IONetworkData::reset()
{
IOReturn ret = kIOReturnUnsupported;
LOCK;
do {
if ( (_access & kIONetworkDataAccessTypeReset) == 0 )
{
ret = kIOReturnNotWritable;
break;
}
if ( clearBuffer() )
{
ret = kIOReturnSuccess;
}
if ( TAP_IS_VALID )
{
ret = (*_tapAction)(_tapTarget, _tapParam,
this,
(UInt32) kIONetworkDataAccessTypeReset,
0, 0, 0);
}
}
while (0);
UNLOCK;
return ret;
}
IOReturn IONetworkData::read(void * dstBuffer,
UInt32 * dstBufferSize,
UInt32 readOffset)
{
IOReturn ret = kIOReturnUnsupported;
LOCK;
do {
if ( !dstBuffer || !dstBufferSize )
{
ret = kIOReturnBadArgument;
break;
}
if ( (_access & kIONetworkDataAccessTypeRead) == 0 )
{
ret = kIOReturnNotReadable;
break;
}
if ( TAP_IS_VALID )
{
ret = (*_tapAction)(_tapTarget, _tapParam,
this,
(UInt32) kIONetworkDataAccessTypeRead,
dstBuffer,
dstBufferSize,
readOffset);
if (ret != kIOReturnSuccess)
break;
}
if ( _buffer )
{
ret = readBytes(dstBuffer, dstBufferSize, readOffset) ?
kIOReturnSuccess : kIOReturnBadArgument;
}
}
while (0);
UNLOCK;
return ret;
}
IOReturn IONetworkData::write(void * srcBuffer,
UInt32 srcBufferSize,
UInt32 writeOffset)
{
IOReturn ret = kIOReturnUnsupported;
LOCK;
do {
if ( srcBuffer == 0 )
{
ret = kIOReturnBadArgument;
break;
}
if ( (_access & kIONetworkDataAccessTypeWrite) == 0 )
{
ret = kIOReturnNotWritable;
break;
}
if ( _buffer &&
(writeBytes(srcBuffer, srcBufferSize, writeOffset) == false) )
{
ret = kIOReturnBadArgument;
break;
}
if ( TAP_IS_VALID )
{
ret = (*_tapAction)(_tapTarget, _tapParam,
this,
(UInt32) kIONetworkDataAccessTypeWrite,
srcBuffer,
&srcBufferSize,
writeOffset);
}
else
ret = kIOReturnSuccess;
}
while (0);
UNLOCK;
return ret;
}
bool IONetworkData::serialize(OSSerialize * s) const
{
bool ok;
OSDictionary * dictToSerialize;
OSData * dataEntry;
OSNumber * numberEntry;
dictToSerialize = OSDictionary::withCapacity(3);
if (!dictToSerialize)
return false;
numberEntry = OSNumber::withNumber(_access, sizeof(_access) * 8);
if (numberEntry) {
dictToSerialize->setObject(gIONDAccessKey, numberEntry);
numberEntry->release();
}
numberEntry = OSNumber::withNumber(_size, sizeof(_size) * 8);
if (numberEntry) {
dictToSerialize->setObject(gIONDSizeKey, numberEntry);
numberEntry->release();
}
LOCK;
do {
if ((_access & kIONetworkDataAccessTypeSerialize) == 0)
break;
if (_buffer == 0)
break;
if (TAP_IS_VALID &&
((*_tapAction)(_tapTarget, _tapParam,
(IONetworkData *) this,
kIONetworkDataAccessTypeSerialize,
0, 0, 0) != kIOReturnSuccess))
{
break;
}
dataEntry = OSData::withBytesNoCopy(_buffer, _size);
if (dataEntry) {
dictToSerialize->setObject(gIONDDataKey, dataEntry);
dataEntry->release();
}
}
while (0);
ok = dictToSerialize->serialize(s);
dictToSerialize->release();
UNLOCK;
return ok;
}