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