--- /dev/null
+/*
+ * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <IOKit/IOKernelReportStructs.h>
+#include <IOKit/IOKernelReporters.h>
+#include "IOReporterDefs.h"
+
+#include <string.h>
+#include <IOKit/IORegistryEntry.h>
+
+#define super OSObject
+OSDefineMetaClassAndStructors(IOReporter, OSObject);
+
+// be careful to retain and release as necessary
+static const OSSymbol *gIOReportNoChannelName = OSSymbol::withCString("_NO_NAME_4");
+
+// * We might someday want an IOReportManager (vs. these static funcs)
+
+/**************************************/
+/*** STATIC METHODS ***/
+/**************************************/
+IOReturn
+IOReporter::configureAllReports(OSSet *reporters,
+ IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ IOReturn rval = kIOReturnError;
+ OSCollectionIterator *iterator = NULL;
+
+ if (reporters == NULL || channelList == NULL || result == NULL) {
+ rval = kIOReturnBadArgument;
+ goto finish;
+ }
+
+ switch (action) {
+
+ case kIOReportGetDimensions:
+ case kIOReportEnable:
+ case kIOReportDisable:
+ {
+ OSObject * object;
+ iterator = OSCollectionIterator::withCollection(reporters);
+
+ while ((object = iterator->getNextObject())) {
+
+ IOReporter *rep = OSDynamicCast(IOReporter, object);
+
+ if (rep) {
+ (void)rep->configureReport(channelList, action, result, destination);
+ } else {
+ rval = kIOReturnUnsupported; // kIOReturnNotFound?
+ goto finish;
+ }
+ }
+
+ break;
+ }
+
+ case kIOReportTraceOnChange:
+ case kIOReportNotifyHubOnChange:
+ default:
+ rval = kIOReturnUnsupported;
+ goto finish;
+ }
+
+ rval = kIOReturnSuccess;
+
+finish:
+ if (iterator) iterator->release();
+
+ return rval;
+}
+
+// the duplication in these functions almost makes one want Objective-C SEL* ;)
+IOReturn
+IOReporter::updateAllReports(OSSet *reporters,
+ IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ IOReturn rval = kIOReturnError;
+ OSCollectionIterator *iterator = NULL;
+
+ if (reporters == NULL ||
+ channelList == NULL ||
+ result == NULL ||
+ destination == NULL) {
+ rval = kIOReturnBadArgument;
+ goto finish;
+ }
+
+ switch (action) {
+
+ case kIOReportCopyChannelData:
+ {
+ OSObject * object;
+ iterator = OSCollectionIterator::withCollection(reporters);
+
+ while ((object = iterator->getNextObject())) {
+
+ IOReporter *rep = OSDynamicCast(IOReporter, object);
+
+ if (rep) {
+ (void)rep->updateReport(channelList, action, result, destination);
+ } else {
+ rval = kIOReturnUnsupported; // kIOReturnNotFound?
+ goto finish;
+ }
+ }
+
+ break;
+ }
+
+ case kIOReportTraceChannelData:
+ default:
+ rval = kIOReturnUnsupported;
+ goto finish;
+ }
+
+ rval = kIOReturnSuccess;
+
+finish:
+ if (iterator) iterator->release();
+
+ return rval;
+}
+
+
+/**************************************/
+/*** COMMON INIT METHODS ***/
+/**************************************/
+
+bool
+IOReporter::init(IOService *reportingService,
+ IOReportChannelType channelType,
+ IOReportUnits unit)
+{
+ bool success = false;
+
+ // ::free() relies on these being initialized
+ _reporterLock = NULL;
+ _configLock = NULL;
+ _elements = NULL;
+ _enableCounts = NULL;
+ _channelNames = NULL;
+
+ if (channelType.report_format == kIOReportInvalidFormat) {
+ IORLOG("init ERROR: Channel Type ill-defined");
+ goto finish;
+ }
+
+ _driver_id = reportingService->getRegistryEntryID();
+ if (_driver_id == 0) {
+ IORLOG("init() ERROR: no registry ID");
+ goto finish;
+ }
+
+ if (!super::init()) return false;
+
+ _channelDimension = channelType.nelements;
+ _channelType = channelType;
+ // FIXME: need to look up dynamically
+ if (unit == kIOReportUnitHWTicks) {
+#if defined(__i386__) || defined(__x86_64__)
+ // Most, but not all Macs use 1GHz
+ unit = kIOReportUnit1GHzTicks;
+#else
+#error kIOReportUnitHWTicks not defined
+#endif
+ }
+ _unit = unit;
+
+ // Allocate a reporter (data) lock
+ _reporterLock = IOSimpleLockAlloc();
+ if (!_reporterLock) goto finish;
+ _reporterIsLocked = false;
+
+ // Allocate a config lock
+ _configLock = IOLockAlloc();
+ if (!_configLock) goto finish;
+ _reporterConfigIsLocked = false;
+
+ // Allocate channel names array
+ _channelNames = OSArray::withCapacity(1);
+ if (!_channelNames) goto finish;
+
+ // success
+ success = true;
+
+finish:
+ if (!success) {
+ if (_configLock) IOLockFree(_configLock);
+ if (_reporterLock) IOSimpleLockFree(_reporterLock);
+ if (_channelNames) _channelNames->release();
+ }
+
+ return success;
+}
+
+
+/*******************************/
+/*** PUBLIC METHODS ***/
+/*******************************/
+
+// init() [possibly via init*()] must be called before free()
+// to ensure that _<var> = NULL
+void
+IOReporter::free(void)
+{
+ if (_configLock) IOLockFree(_configLock);
+ if (_reporterLock) IOSimpleLockFree(_reporterLock);
+
+ if (_elements) {
+ PREFL_MEMOP_PANIC(_nElements, IOReportElement);
+ IOFree(_elements, (size_t)_nElements * sizeof(IOReportElement));
+ }
+ if (_enableCounts) {
+ PREFL_MEMOP_PANIC(_nChannels, int);
+ IOFree(_enableCounts, (size_t)_nChannels * sizeof(int));
+ }
+
+ super::free();
+}
+
+/*
+#define TESTALLOC() do { \
+ void *tbuf; \
+ tbuf = IOMalloc(10); \
+ IOFree(tbuf, 10); \
+ IORLOG("%s:%d - _reporterIsLocked = %d & allocation successful", \
+ __PRETTY_FUNCTION__, __LINE__, _reporterIsLocked); \
+} while (0);
+*/
+IOReturn
+IOReporter::addChannel(uint64_t channelID,
+ const char *channelName /* = NULL */)
+{
+ IOReturn res = kIOReturnError, kerr;
+ const OSSymbol *symChannelName = NULL;
+ int oldNChannels, newNChannels = 0, freeNChannels = 0;
+
+ IORLOG("IOReporter::addChannel %llx", channelID);
+
+ // protect instance variables (but not contents)
+ lockReporterConfig();
+
+ // FIXME: Check if any channel is already present and return error
+
+ // addChannel() always adds one channel
+ oldNChannels = _nChannels;
+ if (oldNChannels < 0 || oldNChannels > INT_MAX - 1) {
+ res = kIOReturnOverrun;
+ goto finish;
+ }
+ newNChannels = oldNChannels + 1;
+ freeNChannels = newNChannels; // until swap success
+
+ // Expand addChannel()-specific data structure
+ if (_channelNames->ensureCapacity((unsigned)newNChannels) <
+ (unsigned)newNChannels) {
+ res = kIOReturnNoMemory; goto finish;
+ }
+ if (channelName) {
+ symChannelName = OSSymbol::withCString(channelName);
+ if (!symChannelName) {
+ res = kIOReturnNoMemory; goto finish;
+ }
+ } else {
+ // grab a reference to our shared global
+ symChannelName = gIOReportNoChannelName;
+ symChannelName->retain();
+ }
+
+ // allocate new buffers into _swap* variables
+ if ((kerr = handleSwapPrepare(newNChannels))) {
+ // on error, channels are *not* swapped
+ res = kerr; goto finish;
+ }
+
+ // exchange main and _swap* buffers with buffer contents protected
+ // IOReporter::handleAddChannelSwap() also increments _nElements, etc
+ lockReporter();
+ res = handleAddChannelSwap(channelID, symChannelName);
+ unlockReporter();
+ // On failure, handleAddChannelSwap() leaves *new* buffers in _swap*.
+ // On success, it's the old buffers, so we put the right size in here.
+ if (res == kIOReturnSuccess) {
+ freeNChannels = oldNChannels;
+ }
+
+finish:
+ // free up not-in-use buffers (tracked by _swap*)
+ handleSwapCleanup(freeNChannels);
+ if (symChannelName) symChannelName->release();
+ unlockReporterConfig();
+
+ return res;
+}
+
+
+IOReportLegendEntry*
+IOReporter::createLegend(void)
+{
+ IOReportLegendEntry *legendEntry = NULL;
+
+ lockReporterConfig();
+
+ legendEntry = handleCreateLegend();
+
+ unlockReporterConfig();
+
+ return legendEntry;
+}
+
+
+IOReturn
+IOReporter::configureReport(IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ IOReturn res = kIOReturnError;
+
+ lockReporterConfig();
+
+ res = handleConfigureReport(channelList, action, result, destination);
+
+ unlockReporterConfig();
+
+ return res;
+
+}
+
+
+IOReturn
+IOReporter::updateReport(IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ IOReturn res = kIOReturnError;
+
+ lockReporter();
+
+ res = handleUpdateReport(channelList, action, result, destination);
+
+ unlockReporter();
+
+ return res;
+
+}
+
+
+/*******************************/
+/*** PROTECTED METHODS ***/
+/*******************************/
+
+
+void
+IOReporter::lockReporter()
+{
+ _interruptState = IOSimpleLockLockDisableInterrupt(_reporterLock);
+ _reporterIsLocked = true;
+}
+
+
+void
+IOReporter::unlockReporter()
+{
+ _reporterIsLocked = false;
+ IOSimpleLockUnlockEnableInterrupt(_reporterLock, _interruptState);
+}
+
+void
+IOReporter::lockReporterConfig()
+{
+ IOLockLock(_configLock);
+ _reporterConfigIsLocked = true;
+}
+
+void
+IOReporter::unlockReporterConfig()
+{
+ _reporterConfigIsLocked = false;
+ IOLockUnlock(_configLock);
+}
+
+
+IOReturn
+IOReporter::handleSwapPrepare(int newNChannels)
+{
+ IOReturn res = kIOReturnError;
+ int newNElements;
+ size_t newElementsSize, newECSize;
+
+ // analyzer appeasement
+ newElementsSize = newECSize = 0;
+
+ //IORLOG("IOReporter::handleSwapPrepare");
+
+ IOREPORTER_CHECK_CONFIG_LOCK();
+
+ if (newNChannels < _nChannels) {
+ panic("%s doesn't support shrinking", __func__);
+ }
+ if (newNChannels <= 0 || _channelDimension <= 0) {
+ res = kIOReturnUnderrun;
+ goto finish;
+ }
+ if (_swapElements || _swapEnableCounts) {
+ panic("IOReporter::_swap* already in use");
+ }
+
+ // calculate the number of elements given #ch & the dimension of each
+ if (newNChannels < 0 || newNChannels > INT_MAX / _channelDimension) {
+ res = kIOReturnOverrun;
+ goto finish;
+ }
+ newNElements = newNChannels * _channelDimension;
+
+ // Allocate memory for the new array of report elements
+ PREFL_MEMOP_FAIL(newNElements, IOReportElement);
+ newElementsSize = (size_t)newNElements * sizeof(IOReportElement);
+ _swapElements = (IOReportElement *)IOMalloc(newElementsSize);
+ if (_swapElements == NULL) {
+ res = kIOReturnNoMemory; goto finish;
+ }
+ memset(_swapElements, 0, newElementsSize);
+
+ // Allocate memory for the new array of channel watch counts
+ PREFL_MEMOP_FAIL(newNChannels, int);
+ newECSize = (size_t)newNChannels * sizeof(int);
+ _swapEnableCounts = (int *)IOMalloc(newECSize);
+ if (_swapEnableCounts == NULL){
+ res = kIOReturnNoMemory; goto finish;
+ }
+ memset(_swapEnableCounts, 0, newECSize);
+
+ // success
+ res = kIOReturnSuccess;
+
+finish:
+ if (res) {
+ if (_swapElements) {
+ IOFree(_swapElements, newElementsSize);
+ _swapElements = NULL;
+ }
+ if (_swapEnableCounts) {
+ IOFree(_swapEnableCounts, newECSize);
+ _swapEnableCounts = NULL;
+ }
+ }
+
+ return res;
+}
+
+
+IOReturn
+IOReporter::handleAddChannelSwap(uint64_t channel_id,
+ const OSSymbol *symChannelName)
+{
+ IOReturn res = kIOReturnError;
+ int cnt;
+ int *tmpWatchCounts = NULL;
+ IOReportElement *tmpElements = NULL;
+ bool swapComplete = false;
+
+ //IORLOG("IOReporter::handleSwap");
+
+ IOREPORTER_CHECK_CONFIG_LOCK();
+ IOREPORTER_CHECK_LOCK();
+
+ if (!_swapElements || !_swapEnableCounts) {
+ IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
+ goto finish;
+ }
+
+ // Copy any existing elements to the new location
+ //IORLOG("handleSwap (base) -> copying %u elements over...", _nChannels);
+ if (_elements) {
+ PREFL_MEMOP_PANIC(_nElements, IOReportElement);
+ memcpy(_swapElements, _elements,
+ (size_t)_nElements * sizeof(IOReportElement));
+
+ PREFL_MEMOP_PANIC(_nElements, int);
+ memcpy(_swapEnableCounts, _enableCounts,
+ (size_t)_nChannels * sizeof(int));
+ }
+
+ // Update principal instance variables, keep old buffers for cleanup
+ tmpElements = _elements;
+ _elements = _swapElements;
+ _swapElements = tmpElements;
+
+ tmpWatchCounts = _enableCounts;
+ _enableCounts = _swapEnableCounts;
+ _swapEnableCounts = tmpWatchCounts;
+
+ swapComplete = true;
+
+ // but _nChannels & _nElements is still the old (one smaller) size
+
+ // Initialize new element metadata (existing elements copied above)
+ for (cnt = 0; cnt < _channelDimension; cnt++) {
+
+ _elements[_nElements + cnt].channel_id = channel_id;
+ _elements[_nElements + cnt].provider_id = _driver_id;
+ _elements[_nElements + cnt].channel_type = _channelType;
+ _elements[_nElements + cnt].channel_type.element_idx = cnt;
+
+ //IOREPORTER_DEBUG_ELEMENT(_swapNElements + cnt);
+ }
+
+ // Store a channel name at the end
+ if (!_channelNames->setObject((unsigned)_nChannels, symChannelName)) {
+ // Should never happen because we ensured capacity in addChannel()
+ res = kIOReturnNoMemory;
+ goto finish;
+ }
+
+ // And update the metadata: addChannel() always adds just one channel
+ _nChannels += 1;
+ _nElements += _channelDimension;
+
+ // success
+ res = kIOReturnSuccess;
+
+finish:
+ if (res && swapComplete) {
+ // unswap so new buffers get cleaned up instead of old
+ tmpElements = _elements;
+ _elements = _swapElements;
+ _swapElements = tmpElements;
+
+ tmpWatchCounts = _enableCounts;
+ _enableCounts = _swapEnableCounts;
+ _swapEnableCounts = tmpWatchCounts;
+ }
+ return res;
+}
+
+void
+IOReporter::handleSwapCleanup(int swapNChannels)
+{
+ int swapNElements;
+
+ if (!_channelDimension || swapNChannels > INT_MAX / _channelDimension) {
+ panic("%s - can't free %d channels of dimension %d", __func__,
+ swapNChannels, _channelDimension);
+ }
+ swapNElements = swapNChannels * _channelDimension;
+
+ IOREPORTER_CHECK_CONFIG_LOCK();
+
+ // release buffers no longer used after swapping
+ if (_swapElements) {
+ PREFL_MEMOP_PANIC(swapNElements, IOReportElement);
+ IOFree(_swapElements, (size_t)swapNElements * sizeof(IOReportElement));
+ _swapElements = NULL;
+ }
+ if (_swapEnableCounts) {
+ PREFL_MEMOP_PANIC(swapNChannels, int);
+ IOFree(_swapEnableCounts, (size_t)swapNChannels * sizeof(int));
+ _swapEnableCounts = NULL;
+ }
+}
+
+
+// The reporter wants to know if its channels have observers.
+// Eventually we'll add some sort of bool ::anyChannelsInUse() which
+// clients can use to cull unused reporters after configureReport(disable).
+IOReturn
+IOReporter::handleConfigureReport(IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ IOReturn res = kIOReturnError;
+ int channel_index = 0;
+ uint32_t chIdx;
+ int *nElements, *nChannels;
+
+ // Check on channelList and result because used below
+ if (!channelList || !result) goto finish;
+
+ //IORLOG("IOReporter::configureReport action %u for %u channels",
+ // action, channelList->nchannels);
+
+ // Make sure channel is present, increase matching watch count, 'result'
+ for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) {
+
+ if (getChannelIndex(channelList->channels[chIdx].channel_id,
+ &channel_index) == kIOReturnSuccess) {
+ // IORLOG("reporter %p recognizes channel %lld", this, channelList->channels[chIdx].channel_id);
+
+ switch (action) {
+
+ case kIOReportEnable:
+ nChannels = (int*)result;
+ _enabled++;
+ _enableCounts[channel_index]++;
+ (*nChannels)++;
+ break;
+
+ case kIOReportDisable:
+ nChannels = (int*)result;
+ _enabled--;
+ _enableCounts[channel_index]--;
+ (*nChannels)++;
+ break;
+
+ case kIOReportGetDimensions:
+ nElements = (int *)result;
+ *nElements += _channelDimension;
+ break;
+
+ default:
+ IORLOG("ERROR configureReport unknown action!");
+ break;
+ }
+ }
+ }
+
+ // success
+ res = kIOReturnSuccess;
+
+finish:
+ return res;
+}
+
+
+IOReturn
+IOReporter::handleUpdateReport(IOReportChannelList *channelList,
+ IOReportConfigureAction action,
+ void *result,
+ void *destination)
+{
+ IOReturn res = kIOReturnError;
+ int *nElements = (int *)result;
+ int channel_index = 0;
+ uint32_t chIdx;
+ IOBufferMemoryDescriptor *dest;
+
+ if (!channelList || !result || !destination) goto finish;
+
+ dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination);
+ if (dest == NULL) {
+ // Invalid destination
+ res = kIOReturnBadArgument;
+ goto finish;
+ }
+
+ if (!_enabled) {
+ goto finish;
+ }
+
+ for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) {
+
+ if (getChannelIndex(channelList->channels[chIdx].channel_id,
+ &channel_index) == kIOReturnSuccess) {
+
+ //IORLOG("%s - found channel_id %llx @ index %d", __func__,
+ // channelList->channels[chIdx].channel_id,
+ // channel_index);
+
+ switch(action) {
+
+ case kIOReportCopyChannelData:
+ res = updateChannelValues(channel_index);
+ if (res) {
+ IORLOG("ERROR: updateChannelValues() failed: %x", res);
+ goto finish;
+ }
+
+ res = updateReportChannel(channel_index, nElements, dest);
+ if (res) {
+ IORLOG("ERROR: updateReportChannel() failed: %x", res);
+ goto finish;
+ }
+ break;
+
+ default:
+ IORLOG("ERROR updateReport unknown action!");
+ res = kIOReturnError;
+ goto finish;
+ }
+ }
+ }
+
+ // success
+ res = kIOReturnSuccess;
+
+finish:
+ return res;
+}
+
+
+IOReportLegendEntry*
+IOReporter::handleCreateLegend(void)
+{
+ IOReportLegendEntry *legendEntry = NULL;
+ OSArray *channelIDs;
+
+ channelIDs = copyChannelIDs();
+
+ if (channelIDs) {
+ legendEntry = IOReporter::legendWith(channelIDs, _channelNames, _channelType, _unit);
+ channelIDs->release();
+ }
+
+ return legendEntry;
+}
+
+
+IOReturn
+IOReporter::setElementValues(int element_index,
+ IOReportElementValues *values,
+ uint64_t record_time /* = 0 */)
+{
+ IOReturn res = kIOReturnError;
+
+ IOREPORTER_CHECK_LOCK();
+
+ if (record_time == 0) {
+ record_time = mach_absolute_time();
+ }
+
+ if (element_index >= _nElements || values == NULL) {
+ res = kIOReturnBadArgument;
+ goto finish;
+ }
+
+ memcpy(&_elements[element_index].values, values, sizeof(IOReportElementValues));
+
+ _elements[element_index].timestamp = record_time;
+
+ //IOREPORTER_DEBUG_ELEMENT(index);
+
+ res = kIOReturnSuccess;
+
+finish:
+ return res;
+}
+
+
+const IOReportElementValues*
+IOReporter::getElementValues(int element_index)
+{
+ IOReportElementValues *elementValues = NULL;
+
+ IOREPORTER_CHECK_LOCK();
+
+ if (element_index < 0 || element_index >= _nElements) {
+ IORLOG("ERROR getElementValues out of bounds!");
+ goto finish;
+ }
+
+ elementValues = &_elements[element_index].values;
+
+finish:
+ return elementValues;
+}
+
+
+IOReturn
+IOReporter::updateChannelValues(int channel_index)
+{
+ return kIOReturnSuccess;
+}
+
+
+IOReturn
+IOReporter::updateReportChannel(int channel_index,
+ int *nElements,
+ IOBufferMemoryDescriptor *destination)
+{
+ IOReturn res = kIOReturnError;
+ int start_element_idx, chElems;
+ size_t size2cpy;
+
+ res = kIOReturnBadArgument;
+ if (!nElements || !destination) {
+ goto finish;
+ }
+ if (channel_index > _nChannels) {
+ goto finish;
+ }
+
+ IOREPORTER_CHECK_LOCK();
+
+ res = kIOReturnOverrun;
+
+ start_element_idx = channel_index * _channelDimension;
+ if (start_element_idx >= _nElements) goto finish;
+
+ chElems = _elements[start_element_idx].channel_type.nelements;
+
+ // make sure we don't go beyond the end of _elements[_nElements-1]
+ if (start_element_idx + chElems > _nElements) {
+ goto finish;
+ }
+
+ PREFL_MEMOP_FAIL(chElems, IOReportElement);
+ size2cpy = (size_t)chElems * sizeof(IOReportElement);
+
+ // make sure there's space in the destination
+ if (size2cpy > (destination->getCapacity() - destination->getLength())) {
+ IORLOG("CRITICAL ERROR: Report Buffer Overflow (buffer cap %luB, length %luB, size2cpy %luB",
+ (unsigned long)destination->getCapacity(),
+ (unsigned long)destination->getLength(),
+ (unsigned long)size2cpy);
+ goto finish;
+ }
+
+ destination->appendBytes(&_elements[start_element_idx], size2cpy);
+ *nElements += chElems;
+
+ res = kIOReturnSuccess;
+
+finish:
+ return res;
+}
+
+
+IOReturn
+IOReporter::copyElementValues(int element_index,
+ IOReportElementValues *elementValues)
+{
+ IOReturn res = kIOReturnError;
+
+ if (!elementValues) goto finish;
+
+ IOREPORTER_CHECK_LOCK();
+
+ if (element_index >= _nElements) {
+ IORLOG("ERROR getElementValues out of bounds!");
+ res = kIOReturnBadArgument;
+ goto finish;
+ }
+
+ memcpy(elementValues, &_elements[element_index].values, sizeof(IOReportElementValues));
+ res = kIOReturnSuccess;
+
+finish:
+ return res;
+}
+
+
+IOReturn
+IOReporter::getFirstElementIndex(uint64_t channel_id,
+ int *index)
+{
+ IOReturn res = kIOReturnError;
+ int channel_index = 0, element_index = 0;
+
+ if (!index) goto finish;
+
+ res = getChannelIndices(channel_id, &channel_index, &element_index);
+
+ if (res == kIOReturnSuccess) {
+ *index = element_index;
+ }
+
+finish:
+ return res;
+}
+
+
+IOReturn
+IOReporter::getChannelIndex(uint64_t channel_id,
+ int *index)
+{
+ IOReturn res = kIOReturnError;
+ int channel_index = 0, element_index = 0;
+
+ if (!index) goto finish;
+
+ res = getChannelIndices(channel_id, &channel_index, &element_index);
+
+ if (res == kIOReturnSuccess) {
+ *index = channel_index;
+ }
+
+finish:
+ return res;
+}
+
+
+IOReturn
+IOReporter::getChannelIndices(uint64_t channel_id,
+ int *channel_index,
+ int *element_index)
+{
+ IOReturn res = kIOReturnNotFound;
+ int chIdx, elemIdx;
+
+ if (!channel_index || !element_index) goto finish;
+
+ for (chIdx = 0; chIdx < _nChannels; chIdx++) {
+
+ elemIdx = chIdx * _channelDimension;
+ if (elemIdx >= _nElements) {
+ IORLOG("ERROR getChannelIndices out of bounds!");
+ res = kIOReturnOverrun;
+ goto finish;
+ }
+
+ if (channel_id == _elements[elemIdx].channel_id) {
+
+ // The channel index does not care about the depth of elements...
+ *channel_index = chIdx;
+ *element_index = elemIdx;
+
+ res = kIOReturnSuccess;
+ goto finish;
+ }
+ }
+
+finish:
+ return res;
+}
+
+/********************************/
+/*** PRIVATE METHODS ***/
+/********************************/
+
+
+// copyChannelIDs relies on the caller to take lock
+OSArray*
+IOReporter::copyChannelIDs()
+{
+ int cnt, cnt2;
+ OSArray *channelIDs = NULL;
+ OSNumber *tmpNum;
+
+ channelIDs = OSArray::withCapacity((unsigned)_nChannels);
+
+ if (!channelIDs) goto finish;
+
+ for (cnt = 0; cnt < _nChannels; cnt++) {
+
+ cnt2 = cnt * _channelDimension;
+
+ // Encapsulate the Channel ID in OSNumber
+ tmpNum = OSNumber::withNumber(_elements[cnt2].channel_id, 64);
+ if (!tmpNum) {
+ IORLOG("ERROR: Could not create array of channelIDs");
+ channelIDs->release();
+ channelIDs = NULL;
+ goto finish;
+ }
+
+ channelIDs->setObject((unsigned)cnt, tmpNum);
+ tmpNum->release();
+ }
+
+finish:
+ return channelIDs;
+}
+
+
+// DO NOT REMOVE THIS METHOD WHICH IS THE MAIN LEGEND CREATION FUNCTION
+/*static */ IOReportLegendEntry*
+IOReporter::legendWith(OSArray *channelIDs,
+ OSArray *channelNames,
+ IOReportChannelType channelType,
+ IOReportUnits unit)
+{
+ unsigned int cnt, chCnt;
+ uint64_t type64;
+ OSNumber *tmpNum;
+ const OSSymbol *tmpSymbol;
+ OSArray *channelLegendArray = NULL, *tmpChannelArray = NULL;
+ OSDictionary *channelInfoDict = NULL;
+ IOReportLegendEntry *legendEntry = NULL;
+
+ // No need to check validity of channelNames because param is optional
+ if (!channelIDs) goto finish;
+ chCnt = channelIDs->getCount();
+
+ channelLegendArray = OSArray::withCapacity(chCnt);
+
+ for (cnt = 0; cnt < chCnt; cnt++) {
+
+ tmpChannelArray = OSArray::withCapacity(3);
+
+ // Encapsulate the Channel ID in OSNumber
+ tmpChannelArray->setObject(kIOReportChannelIDIdx, channelIDs->getObject(cnt));
+
+ // Encapsulate the Channel Type in OSNumber
+ memcpy(&type64, &channelType, sizeof(type64));
+ tmpNum = OSNumber::withNumber(type64, 64);
+ if (!tmpNum) {
+ goto finish;
+ }
+ tmpChannelArray->setObject(kIOReportChannelTypeIdx, tmpNum);
+ tmpNum->release();
+
+ // Encapsulate the Channel Name in OSSymbol
+ // Use channelNames if provided
+ if (channelNames != NULL) {
+ tmpSymbol = OSDynamicCast(OSSymbol, channelNames->getObject(cnt));
+ if (tmpSymbol && tmpSymbol != gIOReportNoChannelName) {
+ tmpChannelArray->setObject(kIOReportChannelNameIdx, tmpSymbol);
+ } // Else, skip and leave name field empty
+ }
+
+ channelLegendArray->setObject(cnt, tmpChannelArray);
+ tmpChannelArray->release();
+ tmpChannelArray = NULL;
+ }
+
+ // Stuff the legend entry only if we have channels...
+ if (channelLegendArray->getCount() != 0) {
+
+ channelInfoDict = OSDictionary::withCapacity(1);
+
+ if (!channelInfoDict) {
+ goto finish;
+ }
+
+ tmpNum = OSNumber::withNumber(unit, 64);
+ if (tmpNum) {
+ channelInfoDict->setObject(kIOReportLegendUnitKey, tmpNum);
+ tmpNum->release();
+ }
+
+ legendEntry = OSDictionary::withCapacity(1);
+
+ if (legendEntry) {
+ legendEntry->setObject(kIOReportLegendChannelsKey, channelLegendArray);
+ legendEntry->setObject(kIOReportLegendInfoKey, channelInfoDict);
+ }
+ }
+
+finish:
+ if (tmpChannelArray) tmpChannelArray->release();
+ if (channelInfoDict) channelInfoDict->release();
+ if (channelLegendArray) channelLegendArray->release();
+
+ return legendEntry;
+}