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
35 OSDefineMetaClassAndStructors(IOStateReporter, IOReporter);
36 
37 
38 /* static */
39 IOStateReporter*
40 IOStateReporter::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 
59 finish:
60     if (!rval) {
61         OSSafeReleaseNULL(reporter);
62     }
63 
64     return rval;
65 }
66 
67 bool
68 IOStateReporter::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 
93 finish:
94     return success;
95 }
96 
97 
98 void
99 IOStateReporter::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 
114 IOReturn
115 IOStateReporter::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 
148 finish:
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 
163 IOReturn
164 IOStateReporter::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 
222 finish:
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 
238 void
239 IOStateReporter::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 
258 IOReturn
259 IOStateReporter::_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 
298 finish:
299     return res;
300 }
301 
302 
303 IOReturn
304 IOStateReporter::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 
323 finish:
324     unlockReporter();
325     return res;
326 }
327 
328 IOReturn
329 IOStateReporter::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 
337 IOReturn
338 IOStateReporter::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 
363 finish:
364     unlockReporter();
365     return res;
366 }
367 
368 
369 IOReturn
370 IOStateReporter::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 
409 finish:
410     return result;
411 }
412 
413 
414 IOReturn
415 IOStateReporter::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 
440 finish:
441     unlockReporter();
442     return res;
443 
444 }
445 
446 
447 IOReturn
448 IOStateReporter::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 
488 finish:
489     return result;
490 }
491 
492 
493 IOReturn
494 IOStateReporter::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 
530 finish:
531     unlockReporter();
532     return res;
533 }
534 
535 IOReturn
536 IOStateReporter::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 
543 IOReturn
544 IOStateReporter::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 
560 IOReturn
561 IOStateReporter::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
593 finish:
594     return res;
595 }
596 
597 IOReturn
598 IOStateReporter::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 
615 IOReturn
616 IOStateReporter::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 
624 IOReturn
625 IOStateReporter::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 
724 finish:
725     return res;
726 }
727 
728 
729 // blocks might make this slightly easier?
730 uint64_t
731 IOStateReporter::getStateInTransitions(uint64_t channel_id,
732                                        uint64_t state_id)
733 {
734     return _getStateValue(channel_id, state_id, kInTransitions);
735 }
736 
737 uint64_t
738 IOStateReporter::getStateResidencyTime(uint64_t channel_id,
739                                        uint64_t state_id)
740 {
741     return _getStateValue(channel_id, state_id, kResidencyTime);
742 }
743 
744 uint64_t
745 IOStateReporter::getStateLastTransitionTime(uint64_t channel_id,
746                                             uint64_t state_id)
747 {
748     return _getStateValue(channel_id, state_id, kLastTransitionTime);
749 }
750 
751 uint64_t
752 IOStateReporter::_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                             break;
782                         default:
783                             break;
784                     }
785 
786                     break;
787                 }
788 
789                 element_index++;
790             }
791         }
792     }
793 
794     unlockReporter();
795     return result;
796 }
797 
798 
799 uint64_t
800 IOStateReporter::getStateLastChannelUpdateTime(uint64_t channel_id)
801 {
802     int channel_index;
803     uint64_t result = kIOReportInvalidValue;
804 
805     lockReporter();
806 
807     if (getChannelIndex(channel_id, &channel_index) == kIOReturnSuccess) {
808 
809         result = _lastUpdateTimes[channel_index];
810     }
811 
812     unlockReporter();
813 
814     return result;
815 }
816 
817 
818 /* updateChannelValues() is called to refresh state before being
819    reported outside the reporter.  In the case of IOStateReporter,
820    this is primarily an update to the "time in state" data.
821 */
822 IOReturn
823 IOStateReporter::updateChannelValues(int channel_index)
824 {
825     IOReturn kerr, result = kIOReturnError;
826 
827     int state_index, element_idx;
828     uint64_t currentTime;
829     uint64_t last_ch_update_time;
830     uint64_t time_in_state;
831     IOStateReportValues state_values;
832 
833     IOREPORTER_CHECK_LOCK();
834 
835     if (channel_index < 0 || channel_index >= _nChannels) {
836         result = kIOReturnBadArgument; goto finish;
837     }
838 
839     /* First check to see whether this channel has begun self-
840        calculation of time in state.  It's possible this channel
841        has yet to be initialized or that the driver is updating
842        the channel with override/incrementChannelState() which
843        never enable automatic time-in-state updates.  In that case,
844        there is nothing to update and we return success.
845      */
846     last_ch_update_time = _lastUpdateTimes[channel_index];
847     if (last_ch_update_time == 0) {
848         result = kIOReturnSuccess; goto finish;
849     }
850 
851     // figure out the current state (if any)
852     state_index = _currentStates[channel_index];
853 
854     // e.g. given 4 4-state channels, the boundary is ch[3].st[3] <- _elems[15]
855     if (channel_index < 0 || channel_index > (_nElements - state_index)
856                                              / _channelDimension) {
857         result = kIOReturnOverrun; goto finish;
858     }
859     element_idx = channel_index * _channelDimension + state_index;
860 
861     // get the current values
862     kerr = copyElementValues(element_idx,(IOReportElementValues*)&state_values);
863     if (kerr) {
864         result = kerr; goto finish;
865     }
866 
867     // calculate time in state
868     currentTime = mach_absolute_time();
869     time_in_state = currentTime - last_ch_update_time;
870     state_values.upticks += time_in_state;
871 
872     // and store the values
873     kerr = setElementValues(element_idx,
874                             (IOReportElementValues *)&state_values,
875                             currentTime);
876     if (kerr) {
877         result = kerr; goto finish;
878     }
879 
880     // Record basis for next "prior time" calculation
881     _lastUpdateTimes[channel_index] = currentTime;
882 
883 
884     // success
885     result = kIOReturnSuccess;
886 
887 finish:
888     return result;
889 }
890