2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * DEPRECATED INTERFACES - Should be removed
34 * Purpose: Routines for the creation and use of kernel
35 * alarm clock services. This file and the ipc
36 * routines in kern/ipc_clock.c constitute the
37 * machine-independent clock service layer.
40 #include <mach/mach_types.h>
42 #include <kern/host.h>
44 #include <kern/sched_prim.h>
45 #include <kern/thread.h>
46 #include <kern/ipc_host.h>
47 #include <kern/clock.h>
48 #include <kern/zalloc.h>
50 #include <ipc/ipc_types.h>
51 #include <ipc/ipc_port.h>
53 #include <mach/mach_traps.h>
54 #include <mach/mach_time.h>
56 #include <mach/clock_server.h>
57 #include <mach/clock_reply.h>
58 #include <mach/clock_priv_server.h>
60 #include <mach/mach_host_server.h>
61 #include <mach/host_priv_server.h>
62 #include <libkern/section_keywords.h>
65 * Actual clock alarm structure. Used for user clock_sleep() and
66 * clock_alarm() calls. Alarms are allocated from the alarm free
67 * list and entered in time priority order into the active alarm
68 * chain of the target clock.
71 struct alarm
*al_next
; /* next alarm in chain */
72 struct alarm
*al_prev
; /* previous alarm in chain */
73 int al_status
; /* alarm status */
74 mach_timespec_t al_time
; /* alarm time */
75 struct { /* message alarm data */
76 int type
; /* alarm type */
77 ipc_port_t port
; /* alarm port */
79 port_type
; /* alarm port type */
80 struct clock
*clock
; /* alarm clock */
81 void *data
; /* alarm data */
83 #define al_type al_alrm.type
84 #define al_port al_alrm.port
85 #define al_port_type al_alrm.port_type
86 #define al_clock al_alrm.clock
87 #define al_data al_alrm.data
88 long al_seqno
; /* alarm sequence number */
90 typedef struct alarm alarm_data_t
;
93 #define ALARM_FREE 0 /* alarm is on free list */
94 #define ALARM_SLEEP 1 /* active clock_sleep() */
95 #define ALARM_CLOCK 2 /* active clock_alarm() */
96 #define ALARM_DONE 4 /* alarm has expired */
98 /* local data declarations */
99 decl_simple_lock_data(static,alarm_lock
) /* alarm synchronization */
100 static struct zone
*alarm_zone
; /* zone for user alarms */
101 static struct alarm
*alrmfree
; /* alarm free list pointer */
102 static struct alarm
*alrmdone
; /* alarm done list pointer */
103 static struct alarm
*alrmlist
;
104 static long alrm_seqno
; /* uniquely identifies alarms */
105 static thread_call_data_t alarm_done_call
;
106 static timer_call_data_t alarm_expire_timer
;
108 extern struct clock clock_list
[];
109 extern int clock_count
;
111 static void post_alarm(
114 static void set_alarm(
115 mach_timespec_t
*alarm_time
);
117 static int check_time(
118 alarm_type_t alarm_type
,
119 mach_timespec_t
*alarm_time
,
120 mach_timespec_t
*clock_time
);
122 static void alarm_done(void);
124 static void alarm_expire(void);
126 static kern_return_t
clock_sleep_internal(
128 sleep_type_t sleep_type
,
129 mach_timespec_t
*sleep_time
);
131 int rtclock_init(void);
133 kern_return_t
rtclock_gettime(
134 mach_timespec_t
*cur_time
);
136 kern_return_t
rtclock_getattr(
137 clock_flavor_t flavor
,
139 mach_msg_type_number_t
*count
);
141 SECURITY_READ_ONLY_EARLY(struct clock_ops
) sysclk_ops
= {
147 kern_return_t
calend_gettime(
148 mach_timespec_t
*cur_time
);
150 kern_return_t
calend_getattr(
151 clock_flavor_t flavor
,
153 mach_msg_type_number_t
*count
);
155 SECURITY_READ_ONLY_EARLY(struct clock_ops
) calend_ops
= {
162 * List of clock devices.
164 SECURITY_READ_ONLY_LATE(struct clock
) clock_list
[] = {
167 { &sysclk_ops
, 0, 0 },
170 { &calend_ops
, 0, 0 }
172 int clock_count
= sizeof(clock_list
) / sizeof(clock_list
[0]);
175 * Macros to lock/unlock clock system.
177 #define LOCK_ALARM(s) \
179 simple_lock(&alarm_lock);
181 #define UNLOCK_ALARM(s) \
182 simple_unlock(&alarm_lock); \
186 clock_oldconfig(void)
191 simple_lock_init(&alarm_lock
, 0);
192 thread_call_setup(&alarm_done_call
, (thread_call_func_t
)alarm_done
, NULL
);
193 timer_call_setup(&alarm_expire_timer
, (timer_call_func_t
)alarm_expire
, NULL
);
196 * Configure clock devices.
198 for (i
= 0; i
< clock_count
; i
++) {
199 clock
= &clock_list
[i
];
200 if (clock
->cl_ops
&& clock
->cl_ops
->c_config
) {
201 if ((*clock
->cl_ops
->c_config
)() == 0)
202 clock
->cl_ops
= NULL
;
206 /* start alarm sequence numbers at 0 */
217 * Initialize basic clock structures.
219 for (i
= 0; i
< clock_count
; i
++) {
220 clock
= &clock_list
[i
];
221 if (clock
->cl_ops
&& clock
->cl_ops
->c_init
)
222 (*clock
->cl_ops
->c_init
)();
227 * Initialize the clock ipc service facility.
230 clock_service_create(void)
236 * Initialize ipc clock services.
238 for (i
= 0; i
< clock_count
; i
++) {
239 clock
= &clock_list
[i
];
241 ipc_clock_init(clock
);
242 ipc_clock_enable(clock
);
247 * Perform miscellaneous late
250 i
= sizeof(struct alarm
);
251 alarm_zone
= zinit(i
, (4096/i
)*i
, 10*i
, "alarms");
255 * Get the service port on a clock.
258 host_get_clock_service(
261 clock_t *clock
) /* OUT */
263 if (host
== HOST_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
265 return (KERN_INVALID_ARGUMENT
);
268 *clock
= &clock_list
[clock_id
];
269 if ((*clock
)->cl_ops
== 0)
270 return (KERN_FAILURE
);
271 return (KERN_SUCCESS
);
275 * Get the control port on a clock.
278 host_get_clock_control(
279 host_priv_t host_priv
,
281 clock_t *clock
) /* OUT */
283 if (host_priv
== HOST_PRIV_NULL
||
284 clock_id
< 0 || clock_id
>= clock_count
) {
286 return (KERN_INVALID_ARGUMENT
);
289 *clock
= &clock_list
[clock_id
];
290 if ((*clock
)->cl_ops
== 0)
291 return (KERN_FAILURE
);
292 return (KERN_SUCCESS
);
296 * Get the current clock time.
301 mach_timespec_t
*cur_time
) /* OUT */
303 if (clock
== CLOCK_NULL
)
304 return (KERN_INVALID_ARGUMENT
);
305 return ((*clock
->cl_ops
->c_gettime
)(cur_time
));
310 mach_timespec_t
*time
) /* OUT */
315 clock_get_system_nanotime(&secs
, &nsecs
);
316 time
->tv_sec
= (unsigned int)secs
;
317 time
->tv_nsec
= nsecs
;
319 return (KERN_SUCCESS
);
324 mach_timespec_t
*time
) /* OUT */
329 clock_get_calendar_nanotime(&secs
, &nsecs
);
330 time
->tv_sec
= (unsigned int)secs
;
331 time
->tv_nsec
= nsecs
;
333 return (KERN_SUCCESS
);
337 * Get clock attributes.
340 clock_get_attributes(
342 clock_flavor_t flavor
,
343 clock_attr_t attr
, /* OUT */
344 mach_msg_type_number_t
*count
) /* IN/OUT */
346 if (clock
== CLOCK_NULL
)
347 return (KERN_INVALID_ARGUMENT
);
348 if (clock
->cl_ops
->c_getattr
)
349 return (clock
->cl_ops
->c_getattr(flavor
, attr
, count
));
350 return (KERN_FAILURE
);
355 clock_flavor_t flavor
,
356 clock_attr_t attr
, /* OUT */
357 mach_msg_type_number_t
*count
) /* IN/OUT */
360 return (KERN_FAILURE
);
364 case CLOCK_GET_TIME_RES
: /* >0 res */
365 case CLOCK_ALARM_CURRES
: /* =0 no alarm */
366 case CLOCK_ALARM_MINRES
:
367 case CLOCK_ALARM_MAXRES
:
368 *(clock_res_t
*) attr
= NSEC_PER_SEC
/ 100;
372 return (KERN_INVALID_VALUE
);
375 return (KERN_SUCCESS
);
380 clock_flavor_t flavor
,
381 clock_attr_t attr
, /* OUT */
382 mach_msg_type_number_t
*count
) /* IN/OUT */
385 return (KERN_FAILURE
);
389 case CLOCK_GET_TIME_RES
: /* >0 res */
390 *(clock_res_t
*) attr
= NSEC_PER_SEC
/ 100;
393 case CLOCK_ALARM_CURRES
: /* =0 no alarm */
394 case CLOCK_ALARM_MINRES
:
395 case CLOCK_ALARM_MAXRES
:
396 *(clock_res_t
*) attr
= 0;
400 return (KERN_INVALID_VALUE
);
403 return (KERN_SUCCESS
);
407 * Set the current clock time.
412 __unused mach_timespec_t new_time
)
414 if (clock
== CLOCK_NULL
)
415 return (KERN_INVALID_ARGUMENT
);
416 return (KERN_FAILURE
);
420 * Set the clock alarm resolution.
423 clock_set_attributes(
425 __unused clock_flavor_t flavor
,
426 __unused clock_attr_t attr
,
427 __unused mach_msg_type_number_t count
)
429 if (clock
== CLOCK_NULL
)
430 return (KERN_INVALID_ARGUMENT
);
431 return (KERN_FAILURE
);
435 * Setup a clock alarm.
440 alarm_type_t alarm_type
,
441 mach_timespec_t alarm_time
,
442 ipc_port_t alarm_port
,
443 mach_msg_type_name_t alarm_port_type
)
446 mach_timespec_t clock_time
;
448 kern_return_t reply_code
;
451 if (clock
== CLOCK_NULL
)
452 return (KERN_INVALID_ARGUMENT
);
453 if (clock
!= &clock_list
[SYSTEM_CLOCK
])
454 return (KERN_FAILURE
);
455 if (IP_VALID(alarm_port
) == 0)
456 return (KERN_INVALID_CAPABILITY
);
459 * Check alarm parameters. If parameters are invalid,
460 * send alarm message immediately.
462 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
463 chkstat
= check_time(alarm_type
, &alarm_time
, &clock_time
);
465 reply_code
= (chkstat
< 0 ? KERN_INVALID_VALUE
: KERN_SUCCESS
);
466 clock_alarm_reply(alarm_port
, alarm_port_type
,
467 reply_code
, alarm_type
, clock_time
);
468 return (KERN_SUCCESS
);
472 * Get alarm and add to clock alarm list.
476 if ((alarm
= alrmfree
) == 0) {
478 alarm
= (alarm_t
) zalloc(alarm_zone
);
480 return (KERN_RESOURCE_SHORTAGE
);
484 alrmfree
= alarm
->al_next
;
486 alarm
->al_status
= ALARM_CLOCK
;
487 alarm
->al_time
= alarm_time
;
488 alarm
->al_type
= alarm_type
;
489 alarm
->al_port
= alarm_port
;
490 alarm
->al_port_type
= alarm_port_type
;
491 alarm
->al_clock
= clock
;
492 alarm
->al_seqno
= alrm_seqno
++;
496 return (KERN_SUCCESS
);
500 * Sleep on a clock. System trap. User-level libmach clock_sleep
501 * interface call takes a mach_timespec_t sleep_time argument which it
502 * converts to sleep_sec and sleep_nsec arguments which are then
503 * passed to clock_sleep_trap.
507 struct clock_sleep_trap_args
*args
)
509 mach_port_name_t clock_name
= args
->clock_name
;
510 sleep_type_t sleep_type
= args
->sleep_type
;
511 int sleep_sec
= args
->sleep_sec
;
512 int sleep_nsec
= args
->sleep_nsec
;
513 mach_vm_address_t wakeup_time_addr
= args
->wakeup_time
;
515 mach_timespec_t swtime
= {};
516 kern_return_t rvalue
;
519 * Convert the trap parameters.
521 if (clock_name
== MACH_PORT_NULL
)
522 clock
= &clock_list
[SYSTEM_CLOCK
];
524 clock
= port_name_to_clock(clock_name
);
526 swtime
.tv_sec
= sleep_sec
;
527 swtime
.tv_nsec
= sleep_nsec
;
530 * Call the actual clock_sleep routine.
532 rvalue
= clock_sleep_internal(clock
, sleep_type
, &swtime
);
535 * Return current time as wakeup time.
537 if (rvalue
!= KERN_INVALID_ARGUMENT
&& rvalue
!= KERN_FAILURE
) {
538 copyout((char *)&swtime
, wakeup_time_addr
, sizeof(mach_timespec_t
));
544 clock_sleep_internal(
546 sleep_type_t sleep_type
,
547 mach_timespec_t
*sleep_time
)
550 mach_timespec_t clock_time
;
551 kern_return_t rvalue
;
555 if (clock
== CLOCK_NULL
)
556 return (KERN_INVALID_ARGUMENT
);
558 if (clock
!= &clock_list
[SYSTEM_CLOCK
])
559 return (KERN_FAILURE
);
562 * Check sleep parameters. If parameters are invalid
563 * return an error, otherwise post alarm request.
565 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
567 chkstat
= check_time(sleep_type
, sleep_time
, &clock_time
);
569 return (KERN_INVALID_VALUE
);
570 rvalue
= KERN_SUCCESS
;
572 wait_result_t wait_result
;
575 * Get alarm and add to clock alarm list.
579 if ((alarm
= alrmfree
) == 0) {
581 alarm
= (alarm_t
) zalloc(alarm_zone
);
583 return (KERN_RESOURCE_SHORTAGE
);
587 alrmfree
= alarm
->al_next
;
590 * Wait for alarm to occur.
592 wait_result
= assert_wait((event_t
)alarm
, THREAD_ABORTSAFE
);
593 if (wait_result
== THREAD_WAITING
) {
594 alarm
->al_time
= *sleep_time
;
595 alarm
->al_status
= ALARM_SLEEP
;
599 wait_result
= thread_block(THREAD_CONTINUE_NULL
);
602 * Note if alarm expired normally or whether it
603 * was aborted. If aborted, delete alarm from
604 * clock alarm list. Return alarm to free list.
607 if (alarm
->al_status
!= ALARM_DONE
) {
608 assert(wait_result
!= THREAD_AWAKENED
);
609 if (((alarm
->al_prev
)->al_next
= alarm
->al_next
) != NULL
)
610 (alarm
->al_next
)->al_prev
= alarm
->al_prev
;
611 rvalue
= KERN_ABORTED
;
613 *sleep_time
= alarm
->al_time
;
614 alarm
->al_status
= ALARM_FREE
;
616 assert(wait_result
== THREAD_INTERRUPTED
);
617 assert(alarm
->al_status
== ALARM_FREE
);
618 rvalue
= KERN_ABORTED
;
620 alarm
->al_next
= alrmfree
;
625 *sleep_time
= clock_time
;
631 * Service clock alarm expirations.
639 mach_timespec_t clock_time
;
640 mach_timespec_t
*alarm_time
;
643 clock
= &clock_list
[SYSTEM_CLOCK
];
644 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
647 * Update clock alarm list. Alarms that are due are moved
648 * to the alarmdone list to be serviced by a thread callout.
651 alrm1
= (alarm_t
)&alrmlist
;
652 while ((alrm2
= alrm1
->al_next
) != NULL
) {
653 alarm_time
= &alrm2
->al_time
;
654 if (CMP_MACH_TIMESPEC(alarm_time
, &clock_time
) > 0)
658 * Alarm has expired, so remove it from the
661 if ((alrm1
->al_next
= alrm2
->al_next
) != NULL
)
662 (alrm1
->al_next
)->al_prev
= alrm1
;
665 * If a clock_sleep() alarm, wakeup the thread
666 * which issued the clock_sleep() call.
668 if (alrm2
->al_status
== ALARM_SLEEP
) {
669 alrm2
->al_next
= NULL
;
670 alrm2
->al_status
= ALARM_DONE
;
671 alrm2
->al_time
= clock_time
;
672 thread_wakeup((event_t
)alrm2
);
676 * If a clock_alarm() alarm, place the alarm on
677 * the alarm done list and schedule the alarm
678 * delivery mechanism.
681 assert(alrm2
->al_status
== ALARM_CLOCK
);
682 if ((alrm2
->al_next
= alrmdone
) != NULL
)
683 alrmdone
->al_prev
= alrm2
;
685 thread_call_enter(&alarm_done_call
);
686 alrm2
->al_prev
= (alarm_t
)&alrmdone
;
688 alrm2
->al_status
= ALARM_DONE
;
689 alrm2
->al_time
= clock_time
;
694 * Setup to expire for the next pending alarm.
697 set_alarm(alarm_time
);
709 while ((alrm
= alrmdone
) != NULL
) {
710 if ((alrmdone
= alrm
->al_next
) != NULL
)
711 alrmdone
->al_prev
= (alarm_t
)&alrmdone
;
714 code
= (alrm
->al_status
== ALARM_DONE
? KERN_SUCCESS
: KERN_ABORTED
);
715 if (alrm
->al_port
!= IP_NULL
) {
716 /* Deliver message to designated port */
717 if (IP_VALID(alrm
->al_port
)) {
718 clock_alarm_reply(alrm
->al_port
, alrm
->al_port_type
, code
,
719 alrm
->al_type
, alrm
->al_time
);
723 alrm
->al_status
= ALARM_FREE
;
724 alrm
->al_next
= alrmfree
;
728 panic("clock_alarm_deliver");
735 * Post an alarm on the active alarm list.
737 * Always called from within a LOCK_ALARM() code section.
743 alarm_t alrm1
, alrm2
;
744 mach_timespec_t
*alarm_time
;
745 mach_timespec_t
*queue_time
;
748 * Traverse alarm list until queue time is greater
749 * than alarm time, then insert alarm.
751 alarm_time
= &alarm
->al_time
;
752 alrm1
= (alarm_t
)&alrmlist
;
753 while ((alrm2
= alrm1
->al_next
) != NULL
) {
754 queue_time
= &alrm2
->al_time
;
755 if (CMP_MACH_TIMESPEC(queue_time
, alarm_time
) > 0)
759 alrm1
->al_next
= alarm
;
760 alarm
->al_next
= alrm2
;
761 alarm
->al_prev
= alrm1
;
763 alrm2
->al_prev
= alarm
;
766 * If the inserted alarm is the 'earliest' alarm,
767 * reset the device layer alarm time accordingly.
769 if (alrmlist
== alarm
)
770 set_alarm(alarm_time
);
775 mach_timespec_t
*alarm_time
)
779 nanotime_to_absolutetime(alarm_time
->tv_sec
, alarm_time
->tv_nsec
, &abstime
);
780 timer_call_enter_with_leeway(&alarm_expire_timer
, NULL
, abstime
, 0, TIMER_CALL_USER_NORMAL
, FALSE
);
784 * Check the validity of 'alarm_time' and 'alarm_type'. If either
785 * argument is invalid, return a negative value. If the 'alarm_time'
786 * is now, return a 0 value. If the 'alarm_time' is in the future,
787 * return a positive value.
791 alarm_type_t alarm_type
,
792 mach_timespec_t
*alarm_time
,
793 mach_timespec_t
*clock_time
)
797 if (BAD_ALRMTYPE(alarm_type
))
799 if (BAD_MACH_TIMESPEC(alarm_time
))
801 if ((alarm_type
& ALRMTYPE
) == TIME_RELATIVE
)
802 ADD_MACH_TIMESPEC(alarm_time
, clock_time
);
804 result
= CMP_MACH_TIMESPEC(alarm_time
, clock_time
);
806 return ((result
>= 0)? result
: 0);
812 clock_get_system_value(void)
814 clock_t clock
= &clock_list
[SYSTEM_CLOCK
];
815 mach_timespec_t value
;
817 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
823 clock_get_calendar_value(void)
825 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
826 mach_timespec_t value
= MACH_TIMESPEC_ZERO
;
828 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
833 #endif /* __LP64__ */