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