aboutsummaryrefslogtreecommitdiff
path: root/lib/chibios/os/rt/src/chevents.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chibios/os/rt/src/chevents.c')
-rw-r--r--lib/chibios/os/rt/src/chevents.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/lib/chibios/os/rt/src/chevents.c b/lib/chibios/os/rt/src/chevents.c
new file mode 100644
index 000000000..2ada41eb2
--- /dev/null
+++ b/lib/chibios/os/rt/src/chevents.c
@@ -0,0 +1,603 @@
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 Concepts and parts of this file have been contributed by Scott (skute).
21 */
22
23/**
24 * @file rt/src/chevents.c
25 * @brief Events code.
26 *
27 * @addtogroup events
28 * @details Event Flags, Event Sources and Event Listeners.
29 * <h2>Operation mode</h2>
30 * Each thread has a mask of pending events inside its
31 * @p thread_t structure.
32 * Operations defined for events:
33 * - <b>Wait</b>, the invoking thread goes to sleep until a certain
34 * AND/OR combination of events are signaled.
35 * - <b>Clear</b>, a mask of events is cleared from the pending
36 * events, the cleared events mask is returned (only the
37 * events that were actually pending and then cleared).
38 * - <b>Signal</b>, an events mask is directly ORed to the mask of
39 * the signaled thread.
40 * - <b>Broadcast</b>, each thread registered on an Event Source is
41 * signaled with the events specified in its Event Listener.
42 * - <b>Dispatch</b>, an events mask is scanned and for each bit set
43 * to one an associated handler function is invoked. Bit masks are
44 * scanned from bit zero upward.
45 * .
46 * An Event Source is a special object that can be "broadcasted" by
47 * a thread or an interrupt service routine. Broadcasting an Event
48 * Source has the effect that all the threads registered on the
49 * Event Source will be signaled with an events mask.<br>
50 * An unlimited number of Event Sources can exists in a system and
51 * each thread can be listening on an unlimited number of
52 * them.
53 * @pre In order to use the Events APIs the @p CH_CFG_USE_EVENTS option
54 * must be enabled in @p chconf.h.
55 * @post Enabling events requires 1-4 (depending on the architecture)
56 * extra bytes in the @p thread_t structure.
57 * @{
58 */
59
60#include "ch.h"
61
62#if (CH_CFG_USE_EVENTS == TRUE) || defined(__DOXYGEN__)
63
64/*===========================================================================*/
65/* Module local definitions. */
66/*===========================================================================*/
67
68/*===========================================================================*/
69/* Module exported variables. */
70/*===========================================================================*/
71
72/*===========================================================================*/
73/* Module local types. */
74/*===========================================================================*/
75
76/*===========================================================================*/
77/* Module local variables. */
78/*===========================================================================*/
79
80/*===========================================================================*/
81/* Module local functions. */
82/*===========================================================================*/
83
84/*===========================================================================*/
85/* Module exported functions. */
86/*===========================================================================*/
87
88/**
89 * @brief Registers an Event Listener on an Event Source.
90 * @details Once a thread has registered as listener on an event source it
91 * will be notified of all events broadcasted there.
92 * @note Multiple Event Listeners can specify the same bits to be ORed to
93 * different threads.
94 *
95 * @param[in] esp pointer to the @p event_source_t structure
96 * @param[in] elp pointer to the @p event_listener_t structure
97 * @param[in] events events to be ORed to the thread when
98 * the event source is broadcasted
99 * @param[in] wflags mask of flags the listening thread is interested in
100 *
101 * @api
102 */
103void chEvtRegisterMaskWithFlags(event_source_t *esp,
104 event_listener_t *elp,
105 eventmask_t events,
106 eventflags_t wflags) {
107
108 chDbgCheck((esp != NULL) && (elp != NULL));
109
110 chSysLock();
111 elp->next = esp->next;
112 esp->next = elp;
113 elp->listener = currp;
114 elp->events = events;
115 elp->flags = (eventflags_t)0;
116 elp->wflags = wflags;
117 chSysUnlock();
118}
119
120/**
121 * @brief Unregisters an Event Listener from its Event Source.
122 * @note If the event listener is not registered on the specified event
123 * source then the function does nothing.
124 * @note For optimal performance it is better to perform the unregister
125 * operations in inverse order of the register operations (elements
126 * are found on top of the list).
127 *
128 * @param[in] esp pointer to the @p event_source_t structure
129 * @param[in] elp pointer to the @p event_listener_t structure
130 *
131 * @api
132 */
133void chEvtUnregister(event_source_t *esp, event_listener_t *elp) {
134 event_listener_t *p;
135
136 chDbgCheck((esp != NULL) && (elp != NULL));
137
138 /*lint -save -e9087 -e740 [11.3, 1.3] Cast required by list handling.*/
139 p = (event_listener_t *)esp;
140 /*lint -restore*/
141 chSysLock();
142 /*lint -save -e9087 -e740 [11.3, 1.3] Cast required by list handling.*/
143 while (p->next != (event_listener_t *)esp) {
144 /*lint -restore*/
145 if (p->next == elp) {
146 p->next = elp->next;
147 break;
148 }
149 p = p->next;
150 }
151 chSysUnlock();
152}
153
154/**
155 * @brief Clears the pending events specified in the events mask.
156 *
157 * @param[in] events the events to be cleared
158 * @return The mask of pending events that were cleared.
159 *
160 * @iclass
161 */
162eventmask_t chEvtGetAndClearEventsI(eventmask_t events) {
163 eventmask_t m;
164
165 m = currp->epending & events;
166 currp->epending &= ~events;
167
168 return m;
169}
170
171/**
172 * @brief Clears the pending events specified in the events mask.
173 *
174 * @param[in] events the events to be cleared
175 * @return The mask of pending events that were cleared.
176 *
177 * @api
178 */
179eventmask_t chEvtGetAndClearEvents(eventmask_t events) {
180 eventmask_t m;
181
182 chSysLock();
183 m = chEvtGetAndClearEventsI(events);
184 chSysUnlock();
185
186 return m;
187}
188
189/**
190 * @brief Adds (OR) a set of events to the current thread, this is
191 * @b much faster than using @p chEvtBroadcast() or @p chEvtSignal().
192 *
193 * @param[in] events the events to be added
194 * @return The mask of currently pending events.
195 *
196 * @api
197 */
198eventmask_t chEvtAddEvents(eventmask_t events) {
199 eventmask_t newevt;
200
201 chSysLock();
202 newevt = chEvtAddEventsI(events);
203 chSysUnlock();
204
205 return newevt;
206}
207
208/**
209 * @brief Signals all the Event Listeners registered on the specified Event
210 * Source.
211 * @details This function variants ORs the specified event flags to all the
212 * threads registered on the @p event_source_t in addition to the
213 * event flags specified by the threads themselves in the
214 * @p event_listener_t objects.
215 * @post This function does not reschedule so a call to a rescheduling
216 * function must be performed before unlocking the kernel. Note that
217 * interrupt handlers always reschedule on exit so an explicit
218 * reschedule must not be performed in ISRs.
219 *
220 * @param[in] esp pointer to the @p event_source_t structure
221 * @param[in] flags the flags set to be added to the listener flags mask
222 *
223 * @iclass
224 */
225void chEvtBroadcastFlagsI(event_source_t *esp, eventflags_t flags) {
226 event_listener_t *elp;
227
228 chDbgCheckClassI();
229 chDbgCheck(esp != NULL);
230
231 elp = esp->next;
232 /*lint -save -e9087 -e740 [11.3, 1.3] Cast required by list handling.*/
233 while (elp != (event_listener_t *)esp) {
234 /*lint -restore*/
235 elp->flags |= flags;
236 /* When flags == 0 the thread will always be signaled because the
237 source does not emit any flag.*/
238 if ((flags == (eventflags_t)0) ||
239 ((flags & elp->wflags) != (eventflags_t)0)) {
240 chEvtSignalI(elp->listener, elp->events);
241 }
242 elp = elp->next;
243 }
244}
245
246/**
247 * @brief Returns the flags associated to an @p event_listener_t.
248 * @details The flags are returned and the @p event_listener_t flags mask is
249 * cleared.
250 *
251 * @param[in] elp pointer to the @p event_listener_t structure
252 * @return The flags added to the listener by the associated
253 * event source.
254 *
255 * @api
256 */
257eventflags_t chEvtGetAndClearFlags(event_listener_t *elp) {
258 eventflags_t flags;
259
260 chSysLock();
261 flags = elp->flags;
262 elp->flags = (eventflags_t)0;
263 chSysUnlock();
264
265 return flags & elp->wflags;
266}
267
268/**
269 * @brief Adds a set of event flags directly to the specified @p thread_t.
270 *
271 * @param[in] tp the thread to be signaled
272 * @param[in] events the events set to be ORed
273 *
274 * @api
275 */
276void chEvtSignal(thread_t *tp, eventmask_t events) {
277
278 chDbgCheck(tp != NULL);
279
280 chSysLock();
281 chEvtSignalI(tp, events);
282 chSchRescheduleS();
283 chSysUnlock();
284}
285
286/**
287 * @brief Adds a set of event flags directly to the specified @p thread_t.
288 * @post This function does not reschedule so a call to a rescheduling
289 * function must be performed before unlocking the kernel. Note that
290 * interrupt handlers always reschedule on exit so an explicit
291 * reschedule must not be performed in ISRs.
292 *
293 * @param[in] tp the thread to be signaled
294 * @param[in] events the events set to be ORed
295 *
296 * @iclass
297 */
298void chEvtSignalI(thread_t *tp, eventmask_t events) {
299
300 chDbgCheckClassI();
301 chDbgCheck(tp != NULL);
302
303 tp->epending |= events;
304 /* Test on the AND/OR conditions wait states.*/
305 if (((tp->state == CH_STATE_WTOREVT) &&
306 ((tp->epending & tp->u.ewmask) != (eventmask_t)0)) ||
307 ((tp->state == CH_STATE_WTANDEVT) &&
308 ((tp->epending & tp->u.ewmask) == tp->u.ewmask))) {
309 tp->u.rdymsg = MSG_OK;
310 (void) chSchReadyI(tp);
311 }
312}
313
314/**
315 * @brief Signals all the Event Listeners registered on the specified Event
316 * Source.
317 * @details This function variants ORs the specified event flags to all the
318 * threads registered on the @p event_source_t in addition to the
319 * event flags specified by the threads themselves in the
320 * @p event_listener_t objects.
321 *
322 * @param[in] esp pointer to the @p event_source_t structure
323 * @param[in] flags the flags set to be added to the listener flags mask
324 *
325 * @api
326 */
327void chEvtBroadcastFlags(event_source_t *esp, eventflags_t flags) {
328
329 chSysLock();
330 chEvtBroadcastFlagsI(esp, flags);
331 chSchRescheduleS();
332 chSysUnlock();
333}
334
335/**
336 * @brief Returns the unmasked flags associated to an @p event_listener_t.
337 * @details The flags are returned and the @p event_listener_t flags mask is
338 * cleared.
339 *
340 * @param[in] elp pointer to the @p event_listener_t structure
341 * @return The flags added to the listener by the associated
342 * event source.
343 *
344 * @iclass
345 */
346eventflags_t chEvtGetAndClearFlagsI(event_listener_t *elp) {
347 eventflags_t flags;
348
349 flags = elp->flags;
350 elp->flags = (eventflags_t)0;
351
352 return flags & elp->wflags;
353}
354
355/**
356 * @brief Invokes the event handlers associated to an event flags mask.
357 *
358 * @param[in] events mask of events to be dispatched
359 * @param[in] handlers an array of @p evhandler_t. The array must have size
360 * equal to the number of bits in eventmask_t.
361 *
362 * @api
363 */
364void chEvtDispatch(const evhandler_t *handlers, eventmask_t events) {
365 eventid_t eid;
366
367 chDbgCheck(handlers != NULL);
368
369 eid = (eventid_t)0;
370 while (events != (eventmask_t)0) {
371 if ((events & EVENT_MASK(eid)) != (eventmask_t)0) {
372 chDbgAssert(handlers[eid] != NULL, "null handler");
373 events &= ~EVENT_MASK(eid);
374 handlers[eid](eid);
375 }
376 eid++;
377 }
378}
379
380#if (CH_CFG_OPTIMIZE_SPEED == TRUE) || \
381 (CH_CFG_USE_EVENTS_TIMEOUT == FALSE) || \
382 defined(__DOXYGEN__)
383/**
384 * @brief Waits for exactly one of the specified events.
385 * @details The function waits for one event among those specified in
386 * @p events to become pending then the event is cleared and returned.
387 * @note One and only one event is served in the function, the one with the
388 * lowest event id. The function is meant to be invoked into a loop in
389 * order to serve all the pending events.<br>
390 * This means that Event Listeners with a lower event identifier have
391 * an higher priority.
392 *
393 * @param[in] events events that the function should wait
394 * for, @p ALL_EVENTS enables all the events
395 * @return The mask of the lowest event id served and cleared.
396 *
397 * @api
398 */
399eventmask_t chEvtWaitOne(eventmask_t events) {
400 thread_t *ctp = currp;
401 eventmask_t m;
402
403 chSysLock();
404 m = ctp->epending & events;
405 if (m == (eventmask_t)0) {
406 ctp->u.ewmask = events;
407 chSchGoSleepS(CH_STATE_WTOREVT);
408 m = ctp->epending & events;
409 }
410 m ^= m & (m - (eventmask_t)1);
411 ctp->epending &= ~m;
412 chSysUnlock();
413
414 return m;
415}
416
417/**
418 * @brief Waits for any of the specified events.
419 * @details The function waits for any event among those specified in
420 * @p events to become pending then the events are cleared and
421 * returned.
422 *
423 * @param[in] events events that the function should wait
424 * for, @p ALL_EVENTS enables all the events
425 * @return The mask of the served and cleared events.
426 *
427 * @api
428 */
429eventmask_t chEvtWaitAny(eventmask_t events) {
430 thread_t *ctp = currp;
431 eventmask_t m;
432
433 chSysLock();
434 m = ctp->epending & events;
435 if (m == (eventmask_t)0) {
436 ctp->u.ewmask = events;
437 chSchGoSleepS(CH_STATE_WTOREVT);
438 m = ctp->epending & events;
439 }
440 ctp->epending &= ~m;
441 chSysUnlock();
442
443 return m;
444}
445
446/**
447 * @brief Waits for all the specified events.
448 * @details The function waits for all the events specified in @p events to
449 * become pending then the events are cleared and returned.
450 *
451 * @param[in] events events that the function should wait
452 * for, @p ALL_EVENTS requires all the events
453 * @return The mask of the served and cleared events.
454 *
455 * @api
456 */
457eventmask_t chEvtWaitAll(eventmask_t events) {
458 thread_t *ctp = currp;
459
460 chSysLock();
461 if ((ctp->epending & events) != events) {
462 ctp->u.ewmask = events;
463 chSchGoSleepS(CH_STATE_WTANDEVT);
464 }
465 ctp->epending &= ~events;
466 chSysUnlock();
467
468 return events;
469}
470#endif /* CH_CFG_OPTIMIZE_SPEED || !CH_CFG_USE_EVENTS_TIMEOUT */
471
472#if (CH_CFG_USE_EVENTS_TIMEOUT == TRUE) || defined(__DOXYGEN__)
473/**
474 * @brief Waits for exactly one of the specified events.
475 * @details The function waits for one event among those specified in
476 * @p events to become pending then the event is cleared and returned.
477 * @note One and only one event is served in the function, the one with the
478 * lowest event id. The function is meant to be invoked into a loop
479 * in order to serve all the pending events.<br>
480 * This means that Event Listeners with a lower event identifier have
481 * an higher priority.
482 *
483 * @param[in] events events that the function should wait
484 * for, @p ALL_EVENTS enables all the events
485 * @param[in] timeout the number of ticks before the operation timeouts,
486 * the following special values are allowed:
487 * - @a TIME_IMMEDIATE immediate timeout.
488 * - @a TIME_INFINITE no timeout.
489 * .
490 * @return The mask of the lowest event id served and cleared.
491 * @retval 0 if the operation has timed out.
492 *
493 * @api
494 */
495eventmask_t chEvtWaitOneTimeout(eventmask_t events, sysinterval_t timeout) {
496 thread_t *ctp = currp;
497 eventmask_t m;
498
499 chSysLock();
500 m = ctp->epending & events;
501 if (m == (eventmask_t)0) {
502 if (TIME_IMMEDIATE == timeout) {
503 chSysUnlock();
504 return (eventmask_t)0;
505 }
506 ctp->u.ewmask = events;
507 if (chSchGoSleepTimeoutS(CH_STATE_WTOREVT, timeout) < MSG_OK) {
508 chSysUnlock();
509 return (eventmask_t)0;
510 }
511 m = ctp->epending & events;
512 }
513 m ^= m & (m - (eventmask_t)1);
514 ctp->epending &= ~m;
515 chSysUnlock();
516
517 return m;
518}
519
520/**
521 * @brief Waits for any of the specified events.
522 * @details The function waits for any event among those specified in
523 * @p events to become pending then the events are cleared and
524 * returned.
525 *
526 * @param[in] events events that the function should wait
527 * for, @p ALL_EVENTS enables all the events
528 * @param[in] timeout the number of ticks before the operation timeouts,
529 * the following special values are allowed:
530 * - @a TIME_IMMEDIATE immediate timeout.
531 * - @a TIME_INFINITE no timeout.
532 * .
533 * @return The mask of the served and cleared events.
534 * @retval 0 if the operation has timed out.
535 *
536 * @api
537 */
538eventmask_t chEvtWaitAnyTimeout(eventmask_t events, sysinterval_t timeout) {
539 thread_t *ctp = currp;
540 eventmask_t m;
541
542 chSysLock();
543 m = ctp->epending & events;
544 if (m == (eventmask_t)0) {
545 if (TIME_IMMEDIATE == timeout) {
546 chSysUnlock();
547 return (eventmask_t)0;
548 }
549 ctp->u.ewmask = events;
550 if (chSchGoSleepTimeoutS(CH_STATE_WTOREVT, timeout) < MSG_OK) {
551 chSysUnlock();
552 return (eventmask_t)0;
553 }
554 m = ctp->epending & events;
555 }
556 ctp->epending &= ~m;
557 chSysUnlock();
558
559 return m;
560}
561
562/**
563 * @brief Waits for all the specified events.
564 * @details The function waits for all the events specified in @p events to
565 * become pending then the events are cleared and returned.
566 *
567 * @param[in] events events that the function should wait
568 * for, @p ALL_EVENTS requires all the events
569 * @param[in] timeout the number of ticks before the operation timeouts,
570 * the following special values are allowed:
571 * - @a TIME_IMMEDIATE immediate timeout.
572 * - @a TIME_INFINITE no timeout.
573 * .
574 * @return The mask of the served and cleared events.
575 * @retval 0 if the operation has timed out.
576 *
577 * @api
578 */
579eventmask_t chEvtWaitAllTimeout(eventmask_t events, sysinterval_t timeout) {
580 thread_t *ctp = currp;
581
582 chSysLock();
583 if ((ctp->epending & events) != events) {
584 if (TIME_IMMEDIATE == timeout) {
585 chSysUnlock();
586 return (eventmask_t)0;
587 }
588 ctp->u.ewmask = events;
589 if (chSchGoSleepTimeoutS(CH_STATE_WTANDEVT, timeout) < MSG_OK) {
590 chSysUnlock();
591 return (eventmask_t)0;
592 }
593 }
594 ctp->epending &= ~events;
595 chSysUnlock();
596
597 return events;
598}
599#endif /* CH_CFG_USE_EVENTS_TIMEOUT == TRUE */
600
601#endif /* CH_CFG_USE_EVENTS == TRUE */
602
603/** @} */