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. |