move delay code into separate function.
[AROS.git] / rom / timer / lowlevel.c
blob0c64c5aac1e44679aea23170077b3b37e8db3b7d
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Common IORequest processing routines
6 Lang: english
7 */
9 #include <aros/debug.h>
10 #include <aros/symbolsets.h>
11 #include <devices/newstyle.h>
12 #include <exec/errors.h>
13 #include <exec/initializers.h>
14 #include <hardware/intbits.h>
15 #include <proto/exec.h>
16 #include <proto/timer.h>
18 #include "timer_intern.h"
19 #include "timer_macros.h"
21 /****************************************************************************************/
23 #define NEWSTYLE_DEVICE 1
25 #define ioStd(x) ((struct IOStdReq *)x)
27 /****************************************************************************************/
29 #if NEWSTYLE_DEVICE
31 static const UWORD SupportedCommands[] =
33 TR_GETSYSTIME,
34 TR_SETSYSTIME,
35 TR_ADDREQUEST,
36 NSCMD_DEVICEQUERY,
40 #endif
42 static void addToWaitList(struct MinList *list, struct timerequest *iotr, struct ExecBase *SysBase)
44 /* We are disabled, so we should take as little time as possible. */
45 struct timerequest *tr;
46 BOOL added = FALSE;
48 ForeachNode(list, tr)
50 /* If the time in the new request is less than the next request */
51 if (CMPTIME(&tr->tr_time, &iotr->tr_time) < 0)
53 /* Add the node before the next request */
54 Insert((struct List *)list, &iotr->tr_node.io_Message.mn_Node, tr->tr_node.io_Message.mn_Node.ln_Pred);
56 added = TRUE;
57 break;
62 * This will catch the case of either an empty list, or request is
63 * for after all other requests
65 if(!added)
66 ADDTAIL(list, iotr);
68 #if PRINT_LIST
69 bug("Current list contents:\n");
71 ForeachNode(list, tr)
73 bug("%u.%u\n", tr->tr_time.tv_secs, tr->tr_time.tv_micro);
75 #endif
78 BOOL common_BeginIO(struct timerequest *timereq, struct TimerBase *TimerBase)
80 ULONG unitNum = (IPTR)timereq->tr_node.io_Unit;
81 BOOL replyit = FALSE;
82 BOOL addedhead = FALSE;
84 timereq->tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
85 timereq->tr_node.io_Error = 0;
87 switch(timereq->tr_node.io_Command)
89 #if NEWSTYLE_DEVICE
90 case NSCMD_DEVICEQUERY:
93 * CHECKME: In timer.device this is maybe a bit problematic, as the
94 * timerequest structure does not have io_Data and io_Length members
96 if (timereq->tr_node.io_Message.mn_Length < sizeof(struct IOStdReq))
98 timereq->tr_node.io_Error = IOERR_BADLENGTH;
100 else if(ioStd(timereq)->io_Length < ((LONG)OFFSET(NSDeviceQueryResult, SupportedCommands)) + sizeof(UWORD *))
102 timereq->tr_node.io_Error = IOERR_BADLENGTH;
104 else
106 struct NSDeviceQueryResult *d = (struct NSDeviceQueryResult *)ioStd(timereq)->io_Data;
108 d->DevQueryFormat = 0;
109 d->SizeAvailable = sizeof(struct NSDeviceQueryResult);
110 d->DeviceType = NSDEVTYPE_TIMER;
111 d->DeviceSubType = 0;
112 d->SupportedCommands = (UWORD *)SupportedCommands;
114 ioStd(timereq)->io_Actual = sizeof(struct NSDeviceQueryResult);
116 break;
117 #endif
119 case TR_GETSYSTIME:
120 GetSysTime(&timereq->tr_time);
122 if (!(timereq->tr_node.io_Flags & IOF_QUICK))
123 ReplyMsg(&timereq->tr_node.io_Message);
125 replyit = FALSE; /* Because replyit will clear the timeval */
126 break;
128 case TR_SETSYSTIME:
129 Disable();
131 /* Set current time value */
132 TimerBase->tb_CurrentTime.tv_secs = timereq->tr_time.tv_secs;
133 TimerBase->tb_CurrentTime.tv_micro = timereq->tr_time.tv_micro;
134 /* Update hardware */
135 EClockSet(TimerBase);
137 Enable();
138 replyit = TRUE;
139 break;
141 case TR_ADDREQUEST:
142 switch(unitNum)
144 case UNIT_WAITUNTIL:
145 Disable();
147 /* Query the hardware first */
148 EClockUpdate(TimerBase);
150 if (CMPTIME(&TimerBase->tb_CurrentTime, &timereq->tr_time) <= 0)
152 timereq->tr_time.tv_secs = timereq->tr_time.tv_micro = 0;
153 timereq->tr_node.io_Error = 0;
154 replyit = TRUE;
156 else
158 /* Ok, we add this to the list */
159 addToWaitList(&TimerBase->tb_Lists[TL_WAITVBL], timereq, SysBase);
162 * If our request was added to the head of the list, we may need to
163 * readjust our hardware interrupt (reset elapsed time).
164 * This routine returns TRUE in order to indicate this.
166 if (TimerBase->tb_Lists[TL_WAITVBL].mlh_Head == (struct MinNode *)timereq)
167 addedhead = TRUE;
169 replyit = FALSE;
170 timereq->tr_node.io_Flags &= ~IOF_QUICK;
173 Enable();
174 break;
176 case UNIT_VBLANK:
177 case UNIT_MICROHZ:
178 Disable();
180 /* Query the hardware first */
181 EClockUpdate(TimerBase);
185 * Adjust the time request to be relative to the
186 * the elapsed time counter that we keep.
188 ADDTIME(&timereq->tr_time, &TimerBase->tb_Elapsed);
190 /* Slot it into the list. Use unit number as index. */
191 addToWaitList(&TimerBase->tb_Lists[unitNum], timereq, SysBase);
193 /* Indicate if HW need to be reprogrammed */
194 if (TimerBase->tb_Lists[unitNum].mlh_Head == (struct MinNode *)timereq)
195 addedhead = TRUE;
197 Enable();
198 timereq->tr_node.io_Flags &= ~IOF_QUICK;
199 replyit = FALSE;
200 break;
202 case UNIT_ECLOCK:
203 case UNIT_WAITECLOCK:
204 /* TODO: implement these (backport from m68k-Amiga) */
205 default:
206 replyit = FALSE;
207 timereq->tr_node.io_Error = IOERR_NOCMD;
208 break;
210 } /* switch(unitNum) */
211 break;
213 case CMD_CLEAR:
214 case CMD_FLUSH:
215 case CMD_INVALID:
216 case CMD_READ:
217 case CMD_RESET:
218 case CMD_START:
219 case CMD_STOP:
220 case CMD_UPDATE:
221 case CMD_WRITE:
222 default:
223 replyit = TRUE;
224 timereq->tr_node.io_Error = IOERR_NOCMD;
225 break;
227 } /* switch(command) */
229 if (replyit)
231 timereq->tr_time.tv_secs = 0;
232 timereq->tr_time.tv_micro = 0;
234 if (!(timereq->tr_node.io_Flags & IOF_QUICK))
235 ReplyMsg(&timereq->tr_node.io_Message);
238 return addedhead;
241 void handleMicroHZ(struct TimerBase *TimerBase, struct ExecBase *SysBase)
243 struct MinList *unit = &TimerBase->tb_Lists[TL_MICROHZ];
244 struct timerequest *tr, *next;
247 * Go through the list and return requests that have completed.
248 * A completed request is one whose time is less than that of the elapsed time.
250 ForeachNodeSafe(unit, tr, next)
252 if (CMPTIME(&TimerBase->tb_Elapsed, &tr->tr_time) <= 0)
254 /* This request has finished */
255 REMOVE(tr);
257 #ifdef USE_VBLANK_EMU
258 if (tr == &TimerBase->tb_vblank_timerequest)
260 struct IntVector *iv = &SysBase->IntVects[INTB_VERTB];
262 /* VBlank Emu */
263 if (iv->iv_Code)
265 AROS_INTC2(iv->iv_Code, iv->iv_Data, INTF_VERTB);
269 * Process VBlank unit.
270 * The philosophy behind is that only software which needs to measure
271 * exact intervals uses MICROHZ unit. Others use VBLANK one. As a result,
272 * VBLANK queue is generally more populated than MICROHZ one.
273 * VBLANK queue is checked more rarely than MICROHZ, this helps to decrease
274 * CPU usage.
276 handleVBlank(TimerBase, SysBase);
279 * Automatically requeue/reactivate request.
280 * Feature: get value every time from SysBase. This means
281 * that the user can change our VBlank rate in runtime by modifying
282 * this field.
284 tr->tr_time.tv_secs = 0;
285 tr->tr_time.tv_micro = 1000000 / SysBase->VBlankFrequency;
286 ADDTIME(&tr->tr_time, &TimerBase->tb_Elapsed);
287 addToWaitList(unit, tr, SysBase);
289 continue;
291 #endif
292 D(bug("[Timer] Replying msg 0x%p\n", tr));
294 tr->tr_time.tv_secs = 0;
295 tr->tr_time.tv_micro = 0;
296 tr->tr_node.io_Error = 0;
298 ReplyMsg(&tr->tr_node.io_Message);
300 else
303 The first request hasn't finished, as all requests are in
304 order, we don't bother searching through the remaining
306 break;
311 void handleVBlank(struct TimerBase *TimerBase, struct ExecBase *SysBase)
314 * VBlank handler is the same as above, with two differences:
315 * 1. We don't check for VBlank emulation request.
316 * 2. VBlank unit consists of two list, not one. The second list
317 * is UNIT_WAITUNTIL queue.
318 * We could use subroutines and save some space, but we prefer speed here.
320 struct timerequest *tr, *next;
323 * Go through the "wait for x seconds" list and return requests
324 * that have completed. A completed request is one whose time
325 * is less than that of the elapsed time.
327 ForeachNodeSafe(&TimerBase->tb_Lists[TL_VBLANK], tr, next)
329 if (CMPTIME(&TimerBase->tb_Elapsed, &tr->tr_time) <= 0)
331 /* This request has finished */
332 REMOVE(tr);
334 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
335 tr->tr_node.io_Error = 0;
337 ReplyMsg(&tr->tr_node.io_Message);
339 else
340 break;
344 * The other this is the "wait until a specified time". Here a request
345 * is complete if the time we are waiting for is before the current time.
347 ForeachNodeSafe(&TimerBase->tb_Lists[TL_WAITVBL], tr, next)
349 if (CMPTIME(&TimerBase->tb_CurrentTime, &tr->tr_time) <= 0)
351 /* This request has finished */
352 REMOVE(tr);
354 tr->tr_time.tv_secs = tr->tr_time.tv_micro = 0;
355 tr->tr_node.io_Error = 0;
357 ReplyMsg(&tr->tr_node.io_Message);
359 else
360 break;
364 /****************************************************************************************/
366 static int Timer_Open(struct TimerBase *LIBBASE, struct timerequest *tr, ULONG unitNum, ULONG flags)
369 * Normally, we should check the length of the message and other
370 * such things, however the RKM documents an example where the
371 * length of the timerrequest isn't set, so we must not check
372 * this.
373 * This fixes bug SF# 741580
376 if (unitNum > UNIT_WAITECLOCK)
377 tr->tr_node.io_Error = IOERR_OPENFAIL;
378 else
380 tr->tr_node.io_Error = 0;
381 tr->tr_node.io_Unit = (NULL + unitNum);
382 tr->tr_node.io_Device = &LIBBASE->tb_Device;
385 return TRUE;
388 ADD2OPENDEV(Timer_Open, 0);