kernelLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:20k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* kernelLib.c - VxWorks kernel library */
  2. /* Copyright 1984-2001 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history
  6. --------------------
  7. 02n,13may02,cyr  update round-robin scheduling documentation section
  8. 02m,09nov01,jhw  Revert WDB_INFO to be inside WIND_TCB.
  9. 02l,27oct01,jhw  Set pTcb->pWdbInfo to NULL for the root task.
  10. 02k,11oct01,cjj  updated kernel version to 2.6.  Removed Am29k support.
  11. 02j,10apr00,hk   put SH interrupt stack in same space to VBR. use the original
  12.                  vxIntStackBase and removed sysIntStackSpace reference for SH.
  13. 02i,03mar00,zl   merged SH support into T2
  14. 02h,24feb98,dbt  initialize activeQHead to be able to dectect from a host tool
  15.                  if the OS is multitasking
  16. 02h,28nov96,cdp  added ARM support.
  17. 02g,02oct96,p_m  set version number to 2.5.
  18. 02f,07jul94,tpr  added MC68060 cpu support.
  19. 02g,26apr94,hdn  added a int stack alloc to make checkStack() work for I80X86.
  20. 02f,09jun93,hdn  added a support for I80X86
  21. 02f,02dec93,pme  added Am29K family support with 512 bytes aligned 
  22.  interrupt stack.
  23. 02e,20jan93,jdi  documentation cleanup for 5.1.
  24. 02d,23aug92,jcf  changed bzero to bfill.
  25. 02c,12jul92,jcf  cleaned up padding around root task's tcb.
  26. 02b,07jul92,rrr  added pad around the root task's tcb and stack so that
  27.                  memAddToPool (called in taskDestroy) woundn't clobber the tcb.
  28. 02a,04jul92,jcf  moved most of kernelInit to usrInit.
  29.  changed the way root task is initialized to avoid excJobAdd.
  30.  removed kernelRoot().
  31. 01w,26may92,rrr  the tree shuffle
  32. 01v,21may92,yao  added bzero of initTcb for all architectures (spr 1272).
  33. 01u,18mar92,yao  removed conditional definition for STACK_GROWS_DOWN/UP.
  34.  removed macro MEM_ROUND_UP.  removed unused sentinel[].
  35.  changed copyright notice.  removed bzero of initTcb for
  36.  I960.  changed not to carve interrupt stack for MC680{0,1}0.
  37. 01t,23oct91,wmd  fixed bug in kernelInit() to compensate for pumped up stack
  38.  size for i960.
  39. 01s,04oct91,rrr  passed through the ansification filter
  40.                   -changed functions to ansi style
  41.   -fixed #else and #endif
  42.   -changed VOID to void
  43.   -changed copyright notice
  44. 01r,26sep91,jwt  added code to 8-byte align input address parameters.
  45. 01q,21aug91,ajm  added MIPS support.
  46. 01p,14aug91,del  padded excJobAdd, qInit, and taskInit calls with 0's.
  47. 01o,17jun91,del  added bzero of initTcb for I960.
  48. 01n,23may91,jwt  forced 8-byte alignment of stack for SPARC and other RISCs.
  49. 01m,26apr91,hdn  added conditional checks for TRON family of processors.
  50. 01l,05apr91,jdi  documentation -- removed header parens and x-ref numbers;
  51.  doc review by jcf.
  52. 01k,25mar91,del  I960 needs init stack base aligned.
  53. 01j,08feb91,jaa  documentation cleanup.
  54. 01i,30sep90,jcf  added kernelRootCleanup, to reclaim root task memory safely.
  55. 01h,28aug90,jcf  documentation.
  56. 01g,09jul90,dnw  added initialization of msg queue class
  57.  deleted include of qPriHeapLib.h
  58. 01f,05jul90,jcf  added STACK_GROWS_XXX to resolve stack growth direction issues.
  59.  reworked object initialization.
  60.  timer queue is now a simple linked list.
  61.  added room for MEM_TAG at beginning of root task memory.
  62. 01e,26jun90,jcf  changed semaphore object initialization.
  63.  changed topOfStack to endOfStack.
  64. 01d,28aug89,jcf  modified to wind version 2.0.
  65. 01c,21apr89,jcf  added kernelType.
  66. 01b,23aug88,gae  documentation.
  67. 01a,19jan88,jcf  written.
  68. */
  69. /*
  70. DESCRIPTION
  71. The VxWorks kernel provides tasking control services to an application.  The
  72. libraries kernelLib, taskLib, semLib, tickLib, and wdLib comprise the kernel
  73. functionality.  This library is the interface to the VxWorks kernel
  74. initialization, revision information, and scheduling control.
  75. KERNEL INITIALIZATION
  76. The kernel must be initialized before any other kernel operation is
  77. performed.  Normally kernel initialization is taken care of by the system
  78. configuration code in usrInit() in usrConfig.c.
  79. Kernel initialization consists of the following:
  80. .IP "(1)" 4
  81. Defining the starting address and size of the system memory partition.
  82. The malloc() routine uses this partition to satisfy memory allocation
  83. requests of other facilities in VxWorks.
  84. .IP "(2)"
  85. Allocating the specified memory size for an interrupt stack.  Interrupt
  86. service routines will use this stack unless the underlying architecture
  87. does not support a separate interrupt stack, in which case the service
  88. routine will use the stack of the interrupted task.
  89. .IP "(3)"
  90. Specifying the interrupt lock-out level.  VxWorks will not exceed the
  91. specified level during any operation.  The lock-out level is normally
  92. defined to mask the highest priority possible.  However, in situations
  93. where extremely low interrupt latency is required, the lock-out level may
  94. be set to ensure timely response to the interrupt in question.  Interrupt
  95. service routines handling interrupts of priority greater than the
  96. interrupt lock-out level may not call any VxWorks routine.
  97. .LP
  98. Once the kernel initialization is complete, a root task is spawned with
  99. the specified entry point and stack size.  The root entry point is normally
  100. usrRoot() of the usrConfig.c module.  The remaining VxWorks initialization
  101. takes place in usrRoot().
  102. ROUND-ROBIN SCHEDULING
  103. Round-robin scheduling allows the processor to be shared fairly by all tasks
  104. of the same priority.  Without round-robin scheduling, when multiple tasks of
  105. equal priority must share the processor, a single non-blocking task can usurp
  106. the processor until preempted by a task of higher priority, thus never giving
  107. the other equal-priority tasks a chance to run.
  108. Round-robin scheduling is disabled by default.  It can be enabled or
  109. disabled with the routine kernelTimeSlice(), which takes a parameter for
  110. the "time slice" (or interval) that each task will be allowed to run
  111. before relinquishing the processor to another equal-priority task.  If the
  112. parameter is zero, round-robin scheduling is turned off.  If round-robin
  113. scheduling is enabled and preemption is enabled for the executing task,
  114. the system tick handler will increment the task's time-slice count.
  115. When the specified time-slice interval is completed, the system tick
  116. handler clears the counter and the task is placed at the tail of the list
  117. of tasks at its priority.  New tasks joining a given priority group are
  118. placed at the tail of the group with a run-time counter initialized to zero.
  119. Enabling round-robin scheduling does not affect the performance of task
  120. context switches, nor is additional memory allocated.
  121. If a task blocks or is preempted by a higher priority task during its
  122. interval, it's time-slice count is saved and then restored when the task
  123. is eligible for execution.  In the case of preemption, the task will
  124. resume execution once the higher priority task completes, assuming no
  125. other task of a higher priority is ready to run.  For the case when the
  126. task blocks, it is placed at the tail of the list of tasks at its priority.
  127. If preemption is disabled during round-robin scheduling, the time-slice
  128. count of the executing task is not incremented.
  129. Time-slice counts are accrued against the task that is executing when a system
  130. tick occurs regardless of whether the task has executed for the entire tick
  131. interval.  Due to preemption by higher priority tasks or ISRs stealing CPU
  132. time from the task, scenarios exist where a task can execute for less or more
  133. total CPU time than it's allotted time slice.
  134. INCLUDE FILES: kernelLib.h
  135. SEE ALSO: taskLib, intLib,
  136. .pG "Basic OS"
  137. */
  138. #include "vxWorks.h"
  139. #include "tickLib.h"
  140. #include "memLib.h"
  141. #include "qLib.h"
  142. #include "string.h"
  143. #include "intLib.h"
  144. #include "qPriBMapLib.h"
  145. #include "classLib.h"
  146. #include "semLib.h"
  147. #include "wdLib.h"
  148. #include "msgQLib.h"
  149. #include "private/kernelLibP.h"
  150. #include "private/windLibP.h"
  151. #include "private/workQLibP.h"
  152. #include "private/taskLibP.h"
  153. #include "private/memPartLibP.h"
  154. #if (CPU==SH7750 || CPU==SH7729 || CPU==SH7700)
  155. #include "cacheLib.h"
  156. #endif
  157. /* macros */
  158. #define WIND_TCB_SIZE ((unsigned) STACK_ROUND_UP (sizeof(WIND_TCB)))
  159. #define MEM_BLOCK_HDR_SIZE ((unsigned) STACK_ROUND_UP (sizeof(BLOCK_HDR)))
  160. #define MEM_FREE_BLOCK_SIZE ((unsigned) STACK_ROUND_UP (sizeof(FREE_BLOCK)))
  161. #define MEM_BASE_BLOCK_SIZE (MEM_BLOCK_HDR_SIZE + MEM_FREE_BLOCK_SIZE)
  162. #define MEM_END_BLOCK_SIZE (MEM_BLOCK_HDR_SIZE)
  163. #define MEM_TOT_BLOCK_SIZE ((2 * MEM_BLOCK_HDR_SIZE) + MEM_FREE_BLOCK_SIZE)
  164. /* global variables */
  165. char *    vxIntStackEnd; /* end of interrupt stack */
  166. char *   vxIntStackBase; /* base of interrupt stack */
  167. char *   pRootMemStart; /* bottom of root task's stack */
  168. unsigned  rootMemNBytes; /* memory for TCB and root stack */
  169. int    rootTaskId; /* root task ID */
  170. BOOL   roundRobinOn; /* boolean reflecting round robin mode*/
  171. ULONG   roundRobinSlice; /* round robin slice in ticks */
  172. Q_HEAD   activeQHead = {NULL,0,0,NULL};/* multi-way active queue head */
  173. Q_HEAD   tickQHead; /* multi-way tick queue head */
  174. Q_HEAD   readyQHead; /* multi-way ready queue head */
  175. #if     (CPU_FAMILY == ARM)
  176. char *    vxSvcIntStackBase;            /* base of SVC-mode interrupt stack */
  177. char *    vxSvcIntStackEnd;             /* end of SVC-mode interrupt stack */
  178. char *    vxIrqIntStackBase;            /* base of IRQ-mode interrupt stack */
  179. char *    vxIrqIntStackEnd;             /* end of IRQ-mode interrupt stack */
  180. VOIDFUNCPTR _func_armIntStackSplit = NULL;      /* ptr to fn to split stack */
  181. #endif  /* (CPU_FAMILY == ARM) */
  182. /*******************************************************************************
  183. *
  184. * kernelInit - initialize the kernel
  185. *
  186. * This routine initializes and starts the kernel.  It should be called only
  187. * once.  The parameter <rootRtn> specifies the entry point of the user's
  188. * start-up code that subsequently initializes system facilities (i.e., the
  189. * I/O system, network).  Typically, <rootRtn> is set to usrRoot().
  190. *
  191. * Interrupts are enabled for the first time after kernelInit() exits.
  192. * VxWorks will not exceed the specified interrupt lock-out level during any
  193. * of its brief uses of interrupt locking as a means of mutual exclusion.
  194. *
  195. * The system memory partition is initialized by kernelInit() with the size
  196. * set by <pMemPoolStart> and <pMemPoolEnd>.  Architectures that support a
  197. * separate interrupt stack allocate a portion of memory for this
  198. * purpose, of <intStackSize> bytes starting at <pMemPoolStart>.
  199. *
  200. * NOTE SH77XX:
  201. * The interrupt stack is emulated by software, and it has to be located in
  202. * a fixed physical address space (P1 or P2) if the on-chip MMU is enabled.
  203. * If <pMemPoolStart> is in a logical address space (P0 or P3), the interrupt
  204. * stack area is reserved on the same logical address space.  The actual
  205. * interrupt stack is relocated to a fixed physical space pointed by VBR.
  206. *
  207. * INTERNAL
  208. * The routine kernelRoot() is called before the user's root routine so that
  209. * memory management can be initialized.  The memory setup is as follows:
  210. *
  211. * For _STACK_GROWS_DOWN:
  212. *
  213. * .CS
  214. *   - HIGH MEMORY -
  215. *     ------------------------ <--- pMemPoolEnd
  216. *     |                      |  We have to leave room for this block headers
  217. *     |     1 BLOCK_HDR      | so we can add the root task memory to the pool.
  218. *     |                      |
  219. *     ------------------------
  220. *     |                      |   
  221. *     |       WIND_TCB       |
  222. *     |                      |
  223. *     ------------------------ <--- pRootStackBase;
  224. *     |                      |
  225. *     |      ROOT STACK      |
  226. *     |                      |
  227. *     ------------------------
  228. *     |                      |  We have to leave room for these block headers
  229. *     |     1 FREE_BLOCK     | so we can add the root task memory to the pool.
  230. *     |     1 BLOCK_HDR      |
  231. *     |                      |
  232. *     ------------------------ <--- pRootMemStart;
  233. *     ------------------------
  234. *     |                      |
  235. *     ~   FREE MEMORY POOL   ~ pool initialized in kernelRoot()
  236. *     |                      |
  237. *     ------------------------ <--- pMemPoolStart + intStackSize; vxIntStackBase
  238. *     |                      |
  239. *     |   INTERRUPT STACK    |
  240. *     |                      |
  241. *     ------------------------ <--- pMemPoolStart; vxIntStackEnd
  242. *         - LOW  MEMORY -
  243. * .CE
  244. *
  245. * For _STACK_GROWS_UP:
  246. *
  247. * .CS
  248. *    - HIGH MEMORY -
  249. *     ------------------------ <--- pMemPoolEnd;
  250. *     |                      |  We have to leave room for this block header
  251. *     |     1 BLOCK_HDR      | so we can add the root task memory to the pool.
  252. *     |                      |
  253. *     ------------------------ <--- pRootStackEnd;
  254. *     |                      |
  255. *     |      ROOT STACK      |
  256. *     |                      |
  257. *     ------------------------ <--- pRootStackBase;
  258. *     |                      |
  259. *     |       WIND_TCB       |
  260. *     |                      |
  261. *     ------------------------
  262. *     |                      |  We have to leave room for these block headers
  263. *     |     1 FREE_BLOCK     | so we can add the root task memory to the pool.
  264. *     |     1 BLOCK_HDR      |
  265. *     |                      |
  266. *     ------------------------ <--- pRootMemStart;
  267. *     ------------------------
  268. *     |                      |
  269. *     ~   FREE MEMORY POOL   ~ pool initialized in kernelRoot()
  270. *     |                      |
  271. *     ------------------------ <--- pMemPoolStart + intStackSize; vxIntStackEnd
  272. *     |                      |
  273. *     |   INTERRUPT STACK    |
  274. *     |                      |
  275. *     ------------------------ <--- pMemPoolStart; vxIntStackBase
  276. *    - LOW  MEMORY -
  277. * .CE
  278. *
  279. * RETURNS: N/A
  280. *
  281. * SEE ALSO: intLockLevelSet()
  282. */
  283. void kernelInit
  284.     (
  285.     FUNCPTR rootRtn, /* user start-up routine */
  286.     unsigned rootMemSize, /* memory for TCB and root stack */
  287.     char * pMemPoolStart, /* beginning of memory pool */
  288.     char * pMemPoolEnd, /* end of memory pool */
  289.     unsigned intStackSize, /* interrupt stack size */
  290.     int lockOutLevel /* interrupt lock-out level (1-7) */
  291.     )
  292.     {
  293.     union
  294. {
  295. double   align8;        /* 8-byte alignment dummy */
  296. WIND_TCB initTcb;       /* context from which to activate root */
  297. } tcbAligned;
  298.     WIND_TCB *  pTcb;           /* pTcb for root task */
  299.     unsigned rootStackSize; /* actual stacksize of root task */
  300.     unsigned memPoolSize; /* initial size of mem pool */
  301.     char * pRootStackBase; /* base of root task's stack */
  302.     /* align input size and address parameters */
  303.     rootMemNBytes = STACK_ROUND_UP(rootMemSize);
  304.     pMemPoolStart = (char *) STACK_ROUND_UP(pMemPoolStart);
  305.     pMemPoolEnd   = (char *) STACK_ROUND_DOWN(pMemPoolEnd);
  306.     intStackSize  = STACK_ROUND_UP(intStackSize);
  307.     /* initialize VxWorks interrupt lock-out level */
  308.     intLockLevelSet (lockOutLevel);
  309.     /* round-robin mode is disabled by default */
  310.     roundRobinOn = FALSE;
  311.     /* initialize the time to zero */
  312.     vxTicks = 0; /* good morning */
  313.     /* If the architecture supports a separate interrupt stack,
  314.      * carve the interrupt stack from the beginning of the memory pool
  315.      * and fill it with 0xee for checkStack ().  The MC680[016]0, I960, and
  316.      * I80X86 not do support a separate interrupt stack.  I80X86, however, 
  317.      * allocate the stack for checkStack () which is not used.
  318.      */
  319. #if  (_STACK_DIR == _STACK_GROWS_DOWN)
  320. #if  (CPU != MC68000 && CPU != MC68010 && CPU != MC68060)
  321.     vxIntStackBase = pMemPoolStart + intStackSize;
  322.     vxIntStackEnd  = pMemPoolStart;
  323.     bfill (vxIntStackEnd, (int) intStackSize, 0xee);
  324. #if (CPU != SH7750 && CPU != SH7729 && CPU != SH7700)
  325.     windIntStackSet (vxIntStackBase);
  326.     pMemPoolStart = vxIntStackBase;
  327. #else /* CPU == SH7750 || CPU == SH7729 || CPU == SH7700 */
  328.     /* If mmu is enabled, emulated SH7700 interrupt stack needs to be relocated
  329.      * on a fixed physical address space (P1/P2).  If mmu is disabled, it is
  330.      * also possible to put the interrupt stack on copy-back cache (P0/P3).
  331.      * Note that cache flush is necessary, since above bfill() might be done
  332.      * on copy-back cache and we may use the area from its behind.
  333.      */
  334.     {
  335.     pMemPoolStart = vxIntStackBase;
  336.     /* push out 0xee's on copy-back cache to memory (nop if write-through) */
  337.     CACHE_DRV_FLUSH (&cacheLib, vxIntStackEnd, (int) intStackSize);
  338.     /* relocate interrupt stack to same address space to vector base */
  339.     vxIntStackBase = (char *)(((UINT32)vxIntStackBase  & 0x1fffffff)
  340.     | ((UINT32)intVecBaseGet() & 0xe0000000));
  341.     vxIntStackEnd  = (char *)(((UINT32)vxIntStackEnd   & 0x1fffffff)
  342.     | ((UINT32)intVecBaseGet() & 0xe0000000));
  343.     /* load vxIntStackBase to P1/P2 */
  344.     windIntStackSet (vxIntStackBase);
  345.     }
  346. #endif /* CPU == SH7750 || CPU == SH7729 || CPU == SH7700 */
  347. #if     (CPU_FAMILY == ARM)
  348.     /*
  349.      * The ARM family uses 3 interrupt stacks. The ratio of the sizes of
  350.      * these stacks is dependent on the interrupt structure of the board
  351.      * and so is handled in the BSP code. Note that FIQ is now external to
  352.      * VxWorks.
  353.      */
  354.     if (_func_armIntStackSplit != NULL)
  355.         (_func_armIntStackSplit)(vxIntStackBase, intStackSize);
  356. #endif  /* (CPU_FAMILY == ARM) */
  357. #endif  /* (CPU != MC68000 && CPU != MC68010 && CPU != MC68060) */
  358. #else /* _STACK_DIR == _STACK_GROWS_UP */
  359. #if CPU_FAMILY != I960
  360.     vxIntStackBase = pMemPoolStart;
  361.     vxIntStackEnd  = pMemPoolStart + intStackSize;
  362.     bfill (vxIntStackBase, intStackSize, 0xee);
  363.     windIntStackSet (vxIntStackBase);
  364.     pMemPoolStart = vxIntStackEnd;
  365. #endif /* CPU_FAMILY != I960 */
  366. #endif  /* (_STACK_DIR == _STACK_GROWS_UP) */
  367.     /* Carve the root stack and tcb from the end of the memory pool.  We have
  368.      * to leave room at the very top and bottom of the root task memory for
  369.      * the memory block headers that are put at the end and beginning of a
  370.      * free memory block by memLib's memAddToPool() routine.  The root stack
  371.      * is added to the memory pool with memAddToPool as the root task's
  372.      * dieing breath.
  373.      */
  374.     rootStackSize  = rootMemNBytes - WIND_TCB_SIZE - MEM_TOT_BLOCK_SIZE;
  375.     pRootMemStart  = pMemPoolEnd - rootMemNBytes;
  376. #if (_STACK_DIR == _STACK_GROWS_DOWN)
  377.     pRootStackBase = pRootMemStart + rootStackSize + MEM_BASE_BLOCK_SIZE;
  378.     pTcb           = (WIND_TCB *) pRootStackBase;
  379. #else /* _STACK_GROWS_UP */
  380.     pTcb           = (WIND_TCB *) (pRootMemStart   + MEM_BASE_BLOCK_SIZE);
  381.     pRootStackBase = pRootMemStart + WIND_TCB_SIZE + MEM_BASE_BLOCK_SIZE;
  382. #endif /* _STACK_GROWS_UP */
  383.     /* We initialize the root task with taskIdCurrent == 0.  This only works
  384.      * because when taskInit calls windExit (), the ready queue will be
  385.      * empty and thus the comparison of taskIdCurrent to the head of the
  386.      * ready queue will result in equivalence.  In this case windExit ()
  387.      * just returns to the caller, without changing anybody's context.
  388.      */
  389.     taskIdCurrent = (WIND_TCB *) NULL; /* initialize taskIdCurrent */
  390.     bfill ((char *) &tcbAligned.initTcb, sizeof (WIND_TCB), 0);
  391.     memPoolSize = (unsigned) ((int) pRootMemStart - (int) pMemPoolStart);
  392.     taskInit (pTcb, "tRootTask", 0, VX_UNBREAKABLE | VX_DEALLOC_STACK,
  393.       pRootStackBase, (int) rootStackSize, (FUNCPTR) rootRtn,
  394.       (int) pMemPoolStart, (int)memPoolSize, 0, 0, 0, 0, 0, 0, 0, 0);
  395.     rootTaskId = (int) pTcb; /* fill in the root task ID */
  396.     /* Now taskIdCurrent needs to point at a context so when we switch into
  397.      * the root task, we have some place for windExit () to store the old
  398.      * context.  We just use a local stack variable to save memory.
  399.      */
  400.     taskIdCurrent = &tcbAligned.initTcb;        /* update taskIdCurrent */
  401.     taskActivate ((int) pTcb); /* activate root task */
  402.     }
  403. /*******************************************************************************
  404. *
  405. * kernelVersion - return the kernel revision string
  406. *
  407. * This routine returns a string which contains the current revision of the
  408. * kernel.  The string is of the form "WIND version x.y", where "x"
  409. * corresponds to the kernel major revision, and "y" corresponds to the
  410. * kernel minor revision.
  411. *
  412. * RETURNS: A pointer to a string of format "WIND version x.y".
  413. */
  414. char *kernelVersion (void)
  415.     {
  416.     return ("WIND version 2.6");
  417.     }
  418. /*******************************************************************************
  419. *
  420. * kernelTimeSlice - enable round-robin selection
  421. *
  422. * This routine enables round-robin selection among tasks of same priority
  423. * and sets the system time-slice to <ticks>.  Round-robin scheduling is
  424. * disabled by default.  A time-slice of zero ticks disables round-robin
  425. * scheduling.
  426. *
  427. * For more information about round-robin scheduling, see the manual entry
  428. * for kernelLib.
  429. *
  430. * RETURNS: OK, always.
  431. */
  432. STATUS kernelTimeSlice
  433.     (
  434.     int ticks /* time-slice in ticks or 0 to disable round-robin */
  435.     )
  436.     {
  437.     if (ticks == 0) /* 0 means turn off round-robin mode */
  438. roundRobinOn = FALSE;
  439.     else
  440. {
  441. roundRobinSlice = ticks; /* set new time-slice */
  442. roundRobinOn = TRUE; /* enable round-robin scheduling mode */
  443. }
  444.     return (OK);
  445.     }