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 
58 pchain_t *priority_head;
59 
61 tdata_t *ctid;
62 
63 volatile unsigned int nb_tasks;
64 volatile unsigned int nb_system_tasks;
65 
67 //
68 // Functions
69 //
71 
72 #if 0
73 void 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 
104 void 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 
147 size_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;
160  ctid->next->prev=ctid->prev;
161  ctid->prev->next=ctid->next;
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)
186  --nb_system_tasks;
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 
206  ctid=&td_single;
207 
208  return ctid->sp_save;
209  }
210  break;
211 
212  case T_RUNNING:
213  ctid->tstate=T_SLEEPING;
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
263  ctid->tstate=T_RUNNING;
264 
265  return ctid->sp_save;
266 }
267 
269 
271 extern 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 
299 extern 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
311 
314 int 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
330 
333 int 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 
355 void tm_init(void) {
356  tdata_t* td_idle;
357 
358  // no tasks right now.
359  //
360  nb_tasks=0;
361  nb_system_tasks=0;
362  priority_head=NULL;
364 
365  // the single tasking context
366  //
367  td_single.tstate=T_RUNNING;
368  ctid=&td_single;
369 
370  // the idle task is an institution
371  //
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 
390 void 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 
410 tid_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;
441  nb_system_tasks++;
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 
531 void exit(int code) {
532  enable_irqs(); // just in case...
533  if (!(ctid->tflags & T_KERNEL))
534  mm_reaper();
535  ctid->tstate=T_ZOMBIE;
536  // Yield till dead
537  while(1)
538  yield();
539 }
540 
542 
546 wakeup_t wait_event(wakeup_t (*wakeup)(wakeup_t),wakeup_t data) {
547  ctid->wakeup =wakeup;
548  ctid->wakeup_data=data;
549  ctid->tstate =T_WAITING;
550 
551  yield();
552 
553  return ctid->wakeup_data;
554 }
555 
557 
559 static 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 
578 unsigned 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 
593 unsigned int sleep(unsigned int sec)
594 {
595  return msleep(1000*sec)/1000;
596 }
597 
599 
601 void shutdown_task(tid_t tid) {
602  tdata_t *td=(tdata_t*) tid;
603  td->tflags |= T_SHUTDOWN;
604 }
605 
607 
609 void 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 
635 void 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 
655 void 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 
#define IDLE_STACK_SIZE
should suffice for IRQ service
Definition: tm.h:48
void rom_ocia_return()
return address in ROM OCIA handler
struct _pchain_t * next
lower priority chain
Definition: tm.h:91
wakeup_t wakeup_data
user data for wakeup fn
Definition: tm.h:119
size_t * tm_scheduler(size_t *old_sp)
the process scheduler
tstate_t tstate
task state
Definition: tm.h:108
int get_battery_mv()
get current battery voltage
void free(void *ptr)
return the allocated memory to memory management.
void enable_irqs()
enable interrupt processing
Definition: irq.h:103
Interface: kernel level critical sections.
Interface: console input / output.
#define dlcd_show(a)
set a segment directly in the LCD buffer
Definition: dlcd.h:175
#define T_SLEEPING
sleeping. wants to run.
Definition: tm.h:68
volatile unsigned int nb_tasks
number of tasks
priority chain data structure
Definition: tm.h:88
#define BATTERY_LOW_THRESHOLD_MV
Definition: battery.h:42
void mm_reaper()
free all blocks allocated by the current process
Internal Interface: H8/300 bit operations.
unsigned char priority_t
task priority type
Definition: tm.h:48
#define LEAVE_KERNEL_CRITICAL_SECTION()
Definition: critsec.h:42
volatile unsigned int nb_system_tasks
#define NULL
null pointer value
Definition: mem.h:35
#define T_IDLE
idle task
Definition: tm.h:77
wakeup_t(* wakeup)(wakeup_t)
event wakeup function
Definition: tm.h:118
#define dlcd_hide(a)
clear a segment directly in the LCD buffer
Definition: dlcd.h:180
#define ENTER_KERNEL_CRITICAL_SECTION()
Definition: critsec.h:41
time_t get_system_up_time(void)
retrieve the current system time
Internal Interface: battery handling.
#define T_USER
user task
Definition: tm.h:76
#define BATTERY_NORMAL_THRESHOLD_MV
Definition: battery.h:41
void shutdown_tasks(tflags_t flags)
signed int tid_t
task id type
Definition: tm.h:143
#define TM_DEFAULT_SLICE
default multitasking timeslice
Definition: time.h:44
Internal Interface: task management.
#define T_RUNNING
running
Definition: tm.h:69
#define T_WAITING
waiting for an event
Definition: tm.h:67
Interface: reduced UNIX standard library.
void killall(priority_t p)
#define PRIO_HIGHEST
The highest possible task priority.
Definition: tm.h:55
void systime_set_switcher(void *switcher)
set task switcher vector
Definition: systime.c:326
Internal Interface: memory management.
size_t * stack_base
lower stack boundary
Definition: tm.h:116
priority_t priority
numeric priority level
Definition: tm.h:89
void lcd_show(lcd_segment segment)
show LCD segment
Definition: lcd.h:156
tdata_t * ctid
ptr to current process data
tdata_t td_single
single process process data
tflags_t tflags
task flags
Definition: tm.h:109
void disable_irqs()
disable interrupt processing
Definition: irq.h:98
void delay(unsigned ms)
uncalibrated delay loop
Definition: conio.c:204
unsigned long wakeup_t
wakeup data area type
Definition: tm.h:57
struct _pchain_t * prev
higher priority chain
Definition: tm.h:92
#define T_KERNEL
task flags
Definition: tm.h:75
struct _tdata_t * prev
previous task in queue
Definition: tm.h:113
tid_t execi(int(*code_start)(int, char **), int argc, char **argv, priority_t priority, size_t stack_size)
volatile unsigned char tm_timeslice
task time slice
Definition: systime.c:72
#define LCD_BATTERY_X
Definition: dlcd.h:159
void shutdown_task(tid_t tid)
#define T_ZOMBIE
terminated, cleanup pending
Definition: tm.h:66
#define INITIALIZE_KERNEL_CRITICAL_SECTION()
Definition: critsec.h:40
struct _tdata_t * parent
parent task
Definition: tm.h:114
void * malloc(size_t size)
allocate and return pointer to uninitialized memory
volatile unsigned char tflags_t
task flags type
Definition: tm.h:46
int tm_idle_task(int, char **)
the idle task
__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\ ")
void kill(tid_t tid)
unsigned long time_t
time type
Definition: time.h:50
#define MSECS_TO_TICKS(a)
conv. mSec's to TICKs
Definition: time.h:62
void lcd_refresh(void)
refresh the entire LCD display
Definition: lcd.c:254
void tm_init(void)
init task management
Definition: lcd.h:54
void * systime_tm_return
return address for the task switcher
size_t mm_start
end of kernel code + data
unsigned size_t
data type for memory sizes
Definition: mem.h:37
void exit(int code) __attribute__((noreturn))
unsigned int sleep(unsigned int sec)
delay execution allowing other tasks to run
task data structure
Definition: tm.h:105
struct _tdata_t * ctid
current task in chain
Definition: tm.h:94
void rom_dummy_handler()
address of an RTS instruction
#define SP_RETURN_OFFSET
return address offset on stack in words.
Definition: tm.h:46
#define T_SHUTDOWN
shutdown requested
Definition: tm.h:78
Definition: lcd.h:55
struct _tdata_t * next
next task in queue
Definition: tm.h:112
Interface: reduced standard C library.
void tm_switcher(void)
the task switcher IRQ handler
void tm_start(void)
start task management
void yield(void)
current task yields the rest of timeslice
Internal Interface: LCD control and constants.
unsigned int msleep(unsigned int msec)
Internal LNP Interface: RCX redirected IRQ vectors.
size_t * sp_save
saved stack pointer
Definition: tm.h:106
wakeup_t wait_event(wakeup_t(*wakeup)(wakeup_t), wakeup_t data)
Internal Interface: system time functions.
#define shutdown_requested()
test to see if task has been asked to shutdown
Definition: tm.h:134
void systime_set_timeslice(unsigned char slice)
set multitasking timeslice in ms
Definition: systime.c:333
pchain_t * priority
priority chain
Definition: tm.h:110

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

Generated for brickOS Kernel Developer by doxygen 1.8.9.1