2 Copyright © 1995-2018, 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/execlock.h>
17 #include <proto/timer.h>
19 #include "timer_intern.h"
20 #include "timer_macros.h"
22 /****************************************************************************************/
24 #define NEWSTYLE_DEVICE 1
26 #define ioStd(x) ((struct IOStdReq *)x)
28 /****************************************************************************************/
32 static const UWORD SupportedCommands
[] =
43 static void addToWaitList(struct MinList
*list
, struct timerequest
*iotr
, struct ExecBase
*SysBase
)
45 /* We are disabled, so we should take as little time as possible. */
46 struct timerequest
*tr
;
51 /* If the time in the new request is less than the next request */
52 if (CMPTIME(&tr
->tr_time
, &iotr
->tr_time
) < 0)
54 /* Add the node before the next request */
55 Insert((struct List
*)list
, &iotr
->tr_node
.io_Message
.mn_Node
, tr
->tr_node
.io_Message
.mn_Node
.ln_Pred
);
63 * This will catch the case of either an empty list, or request is
64 * for after all other requests
70 bug("Current list contents:\n");
74 bug("%u.%u\n", tr
->tr_time
.tv_secs
, tr
->tr_time
.tv_micro
);
79 BOOL
common_BeginIO(struct timerequest
*timereq
, struct TimerBase
*TimerBase
)
81 ULONG unitNum
= (IPTR
)timereq
->tr_node
.io_Unit
;
83 BOOL addedhead
= FALSE
;
85 timereq
->tr_node
.io_Message
.mn_Node
.ln_Type
= NT_MESSAGE
;
86 timereq
->tr_node
.io_Error
= 0;
88 switch(timereq
->tr_node
.io_Command
)
91 case NSCMD_DEVICEQUERY
:
94 * CHECKME: In timer.device this is maybe a bit problematic, as the
95 * timerequest structure does not have io_Data and io_Length members
97 if (timereq
->tr_node
.io_Message
.mn_Length
< sizeof(struct IOStdReq
))
99 timereq
->tr_node
.io_Error
= IOERR_BADLENGTH
;
101 else if(ioStd(timereq
)->io_Length
< ((LONG
)OFFSET(NSDeviceQueryResult
, SupportedCommands
)) + sizeof(UWORD
*))
103 timereq
->tr_node
.io_Error
= IOERR_BADLENGTH
;
107 struct NSDeviceQueryResult
*d
= (struct NSDeviceQueryResult
*)ioStd(timereq
)->io_Data
;
109 d
->DevQueryFormat
= 0;
110 d
->SizeAvailable
= sizeof(struct NSDeviceQueryResult
);
111 d
->DeviceType
= NSDEVTYPE_TIMER
;
112 d
->DeviceSubType
= 0;
113 d
->SupportedCommands
= (UWORD
*)SupportedCommands
;
115 ioStd(timereq
)->io_Actual
= sizeof(struct NSDeviceQueryResult
);
121 GetSysTime(&timereq
->tr_time
);
123 if (!(timereq
->tr_node
.io_Flags
& IOF_QUICK
))
124 ReplyMsg(&timereq
->tr_node
.io_Message
);
126 replyit
= FALSE
; /* Because replyit will clear the timeval */
132 /* Set current time value */
133 TimerBase
->tb_CurrentTime
.tv_secs
= timereq
->tr_time
.tv_secs
;
134 TimerBase
->tb_CurrentTime
.tv_micro
= timereq
->tr_time
.tv_micro
;
135 /* Update hardware */
136 EClockSet(TimerBase
);
148 /* Query the hardware first */
149 EClockUpdate(TimerBase
);
151 if (CMPTIME(&TimerBase
->tb_CurrentTime
, &timereq
->tr_time
) <= 0)
153 timereq
->tr_time
.tv_secs
= timereq
->tr_time
.tv_micro
= 0;
154 timereq
->tr_node
.io_Error
= 0;
159 #if defined(__AROSEXEC_SMP__)
160 struct ExecLockBase
*ExecLockBase
= TimerBase
->tb_ExecLockBase
;
161 if (ExecLockBase
) ObtainLock(TimerBase
->tb_ListLock
, SPINLOCK_MODE_WRITE
, 0);
163 /* Ok, we add this to the list */
164 addToWaitList(&TimerBase
->tb_Lists
[TL_WAITVBL
], timereq
, SysBase
);
165 timereq
->tr_node
.io_Flags
&= ~IOF_QUICK
;
168 * If our request was added to the head of the list, we may need to
169 * readjust our hardware interrupt (reset elapsed time).
170 * This routine returns TRUE in order to indicate this.
172 if (TimerBase
->tb_Lists
[TL_WAITVBL
].mlh_Head
== (struct MinNode
*)timereq
)
175 #if defined(__AROSEXEC_SMP__)
176 if (ExecLockBase
) ReleaseLock(TimerBase
->tb_ListLock
, 0);
188 #if defined(__AROSEXEC_SMP__)
189 struct ExecLockBase
*ExecLockBase
= TimerBase
->tb_ExecLockBase
;
194 /* Query the hardware first */
195 EClockUpdate(TimerBase
);
199 * Adjust the time request to be relative to the
200 * elapsed time counter that we keep.
202 ADDTIME(&timereq
->tr_time
, &TimerBase
->tb_Elapsed
);
204 #if defined(__AROSEXEC_SMP__)
205 if (ExecLockBase
) ObtainLock(TimerBase
->tb_ListLock
, SPINLOCK_MODE_WRITE
, 0);
207 /* Slot it into the list. Use unit number as index. */
208 addToWaitList(&TimerBase
->tb_Lists
[unitNum
], timereq
, SysBase
);
209 timereq
->tr_node
.io_Flags
&= ~IOF_QUICK
;
211 /* Indicate if HW need to be reprogrammed */
212 if (TimerBase
->tb_Lists
[unitNum
].mlh_Head
== (struct MinNode
*)timereq
)
215 #if defined(__AROSEXEC_SMP__)
216 if (ExecLockBase
) ReleaseLock(TimerBase
->tb_ListLock
, 0);
224 case UNIT_WAITECLOCK
:
225 /* TODO: implement these (backport from m68k-Amiga) */
228 timereq
->tr_node
.io_Error
= IOERR_NOCMD
;
231 } /* switch(unitNum) */
245 timereq
->tr_node
.io_Error
= IOERR_NOCMD
;
248 } /* switch(command) */
252 timereq
->tr_time
.tv_secs
= 0;
253 timereq
->tr_time
.tv_micro
= 0;
255 if (!(timereq
->tr_node
.io_Flags
& IOF_QUICK
))
256 ReplyMsg(&timereq
->tr_node
.io_Message
);
262 void TimerProcessMicroHZ(struct TimerBase
*TimerBase
, struct ExecBase
*SysBase
, BOOL locked
)
264 #if defined(__AROSEXEC_SMP__)
265 struct ExecLockBase
*ExecLockBase
= TimerBase
->tb_ExecLockBase
;
267 struct MinList
*unit
= &TimerBase
->tb_Lists
[TL_MICROHZ
];
268 struct timerequest
*tr
, *next
;
271 * Go through the list and return requests that have completed.
272 * A completed request is one whose time is less than that of the elapsed time.
274 #if defined(__AROSEXEC_SMP__)
275 if (ExecLockBase
&& !locked
) ObtainLock(TimerBase
->tb_ListLock
, SPINLOCK_MODE_WRITE
, 0);
277 ForeachNodeSafe(unit
, tr
, next
)
279 if (CMPTIME(&TimerBase
->tb_Elapsed
, &tr
->tr_time
) <= 0)
281 /* This request has finished */
284 #ifdef USE_VBLANK_EMU
285 if (tr
== &TimerBase
->tb_vblank_timerequest
)
287 struct IntVector
*iv
= &SysBase
->IntVects
[INTB_VERTB
];
292 AROS_INTC2(iv
->iv_Code
, iv
->iv_Data
, INTF_VERTB
);
296 * Process VBlank unit.
297 * The philosophy behind is that only software which needs to measure
298 * exact intervals uses MICROHZ unit. Others use VBLANK one. As a result,
299 * VBLANK queue is generally more populated than MICROHZ one.
300 * VBLANK queue is checked more rarely than MICROHZ, this helps to decrease
303 TimerProcessVBlank(TimerBase
, SysBase
, TRUE
);
306 * Automatically requeue/reactivate request.
307 * Feature: get value every time from SysBase. This means
308 * that the user can change our VBlank rate in runtime by modifying
311 tr
->tr_time
.tv_secs
= 0;
312 tr
->tr_time
.tv_micro
= 1000000 / SysBase
->VBlankFrequency
;
313 ADDTIME(&tr
->tr_time
, &TimerBase
->tb_Elapsed
);
314 addToWaitList(unit
, tr
, SysBase
);
319 D(bug("[Timer] Replying msg 0x%p\n", tr
));
321 tr
->tr_time
.tv_secs
= 0;
322 tr
->tr_time
.tv_micro
= 0;
323 tr
->tr_node
.io_Error
= 0;
325 ReplyMsg(&tr
->tr_node
.io_Message
);
330 The first request hasn't finished, as all requests are in
331 order, we don't bother searching through the remaining
336 #if defined(__AROSEXEC_SMP__)
337 if (ExecLockBase
&& !locked
) ReleaseLock(TimerBase
->tb_ListLock
, 0);
341 void TimerProcessVBlank(struct TimerBase
*TimerBase
, struct ExecBase
*SysBase
, BOOL locked
)
343 #if defined(__AROSEXEC_SMP__)
344 struct ExecLockBase
*ExecLockBase
= TimerBase
->tb_ExecLockBase
;
347 * VBlank handler is the same as above, with two differences:
348 * 1. We don't check for VBlank emulation request.
349 * 2. VBlank unit consists of two list, not one. The second list
350 * is UNIT_WAITUNTIL queue.
351 * We could use subroutines and save some space, but we prefer speed here.
353 struct timerequest
*tr
, *next
;
356 * Go through the "wait for x seconds" list and return requests
357 * that have completed. A completed request is one whose time
358 * is less than that of the elapsed time.
360 #if defined(__AROSEXEC_SMP__)
361 if (ExecLockBase
&& !locked
) ObtainLock(TimerBase
->tb_ListLock
, SPINLOCK_MODE_WRITE
, 0);
363 ForeachNodeSafe(&TimerBase
->tb_Lists
[TL_VBLANK
], tr
, next
)
365 if (CMPTIME(&TimerBase
->tb_Elapsed
, &tr
->tr_time
) <= 0)
367 /* This request has finished */
370 tr
->tr_time
.tv_secs
= tr
->tr_time
.tv_micro
= 0;
371 tr
->tr_node
.io_Error
= 0;
373 ReplyMsg(&tr
->tr_node
.io_Message
);
380 * The other this is the "wait until a specified time". Here a request
381 * is complete if the time we are waiting for is before the current time.
383 ForeachNodeSafe(&TimerBase
->tb_Lists
[TL_WAITVBL
], tr
, next
)
385 if (CMPTIME(&TimerBase
->tb_CurrentTime
, &tr
->tr_time
) <= 0)
387 /* This request has finished */
390 tr
->tr_time
.tv_secs
= tr
->tr_time
.tv_micro
= 0;
391 tr
->tr_node
.io_Error
= 0;
393 ReplyMsg(&tr
->tr_node
.io_Message
);
398 #if defined(__AROSEXEC_SMP__)
399 if (ExecLockBase
&& !locked
) ReleaseLock(TimerBase
->tb_ListLock
, 0);
403 /****************************************************************************************/
405 static int Timer_Open(struct TimerBase
*LIBBASE
, struct timerequest
*tr
, ULONG unitNum
, ULONG flags
)
408 * Normally, we should check the length of the message and other
409 * such things, however the RKM documents an example where the
410 * length of the timerrequest isn't set, so we must not check
412 * This fixes bug SF# 741580
415 if (unitNum
> UNIT_WAITECLOCK
)
416 tr
->tr_node
.io_Error
= IOERR_OPENFAIL
;
419 tr
->tr_node
.io_Error
= 0;
420 tr
->tr_node
.io_Unit
= (NULL
+ unitNum
);
421 tr
->tr_node
.io_Device
= &LIBBASE
->tb_Device
;
427 ADD2OPENDEV(Timer_Open
, 0);