2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
7 * UNIX-hosted timer driver.
8 * Unix operating systems have only one usable timer, producing SIGALRM.
9 * In this implementation we run the timer with a fixed frequency, which
10 * is a multiple of VBlank frequency.
11 * TODO: Rewrite UNIX-hosted timer.device to use variable delays, for improved accuracy.
14 #include <aros/bootloader.h>
15 #include <aros/debug.h>
16 #include <aros/symbolsets.h>
17 #include <exec/execbase.h>
18 #include <exec/interrupts.h>
19 #include <hardware/intbits.h>
20 #include <proto/arossupport.h>
21 #include <proto/bootloader.h>
22 #include <proto/exec.h>
23 #include <proto/hostlib.h>
24 #include <proto/kernel.h>
26 #include "timer_intern.h"
27 #include "timer_macros.h"
29 #define timeval sys_timeval
37 /* Android is not a true Linux ;-) */
38 #ifdef HOST_OS_android
43 #define LIBC_NAME "libc.so.6"
47 #define LIBC_NAME "libSystem.dylib"
51 #define LIBC_NAME "libc.so"
55 * In Linux stdlib.h #define's atoi() to something internal, causing link failure
56 * because we link our kickstart binaries against AROS libraries, and not against
58 * Explicit prototype here avoids this.
60 int atoi(const char *nptr
);
62 /* Handle periodic timer and drive exec VBlank */
63 static void TimerTick(struct TimerBase
*TimerBase
, struct ExecBase
*SysBase
)
65 /* Increment EClock value and process microhz requests */
66 ADDTIME(&TimerBase
->tb_CurrentTime
, &TimerBase
->tb_Platform
.tb_VBlankTime
);
67 ADDTIME(&TimerBase
->tb_Elapsed
, &TimerBase
->tb_Platform
.tb_VBlankTime
);
68 TimerBase
->tb_ticks_total
++;
70 handleMicroHZ(TimerBase
, SysBase
);
72 /* Now handle VBlank emulation via divisor */
73 TimerBase
->tb_Platform
.tb_TimerCount
++;
74 if (TimerBase
->tb_Platform
.tb_TimerCount
== TimerBase
->tb_Platform
.tb_VBlankTicks
)
76 vblank_Cause(SysBase
);
77 handleVBlank(TimerBase
, SysBase
);
79 TimerBase
->tb_Platform
.tb_TimerCount
= 0;
83 #define KernelBase TimerBase->tb_KernelBase
85 static int Timer_Init(struct TimerBase
*TimerBase
)
88 struct itimerval interval
;
91 HostLibBase
= OpenResource("hostlib.resource");
95 TimerBase
->tb_Platform
.libcHandle
= HostLib_Open(LIBC_NAME
, NULL
);
96 if (!TimerBase
->tb_Platform
.libcHandle
)
99 TimerBase
->tb_Platform
.setitimer
= HostLib_GetPointer(TimerBase
->tb_Platform
.libcHandle
, "setitimer", NULL
);
100 if (!TimerBase
->tb_Platform
.setitimer
)
103 /* Install timer IRQ handler */
105 /* Uses ITIMER_VIRTUAL/SIGVTALRM instead of the
106 * ITIMER_REAL/SIGALRM in debug builds, so
107 * that stepping though code won't have to deal
108 * with constant SIGALRM processing.
110 * NOTE: This will cause the AROS clock to march slower
111 * than the host clock in debug builds!
113 TimerBase
->tb_TimerIRQHandle
= KrnAddIRQHandler(SIGVTALRM
, TimerTick
, TimerBase
, SysBase
);
115 TimerBase
->tb_TimerIRQHandle
= KrnAddIRQHandler(SIGALRM
, TimerTick
, TimerBase
, SysBase
);
117 if (!TimerBase
->tb_TimerIRQHandle
)
120 /* Our defaults: 50 Hz VBlank and 4x timer rate. 1x gives very poor results. */
121 SysBase
->VBlankFrequency
= 50;
122 TimerBase
->tb_Platform
.tb_VBlankTicks
= 4;
125 * Since we are software-driven, we can just ask the user which
126 * frequencies he wishes to use.
128 BootLoaderBase
= OpenResource("bootloader.resource");
131 struct List
*args
= GetBootInfo(BL_Args
);
137 for (node
= args
->lh_Head
; node
->ln_Succ
; node
= node
->ln_Succ
)
139 if (strncasecmp(node
->ln_Name
, "vblank=", 7) == 0)
140 SysBase
->VBlankFrequency
= atoi(&node
->ln_Name
[7]);
141 else if (strncasecmp(node
->ln_Name
, "tickrate=", 9) == 0)
142 TimerBase
->tb_Platform
.tb_VBlankTicks
= atoi(&node
->ln_Name
[9]);
147 /* Calculate effective EClock timer frequency. Set it also in ExecBase public field. */
148 TimerBase
->tb_eclock_rate
= SysBase
->VBlankFrequency
* TimerBase
->tb_Platform
.tb_VBlankTicks
;
149 SysBase
->ex_EClockFrequency
= TimerBase
->tb_eclock_rate
;
150 D(bug("[Timer_Init] Timer frequency is %d\n", TimerBase
->tb_eclock_rate
));
152 /* Calculate timer period in us */
153 TimerBase
->tb_Platform
.tb_VBlankTime
.tv_secs
= 0;
154 TimerBase
->tb_Platform
.tb_VBlankTime
.tv_micro
= 1000000 / TimerBase
->tb_eclock_rate
;
156 /* Start up our system timer. */
157 interval
.it_interval
.tv_sec
= interval
.it_value
.tv_sec
= 0;
158 interval
.it_interval
.tv_usec
= interval
.it_value
.tv_usec
= TimerBase
->tb_Platform
.tb_VBlankTime
.tv_micro
;
163 ret
= TimerBase
->tb_Platform
.setitimer(ITIMER_VIRTUAL
, &interval
, NULL
);
165 ret
= TimerBase
->tb_Platform
.setitimer(ITIMER_REAL
, &interval
, NULL
);
171 D(bug("[Timer_Init] setitimer() returned %d\n", ret
));
175 static int Timer_Expunge(struct TimerBase
*TimerBase
)
180 if (TimerBase
->tb_TimerIRQHandle
)
181 KrnRemIRQHandler(TimerBase
->tb_TimerIRQHandle
);
183 if (TimerBase
->tb_Platform
.libcHandle
)
184 HostLib_Close(TimerBase
->tb_Platform
.libcHandle
, NULL
);
189 ADD2INITLIB(Timer_Init
, 0)
190 ADD2EXPUNGELIB(Timer_Expunge
, 0)