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