1/*
2 * Copyright (c) 2012-2013 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <IOKit/IOKernelReportStructs.h>
30#include <IOKit/IOKernelReporters.h>
31#include "IOReporterDefs.h"
32
33#include <string.h>
34#include <IOKit/IORegistryEntry.h>
35
36#define super OSObject
37OSDefineMetaClassAndStructors(IOReporter, OSObject);
38
39// be careful to retain and release as necessary
40static const OSSymbol *gIOReportNoChannelName = OSSymbol::withCString("_NO_NAME_4");
41
42// * We might someday want an IOReportManager (vs. these static funcs)
43
44/**************************************/
45/*** STATIC METHODS ***/
46/**************************************/
47IOReturn
48IOReporter::configureAllReports(OSSet *reporters,
49 IOReportChannelList *channelList,
50 IOReportConfigureAction action,
51 void *result,
52 void *destination)
53{
54 IOReturn rval = kIOReturnError;
55 OSCollectionIterator *iterator = NULL;
56
57 if (reporters == NULL || channelList == NULL || result == NULL) {
58 rval = kIOReturnBadArgument;
59 goto finish;
60 }
61
62 switch (action) {
63
64 case kIOReportGetDimensions:
65 case kIOReportEnable:
66 case kIOReportDisable:
67 {
68 OSObject * object;
69 iterator = OSCollectionIterator::withCollection(reporters);
70
71 while ((object = iterator->getNextObject())) {
72
73 IOReporter *rep = OSDynamicCast(IOReporter, object);
74
75 if (rep) {
76 (void)rep->configureReport(channelList, action, result, destination);
77 } else {
78 rval = kIOReturnUnsupported; // kIOReturnNotFound?
79 goto finish;
80 }
81 }
82
83 break;
84 }
85
86 case kIOReportTraceOnChange:
87 case kIOReportNotifyHubOnChange:
88 default:
89 rval = kIOReturnUnsupported;
90 goto finish;
91 }
92
93 rval = kIOReturnSuccess;
94
95finish:
96 if (iterator) iterator->release();
97
98 return rval;
99}
100
101// the duplication in these functions almost makes one want Objective-C SEL* ;)
102IOReturn
103IOReporter::updateAllReports(OSSet *reporters,
104 IOReportChannelList *channelList,
105 IOReportConfigureAction action,
106 void *result,
107 void *destination)
108{
109 IOReturn rval = kIOReturnError;
110 OSCollectionIterator *iterator = NULL;
111
112 if (reporters == NULL ||
113 channelList == NULL ||
114 result == NULL ||
115 destination == NULL) {
116 rval = kIOReturnBadArgument;
117 goto finish;
118 }
119
120 switch (action) {
121
122 case kIOReportCopyChannelData:
123 {
124 OSObject * object;
125 iterator = OSCollectionIterator::withCollection(reporters);
126
127 while ((object = iterator->getNextObject())) {
128
129 IOReporter *rep = OSDynamicCast(IOReporter, object);
130
131 if (rep) {
132 (void)rep->updateReport(channelList, action, result, destination);
133 } else {
134 rval = kIOReturnUnsupported; // kIOReturnNotFound?
135 goto finish;
136 }
137 }
138
139 break;
140 }
141
142 case kIOReportTraceChannelData:
143 default:
144 rval = kIOReturnUnsupported;
145 goto finish;
146 }
147
148 rval = kIOReturnSuccess;
149
150finish:
151 if (iterator) iterator->release();
152
153 return rval;
154}
155
156
157/**************************************/
158/*** COMMON INIT METHODS ***/
159/**************************************/
160
161bool
162IOReporter::init(IOService *reportingService,
163 IOReportChannelType channelType,
164 IOReportUnit unit)
165{
166 bool success = false;
167
168 // ::free() relies on these being initialized
169 _reporterLock = NULL;
170 _configLock = NULL;
171 _elements = NULL;
172 _enableCounts = NULL;
173 _channelNames = NULL;
174
175 if (channelType.report_format == kIOReportInvalidFormat) {
176 IORLOG("init ERROR: Channel Type ill-defined");
177 goto finish;
178 }
179
180 _driver_id = reportingService->getRegistryEntryID();
181 if (_driver_id == 0) {
182 IORLOG("init() ERROR: no registry ID");
183 goto finish;
184 }
185
186 if (!super::init()) return false;
187
188 _channelDimension = channelType.nelements;
189 _channelType = channelType;
190 // FIXME: need to look up dynamically
191 if (unit == kIOReportUnitHWTicks) {
192#if defined(__arm__) || defined(__arm64__)
193 unit = kIOReportUnit24MHzTicks;
194#elif defined(__i386__) || defined(__x86_64__)
195 // Most, but not all Macs use 1GHz
196 unit = kIOReportUnit1GHzTicks;
197#else
198#error kIOReportUnitHWTicks not defined
199#endif
200 }
201 _unit = unit;
202
203 // Allocate a reporter (data) lock
204 _reporterLock = IOSimpleLockAlloc();
205 if (!_reporterLock) goto finish;
206 _reporterIsLocked = false;
207
208 // Allocate a config lock
209 _configLock = IOLockAlloc();
210 if (!_configLock) goto finish;
211 _reporterConfigIsLocked = false;
212
213 // Allocate channel names array
214 _channelNames = OSArray::withCapacity(1);
215 if (!_channelNames) goto finish;
216
217 // success
218 success = true;
219
220finish:
221 return success;
222}
223
224
225/*******************************/
226/*** PUBLIC METHODS ***/
227/*******************************/
228
229// init() [possibly via init*()] must be called before free()
230// to ensure that _<var> = NULL
231void
232IOReporter::free(void)
233{
234 OSSafeReleaseNULL(_channelNames);
235
236 if (_configLock) IOLockFree(_configLock);
237 if (_reporterLock) IOSimpleLockFree(_reporterLock);
238
239 if (_elements) {
240 PREFL_MEMOP_PANIC(_nElements, IOReportElement);
241 IOFree(_elements, (size_t)_nElements * sizeof(IOReportElement));
242 }
243 if (_enableCounts) {
244 PREFL_MEMOP_PANIC(_nChannels, int);
245 IOFree(_enableCounts, (size_t)_nChannels * sizeof(int));
246 }
247
248 super::free();
249}
250
251/*
252#define TESTALLOC() do { \
253 void *tbuf; \
254 tbuf = IOMalloc(10); \
255 IOFree(tbuf, 10); \
256 IORLOG("%s:%d - _reporterIsLocked = %d & allocation successful", \
257 __PRETTY_FUNCTION__, __LINE__, _reporterIsLocked); \
258} while (0);
259*/
260IOReturn
261IOReporter::addChannel(uint64_t channelID,
262 const char *channelName /* = NULL */)
263{
264 IOReturn res = kIOReturnError, kerr;
265 const OSSymbol *symChannelName = NULL;
266 int oldNChannels, newNChannels = 0, freeNChannels = 0;
267
268 IORLOG("IOReporter::addChannel %llx", channelID);
269
270 // protect instance variables (but not contents)
271 lockReporterConfig();
272
273 // FIXME: Check if any channel is already present and return error
274
275 // addChannel() always adds one channel
276 oldNChannels = _nChannels;
277 if (oldNChannels < 0 || oldNChannels > INT_MAX - 1) {
278 res = kIOReturnOverrun;
279 goto finish;
280 }
281 newNChannels = oldNChannels + 1;
282 freeNChannels = newNChannels; // until swap success
283
284 // Expand addChannel()-specific data structure
285 if (_channelNames->ensureCapacity((unsigned)newNChannels) <
286 (unsigned)newNChannels) {
287 res = kIOReturnNoMemory; goto finish;
288 }
289 if (channelName) {
290 symChannelName = OSSymbol::withCString(channelName);
291 if (!symChannelName) {
292 res = kIOReturnNoMemory; goto finish;
293 }
294 } else {
295 // grab a reference to our shared global
296 symChannelName = gIOReportNoChannelName;
297 symChannelName->retain();
298 }
299
300 // allocate new buffers into _swap* variables
301 if ((kerr = handleSwapPrepare(newNChannels))) {
302 // on error, channels are *not* swapped
303 res = kerr; goto finish;
304 }
305
306 // exchange main and _swap* buffers with buffer contents protected
307 // IOReporter::handleAddChannelSwap() also increments _nElements, etc
308 lockReporter();
309 res = handleAddChannelSwap(channelID, symChannelName);
310 unlockReporter();
311 // On failure, handleAddChannelSwap() leaves *new* buffers in _swap*.
312 // On success, it's the old buffers, so we put the right size in here.
313 if (res == kIOReturnSuccess) {
314 freeNChannels = oldNChannels;
315 }
316
317finish:
318 // free up not-in-use buffers (tracked by _swap*)
319 handleSwapCleanup(freeNChannels);
320 if (symChannelName) symChannelName->release();
321 unlockReporterConfig();
322
323 return res;
324}
325
326
327IOReportLegendEntry*
328IOReporter::createLegend(void)
329{
330 IOReportLegendEntry *legendEntry = NULL;
331
332 lockReporterConfig();
333
334 legendEntry = handleCreateLegend();
335
336 unlockReporterConfig();
337
338 return legendEntry;
339}
340
341
342IOReturn
343IOReporter::configureReport(IOReportChannelList *channelList,
344 IOReportConfigureAction action,
345 void *result,
346 void *destination)
347{
348 IOReturn res = kIOReturnError;
349
350 lockReporterConfig();
351
352 res = handleConfigureReport(channelList, action, result, destination);
353
354 unlockReporterConfig();
355
356 return res;
357
358}
359
360
361IOReturn
362IOReporter::updateReport(IOReportChannelList *channelList,
363 IOReportConfigureAction action,
364 void *result,
365 void *destination)
366{
367 IOReturn res = kIOReturnError;
368
369 lockReporter();
370
371 res = handleUpdateReport(channelList, action, result, destination);
372
373 unlockReporter();
374
375 return res;
376
377}
378
379
380/*******************************/
381/*** PROTECTED METHODS ***/
382/*******************************/
383
384
385void
386IOReporter::lockReporter()
387{
388 _interruptState = IOSimpleLockLockDisableInterrupt(_reporterLock);
389 _reporterIsLocked = true;
390}
391
392
393void
394IOReporter::unlockReporter()
395{
396 _reporterIsLocked = false;
397 IOSimpleLockUnlockEnableInterrupt(_reporterLock, _interruptState);
398}
399
400void
401IOReporter::lockReporterConfig()
402{
403 IOLockLock(_configLock);
404 _reporterConfigIsLocked = true;
405}
406
407void
408IOReporter::unlockReporterConfig()
409{
410 _reporterConfigIsLocked = false;
411 IOLockUnlock(_configLock);
412}
413
414
415IOReturn
416IOReporter::handleSwapPrepare(int newNChannels)
417{
418 IOReturn res = kIOReturnError;
419 int newNElements;
420 size_t newElementsSize, newECSize;
421
422 // analyzer appeasement
423 newElementsSize = newECSize = 0;
424
425 //IORLOG("IOReporter::handleSwapPrepare");
426
427 IOREPORTER_CHECK_CONFIG_LOCK();
428
429 if (newNChannels < _nChannels) {
430 panic("%s doesn't support shrinking", __func__);
431 }
432 if (newNChannels <= 0 || _channelDimension <= 0) {
433 res = kIOReturnUnderrun;
434 goto finish;
435 }
436 if (_swapElements || _swapEnableCounts) {
437 panic("IOReporter::_swap* already in use");
438 }
439
440 // calculate the number of elements given #ch & the dimension of each
441 if (newNChannels < 0 || newNChannels > INT_MAX / _channelDimension) {
442 res = kIOReturnOverrun;
443 goto finish;
444 }
445 newNElements = newNChannels * _channelDimension;
446
447 // Allocate memory for the new array of report elements
448 PREFL_MEMOP_FAIL(newNElements, IOReportElement);
449 newElementsSize = (size_t)newNElements * sizeof(IOReportElement);
450 _swapElements = (IOReportElement *)IOMalloc(newElementsSize);
451 if (_swapElements == NULL) {
452 res = kIOReturnNoMemory; goto finish;
453 }
454 memset(_swapElements, 0, newElementsSize);
455
456 // Allocate memory for the new array of channel watch counts
457 PREFL_MEMOP_FAIL(newNChannels, int);
458 newECSize = (size_t)newNChannels * sizeof(int);
459 _swapEnableCounts = (int *)IOMalloc(newECSize);
460 if (_swapEnableCounts == NULL){
461 res = kIOReturnNoMemory; goto finish;
462 }
463 memset(_swapEnableCounts, 0, newECSize);
464
465 // success
466 res = kIOReturnSuccess;
467
468finish:
469 if (res) {
470 if (_swapElements) {
471 IOFree(_swapElements, newElementsSize);
472 _swapElements = NULL;
473 }
474 if (_swapEnableCounts) {
475 IOFree(_swapEnableCounts, newECSize);
476 _swapEnableCounts = NULL;
477 }
478 }
479
480 return res;
481}
482
483
484IOReturn
485IOReporter::handleAddChannelSwap(uint64_t channel_id,
486 const OSSymbol *symChannelName)
487{
488 IOReturn res = kIOReturnError;
489 int cnt;
490 int *tmpWatchCounts = NULL;
491 IOReportElement *tmpElements = NULL;
492 bool swapComplete = false;
493
494 //IORLOG("IOReporter::handleSwap");
495
496 IOREPORTER_CHECK_CONFIG_LOCK();
497 IOREPORTER_CHECK_LOCK();
498
499 if (!_swapElements || !_swapEnableCounts) {
500 IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
501 goto finish;
502 }
503
504 // Copy any existing elements to the new location
505 //IORLOG("handleSwap (base) -> copying %u elements over...", _nChannels);
506 if (_elements) {
507 PREFL_MEMOP_PANIC(_nElements, IOReportElement);
508 memcpy(_swapElements, _elements,
509 (size_t)_nElements * sizeof(IOReportElement));
510
511 PREFL_MEMOP_PANIC(_nElements, int);
512 memcpy(_swapEnableCounts, _enableCounts,
513 (size_t)_nChannels * sizeof(int));
514 }
515
516 // Update principal instance variables, keep old buffers for cleanup
517 tmpElements = _elements;
518 _elements = _swapElements;
519 _swapElements = tmpElements;
520
521 tmpWatchCounts = _enableCounts;
522 _enableCounts = _swapEnableCounts;
523 _swapEnableCounts = tmpWatchCounts;
524
525 swapComplete = true;
526
527 // but _nChannels & _nElements is still the old (one smaller) size
528
529 // Initialize new element metadata (existing elements copied above)
530 for (cnt = 0; cnt < _channelDimension; cnt++) {
531
532 _elements[_nElements + cnt].channel_id = channel_id;
533 _elements[_nElements + cnt].provider_id = _driver_id;
534 _elements[_nElements + cnt].channel_type = _channelType;
535 _elements[_nElements + cnt].channel_type.element_idx = cnt;
536
537 //IOREPORTER_DEBUG_ELEMENT(_swapNElements + cnt);
538 }
539
540 // Store a channel name at the end
541 if (!_channelNames->setObject((unsigned)_nChannels, symChannelName)) {
542 // Should never happen because we ensured capacity in addChannel()
543 res = kIOReturnNoMemory;
544 goto finish;
545 }
546
547 // And update the metadata: addChannel() always adds just one channel
548 _nChannels += 1;
549 _nElements += _channelDimension;
550
551 // success
552 res = kIOReturnSuccess;
553
554finish:
555 if (res && swapComplete) {
556 // unswap so new buffers get cleaned up instead of old
557 tmpElements = _elements;
558 _elements = _swapElements;
559 _swapElements = tmpElements;
560
561 tmpWatchCounts = _enableCounts;
562 _enableCounts = _swapEnableCounts;
563 _swapEnableCounts = tmpWatchCounts;
564 }
565 return res;
566}
567
568void
569IOReporter::handleSwapCleanup(int swapNChannels)
570{
571 int swapNElements;
572
573 if (!_channelDimension || swapNChannels > INT_MAX / _channelDimension) {
574 panic("%s - can't free %d channels of dimension %d", __func__,
575 swapNChannels, _channelDimension);
576 }
577 swapNElements = swapNChannels * _channelDimension;
578
579 IOREPORTER_CHECK_CONFIG_LOCK();
580
581 // release buffers no longer used after swapping
582 if (_swapElements) {
583 PREFL_MEMOP_PANIC(swapNElements, IOReportElement);
584 IOFree(_swapElements, (size_t)swapNElements * sizeof(IOReportElement));
585 _swapElements = NULL;
586 }
587 if (_swapEnableCounts) {
588 PREFL_MEMOP_PANIC(swapNChannels, int);
589 IOFree(_swapEnableCounts, (size_t)swapNChannels * sizeof(int));
590 _swapEnableCounts = NULL;
591 }
592}
593
594
595// The reporter wants to know if its channels have observers.
596// Eventually we'll add some sort of bool ::anyChannelsInUse() which
597// clients can use to cull unused reporters after configureReport(disable).
598IOReturn
599IOReporter::handleConfigureReport(IOReportChannelList *channelList,
600 IOReportConfigureAction action,
601 void *result,
602 void *destination)
603{
604 IOReturn res = kIOReturnError;
605 int channel_index = 0;
606 uint32_t chIdx;
607 int *nElements, *nChannels;
608
609 // Check on channelList and result because used below
610 if (!channelList || !result) goto finish;
611
612 //IORLOG("IOReporter::configureReport action %u for %u channels",
613 // action, channelList->nchannels);
614
615 // Make sure channel is present, increase matching watch count, 'result'
616 for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) {
617
618 if (getChannelIndex(channelList->channels[chIdx].channel_id,
619 &channel_index) == kIOReturnSuccess) {
620 // IORLOG("reporter %p recognizes channel %lld", this, channelList->channels[chIdx].channel_id);
621
622 switch (action) {
623
624 case kIOReportEnable:
625 nChannels = (int*)result;
626 _enabled++;
627 _enableCounts[channel_index]++;
628 (*nChannels)++;
629 break;
630
631 case kIOReportDisable:
632 nChannels = (int*)result;
633 _enabled--;
634 _enableCounts[channel_index]--;
635 (*nChannels)++;
636 break;
637
638 case kIOReportGetDimensions:
639 nElements = (int *)result;
640 *nElements += _channelDimension;
641 break;
642
643 default:
644 IORLOG("ERROR configureReport unknown action!");
645 break;
646 }
647 }
648 }
649
650 // success
651 res = kIOReturnSuccess;
652
653finish:
654 return res;
655}
656
657
658IOReturn
659IOReporter::handleUpdateReport(IOReportChannelList *channelList,
660 IOReportConfigureAction action,
661 void *result,
662 void *destination)
663{
664 IOReturn res = kIOReturnError;
665 int *nElements = (int *)result;
666 int channel_index = 0;
667 uint32_t chIdx;
668 IOBufferMemoryDescriptor *dest;
669
670 if (!channelList || !result || !destination) goto finish;
671
672 dest = OSDynamicCast(IOBufferMemoryDescriptor, (OSObject *)destination);
673 if (dest == NULL) {
674 // Invalid destination
675 res = kIOReturnBadArgument;
676 goto finish;
677 }
678
679 if (!_enabled) {
680 goto finish;
681 }
682
683 for (chIdx = 0; chIdx < channelList->nchannels; chIdx++) {
684
685 if (getChannelIndex(channelList->channels[chIdx].channel_id,
686 &channel_index) == kIOReturnSuccess) {
687
688 //IORLOG("%s - found channel_id %llx @ index %d", __func__,
689 // channelList->channels[chIdx].channel_id,
690 // channel_index);
691
692 switch(action) {
693
694 case kIOReportCopyChannelData:
695 res = updateChannelValues(channel_index);
696 if (res) {
697 IORLOG("ERROR: updateChannelValues() failed: %x", res);
698 goto finish;
699 }
700
701 res = updateReportChannel(channel_index, nElements, dest);
702 if (res) {
703 IORLOG("ERROR: updateReportChannel() failed: %x", res);
704 goto finish;
705 }
706 break;
707
708 default:
709 IORLOG("ERROR updateReport unknown action!");
710 res = kIOReturnError;
711 goto finish;
712 }
713 }
714 }
715
716 // success
717 res = kIOReturnSuccess;
718
719finish:
720 return res;
721}
722
723
724IOReportLegendEntry*
725IOReporter::handleCreateLegend(void)
726{
727 IOReportLegendEntry *legendEntry = NULL;
728 OSArray *channelIDs;
729
730 channelIDs = copyChannelIDs();
731
732 if (channelIDs) {
733 legendEntry = IOReporter::legendWith(channelIDs, _channelNames, _channelType, _unit);
734 channelIDs->release();
735 }
736
737 return legendEntry;
738}
739
740
741IOReturn
742IOReporter::setElementValues(int element_index,
743 IOReportElementValues *values,
744 uint64_t record_time /* = 0 */)
745{
746 IOReturn res = kIOReturnError;
747
748 IOREPORTER_CHECK_LOCK();
749
750 if (record_time == 0) {
751 record_time = mach_absolute_time();
752 }
753
754 if (element_index >= _nElements || values == NULL) {
755 res = kIOReturnBadArgument;
756 goto finish;
757 }
758
759 memcpy(&_elements[element_index].values, values, sizeof(IOReportElementValues));
760
761 _elements[element_index].timestamp = record_time;
762
763 //IOREPORTER_DEBUG_ELEMENT(index);
764
765 res = kIOReturnSuccess;
766
767finish:
768 return res;
769}
770
771
772const IOReportElementValues*
773IOReporter::getElementValues(int element_index)
774{
775 IOReportElementValues *elementValues = NULL;
776
777 IOREPORTER_CHECK_LOCK();
778
779 if (element_index < 0 || element_index >= _nElements) {
780 IORLOG("ERROR getElementValues out of bounds!");
781 goto finish;
782 }
783
784 elementValues = &_elements[element_index].values;
785
786finish:
787 return elementValues;
788}
789
790
791IOReturn
792IOReporter::updateChannelValues(int channel_index)
793{
794 return kIOReturnSuccess;
795}
796
797
798IOReturn
799IOReporter::updateReportChannel(int channel_index,
800 int *nElements,
801 IOBufferMemoryDescriptor *destination)
802{
803 IOReturn res = kIOReturnError;
804 int start_element_idx, chElems;
805 size_t size2cpy;
806
807 res = kIOReturnBadArgument;
808 if (!nElements || !destination) {
809 goto finish;
810 }
811 if (channel_index > _nChannels) {
812 goto finish;
813 }
814
815 IOREPORTER_CHECK_LOCK();
816
817 res = kIOReturnOverrun;
818
819 start_element_idx = channel_index * _channelDimension;
820 if (start_element_idx >= _nElements) goto finish;
821
822 chElems = _elements[start_element_idx].channel_type.nelements;
823
824 // make sure we don't go beyond the end of _elements[_nElements-1]
825 if (start_element_idx + chElems > _nElements) {
826 goto finish;
827 }
828
829 PREFL_MEMOP_FAIL(chElems, IOReportElement);
830 size2cpy = (size_t)chElems * sizeof(IOReportElement);
831
832 // make sure there's space in the destination
833 if (size2cpy > (destination->getCapacity() - destination->getLength())) {
834 IORLOG("CRITICAL ERROR: Report Buffer Overflow (buffer cap %luB, length %luB, size2cpy %luB",
835 (unsigned long)destination->getCapacity(),
836 (unsigned long)destination->getLength(),
837 (unsigned long)size2cpy);
838 goto finish;
839 }
840
841 destination->appendBytes(&_elements[start_element_idx], size2cpy);
842 *nElements += chElems;
843
844 res = kIOReturnSuccess;
845
846finish:
847 return res;
848}
849
850
851IOReturn
852IOReporter::copyElementValues(int element_index,
853 IOReportElementValues *elementValues)
854{
855 IOReturn res = kIOReturnError;
856
857 if (!elementValues) goto finish;
858
859 IOREPORTER_CHECK_LOCK();
860
861 if (element_index >= _nElements) {
862 IORLOG("ERROR getElementValues out of bounds!");
863 res = kIOReturnBadArgument;
864 goto finish;
865 }
866
867 memcpy(elementValues, &_elements[element_index].values, sizeof(IOReportElementValues));
868 res = kIOReturnSuccess;
869
870finish:
871 return res;
872}
873
874
875IOReturn
876IOReporter::getFirstElementIndex(uint64_t channel_id,
877 int *index)
878{
879 IOReturn res = kIOReturnError;
880 int channel_index = 0, element_index = 0;
881
882 if (!index) goto finish;
883
884 res = getChannelIndices(channel_id, &channel_index, &element_index);
885
886 if (res == kIOReturnSuccess) {
887 *index = element_index;
888 }
889
890finish:
891 return res;
892}
893
894
895IOReturn
896IOReporter::getChannelIndex(uint64_t channel_id,
897 int *index)
898{
899 IOReturn res = kIOReturnError;
900 int channel_index = 0, element_index = 0;
901
902 if (!index) goto finish;
903
904 res = getChannelIndices(channel_id, &channel_index, &element_index);
905
906 if (res == kIOReturnSuccess) {
907 *index = channel_index;
908 }
909
910finish:
911 return res;
912}
913
914
915IOReturn
916IOReporter::getChannelIndices(uint64_t channel_id,
917 int *channel_index,
918 int *element_index)
919{
920 IOReturn res = kIOReturnNotFound;
921 int chIdx, elemIdx;
922
923 if (!channel_index || !element_index) goto finish;
924
925 for (chIdx = 0; chIdx < _nChannels; chIdx++) {
926
927 elemIdx = chIdx * _channelDimension;
928 if (elemIdx >= _nElements) {
929 IORLOG("ERROR getChannelIndices out of bounds!");
930 res = kIOReturnOverrun;
931 goto finish;
932 }
933
934 if (channel_id == _elements[elemIdx].channel_id) {
935
936 // The channel index does not care about the depth of elements...
937 *channel_index = chIdx;
938 *element_index = elemIdx;
939
940 res = kIOReturnSuccess;
941 goto finish;
942 }
943 }
944
945finish:
946 return res;
947}
948
949/********************************/
950/*** PRIVATE METHODS ***/
951/********************************/
952
953
954// copyChannelIDs relies on the caller to take lock
955OSArray*
956IOReporter::copyChannelIDs()
957{
958 int cnt, cnt2;
959 OSArray *channelIDs = NULL;
960 OSNumber *tmpNum;
961
962 channelIDs = OSArray::withCapacity((unsigned)_nChannels);
963
964 if (!channelIDs) goto finish;
965
966 for (cnt = 0; cnt < _nChannels; cnt++) {
967
968 cnt2 = cnt * _channelDimension;
969
970 // Encapsulate the Channel ID in OSNumber
971 tmpNum = OSNumber::withNumber(_elements[cnt2].channel_id, 64);
972 if (!tmpNum) {
973 IORLOG("ERROR: Could not create array of channelIDs");
974 channelIDs->release();
975 channelIDs = NULL;
976 goto finish;
977 }
978
979 channelIDs->setObject((unsigned)cnt, tmpNum);
980 tmpNum->release();
981 }
982
983finish:
984 return channelIDs;
985}
986
987
988// DO NOT REMOVE THIS METHOD WHICH IS THE MAIN LEGEND CREATION FUNCTION
989/*static */ IOReportLegendEntry*
990IOReporter::legendWith(OSArray *channelIDs,
991 OSArray *channelNames,
992 IOReportChannelType channelType,
993 IOReportUnit unit)
994{
995 unsigned int cnt, chCnt;
996 uint64_t type64;
997 OSNumber *tmpNum;
998 const OSSymbol *tmpSymbol;
999 OSArray *channelLegendArray = NULL, *tmpChannelArray = NULL;
1000 OSDictionary *channelInfoDict = NULL;
1001 IOReportLegendEntry *legendEntry = NULL;
1002
1003 // No need to check validity of channelNames because param is optional
1004 if (!channelIDs) goto finish;
1005 chCnt = channelIDs->getCount();
1006
1007 channelLegendArray = OSArray::withCapacity(chCnt);
1008
1009 for (cnt = 0; cnt < chCnt; cnt++) {
1010
1011 tmpChannelArray = OSArray::withCapacity(3);
1012
1013 // Encapsulate the Channel ID in OSNumber
1014 tmpChannelArray->setObject(kIOReportChannelIDIdx, channelIDs->getObject(cnt));
1015
1016 // Encapsulate the Channel Type in OSNumber
1017 memcpy(&type64, &channelType, sizeof(type64));
1018 tmpNum = OSNumber::withNumber(type64, 64);
1019 if (!tmpNum) {
1020 goto finish;
1021 }
1022 tmpChannelArray->setObject(kIOReportChannelTypeIdx, tmpNum);
1023 tmpNum->release();
1024
1025 // Encapsulate the Channel Name in OSSymbol
1026 // Use channelNames if provided
1027 if (channelNames != NULL) {
1028 tmpSymbol = OSDynamicCast(OSSymbol, channelNames->getObject(cnt));
1029 if (tmpSymbol && tmpSymbol != gIOReportNoChannelName) {
1030 tmpChannelArray->setObject(kIOReportChannelNameIdx, tmpSymbol);
1031 } // Else, skip and leave name field empty
1032 }
1033
1034 channelLegendArray->setObject(cnt, tmpChannelArray);
1035 tmpChannelArray->release();
1036 tmpChannelArray = NULL;
1037 }
1038
1039 // Stuff the legend entry only if we have channels...
1040 if (channelLegendArray->getCount() != 0) {
1041
1042 channelInfoDict = OSDictionary::withCapacity(1);
1043
1044 if (!channelInfoDict) {
1045 goto finish;
1046 }
1047
1048 tmpNum = OSNumber::withNumber(unit, 64);
1049 if (tmpNum) {
1050 channelInfoDict->setObject(kIOReportLegendUnitKey, tmpNum);
1051 tmpNum->release();
1052 }
1053
1054 legendEntry = OSDictionary::withCapacity(1);
1055
1056 if (legendEntry) {
1057 legendEntry->setObject(kIOReportLegendChannelsKey, channelLegendArray);
1058 legendEntry->setObject(kIOReportLegendInfoKey, channelInfoDict);
1059 }
1060 }
1061
1062finish:
1063 if (tmpChannelArray) tmpChannelArray->release();
1064 if (channelInfoDict) channelInfoDict->release();
1065 if (channelLegendArray) channelLegendArray->release();
1066
1067 return legendEntry;
1068}
1069