2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
5 Desc: Common IORequest processing routines
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 /****************************************************************************************/
31 static const UWORD SupportedCommands
[] =
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
;
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
);
62 * This will catch the case of either an empty list, or request is
63 * for after all other requests
69 bug("Current list contents:\n");
73 bug("%u.%u\n", tr
->tr_time
.tv_secs
, tr
->tr_time
.tv_micro
);
78 BOOL
common_BeginIO(struct timerequest
*timereq
, struct TimerBase
*TimerBase
)
80 ULONG unitNum
= (IPTR
)timereq
->tr_node
.io_Unit
;
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
)
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
;
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
);
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 */
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
);
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;
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
)
170 timereq
->tr_node
.io_Flags
&= ~IOF_QUICK
;
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
)
198 timereq
->tr_node
.io_Flags
&= ~IOF_QUICK
;
203 case UNIT_WAITECLOCK
:
204 /* TODO: implement these (backport from m68k-Amiga) */
207 timereq
->tr_node
.io_Error
= IOERR_NOCMD
;
210 } /* switch(unitNum) */
224 timereq
->tr_node
.io_Error
= IOERR_NOCMD
;
227 } /* switch(command) */
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
);
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 */
257 #ifdef USE_VBLANK_EMU
258 if (tr
== &TimerBase
->tb_vblank_timerequest
)
260 struct IntVector
*iv
= &SysBase
->IntVects
[INTB_VERTB
];
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
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
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
);
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
);
303 The first request hasn't finished, as all requests are in
304 order, we don't bother searching through the remaining
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 */
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
);
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 */
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
);
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
373 * This fixes bug SF# 741580
376 if (unitNum
> UNIT_WAITECLOCK
)
377 tr
->tr_node
.io_Error
= IOERR_OPENFAIL
;
380 tr
->tr_node
.io_Error
= 0;
381 tr
->tr_node
.io_Unit
= (NULL
+ unitNum
);
382 tr
->tr_node
.io_Device
= &LIBBASE
->tb_Device
;
388 ADD2OPENDEV(Timer_Open
, 0);