- Set a default PCM volume so that something can be heard when driver is
[AROS.git] / arch / all-unix / kernel / coretest.c
blob6465cc11ba3953522806088cf9c2eb714776895d
1 /*
2 Copyright © 1995-2010, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <exec/lists.h>
7 #include <exec/tasks.h>
8 #include <exec/execbase.h>
9 #include <unistd.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <kernel_cpu.h>
15 #include <sys/time.h>
17 #include "etask.h"
19 /* Prototypes */
20 void Dispatch (void);
21 void Enqueue (struct List * list, struct Node * node);
22 void AddTail (struct List * list, struct Node * node);
23 void Switch (void);
25 /* This is used to count the number of interrupts. */
26 ULONG cnt;
28 /* This flag means that a task switch is pending */
29 #define SB_SAR 15
30 #define SF_SAR 0x8000
32 /* Dummy SysBase */
33 struct ExecBase _SysBase;
34 struct ExecBase* SysBase = &_SysBase;
36 /* Dummy KernelBase */
37 struct PlatformData
39 sigset_t sig_int_mask;
42 struct PlatformData Kernel_PlatformData;
44 #define PD(x) Kernel_PlatformData
46 /* Some tasks */
47 struct Task Task1, Task2, Task3, Task4, TaskMain;
49 /* List functions */
50 void AddTail (struct List * list, struct Node * node)
52 ADDTAIL(list,node);
53 } /* AddTail */
55 void AddHead (struct List * list, struct Node * node)
57 ADDHEAD (list, node);
58 } /* AddHead */
60 void Remove (struct Node * node)
62 REMOVE(node);
63 } /* Remove */
65 /* Enter a node into a sorted list */
66 void Enqueue (struct List * list, struct Node * node)
68 struct Node * next;
70 ForeachNode (list, next)
72 /* Look for the first node with a lower priority */
73 if (node->ln_Pri > next->ln_Pri)
74 break;
77 /* Insert "node" before "next" */
78 node->ln_Pred = next->ln_Pred;
79 next->ln_Pred->ln_Succ = node;
80 next->ln_Pred = node;
81 node->ln_Succ = next;
82 } /* Enqueue */
84 #define STR(x) (x && x->ln_Name ? (char *)x->ln_Name : "NULL")
86 /* Print a list with nodes with names. */
87 void PrintList (struct List * list)
89 struct Node * node;
90 int t = 0;
92 printf ("list %p { head=%s, tail=%s } = ", list,
93 STR(list->lh_Head), STR(list->lh_TailPred));
95 for (node=GetHead(list); node; node=GetSucc(node))
97 printf ("%s (%p { succ=%s pred=%s pri=%d}), ", node->ln_Name, node,
98 STR(GetSucc(node)), STR(GetPred(node)),
99 node->ln_Pri
101 if (++t > 10)
102 break;
105 printf ("\n");
108 #undef STR
110 /* Macro to get a pointer to the current running task */
111 #define THISTASK (SysBase->ThisTask)
114 Disable and enable signals. Don't use these in the signal handler
115 because then some signal might break the signal handler and then
116 stack will overflow.
118 #define DISABLE sigprocmask (SIG_BLOCK, &Kernel_PlatformData.sig_int_mask, NULL)
119 #define ENABLE sigprocmask (SIG_UNBLOCK, &Kernel_PlatformData.sig_int_mask, NULL)
121 /* Enable/Disable interrupts */
122 void Disable (void)
125 Disable signals only if they are not already. The initial value of
126 IDNestCnt is -1.
128 if (SysBase->IDNestCnt++ < 0)
130 DISABLE;
132 } /* Disable */
134 void Enable (void)
137 Enable signals only if the number of calls of Enable() matches the
138 calls of Disable().
140 if (--SysBase->IDNestCnt < 0)
142 ENABLE;
144 } /* Enable */
146 /* Allow/forbid task switches */
147 void Forbid (void)
150 Task switches are only allowed if TDNestCnt is < 0. The initial
151 value of TDNestCnt is -1.
153 ++ SysBase->TDNestCnt;
154 } /* Forbid */
156 void Permit (void)
158 /* Count calls and check if interrupts are allowed. */
159 if (--SysBase->TDNestCnt < 0
160 && SysBase->IDNestCnt < 0
164 Task switches are allowed again. If a switch is pending
165 right now, do it.
167 if (SysBase->SysFlags & SF_SAR)
169 /* Clear flag */
170 SysBase->SysFlags &= ~SF_SAR;
172 /* Do task switch */
173 Switch ();
176 } /* Permit */
178 /* Main routine: Insert a task into the list of tasks to run. */
179 void Reschedule (struct Task * task)
182 The code in here defines how "good" the task switching is.
183 There are seveal things which should be taken into account:
185 1. No task should block the CPU forever even if it is an
186 endless loop.
188 2. Tasks with a higher priority should get the CPU more often.
190 3. Tasks with a low priority should get the CPU every now and then.
192 Enqueue() fulfills 2 but not 1 and 3. AddTail() fulfills 1 and 3.
194 A better way would be to "deteriorate" a task, ie. decrease the
195 priority and use Enqueue() but at this time, I can't do this
196 because I have no good way to extend the task structure (I
197 need a variable to store the original prio).
199 AddTail (&SysBase->TaskReady, (struct Node *)task);
200 } /* Reschedule */
202 /* Switch to a new task if the current task is not running and no
203 exception has been raised. */
204 ULONG Wait (ULONG sigmask);
205 void Switch (void)
207 struct Task * task = THISTASK;
209 /* Check that the task is not running and no exception is pending */
210 if (task->tc_State != TS_RUN && !(task->tc_Flags & TF_EXCEPT) )
212 /* Allow signals. */
213 ENABLE;
216 Make sure there is a signal. This is somewhat tricky: The
217 current task (which is excuting this funcion) will loose the
218 CPU (ie. some code of another task will be executed). Then
219 at some time in the future, the current task (ie. the one
220 that has executed the kill()) will get the CPU back and
221 continue with the code after the kill().
223 kill (getpid(), SIGALRM);
225 } /* Switch */
228 This waits for a "signal". That's not a Unix signal but a flag set
229 by some other task (eg. if you send a command to a device, the
230 device will call you back when it has processes the command.
231 When this happens such a "signal" will be set). The task will be
232 suspended until any of the bits given to Wait() are set in the
233 tasks' signal mask. Again, this signal mask has nothing to do
234 with the Unix signal mask.
236 It's a dummy right now. All it does is switch to another task.
238 ULONG Wait (ULONG sigmask)
240 struct Task * task = THISTASK;
243 Task is no longer running. If we didn't do this, Switch() would do
244 nothing.
246 task->tc_State = TS_READY;
248 /* Let another task run. */
249 Switch ();
251 /* When I get the CPU back, this code is executed */
252 return 0;
255 /* Simple main for a task: Print a message and wait for a signal. */
256 void Main1 (void)
258 struct Task * task = THISTASK;
259 STRPTR name = task->tc_Node.ln_Name;
261 while (1)
263 printf ("Main1: %s\n", name);
265 Wait (1);
269 /* Another method of waiting (but an inferior one). */
270 void busy_wait (void)
272 int t;
274 for (t=cnt; t==cnt; );
277 /* Same as Main1 but wait by polling */
278 void Main2 (void)
280 struct Task * task = THISTASK;
281 STRPTR name = task->tc_Node.ln_Name;
283 while (1)
285 printf ("Main2: %s\n", name);
288 Kids, don't do this at home. I'm a professional.
290 This is to make sure even endless loops don't harm the
291 system. Even if this task has a higher priority than any
292 other task in the system, the other tasks will get the
293 CPU every now and then.
295 busy_wait();
299 #define DEBUG_STACK 0
300 #define STACKOFFSET 0
303 The signal handler. It will store the current tasks context and
304 switch to another task if this is allowed right now.
306 static void sighandler (int sig, regs_t *sc)
308 cnt ++;
310 /* Are task switches allowed ? */
311 if (SysBase->TDNestCnt < 0)
313 struct AROSCPUContext *ctx = GetIntETask(THISTASK)->iet_Context;
315 #if DEBUG_STACK
316 PRINT_SC(sc);
317 #endif
319 /* Save all registers and the stack pointer */
320 SAVEREGS(ctx, sc);
321 THISTASK->tc_SPReg = (APTR)SP(sc);
323 /* Find a new task to run */
324 Dispatch ();
325 /* Restore signal mask */
326 if (SysBase->IDNestCnt < 0)
327 SC_ENABLE(sc);
328 else
329 SC_DISABLE(sc);
331 ctx = GetIntETask(THISTASK)->iet_Context;
332 RESTOREREGS(ctx, sc);
333 SP(sc) = (IPTR)THISTASK->tc_SPReg;
335 #if DEBUG_STACK
336 PRINT_SC(sc);
337 #endif
340 else
342 /* Set flag: switch tasks as soon as switches are allowed again */
343 SysBase->SysFlags |= SF_SAR;
345 } /* sighandler */
347 /* Let the sigcore do it's magic */
348 GLOBAL_SIGNAL_INIT(sighandler)
350 /* Find another task which is allowed to run and modify SysBase accordingly */
351 void Dispatch (void)
353 struct Task * this = THISTASK;
354 struct Task * task;
356 /* Check the stack */
357 if (this->tc_SPReg <= this->tc_SPLower
358 || this->tc_SPReg >= this->tc_SPUpper
361 printf ("illegal stack (SP %p, lower %p, upper %p)\n", this->tc_SPReg, this->tc_SPLower, this->tc_SPUpper);
364 /* Try to find a task which is ready to run */
365 if ((task = (struct Task *)GetHead (&SysBase->TaskReady)))
367 printf ("Dispatch: Old = %s (Stack = %lx), new = %s\n",
368 this->tc_Node.ln_Name,
369 (IPTR)this->tc_SPUpper - (IPTR)this->tc_SPReg,
370 task->tc_Node.ln_Name);
372 /* Remove new task from the list */
373 Remove ((struct Node *)task);
375 /* Sort the old task into the list of tasks which want to run */
376 Reschedule (this);
378 /* Save disable counters */
379 this->tc_TDNestCnt = SysBase->TDNestCnt;
380 this->tc_IDNestCnt = SysBase->IDNestCnt;
382 /* Set new counters */
383 SysBase->TDNestCnt = task->tc_TDNestCnt;
384 SysBase->IDNestCnt = task->tc_IDNestCnt;
386 /* Switch task */
387 THISTASK = task;
389 /* Set new states of the tasks */
390 this->tc_State = TS_READY;
391 task->tc_State = TS_RUN;
393 printf("leaving dispatch!\n");
394 } /* Dispatch */
397 Initialize the system: Install an interrupt handler and make sure
398 it is called at 50Hz
400 void InitCore(void)
402 struct sigaction sa;
403 struct itimerval interval;
405 /* Install a handler for the ALARM signal */
406 sa.sa_handler = (SIGHANDLER_T)sighandler_gate;
407 sa.sa_flags = SA_RESTART;
408 #ifdef __linux__
409 sa.sa_restorer = NULL;
410 #endif /* __linux__ */
411 sigfillset (&sa.sa_mask);
413 sigaction (SIGALRM, &sa, NULL);
415 /* Set 50Hz intervall for ALARM signal */
416 interval.it_interval.tv_sec = interval.it_value.tv_sec = 1;
417 interval.it_interval.tv_usec = interval.it_value.tv_usec = 1000000/50;
419 setitimer (ITIMER_REAL, &interval, NULL);
420 } /* InitCore */
422 #define STACK_SIZE 4096
424 /* Create a new task */
425 void AddTask (struct Task * task, STRPTR name, BYTE pri, APTR pc)
427 IPTR *sp;
428 struct AROSCPUContext *ctx;
430 /* Init task structure */
431 memset (task, 0, sizeof (struct Task));
433 /* Init fields with real values */
434 task->tc_Node.ln_Pri = pri;
435 task->tc_Node.ln_Name = name;
436 task->tc_State = TS_READY;
438 /* Allow task switches and signals */
439 task->tc_TDNestCnt = -1;
440 task->tc_IDNestCnt = -1;
442 /* Allocate a stack */
443 sp = malloc (STACK_SIZE * sizeof(IPTR));
446 Copy bounds of stack in task structure. Note that the stack
447 grows to small addresses (ie. storing something on the stack
448 decreases the stack pointer).
450 task->tc_SPLower = sp;
451 sp += STACK_SIZE;
452 task->tc_SPUpper = sp;
455 Let the sigcore do it's magic. Create a frame from which an
456 initial task context can be restored from.
459 task->tc_UnionETask.tc_ETask = malloc(sizeof(struct IntETask));
460 task->tc_Flags |= TF_ETASK;
461 ctx = malloc(sizeof(struct AROSCPUContext));
462 GetIntETask(task)->iet_Context = ctx;
464 PREPARE_INITIAL_CONTEXT(ctx);
465 PREPARE_INITIAL_FRAME(ctx, sp, (IPTR)pc);
467 /* Save new stack pointer */
468 task->tc_SPReg = sp;
470 /* Add task to queue by priority */
471 Enqueue (&SysBase->TaskReady, (struct Node *)task);
472 } /* AddTask */
475 Main routine: Create four tasks (three with the Mains above and one
476 for main(). Wait for some task switches then terminate cleanly.
478 int main (int argc, char ** argv)
480 /* Init SysBase */
481 NEWLIST (&SysBase->TaskReady);
482 NEWLIST (&SysBase->TaskWait);
484 sigfillset(&Kernel_PlatformData.sig_int_mask);
486 /* Signals and task switches are not allowed right now */
487 SysBase->IDNestCnt = 0;
488 SysBase->TDNestCnt = 0;
490 /* Add three tasks */
491 AddTask (&Task1, "Task 1", 0, Main1);
492 AddTask (&Task2, "Task 2", 5, Main2);
493 AddTask (&Task3, "Task 3", 0, Main2);
494 PrintList (&SysBase->TaskReady);
497 Add main task. Make sure the stack check is ok. This task is *not*
498 added to the list. It is stored in THISTASK and will be added to
499 the list at the next call to Dispatch().
501 Also a trick with the stack: This is the stack of the Unix process.
502 We don't know where it lies in memory nor how big it is (it can
503 grow), so we set the maximum possible limits.
505 TaskMain.tc_Node.ln_Pri = 0;
506 TaskMain.tc_Node.ln_Name = "Main";
507 TaskMain.tc_State = TS_READY;
508 TaskMain.tc_SPLower = 0;
509 TaskMain.tc_SPUpper = (APTR)-1;
511 TaskMain.tc_UnionETask.tc_ETask = malloc(sizeof(struct IntETask));
512 TaskMain.tc_Flags |= TF_ETASK;
513 ((struct IntETask *)TaskMain.tc_UnionETask.tc_ETask)->iet_Context = malloc(SIZEOF_ALL_REGISTERS);
515 /* The currently running task is me, myself and I */
516 THISTASK = &TaskMain;
518 /* Start interrupts and allow them. */
519 InitCore ();
520 Enable ();
521 Permit ();
523 /* Wait for 10000 signals */
524 while (cnt < 20)
526 printf ("%6d\n", cnt);
528 /* Wait for a "signal" from another task. */
529 Wait (1);
532 /* Make sure we don't get disturbed in the cleanup */
533 Disable ();
535 /* Show how many signals have been processed */
536 printf ("Exit after %d signals\n", cnt);
538 return 0;
539 } /* main */