diff options
Diffstat (limited to 'lib/chibios/os/nil/src/ch.c')
-rw-r--r-- | lib/chibios/os/nil/src/ch.c | 1080 |
1 files changed, 1080 insertions, 0 deletions
diff --git a/lib/chibios/os/nil/src/ch.c b/lib/chibios/os/nil/src/ch.c new file mode 100644 index 000000000..b8c81cacb --- /dev/null +++ b/lib/chibios/os/nil/src/ch.c | |||
@@ -0,0 +1,1080 @@ | |||
1 | /* | ||
2 | ChibiOS - Copyright (C) 2006,2007,2008,2009,2010,2011,2012,2013,2014, | ||
3 | 2015,2016,2017,2018,2019,2020,2021 Giovanni Di Sirio. | ||
4 | |||
5 | This file is part of ChibiOS. | ||
6 | |||
7 | ChibiOS is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation version 3 of the License. | ||
10 | |||
11 | ChibiOS is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file nil/src/ch.c | ||
22 | * @brief Nil RTOS main source file. | ||
23 | * | ||
24 | * @addtogroup NIL_KERNEL | ||
25 | * @{ | ||
26 | */ | ||
27 | |||
28 | #include "ch.h" | ||
29 | |||
30 | /*===========================================================================*/ | ||
31 | /* Module local definitions. */ | ||
32 | /*===========================================================================*/ | ||
33 | |||
34 | /*===========================================================================*/ | ||
35 | /* Module exported variables. */ | ||
36 | /*===========================================================================*/ | ||
37 | |||
38 | /** | ||
39 | * @brief System data structures. | ||
40 | */ | ||
41 | nil_system_t nil; | ||
42 | |||
43 | /*===========================================================================*/ | ||
44 | /* Module local variables. */ | ||
45 | /*===========================================================================*/ | ||
46 | |||
47 | /*===========================================================================*/ | ||
48 | /* Module local functions. */ | ||
49 | /*===========================================================================*/ | ||
50 | |||
51 | /*===========================================================================*/ | ||
52 | /* Module interrupt handlers. */ | ||
53 | /*===========================================================================*/ | ||
54 | |||
55 | /*===========================================================================*/ | ||
56 | /* Module exported functions. */ | ||
57 | /*===========================================================================*/ | ||
58 | |||
59 | /** | ||
60 | * @brief Retrieves the highest priority thread in the specified state and | ||
61 | * associated to the specified object. | ||
62 | * | ||
63 | * @param[in] state thread state | ||
64 | * @param[in] p object pointer | ||
65 | * @return The pointer to the found thread. | ||
66 | * @retval NULL if the thread is not found. | ||
67 | * | ||
68 | * @notapi | ||
69 | */ | ||
70 | thread_t *nil_find_thread(tstate_t state, void *p) { | ||
71 | thread_t *tp = nil.threads; | ||
72 | |||
73 | while (tp < &nil.threads[CH_CFG_MAX_THREADS]) { | ||
74 | /* Is this thread matching?*/ | ||
75 | if ((tp->state == state) && (tp->u1.p == p)) { | ||
76 | return tp; | ||
77 | } | ||
78 | tp++; | ||
79 | } | ||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * @brief Puts in ready state all thread matching the specified status and | ||
85 | * associated object. | ||
86 | * | ||
87 | * @param[in] p object pointer | ||
88 | * @param[in] cnt number of threads to be readied as a negative number, | ||
89 | * non negative numbers are ignored | ||
90 | * @param[in] msg the wakeup message | ||
91 | * @return The number of readied threads. | ||
92 | * | ||
93 | * @notapi | ||
94 | */ | ||
95 | cnt_t nil_ready_all(void *p, cnt_t cnt, msg_t msg) { | ||
96 | thread_t *tp = nil.threads;; | ||
97 | |||
98 | while (cnt < (cnt_t)0) { | ||
99 | |||
100 | chDbgAssert(tp < &nil.threads[CH_CFG_MAX_THREADS], | ||
101 | "pointer out of range"); | ||
102 | |||
103 | /* Is this thread waiting on this queue?*/ | ||
104 | if ((tp->state == NIL_STATE_WTQUEUE) && (tp->u1.p == p)) { | ||
105 | cnt++; | ||
106 | (void) chSchReadyI(tp, msg); | ||
107 | } | ||
108 | tp++; | ||
109 | } | ||
110 | |||
111 | return cnt; | ||
112 | } | ||
113 | |||
114 | #if (CH_DBG_SYSTEM_STATE_CHECK == TRUE) || defined(__DOXYGEN__) | ||
115 | /** | ||
116 | * @brief Guard code for @p chSysDisable(). | ||
117 | * | ||
118 | * @notapi | ||
119 | */ | ||
120 | void _dbg_check_disable(void) { | ||
121 | |||
122 | if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
123 | chSysHalt("SV#1"); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * @brief Guard code for @p chSysSuspend(). | ||
129 | * | ||
130 | * @notapi | ||
131 | */ | ||
132 | void _dbg_check_suspend(void) { | ||
133 | |||
134 | if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
135 | chSysHalt("SV#2"); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * @brief Guard code for @p chSysEnable(). | ||
141 | * | ||
142 | * @notapi | ||
143 | */ | ||
144 | void _dbg_check_enable(void) { | ||
145 | |||
146 | if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
147 | chSysHalt("SV#3"); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * @brief Guard code for @p chSysLock(). | ||
153 | * | ||
154 | * @notapi | ||
155 | */ | ||
156 | void _dbg_check_lock(void) { | ||
157 | |||
158 | if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
159 | chSysHalt("SV#4"); | ||
160 | } | ||
161 | _dbg_enter_lock(); | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * @brief Guard code for @p chSysUnlock(). | ||
166 | * | ||
167 | * @notapi | ||
168 | */ | ||
169 | void _dbg_check_unlock(void) { | ||
170 | |||
171 | if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) { | ||
172 | chSysHalt("SV#5"); | ||
173 | } | ||
174 | _dbg_leave_lock(); | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * @brief Guard code for @p chSysLockFromIsr(). | ||
179 | * | ||
180 | * @notapi | ||
181 | */ | ||
182 | void _dbg_check_lock_from_isr(void) { | ||
183 | |||
184 | if ((nil.isr_cnt <= (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
185 | chSysHalt("SV#6"); | ||
186 | } | ||
187 | _dbg_enter_lock(); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * @brief Guard code for @p chSysUnlockFromIsr(). | ||
192 | * | ||
193 | * @notapi | ||
194 | */ | ||
195 | void _dbg_check_unlock_from_isr(void) { | ||
196 | |||
197 | if ((nil.isr_cnt <= (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) { | ||
198 | chSysHalt("SV#7"); | ||
199 | } | ||
200 | _dbg_leave_lock(); | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * @brief Guard code for @p CH_IRQ_PROLOGUE(). | ||
205 | * | ||
206 | * @notapi | ||
207 | */ | ||
208 | void _dbg_check_enter_isr(void) { | ||
209 | |||
210 | port_lock_from_isr(); | ||
211 | if ((nil.isr_cnt < (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
212 | chSysHalt("SV#8"); | ||
213 | } | ||
214 | nil.isr_cnt++; | ||
215 | port_unlock_from_isr(); | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * @brief Guard code for @p CH_IRQ_EPILOGUE(). | ||
220 | * | ||
221 | * @notapi | ||
222 | */ | ||
223 | void _dbg_check_leave_isr(void) { | ||
224 | |||
225 | port_lock_from_isr(); | ||
226 | if ((nil.isr_cnt <= (cnt_t)0) || (nil.lock_cnt != (cnt_t)0)) { | ||
227 | chSysHalt("SV#9"); | ||
228 | } | ||
229 | nil.isr_cnt--; | ||
230 | port_unlock_from_isr(); | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * @brief I-class functions context check. | ||
235 | * @details Verifies that the system is in an appropriate state for invoking | ||
236 | * an I-class API function. A panic is generated if the state is | ||
237 | * not compatible. | ||
238 | * | ||
239 | * @api | ||
240 | */ | ||
241 | void chDbgCheckClassI(void) { | ||
242 | |||
243 | if ((nil.isr_cnt < (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) { | ||
244 | chSysHalt("SV#10"); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * @brief S-class functions context check. | ||
250 | * @details Verifies that the system is in an appropriate state for invoking | ||
251 | * an S-class API function. A panic is generated if the state is | ||
252 | * not compatible. | ||
253 | * | ||
254 | * @api | ||
255 | */ | ||
256 | void chDbgCheckClassS(void) { | ||
257 | |||
258 | if ((nil.isr_cnt != (cnt_t)0) || (nil.lock_cnt <= (cnt_t)0)) { | ||
259 | chSysHalt("SV#11"); | ||
260 | } | ||
261 | } | ||
262 | #endif /* CH_DBG_SYSTEM_STATE_CHECK == TRUE */ | ||
263 | |||
264 | /** | ||
265 | * @brief Initializes the kernel. | ||
266 | * @details Initializes the kernel structures, the current instructions flow | ||
267 | * becomes the idle thread upon return. The idle thread must not | ||
268 | * invoke any kernel primitive able to change state to not runnable. | ||
269 | * @note This function assumes that the @p nil global variable has been | ||
270 | * zeroed by the runtime environment. If this is not the case then | ||
271 | * make sure to clear it before calling this function. | ||
272 | * | ||
273 | * @special | ||
274 | */ | ||
275 | void chSysInit(void) { | ||
276 | const thread_descriptor_t *tdp; | ||
277 | |||
278 | /* Optional library modules.*/ | ||
279 | _oslib_init(); | ||
280 | |||
281 | /* Architecture layer initialization.*/ | ||
282 | port_init(); | ||
283 | |||
284 | /* System initialization hook.*/ | ||
285 | CH_CFG_SYSTEM_INIT_HOOK(); | ||
286 | |||
287 | /* Making idle the current thread, this may change after rescheduling.*/ | ||
288 | nil.next = nil.current = &nil.threads[CH_CFG_MAX_THREADS]; | ||
289 | nil.current->state = NIL_STATE_READY; | ||
290 | |||
291 | #if CH_DBG_ENABLE_STACK_CHECK == TRUE | ||
292 | /* The idle thread is a special case because its stack is set up by the | ||
293 | runtime environment.*/ | ||
294 | nil.threads[CH_CFG_MAX_THREADS].wabase = THD_IDLE_BASE; | ||
295 | #endif | ||
296 | |||
297 | /* Interrupts partially enabled. It is equivalent to entering the | ||
298 | kernel critical zone.*/ | ||
299 | chSysSuspend(); | ||
300 | #if CH_DBG_SYSTEM_STATE_CHECK == TRUE | ||
301 | nil.lock_cnt = (cnt_t)1; | ||
302 | #endif | ||
303 | |||
304 | #if CH_CFG_AUTOSTART_THREADS == TRUE | ||
305 | /* Iterates through the list of threads to be auto-started.*/ | ||
306 | tdp = nil_thd_configs; | ||
307 | do { | ||
308 | (void) chThdCreateI(tdp); | ||
309 | tdp++; | ||
310 | } while (tdp->funcp != NULL); | ||
311 | #endif | ||
312 | |||
313 | /* Starting the dance.*/ | ||
314 | chSchRescheduleS(); | ||
315 | chSysUnlock(); | ||
316 | } | ||
317 | |||
318 | /** | ||
319 | * @brief Halts the system. | ||
320 | * @details This function is invoked by the operating system when an | ||
321 | * unrecoverable error is detected, for example because a programming | ||
322 | * error in the application code that triggers an assertion while | ||
323 | * in debug mode. | ||
324 | * @note Can be invoked from any system state. | ||
325 | * | ||
326 | * @param[in] reason pointer to an error string | ||
327 | * | ||
328 | * @special | ||
329 | */ | ||
330 | void chSysHalt(const char *reason) { | ||
331 | |||
332 | port_disable(); | ||
333 | |||
334 | #if NIL_DBG_ENABLED | ||
335 | nil.dbg_panic_msg = reason; | ||
336 | #else | ||
337 | (void)reason; | ||
338 | #endif | ||
339 | |||
340 | /* Halt hook code, usually empty.*/ | ||
341 | CH_CFG_SYSTEM_HALT_HOOK(reason); | ||
342 | |||
343 | /* Harmless infinite loop.*/ | ||
344 | while (true) { | ||
345 | } | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * @brief Time management handler. | ||
350 | * @note This handler has to be invoked by a periodic ISR in order to | ||
351 | * reschedule the waiting threads. | ||
352 | * | ||
353 | * @iclass | ||
354 | */ | ||
355 | void chSysTimerHandlerI(void) { | ||
356 | |||
357 | chDbgCheckClassI(); | ||
358 | |||
359 | #if CH_CFG_ST_TIMEDELTA == 0 | ||
360 | thread_t *tp = &nil.threads[0]; | ||
361 | nil.systime++; | ||
362 | do { | ||
363 | /* Is the thread in a wait state with timeout?.*/ | ||
364 | if (tp->timeout > (sysinterval_t)0) { | ||
365 | |||
366 | chDbgAssert(!NIL_THD_IS_READY(tp), "is ready"); | ||
367 | |||
368 | /* Did the timer reach zero?*/ | ||
369 | if (--tp->timeout == (sysinterval_t)0) { | ||
370 | /* Timeout on queues/semaphores requires a special handling because | ||
371 | the counter must be incremented.*/ | ||
372 | /*lint -save -e9013 [15.7] There is no else because it is not needed.*/ | ||
373 | #if CH_CFG_USE_SEMAPHORES == TRUE | ||
374 | if (NIL_THD_IS_WTQUEUE(tp)) { | ||
375 | tp->u1.semp->cnt++; | ||
376 | } | ||
377 | else | ||
378 | #endif | ||
379 | if (NIL_THD_IS_SUSPENDED(tp)) { | ||
380 | *tp->u1.trp = NULL; | ||
381 | } | ||
382 | /*lint -restore*/ | ||
383 | (void) chSchReadyI(tp, MSG_TIMEOUT); | ||
384 | } | ||
385 | } | ||
386 | /* Lock released in order to give a preemption chance on those | ||
387 | architectures supporting IRQ preemption.*/ | ||
388 | chSysUnlockFromISR(); | ||
389 | tp++; | ||
390 | chSysLockFromISR(); | ||
391 | } while (tp < &nil.threads[CH_CFG_MAX_THREADS]); | ||
392 | #else | ||
393 | thread_t *tp = &nil.threads[0]; | ||
394 | sysinterval_t next = (sysinterval_t)0; | ||
395 | |||
396 | chDbgAssert(nil.nexttime == port_timer_get_alarm(), "time mismatch"); | ||
397 | |||
398 | do { | ||
399 | sysinterval_t timeout = tp->timeout; | ||
400 | |||
401 | /* Is the thread in a wait state with timeout?.*/ | ||
402 | if (timeout > (sysinterval_t)0) { | ||
403 | |||
404 | chDbgAssert(!NIL_THD_IS_READY(tp), "is ready"); | ||
405 | chDbgAssert(timeout >= chTimeDiffX(nil.lasttime, nil.nexttime), | ||
406 | "skipped one"); | ||
407 | |||
408 | /* The volatile field is updated once, here.*/ | ||
409 | timeout -= chTimeDiffX(nil.lasttime, nil.nexttime); | ||
410 | tp->timeout = timeout; | ||
411 | |||
412 | if (timeout == (sysinterval_t)0) { | ||
413 | /* Timeout on thread queues requires a special handling because the | ||
414 | counter must be incremented.*/ | ||
415 | if (NIL_THD_IS_WTQUEUE(tp)) { | ||
416 | tp->u1.tqp->cnt++; | ||
417 | } | ||
418 | else { | ||
419 | if (NIL_THD_IS_SUSPENDED(tp)) { | ||
420 | *tp->u1.trp = NULL; | ||
421 | } | ||
422 | } | ||
423 | (void) chSchReadyI(tp, MSG_TIMEOUT); | ||
424 | } | ||
425 | else { | ||
426 | if (timeout <= (sysinterval_t)(next - (sysinterval_t)1)) { | ||
427 | next = timeout; | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | /* Lock released in order to give a preemption chance on those | ||
433 | architectures supporting IRQ preemption.*/ | ||
434 | chSysUnlockFromISR(); | ||
435 | tp++; | ||
436 | chSysLockFromISR(); | ||
437 | } while (tp < &nil.threads[CH_CFG_MAX_THREADS]); | ||
438 | |||
439 | nil.lasttime = nil.nexttime; | ||
440 | if (next > (sysinterval_t)0) { | ||
441 | nil.nexttime = chTimeAddX(nil.nexttime, next); | ||
442 | port_timer_set_alarm(nil.nexttime); | ||
443 | } | ||
444 | else { | ||
445 | /* No tick event needed.*/ | ||
446 | port_timer_stop_alarm(); | ||
447 | } | ||
448 | #endif | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * @brief Unconditionally enters the kernel lock state. | ||
453 | * @note Can be called without previous knowledge of the current lock state. | ||
454 | * The final state is "s-locked". | ||
455 | * | ||
456 | * @special | ||
457 | */ | ||
458 | void chSysUnconditionalLock(void) { | ||
459 | |||
460 | if (port_irq_enabled(port_get_irq_status())) { | ||
461 | chSysLock(); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | /** | ||
466 | * @brief Unconditionally leaves the kernel lock state. | ||
467 | * @note Can be called without previous knowledge of the current lock state. | ||
468 | * The final state is "normal". | ||
469 | * | ||
470 | * @special | ||
471 | */ | ||
472 | void chSysUnconditionalUnlock(void) { | ||
473 | |||
474 | if (!port_irq_enabled(port_get_irq_status())) { | ||
475 | chSysUnlock(); | ||
476 | } | ||
477 | } | ||
478 | |||
479 | /** | ||
480 | * @brief Returns the execution status and enters a critical zone. | ||
481 | * @details This functions enters into a critical zone and can be called | ||
482 | * from any context. Because its flexibility it is less efficient | ||
483 | * than @p chSysLock() which is preferable when the calling context | ||
484 | * is known. | ||
485 | * @post The system is in a critical zone. | ||
486 | * | ||
487 | * @return The previous system status, the encoding of this | ||
488 | * status word is architecture-dependent and opaque. | ||
489 | * | ||
490 | * @xclass | ||
491 | */ | ||
492 | syssts_t chSysGetStatusAndLockX(void) { | ||
493 | |||
494 | syssts_t sts = port_get_irq_status(); | ||
495 | if (port_irq_enabled(sts)) { | ||
496 | if (port_is_isr_context()) { | ||
497 | chSysLockFromISR(); | ||
498 | } | ||
499 | else { | ||
500 | chSysLock(); | ||
501 | } | ||
502 | } | ||
503 | return sts; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * @brief Restores the specified execution status and leaves a critical zone. | ||
508 | * @note A call to @p chSchRescheduleS() is automatically performed | ||
509 | * if exiting the critical zone and if not in ISR context. | ||
510 | * | ||
511 | * @param[in] sts the system status to be restored. | ||
512 | * | ||
513 | * @xclass | ||
514 | */ | ||
515 | void chSysRestoreStatusX(syssts_t sts) { | ||
516 | |||
517 | if (port_irq_enabled(sts)) { | ||
518 | if (port_is_isr_context()) { | ||
519 | chSysUnlockFromISR(); | ||
520 | } | ||
521 | else { | ||
522 | chSchRescheduleS(); | ||
523 | chSysUnlock(); | ||
524 | } | ||
525 | } | ||
526 | } | ||
527 | |||
528 | #if (PORT_SUPPORTS_RT == TRUE) || defined(__DOXYGEN__) | ||
529 | /** | ||
530 | * @brief Realtime window test. | ||
531 | * @details This function verifies if the current realtime counter value | ||
532 | * lies within the specified range or not. The test takes care | ||
533 | * of the realtime counter wrapping to zero on overflow. | ||
534 | * @note When start==end then the function returns always false because a | ||
535 | * null time range is specified. | ||
536 | * @note This function is only available if the port layer supports the | ||
537 | * option @p PORT_SUPPORTS_RT. | ||
538 | * | ||
539 | * @param[in] cnt the counter value to be tested | ||
540 | * @param[in] start the start of the time window (inclusive) | ||
541 | * @param[in] end the end of the time window (non inclusive) | ||
542 | * @retval true current time within the specified time window. | ||
543 | * @retval false current time not within the specified time window. | ||
544 | * | ||
545 | * @xclass | ||
546 | */ | ||
547 | bool chSysIsCounterWithinX(rtcnt_t cnt, rtcnt_t start, rtcnt_t end) { | ||
548 | |||
549 | return (bool)(((rtcnt_t)cnt - (rtcnt_t)start) < | ||
550 | ((rtcnt_t)end - (rtcnt_t)start)); | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * @brief Polled delay. | ||
555 | * @note The real delay is always few cycles in excess of the specified | ||
556 | * value. | ||
557 | * @note This function is only available if the port layer supports the | ||
558 | * option @p PORT_SUPPORTS_RT. | ||
559 | * | ||
560 | * @param[in] cycles number of cycles | ||
561 | * | ||
562 | * @xclass | ||
563 | */ | ||
564 | void chSysPolledDelayX(rtcnt_t cycles) { | ||
565 | rtcnt_t start = chSysGetRealtimeCounterX(); | ||
566 | rtcnt_t end = start + cycles; | ||
567 | |||
568 | while (chSysIsCounterWithinX(chSysGetRealtimeCounterX(), start, end)) { | ||
569 | } | ||
570 | } | ||
571 | #endif /* PORT_SUPPORTS_RT == TRUE */ | ||
572 | |||
573 | /** | ||
574 | * @brief Makes the specified thread ready for execution. | ||
575 | * | ||
576 | * @param[in] tp pointer to the @p thread_t object | ||
577 | * @param[in] msg the wakeup message | ||
578 | * | ||
579 | * @return The same reference passed as parameter. | ||
580 | */ | ||
581 | thread_t *chSchReadyI(thread_t *tp, msg_t msg) { | ||
582 | |||
583 | chDbgCheckClassI(); | ||
584 | chDbgCheck((tp >= nil.threads) && (tp < &nil.threads[CH_CFG_MAX_THREADS])); | ||
585 | chDbgAssert(!NIL_THD_IS_READY(tp), "already ready"); | ||
586 | chDbgAssert(nil.next <= nil.current, "priority ordering"); | ||
587 | |||
588 | tp->u1.msg = msg; | ||
589 | tp->state = NIL_STATE_READY; | ||
590 | tp->timeout = (sysinterval_t)0; | ||
591 | if (tp < nil.next) { | ||
592 | nil.next = tp; | ||
593 | } | ||
594 | return tp; | ||
595 | } | ||
596 | |||
597 | /** | ||
598 | * @brief Evaluates if preemption is required. | ||
599 | * @details The decision is taken by comparing the relative priorities and | ||
600 | * depending on the state of the round robin timeout counter. | ||
601 | * @note Not a user function, it is meant to be invoked by the scheduler | ||
602 | * itself or from within the port layer. | ||
603 | * | ||
604 | * @retval true if there is a thread that must go in running state | ||
605 | * immediately. | ||
606 | * @retval false if preemption is not required. | ||
607 | * | ||
608 | * @special | ||
609 | */ | ||
610 | bool chSchIsPreemptionRequired(void) { | ||
611 | |||
612 | return chSchIsRescRequiredI(); | ||
613 | } | ||
614 | |||
615 | /** | ||
616 | * @brief Switches to the first thread on the runnable queue. | ||
617 | * @note Not a user function, it is meant to be invoked by the scheduler | ||
618 | * itself or from within the port layer. | ||
619 | * | ||
620 | * @special | ||
621 | */ | ||
622 | void chSchDoReschedule(void) { | ||
623 | thread_t *otp = nil.current; | ||
624 | |||
625 | nil.current = nil.next; | ||
626 | if (otp == &nil.threads[CH_CFG_MAX_THREADS]) { | ||
627 | CH_CFG_IDLE_LEAVE_HOOK(); | ||
628 | } | ||
629 | port_switch(nil.next, otp); | ||
630 | } | ||
631 | |||
632 | /** | ||
633 | * @brief Reschedules if needed. | ||
634 | * | ||
635 | * @sclass | ||
636 | */ | ||
637 | void chSchRescheduleS(void) { | ||
638 | |||
639 | chDbgCheckClassS(); | ||
640 | |||
641 | if (chSchIsRescRequiredI()) { | ||
642 | chSchDoReschedule(); | ||
643 | } | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * @brief Puts the current thread to sleep into the specified state with | ||
648 | * timeout specification. | ||
649 | * @details The thread goes into a sleeping state, if it is not awakened | ||
650 | * explicitly within the specified system time then it is forcibly | ||
651 | * awakened with a @p MSG_TIMEOUT low level message. | ||
652 | * | ||
653 | * @param[in] newstate the new thread state or a semaphore pointer | ||
654 | * @param[in] timeout the number of ticks before the operation timeouts. | ||
655 | * the following special values are allowed: | ||
656 | * - @a TIME_INFINITE no timeout. | ||
657 | * . | ||
658 | * @return The wakeup message. | ||
659 | * @retval MSG_TIMEOUT if a timeout occurred. | ||
660 | * | ||
661 | * @sclass | ||
662 | */ | ||
663 | msg_t chSchGoSleepTimeoutS(tstate_t newstate, sysinterval_t timeout) { | ||
664 | thread_t *ntp, *otp = nil.current; | ||
665 | |||
666 | chDbgCheckClassS(); | ||
667 | |||
668 | chDbgAssert(otp != &nil.threads[CH_CFG_MAX_THREADS], | ||
669 | "idle cannot sleep"); | ||
670 | |||
671 | /* Storing the wait object for the current thread.*/ | ||
672 | otp->state = newstate; | ||
673 | |||
674 | #if CH_CFG_ST_TIMEDELTA > 0 | ||
675 | if (timeout != TIME_INFINITE) { | ||
676 | systime_t abstime; | ||
677 | |||
678 | /* TIMEDELTA makes sure to have enough time to reprogram the timer | ||
679 | before the free-running timer counter reaches the selected timeout.*/ | ||
680 | if (timeout < (sysinterval_t)CH_CFG_ST_TIMEDELTA) { | ||
681 | timeout = (sysinterval_t)CH_CFG_ST_TIMEDELTA; | ||
682 | } | ||
683 | |||
684 | /* Absolute time of the timeout event.*/ | ||
685 | abstime = chTimeAddX(chVTGetSystemTimeX(), timeout); | ||
686 | |||
687 | if (nil.lasttime == nil.nexttime) { | ||
688 | /* Special case, first thread asking for a timeout.*/ | ||
689 | port_timer_start_alarm(abstime); | ||
690 | nil.nexttime = abstime; | ||
691 | } | ||
692 | else { | ||
693 | /* Special case, there are already other threads with a timeout | ||
694 | activated, evaluating the order.*/ | ||
695 | if (chTimeIsInRangeX(abstime, nil.lasttime, nil.nexttime)) { | ||
696 | port_timer_set_alarm(abstime); | ||
697 | nil.nexttime = abstime; | ||
698 | } | ||
699 | } | ||
700 | |||
701 | /* Timeout settings.*/ | ||
702 | otp->timeout = abstime - nil.lasttime; | ||
703 | } | ||
704 | #else | ||
705 | |||
706 | /* Timeout settings.*/ | ||
707 | otp->timeout = timeout; | ||
708 | #endif | ||
709 | |||
710 | /* Scanning the whole threads array.*/ | ||
711 | ntp = nil.threads; | ||
712 | while (true) { | ||
713 | /* Is this thread ready to execute?*/ | ||
714 | if (NIL_THD_IS_READY(ntp)) { | ||
715 | nil.current = nil.next = ntp; | ||
716 | if (ntp == &nil.threads[CH_CFG_MAX_THREADS]) { | ||
717 | CH_CFG_IDLE_ENTER_HOOK(); | ||
718 | } | ||
719 | port_switch(ntp, otp); | ||
720 | return nil.current->u1.msg; | ||
721 | } | ||
722 | |||
723 | /* Points to the next thread in lowering priority order.*/ | ||
724 | ntp++; | ||
725 | chDbgAssert(ntp <= &nil.threads[CH_CFG_MAX_THREADS], | ||
726 | "pointer out of range"); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | /** | ||
731 | * @brief Checks if the specified time is within the specified time range. | ||
732 | * @note When start==end then the function returns always false because the | ||
733 | * time window has zero size. | ||
734 | * | ||
735 | * @param[in] time the time to be verified | ||
736 | * @param[in] start the start of the time window (inclusive) | ||
737 | * @param[in] end the end of the time window (non inclusive) | ||
738 | * @retval true current time within the specified time window. | ||
739 | * @retval false current time not within the specified time window. | ||
740 | * | ||
741 | * @xclass | ||
742 | */ | ||
743 | bool chTimeIsInRangeX(systime_t time, systime_t start, systime_t end) { | ||
744 | |||
745 | return (bool)((systime_t)((systime_t)(time) - (systime_t)(start)) < | ||
746 | (systime_t)((systime_t)(end) - (systime_t)(start))); | ||
747 | } | ||
748 | |||
749 | /** | ||
750 | * @brief Creates a new thread into a static memory area. | ||
751 | * @details The new thread is initialized and make ready to execute. | ||
752 | * @note A thread can terminate by calling @p chThdExit() or by simply | ||
753 | * returning from its main function. | ||
754 | * | ||
755 | * @param[out] tdp pointer to the thread descriptor structure | ||
756 | * @return The pointer to the @p thread_t structure allocated for | ||
757 | * the thread. | ||
758 | * | ||
759 | * @iclass | ||
760 | */ | ||
761 | thread_t *chThdCreateI(const thread_descriptor_t *tdp) { | ||
762 | thread_t *tp; | ||
763 | |||
764 | chDbgCheck((tdp->prio < (tprio_t)CH_CFG_MAX_THREADS) && | ||
765 | (tdp->wbase != NULL) && | ||
766 | MEM_IS_ALIGNED(tdp->wbase, PORT_WORKING_AREA_ALIGN) && | ||
767 | (tdp->wend > tdp->wbase) && | ||
768 | MEM_IS_ALIGNED(tdp->wbase, PORT_STACK_ALIGN) && | ||
769 | (tdp->funcp != NULL)); | ||
770 | |||
771 | chDbgCheckClassI(); | ||
772 | |||
773 | /* Pointer to the thread slot to be used.*/ | ||
774 | tp = &nil.threads[tdp->prio]; | ||
775 | chDbgAssert(NIL_THD_IS_WTSTART(tp) || NIL_THD_IS_FINAL(tp), | ||
776 | "priority slot taken"); | ||
777 | |||
778 | #if CH_CFG_USE_EVENTS == TRUE | ||
779 | tp->epmask = (eventmask_t)0; | ||
780 | #endif | ||
781 | #if CH_DBG_ENABLE_STACK_CHECK == TRUE | ||
782 | tp->wabase = (stkalign_t *)tdp->wbase; | ||
783 | #endif | ||
784 | |||
785 | /* Port dependent thread initialization.*/ | ||
786 | PORT_SETUP_CONTEXT(tp, tdp->wbase, tdp->wend, tdp->funcp, tdp->arg); | ||
787 | |||
788 | /* Initialization hook.*/ | ||
789 | CH_CFG_THREAD_EXT_INIT_HOOK(tp); | ||
790 | |||
791 | /* Readying up thread.*/ | ||
792 | return chSchReadyI(tp, MSG_OK); | ||
793 | } | ||
794 | |||
795 | /** | ||
796 | * @brief Creates a new thread into a static memory area. | ||
797 | * @details The new thread is initialized and make ready to execute. | ||
798 | * @note A thread can terminate by calling @p chThdExit() or by simply | ||
799 | * returning from its main function. | ||
800 | * | ||
801 | * @param[out] tdp pointer to the thread descriptor structure | ||
802 | * @return The pointer to the @p thread_t structure allocated for | ||
803 | * the thread. | ||
804 | * | ||
805 | * @api | ||
806 | */ | ||
807 | thread_t *chThdCreate(const thread_descriptor_t *tdp) { | ||
808 | thread_t *tp; | ||
809 | |||
810 | chSysLock(); | ||
811 | tp = chThdCreateI(tdp); | ||
812 | chSchRescheduleS(); | ||
813 | chSysUnlock(); | ||
814 | |||
815 | return tp; | ||
816 | } | ||
817 | |||
818 | /** | ||
819 | * @brief Terminates the current thread. | ||
820 | * @details The thread goes in the @p CH_STATE_FINAL state holding the | ||
821 | * specified exit status code, other threads can retrieve the | ||
822 | * exit status code by invoking the function @p chThdWait(). | ||
823 | * @post Exiting a non-static thread that does not have references | ||
824 | * (detached) causes the thread to remain in the registry. | ||
825 | * It can only be removed by performing a registry scan operation. | ||
826 | * @post Eventual code after this function will never be executed, | ||
827 | * this function never returns. The compiler has no way to | ||
828 | * know this so do not assume that the compiler would remove | ||
829 | * the dead code. | ||
830 | * | ||
831 | * @param[in] msg thread exit code | ||
832 | * | ||
833 | * @api | ||
834 | */ | ||
835 | void chThdExit(msg_t msg) { | ||
836 | |||
837 | chSysLock(); | ||
838 | |||
839 | /* Exit handler hook.*/ | ||
840 | CH_CFG_THREAD_EXIT_HOOK(tp); | ||
841 | |||
842 | #if CH_CFG_USE_WAITEXIT == TRUE | ||
843 | { | ||
844 | /* Waking up any waiting thread.*/ | ||
845 | thread_t *tp = nil.threads; | ||
846 | while (tp < &nil.threads[CH_CFG_MAX_THREADS]) { | ||
847 | /* Is this thread waiting for current thread termination?*/ | ||
848 | if ((tp->state == NIL_STATE_WTEXIT) && (tp->u1.tp == nil.current)) { | ||
849 | (void) chSchReadyI(tp, msg); | ||
850 | } | ||
851 | tp++; | ||
852 | } | ||
853 | } | ||
854 | #endif | ||
855 | |||
856 | /* Going into final state with exit message stored.*/ | ||
857 | nil.current->u1.msg = msg; | ||
858 | (void) chSchGoSleepTimeoutS(NIL_STATE_FINAL, TIME_INFINITE); | ||
859 | |||
860 | /* The thread never returns here.*/ | ||
861 | chDbgAssert(false, "zombies apocalypse"); | ||
862 | } | ||
863 | |||
864 | /** | ||
865 | * @brief Blocks the execution of the invoking thread until the specified | ||
866 | * thread terminates then the exit code is returned. | ||
867 | * | ||
868 | * @param[in] tp pointer to the thread | ||
869 | * @return The exit code from the terminated thread. | ||
870 | * | ||
871 | * @api | ||
872 | */ | ||
873 | msg_t chThdWait(thread_t *tp) { | ||
874 | msg_t msg; | ||
875 | |||
876 | chSysLock(); | ||
877 | if (NIL_THD_IS_FINAL(tp)) { | ||
878 | msg = tp->u1.msg; | ||
879 | } | ||
880 | else { | ||
881 | nil.current->u1.tp = tp; | ||
882 | msg = chSchGoSleepTimeoutS(NIL_STATE_WTEXIT, TIME_INFINITE); | ||
883 | } | ||
884 | chSysUnlock(); | ||
885 | |||
886 | return msg; | ||
887 | } | ||
888 | |||
889 | /** | ||
890 | * @brief Sends the current thread sleeping and sets a reference variable. | ||
891 | * @note This function must reschedule, it can only be called from thread | ||
892 | * context. | ||
893 | * | ||
894 | * @param[in] trp a pointer to a thread reference object | ||
895 | * @param[in] timeout the number of ticks before the operation timeouts, | ||
896 | * the following special values are allowed: | ||
897 | * - @a TIME_IMMEDIATE immediate timeout. | ||
898 | * - @a TIME_INFINITE no timeout. | ||
899 | * . | ||
900 | * @return The wake up message. | ||
901 | * | ||
902 | * @sclass | ||
903 | */ | ||
904 | msg_t chThdSuspendTimeoutS(thread_reference_t *trp, sysinterval_t timeout) { | ||
905 | |||
906 | chDbgAssert(*trp == NULL, "not NULL"); | ||
907 | |||
908 | if (TIME_IMMEDIATE == timeout) { | ||
909 | return MSG_TIMEOUT; | ||
910 | } | ||
911 | |||
912 | *trp = nil.current; | ||
913 | nil.current->u1.trp = trp; | ||
914 | return chSchGoSleepTimeoutS(NIL_STATE_SUSPENDED, timeout); | ||
915 | } | ||
916 | |||
917 | /** | ||
918 | * @brief Wakes up a thread waiting on a thread reference object. | ||
919 | * @note This function must not reschedule because it can be called from | ||
920 | * ISR context. | ||
921 | * | ||
922 | * @param[in] trp a pointer to a thread reference object | ||
923 | * @param[in] msg the message code | ||
924 | * | ||
925 | * @iclass | ||
926 | */ | ||
927 | void chThdResumeI(thread_reference_t *trp, msg_t msg) { | ||
928 | |||
929 | if (*trp != NULL) { | ||
930 | thread_reference_t tr = *trp; | ||
931 | |||
932 | chDbgAssert(NIL_THD_IS_SUSPENDED(tr), "not suspended"); | ||
933 | |||
934 | *trp = NULL; | ||
935 | (void) chSchReadyI(tr, msg); | ||
936 | } | ||
937 | } | ||
938 | |||
939 | /** | ||
940 | * @brief Wakes up a thread waiting on a thread reference object. | ||
941 | * @note This function must reschedule, it can only be called from thread | ||
942 | * context. | ||
943 | * | ||
944 | * @param[in] trp a pointer to a thread reference object | ||
945 | * @param[in] msg the message code | ||
946 | * | ||
947 | * @api | ||
948 | */ | ||
949 | void chThdResume(thread_reference_t *trp, msg_t msg) { | ||
950 | |||
951 | chSysLock(); | ||
952 | chThdResumeS(trp, msg); | ||
953 | chSysUnlock(); | ||
954 | } | ||
955 | |||
956 | /** | ||
957 | * @brief Suspends the invoking thread for the specified time. | ||
958 | * | ||
959 | * @param[in] timeout the delay in system ticks | ||
960 | * | ||
961 | * @api | ||
962 | */ | ||
963 | void chThdSleep(sysinterval_t timeout) { | ||
964 | |||
965 | chSysLock(); | ||
966 | chThdSleepS(timeout); | ||
967 | chSysUnlock(); | ||
968 | } | ||
969 | |||
970 | /** | ||
971 | * @brief Suspends the invoking thread until the system time arrives to the | ||
972 | * specified value. | ||
973 | * | ||
974 | * @param[in] abstime absolute system time | ||
975 | * | ||
976 | * @api | ||
977 | */ | ||
978 | void chThdSleepUntil(systime_t abstime) { | ||
979 | |||
980 | chSysLock(); | ||
981 | chThdSleepUntilS(abstime); | ||
982 | chSysUnlock(); | ||
983 | } | ||
984 | |||
985 | /** | ||
986 | * @brief Enqueues the caller thread on a threads queue object. | ||
987 | * @details The caller thread is enqueued and put to sleep until it is | ||
988 | * dequeued or the specified timeouts expires. | ||
989 | * | ||
990 | * @param[in] tqp pointer to the threads queue object | ||
991 | * @param[in] timeout the timeout in system ticks, the special values are | ||
992 | * handled as follow: | ||
993 | * - @a TIME_IMMEDIATE immediate timeout. | ||
994 | * - @a TIME_INFINITE no timeout. | ||
995 | * . | ||
996 | * @return The message from @p osalQueueWakeupOneI() or | ||
997 | * @p osalQueueWakeupAllI() functions. | ||
998 | * @retval MSG_TIMEOUT if the thread has not been dequeued within the | ||
999 | * specified timeout or if the function has been | ||
1000 | * invoked with @p TIME_IMMEDIATE as timeout | ||
1001 | * specification. | ||
1002 | * | ||
1003 | * @sclass | ||
1004 | */ | ||
1005 | msg_t chThdEnqueueTimeoutS(threads_queue_t *tqp, sysinterval_t timeout) { | ||
1006 | |||
1007 | chDbgCheckClassS(); | ||
1008 | chDbgCheck(tqp != NULL); | ||
1009 | |||
1010 | chDbgAssert(tqp->cnt <= (cnt_t)0, "invalid counter"); | ||
1011 | |||
1012 | if (TIME_IMMEDIATE == timeout) { | ||
1013 | return MSG_TIMEOUT; | ||
1014 | } | ||
1015 | |||
1016 | tqp->cnt--; | ||
1017 | nil.current->u1.tqp = tqp; | ||
1018 | return chSchGoSleepTimeoutS(NIL_STATE_WTQUEUE, timeout); | ||
1019 | } | ||
1020 | |||
1021 | /** | ||
1022 | * @brief Dequeues and wakes up one thread from the threads queue object. | ||
1023 | * @details Dequeues one thread from the queue without checking if the queue | ||
1024 | * is empty. | ||
1025 | * @pre The queue must contain at least an object. | ||
1026 | * | ||
1027 | * @param[in] tqp pointer to the threads queue object | ||
1028 | * @param[in] msg the message code | ||
1029 | * | ||
1030 | * @iclass | ||
1031 | */ | ||
1032 | void chThdDoDequeueNextI(threads_queue_t *tqp, msg_t msg) { | ||
1033 | thread_t *tp; | ||
1034 | |||
1035 | chDbgAssert(tqp->cnt < (cnt_t)0, "empty queue"); | ||
1036 | |||
1037 | tqp->cnt++; | ||
1038 | tp = nil_find_thread(NIL_STATE_WTQUEUE, (void *)tqp); | ||
1039 | |||
1040 | chDbgAssert(tp != NULL, "thread not found"); | ||
1041 | |||
1042 | (void) chSchReadyI(tp, msg); | ||
1043 | } | ||
1044 | |||
1045 | /** | ||
1046 | * @brief Dequeues and wakes up one thread from the threads queue object, | ||
1047 | * if any. | ||
1048 | * | ||
1049 | * @param[in] tqp pointer to the threads queue object | ||
1050 | * @param[in] msg the message code | ||
1051 | * | ||
1052 | * @iclass | ||
1053 | */ | ||
1054 | void chThdDequeueNextI(threads_queue_t *tqp, msg_t msg) { | ||
1055 | |||
1056 | chDbgCheckClassI(); | ||
1057 | chDbgCheck(tqp != NULL); | ||
1058 | |||
1059 | if (tqp->cnt < (cnt_t)0) { | ||
1060 | chThdDoDequeueNextI(tqp, msg); | ||
1061 | } | ||
1062 | } | ||
1063 | |||
1064 | /** | ||
1065 | * @brief Dequeues and wakes up all threads from the threads queue object. | ||
1066 | * | ||
1067 | * @param[in] tqp pointer to the threads queue object | ||
1068 | * @param[in] msg the message code | ||
1069 | * | ||
1070 | * @iclass | ||
1071 | */ | ||
1072 | void chThdDequeueAllI(threads_queue_t *tqp, msg_t msg) { | ||
1073 | |||
1074 | chDbgCheckClassI(); | ||
1075 | chDbgCheck(tqp != NULL); | ||
1076 | |||
1077 | tqp->cnt = nil_ready_all((void *)tqp, tqp->cnt, msg); | ||
1078 | } | ||
1079 | |||
1080 | /** @} */ | ||