2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
34 * DEPRECATED INTERFACES - Should be removed
36 * Purpose: Routines for the creation and use of kernel
37 * alarm clock services. This file and the ipc
38 * routines in kern/ipc_clock.c constitute the
39 * machine-independent clock service layer.
42 #include <mach/mach_types.h>
44 #include <kern/lock.h>
45 #include <kern/host.h>
47 #include <kern/sched_prim.h>
48 #include <kern/thread.h>
49 #include <kern/ipc_host.h>
50 #include <kern/clock.h>
51 #include <kern/zalloc.h>
53 #include <ipc/ipc_types.h>
54 #include <ipc/ipc_port.h>
56 #include <mach/mach_traps.h>
57 #include <mach/mach_time.h>
59 #include <mach/clock_server.h>
60 #include <mach/clock_reply.h>
61 #include <mach/clock_priv_server.h>
63 #include <mach/mach_host_server.h>
64 #include <mach/host_priv_server.h>
67 * Actual clock alarm structure. Used for user clock_sleep() and
68 * clock_alarm() calls. Alarms are allocated from the alarm free
69 * list and entered in time priority order into the active alarm
70 * chain of the target clock.
73 struct alarm
*al_next
; /* next alarm in chain */
74 struct alarm
*al_prev
; /* previous alarm in chain */
75 int al_status
; /* alarm status */
76 mach_timespec_t al_time
; /* alarm time */
77 struct { /* message alarm data */
78 int type
; /* alarm type */
79 ipc_port_t port
; /* alarm port */
81 port_type
; /* alarm port type */
82 struct clock
*clock
; /* alarm clock */
83 void *data
; /* alarm data */
85 #define al_type al_alrm.type
86 #define al_port al_alrm.port
87 #define al_port_type al_alrm.port_type
88 #define al_clock al_alrm.clock
89 #define al_data al_alrm.data
90 long al_seqno
; /* alarm sequence number */
92 typedef struct alarm alarm_data_t
;
95 #define ALARM_FREE 0 /* alarm is on free list */
96 #define ALARM_SLEEP 1 /* active clock_sleep() */
97 #define ALARM_CLOCK 2 /* active clock_alarm() */
98 #define ALARM_DONE 4 /* alarm has expired */
100 /* local data declarations */
101 decl_simple_lock_data(static,alarm_lock
) /* alarm synchronization */
102 static struct zone
*alarm_zone
; /* zone for user alarms */
103 static struct alarm
*alrmfree
; /* alarm free list pointer */
104 static struct alarm
*alrmdone
; /* alarm done list pointer */
105 static struct alarm
*alrmlist
;
106 static long alrm_seqno
; /* uniquely identifies alarms */
107 static thread_call_data_t alarm_done_call
;
108 static timer_call_data_t alarm_expire_timer
;
110 extern struct clock clock_list
[];
111 extern int clock_count
;
113 static void post_alarm(
116 static void set_alarm(
117 mach_timespec_t
*alarm_time
);
119 static int check_time(
120 alarm_type_t alarm_type
,
121 mach_timespec_t
*alarm_time
,
122 mach_timespec_t
*clock_time
);
124 static void alarm_done(void);
126 static void alarm_expire(void);
128 static kern_return_t
clock_sleep_internal(
130 sleep_type_t sleep_type
,
131 mach_timespec_t
*sleep_time
);
133 int rtclock_config(void);
135 int rtclock_init(void);
137 kern_return_t
rtclock_gettime(
138 mach_timespec_t
*cur_time
);
140 kern_return_t
rtclock_getattr(
141 clock_flavor_t flavor
,
143 mach_msg_type_number_t
*count
);
145 struct clock_ops sysclk_ops
= {
146 rtclock_config
, rtclock_init
,
151 kern_return_t
calend_gettime(
152 mach_timespec_t
*cur_time
);
154 kern_return_t
calend_getattr(
155 clock_flavor_t flavor
,
157 mach_msg_type_number_t
*count
);
159 struct clock_ops calend_ops
= {
166 * Macros to lock/unlock clock system.
168 #define LOCK_ALARM(s) \
170 simple_lock(&alarm_lock);
172 #define UNLOCK_ALARM(s) \
173 simple_unlock(&alarm_lock); \
177 clock_oldconfig(void)
182 simple_lock_init(&alarm_lock
, 0);
183 thread_call_setup(&alarm_done_call
, (thread_call_func_t
)alarm_done
, NULL
);
184 timer_call_setup(&alarm_expire_timer
, (timer_call_func_t
)alarm_expire
, NULL
);
187 * Configure clock devices.
189 for (i
= 0; i
< clock_count
; i
++) {
190 clock
= &clock_list
[i
];
191 if (clock
->cl_ops
&& clock
->cl_ops
->c_config
) {
192 if ((*clock
->cl_ops
->c_config
)() == 0)
197 /* start alarm sequence numbers at 0 */
208 * Initialize basic clock structures.
210 for (i
= 0; i
< clock_count
; i
++) {
211 clock
= &clock_list
[i
];
212 if (clock
->cl_ops
&& clock
->cl_ops
->c_init
)
213 (*clock
->cl_ops
->c_init
)();
218 * Initialize the clock ipc service facility.
221 clock_service_create(void)
227 * Initialize ipc clock services.
229 for (i
= 0; i
< clock_count
; i
++) {
230 clock
= &clock_list
[i
];
232 ipc_clock_init(clock
);
233 ipc_clock_enable(clock
);
238 * Perform miscellaneous late
241 i
= sizeof(struct alarm
);
242 alarm_zone
= zinit(i
, (4096/i
)*i
, 10*i
, "alarms");
246 * Get the service port on a clock.
249 host_get_clock_service(
252 clock_t *clock
) /* OUT */
254 if (host
== HOST_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
256 return (KERN_INVALID_ARGUMENT
);
259 *clock
= &clock_list
[clock_id
];
260 if ((*clock
)->cl_ops
== 0)
261 return (KERN_FAILURE
);
262 return (KERN_SUCCESS
);
266 * Get the control port on a clock.
269 host_get_clock_control(
270 host_priv_t host_priv
,
272 clock_t *clock
) /* OUT */
274 if (host_priv
== HOST_PRIV_NULL
||
275 clock_id
< 0 || clock_id
>= clock_count
) {
277 return (KERN_INVALID_ARGUMENT
);
280 *clock
= &clock_list
[clock_id
];
281 if ((*clock
)->cl_ops
== 0)
282 return (KERN_FAILURE
);
283 return (KERN_SUCCESS
);
287 * Get the current clock time.
292 mach_timespec_t
*cur_time
) /* OUT */
294 if (clock
== CLOCK_NULL
)
295 return (KERN_INVALID_ARGUMENT
);
296 return ((*clock
->cl_ops
->c_gettime
)(cur_time
));
301 mach_timespec_t
*time
) /* OUT */
303 clock_get_system_nanotime(&time
->tv_sec
, (uint32_t *)&time
->tv_nsec
);
305 return (KERN_SUCCESS
);
310 mach_timespec_t
*time
) /* OUT */
312 clock_get_calendar_nanotime(&time
->tv_sec
, (uint32_t *)&time
->tv_nsec
);
314 return (KERN_SUCCESS
);
318 * Get clock attributes.
321 clock_get_attributes(
323 clock_flavor_t flavor
,
324 clock_attr_t attr
, /* OUT */
325 mach_msg_type_number_t
*count
) /* IN/OUT */
327 if (clock
== CLOCK_NULL
)
328 return (KERN_INVALID_ARGUMENT
);
329 if (clock
->cl_ops
->c_getattr
)
330 return (clock
->cl_ops
->c_getattr(flavor
, attr
, count
));
331 return (KERN_FAILURE
);
336 clock_flavor_t flavor
,
337 clock_attr_t attr
, /* OUT */
338 mach_msg_type_number_t
*count
) /* IN/OUT */
341 return (KERN_FAILURE
);
345 case CLOCK_GET_TIME_RES
: /* >0 res */
346 case CLOCK_ALARM_CURRES
: /* =0 no alarm */
347 case CLOCK_ALARM_MINRES
:
348 case CLOCK_ALARM_MAXRES
:
349 *(clock_res_t
*) attr
= NSEC_PER_SEC
/ 100;
353 return (KERN_INVALID_VALUE
);
356 return (KERN_SUCCESS
);
361 clock_flavor_t flavor
,
362 clock_attr_t attr
, /* OUT */
363 mach_msg_type_number_t
*count
) /* IN/OUT */
366 return (KERN_FAILURE
);
370 case CLOCK_GET_TIME_RES
: /* >0 res */
371 *(clock_res_t
*) attr
= NSEC_PER_SEC
/ 100;
374 case CLOCK_ALARM_CURRES
: /* =0 no alarm */
375 case CLOCK_ALARM_MINRES
:
376 case CLOCK_ALARM_MAXRES
:
377 *(clock_res_t
*) attr
= 0;
381 return (KERN_INVALID_VALUE
);
384 return (KERN_SUCCESS
);
388 * Set the current clock time.
393 __unused mach_timespec_t new_time
)
395 if (clock
== CLOCK_NULL
)
396 return (KERN_INVALID_ARGUMENT
);
397 return (KERN_FAILURE
);
401 * Set the clock alarm resolution.
404 clock_set_attributes(
406 __unused clock_flavor_t flavor
,
407 __unused clock_attr_t attr
,
408 __unused mach_msg_type_number_t count
)
410 if (clock
== CLOCK_NULL
)
411 return (KERN_INVALID_ARGUMENT
);
412 return (KERN_FAILURE
);
416 * Setup a clock alarm.
421 alarm_type_t alarm_type
,
422 mach_timespec_t alarm_time
,
423 ipc_port_t alarm_port
,
424 mach_msg_type_name_t alarm_port_type
)
427 mach_timespec_t clock_time
;
429 kern_return_t reply_code
;
432 if (clock
== CLOCK_NULL
)
433 return (KERN_INVALID_ARGUMENT
);
434 if (clock
!= &clock_list
[SYSTEM_CLOCK
])
435 return (KERN_FAILURE
);
436 if (IP_VALID(alarm_port
) == 0)
437 return (KERN_INVALID_CAPABILITY
);
440 * Check alarm parameters. If parameters are invalid,
441 * send alarm message immediately.
443 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
444 chkstat
= check_time(alarm_type
, &alarm_time
, &clock_time
);
446 reply_code
= (chkstat
< 0 ? KERN_INVALID_VALUE
: KERN_SUCCESS
);
447 clock_alarm_reply(alarm_port
, alarm_port_type
,
448 reply_code
, alarm_type
, clock_time
);
449 return (KERN_SUCCESS
);
453 * Get alarm and add to clock alarm list.
457 if ((alarm
= alrmfree
) == 0) {
459 alarm
= (alarm_t
) zalloc(alarm_zone
);
461 return (KERN_RESOURCE_SHORTAGE
);
465 alrmfree
= alarm
->al_next
;
467 alarm
->al_status
= ALARM_CLOCK
;
468 alarm
->al_time
= alarm_time
;
469 alarm
->al_type
= alarm_type
;
470 alarm
->al_port
= alarm_port
;
471 alarm
->al_port_type
= alarm_port_type
;
472 alarm
->al_clock
= clock
;
473 alarm
->al_seqno
= alrm_seqno
++;
477 return (KERN_SUCCESS
);
481 * Sleep on a clock. System trap. User-level libmach clock_sleep
482 * interface call takes a mach_timespec_t sleep_time argument which it
483 * converts to sleep_sec and sleep_nsec arguments which are then
484 * passed to clock_sleep_trap.
488 struct clock_sleep_trap_args
*args
)
490 mach_port_name_t clock_name
= args
->clock_name
;
491 sleep_type_t sleep_type
= args
->sleep_type
;
492 int sleep_sec
= args
->sleep_sec
;
493 int sleep_nsec
= args
->sleep_nsec
;
494 mach_vm_address_t wakeup_time_addr
= args
->wakeup_time
;
496 mach_timespec_t swtime
;
497 kern_return_t rvalue
;
500 * Convert the trap parameters.
502 if (clock_name
== MACH_PORT_NULL
)
503 clock
= &clock_list
[SYSTEM_CLOCK
];
505 clock
= port_name_to_clock(clock_name
);
507 swtime
.tv_sec
= sleep_sec
;
508 swtime
.tv_nsec
= sleep_nsec
;
511 * Call the actual clock_sleep routine.
513 rvalue
= clock_sleep_internal(clock
, sleep_type
, &swtime
);
516 * Return current time as wakeup time.
518 if (rvalue
!= KERN_INVALID_ARGUMENT
&& rvalue
!= KERN_FAILURE
) {
519 copyout((char *)&swtime
, wakeup_time_addr
, sizeof(mach_timespec_t
));
525 clock_sleep_internal(
527 sleep_type_t sleep_type
,
528 mach_timespec_t
*sleep_time
)
531 mach_timespec_t clock_time
;
532 kern_return_t rvalue
;
536 if (clock
== CLOCK_NULL
)
537 return (KERN_INVALID_ARGUMENT
);
539 if (clock
!= &clock_list
[SYSTEM_CLOCK
])
540 return (KERN_FAILURE
);
543 * Check sleep parameters. If parameters are invalid
544 * return an error, otherwise post alarm request.
546 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
548 chkstat
= check_time(sleep_type
, sleep_time
, &clock_time
);
550 return (KERN_INVALID_VALUE
);
551 rvalue
= KERN_SUCCESS
;
553 wait_result_t wait_result
;
556 * Get alarm and add to clock alarm list.
560 if ((alarm
= alrmfree
) == 0) {
562 alarm
= (alarm_t
) zalloc(alarm_zone
);
564 return (KERN_RESOURCE_SHORTAGE
);
568 alrmfree
= alarm
->al_next
;
571 * Wait for alarm to occur.
573 wait_result
= assert_wait((event_t
)alarm
, THREAD_ABORTSAFE
);
574 if (wait_result
== THREAD_WAITING
) {
575 alarm
->al_time
= *sleep_time
;
576 alarm
->al_status
= ALARM_SLEEP
;
580 wait_result
= thread_block(THREAD_CONTINUE_NULL
);
583 * Note if alarm expired normally or whether it
584 * was aborted. If aborted, delete alarm from
585 * clock alarm list. Return alarm to free list.
588 if (alarm
->al_status
!= ALARM_DONE
) {
589 assert(wait_result
!= THREAD_AWAKENED
);
590 if (((alarm
->al_prev
)->al_next
= alarm
->al_next
) != NULL
)
591 (alarm
->al_next
)->al_prev
= alarm
->al_prev
;
592 rvalue
= KERN_ABORTED
;
594 *sleep_time
= alarm
->al_time
;
595 alarm
->al_status
= ALARM_FREE
;
597 assert(wait_result
== THREAD_INTERRUPTED
);
598 assert(alarm
->al_status
== ALARM_FREE
);
599 rvalue
= KERN_ABORTED
;
601 alarm
->al_next
= alrmfree
;
606 *sleep_time
= clock_time
;
612 * Service clock alarm expirations.
618 register alarm_t alrm1
;
619 register alarm_t alrm2
;
620 mach_timespec_t clock_time
;
621 mach_timespec_t
*alarm_time
;
624 clock
= &clock_list
[SYSTEM_CLOCK
];
625 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
628 * Update clock alarm list. Alarms that are due are moved
629 * to the alarmdone list to be serviced by a thread callout.
632 alrm1
= (alarm_t
)&alrmlist
;
633 while ((alrm2
= alrm1
->al_next
) != NULL
) {
634 alarm_time
= &alrm2
->al_time
;
635 if (CMP_MACH_TIMESPEC(alarm_time
, &clock_time
) > 0)
639 * Alarm has expired, so remove it from the
642 if ((alrm1
->al_next
= alrm2
->al_next
) != NULL
)
643 (alrm1
->al_next
)->al_prev
= alrm1
;
646 * If a clock_sleep() alarm, wakeup the thread
647 * which issued the clock_sleep() call.
649 if (alrm2
->al_status
== ALARM_SLEEP
) {
651 alrm2
->al_status
= ALARM_DONE
;
652 alrm2
->al_time
= clock_time
;
653 thread_wakeup((event_t
)alrm2
);
657 * If a clock_alarm() alarm, place the alarm on
658 * the alarm done list and schedule the alarm
659 * delivery mechanism.
662 assert(alrm2
->al_status
== ALARM_CLOCK
);
663 if ((alrm2
->al_next
= alrmdone
) != NULL
)
664 alrmdone
->al_prev
= alrm2
;
666 thread_call_enter(&alarm_done_call
);
667 alrm2
->al_prev
= (alarm_t
)&alrmdone
;
669 alrm2
->al_status
= ALARM_DONE
;
670 alrm2
->al_time
= clock_time
;
675 * Setup to expire for the next pending alarm.
678 set_alarm(alarm_time
);
685 register alarm_t alrm
;
690 while ((alrm
= alrmdone
) != NULL
) {
691 if ((alrmdone
= alrm
->al_next
) != NULL
)
692 alrmdone
->al_prev
= (alarm_t
)&alrmdone
;
695 code
= (alrm
->al_status
== ALARM_DONE
? KERN_SUCCESS
: KERN_ABORTED
);
696 if (alrm
->al_port
!= IP_NULL
) {
697 /* Deliver message to designated port */
698 if (IP_VALID(alrm
->al_port
)) {
699 clock_alarm_reply(alrm
->al_port
, alrm
->al_port_type
, code
,
700 alrm
->al_type
, alrm
->al_time
);
704 alrm
->al_status
= ALARM_FREE
;
705 alrm
->al_next
= alrmfree
;
709 panic("clock_alarm_deliver");
716 * Post an alarm on the active alarm list.
718 * Always called from within a LOCK_ALARM() code section.
724 register alarm_t alrm1
, alrm2
;
725 mach_timespec_t
*alarm_time
;
726 mach_timespec_t
*queue_time
;
729 * Traverse alarm list until queue time is greater
730 * than alarm time, then insert alarm.
732 alarm_time
= &alarm
->al_time
;
733 alrm1
= (alarm_t
)&alrmlist
;
734 while ((alrm2
= alrm1
->al_next
) != NULL
) {
735 queue_time
= &alrm2
->al_time
;
736 if (CMP_MACH_TIMESPEC(queue_time
, alarm_time
) > 0)
740 alrm1
->al_next
= alarm
;
741 alarm
->al_next
= alrm2
;
742 alarm
->al_prev
= alrm1
;
744 alrm2
->al_prev
= alarm
;
747 * If the inserted alarm is the 'earliest' alarm,
748 * reset the device layer alarm time accordingly.
750 if (alrmlist
== alarm
)
751 set_alarm(alarm_time
);
756 mach_timespec_t
*alarm_time
)
760 nanotime_to_absolutetime(alarm_time
->tv_sec
, alarm_time
->tv_nsec
, &abstime
);
761 timer_call_enter(&alarm_expire_timer
, abstime
);
765 * Check the validity of 'alarm_time' and 'alarm_type'. If either
766 * argument is invalid, return a negative value. If the 'alarm_time'
767 * is now, return a 0 value. If the 'alarm_time' is in the future,
768 * return a positive value.
772 alarm_type_t alarm_type
,
773 mach_timespec_t
*alarm_time
,
774 mach_timespec_t
*clock_time
)
778 if (BAD_ALRMTYPE(alarm_type
))
780 if (BAD_MACH_TIMESPEC(alarm_time
))
782 if ((alarm_type
& ALRMTYPE
) == TIME_RELATIVE
)
783 ADD_MACH_TIMESPEC(alarm_time
, clock_time
);
785 result
= CMP_MACH_TIMESPEC(alarm_time
, clock_time
);
787 return ((result
>= 0)? result
: 0);
791 clock_get_system_value(void)
793 clock_t clock
= &clock_list
[SYSTEM_CLOCK
];
794 mach_timespec_t value
;
796 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
802 clock_get_calendar_value(void)
804 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
805 mach_timespec_t value
= MACH_TIMESPEC_ZERO
;
807 (void) (*clock
->cl_ops
->c_gettime
)(&value
);