2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * DEPRECATED INTERFACES - Should be removed
28 * Purpose: Routines for the creation and use of kernel
29 * alarm clock services. This file and the ipc
30 * routines in kern/ipc_clock.c constitute the
31 * machine-independent clock service layer.
34 #include <mach/mach_types.h>
36 #include <kern/lock.h>
37 #include <kern/host.h>
39 #include <kern/sched_prim.h>
40 #include <kern/thread.h>
41 #include <kern/ipc_host.h>
42 #include <kern/clock.h>
43 #include <kern/zalloc.h>
45 #include <ipc/ipc_types.h>
46 #include <ipc/ipc_port.h>
48 #include <mach/mach_traps.h>
49 #include <mach/mach_time.h>
51 #include <mach/clock_server.h>
52 #include <mach/clock_reply.h>
53 #include <mach/clock_priv_server.h>
55 #include <mach/mach_host_server.h>
56 #include <mach/host_priv_server.h>
59 * Actual clock alarm structure. Used for user clock_sleep() and
60 * clock_alarm() calls. Alarms are allocated from the alarm free
61 * list and entered in time priority order into the active alarm
62 * chain of the target clock.
65 struct alarm
*al_next
; /* next alarm in chain */
66 struct alarm
*al_prev
; /* previous alarm in chain */
67 int al_status
; /* alarm status */
68 mach_timespec_t al_time
; /* alarm time */
69 struct { /* message alarm data */
70 int type
; /* alarm type */
71 ipc_port_t port
; /* alarm port */
73 port_type
; /* alarm port type */
74 struct clock
*clock
; /* alarm clock */
75 void *data
; /* alarm data */
77 #define al_type al_alrm.type
78 #define al_port al_alrm.port
79 #define al_port_type al_alrm.port_type
80 #define al_clock al_alrm.clock
81 #define al_data al_alrm.data
82 long al_seqno
; /* alarm sequence number */
84 typedef struct alarm alarm_data_t
;
87 #define ALARM_FREE 0 /* alarm is on free list */
88 #define ALARM_SLEEP 1 /* active clock_sleep() */
89 #define ALARM_CLOCK 2 /* active clock_alarm() */
90 #define ALARM_DONE 4 /* alarm has expired */
92 /* local data declarations */
93 decl_simple_lock_data(static,alarm_lock
) /* alarm synchronization */
94 static struct zone
*alarm_zone
; /* zone for user alarms */
95 static struct alarm
*alrmfree
; /* alarm free list pointer */
96 static struct alarm
*alrmdone
; /* alarm done list pointer */
97 static struct alarm
*alrmlist
;
98 static long alrm_seqno
; /* uniquely identifies alarms */
99 static thread_call_data_t alarm_done_call
;
100 static timer_call_data_t alarm_expire_timer
;
102 extern struct clock clock_list
[];
103 extern int clock_count
;
105 static void post_alarm(
108 static void set_alarm(
109 mach_timespec_t
*alarm_time
);
111 static int check_time(
112 alarm_type_t alarm_type
,
113 mach_timespec_t
*alarm_time
,
114 mach_timespec_t
*clock_time
);
116 static void alarm_done(void);
118 static void alarm_expire(void);
120 static kern_return_t
clock_sleep_internal(
122 sleep_type_t sleep_type
,
123 mach_timespec_t
*sleep_time
);
125 int rtclock_config(void);
127 int rtclock_init(void);
129 kern_return_t
rtclock_gettime(
130 mach_timespec_t
*cur_time
);
132 kern_return_t
rtclock_getattr(
133 clock_flavor_t flavor
,
135 mach_msg_type_number_t
*count
);
137 struct clock_ops sysclk_ops
= {
138 rtclock_config
, rtclock_init
,
143 kern_return_t
calend_gettime(
144 mach_timespec_t
*cur_time
);
146 kern_return_t
calend_getattr(
147 clock_flavor_t flavor
,
149 mach_msg_type_number_t
*count
);
151 struct clock_ops calend_ops
= {
158 * Macros to lock/unlock clock system.
160 #define LOCK_ALARM(s) \
162 simple_lock(&alarm_lock);
164 #define UNLOCK_ALARM(s) \
165 simple_unlock(&alarm_lock); \
169 clock_oldconfig(void)
174 simple_lock_init(&alarm_lock
, 0);
175 thread_call_setup(&alarm_done_call
, (thread_call_func_t
)alarm_done
, NULL
);
176 timer_call_setup(&alarm_expire_timer
, (timer_call_func_t
)alarm_expire
, NULL
);
179 * Configure clock devices.
181 for (i
= 0; i
< clock_count
; i
++) {
182 clock
= &clock_list
[i
];
183 if (clock
->cl_ops
&& clock
->cl_ops
->c_config
) {
184 if ((*clock
->cl_ops
->c_config
)() == 0)
189 /* start alarm sequence numbers at 0 */
200 * Initialize basic clock structures.
202 for (i
= 0; i
< clock_count
; i
++) {
203 clock
= &clock_list
[i
];
204 if (clock
->cl_ops
&& clock
->cl_ops
->c_init
)
205 (*clock
->cl_ops
->c_init
)();
210 * Initialize the clock ipc service facility.
213 clock_service_create(void)
219 * Initialize ipc clock services.
221 for (i
= 0; i
< clock_count
; i
++) {
222 clock
= &clock_list
[i
];
224 ipc_clock_init(clock
);
225 ipc_clock_enable(clock
);
230 * Perform miscellaneous late
233 i
= sizeof(struct alarm
);
234 alarm_zone
= zinit(i
, (4096/i
)*i
, 10*i
, "alarms");
238 * Get the service port on a clock.
241 host_get_clock_service(
244 clock_t *clock
) /* OUT */
246 if (host
== HOST_NULL
|| clock_id
< 0 || clock_id
>= clock_count
) {
248 return (KERN_INVALID_ARGUMENT
);
251 *clock
= &clock_list
[clock_id
];
252 if ((*clock
)->cl_ops
== 0)
253 return (KERN_FAILURE
);
254 return (KERN_SUCCESS
);
258 * Get the control port on a clock.
261 host_get_clock_control(
262 host_priv_t host_priv
,
264 clock_t *clock
) /* OUT */
266 if (host_priv
== HOST_PRIV_NULL
||
267 clock_id
< 0 || clock_id
>= clock_count
) {
269 return (KERN_INVALID_ARGUMENT
);
272 *clock
= &clock_list
[clock_id
];
273 if ((*clock
)->cl_ops
== 0)
274 return (KERN_FAILURE
);
275 return (KERN_SUCCESS
);
279 * Get the current clock time.
284 mach_timespec_t
*cur_time
) /* OUT */
286 if (clock
== CLOCK_NULL
)
287 return (KERN_INVALID_ARGUMENT
);
288 return ((*clock
->cl_ops
->c_gettime
)(cur_time
));
293 mach_timespec_t
*time
) /* OUT */
295 clock_get_system_nanotime(&time
->tv_sec
, (uint32_t *)&time
->tv_nsec
);
297 return (KERN_SUCCESS
);
302 mach_timespec_t
*time
) /* OUT */
304 clock_get_calendar_nanotime(&time
->tv_sec
, (uint32_t *)&time
->tv_nsec
);
306 return (KERN_SUCCESS
);
310 * Get clock attributes.
313 clock_get_attributes(
315 clock_flavor_t flavor
,
316 clock_attr_t attr
, /* OUT */
317 mach_msg_type_number_t
*count
) /* IN/OUT */
319 if (clock
== CLOCK_NULL
)
320 return (KERN_INVALID_ARGUMENT
);
321 if (clock
->cl_ops
->c_getattr
)
322 return (clock
->cl_ops
->c_getattr(flavor
, attr
, count
));
323 return (KERN_FAILURE
);
328 clock_flavor_t flavor
,
329 clock_attr_t attr
, /* OUT */
330 mach_msg_type_number_t
*count
) /* IN/OUT */
333 return (KERN_FAILURE
);
337 case CLOCK_GET_TIME_RES
: /* >0 res */
338 case CLOCK_ALARM_CURRES
: /* =0 no alarm */
339 case CLOCK_ALARM_MINRES
:
340 case CLOCK_ALARM_MAXRES
:
341 *(clock_res_t
*) attr
= NSEC_PER_SEC
/ 100;
345 return (KERN_INVALID_VALUE
);
348 return (KERN_SUCCESS
);
353 clock_flavor_t flavor
,
354 clock_attr_t attr
, /* OUT */
355 mach_msg_type_number_t
*count
) /* IN/OUT */
358 return (KERN_FAILURE
);
362 case CLOCK_GET_TIME_RES
: /* >0 res */
363 *(clock_res_t
*) attr
= NSEC_PER_SEC
/ 100;
366 case CLOCK_ALARM_CURRES
: /* =0 no alarm */
367 case CLOCK_ALARM_MINRES
:
368 case CLOCK_ALARM_MAXRES
:
369 *(clock_res_t
*) attr
= 0;
373 return (KERN_INVALID_VALUE
);
376 return (KERN_SUCCESS
);
380 * Set the current clock time.
385 __unused mach_timespec_t new_time
)
387 if (clock
== CLOCK_NULL
)
388 return (KERN_INVALID_ARGUMENT
);
389 return (KERN_FAILURE
);
393 * Set the clock alarm resolution.
396 clock_set_attributes(
398 __unused clock_flavor_t flavor
,
399 __unused clock_attr_t attr
,
400 __unused mach_msg_type_number_t count
)
402 if (clock
== CLOCK_NULL
)
403 return (KERN_INVALID_ARGUMENT
);
404 return (KERN_FAILURE
);
408 * Setup a clock alarm.
413 alarm_type_t alarm_type
,
414 mach_timespec_t alarm_time
,
415 ipc_port_t alarm_port
,
416 mach_msg_type_name_t alarm_port_type
)
419 mach_timespec_t clock_time
;
421 kern_return_t reply_code
;
424 if (clock
== CLOCK_NULL
)
425 return (KERN_INVALID_ARGUMENT
);
426 if (clock
!= &clock_list
[SYSTEM_CLOCK
])
427 return (KERN_FAILURE
);
428 if (IP_VALID(alarm_port
) == 0)
429 return (KERN_INVALID_CAPABILITY
);
432 * Check alarm parameters. If parameters are invalid,
433 * send alarm message immediately.
435 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
436 chkstat
= check_time(alarm_type
, &alarm_time
, &clock_time
);
438 reply_code
= (chkstat
< 0 ? KERN_INVALID_VALUE
: KERN_SUCCESS
);
439 clock_alarm_reply(alarm_port
, alarm_port_type
,
440 reply_code
, alarm_type
, clock_time
);
441 return (KERN_SUCCESS
);
445 * Get alarm and add to clock alarm list.
449 if ((alarm
= alrmfree
) == 0) {
451 alarm
= (alarm_t
) zalloc(alarm_zone
);
453 return (KERN_RESOURCE_SHORTAGE
);
457 alrmfree
= alarm
->al_next
;
459 alarm
->al_status
= ALARM_CLOCK
;
460 alarm
->al_time
= alarm_time
;
461 alarm
->al_type
= alarm_type
;
462 alarm
->al_port
= alarm_port
;
463 alarm
->al_port_type
= alarm_port_type
;
464 alarm
->al_clock
= clock
;
465 alarm
->al_seqno
= alrm_seqno
++;
469 return (KERN_SUCCESS
);
473 * Sleep on a clock. System trap. User-level libmach clock_sleep
474 * interface call takes a mach_timespec_t sleep_time argument which it
475 * converts to sleep_sec and sleep_nsec arguments which are then
476 * passed to clock_sleep_trap.
480 struct clock_sleep_trap_args
*args
)
482 mach_port_name_t clock_name
= args
->clock_name
;
483 sleep_type_t sleep_type
= args
->sleep_type
;
484 int sleep_sec
= args
->sleep_sec
;
485 int sleep_nsec
= args
->sleep_nsec
;
486 mach_vm_address_t wakeup_time_addr
= args
->wakeup_time
;
488 mach_timespec_t swtime
;
489 kern_return_t rvalue
;
492 * Convert the trap parameters.
494 if (clock_name
== MACH_PORT_NULL
)
495 clock
= &clock_list
[SYSTEM_CLOCK
];
497 clock
= port_name_to_clock(clock_name
);
499 swtime
.tv_sec
= sleep_sec
;
500 swtime
.tv_nsec
= sleep_nsec
;
503 * Call the actual clock_sleep routine.
505 rvalue
= clock_sleep_internal(clock
, sleep_type
, &swtime
);
508 * Return current time as wakeup time.
510 if (rvalue
!= KERN_INVALID_ARGUMENT
&& rvalue
!= KERN_FAILURE
) {
511 copyout((char *)&swtime
, wakeup_time_addr
, sizeof(mach_timespec_t
));
517 clock_sleep_internal(
519 sleep_type_t sleep_type
,
520 mach_timespec_t
*sleep_time
)
523 mach_timespec_t clock_time
;
524 kern_return_t rvalue
;
528 if (clock
== CLOCK_NULL
)
529 return (KERN_INVALID_ARGUMENT
);
531 if (clock
!= &clock_list
[SYSTEM_CLOCK
])
532 return (KERN_FAILURE
);
535 * Check sleep parameters. If parameters are invalid
536 * return an error, otherwise post alarm request.
538 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
540 chkstat
= check_time(sleep_type
, sleep_time
, &clock_time
);
542 return (KERN_INVALID_VALUE
);
543 rvalue
= KERN_SUCCESS
;
545 wait_result_t wait_result
;
548 * Get alarm and add to clock alarm list.
552 if ((alarm
= alrmfree
) == 0) {
554 alarm
= (alarm_t
) zalloc(alarm_zone
);
556 return (KERN_RESOURCE_SHORTAGE
);
560 alrmfree
= alarm
->al_next
;
563 * Wait for alarm to occur.
565 wait_result
= assert_wait((event_t
)alarm
, THREAD_ABORTSAFE
);
566 if (wait_result
== THREAD_WAITING
) {
567 alarm
->al_time
= *sleep_time
;
568 alarm
->al_status
= ALARM_SLEEP
;
572 wait_result
= thread_block(THREAD_CONTINUE_NULL
);
575 * Note if alarm expired normally or whether it
576 * was aborted. If aborted, delete alarm from
577 * clock alarm list. Return alarm to free list.
580 if (alarm
->al_status
!= ALARM_DONE
) {
581 assert(wait_result
!= THREAD_AWAKENED
);
582 if (((alarm
->al_prev
)->al_next
= alarm
->al_next
) != NULL
)
583 (alarm
->al_next
)->al_prev
= alarm
->al_prev
;
584 rvalue
= KERN_ABORTED
;
586 *sleep_time
= alarm
->al_time
;
587 alarm
->al_status
= ALARM_FREE
;
589 assert(wait_result
== THREAD_INTERRUPTED
);
590 assert(alarm
->al_status
== ALARM_FREE
);
591 rvalue
= KERN_ABORTED
;
593 alarm
->al_next
= alrmfree
;
598 *sleep_time
= clock_time
;
604 * Service clock alarm expirations.
610 register alarm_t alrm1
;
611 register alarm_t alrm2
;
612 mach_timespec_t clock_time
;
613 mach_timespec_t
*alarm_time
;
616 clock
= &clock_list
[SYSTEM_CLOCK
];
617 (*clock
->cl_ops
->c_gettime
)(&clock_time
);
620 * Update clock alarm list. Alarms that are due are moved
621 * to the alarmdone list to be serviced by a thread callout.
624 alrm1
= (alarm_t
)&alrmlist
;
625 while ((alrm2
= alrm1
->al_next
) != NULL
) {
626 alarm_time
= &alrm2
->al_time
;
627 if (CMP_MACH_TIMESPEC(alarm_time
, &clock_time
) > 0)
631 * Alarm has expired, so remove it from the
634 if ((alrm1
->al_next
= alrm2
->al_next
) != NULL
)
635 (alrm1
->al_next
)->al_prev
= alrm1
;
638 * If a clock_sleep() alarm, wakeup the thread
639 * which issued the clock_sleep() call.
641 if (alrm2
->al_status
== ALARM_SLEEP
) {
643 alrm2
->al_status
= ALARM_DONE
;
644 alrm2
->al_time
= clock_time
;
645 thread_wakeup((event_t
)alrm2
);
649 * If a clock_alarm() alarm, place the alarm on
650 * the alarm done list and schedule the alarm
651 * delivery mechanism.
654 assert(alrm2
->al_status
== ALARM_CLOCK
);
655 if ((alrm2
->al_next
= alrmdone
) != NULL
)
656 alrmdone
->al_prev
= alrm2
;
658 thread_call_enter(&alarm_done_call
);
659 alrm2
->al_prev
= (alarm_t
)&alrmdone
;
661 alrm2
->al_status
= ALARM_DONE
;
662 alrm2
->al_time
= clock_time
;
667 * Setup to expire for the next pending alarm.
670 set_alarm(alarm_time
);
677 register alarm_t alrm
;
682 while ((alrm
= alrmdone
) != NULL
) {
683 if ((alrmdone
= alrm
->al_next
) != NULL
)
684 alrmdone
->al_prev
= (alarm_t
)&alrmdone
;
687 code
= (alrm
->al_status
== ALARM_DONE
? KERN_SUCCESS
: KERN_ABORTED
);
688 if (alrm
->al_port
!= IP_NULL
) {
689 /* Deliver message to designated port */
690 if (IP_VALID(alrm
->al_port
)) {
691 clock_alarm_reply(alrm
->al_port
, alrm
->al_port_type
, code
,
692 alrm
->al_type
, alrm
->al_time
);
696 alrm
->al_status
= ALARM_FREE
;
697 alrm
->al_next
= alrmfree
;
701 panic("clock_alarm_deliver");
708 * Post an alarm on the active alarm list.
710 * Always called from within a LOCK_ALARM() code section.
716 register alarm_t alrm1
, alrm2
;
717 mach_timespec_t
*alarm_time
;
718 mach_timespec_t
*queue_time
;
721 * Traverse alarm list until queue time is greater
722 * than alarm time, then insert alarm.
724 alarm_time
= &alarm
->al_time
;
725 alrm1
= (alarm_t
)&alrmlist
;
726 while ((alrm2
= alrm1
->al_next
) != NULL
) {
727 queue_time
= &alrm2
->al_time
;
728 if (CMP_MACH_TIMESPEC(queue_time
, alarm_time
) > 0)
732 alrm1
->al_next
= alarm
;
733 alarm
->al_next
= alrm2
;
734 alarm
->al_prev
= alrm1
;
736 alrm2
->al_prev
= alarm
;
739 * If the inserted alarm is the 'earliest' alarm,
740 * reset the device layer alarm time accordingly.
742 if (alrmlist
== alarm
)
743 set_alarm(alarm_time
);
748 mach_timespec_t
*alarm_time
)
752 nanotime_to_absolutetime(alarm_time
->tv_sec
, alarm_time
->tv_nsec
, &abstime
);
753 timer_call_enter(&alarm_expire_timer
, abstime
);
757 * Check the validity of 'alarm_time' and 'alarm_type'. If either
758 * argument is invalid, return a negative value. If the 'alarm_time'
759 * is now, return a 0 value. If the 'alarm_time' is in the future,
760 * return a positive value.
764 alarm_type_t alarm_type
,
765 mach_timespec_t
*alarm_time
,
766 mach_timespec_t
*clock_time
)
770 if (BAD_ALRMTYPE(alarm_type
))
772 if (BAD_MACH_TIMESPEC(alarm_time
))
774 if ((alarm_type
& ALRMTYPE
) == TIME_RELATIVE
)
775 ADD_MACH_TIMESPEC(alarm_time
, clock_time
);
777 result
= CMP_MACH_TIMESPEC(alarm_time
, clock_time
);
779 return ((result
>= 0)? result
: 0);
783 clock_get_system_value(void)
785 clock_t clock
= &clock_list
[SYSTEM_CLOCK
];
786 mach_timespec_t value
;
788 (void) (*clock
->cl_ops
->c_gettime
)(&value
);
794 clock_get_calendar_value(void)
796 clock_t clock
= &clock_list
[CALENDAR_CLOCK
];
797 mach_timespec_t value
= MACH_TIMESPEC_ZERO
;
799 (void) (*clock
->cl_ops
->c_gettime
)(&value
);