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
34#define super IOReporter
35OSDefineMetaClassAndStructors(IOStateReporter, IOReporter);
36
37
38/* static */
39IOStateReporter*
40IOStateReporter::with(IOService *reportingService,
41                      IOReportCategories categories,
42                      int nstates,
43                      IOReportUnits unit/* = kIOReportUnitHWTicks*/)
44{
45    IOStateReporter *reporter, *rval = NULL;
46
47    // kprintf("%s\n", __func__);      // can't IORLOG() from static
48
49    reporter = new IOStateReporter;
50    if (!reporter)      goto finish;
51
52    if (!reporter->initWith(reportingService, categories, nstates, unit)) {
53        goto finish;
54    }
55
56    // success
57    rval = reporter;
58
59finish:
60    if (!rval) {
61        if (reporter)       delete reporter;
62    }
63
64    return rval;
65}
66
67bool
68IOStateReporter::initWith(IOService *reportingService,
69                          IOReportCategories categories,
70                          int16_t nstates,
71                          IOReportUnits unit)
72{
73    bool success = false;
74
75    IOReportChannelType channelType = {
76        .categories = categories,
77        .report_format = kIOReportFormatState,
78        .nelements = static_cast<uint16_t>(nstates),
79        .element_idx = 0
80    };
81
82    if(super::init(reportingService, channelType, unit) != true) {
83        IORLOG("ERROR super::initWith failed");
84        success = false;
85        goto finish;
86    }
87
88    _currentStates = NULL;
89    _lastUpdateTimes = NULL;
90
91    success = true;
92
93finish:
94    return success;
95}
96
97
98void
99IOStateReporter::free(void)
100{
101    if (_currentStates) {
102        PREFL_MEMOP_PANIC(_nChannels, int);
103        IOFree(_currentStates, (size_t)_nChannels * sizeof(int));
104    }
105    if (_lastUpdateTimes) {
106        PREFL_MEMOP_PANIC(_nChannels, uint64_t);
107        IOFree(_lastUpdateTimes, (size_t)_nChannels * sizeof(uint64_t));
108    }
109
110    super::free();
111}
112
113
114IOReturn
115IOStateReporter::handleSwapPrepare(int newNChannels)
116{
117    IOReturn res = kIOReturnError;
118    size_t newCurStatesSize, newTSSize;
119
120    //IORLOG("handleSwapPrepare (state) _nChannels before = %u", _nChannels);
121
122    IOREPORTER_CHECK_CONFIG_LOCK();
123
124    if (_swapCurrentStates || _swapLastUpdateTimes) {
125        panic("IOStateReporter::_swap* already in use");
126    }
127
128    // new currentStates buffer
129    PREFL_MEMOP_FAIL(newNChannels, int);
130    newCurStatesSize = (size_t)newNChannels * sizeof(int);
131    _swapCurrentStates = (int*)IOMalloc(newCurStatesSize);
132    if (_swapCurrentStates == NULL) {
133        res = kIOReturnNoMemory; goto finish;
134    }
135    memset(_swapCurrentStates, -1, newCurStatesSize);   // init w/"no state"
136
137    // new timestamps buffer
138    PREFL_MEMOP_FAIL(newNChannels, uint64_t);
139    newTSSize = (size_t)newNChannels * sizeof(uint64_t);
140    _swapLastUpdateTimes = (uint64_t *)IOMalloc(newTSSize);
141    if (_swapLastUpdateTimes == NULL) {
142        res = kIOReturnNoMemory; goto finish;
143    }
144    memset(_swapLastUpdateTimes, 0, newTSSize);
145
146    res = super::handleSwapPrepare(newNChannels);
147
148finish:
149    if (res) {
150        if (_swapCurrentStates) {
151            IOFree(_swapCurrentStates, newCurStatesSize);
152            _swapCurrentStates = NULL;
153        }
154        if (_swapLastUpdateTimes) {
155            IOFree(_swapLastUpdateTimes, newTSSize);
156            _swapLastUpdateTimes = NULL;
157        }
158    }
159
160    return res;
161}
162
163IOReturn
164IOStateReporter::handleAddChannelSwap(uint64_t channelID,
165                                      const OSSymbol *symChannelName)
166{
167    IOReturn res = kIOReturnError;
168    int cnt;
169    int *tmpCurStates;
170    uint64_t *tmpTimestamps;
171    bool swapComplete = false;
172
173    //IORLOG("IOStateReporter::handleSwap");
174
175    if (!_swapCurrentStates || !_swapLastUpdateTimes) {
176        IORLOG("IOReporter::handleSwap ERROR swap variables uninitialized!");
177        goto finish;
178    }
179
180    IOREPORTER_CHECK_CONFIG_LOCK();
181    IOREPORTER_CHECK_LOCK();
182
183    // Copy any existing buffers
184    if (_currentStates) {
185        PREFL_MEMOP_FAIL(_nChannels, int);
186        memcpy(_swapCurrentStates, _currentStates,
187               (size_t)_nChannels * sizeof(int));
188
189        if (!_lastUpdateTimes) {
190            panic("IOStateReporter::handleAddChannelSwap _lastUpdateTimes unset despite non-NULL _currentStates");
191        }
192        PREFL_MEMOP_FAIL(_nChannels, uint64_t);
193        memcpy(_swapLastUpdateTimes, _lastUpdateTimes,
194               (size_t)_nChannels * sizeof(uint64_t));
195    }
196
197    // Update principal instance variables, keep old values in _swap* for cleanup
198    tmpCurStates = _currentStates;
199    _currentStates = _swapCurrentStates;
200    _swapCurrentStates = tmpCurStates;
201
202    tmpTimestamps = _lastUpdateTimes;
203    _lastUpdateTimes = _swapLastUpdateTimes;
204    _swapLastUpdateTimes = tmpTimestamps;
205
206    swapComplete = true;
207
208    // subclass success
209
210    // invoke superclass(es): base class updates _nChannels & _nElements
211    res = super::handleAddChannelSwap(channelID, symChannelName);
212    if (res) {
213        IORLOG("handleSwap(state) ERROR super::handleSwap failed!");
214        goto finish;
215    }
216
217    // Channel added successfully, initialize the new channel's state_ids to 0..nStates-1
218    for (cnt = 0; cnt < _channelDimension; cnt++) {
219        handleSetStateID(channelID, cnt, (uint64_t)cnt);
220    }
221
222finish:
223    if (res && swapComplete) {
224        // unswap so the unused buffers get cleaned up
225        tmpCurStates = _currentStates;
226        _currentStates = _swapCurrentStates;
227        _swapCurrentStates = tmpCurStates;
228
229        tmpTimestamps = _lastUpdateTimes;
230        _lastUpdateTimes = _swapLastUpdateTimes;
231        _swapLastUpdateTimes = tmpTimestamps;
232    }
233
234    return res;
235}
236
237
238void
239IOStateReporter::handleSwapCleanup(int swapNChannels)
240{
241    IOREPORTER_CHECK_CONFIG_LOCK();
242
243    super::handleSwapCleanup(swapNChannels);
244
245    if (_swapCurrentStates) {
246        PREFL_MEMOP_PANIC(swapNChannels, int);
247        IOFree(_swapCurrentStates, (size_t)swapNChannels * sizeof(int));
248        _swapCurrentStates = NULL;
249    }
250    if (_swapLastUpdateTimes) {
251        PREFL_MEMOP_PANIC(swapNChannels, uint64_t);
252        IOFree(_swapLastUpdateTimes, (size_t)swapNChannels * sizeof(uint64_t));
253        _swapLastUpdateTimes = NULL;
254    }
255}
256
257
258IOReturn
259IOStateReporter::_getStateIndices(uint64_t channel_id,
260                                  uint64_t state_id,
261                                  int *channel_index,
262                                  int *state_index)
263{
264    IOReturn res = kIOReturnError;
265    int cnt;
266    IOStateReportValues *values;
267    int element_index = 0;
268
269    IOREPORTER_CHECK_LOCK();
270
271    if (getChannelIndices(channel_id,
272                          channel_index,
273                          &element_index) != kIOReturnSuccess) {
274        res = kIOReturnBadArgument;
275
276        goto finish;
277    }
278
279    for (cnt = 0; cnt < _channelDimension; cnt++) {
280
281        values = (IOStateReportValues *)getElementValues(element_index + cnt);
282
283        if (values == NULL) {
284
285            res = kIOReturnError;
286            goto finish;
287        }
288
289        if (values->state_id == state_id) {
290            *state_index = cnt;
291            res = kIOReturnSuccess;
292            goto finish;
293        }
294    }
295
296    res = kIOReturnBadArgument;
297
298finish:
299    return res;
300}
301
302
303IOReturn
304IOStateReporter::setChannelState(uint64_t channel_id,
305                                 uint64_t new_state_id)
306{
307    IOReturn res = kIOReturnError;
308    int channel_index, new_state_index;
309    uint64_t last_intransition = 0;
310    uint64_t prev_state_residency = 0;
311
312    lockReporter();
313
314    if (_getStateIndices(channel_id, new_state_id, &channel_index, &new_state_index) == kIOReturnSuccess) {
315        res = handleSetStateByIndices(channel_index, new_state_index,
316                                      last_intransition,
317                                      prev_state_residency);
318        goto finish;
319    }
320
321    res = kIOReturnBadArgument;
322
323finish:
324    unlockReporter();
325    return res;
326}
327
328IOReturn
329IOStateReporter::setChannelState(uint64_t channel_id,
330                                 uint64_t new_state_id,
331                                 uint64_t last_intransition,
332                                 uint64_t prev_state_residency)
333{
334    return setChannelState(channel_id, new_state_id);
335}
336
337IOReturn
338IOStateReporter::overrideChannelState(uint64_t channel_id,
339                                      uint64_t state_id,
340                                      uint64_t time_in_state,
341                                      uint64_t intransitions,
342                                      uint64_t last_intransition /*=0*/)
343{
344    IOReturn res = kIOReturnError;
345    int channel_index, state_index;
346
347    lockReporter();
348
349    if (_getStateIndices(channel_id, state_id, &channel_index, &state_index) == kIOReturnSuccess) {
350
351        if (_lastUpdateTimes[channel_index]) {
352            panic("overrideChannelState() cannot be used after setChannelState()!\n");
353        }
354
355        res = handleOverrideChannelStateByIndices(channel_index, state_index,
356                                                  time_in_state, intransitions,
357                                                  last_intransition);
358        goto finish;
359    }
360
361    res = kIOReturnBadArgument;
362
363finish:
364    unlockReporter();
365    return res;
366}
367
368
369IOReturn
370IOStateReporter::handleOverrideChannelStateByIndices(int channel_index,
371                                             int state_index,
372                                             uint64_t time_in_state,
373                                             uint64_t intransitions,
374                                             uint64_t last_intransition /*=0*/)
375{
376    IOReturn kerr, result = kIOReturnError;
377    IOStateReportValues state_values;
378    int element_index;
379
380    if (channel_index < 0 || channel_index >= _nChannels) {
381        result = kIOReturnBadArgument; goto finish;
382    }
383
384    if (channel_index < 0 || channel_index > (_nElements - state_index)
385                                             / _channelDimension) {
386        result = kIOReturnOverrun; goto finish;
387    }
388    element_index = channel_index * _channelDimension + state_index;
389
390    kerr = copyElementValues(element_index,(IOReportElementValues*)&state_values);
391    if (kerr) {
392        result = kerr; goto finish;
393    }
394
395    // last_intransition = 0 -> no current state ("residency summary only")
396    state_values.last_intransition = last_intransition;
397    state_values.intransitions = intransitions;
398    state_values.upticks = time_in_state;
399
400    // determines current time for metadata
401    kerr = setElementValues(element_index, (IOReportElementValues *)&state_values);
402    if (kerr) {
403        result = kerr; goto finish;
404    }
405
406    // success
407    result = kIOReturnSuccess;
408
409finish:
410    return result;
411}
412
413
414IOReturn
415IOStateReporter::incrementChannelState(uint64_t channel_id,
416                                       uint64_t state_id,
417                                       uint64_t time_in_state,
418                                       uint64_t intransitions,
419                                       uint64_t last_intransition /*=0*/)
420{
421    IOReturn res = kIOReturnError;
422    int channel_index, state_index;
423
424    lockReporter();
425
426    if (_getStateIndices(channel_id, state_id, &channel_index, &state_index) == kIOReturnSuccess) {
427
428        if (_lastUpdateTimes[channel_index]) {
429            panic("incrementChannelState() cannot be used after setChannelState()!\n");
430        }
431
432        res = handleIncrementChannelStateByIndices(channel_index, state_index,
433                                                   time_in_state, intransitions,
434                                                   last_intransition);
435        goto finish;
436    }
437
438    res = kIOReturnBadArgument;
439
440finish:
441    unlockReporter();
442    return res;
443
444}
445
446
447IOReturn
448IOStateReporter::handleIncrementChannelStateByIndices(int channel_index,
449                                                      int state_index,
450                                                      uint64_t time_in_state,
451                                                      uint64_t intransitions,
452                                                      uint64_t last_intransition /*=0*/)
453{
454    IOReturn kerr, result = kIOReturnError;
455    IOStateReportValues state_values;
456    int element_index;
457
458    if (channel_index < 0 || channel_index >= _nChannels) {
459        result = kIOReturnBadArgument; goto finish;
460    }
461
462    if (channel_index < 0 || channel_index > (_nElements - state_index)
463                                             / _channelDimension) {
464        result = kIOReturnOverrun; goto finish;
465    }
466    element_index = channel_index * _channelDimension + state_index;
467
468    kerr = copyElementValues(element_index,(IOReportElementValues*)&state_values);
469    if (kerr) {
470        result = kerr;
471        goto finish;
472    }
473
474    state_values.last_intransition = last_intransition;
475    state_values.intransitions += intransitions;
476    state_values.upticks += time_in_state;
477
478    // determines current time for metadata
479    kerr = setElementValues(element_index, (IOReportElementValues *)&state_values);
480    if (kerr) {
481        result = kerr;
482        goto finish;
483    }
484
485    // success
486    result = kIOReturnSuccess;
487
488finish:
489    return result;
490}
491
492
493IOReturn
494IOStateReporter::setState(uint64_t new_state_id)
495{
496    uint64_t last_intransition = 0;
497    uint64_t prev_state_residency = 0;
498    IOReturn res = kIOReturnError;
499    IOStateReportValues *values;
500    int channel_index = 0, element_index = 0, new_state_index = 0;
501    int cnt;
502
503    lockReporter();
504
505    if (_nChannels == 1) {
506
507        for (cnt = 0; cnt < _channelDimension; cnt++) {
508
509            new_state_index = element_index + cnt;
510
511            values = (IOStateReportValues *)getElementValues(new_state_index);
512
513            if (values == NULL) {
514                res = kIOReturnError;
515                goto finish;
516            }
517
518            if (values->state_id == new_state_id) {
519
520                res = handleSetStateByIndices(channel_index, new_state_index,
521                                              last_intransition,
522                                              prev_state_residency);
523                goto finish;
524            }
525        }
526    }
527
528    res = kIOReturnBadArgument;
529
530finish:
531    unlockReporter();
532    return res;
533}
534
535IOReturn
536IOStateReporter::setState(uint64_t new_state_id,
537                          uint64_t last_intransition,
538                          uint64_t prev_state_residency)
539{
540    return setState(new_state_id);
541}
542
543IOReturn
544IOStateReporter::setStateID(uint64_t channel_id,
545                            int state_index,
546                            uint64_t state_id)
547{
548    IOReturn res = kIOReturnError;
549
550    lockReporter();
551
552    res = handleSetStateID(channel_id, state_index, state_id);
553
554    unlockReporter();
555
556    return res;
557}
558
559
560IOReturn
561IOStateReporter::handleSetStateID(uint64_t channel_id,
562                                  int state_index,
563                                  uint64_t state_id)
564{
565    IOReturn res = kIOReturnError;
566    IOStateReportValues state_values;
567    int element_index = 0;
568
569    IOREPORTER_CHECK_LOCK();
570
571    if (getFirstElementIndex(channel_id, &element_index) == kIOReturnSuccess) {
572
573        if (state_index >= _channelDimension) {
574            res = kIOReturnBadArgument; goto finish;
575        }
576        if (_nElements - state_index <= element_index) {
577            res = kIOReturnOverrun; goto finish;
578        }
579        element_index += state_index;
580
581        if (copyElementValues(element_index, (IOReportElementValues *)&state_values) != kIOReturnSuccess) {
582            res = kIOReturnBadArgument;
583            goto finish;
584        }
585
586        state_values.state_id = state_id;
587
588        res = setElementValues(element_index, (IOReportElementValues *)&state_values);
589    }
590
591    // FIXME: set a bit somewhere (reporter-wide?) that state_ids can no longer be
592    // assumed to be contiguous
593finish:
594    return res;
595}
596
597IOReturn
598IOStateReporter::setStateByIndices(int channel_index,
599                                   int new_state_index)
600{
601    IOReturn res = kIOReturnError;
602    uint64_t last_intransition = 0;
603    uint64_t prev_state_residency = 0;
604
605    lockReporter();
606
607    res = handleSetStateByIndices(channel_index, new_state_index,
608                                  last_intransition, prev_state_residency);
609
610    unlockReporter();
611
612    return res;
613}
614
615IOReturn
616IOStateReporter::setStateByIndices(int channel_index,
617                                   int new_state_index,
618                                   uint64_t last_intransition,
619                                   uint64_t prev_state_residency)
620{
621    return setStateByIndices(channel_index, new_state_index);
622}
623
624IOReturn
625IOStateReporter::handleSetStateByIndices(int channel_index,
626                                         int new_state_index,
627                                         uint64_t last_intransition,
628                                         uint64_t prev_state_residency)
629{
630    IOReturn res = kIOReturnError;
631
632    IOStateReportValues curr_state_values, new_state_values;
633    int curr_state_index = 0;
634    int curr_element_index, new_element_index;
635    uint64_t last_ch_update_time = 0;
636    uint64_t recordTime = mach_absolute_time();
637
638    IOREPORTER_CHECK_LOCK();
639
640    if (channel_index < 0 || channel_index >= _nChannels) {
641        res = kIOReturnBadArgument; goto finish;
642    }
643
644    // if no timestamp provided, last_intransition = time of recording (now)
645    if (last_intransition == 0) {
646        last_intransition = recordTime;
647    }
648
649    // First update target state if different than the current state
650    // _currentStates[] initialized to -1 to detect first state transition
651    curr_state_index = _currentStates[channel_index];
652    if (new_state_index != curr_state_index) {
653        // fetch element data
654        if (channel_index < 0 || channel_index > (_nElements-new_state_index)
655                                                 / _channelDimension) {
656            res = kIOReturnOverrun; goto finish;
657        }
658        new_element_index = channel_index*_channelDimension + new_state_index;
659        if (copyElementValues(new_element_index,
660                              (IOReportElementValues *)&new_state_values)) {
661            res = kIOReturnBadArgument;
662            goto finish;
663        }
664
665        // Update new state's transition info
666        new_state_values.intransitions += 1;
667        new_state_values.last_intransition = last_intransition;
668
669        // and store the values
670        res = setElementValues(new_element_index,
671                               (IOReportElementValues *)&new_state_values,
672                               recordTime);
673
674        if (res != kIOReturnSuccess) {
675            goto finish;
676        }
677
678        _currentStates[channel_index] = new_state_index;
679    }
680
681    /* Now update time spent in any previous state
682       If new_state_index = curr_state_index, this updates time in the
683       current state.  If this is the channel's first state transition,
684       the last update time will be zero.
685
686       Note: While setState() should never be called on a channel being
687       updated with increment/overrideChannelState(), that's another way
688       that the last update time might not exist.  Regardless, if there
689       is no basis for determining time spent in previous state, there's
690       nothing to update!
691     */
692    last_ch_update_time = _lastUpdateTimes[channel_index];
693    if (last_ch_update_time != 0) {
694        if (channel_index < 0 || channel_index > (_nElements-curr_state_index)
695                                                 / _channelDimension) {
696            res = kIOReturnOverrun; goto finish;
697        }
698        curr_element_index = channel_index*_channelDimension + curr_state_index;
699        if (copyElementValues(curr_element_index,
700                              (IOReportElementValues *)&curr_state_values)) {
701            res = kIOReturnBadArgument;
702            goto finish;
703        }
704        // compute the time spent in previous state, unless provided
705        if (prev_state_residency == 0) {
706            prev_state_residency = last_intransition - last_ch_update_time;
707        }
708
709        curr_state_values.upticks += prev_state_residency;
710
711        res = setElementValues(curr_element_index,
712                               (IOReportElementValues*)&curr_state_values,
713                               recordTime);
714
715        if (res != kIOReturnSuccess) {
716            goto finish;
717        }
718    }
719
720    // record basis for next "time in prior state" calculation
721    // (also arms a panic in override/incrementChannelState())
722    _lastUpdateTimes[channel_index] = last_intransition;
723
724finish:
725    return res;
726}
727
728
729// blocks might make this slightly easier?
730uint64_t
731IOStateReporter::getStateInTransitions(uint64_t channel_id,
732                                       uint64_t state_id)
733{
734    return _getStateValue(channel_id, state_id, kInTransitions);
735}
736
737uint64_t
738IOStateReporter::getStateResidencyTime(uint64_t channel_id,
739                                       uint64_t state_id)
740{
741    return _getStateValue(channel_id, state_id, kResidencyTime);
742}
743
744uint64_t
745IOStateReporter::getStateLastTransitionTime(uint64_t channel_id,
746                                            uint64_t state_id)
747{
748    return _getStateValue(channel_id, state_id, kLastTransitionTime);
749}
750
751uint64_t
752IOStateReporter::_getStateValue(uint64_t channel_id,
753                                uint64_t state_id,
754                                enum valueSelector value)
755{
756    int channel_index = 0, element_index = 0, cnt;
757    IOStateReportValues *values = NULL;
758    uint64_t result = kIOReportInvalidValue;
759
760    lockReporter();
761
762    if (getChannelIndices(channel_id, &channel_index, &element_index) == kIOReturnSuccess) {
763
764        if (updateChannelValues(channel_index) == kIOReturnSuccess) {
765
766            for (cnt = 0; cnt < _channelDimension; cnt++) {
767
768                values = (IOStateReportValues *)getElementValues(element_index);
769
770                if (state_id == values->state_id) {
771
772                    switch (value) {
773                        case kInTransitions:
774                            result = values->intransitions;
775                            break;
776                        case kResidencyTime:
777                            result = values->upticks;
778                            break;
779                        case kLastTransitionTime:
780                            result = values->last_intransition;
781                        default:
782                            break;
783                    }
784
785                    break;
786                }
787
788                element_index++;
789            }
790        }
791    }
792
793    unlockReporter();
794    return result;
795}
796
797
798uint64_t
799IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id)
800{
801    int channel_index;
802    uint64_t result = kIOReportInvalidValue;
803
804    lockReporter();
805
806    if (getChannelIndex(channel_id, &channel_index) == kIOReturnSuccess) {
807
808        result = _lastUpdateTimes[channel_index];
809    }
810
811    unlockReporter();
812
813    return result;
814}
815
816
817/* updateChannelValues() is called to refresh state before being
818   reported outside the reporter.  In the case of IOStateReporter,
819   this is primarily an update to the "time in state" data.
820*/
821IOReturn
822IOStateReporter::updateChannelValues(int channel_index)
823{
824    IOReturn kerr, result = kIOReturnError;
825
826    int state_index, element_idx;
827    uint64_t currentTime;
828    uint64_t last_ch_update_time;
829    uint64_t time_in_state;
830    IOStateReportValues state_values;
831
832    IOREPORTER_CHECK_LOCK();
833
834    if (channel_index < 0 || channel_index >= _nChannels) {
835        result = kIOReturnBadArgument; goto finish;
836    }
837
838    /* First check to see whether this channel has begun self-
839       calculation of time in state.  It's possible this channel
840       has yet to be initialized or that the driver is updating
841       the channel with override/incrementChannelState() which
842       never enable automatic time-in-state updates.  In that case,
843       there is nothing to update and we return success.
844     */
845    last_ch_update_time = _lastUpdateTimes[channel_index];
846    if (last_ch_update_time == 0) {
847        result = kIOReturnSuccess; goto finish;
848    }
849
850    // figure out the current state (if any)
851    state_index = _currentStates[channel_index];
852
853    // e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15]
854    if (channel_index < 0 || channel_index > (_nElements - state_index)
855                                             / _channelDimension) {
856        result = kIOReturnOverrun; goto finish;
857    }
858    element_idx = channel_index * _channelDimension + state_index;
859
860    // get the current values
861    kerr = copyElementValues(element_idx,(IOReportElementValues*)&state_values);
862    if (kerr) {
863        result = kerr; goto finish;
864    }
865
866    // calculate time in state
867    currentTime = mach_absolute_time();
868    time_in_state = currentTime - last_ch_update_time;
869    state_values.upticks += time_in_state;
870
871    // and store the values
872    kerr = setElementValues(element_idx,
873                            (IOReportElementValues *)&state_values,
874                            currentTime);
875    if (kerr) {
876        result = kerr; goto finish;
877    }
878
879    // Record basis for next "prior time" calculation
880    _lastUpdateTimes[channel_index] = currentTime;
881
882
883    // success
884    result = kIOReturnSuccess;
885
886finish:
887    return result;
888}
889