Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

kernel/tm.c

Go to the documentation of this file.
00001 
00009 /*
00010  *  The contents of this file are subject to the Mozilla Public License
00011  *  Version 1.0 (the "License"); you may not use this file except in
00012  *  compliance with the License. You may obtain a copy of the License at
00013  *  http://www.mozilla.org/MPL/
00014  *
00015  *  Software distributed under the License is distributed on an "AS IS"
00016  *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
00017  *  License for the specific language governing rights and limitations
00018  *  under the License.
00019  *
00020  *  The Original Code is legOS code, released October 17, 1999.
00021  *
00022  *  The Initial Developer of the Original Code is Markus L. Noga.
00023  *  Portions created by Markus L. Noga are Copyright (C) 1999
00024  *  Markus L. Noga. All Rights Reserved.
00025  *
00026  *  Contributor(s): Markus L. Noga <markus@noga.de>
00027  *                  Ben Laurie <ben@algroup.co.uk>
00028  *                  Lou Sortman <lou (at) sunsite (dot) unc (dot) edu>
00029  */
00030 
00031 #include <sys/tm.h>
00032 
00033 #ifdef CONF_TM
00034 
00035 #include <sys/critsec.h>
00036 #include <sys/mm.h>
00037 #include <sys/time.h>
00038 #include <sys/irq.h>
00039 #include <sys/bitops.h>
00040 #include <stdlib.h>
00041 #include <unistd.h>
00042 
00043 #ifdef CONF_VIS
00044 # include <sys/lcd.h>
00045 # include <conio.h>
00046 # include <sys/battery.h>
00047 #endif
00048 
00049 #define fatal(a)
00050 // #define fatal(a)  { lcd_clear(); cputs(a); lcd_refresh(); while(1); }
00051 
00053 //
00054 // Variables
00055 //
00057 
00058 pchain_t *priority_head;                        
00059 
00060 tdata_t td_single;                              
00061 tdata_t *ctid;                                  
00062 
00063 volatile unsigned int nb_tasks;                 
00064 volatile unsigned int nb_system_tasks;          
00065 
00067 //
00068 // Functions
00069 //
00071 
00072 #if 0
00073 void integrity_check(void) {
00074   pchain_t *prio=priority_head;
00075   tdata_t *td;
00076   
00077   if(prio->prev!=NULL) {  fatal("ERR10");  }
00078   
00079   do {
00080     if(prio->next) {
00081       if(prio->next->prev!=prio) { fatal("ERR11"); }
00082       if(prio->next->priority>prio->priority) { fatal("ERR12"); }
00083     }
00084     td=prio->ctid;
00085     do {
00086       if(td==NULL) { fatal("ERR13"); }
00087       if(td->priority!=prio) { fatal("ERR14"); }
00088       if(td->next->prev != td) { fatal("ERR15"); }
00089       if(td->prev->next != td) { fatal("ERR16"); }
00090       
00091       td=td->next;
00092     } while(td!=prio->ctid);
00093     
00094     prio=prio->next;
00095   } while(prio);
00096 } 
00097 #endif 
00098     
00099 
00101 
00104 void tm_switcher(void);
00105 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00106 __asm__("\n\
00107 .text\n\
00108 .align 1\n\
00109 .globl _tm_switcher\n\
00110 _tm_switcher:\n\
00111       ; r6 saved by ROM\n\
00112       ; r0 saved by system timer handler\n\
00113 \n\
00114       mov.w r1,@-r7                           ; save registers\n\
00115       mov.w r2,@-r7 \n\
00116       mov.w r3,@-r7 \n\
00117       mov.w r4,@-r7 \n\
00118       mov.w r5,@-r7 \n\
00119 \n\
00120       mov.w r7,r0                             ; pass sp\n\
00121 \n\
00122       jsr _tm_scheduler                       ; call scheduler\n\
00123 \n\
00124 _tm_switcher_return:    \n\
00125       mov.w r0,r7                             ; set new sp\n\
00126 \n\
00127       mov.w @r7+,r5\n\
00128       mov.w @r7+,r4\n\
00129       mov.w @r7+,r3\n\
00130       mov.w @r7+,r2\n\
00131       mov.w @r7+,r1\n\
00132 \n\
00133       ; r0 will be restored by system timer handler\n\
00134       ; r6 will be restored by ROM\n\
00135 \n\
00136       rts                                     ; return to new task\n\
00137 ");
00138 #endif  // DOXYGEN_SHOULD_SKIP_THIS
00139 
00140 
00142 
00147 size_t *tm_scheduler(size_t *old_sp) {
00148   tdata_t  *next;                             // next task to execute
00149   pchain_t *priority;
00150   wakeup_t tmp;
00151 
00152   priority=ctid->priority;
00153   switch(ctid->tstate) {
00154   case T_ZOMBIE:
00155     if(ctid->next!=ctid) {
00156       // remove from chain for this priority level
00157       //
00158 
00159       priority->ctid  =ctid->prev;
00160       ctid->next->prev=ctid->prev;
00161       ctid->prev->next=ctid->next;
00162     } else {
00163       // remove priority chain for this priority level
00164       //
00165 
00166       if(priority->next)
00167         priority->next->prev = priority->prev;
00168       if(priority->prev)
00169         priority->prev->next = priority->next;
00170       else
00171         priority_head = priority->next;
00172       free(priority);
00173     }
00174       
00175     // We're on that stack frame being freed right now,
00176     // but nobody can interrupt us anyways.
00177     //
00178     free(ctid->stack_base);                   // free stack
00179     free(ctid);                               // free task data
00180 
00181     //
00182     // FIXME: exit code?
00183     //
00184 
00185     if ((ctid->tflags & T_KERNEL)==T_KERNEL)
00186       --nb_system_tasks;
00187 
00188     switch(--nb_tasks) {
00189     case 1:
00190 #ifdef CONF_TM_DEBUG    
00191       if((priority_head->ctid->tflags & T_IDLE)==0) {
00192         // last task is not the idle task
00193         fatal("ERR00");
00194       }
00195 #endif // CONF_TM_DEBUG
00196       // only the idle task remains
00197       *((priority_head->ctid->sp_save) + SP_RETURN_OFFSET ) = (size_t) &exit;
00198       priority_head->ctid->tstate=T_SLEEPING;
00199       break;
00200     
00201     case 0:
00202       // the last task has been removed
00203       // -> stop switcher, go single tasking
00204     
00205       systime_set_switcher(&rom_dummy_handler);
00206       ctid=&td_single;
00207     
00208       return ctid->sp_save;
00209     }
00210     break;
00211 
00212   case T_RUNNING:
00213     ctid->tstate=T_SLEEPING;
00214     // no break
00215 
00216   case T_WAITING:
00217     ctid->sp_save=old_sp;
00218   }
00219 
00220 
00221   // find next task willing to run
00222   //  
00223   priority=priority_head;
00224   next=priority->ctid->next;
00225   while (1) {
00226     if (next->tstate==T_SLEEPING)
00227       break;
00228     
00229     if (next->tstate==T_WAITING) {
00230       if ((next->tflags & T_SHUTDOWN) != 0) {
00231         next->wakeup_data = 0;
00232         break;
00233       }
00234       ctid = next;
00235       tmp = next->wakeup(next->wakeup_data);
00236       if (tmp != 0) {
00237         next->wakeup_data = tmp;
00238         break;
00239       }
00240     }
00241     
00242     if(next == priority->ctid) {
00243       // if we've scanned the whole chain,
00244       // go to next priority
00245       
00246       if(priority->next != NULL) 
00247         priority = priority->next;
00248 #ifdef CONF_TM_DEBUG        
00249       else {
00250         // FIXME: idle task has died
00251         //        this is a severe error.
00252         fatal("ERR01");
00253       }
00254 #else
00255       else
00256         priority = priority_head;
00257 #endif
00258       next=priority->ctid->next;
00259     } else
00260       next=next->next;
00261   }
00262   ctid=next->priority->ctid=next;             // execute next task
00263   ctid->tstate=T_RUNNING;
00264 
00265   return ctid->sp_save;
00266 }
00267 
00269 
00271 extern void yield(void);
00272 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00273 __asm__("\n\
00274 .text\n\
00275 .globl _yield\n\
00276 .align 1\n\
00277 _yield:\n\
00278       stc     ccr,r0h                ; to fake an IRQ, we have to\n\
00279       push    r0                     ; store the registers\n\
00280       orc     #0x80,ccr              ; disable interrupts\n\
00281 \n\
00282       push    r6                     ; store r6\n\
00283 \n\
00284       mov.w   #0x04d4,r0             ; store rom return addr\n\
00285       push    r0\n\
00286 \n\
00287       push    r0                     ; store r0 (destroyed by call.)\n\
00288 \n\
00289       mov.w   #_systime_tm_return,r0 ; store systime return addr\n\
00290       push    r0\n\
00291 \n\
00292       jmp     @_tm_switcher          ; call task switcher\n\
00293 ");
00294 #endif  // DOXYGEN_SHOULD_SKIP_THIS
00295 
00297 
00299 extern int tm_idle_task(int argc,char **argv) __attribute__ ((noreturn));
00300 #ifndef DOXYGEN_SHOULD_SKIP_THIS
00301 __asm__("\n\
00302 .text\n\
00303 .align 1\n\
00304 _tm_idle_task:\n\
00305       sleep\n\
00306       bra _tm_idle_task\n\
00307 ");
00308 #endif  // DOXYGEN_SHOULD_SKIP_THIS
00309 
00310 #ifdef CONF_VIS
00311 
00312 
00314 int tm_man_task(int argc, char **argv)
00315 {
00316   int state=0;
00317 
00318   while (!shutdown_requested()) {
00319     if(nb_tasks > nb_system_tasks) state ^= 1; else state=0;
00320     lcd_show(state == 0 ? man_stand : man_run);
00321 #ifndef CONF_LCD_REFRESH
00322     lcd_refresh();
00323 #endif // CONF_LCD_REFRESH
00324     msleep(500);
00325   }
00326   return 0;
00327 }
00328 
00329 #ifdef CONF_BATTERY_INDICATOR
00330 
00331 
00333 int tm_battery_task(int argc, char **argv) {
00334   int bmv;
00335 
00336   while (!shutdown_requested()) {
00337     bmv=get_battery_mv();
00338 
00339     if(bmv>BATTERY_NORMAL_THRESHOLD_MV)
00340       dlcd_hide(LCD_BATTERY_X);
00341     else if(bmv<BATTERY_LOW_THRESHOLD_MV)
00342       dlcd_show(LCD_BATTERY_X);
00343 
00344     msleep(2000);
00345   }
00346   return 0;
00347 }
00348 #endif // CONF_BATTERY_INDICATOR
00349 #endif // CONF_VIS
00350 
00352 
00355 void tm_init(void) {
00356   tdata_t* td_idle;
00357 
00358   // no tasks right now.
00359   //
00360   nb_tasks=0;
00361   nb_system_tasks=0;
00362   priority_head=NULL;
00363   INITIALIZE_KERNEL_CRITICAL_SECTION(); 
00364  
00365   // the single tasking context
00366   //
00367   td_single.tstate=T_RUNNING;
00368   ctid=&td_single;
00369 
00370   // the idle task is an institution
00371   //  
00372   td_idle=(tdata_t*)execi(&tm_idle_task,0,NULL,0,IDLE_STACK_SIZE);
00373   td_idle->tflags |= T_IDLE;
00374 
00375 #ifdef CONF_VIS
00376   execi(&tm_man_task, 0, NULL, 1, IDLE_STACK_SIZE);
00377 
00378 #ifdef CONF_BATTERY_INDICATOR
00379   execi(&tm_battery_task, 0, NULL, 1, IDLE_STACK_SIZE);
00380 #endif // CONF_BATTERY_INDICATOR
00381 #endif // CONF_VIS
00382 
00383   systime_set_timeslice(TM_DEFAULT_SLICE);
00384 } 
00385 
00386 
00388 
00390 void tm_start(void) {
00391   disable_irqs();                               // no interruptions, please
00392 
00393   systime_set_switcher(&tm_switcher);
00394   yield();                                      // go!
00395 
00396   enable_irqs();                                // restored state would 
00397                                                 // disallow interrupts
00398 }
00399 
00401 
00410 tid_t execi(int (*code_start)(int,char**),int argc, char **argv,
00411             priority_t priority,size_t stack_size) {
00412   pchain_t *pchain, *ppchain; // for traversing priority chain
00413   int freepchain=0;
00414   
00415   // get memory
00416   //
00417   // task & stack area belong to parent task
00418   // they aren't freed by mm_reaper()
00419   //
00420   // avoid deadlock of memory and task semaphores
00421   // by preallocation.
00422   
00423   tdata_t *td=malloc(sizeof(tdata_t));
00424   size_t *sp=malloc(stack_size);
00425   
00426   // for allocating new priority chain
00427   pchain_t *newpchain=malloc(sizeof(pchain_t));
00428 
00429   if (td == NULL || sp == NULL || newpchain == NULL)
00430   {
00431     free(td);
00432     free(sp);
00433     free(newpchain);
00434     return -1;
00435   }
00436   
00437   td->tflags = 0;
00438   if ((size_t)code_start < (size_t)&mm_start)
00439   {
00440     td->tflags |= T_KERNEL;
00441     nb_system_tasks++;
00442   }
00443   else
00444     td->tflags |= T_USER;
00445 
00446   td->stack_base=sp;                  // these we know already.
00447 
00448   sp+=(stack_size>>1);                // setup initial stack
00449 
00450   // when main() returns a value, it passes it in r0
00451   // as r0 is also the register to pass single int arguments by
00452   // gcc convention, we can just put the address of exit on the stack.
00453 
00454   *(--sp)=(size_t) &exit;
00455 
00456   // we have to construct a stack stub so tm_switcher,
00457   // systime_handler and the ROM routine can fill the 
00458   // right values on startup.
00459 
00460   *(--sp)=(size_t) code_start;        // entry point   < these two are for
00461   *(--sp)=0;                          // ccr           < rte in ROM
00462   *(--sp)=0;                          // r6      < pop r6 in ROM
00463   *(--sp)=(size_t)
00464           &rom_ocia_return;         // ROM return < rts in systime_handler
00465 
00466   *(--sp)=(size_t) argc;              // r0     < pop r0 in systime handler
00467   *(--sp)=(size_t)              
00468           &systime_tm_return;       // systime return < rts in tm_switcher
00469 
00470   *(--sp)=(size_t) argv;              // r1..r5 < pop r1..r5 in tm_switcher
00471   *(--sp)=0;
00472   *(--sp)=0;
00473   *(--sp)=0;
00474   *(--sp)=0;
00475 
00476   td->sp_save=sp;                   // save sp for tm_switcher
00477   td->tstate=T_SLEEPING;              // task is waiting for execution
00478   td->parent=ctid;                    // set parent
00479 
00480   ENTER_KERNEL_CRITICAL_SECTION();
00481 
00482   ppchain=NULL;
00483   for(  pchain = priority_head;
00484     pchain != NULL && (pchain->priority) > priority;
00485     ppchain = pchain, pchain = pchain->next
00486   );
00487   if(pchain==NULL || pchain->priority!=priority) {
00488     // make new chain
00489     //
00490     newpchain->priority=priority;
00491     newpchain->ctid=td;
00492 
00493     newpchain->next=pchain;
00494     if(pchain)
00495       pchain->prev =newpchain;
00496     newpchain->prev=ppchain;
00497     if(ppchain)
00498       ppchain->next=newpchain;
00499     else
00500       priority_head=newpchain;
00501 
00502     // initial queue setup
00503     //
00504     td->prev=td->next=td;
00505     td->priority=newpchain;
00506   } else {
00507     // add at back of queue
00508     //
00509     td->priority=pchain;
00510     td->prev=pchain->ctid->prev;
00511     td->next=pchain->ctid;
00512     td->next->prev=td->prev->next=td;
00513     freepchain=1; // free superfluous pchain.
00514   }
00515   nb_tasks++;
00516 
00517   LEAVE_KERNEL_CRITICAL_SECTION();  
00518 
00519   if(freepchain)
00520     free(newpchain);
00521   
00522   return (tid_t) td;                  // tid = (tid_t) &tdata_t_struct
00523 }
00524 
00526 
00531 void exit(int code) {
00532   enable_irqs();                                // just in case...
00533   if (!(ctid->tflags & T_KERNEL))
00534     mm_reaper();
00535   ctid->tstate=T_ZOMBIE;
00536   // Yield till dead
00537   while(1)
00538     yield();
00539 }
00540 
00542 
00546 wakeup_t wait_event(wakeup_t (*wakeup)(wakeup_t),wakeup_t data) {
00547   ctid->wakeup     =wakeup;
00548   ctid->wakeup_data=data;
00549   ctid->tstate     =T_WAITING;
00550 
00551   yield();
00552 
00553   return ctid->wakeup_data;
00554 }
00555 
00557 
00559 static wakeup_t tm_sleep_wakeup(wakeup_t data) {
00560   time_t remaining = ((time_t)data) - get_system_up_time();
00561 
00562   if (((time_t)data) <= get_system_up_time())
00563   {
00564     tm_timeslice = TM_DEFAULT_SLICE;
00565     return -1;
00566   }
00567 
00568   if (remaining < tm_timeslice)
00569     tm_timeslice = remaining;
00570 
00571   return 0;
00572 }
00573 
00575 
00578 unsigned int msleep(unsigned int msec)
00579 {
00580 #if defined(CONF_TIME) && defined(CONF_TM)
00581   if (wait_event(&tm_sleep_wakeup, get_system_up_time() + MSECS_TO_TICKS(msec)) == 0)
00582     return (MSECS_TO_TICKS(msec) - get_system_up_time());
00583 #else
00584   delay(msec);
00585 #endif
00586   return 0;
00587 }
00588 
00590 
00593 unsigned int sleep(unsigned int sec)
00594 {
00595   return msleep(1000*sec)/1000;
00596 }
00597 
00599 
00601 void shutdown_task(tid_t tid) {
00602   tdata_t *td=(tdata_t*) tid;
00603   td->tflags |= T_SHUTDOWN;
00604 }
00605 
00607 
00609 void shutdown_tasks(tflags_t flags) {
00610   pchain_t* pchain;
00611   tdata_t* td;
00612 
00613   ENTER_KERNEL_CRITICAL_SECTION();  
00614  
00615   pchain = priority_head;
00616   while (pchain != NULL) {
00617     td = pchain->ctid;
00618     do {
00619       if ((td->tflags & flags) != 0) {
00620         // signal shutdown
00621         //
00622         td->tflags |= T_SHUTDOWN;
00623       }
00624       td = td->next;
00625     } while (td != pchain->ctid);
00626     pchain = pchain->next;
00627   }
00628  
00629   LEAVE_KERNEL_CRITICAL_SECTION();
00630 }
00631 
00633 
00635 void kill(tid_t tid) {
00636   tdata_t *td=(tdata_t*) tid;
00637   if(td==ctid)
00638     exit(-1);
00639   else {
00640     // when the task is switched to the next time,
00641     // make it exit immediatlely.
00642 
00643     ENTER_KERNEL_CRITICAL_SECTION(); 
00644 
00645     *( (td->sp_save) + SP_RETURN_OFFSET )=(size_t) &exit;
00646     td->tstate=T_SLEEPING;    // in case it's waiting.
00647 
00648     LEAVE_KERNEL_CRITICAL_SECTION();
00649   }
00650 }
00651 
00653 
00655 void killall(priority_t prio) {
00656   pchain_t *pchain;
00657   tdata_t *td;
00658   tflags_t flags = T_KERNEL | T_IDLE;
00659   
00660   if (prio == PRIO_HIGHEST)
00661     flags = T_IDLE;
00662 
00663   ENTER_KERNEL_CRITICAL_SECTION();
00664 
00665   // find first chain with smaller or equal priority.
00666   //
00667   pchain=priority_head;
00668   while(pchain!=NULL && prio<pchain->priority)
00669     pchain=pchain->next;
00670 
00671   while(pchain!=NULL) {
00672     td=pchain->ctid;
00673     do {
00674       if((td!=ctid) && ((td->tflags & flags) == 0)) {
00675         // kill it
00676         //
00677         *( (td->sp_save) + SP_RETURN_OFFSET )=(size_t) &exit;
00678         td->tstate=T_SLEEPING;    // in case it's waiting.
00679       }
00680       td=td->next;
00681     } while(td!=pchain->ctid);
00682     pchain=pchain->next;
00683   }  
00684 
00685   LEAVE_KERNEL_CRITICAL_SECTION();
00686 }
00687 
00688 #endif // CONF_TM
00689 

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

Generated on Mon Feb 16 21:02:10 2004 for brickOS Kernel Developer by doxygen 1.3.5