1 /*
2  * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * The contents of this file constitute Original Code as defined in and
7  * are subject to the Apple Public Source License Version 1.1 (the
8  * "License").  You may not use this file except in compliance with the
9  * License.  Please obtain a copy of the License at
10  * http://www.apple.com/publicsource and read it before using this file.
11  *
12  * This Original Code and all software distributed under the License are
13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17  * License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * @APPLE_LICENSE_HEADER_END@
21  */
22 /*
23  * Copyright (c) 1999 Apple Computer, Inc.  All rights reserved.
24  *
25  * IOTimerEventSource.cpp
26  *
27  * HISTORY
28  * 2-Feb-1999		Joe Liu (jliu) created.
29  * 1999-10-14		Godfrey van der Linden(gvdl)
30  *		Revamped to use thread_call APIs
31  *
32  */
33 
34 #include <sys/cdefs.h>
35 
36 __BEGIN_DECLS
37 #include <kern/thread_call.h>
38 __END_DECLS
39 
40 #include <IOKit/assert.h>
41 #include <IOKit/system.h>
42 
43 #include <IOKit/IOLib.h>
44 #include <IOKit/IOTimerEventSource.h>
45 #include <IOKit/IOWorkLoop.h>
46 
47 #include <IOKit/IOTimeStamp.h>
48 
49 #define super IOEventSource
50 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
51 OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
52 OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
53 OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
54 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
55 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
56 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
58 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
59 
60 bool IOTimerEventSource::checkForWork() { return false; }
61 
62 // Timeout handler function. This function is called by the kernel when
63 // the timeout interval expires.
64 //
65 void IOTimerEventSource::timeout(void *self)
66 {
67     IOTimerEventSource *me = (IOTimerEventSource *) self;
68 
69     if (me->enabled) {
70         Action doit = (Action) me->action;
71 
72         if (doit) {
73             IOTimeStampConstant(IODBG_TIMES(IOTIMES_ACTION),
74                                 (unsigned int) doit, (unsigned int) me->owner);
75             me->closeGate();
76             (*doit)(me->owner, me);
77             me->openGate();
78         }
79     }
80 }
81 
82 void IOTimerEventSource::setTimeoutFunc()
83 {
84     calloutEntry = (void *) thread_call_allocate((thread_call_func_t) timeout,
85                                                  (thread_call_param_t) this);
86 }
87 
88 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
89 {
90     if (!super::init(inOwner, (IOEventSource::Action) inAction) )
91         return false;
92 
93     setTimeoutFunc();
94     if (!calloutEntry)
95         return false;
96 
97     return true;
98 }
99 
100 IOTimerEventSource *
101 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
102 {
103     IOTimerEventSource *me = new IOTimerEventSource;
104 
105     if (me && !me->init(inOwner, inAction)) {
106         me->free();
107         return 0;
108     }
109 
110     return me;
111 }
112 
113 void IOTimerEventSource::free()
114 {
115     if (calloutEntry) {
116         cancelTimeout();
117         thread_call_free((thread_call_t) calloutEntry);
118     }
119 
120     super::free();
121 }
122 
123 void IOTimerEventSource::cancelTimeout()
124 {
125     thread_call_cancel((thread_call_t) calloutEntry);
126     AbsoluteTime_to_scalar(&abstime) = 0;
127 }
128 
129 void IOTimerEventSource::enable()
130 {
131     super::enable();
132     if (kIOReturnSuccess != wakeAtTime(abstime))
133         super::disable(); // Problem re-scheduling timeout ignore enable
134 }
135 
136 void IOTimerEventSource::disable()
137 {
138     thread_call_cancel((thread_call_t) calloutEntry);
139     super::disable();
140 }
141 
142 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
143 {
144     return setTimeout(ticks, NSEC_PER_SEC/hz);
145 }
146 
147 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
148 {
149     return setTimeout(ms, kMillisecondScale);
150 }
151 
152 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
153 {
154     return setTimeout(us, kMicrosecondScale);
155 }
156 
157 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
158 {
159     AbsoluteTime end;
160 
161     clock_interval_to_deadline(interval, scale_factor, &end);
162     return wakeAtTime(end);
163 }
164 
165 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
166 {
167     AbsoluteTime end, nsecs;
168 
169     clock_interval_to_absolutetime_interval
170         (interval.tv_nsec, kNanosecondScale, &nsecs);
171     clock_interval_to_deadline
172         (interval.tv_sec, NSEC_PER_SEC, &end);
173     ADD_ABSOLUTETIME(&end, &nsecs);
174 
175     return wakeAtTime(end);
176 }
177 
178 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
179 {
180     AbsoluteTime end;
181 
182     clock_get_uptime(&end);
183     ADD_ABSOLUTETIME(&end, &interval);
184 
185     return wakeAtTime(end);
186 }
187 
188 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
189 {
190     return wakeAtTime(ticks, NSEC_PER_SEC/hz);
191 }
192 
193 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
194 {
195     return wakeAtTime(ms, kMillisecondScale);
196 }
197 
198 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
199 {
200     return wakeAtTime(us, kMicrosecondScale);
201 }
202 
203 IOReturn IOTimerEventSource::wakeAtTime(UInt32 abstime, UInt32 scale_factor)
204 {
205     AbsoluteTime end;
206     clock_interval_to_absolutetime_interval(abstime, scale_factor, &end);
207 
208     return wakeAtTime(end);
209 }
210 
211 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t abstime)
212 {
213     AbsoluteTime end, nsecs;
214 
215     clock_interval_to_absolutetime_interval
216         (abstime.tv_nsec, kNanosecondScale, &nsecs);
217     clock_interval_to_absolutetime_interval
218         (abstime.tv_sec, kSecondScale, &end);
219     ADD_ABSOLUTETIME(&end, &nsecs);
220 
221     return wakeAtTime(end);
222 }
223 
224 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
225 {
226     if (!action)
227         return kIOReturnNoResources;
228 
229     abstime = inAbstime;
230     if ( enabled && AbsoluteTime_to_scalar(&abstime) )
231         thread_call_enter_delayed((thread_call_t) calloutEntry, abstime);
232 
233     return kIOReturnSuccess;
234 }
235