1 /* 2 * Copyright (c) 2004,2007-2008 Apple 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 /* 30 * devtimer.c 31 * - timer source based on <kern/thread_call.h> 32 */ 33 34 /* 35 * Modification History: 36 * 37 * June 22, 2004 Dieter Siegmund ([email protected]) 38 * - created 39 */ 40 #include <sys/param.h> 41 #include <sys/kernel.h> 42 #include <sys/malloc.h> 43 #include <kern/thread_call.h> 44 #include <net/devtimer.h> 45 #include <libkern/OSAtomic.h> 46 47 #ifdef DEVTIMER_DEBUG 48 #define _devtimer_printf printf 49 #else /* !DEVTIMER_DEBUG */ 50 static __inline__ void 51 _devtimer_printf(__unused const char * fmt, ...) 52 { 53 } 54 #endif /* !DEVTIMER_DEBUG */ 55 56 struct devtimer_s { 57 void * dt_callout; 58 devtimer_timeout_func dt_timeout_func; 59 devtimer_process_func dt_process_func; 60 void * dt_arg0; 61 void * dt_arg1; 62 void * dt_arg2; 63 int dt_generation; 64 UInt32 dt_retain_count; 65 }; 66 67 #define M_DEVTIMER M_DEVBUF 68 69 static __inline__ void 70 timeval_add(struct timeval tv1, struct timeval tv2, 71 struct timeval * result) 72 { 73 result->tv_sec = tv1.tv_sec + tv2.tv_sec; 74 result->tv_usec = tv1.tv_usec + tv2.tv_usec; 75 if (result->tv_usec > DEVTIMER_USECS_PER_SEC) { 76 result->tv_usec -= DEVTIMER_USECS_PER_SEC; 77 result->tv_sec++; 78 } 79 return; 80 } 81 82 static __inline__ uint64_t 83 timeval_to_absolutetime(struct timeval tv) 84 { 85 uint64_t secs; 86 uint64_t usecs; 87 88 clock_interval_to_absolutetime_interval(tv.tv_sec, NSEC_PER_SEC, 89 &secs); 90 clock_interval_to_absolutetime_interval(tv.tv_usec, NSEC_PER_USEC, 91 &usecs); 92 return (secs + usecs); 93 } 94 95 96 __private_extern__ int 97 devtimer_valid(devtimer_ref timer) 98 { 99 return (timer->dt_callout != NULL); 100 } 101 102 __private_extern__ void 103 devtimer_retain(devtimer_ref timer) 104 { 105 OSIncrementAtomic(&timer->dt_retain_count); 106 return; 107 } 108 109 __private_extern__ void 110 devtimer_invalidate(devtimer_ref timer) 111 { 112 devtimer_cancel(timer); 113 timer->dt_arg0 = NULL; 114 if (timer->dt_callout != NULL) { 115 thread_call_free(timer->dt_callout); 116 timer->dt_callout = NULL; 117 } 118 return; 119 } 120 121 __private_extern__ void 122 devtimer_release(devtimer_ref timer) 123 { 124 UInt32 old_retain_count; 125 126 old_retain_count = OSDecrementAtomic(&timer->dt_retain_count); 127 switch (old_retain_count) { 128 case 0: 129 panic("devtimer_release: retain count is 0\n"); 130 break; 131 case 1: 132 devtimer_invalidate(timer); 133 FREE(timer, M_DEVTIMER); 134 _devtimer_printf("devtimer: timer released\n"); 135 break; 136 default: 137 break; 138 } 139 return; 140 } 141 142 static void 143 devtimer_process(void * param0, void * param1) 144 { 145 int generation = *(int*)param1; 146 devtimer_process_func process_func; 147 devtimer_timeout_func timeout_func; 148 devtimer_ref timer = (devtimer_ref)param0; 149 150 process_func = timer->dt_process_func; 151 if (process_func != NULL) { 152 (*process_func)(timer, devtimer_process_func_event_lock); 153 } 154 timeout_func = timer->dt_timeout_func; 155 if (timeout_func != NULL) { 156 timer->dt_timeout_func = NULL; 157 if (timer->dt_generation == generation) { 158 (*timeout_func)(timer->dt_arg0, timer->dt_arg1, timer->dt_arg2); 159 } 160 } 161 devtimer_release(timer); 162 if (process_func != NULL) { 163 (*process_func)(timer, devtimer_process_func_event_unlock); 164 } 165 return; 166 } 167 168 __private_extern__ void * 169 devtimer_arg0(devtimer_ref timer) 170 { 171 return (timer->dt_arg0); 172 } 173 174 __private_extern__ devtimer_ref 175 devtimer_create(devtimer_process_func process_func, void * arg0) 176 { 177 devtimer_ref timer; 178 179 timer = _MALLOC(sizeof(*timer), M_DEVTIMER, M_WAITOK); 180 if (timer == NULL) { 181 return (timer); 182 } 183 bzero(timer, sizeof(*timer)); 184 devtimer_retain(timer); 185 timer->dt_callout = thread_call_allocate(devtimer_process, timer); 186 if (timer->dt_callout == NULL) { 187 _devtimer_printf("devtimer: thread_call_allocate failed\n"); 188 devtimer_release(timer); 189 timer = NULL; 190 } 191 timer->dt_process_func = process_func; 192 timer->dt_arg0 = arg0; 193 return (timer); 194 } 195 196 __private_extern__ void 197 devtimer_set_absolute(devtimer_ref timer, 198 struct timeval abs_time, 199 devtimer_timeout_func timeout_func, 200 void * arg1, void * arg2) 201 { 202 if (timer->dt_callout == NULL) { 203 printf("devtimer_set_absolute: uninitialized/freed timer\n"); 204 return; 205 } 206 devtimer_cancel(timer); 207 if (timeout_func == NULL) { 208 return; 209 } 210 timer->dt_timeout_func = timeout_func; 211 timer->dt_arg1 = arg1; 212 timer->dt_arg2 = arg2; 213 _devtimer_printf("devtimer: wakeup time is (%d.%d)\n", 214 abs_time.tv_sec, abs_time.tv_usec); 215 timer->dt_generation++; 216 devtimer_retain(timer); 217 thread_call_enter1_delayed(timer->dt_callout, 218 &timer->dt_generation, 219 timeval_to_absolutetime(abs_time)); 220 return; 221 } 222 223 __private_extern__ void 224 devtimer_set_relative(devtimer_ref timer, 225 struct timeval rel_time, 226 devtimer_timeout_func timeout_func, 227 void * arg1, void * arg2) 228 { 229 struct timeval abs_time; 230 struct timeval current_time; 231 232 current_time = devtimer_current_time(); 233 timeval_add(current_time, rel_time, &abs_time); 234 devtimer_set_absolute(timer, abs_time, timeout_func, arg1, arg2); 235 return; 236 } 237 238 __private_extern__ void 239 devtimer_cancel(devtimer_ref timer) 240 { 241 if (timer->dt_timeout_func != NULL) { 242 timer->dt_timeout_func = NULL; 243 if (timer->dt_callout != NULL) { 244 _devtimer_printf("devtimer: cancelling timer source\n"); 245 if (thread_call_cancel(timer->dt_callout)) { 246 devtimer_release(timer); 247 } 248 else { 249 _devtimer_printf("devtimer: delayed release\n"); 250 } 251 } 252 } 253 return; 254 } 255 256 __private_extern__ int 257 devtimer_enabled(devtimer_ref timer) 258 { 259 return (timer->dt_timeout_func != NULL); 260 } 261 262 __private_extern__ int32_t 263 devtimer_current_secs(void) 264 { 265 struct timeval tv; 266 267 tv = devtimer_current_time(); 268 return (tv.tv_sec); 269 } 270 271 __private_extern__ struct timeval 272 devtimer_current_time(void) 273 { 274 struct timeval tv; 275 clock_sec_t sec; 276 clock_usec_t usec; 277 278 clock_get_system_microtime(&sec, &usec); 279 tv.tv_sec = sec; 280 tv.tv_usec = usec; 281 return (tv); 282 } 283