2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <IOKit/IOKernelReportStructs.h>
30 #include <IOKit/IOKernelReporters.h>
31 #include "IOReporterDefs.h"
34 #include <IOKit/IORegistryEntry.h>
36 #define super OSObject
37 OSDefineMetaClassAndStructors(IOReporter
, OSObject
);
39 // be careful to retain and release as necessary
40 static const OSSymbol
*gIOReportNoChannelName
= OSSymbol::withCString("_NO_NAME_4");
42 // * We might someday want an IOReportManager (vs. these static funcs)
44 /**************************************/
45 /*** STATIC METHODS ***/
46 /**************************************/
48 IOReporter::configureAllReports(OSSet
*reporters
,
49 IOReportChannelList
*channelList
,
50 IOReportConfigureAction action
,
54 IOReturn rval
= kIOReturnError
;
55 OSCollectionIterator
*iterator
= NULL
;
57 if (reporters
== NULL
|| channelList
== NULL
|| result
== NULL
) {
58 rval
= kIOReturnBadArgument
;
64 case kIOReportGetDimensions
:
66 case kIOReportDisable
:
69 iterator
= OSCollectionIterator::withCollection(reporters
);
71 while ((object
= iterator
->getNextObject())) {
73 IOReporter
*rep
= OSDynamicCast(IOReporter
, object
);
76 (void)rep
->configureReport(channelList
, action
, result
, destination
);
78 rval
= kIOReturnUnsupported
; // kIOReturnNotFound?
86 case kIOReportTraceOnChange
:
87 case kIOReportNotifyHubOnChange
:
89 rval
= kIOReturnUnsupported
;
93 rval
= kIOReturnSuccess
;
96 if (iterator
) iterator
->release();
101 // the duplication in these functions almost makes one want Objective-C SEL* ;)
103 IOReporter::updateAllReports(OSSet
*reporters
,
104 IOReportChannelList
*channelList
,
105 IOReportConfigureAction action
,
109 IOReturn rval
= kIOReturnError
;
110 OSCollectionIterator
*iterator
= NULL
;
112 if (reporters
== NULL
||
113 channelList
== NULL
||
115 destination
== NULL
) {
116 rval
= kIOReturnBadArgument
;
122 case kIOReportCopyChannelData
:
125 iterator
= OSCollectionIterator::withCollection(reporters
);
127 while ((object
= iterator
->getNextObject())) {
129 IOReporter
*rep
= OSDynamicCast(IOReporter
, object
);
132 (void)rep
->updateReport(channelList
, action
, result
, destination
);
134 rval
= kIOReturnUnsupported
; // kIOReturnNotFound?
142 case kIOReportTraceChannelData
:
144 rval
= kIOReturnUnsupported
;
148 rval
= kIOReturnSuccess
;
151 if (iterator
) iterator
->release();
157 /**************************************/
158 /*** COMMON INIT METHODS ***/
159 /**************************************/
162 IOReporter::init(IOService
*reportingService
,
163 IOReportChannelType channelType
,
166 bool success
= false;
168 // ::free() relies on these being initialized
169 _reporterLock
= NULL
;
172 _enableCounts
= NULL
;
173 _channelNames
= NULL
;
175 if (channelType
.report_format
== kIOReportInvalidFormat
) {
176 IORLOG("init ERROR: Channel Type ill-defined");
180 _driver_id
= reportingService
->getRegistryEntryID();
181 if (_driver_id
== 0) {
182 IORLOG("init() ERROR: no registry ID");
186 if (!super::init()) return false;
188 _channelDimension
= channelType
.nelements
;
189 _channelType
= channelType
;
190 // FIXME: need to look up dynamically
191 if (unit
== kIOReportUnitHWTicks
) {
192 #if defined(__i386__) || defined(__x86_64__)
193 // Most, but not all Macs use 1GHz
194 unit
= kIOReportUnit1GHzTicks
;
196 #error kIOReportUnitHWTicks not defined
201 // Allocate a reporter (data) lock
202 _reporterLock
= IOSimpleLockAlloc();
203 if (!_reporterLock
) goto finish
;
204 _reporterIsLocked
= false;
206 // Allocate a config lock
207 _configLock
= IOLockAlloc();
208 if (!_configLock
) goto finish
;
209 _reporterConfigIsLocked
= false;
211 // Allocate channel names array
212 _channelNames
= OSArray::withCapacity(1);
213 if (!_channelNames
) goto finish
;
223 /*******************************/
224 /*** PUBLIC METHODS ***/
225 /*******************************/
227 // init() [possibly via init*()] must be called before free()
228 // to ensure that _<var> = NULL
230 IOReporter::free(void)
232 OSSafeReleaseNULL(_channelNames
);
234 if (_configLock
) IOLockFree(_configLock
);
235 if (_reporterLock
) IOSimpleLockFree(_reporterLock
);
238 PREFL_MEMOP_PANIC(_nElements
, IOReportElement
);
239 IOFree(_elements
, (size_t)_nElements
* sizeof(IOReportElement
));
242 PREFL_MEMOP_PANIC(_nChannels
, int);
243 IOFree(_enableCounts
, (size_t)_nChannels
* sizeof(int));
250 #define TESTALLOC() do { \
252 tbuf = IOMalloc(10); \
254 IORLOG("%s:%d - _reporterIsLocked = %d & allocation successful", \
255 __PRETTY_FUNCTION__, __LINE__, _reporterIsLocked); \
259 IOReporter::addChannel(uint64_t channelID
,
260 const char *channelName
/* = NULL */)
262 IOReturn res
= kIOReturnError
, kerr
;
263 const OSSymbol
*symChannelName
= NULL
;
264 int oldNChannels
, newNChannels
= 0, freeNChannels
= 0;
266 IORLOG("IOReporter::addChannel %llx", channelID
);
268 // protect instance variables (but not contents)
269 lockReporterConfig();
271 // FIXME: Check if any channel is already present and return error
273 // addChannel() always adds one channel
274 oldNChannels
= _nChannels
;
275 if (oldNChannels
< 0 || oldNChannels
> INT_MAX
- 1) {
276 res
= kIOReturnOverrun
;
279 newNChannels
= oldNChannels
+ 1;
280 freeNChannels
= newNChannels
; // until swap success
282 // Expand addChannel()-specific data structure
283 if (_channelNames
->ensureCapacity((unsigned)newNChannels
) <
284 (unsigned)newNChannels
) {
285 res
= kIOReturnNoMemory
; goto finish
;
288 symChannelName
= OSSymbol::withCString(channelName
);
289 if (!symChannelName
) {
290 res
= kIOReturnNoMemory
; goto finish
;
293 // grab a reference to our shared global
294 symChannelName
= gIOReportNoChannelName
;
295 symChannelName
->retain();
298 // allocate new buffers into _swap* variables
299 if ((kerr
= handleSwapPrepare(newNChannels
))) {
300 // on error, channels are *not* swapped
301 res
= kerr
; goto finish
;
304 // exchange main and _swap* buffers with buffer contents protected
305 // IOReporter::handleAddChannelSwap() also increments _nElements, etc
307 res
= handleAddChannelSwap(channelID
, symChannelName
);
309 // On failure, handleAddChannelSwap() leaves *new* buffers in _swap*.
310 // On success, it's the old buffers, so we put the right size in here.
311 if (res
== kIOReturnSuccess
) {
312 freeNChannels
= oldNChannels
;
316 // free up not-in-use buffers (tracked by _swap*)
317 handleSwapCleanup(freeNChannels
);
318 if (symChannelName
) symChannelName
->release();
319 unlockReporterConfig();
326 IOReporter::createLegend(void)
328 IOReportLegendEntry
*legendEntry
= NULL
;
330 lockReporterConfig();
332 legendEntry
= handleCreateLegend();
334 unlockReporterConfig();
341 IOReporter::configureReport(IOReportChannelList
*channelList
,
342 IOReportConfigureAction action
,
346 IOReturn res
= kIOReturnError
;
348 lockReporterConfig();
350 res
= handleConfigureReport(channelList
, action
, result
, destination
);
352 unlockReporterConfig();
360 IOReporter::updateReport(IOReportChannelList
*channelList
,
361 IOReportConfigureAction action
,
365 IOReturn res
= kIOReturnError
;
369 res
= handleUpdateReport(channelList
, action
, result
, destination
);
378 /*******************************/
379 /*** PROTECTED METHODS ***/
380 /*******************************/
384 IOReporter::lockReporter()
386 _interruptState
= IOSimpleLockLockDisableInterrupt(_reporterLock
);
387 _reporterIsLocked
= true;
392 IOReporter::unlockReporter()
394 _reporterIsLocked
= false;
395 IOSimpleLockUnlockEnableInterrupt(_reporterLock
, _interruptState
);
399 IOReporter::lockReporterConfig()
401 IOLockLock(_configLock
);
402 _reporterConfigIsLocked
= true;
406 IOReporter::unlockReporterConfig()
408 _reporterConfigIsLocked
= false;
409 IOLockUnlock(_configLock
);
414 IOReporter::handleSwapPrepare(int newNChannels
)
416 IOReturn res
= kIOReturnError
;
418 size_t newElementsSize
, newECSize
;
420 // analyzer appeasement
421 newElementsSize
= newECSize
= 0;
423 //IORLOG("IOReporter::handleSwapPrepare");
425 IOREPORTER_CHECK_CONFIG_LOCK();
427 if (newNChannels
< _nChannels
) {
428 panic("%s doesn't support shrinking", __func__
);
430 if (newNChannels
<= 0 || _channelDimension
<= 0) {
431 res
= kIOReturnUnderrun
;
434 if (_swapElements
|| _swapEnableCounts
) {
435 panic("IOReporter::_swap* already in use");
438 // calculate the number of elements given #ch & the dimension of each
439 if (newNChannels
< 0 || newNChannels
> INT_MAX
/ _channelDimension
) {
440 res
= kIOReturnOverrun
;
443 newNElements
= newNChannels
* _channelDimension
;
445 // Allocate memory for the new array of report elements
446 PREFL_MEMOP_FAIL(newNElements
, IOReportElement
);
447 newElementsSize
= (size_t)newNElements
* sizeof(IOReportElement
);
448 _swapElements
= (IOReportElement
*)IOMalloc(newElementsSize
);
449 if (_swapElements
== NULL
) {
450 res
= kIOReturnNoMemory
; goto finish
;
452 memset(_swapElements
, 0, newElementsSize
);
454 // Allocate memory for the new array of channel watch counts
455 PREFL_MEMOP_FAIL(newNChannels
, int);
456 newECSize
= (size_t)newNChannels
* sizeof(int);
457 _swapEnableCounts
= (int *)IOMalloc(newECSize
);
458 if (_swapEnableCounts
== NULL
){
459 res
= kIOReturnNoMemory
; goto finish
;
461 memset(_swapEnableCounts
, 0, newECSize
);
464 res
= kIOReturnSuccess
;
469 IOFree(_swapElements
, newElementsSize
);
470 _swapElements
= NULL
;
472 if (_swapEnableCounts
) {
473 IOFree(_swapEnableCounts
, newECSize
);
474 _swapEnableCounts
= NULL
;
483 IOReporter::handleAddChannelSwap(uint64_t channel_id
,
484 const OSSymbol
*symChannelName
)
486 IOReturn res
= kIOReturnError
;
488 int *tmpWatchCounts
= NULL
;
489 IOReportElement
*tmpElements
= NULL
;
490 bool swapComplete
= false;
492 //IORLOG("IOReporter::handleSwap");
494 IOREPORTER_CHECK_CONFIG_LOCK();
495 IOREPORTER_CHECK_LOCK();
497 if (!_swapElements
|| !_swapEnableCounts
) {
498 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
502 // Copy any existing elements to the new location
503 //IORLOG("handleSwap (base) -> copying %u elements over...", _nChannels);
505 PREFL_MEMOP_PANIC(_nElements
, IOReportElement
);
506 memcpy(_swapElements
, _elements
,
507 (size_t)_nElements
* sizeof(IOReportElement
));
509 PREFL_MEMOP_PANIC(_nElements
, int);
510 memcpy(_swapEnableCounts
, _enableCounts
,
511 (size_t)_nChannels
* sizeof(int));
514 // Update principal instance variables, keep old buffers for cleanup
515 tmpElements
= _elements
;
516 _elements
= _swapElements
;
517 _swapElements
= tmpElements
;
519 tmpWatchCounts
= _enableCounts
;
520 _enableCounts
= _swapEnableCounts
;
521 _swapEnableCounts
= tmpWatchCounts
;
525 // but _nChannels & _nElements is still the old (one smaller) size
527 // Initialize new element metadata (existing elements copied above)
528 for (cnt
= 0; cnt
< _channelDimension
; cnt
++) {
530 _elements
[_nElements
+ cnt
].channel_id
= channel_id
;
531 _elements
[_nElements
+ cnt
].provider_id
= _driver_id
;
532 _elements
[_nElements
+ cnt
].channel_type
= _channelType
;
533 _elements
[_nElements
+ cnt
].channel_type
.element_idx
= cnt
;
535 //IOREPORTER_DEBUG_ELEMENT(_swapNElements + cnt);
538 // Store a channel name at the end
539 if (!_channelNames
->setObject((unsigned)_nChannels
, symChannelName
)) {
540 // Should never happen because we ensured capacity in addChannel()
541 res
= kIOReturnNoMemory
;
545 // And update the metadata: addChannel() always adds just one channel
547 _nElements
+= _channelDimension
;
550 res
= kIOReturnSuccess
;
553 if (res
&& swapComplete
) {
554 // unswap so new buffers get cleaned up instead of old
555 tmpElements
= _elements
;
556 _elements
= _swapElements
;
557 _swapElements
= tmpElements
;
559 tmpWatchCounts
= _enableCounts
;
560 _enableCounts
= _swapEnableCounts
;
561 _swapEnableCounts
= tmpWatchCounts
;
567 IOReporter::handleSwapCleanup(int swapNChannels
)
571 if (!_channelDimension
|| swapNChannels
> INT_MAX
/ _channelDimension
) {
572 panic("%s - can't free %d channels of dimension %d", __func__
,
573 swapNChannels
, _channelDimension
);
575 swapNElements
= swapNChannels
* _channelDimension
;
577 IOREPORTER_CHECK_CONFIG_LOCK();
579 // release buffers no longer used after swapping
581 PREFL_MEMOP_PANIC(swapNElements
, IOReportElement
);
582 IOFree(_swapElements
, (size_t)swapNElements
* sizeof(IOReportElement
));
583 _swapElements
= NULL
;
585 if (_swapEnableCounts
) {
586 PREFL_MEMOP_PANIC(swapNChannels
, int);
587 IOFree(_swapEnableCounts
, (size_t)swapNChannels
* sizeof(int));
588 _swapEnableCounts
= NULL
;
593 // The reporter wants to know if its channels have observers.
594 // Eventually we'll add some sort of bool ::anyChannelsInUse() which
595 // clients can use to cull unused reporters after configureReport(disable).
597 IOReporter::handleConfigureReport(IOReportChannelList
*channelList
,
598 IOReportConfigureAction action
,
602 IOReturn res
= kIOReturnError
;
603 int channel_index
= 0;
605 int *nElements
, *nChannels
;
607 // Check on channelList and result because used below
608 if (!channelList
|| !result
) goto finish
;
610 //IORLOG("IOReporter::configureReport action %u for %u channels",
611 // action, channelList->nchannels);
613 // Make sure channel is present, increase matching watch count, 'result'
614 for (chIdx
= 0; chIdx
< channelList
->nchannels
; chIdx
++) {
616 if (getChannelIndex(channelList
->channels
[chIdx
].channel_id
,
617 &channel_index
) == kIOReturnSuccess
) {
618 // IORLOG("reporter %p recognizes channel %lld", this, channelList->channels[chIdx].channel_id);
622 case kIOReportEnable
:
623 nChannels
= (int*)result
;
625 _enableCounts
[channel_index
]++;
629 case kIOReportDisable
:
630 nChannels
= (int*)result
;
632 _enableCounts
[channel_index
]--;
636 case kIOReportGetDimensions
:
637 nElements
= (int *)result
;
638 *nElements
+= _channelDimension
;
642 IORLOG("ERROR configureReport unknown action!");
649 res
= kIOReturnSuccess
;
657 IOReporter::handleUpdateReport(IOReportChannelList
*channelList
,
658 IOReportConfigureAction action
,
662 IOReturn res
= kIOReturnError
;
663 int *nElements
= (int *)result
;
664 int channel_index
= 0;
666 IOBufferMemoryDescriptor
*dest
;
668 if (!channelList
|| !result
|| !destination
) goto finish
;
670 dest
= OSDynamicCast(IOBufferMemoryDescriptor
, (OSObject
*)destination
);
672 // Invalid destination
673 res
= kIOReturnBadArgument
;
681 for (chIdx
= 0; chIdx
< channelList
->nchannels
; chIdx
++) {
683 if (getChannelIndex(channelList
->channels
[chIdx
].channel_id
,
684 &channel_index
) == kIOReturnSuccess
) {
686 //IORLOG("%s - found channel_id %llx @ index %d", __func__,
687 // channelList->channels[chIdx].channel_id,
692 case kIOReportCopyChannelData
:
693 res
= updateChannelValues(channel_index
);
695 IORLOG("ERROR: updateChannelValues() failed: %x", res
);
699 res
= updateReportChannel(channel_index
, nElements
, dest
);
701 IORLOG("ERROR: updateReportChannel() failed: %x", res
);
707 IORLOG("ERROR updateReport unknown action!");
708 res
= kIOReturnError
;
715 res
= kIOReturnSuccess
;
723 IOReporter::handleCreateLegend(void)
725 IOReportLegendEntry
*legendEntry
= NULL
;
728 channelIDs
= copyChannelIDs();
731 legendEntry
= IOReporter::legendWith(channelIDs
, _channelNames
, _channelType
, _unit
);
732 channelIDs
->release();
740 IOReporter::setElementValues(int element_index
,
741 IOReportElementValues
*values
,
742 uint64_t record_time
/* = 0 */)
744 IOReturn res
= kIOReturnError
;
746 IOREPORTER_CHECK_LOCK();
748 if (record_time
== 0) {
749 record_time
= mach_absolute_time();
752 if (element_index
>= _nElements
|| values
== NULL
) {
753 res
= kIOReturnBadArgument
;
757 memcpy(&_elements
[element_index
].values
, values
, sizeof(IOReportElementValues
));
759 _elements
[element_index
].timestamp
= record_time
;
761 //IOREPORTER_DEBUG_ELEMENT(index);
763 res
= kIOReturnSuccess
;
770 const IOReportElementValues
*
771 IOReporter::getElementValues(int element_index
)
773 IOReportElementValues
*elementValues
= NULL
;
775 IOREPORTER_CHECK_LOCK();
777 if (element_index
< 0 || element_index
>= _nElements
) {
778 IORLOG("ERROR getElementValues out of bounds!");
782 elementValues
= &_elements
[element_index
].values
;
785 return elementValues
;
790 IOReporter::updateChannelValues(int channel_index
)
792 return kIOReturnSuccess
;
797 IOReporter::updateReportChannel(int channel_index
,
799 IOBufferMemoryDescriptor
*destination
)
801 IOReturn res
= kIOReturnError
;
802 int start_element_idx
, chElems
;
805 res
= kIOReturnBadArgument
;
806 if (!nElements
|| !destination
) {
809 if (channel_index
> _nChannels
) {
813 IOREPORTER_CHECK_LOCK();
815 res
= kIOReturnOverrun
;
817 start_element_idx
= channel_index
* _channelDimension
;
818 if (start_element_idx
>= _nElements
) goto finish
;
820 chElems
= _elements
[start_element_idx
].channel_type
.nelements
;
822 // make sure we don't go beyond the end of _elements[_nElements-1]
823 if (start_element_idx
+ chElems
> _nElements
) {
827 PREFL_MEMOP_FAIL(chElems
, IOReportElement
);
828 size2cpy
= (size_t)chElems
* sizeof(IOReportElement
);
830 // make sure there's space in the destination
831 if (size2cpy
> (destination
->getCapacity() - destination
->getLength())) {
832 IORLOG("CRITICAL ERROR: Report Buffer Overflow (buffer cap %luB, length %luB, size2cpy %luB",
833 (unsigned long)destination
->getCapacity(),
834 (unsigned long)destination
->getLength(),
835 (unsigned long)size2cpy
);
839 destination
->appendBytes(&_elements
[start_element_idx
], size2cpy
);
840 *nElements
+= chElems
;
842 res
= kIOReturnSuccess
;
850 IOReporter::copyElementValues(int element_index
,
851 IOReportElementValues
*elementValues
)
853 IOReturn res
= kIOReturnError
;
855 if (!elementValues
) goto finish
;
857 IOREPORTER_CHECK_LOCK();
859 if (element_index
>= _nElements
) {
860 IORLOG("ERROR getElementValues out of bounds!");
861 res
= kIOReturnBadArgument
;
865 memcpy(elementValues
, &_elements
[element_index
].values
, sizeof(IOReportElementValues
));
866 res
= kIOReturnSuccess
;
874 IOReporter::getFirstElementIndex(uint64_t channel_id
,
877 IOReturn res
= kIOReturnError
;
878 int channel_index
= 0, element_index
= 0;
880 if (!index
) goto finish
;
882 res
= getChannelIndices(channel_id
, &channel_index
, &element_index
);
884 if (res
== kIOReturnSuccess
) {
885 *index
= element_index
;
894 IOReporter::getChannelIndex(uint64_t channel_id
,
897 IOReturn res
= kIOReturnError
;
898 int channel_index
= 0, element_index
= 0;
900 if (!index
) goto finish
;
902 res
= getChannelIndices(channel_id
, &channel_index
, &element_index
);
904 if (res
== kIOReturnSuccess
) {
905 *index
= channel_index
;
914 IOReporter::getChannelIndices(uint64_t channel_id
,
918 IOReturn res
= kIOReturnNotFound
;
921 if (!channel_index
|| !element_index
) goto finish
;
923 for (chIdx
= 0; chIdx
< _nChannels
; chIdx
++) {
925 elemIdx
= chIdx
* _channelDimension
;
926 if (elemIdx
>= _nElements
) {
927 IORLOG("ERROR getChannelIndices out of bounds!");
928 res
= kIOReturnOverrun
;
932 if (channel_id
== _elements
[elemIdx
].channel_id
) {
934 // The channel index does not care about the depth of elements...
935 *channel_index
= chIdx
;
936 *element_index
= elemIdx
;
938 res
= kIOReturnSuccess
;
947 /********************************/
948 /*** PRIVATE METHODS ***/
949 /********************************/
952 // copyChannelIDs relies on the caller to take lock
954 IOReporter::copyChannelIDs()
957 OSArray
*channelIDs
= NULL
;
960 channelIDs
= OSArray::withCapacity((unsigned)_nChannels
);
962 if (!channelIDs
) goto finish
;
964 for (cnt
= 0; cnt
< _nChannels
; cnt
++) {
966 cnt2
= cnt
* _channelDimension
;
968 // Encapsulate the Channel ID in OSNumber
969 tmpNum
= OSNumber::withNumber(_elements
[cnt2
].channel_id
, 64);
971 IORLOG("ERROR: Could not create array of channelIDs");
972 channelIDs
->release();
977 channelIDs
->setObject((unsigned)cnt
, tmpNum
);
986 // DO NOT REMOVE THIS METHOD WHICH IS THE MAIN LEGEND CREATION FUNCTION
987 /*static */ IOReportLegendEntry
*
988 IOReporter::legendWith(OSArray
*channelIDs
,
989 OSArray
*channelNames
,
990 IOReportChannelType channelType
,
993 unsigned int cnt
, chCnt
;
996 const OSSymbol
*tmpSymbol
;
997 OSArray
*channelLegendArray
= NULL
, *tmpChannelArray
= NULL
;
998 OSDictionary
*channelInfoDict
= NULL
;
999 IOReportLegendEntry
*legendEntry
= NULL
;
1001 // No need to check validity of channelNames because param is optional
1002 if (!channelIDs
) goto finish
;
1003 chCnt
= channelIDs
->getCount();
1005 channelLegendArray
= OSArray::withCapacity(chCnt
);
1007 for (cnt
= 0; cnt
< chCnt
; cnt
++) {
1009 tmpChannelArray
= OSArray::withCapacity(3);
1011 // Encapsulate the Channel ID in OSNumber
1012 tmpChannelArray
->setObject(kIOReportChannelIDIdx
, channelIDs
->getObject(cnt
));
1014 // Encapsulate the Channel Type in OSNumber
1015 memcpy(&type64
, &channelType
, sizeof(type64
));
1016 tmpNum
= OSNumber::withNumber(type64
, 64);
1020 tmpChannelArray
->setObject(kIOReportChannelTypeIdx
, tmpNum
);
1023 // Encapsulate the Channel Name in OSSymbol
1024 // Use channelNames if provided
1025 if (channelNames
!= NULL
) {
1026 tmpSymbol
= OSDynamicCast(OSSymbol
, channelNames
->getObject(cnt
));
1027 if (tmpSymbol
&& tmpSymbol
!= gIOReportNoChannelName
) {
1028 tmpChannelArray
->setObject(kIOReportChannelNameIdx
, tmpSymbol
);
1029 } // Else, skip and leave name field empty
1032 channelLegendArray
->setObject(cnt
, tmpChannelArray
);
1033 tmpChannelArray
->release();
1034 tmpChannelArray
= NULL
;
1037 // Stuff the legend entry only if we have channels...
1038 if (channelLegendArray
->getCount() != 0) {
1040 channelInfoDict
= OSDictionary::withCapacity(1);
1042 if (!channelInfoDict
) {
1046 tmpNum
= OSNumber::withNumber(unit
, 64);
1048 channelInfoDict
->setObject(kIOReportLegendUnitKey
, tmpNum
);
1052 legendEntry
= OSDictionary::withCapacity(1);
1055 legendEntry
->setObject(kIOReportLegendChannelsKey
, channelLegendArray
);
1056 legendEntry
->setObject(kIOReportLegendInfoKey
, channelInfoDict
);
1061 if (tmpChannelArray
) tmpChannelArray
->release();
1062 if (channelInfoDict
) channelInfoDict
->release();
1063 if (channelLegendArray
) channelLegendArray
->release();