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 IOReportUnit 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 OSSafeReleaseNULL(reporter);
62 }
63
64 return rval;
65}
66
67bool
68IOStateReporter::initWith(IOService *reportingService,
69 IOReportCategories categories,
70 int16_t nstates,
71 IOReportUnit 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 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
799uint64_t
800IOStateReporter::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*/
822IOReturn
823IOStateReporter::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
887finish:
888 return result;
889}
890