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 (dieter@apple.com) 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 */ 50static __inline__ void 51_devtimer_printf(__unused const char * fmt, ...) 52{ 53} 54#endif /* !DEVTIMER_DEBUG */ 55 56struct 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 69static __inline__ void 70timeval_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 82static __inline__ uint64_t 83timeval_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 97devtimer_valid(devtimer_ref timer) 98{ 99 return (timer->dt_callout != NULL); 100} 101 102__private_extern__ void 103devtimer_retain(devtimer_ref timer) 104{ 105 OSIncrementAtomic(&timer->dt_retain_count); 106 return; 107} 108 109__private_extern__ void 110devtimer_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 122devtimer_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 142static void 143devtimer_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 * 169devtimer_arg0(devtimer_ref timer) 170{ 171 return (timer->dt_arg0); 172} 173 174__private_extern__ devtimer_ref 175devtimer_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 197devtimer_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 224devtimer_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 239devtimer_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 257devtimer_enabled(devtimer_ref timer) 258{ 259 return (timer->dt_timeout_func != NULL); 260} 261 262__private_extern__ int32_t 263devtimer_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 272devtimer_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