tm.c
Go to the documentation of this file.
1
9/*
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * The Original Code is legOS code, released October 17, 1999.
21 *
22 * The Initial Developer of the Original Code is Markus L. Noga.
23 * Portions created by Markus L. Noga are Copyright (C) 1999
24 * Markus L. Noga. All Rights Reserved.
25 *
26 * Contributor(s): Markus L. Noga <markus@noga.de>
27 * Ben Laurie <ben@algroup.co.uk>
28 * Lou Sortman <lou (at) sunsite (dot) unc (dot) edu>
29 */
30
31#include <sys/tm.h>
32
33#ifdef CONF_TM
34
35#include <sys/critsec.h>
36#include <sys/mm.h>
37#include <sys/time.h>
38#include <sys/irq.h>
39#include <sys/bitops.h>
40#include <stdlib.h>
41#include <unistd.h>
42
43#ifdef CONF_VIS
44# include <sys/lcd.h>
45# include <conio.h>
46# include <sys/battery.h>
47#endif
48
49#define fatal(a)
50// #define fatal(a) { lcd_clear(); cputs(a); lcd_refresh(); while(1); }
51
53//
54// Variables
55//
57
58pchain_t *priority_head;
59
62
63volatile unsigned int nb_tasks;
64volatile unsigned int nb_system_tasks;
65
67//
68// Functions
69//
71
72#if 0
73void integrity_check(void) {
74 pchain_t *prio=priority_head;
75 tdata_t *td;
76
77 if(prio->prev!=NULL) { fatal("ERR10"); }
78
79 do {
80 if(prio->next) {
81 if(prio->next->prev!=prio) { fatal("ERR11"); }
82 if(prio->next->priority>prio->priority) { fatal("ERR12"); }
83 }
84 td=prio->ctid;
85 do {
86 if(td==NULL) { fatal("ERR13"); }
87 if(td->priority!=prio) { fatal("ERR14"); }
88 if(td->next->prev != td) { fatal("ERR15"); }
89 if(td->prev->next != td) { fatal("ERR16"); }
90
91 td=td->next;
92 } while(td!=prio->ctid);
93
94 prio=prio->next;
95 } while(prio);
96}
97#endif
98
99
101
104void tm_switcher(void);
105#ifndef DOXYGEN_SHOULD_SKIP_THIS
106__asm__("\n\
107.text\n\
108.align 1\n\
109.globl _tm_switcher\n\
110_tm_switcher:\n\
111 ; r6 saved by ROM\n\
112 ; r0 saved by system timer handler\n\
113\n\
114 mov.w r1,@-r7 ; save registers\n\
115 mov.w r2,@-r7 \n\
116 mov.w r3,@-r7 \n\
117 mov.w r4,@-r7 \n\
118 mov.w r5,@-r7 \n\
119\n\
120 mov.w r7,r0 ; pass sp\n\
121\n\
122 jsr _tm_scheduler ; call scheduler\n\
123\n\
124_tm_switcher_return: \n\
125 mov.w r0,r7 ; set new sp\n\
126\n\
127 mov.w @r7+,r5\n\
128 mov.w @r7+,r4\n\
129 mov.w @r7+,r3\n\
130 mov.w @r7+,r2\n\
131 mov.w @r7+,r1\n\
132\n\
133 ; r0 will be restored by system timer handler\n\
134 ; r6 will be restored by ROM\n\
135\n\
136 rts ; return to new task\n\
137");
138#endif // DOXYGEN_SHOULD_SKIP_THIS
139
140
142
147size_t *tm_scheduler(size_t *old_sp) {
148 tdata_t *next; // next task to execute
149 pchain_t *priority;
150 wakeup_t tmp;
151
152 priority=ctid->priority;
153 switch(ctid->tstate) {
154 case T_ZOMBIE:
155 if(ctid->next!=ctid) {
156 // remove from chain for this priority level
157 //
158
159 priority->ctid =ctid->prev;
162 } else {
163 // remove priority chain for this priority level
164 //
165
166 if(priority->next)
167 priority->next->prev = priority->prev;
168 if(priority->prev)
169 priority->prev->next = priority->next;
170 else
171 priority_head = priority->next;
172 free(priority);
173 }
174
175 // We're on that stack frame being freed right now,
176 // but nobody can interrupt us anyways.
177 //
178 free(ctid->stack_base); // free stack
179 free(ctid); // free task data
180
181 //
182 // FIXME: exit code?
183 //
184
185 if ((ctid->tflags & T_KERNEL)==T_KERNEL)
187
188 switch(--nb_tasks) {
189 case 1:
190#ifdef CONF_TM_DEBUG
191 if((priority_head->ctid->tflags & T_IDLE)==0) {
192 // last task is not the idle task
193 fatal("ERR00");
194 }
195#endif // CONF_TM_DEBUG
196 // only the idle task remains
197 *((priority_head->ctid->sp_save) + SP_RETURN_OFFSET ) = (size_t) &exit;
198 priority_head->ctid->tstate=T_SLEEPING;
199 break;
200
201 case 0:
202 // the last task has been removed
203 // -> stop switcher, go single tasking
204
207
208 return ctid->sp_save;
209 }
210 break;
211
212 case T_RUNNING:
214 // no break
215
216 case T_WAITING:
217 ctid->sp_save=old_sp;
218 }
219
220
221 // find next task willing to run
222 //
223 priority=priority_head;
224 next=priority->ctid->next;
225 while (1) {
226 if (next->tstate==T_SLEEPING)
227 break;
228
229 if (next->tstate==T_WAITING) {
230 if ((next->tflags & T_SHUTDOWN) != 0) {
231 next->wakeup_data = 0;
232 break;
233 }
234 ctid = next;
235 tmp = next->wakeup(next->wakeup_data);
236 if (tmp != 0) {
237 next->wakeup_data = tmp;
238 break;
239 }
240 }
241
242 if(next == priority->ctid) {
243 // if we've scanned the whole chain,
244 // go to next priority
245
246 if(priority->next != NULL)
247 priority = priority->next;
248#ifdef CONF_TM_DEBUG
249 else {
250 // FIXME: idle task has died
251 // this is a severe error.
252 fatal("ERR01");
253 }
254#else
255 else
256 priority = priority_head;
257#endif
258 next=priority->ctid->next;
259 } else
260 next=next->next;
261 }
262 ctid=next->priority->ctid=next; // execute next task
264
265 return ctid->sp_save;
266}
267
269
271extern void yield(void);
272#ifndef DOXYGEN_SHOULD_SKIP_THIS
273__asm__("\n\
274.text\n\
275.globl _yield\n\
276.align 1\n\
277_yield:\n\
278 stc ccr,r0h ; to fake an IRQ, we have to\n\
279 push r0 ; store the registers\n\
280 orc #0x80,ccr ; disable interrupts\n\
281\n\
282 push r6 ; store r6\n\
283\n\
284 mov.w #0x04d4,r0 ; store rom return addr\n\
285 push r0\n\
286\n\
287 push r0 ; store r0 (destroyed by call.)\n\
288\n\
289 mov.w #_systime_tm_return,r0 ; store systime return addr\n\
290 push r0\n\
291\n\
292 jmp @_tm_switcher ; call task switcher\n\
293");
294#endif // DOXYGEN_SHOULD_SKIP_THIS
295
297
299extern int tm_idle_task(int argc,char **argv) __attribute__ ((noreturn));
300#ifndef DOXYGEN_SHOULD_SKIP_THIS
301__asm__("\n\
302.text\n\
303.align 1\n\
304_tm_idle_task:\n\
305 sleep\n\
306 bra _tm_idle_task\n\
307");
308#endif // DOXYGEN_SHOULD_SKIP_THIS
309
310#ifdef CONF_VIS
312
314int tm_man_task(int argc, char **argv)
315{
316 int state=0;
317
318 while (!shutdown_requested()) {
319 if(nb_tasks > nb_system_tasks) state ^= 1; else state=0;
320 lcd_show(state == 0 ? man_stand : man_run);
321#ifndef CONF_LCD_REFRESH
322 lcd_refresh();
323#endif // CONF_LCD_REFRESH
324 msleep(500);
325 }
326 return 0;
327}
328
329#ifdef CONF_BATTERY_INDICATOR
331
333int tm_battery_task(int argc, char **argv) {
334 int bmv;
335
336 while (!shutdown_requested()) {
337 bmv=get_battery_mv();
338
341 else if(bmv<BATTERY_LOW_THRESHOLD_MV)
343
344 msleep(2000);
345 }
346 return 0;
347}
348#endif // CONF_BATTERY_INDICATOR
349#endif // CONF_VIS
350
352
355void tm_init(void) {
356 tdata_t* td_idle;
357
358 // no tasks right now.
359 //
360 nb_tasks=0;
362 priority_head=NULL;
364
365 // the single tasking context
366 //
369
370 // the idle task is an institution
371 //
372 td_idle=(tdata_t*)execi(&tm_idle_task,0,NULL,0,IDLE_STACK_SIZE);
373 td_idle->tflags |= T_IDLE;
374
375#ifdef CONF_VIS
376 execi(&tm_man_task, 0, NULL, 1, IDLE_STACK_SIZE);
377
378#ifdef CONF_BATTERY_INDICATOR
379 execi(&tm_battery_task, 0, NULL, 1, IDLE_STACK_SIZE);
380#endif // CONF_BATTERY_INDICATOR
381#endif // CONF_VIS
382
384}
385
386
388
390void tm_start(void) {
391 disable_irqs(); // no interruptions, please
392
394 yield(); // go!
395
396 enable_irqs(); // restored state would
397 // disallow interrupts
398}
399
401
410tid_t execi(int (*code_start)(int,char**),int argc, char **argv,
411 priority_t priority,size_t stack_size) {
412 pchain_t *pchain, *ppchain; // for traversing priority chain
413 int freepchain=0;
414
415 // get memory
416 //
417 // task & stack area belong to parent task
418 // they aren't freed by mm_reaper()
419 //
420 // avoid deadlock of memory and task semaphores
421 // by preallocation.
422
423 tdata_t *td=malloc(sizeof(tdata_t));
424 size_t *sp=malloc(stack_size);
425
426 // for allocating new priority chain
427 pchain_t *newpchain=malloc(sizeof(pchain_t));
428
429 if (td == NULL || sp == NULL || newpchain == NULL)
430 {
431 free(td);
432 free(sp);
433 free(newpchain);
434 return -1;
435 }
436
437 td->tflags = 0;
438 if ((size_t)code_start < (size_t)&mm_start)
439 {
440 td->tflags |= T_KERNEL;
442 }
443 else
444 td->tflags |= T_USER;
445
446 td->stack_base=sp; // these we know already.
447
448 sp+=(stack_size>>1); // setup initial stack
449
450 // when main() returns a value, it passes it in r0
451 // as r0 is also the register to pass single int arguments by
452 // gcc convention, we can just put the address of exit on the stack.
453
454 *(--sp)=(size_t) &exit;
455
456 // we have to construct a stack stub so tm_switcher,
457 // systime_handler and the ROM routine can fill the
458 // right values on startup.
459
460 *(--sp)=(size_t) code_start; // entry point < these two are for
461 *(--sp)=0; // ccr < rte in ROM
462 *(--sp)=0; // r6 < pop r6 in ROM
463 *(--sp)=(size_t)
464 &rom_ocia_return; // ROM return < rts in systime_handler
465
466 *(--sp)=(size_t) argc; // r0 < pop r0 in systime handler
467 *(--sp)=(size_t)
468 &systime_tm_return; // systime return < rts in tm_switcher
469
470 *(--sp)=(size_t) argv; // r1..r5 < pop r1..r5 in tm_switcher
471 *(--sp)=0;
472 *(--sp)=0;
473 *(--sp)=0;
474 *(--sp)=0;
475
476 td->sp_save=sp; // save sp for tm_switcher
477 td->tstate=T_SLEEPING; // task is waiting for execution
478 td->parent=ctid; // set parent
479
481
482 ppchain=NULL;
483 for( pchain = priority_head;
484 pchain != NULL && (pchain->priority) > priority;
485 ppchain = pchain, pchain = pchain->next
486 );
487 if(pchain==NULL || pchain->priority!=priority) {
488 // make new chain
489 //
490 newpchain->priority=priority;
491 newpchain->ctid=td;
492
493 newpchain->next=pchain;
494 if(pchain)
495 pchain->prev =newpchain;
496 newpchain->prev=ppchain;
497 if(ppchain)
498 ppchain->next=newpchain;
499 else
500 priority_head=newpchain;
501
502 // initial queue setup
503 //
504 td->prev=td->next=td;
505 td->priority=newpchain;
506 } else {
507 // add at back of queue
508 //
509 td->priority=pchain;
510 td->prev=pchain->ctid->prev;
511 td->next=pchain->ctid;
512 td->next->prev=td->prev->next=td;
513 freepchain=1; // free superfluous pchain.
514 }
515 nb_tasks++;
516
518
519 if(freepchain)
520 free(newpchain);
521
522 return (tid_t) td; // tid = (tid_t) &tdata_t_struct
523}
524
526
531void exit(int code) {
532 enable_irqs(); // just in case...
533 if (!(ctid->tflags & T_KERNEL))
534 mm_reaper();
536 // Yield till dead
537 while(1)
538 yield();
539}
540
542
547 ctid->wakeup =wakeup;
548 ctid->wakeup_data=data;
550
551 yield();
552
553 return ctid->wakeup_data;
554}
555
557
559static wakeup_t tm_sleep_wakeup(wakeup_t data) {
560 time_t remaining = ((time_t)data) - get_system_up_time();
561
562 if (((time_t)data) <= get_system_up_time())
563 {
565 return -1;
566 }
567
568 if (remaining < tm_timeslice)
569 tm_timeslice = remaining;
570
571 return 0;
572}
573
575
578unsigned int msleep(unsigned int msec)
579{
580#if defined(CONF_TIME) && defined(CONF_TM)
581 if (wait_event(&tm_sleep_wakeup, get_system_up_time() + MSECS_TO_TICKS(msec)) == 0)
582 return (MSECS_TO_TICKS(msec) - get_system_up_time());
583#else
584 delay(msec);
585#endif
586 return 0;
587}
588
590
593unsigned int sleep(unsigned int sec)
594{
595 return msleep(1000*sec)/1000;
596}
597
599
601void shutdown_task(tid_t tid) {
602 tdata_t *td=(tdata_t*) tid;
603 td->tflags |= T_SHUTDOWN;
604}
605
607
609void shutdown_tasks(tflags_t flags) {
610 pchain_t* pchain;
611 tdata_t* td;
612
614
615 pchain = priority_head;
616 while (pchain != NULL) {
617 td = pchain->ctid;
618 do {
619 if ((td->tflags & flags) != 0) {
620 // signal shutdown
621 //
622 td->tflags |= T_SHUTDOWN;
623 }
624 td = td->next;
625 } while (td != pchain->ctid);
626 pchain = pchain->next;
627 }
628
630}
631
633
635void kill(tid_t tid) {
636 tdata_t *td=(tdata_t*) tid;
637 if(td==ctid)
638 exit(-1);
639 else {
640 // when the task is switched to the next time,
641 // make it exit immediatlely.
642
644
645 *( (td->sp_save) + SP_RETURN_OFFSET )=(size_t) &exit;
646 td->tstate=T_SLEEPING; // in case it's waiting.
647
649 }
650}
651
653
655void killall(priority_t prio) {
656 pchain_t *pchain;
657 tdata_t *td;
658 tflags_t flags = T_KERNEL | T_IDLE;
659
660 if (prio == PRIO_HIGHEST)
661 flags = T_IDLE;
662
664
665 // find first chain with smaller or equal priority.
666 //
667 pchain=priority_head;
668 while(pchain!=NULL && prio<pchain->priority)
669 pchain=pchain->next;
670
671 while(pchain!=NULL) {
672 td=pchain->ctid;
673 do {
674 if((td!=ctid) && ((td->tflags & flags) == 0)) {
675 // kill it
676 //
677 *( (td->sp_save) + SP_RETURN_OFFSET )=(size_t) &exit;
678 td->tstate=T_SLEEPING; // in case it's waiting.
679 }
680 td=td->next;
681 } while(td!=pchain->ctid);
682 pchain=pchain->next;
683 }
684
686}
687
688#endif // CONF_TM
689
__asm__("\n\ .text\n\ .globl _atomic_inc\n\ _atomic_inc:\n\ stc ccr, r1h ; save flags\n\ orc #0x80, ccr ; disable all but NMI\n\ mov.b @r0, r1l\n\ inc r1l\n\ mov.b r1l, @r0\n\ ldc r1h, ccr ; restore flags\n\ rts\n\ ")
Internal Interface: battery handling.
#define BATTERY_NORMAL_THRESHOLD_MV
Definition: battery.h:41
#define BATTERY_LOW_THRESHOLD_MV
Definition: battery.h:42
int get_battery_mv()
get current battery voltage
Internal Interface: H8/300 bit operations.
void delay(unsigned ms)
uncalibrated delay loop
Definition: conio.c:204
Interface: console input / output.
#define dlcd_hide(a)
clear a segment directly in the LCD buffer
Definition: dlcd.h:180
#define LCD_BATTERY_X
Definition: dlcd.h:159
#define dlcd_show(a)
set a segment directly in the LCD buffer
Definition: dlcd.h:175
Internal LNP Interface: RCX redirected IRQ vectors.
void rom_ocia_return()
return address in ROM OCIA handler
void rom_dummy_handler()
address of an RTS instruction
void disable_irqs()
disable interrupt processing
Definition: irq.h:98
void enable_irqs()
enable interrupt processing
Definition: irq.h:103
void lcd_refresh(void)
refresh the entire LCD display
Definition: lcd.c:254
unsigned size_t
data type for memory sizes
Definition: mem.h:37
#define NULL
null pointer value
Definition: mem.h:35
Internal Interface: memory management.
void mm_reaper()
free all blocks allocated by the current process
size_t mm_start
end of kernel code + data
@ man_stand
Definition: lcd.h:54
@ man_run
Definition: lcd.h:55
void lcd_show(lcd_segment segment)
show LCD segment
Definition: lcd.h:156
Interface: reduced standard C library.
void * malloc(size_t size)
allocate and return pointer to uninitialized memory
void free(void *ptr)
return the allocated memory to memory management.
priority chain data structure
Definition: tm.h:88
priority_t priority
numeric priority level
Definition: tm.h:89
struct _pchain_t * prev
higher priority chain
Definition: tm.h:92
struct _tdata_t * ctid
current task in chain
Definition: tm.h:94
struct _pchain_t * next
lower priority chain
Definition: tm.h:91
task data structure
Definition: tm.h:105
wakeup_t(* wakeup)(wakeup_t)
event wakeup function
Definition: tm.h:118
pchain_t * priority
priority chain
Definition: tm.h:110
struct _tdata_t * next
next task in queue
Definition: tm.h:112
struct _tdata_t * parent
parent task
Definition: tm.h:114
tstate_t tstate
task state
Definition: tm.h:108
size_t * sp_save
saved stack pointer
Definition: tm.h:106
wakeup_t wakeup_data
user data for wakeup fn
Definition: tm.h:119
size_t * stack_base
lower stack boundary
Definition: tm.h:116
struct _tdata_t * prev
previous task in queue
Definition: tm.h:113
tflags_t tflags
task flags
Definition: tm.h:109
Interface: kernel level critical sections.
#define ENTER_KERNEL_CRITICAL_SECTION()
Definition: critsec.h:41
#define INITIALIZE_KERNEL_CRITICAL_SECTION()
Definition: critsec.h:40
#define LEAVE_KERNEL_CRITICAL_SECTION()
Definition: critsec.h:42
Internal Interface: LCD control and constants.
Internal Interface: system time functions.
void * systime_tm_return
return address for the task switcher
#define TM_DEFAULT_SLICE
default multitasking timeslice
Definition: time.h:44
Internal Interface: task management.
#define IDLE_STACK_SIZE
should suffice for IRQ service
Definition: tm.h:48
void tm_switcher(void)
the task switcher IRQ handler
#define SP_RETURN_OFFSET
return address offset on stack in words.
Definition: tm.h:46
void tm_init(void)
init task management
volatile unsigned int nb_tasks
number of tasks
tdata_t td_single
single process process data
volatile unsigned int nb_system_tasks
void tm_start(void)
start task management
int tm_idle_task(int, char **)
the idle task
size_t * tm_scheduler(size_t *old_sp)
the process scheduler
tdata_t * ctid
ptr to current process data
time_t get_system_up_time(void)
retrieve the current system time
void systime_set_switcher(void *switcher)
set task switcher vector
Definition: systime.c:326
volatile unsigned char tm_timeslice
task time slice
Definition: systime.c:72
void systime_set_timeslice(unsigned char slice)
set multitasking timeslice in ms
Definition: systime.c:333
#define MSECS_TO_TICKS(a)
conv. mSec's to TICKs
Definition: time.h:62
unsigned long time_t
time type
Definition: time.h:50
#define T_SHUTDOWN
shutdown requested
Definition: tm.h:78
volatile unsigned char tflags_t
task flags type
Definition: tm.h:46
#define T_RUNNING
running
Definition: tm.h:69
#define T_SLEEPING
sleeping. wants to run.
Definition: tm.h:68
#define shutdown_requested()
test to see if task has been asked to shutdown
Definition: tm.h:134
signed int tid_t
task id type
Definition: tm.h:143
#define PRIO_HIGHEST
The highest possible task priority.
Definition: tm.h:55
#define T_WAITING
waiting for an event
Definition: tm.h:67
#define T_IDLE
idle task
Definition: tm.h:77
unsigned long wakeup_t
wakeup data area type
Definition: tm.h:57
#define T_KERNEL
task flags
Definition: tm.h:75
#define T_ZOMBIE
terminated, cleanup pending
Definition: tm.h:66
#define T_USER
user task
Definition: tm.h:76
unsigned char priority_t
task priority type
Definition: tm.h:48
Interface: reduced UNIX standard library.
wakeup_t wait_event(wakeup_t(*wakeup)(wakeup_t), wakeup_t data)
Definition: unistd.h:112
#define sleep(s)
Definition: unistd.h:122
#define msleep(s)
Definition: unistd.h:123

brickOS is released under the Mozilla Public License.
Original code copyright 1998-2005 by the authors.

Generated for brickOS Kernel Developer by doxygen 1.9.4