1 /*
2  * Copyright (c) 1998-2000 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  * Copyright (c) 1999 Apple Computer, Inc.  All rights reserved.
30  *
31  * IOTimerEventSource.cpp
32  *
33  * HISTORY
34  * 2-Feb-1999		Joe Liu (jliu) created.
35  * 1999-10-14		Godfrey van der Linden(gvdl)
36  *		Revamped to use thread_call APIs
37  *
38  */
39 
40 #include <sys/cdefs.h>
41 
42 __BEGIN_DECLS
43 #include <kern/thread_call.h>
44 __END_DECLS
45 
46 #include <IOKit/assert.h>
47 #include <IOKit/system.h>
48 
49 #include <IOKit/IOLib.h>
50 #include <IOKit/IOTimerEventSource.h>
51 #include <IOKit/IOWorkLoop.h>
52 
53 #include <IOKit/IOTimeStamp.h>
54 
55 #define super IOEventSource
56 OSDefineMetaClassAndStructors(IOTimerEventSource, IOEventSource)
57 OSMetaClassDefineReservedUnused(IOTimerEventSource, 0);
58 OSMetaClassDefineReservedUnused(IOTimerEventSource, 1);
59 OSMetaClassDefineReservedUnused(IOTimerEventSource, 2);
60 OSMetaClassDefineReservedUnused(IOTimerEventSource, 3);
61 OSMetaClassDefineReservedUnused(IOTimerEventSource, 4);
62 OSMetaClassDefineReservedUnused(IOTimerEventSource, 5);
63 OSMetaClassDefineReservedUnused(IOTimerEventSource, 6);
64 OSMetaClassDefineReservedUnused(IOTimerEventSource, 7);
65 
66 //
67 // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
68 // not a subclassed implementation.
69 //
70 
71 bool IOTimerEventSource::checkForWork() { return false; }
72 
73 // Timeout handler function. This function is called by the kernel when
74 // the timeout interval expires.
75 //
76 void IOTimerEventSource::timeout(void *self)
77 {
78     IOTimerEventSource *me = (IOTimerEventSource *) self;
79 
80     if (me->enabled && me->action)
81     {
82         IOWorkLoop *
83         wl = me->workLoop;
84         if (wl)
85         {
86             Action doit;
87             wl->closeGate();
88             doit = (Action) me->action;
89             if (doit && me->enabled && AbsoluteTime_to_scalar(&me->abstime))
90             {
91                 IOTimeStampConstant(IODBG_TIMES(IOTIMES_ACTION),
92                                     (unsigned int) doit, (unsigned int) me->owner);
93                 (*doit)(me->owner, me);
94             }
95             wl->openGate();
96         }
97     }
98 }
99 
100 void IOTimerEventSource::timeoutAndRelease(void * self, void * count)
101 {
102     IOTimerEventSource *me = (IOTimerEventSource *) self;
103 
104     if (me->enabled && me->action)
105     {
106         IOWorkLoop *
107         wl = me->reserved->workLoop;
108         if (wl)
109         {
110             Action doit;
111             wl->closeGate();
112             doit = (Action) me->action;
113             if (doit && (me->reserved->calloutGeneration == (SInt32) count))
114             {
115                 IOTimeStampConstant(IODBG_TIMES(IOTIMES_ACTION),
116                                     (unsigned int) doit, (unsigned int) me->owner);
117                 (*doit)(me->owner, me);
118             }
119             wl->openGate();
120         }
121     }
122 
123     me->reserved->workLoop->release();
124     me->release();
125 }
126 
127 void IOTimerEventSource::setTimeoutFunc()
128 {
129     // reserved != 0 means IOTimerEventSource::timeoutAndRelease is being used,
130     // not a subclassed implementation
131     reserved = IONew(ExpansionData, 1);
132     calloutEntry = (void *) thread_call_allocate((thread_call_func_t) &IOTimerEventSource::timeoutAndRelease,
133                                                  (thread_call_param_t) this);
134 }
135 
136 bool IOTimerEventSource::init(OSObject *inOwner, Action inAction)
137 {
138     if (!super::init(inOwner, (IOEventSource::Action) inAction) )
139         return false;
140 
141     setTimeoutFunc();
142     if (!calloutEntry)
143         return false;
144 
145     return true;
146 }
147 
148 IOTimerEventSource *
149 IOTimerEventSource::timerEventSource(OSObject *inOwner, Action inAction)
150 {
151     IOTimerEventSource *me = new IOTimerEventSource;
152 
153     if (me && !me->init(inOwner, inAction)) {
154         me->release();
155         return 0;
156     }
157 
158     return me;
159 }
160 
161 void IOTimerEventSource::free()
162 {
163     if (calloutEntry) {
164         cancelTimeout();
165         thread_call_free((thread_call_t) calloutEntry);
166     }
167 
168     if (reserved)
169         IODelete(reserved, ExpansionData, 1);
170 
171     super::free();
172 }
173 
174 void IOTimerEventSource::cancelTimeout()
175 {
176     if (reserved)
177         reserved->calloutGeneration++;
178     bool active = thread_call_cancel((thread_call_t) calloutEntry);
179     AbsoluteTime_to_scalar(&abstime) = 0;
180     if (active && reserved)
181     {
182         release();
183         workLoop->release();
184     }
185 }
186 
187 void IOTimerEventSource::enable()
188 {
189     super::enable();
190     if (kIOReturnSuccess != wakeAtTime(abstime))
191         super::disable(); // Problem re-scheduling timeout ignore enable
192 }
193 
194 void IOTimerEventSource::disable()
195 {
196     if (reserved)
197         reserved->calloutGeneration++;
198     bool active = thread_call_cancel((thread_call_t) calloutEntry);
199     super::disable();
200     if (active && reserved)
201     {
202         release();
203         workLoop->release();
204     }
205 }
206 
207 IOReturn IOTimerEventSource::setTimeoutTicks(UInt32 ticks)
208 {
209     return setTimeout(ticks, kTickScale);
210 }
211 
212 IOReturn IOTimerEventSource::setTimeoutMS(UInt32 ms)
213 {
214     return setTimeout(ms, kMillisecondScale);
215 }
216 
217 IOReturn IOTimerEventSource::setTimeoutUS(UInt32 us)
218 {
219     return setTimeout(us, kMicrosecondScale);
220 }
221 
222 IOReturn IOTimerEventSource::setTimeout(UInt32 interval, UInt32 scale_factor)
223 {
224     AbsoluteTime end;
225 
226     clock_interval_to_deadline(interval, scale_factor, &end);
227     return wakeAtTime(end);
228 }
229 
230 IOReturn IOTimerEventSource::setTimeout(mach_timespec_t interval)
231 {
232     AbsoluteTime end, nsecs;
233 
234     clock_interval_to_absolutetime_interval
235         (interval.tv_nsec, kNanosecondScale, &nsecs);
236     clock_interval_to_deadline
237         (interval.tv_sec, NSEC_PER_SEC, &end);
238     ADD_ABSOLUTETIME(&end, &nsecs);
239 
240     return wakeAtTime(end);
241 }
242 
243 IOReturn IOTimerEventSource::setTimeout(AbsoluteTime interval)
244 {
245     AbsoluteTime end;
246 
247     clock_get_uptime(&end);
248     ADD_ABSOLUTETIME(&end, &interval);
249 
250     return wakeAtTime(end);
251 }
252 
253 IOReturn IOTimerEventSource::wakeAtTimeTicks(UInt32 ticks)
254 {
255     return wakeAtTime(ticks, kTickScale);
256 }
257 
258 IOReturn IOTimerEventSource::wakeAtTimeMS(UInt32 ms)
259 {
260     return wakeAtTime(ms, kMillisecondScale);
261 }
262 
263 IOReturn IOTimerEventSource::wakeAtTimeUS(UInt32 us)
264 {
265     return wakeAtTime(us, kMicrosecondScale);
266 }
267 
268 IOReturn IOTimerEventSource::wakeAtTime(UInt32 inAbstime, UInt32 scale_factor)
269 {
270     AbsoluteTime end;
271     clock_interval_to_absolutetime_interval(inAbstime, scale_factor, &end);
272 
273     return wakeAtTime(end);
274 }
275 
276 IOReturn IOTimerEventSource::wakeAtTime(mach_timespec_t inAbstime)
277 {
278     AbsoluteTime end, nsecs;
279 
280     clock_interval_to_absolutetime_interval
281         (inAbstime.tv_nsec, kNanosecondScale, &nsecs);
282     clock_interval_to_absolutetime_interval
283         (inAbstime.tv_sec, kSecondScale, &end);
284     ADD_ABSOLUTETIME(&end, &nsecs);
285 
286     return wakeAtTime(end);
287 }
288 
289 void IOTimerEventSource::setWorkLoop(IOWorkLoop *inWorkLoop)
290 {
291     super::setWorkLoop(inWorkLoop);
292     if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
293         wakeAtTime(abstime);
294 }
295 
296 IOReturn IOTimerEventSource::wakeAtTime(AbsoluteTime inAbstime)
297 {
298     if (!action)
299         return kIOReturnNoResources;
300 
301     abstime = inAbstime;
302     if ( enabled && AbsoluteTime_to_scalar(&abstime) && workLoop )
303     {
304         if (reserved)
305         {
306             retain();
307             workLoop->retain();
308             reserved->workLoop = workLoop;
309             reserved->calloutGeneration++;
310             if (thread_call_enter1_delayed((thread_call_t) calloutEntry,
311                     (void *) reserved->calloutGeneration, abstime))
312             {
313                 release();
314                 workLoop->release();
315             }
316         }
317         else
318             thread_call_enter_delayed((thread_call_t) calloutEntry, abstime);
319     }
320 
321     return kIOReturnSuccess;
322 }
323