Source to iokit/Families/IONetworking/IONetworkData.cpp
/*
* Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* IONetworkData.cpp
*/
#include <libkern/c++/OSDictionary.h>
#include <libkern/c++/OSNumber.h>
#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/network/IONetworkData.h>
#define super OSData
OSDefineMetaClassAndStructors( IONetworkData, OSData )
#define TAP_IS_VALID (_tapTarget && _tapAction)
// All access method are serialized by a single global lock,
// shared among all IONetworkData instances.
//
static IOLock * gIONDLock = 0;
#define LOCK IOTakeLock(gIONDLock)
#define UNLOCK IOUnlock(gIONDLock)
static const OSSymbol * gIONDDataKey;
static const OSSymbol * gIONDAccessKey;
static const OSSymbol * gIONDCapacityKey;
//---------------------------------------------------------------------------
// IONetworkData class initializer.
void IONetworkData::initialize()
{
// Allocates the global data lock.
//
gIONDLock = IOLockAlloc();
assert(gIONDLock);
IOLockInitWithState(gIONDLock, kIOLockStateUnlocked);
gIONDDataKey = OSSymbol::withCStringNoCopy("Data");
gIONDAccessKey = OSSymbol::withCStringNoCopy("Access Flags");
gIONDCapacityKey = OSSymbol::withCStringNoCopy("Capacity");
assert(gIONDDataKey && gIONDAccessKey && gIONDCapacityKey);
}
//---------------------------------------------------------------------------
// Initialize an IONetworkData instance. A target/action may be
// registered to receive a notification when the read(), write(),
// reset(), or serialize() methods are called.
//
// name: A name assigned to this object.
//
// buffer: Pointer to an external data buffer. If 0,
// then an internal buffer shall be allocated.
//
// capacity: Capacity of the data buffer.
//
// accessFlags: Access flags.
// Can be later modified by calling setAccessFlags().
//
// target: Notification target. The target will be notified
// when IONetworkData receives a call to one of its
// access methods. If 0, then notification is disabled.
//
// action: Notification action. If 0, then notification is
// disabled.
//
// arg: An argument to passed to the notification action.
//
// Returns true if initialized successfully, false otherwise.
bool
IONetworkData::initWithName(const char * name,
UInt32 inCapacity,
void * buffer = 0,
UInt32 accessFlags = kIONDBasicAccessTypes,
OSObject * target = 0,
Action action = 0,
void * arg = 0)
{
if ( ((buffer == 0) ?
super::initWithCapacity(inCapacity) :
super::initWithBytesNoCopy(buffer, inCapacity)) == false )
{
return false;
}
_access = accessFlags;
_tapTarget = target;
_tapAction = action;
_tapArg = arg;
_capacity = inCapacity;
length = inCapacity; // (ivar in OSData) force length to maximum
// Generate a key for this object based on its assigned name.
//
if ((_key = OSSymbol::withCString(name)) == 0)
return false;
// Clear the data buffer.
//
clearBytes();
return true;
}
//---------------------------------------------------------------------------
// Factory method that will construct and initialize an IONetworkData
// instance.
//
// Returns an IONetworkData instance on success, or 0 otherwise.
IONetworkData *
IONetworkData::withName(const char * name,
UInt32 inCapacity,
void * buffer = 0,
UInt32 accessFlags = kIONDBasicAccessTypes,
OSObject * target = 0,
Action action = 0,
void * arg = 0)
{
IONetworkData * aData = new IONetworkData;
if (aData && !aData->initWithName(name, inCapacity, buffer, accessFlags,
target, action, arg))
{
aData->release();
aData = 0;
}
return aData;
}
//---------------------------------------------------------------------------
// Free the IONetworkData instance.
void IONetworkData::free()
{
if (_key)
_key->release();
// Superclass will free the internal data buffer.
super::free();
}
//---------------------------------------------------------------------------
// Change the access flags.
//
// accessFlags: A bitfield of access flag bits.
// See IONDAccessFlags enum.
void IONetworkData::setAccessFlags(UInt32 accessFlags)
{
LOCK;
_access = (_access & kIONDImmutableAccessFlags) |
(accessFlags & ~kIONDImmutableAccessFlags);
UNLOCK;
}
//---------------------------------------------------------------------------
// Register a target/action to handle access notification.
// A notification is sent by an IONetworkData to the registered
// notification handler when an access method is called.
//
// target: The target object that implements the notification action.
// If 0, then notification will be disabled.
//
// action: The notification action. If 0, then notification will be disabled.
//
// arg: An optional argument passed to the notification handler.
void IONetworkData::setNotificationTarget(OSObject * target,
Action action,
void * arg = 0)
{
LOCK;
_tapTarget = target;
_tapAction = action;
_tapArg = arg;
UNLOCK;
}
//---------------------------------------------------------------------------
// Return a pointer to the data buffer.
const void * IONetworkData::getBuffer() const
{
return getBytesNoCopy();
}
//---------------------------------------------------------------------------
// Return the access flags.
UInt32 IONetworkData::getAccessFlags() const
{
return _access;
}
//---------------------------------------------------------------------------
// Return the notification target.
OSObject * IONetworkData::getTarget() const
{
return _tapTarget;
}
//---------------------------------------------------------------------------
// Return the notification action.
IONetworkData::Action IONetworkData::getAction() const
{
return _tapAction;
}
//---------------------------------------------------------------------------
// Return the notification argument.
void * IONetworkData::getArgument() const
{
return _tapArg;
}
//---------------------------------------------------------------------------
// Get an OSSymbol key associated with this IONetworkData instance.
// During initialization, IONetworkData will create an OSSymbol
// key based on its assigned name.
//
// Return an OSSymbol key generated from the assigned name.
const OSSymbol * IONetworkData::getKey() const
{
return _key;
}
//---------------------------------------------------------------------------
// Override the getCapacity() method inherited from OSData.
// This method overrides the implementation in OSData in
// order to report the capacity for both internal or external
// data buffers.
//
// Return the capacity of the data buffer in bytes.
UInt IONetworkData::getCapacity() const
{
return _capacity;
}
//---------------------------------------------------------------------------
// Update the data buffer from a source buffer provided by the
// caller.
//
// bytes: Pointer to a source buffer provided by the caller.
//
// inLength: Length of the source buffer, or the amount of data
// to copy to the data buffer. The length provided must
// be smaller than or equal to the capacity of the object.
//
// Returns true if the length of the source buffer is within limits, and
// the source buffer was copied, false otherwise.
bool IONetworkData::setBytes(const void * bytes, UInt inLength)
{
if (inLength > _capacity)
return false; // specified buffer is too large.
if (inLength == 0)
return true;
bcopy(bytes, data, inLength);
length = inLength; // set the new data length.
return true;
}
//---------------------------------------------------------------------------
// Set a new data buffer length to indicate the amount of
// valid data in the data buffer.
//
// inLength: Length of the data buffer in bytes. The length provided must
// be smaller than or equal to the capacity of the data object.
//
// Returns true if the length provided was accepted.
bool IONetworkData::setLength(UInt inLength)
{
if (inLength > _capacity)
return false; // Length cannot exceed capacity.
length = inLength;
return true;
}
//---------------------------------------------------------------------------
// Copy the data buffer (with length bytes) to a destination buffer
// provided by the caller.
//
// bytes: Pointer to a destination buffer.
//
// inOutLength: The caller must initialize the integer value pointed
// by inOutLength with the size of the destination buffer
// before the call. This method will overwrite it with the
// number of bytes copied.
//
// Returns true if the destination buffer is larger than or equal to the
// length of the data buffer, false otherwise.
bool IONetworkData::copyBytes(void * bytes, UInt * inOutLength) const
{
if (length > *inOutLength)
return false; // specified buffer is too small.
*inOutLength = length; // return the number of bytes copied.
if (length)
bcopy(data, bytes, length);
return true;
}
//---------------------------------------------------------------------------
// Clear the entire data buffer and fill it with zeroes.
//
// Return true if the buffer was cleared, false otherwise.
bool IONetworkData::clearBytes()
{
if (_capacity)
bzero(data, _capacity);
return true;
}
//---------------------------------------------------------------------------
// Handle a user space request to reset the data buffer. If notication
// is enabled, then the notification handler is called after the data
// buffer has been cleared
//
// Returns kIOReturnNotWritable if reset access is not allowed,
// kIOReturnSuccess otherwise. If a notification handler is called,
// and it returns an error code, then that error is returned.
IOReturn IONetworkData::reset()
{
IOReturn ret = kIOReturnUnsupported;
LOCK;
do {
// Check access.
//
if ((_access & kIONDAccessTypeReset) == 0)
{
ret = kIOReturnNotWritable;
break;
}
// Default action is to bzero the entire buffer.
//
if ((_access & kIONDAccessNoBufferAccess) == 0)
{
clearBytes();
ret = kIOReturnSuccess;
}
// Notify our target.
//
if (TAP_IS_VALID)
{
ret = (_tapTarget->*_tapAction)(this,
(UInt32) kIONDAccessTypeReset,
_tapArg, 0, 0);
}
}
while (0);
UNLOCK;
return ret;
}
//---------------------------------------------------------------------------
// Handle a user space request to read from the data buffer and copy it
// to the destination buffer provided. If notification is enabled,
// then the notification handler is called before the data buffer
// is copied to the destination buffer. The notification handler may
// use this opportunity to update the contents of the data buffer.
//
// inBuffer: Pointer to the destination buffer.
//
// inOutSize: Pointer to an integer containing the size of the destination
// buffer before the call. Overwritten by this method to the
// actual number of bytes copied to the destination buffer.
//
// Returns kIOReturnSuccess if success,
// kIOReturnBadArgument if an argument provided is invalid,
// kIOReturnNoSpace if the destination buffer is too small,
// kIOReturnNotReadable if read access is not permitted,
// or an error from the notification handler.
IOReturn IONetworkData::read(void * inBuffer, UInt * inOutSize)
{
IOReturn ret = kIOReturnUnsupported;
LOCK;
do {
// Check the arguments.
//
if (!inBuffer || !inOutSize)
{
ret = kIOReturnBadArgument;
break;
}
// Check access.
//
if ((_access & kIONDAccessTypeRead) == 0)
{
ret = kIOReturnNotReadable;
break;
}
// Notify the target before the read operation.
// The target can take this opportunity to update the
// data buffer. If the target returns an error,
// abort and return the error.
//
if (TAP_IS_VALID)
{
ret = (_tapTarget->*_tapAction)(this,
(UInt32) kIONDAccessTypeRead,
_tapArg,
inBuffer,
(UInt32 *) inOutSize);
if (ret != kIOReturnSuccess)
break;
}
if ((_access & kIONDAccessNoBufferAccess) == 0)
{
ret = (copyBytes(inBuffer, inOutSize) ?
kIOReturnSuccess : kIOReturnNoSpace);
}
}
while (0);
UNLOCK;
return ret;
}
//---------------------------------------------------------------------------
// Handle a request to write to the data buffer from a source buffer provided.
// If notification is enabled, then the notification handler is called after
// the source buffer has been copied to the data buffer.
//
// inBuffer: Pointer to the source buffer.
//
// size: Size of the source buffer in bytes.
//
// Returns kIOReturnSuccess on success,
// kIOReturnBadArgument if an argument provided is invalid,
// kIOReturnNoSpace if the source buffer is too big,
// kIOReturnNotWritable if write access is not permitted,
// or an error from the notification handler.
IOReturn IONetworkData::write(void * inBuffer, UInt size)
{
IOReturn ret = kIOReturnUnsupported;
LOCK;
do {
// Check the arguments.
//
if (!inBuffer)
{
ret = kIOReturnBadArgument;
break;
}
// Check access.
//
if ((_access & kIONDAccessTypeWrite) == 0)
{
ret = kIOReturnNotWritable;
break;
}
// Update the data buffer.
//
if ((_access & kIONDAccessNoBufferAccess) == 0)
{
ret = (setBytes(inBuffer, size)) ?
kIOReturnSuccess : kIOReturnNoSpace;
if (ret != kIOReturnSuccess)
break;
}
// Notify the target after a successful write operation.
//
if (TAP_IS_VALID)
{
ret = (_tapTarget->*_tapAction)(this,
(UInt32) kIONDAccessTypeWrite,
_tapArg,
inBuffer,
(UInt32 *) &size);
}
}
while (0);
UNLOCK;
return ret;
}
//---------------------------------------------------------------------------
// Serialize the IONetworkData object. If notification is enabled,
// then the notification handler is called before the data buffer is
// serialized.
//
// s: An OSSerialize object.
bool IONetworkData::serialize(OSSerialize * s) const
{
bool ok;
OSDictionary * dictToSerialize;
OSData * dataEntry;
OSNumber * numberEntry;
dictToSerialize = OSDictionary::withCapacity(3);
if (!dictToSerialize)
return false;
dataEntry = OSData::withBytesNoCopy((void *) &_access, sizeof(_access));
if (dataEntry) {
dictToSerialize->setObject(gIONDAccessKey, dataEntry);
dataEntry->release();
}
numberEntry = OSNumber::withNumber(_capacity, sizeof(_capacity) * 8);
if (numberEntry) {
dictToSerialize->setObject(gIONDCapacityKey, numberEntry);
numberEntry->release();
}
LOCK;
do {
// Check access.
//
if ((_access & kIONDAccessTypeSerialize) == 0)
break;
if (_access & kIONDAccessNoBufferAccess)
break;
// Notify the target before the read operation.
// The target can take this opportunity to update the
// data buffer. If the target returns an error,
// then the data buffer is not serialized.
//
if (TAP_IS_VALID &&
((_tapTarget->*_tapAction)((IONetworkData *) this,
kIONDAccessTypeSerialize,
_tapArg, 0, 0) != kIOReturnSuccess))
{
break;
}
dataEntry = OSData::withBytesNoCopy(data, length);
if (dataEntry) {
dictToSerialize->setObject(gIONDDataKey, dataEntry);
dataEntry->release();
}
}
while (0);
ok = dictToSerialize->serialize(s);
dictToSerialize->release();
UNLOCK;
return ok;
}