osal_rs/freertos/event_group.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Event group synchronization primitives for FreeRTOS.
22//!
23//! Event groups allow threads to synchronize on multiple events simultaneously.
24//! Each event group contains a set of event bits (flags) that can be set, cleared,
25//! and waited upon. This is useful for complex synchronization scenarios where
26//! multiple conditions must be met.
27
28use core::fmt::{Debug, Display, Formatter};
29use core::ops::Deref;
30use core::ptr::null_mut;
31
32use super::ffi::{EventGroupHandle, pdFAIL, pdFALSE, vEventGroupDelete, xEventGroupClearBits, xEventGroupClearBitsFromISR, xEventGroupCreate, xEventGroupGetBitsFromISR, xEventGroupSetBits, xEventGroupSetBitsFromISR};
33use super::system::System;
34use super::types::{BaseType, EventBits, TickType};
35use crate::traits::{ToTick, EventGroupFn, SystemFn};
36use crate::utils::{Result, Error};
37
38/// A set of event flags for thread synchronization.
39///
40/// Event groups contain multiple event bits (typically 24 bits) that can be
41/// manipulated independently. Threads can wait for specific combinations of bits
42/// to be set, making them ideal for complex synchronization scenarios.
43///
44/// # Examples
45///
46/// ## Basic event signaling
47///
48/// ```ignore
49/// use osal_rs::os::{EventGroup, EventGroupFn};
50/// use core::time::Duration;
51///
52/// const EVENT_A: u32 = 0b0001;
53/// const EVENT_B: u32 = 0b0010;
54/// const EVENT_C: u32 = 0b0100;
55///
56/// let events = EventGroup::new().unwrap();
57///
58/// // Set event A
59/// events.set(EVENT_A);
60///
61/// // Check if event A is set
62/// let current = events.get();
63/// if current & EVENT_A != 0 {
64/// println!("Event A is set");
65/// }
66///
67/// // Clear event A
68/// events.clear(EVENT_A);
69/// ```
70///
71/// ## Waiting for multiple events
72///
73/// ```ignore
74/// use osal_rs::os::{EventGroup, EventGroupFn, Thread};
75/// use alloc::sync::Arc;
76/// use core::time::Duration;
77///
78/// const READY: u32 = 0b0001;
79/// const DATA_AVAILABLE: u32 = 0b0010;
80/// const STOP: u32 = 0b0100;
81///
82/// let events = Arc::new(EventGroup::new().unwrap());
83/// let events_clone = events.clone();
84///
85/// // Worker thread waits for events
86/// let worker = Thread::new("worker", 2048, 5, move || {
87/// loop {
88/// // Wait for either READY or STOP
89/// let bits = events_clone.wait_with_to_tick(
90/// READY | STOP,
91/// Duration::from_secs(1)
92/// );
93///
94/// if bits & STOP != 0 {
95/// println!("Stopping...");
96/// break;
97/// }
98///
99/// if bits & READY != 0 {
100/// println!("Ready to work!");
101/// }
102/// }
103/// }).unwrap();
104///
105/// worker.start().unwrap();
106///
107/// // Signal events
108/// events.set(READY);
109/// Duration::from_secs(2).sleep();
110/// events.set(STOP);
111/// ```
112///
113/// ## State machine synchronization
114///
115/// ```ignore
116/// use osal_rs::os::{EventGroup, EventGroupFn};
117/// use core::time::Duration;
118///
119/// const INIT_COMPLETE: u32 = 1 << 0;
120/// const CONFIG_LOADED: u32 = 1 << 1;
121/// const NETWORK_UP: u32 = 1 << 2;
122/// const READY_TO_RUN: u32 = INIT_COMPLETE | CONFIG_LOADED | NETWORK_UP;
123///
124/// let state = EventGroup::new().unwrap();
125///
126/// // Different subsystems set their bits
127/// state.set(INIT_COMPLETE);
128/// state.set(CONFIG_LOADED);
129/// state.set(NETWORK_UP);
130///
131/// // Wait for all systems to be ready
132/// let current = state.wait_with_to_tick(READY_TO_RUN, Duration::from_secs(5));
133///
134/// if (current & READY_TO_RUN) == READY_TO_RUN {
135/// println!("All systems ready!");
136/// }
137/// ```
138///
139/// ## ISR to thread signaling
140///
141/// ```ignore
142/// use osal_rs::os::{EventGroup, EventGroupFn, Thread};
143/// use alloc::sync::Arc;
144///
145/// const IRQ_EVENT: u32 = 1 << 0;
146///
147/// let events = Arc::new(EventGroup::new().unwrap());
148/// let events_isr = events.clone();
149///
150/// // In interrupt handler:
151/// // events_isr.set_from_isr(IRQ_EVENT).ok();
152///
153/// // Handler thread
154/// let handler = Thread::new("handler", 2048, 5, move || {
155/// loop {
156/// let bits = events.wait(IRQ_EVENT, 1000);
157/// if bits & IRQ_EVENT != 0 {
158/// println!("Handling interrupt event");
159/// events.clear(IRQ_EVENT);
160/// }
161/// }
162/// }).unwrap();
163/// ```
164pub struct EventGroup (EventGroupHandle);
165
166unsafe impl Send for EventGroup {}
167unsafe impl Sync for EventGroup {}
168
169impl EventGroup {
170
171 /// Maximum usable event bits mask.
172 /// FreeRTOS reserves the top 8 bits for internal use:
173 /// - For u32 (TickType): 0x00FFFFFF (24 bits usable)
174 /// - For u64 (TickType): 0x00FFFFFFFFFFFFFF (56 bits usable)
175 pub const MAX_MASK: EventBits = EventBits::MAX >> 8;
176
177 /// Waits for specified event bits to be set with a timeout in ticks.
178 /// This is a convenience method that converts a `ToTick` type to ticks and calls `wait`.
179 ///
180 /// # Examples
181 ////
182 /// ```ignore
183 /// use osal_rs::os::{EventGroup, EventGroupFn};
184 /// use core::time::Duration;
185 /// let events = EventGroup::new().unwrap();
186 /// let bits = events.wait_with_to_tick(0b0001, Duration::from_secs(1));
187 /// ```
188 pub fn wait_with_to_tick(&self, mask: EventBits, timeout_ticks: impl ToTick) -> EventBits {
189 self.wait(mask, timeout_ticks.to_ticks())
190 }
191}
192
193
194impl EventGroup {
195 /// Creates a new event group.
196 ///
197 /// # Returns
198 ///
199 /// * `Ok(Self)` - Successfully created event group
200 /// * `Err(Error)` - Creation failed (out of memory, etc.)
201 ///
202 /// # Examples
203 ///
204 /// ```ignore
205 /// use osal_rs::os::{EventGroup, EventGroupFn};
206 ///
207 /// let events = EventGroup::new().unwrap();
208 /// ```
209 pub fn new() -> Result<Self> {
210 let handle = unsafe { xEventGroupCreate() };
211 if handle.is_null() {
212 Err(Error::OutOfMemory)
213 } else {
214 Ok(Self (handle))
215 }
216 }
217
218}
219impl EventGroupFn for EventGroup {
220
221 /// Sets specified event bits.
222 ///
223 /// This function sets (raises) the specified event bits in the event group.
224 /// Any threads waiting for these bits may be unblocked.
225 ///
226 /// # Arguments
227 ///
228 /// * `bits` - The event bits to set (bitwise OR to set multiple bits)
229 ///
230 /// # Returns
231 ///
232 /// The event bits value after the set operation.
233 ///
234 /// # Examples
235 ///
236 /// ```ignore
237 /// use osal_rs::os::{EventGroup, EventGroupFn};
238 ///
239 /// let events = EventGroup::new().unwrap();
240 /// events.set(0b0001); // Set bit 0
241 /// events.set(0b0110); // Set bits 1 and 2
242 /// ```
243 fn set(&self, bits: EventBits) -> EventBits {
244 unsafe { xEventGroupSetBits(self.0, bits) }
245 }
246
247 /// Sets specified event bits from an interrupt service routine (ISR).
248 ///
249 /// This is the ISR-safe version of `set()`. It can be called from interrupt
250 /// context and will trigger a context switch if a higher priority thread
251 /// is unblocked by the bit setting.
252 ///
253 /// # Arguments
254 ///
255 /// * `bits` - The event bits to set
256 ///
257 /// # Returns
258 ///
259 /// * `Ok(())` - Bits were successfully set
260 /// * `Err(Error::QueueFull)` - Operation failed
261 ///
262 /// # Examples
263 ///
264 /// ```ignore
265 /// // In interrupt handler
266 /// use osal_rs::os::{EventGroup, EventGroupFn};
267 ///
268 /// fn interrupt_handler(events: &EventGroup) {
269 /// events.set_from_isr(0b0001).ok();
270 /// }
271 /// ```
272 fn set_from_isr(&self, bits: EventBits) -> Result<()> {
273
274 let mut higher_priority_task_woken: BaseType = pdFALSE;
275
276 let ret = unsafe { xEventGroupSetBitsFromISR(self.0, bits, &mut higher_priority_task_woken) };
277 if ret != pdFAIL {
278
279 System::yield_from_isr(higher_priority_task_woken);
280
281 Ok(())
282 } else {
283 Err(Error::QueueFull)
284 }
285 }
286
287 /// Gets the current value of event bits.
288 ///
289 /// Returns the current state of all event bits in the event group.
290 /// This is a non-blocking operation.
291 ///
292 /// # Returns
293 ///
294 /// The current event bits value.
295 ///
296 /// # Examples
297 ///
298 /// ```ignore
299 /// use osal_rs::os::{EventGroup, EventGroupFn};
300 ///
301 /// let events = EventGroup::new().unwrap();
302 /// events.set(0b0101);
303 /// let current = events.get();
304 /// assert_eq!(current & 0b0101, 0b0101);
305 /// ```
306 fn get(&self) -> EventBits {
307 xEventGroupGetBits!(self.0)
308 }
309
310 /// Gets the current value of event bits from an ISR.
311 ///
312 /// This is the ISR-safe version of `get()`. It can be called from
313 /// interrupt context to read the current event bits.
314 ///
315 /// # Returns
316 ///
317 /// The current event bits value.
318 ///
319 /// # Examples
320 ///
321 /// ```ignore
322 /// // In interrupt handler
323 /// use osal_rs::os::{EventGroup, EventGroupFn};
324 ///
325 /// fn interrupt_handler(events: &EventGroup) {
326 /// let current = events.get_from_isr();
327 /// }
328 /// ```
329 fn get_from_isr(&self) -> EventBits {
330 unsafe { xEventGroupGetBitsFromISR(self.0) }
331 }
332
333
334 /// Clears specified event bits.
335 ///
336 /// This function clears (lowers) the specified event bits in the event group.
337 ///
338 /// # Arguments
339 ///
340 /// * `bits` - The event bits to clear (bitwise OR to clear multiple bits)
341 ///
342 /// # Returns
343 ///
344 /// The event bits value before the clear operation.
345 ///
346 /// # Examples
347 ///
348 /// ```ignore
349 /// use osal_rs::os::{EventGroup, EventGroupFn};
350 ///
351 /// let events = EventGroup::new().unwrap();
352 /// events.set(0b1111);
353 /// events.clear(0b0011); // Clear bits 0 and 1
354 /// let current = events.get();
355 /// assert_eq!(current & 0b1111, 0b1100);
356 /// ```
357 fn clear(&self, bits: EventBits) -> EventBits {
358 unsafe { xEventGroupClearBits(self.0, bits) }
359 }
360
361 /// Clears specified event bits from an ISR.
362 ///
363 /// This is the ISR-safe version of `clear()`. It can be called from
364 /// interrupt context to clear event bits.
365 ///
366 /// # Arguments
367 ///
368 /// * `bits` - The event bits to clear
369 ///
370 /// # Returns
371 ///
372 /// * `Ok(())` - Bits were successfully cleared
373 /// * `Err(Error::QueueFull)` - Operation failed
374 ///
375 /// # Examples
376 ///
377 /// ```ignore
378 /// // In interrupt handler
379 /// use osal_rs::os::{EventGroup, EventGroupFn};
380 ///
381 /// fn interrupt_handler(events: &EventGroup) {
382 /// events.clear_from_isr(0b0001).ok();
383 /// }
384 /// ```
385 fn clear_from_isr(&self, bits: EventBits) -> Result<()> {
386 let ret = unsafe { xEventGroupClearBitsFromISR(self.0, bits) };
387 if ret != pdFAIL {
388 Ok(())
389 } else {
390 Err(Error::QueueFull)
391 }
392 }
393
394 /// Waits for specified event bits to be set.
395 ///
396 /// Blocks the calling thread until any of the specified bits are set,
397 /// or until the timeout expires. The bits are not cleared automatically.
398 ///
399 /// # Arguments
400 ///
401 /// * `mask` - The event bits to wait for (bitwise OR for multiple bits)
402 /// * `timeout_ticks` - Maximum time to wait in system ticks (0 = no wait, MAX = wait forever)
403 ///
404 /// # Returns
405 ///
406 /// The event bits value when the function returns. Check if the desired
407 /// bits are set to determine if the wait succeeded or timed out.
408 ///
409 /// # Examples
410 ///
411 /// ```ignore
412 /// use osal_rs::os::{EventGroup, EventGroupFn};
413 ///
414 /// let events = EventGroup::new().unwrap();
415 ///
416 /// // Wait for bit 0 or bit 1, timeout after 1000 ticks
417 /// let result = events.wait(0b0011, 1000);
418 /// if result & 0b0011 != 0 {
419 /// println!("At least one bit was set");
420 /// }
421 /// ```
422 fn wait(&self, mask: EventBits, timeout_ticks: TickType) -> EventBits {
423 unsafe {
424 crate::freertos::ffi::xEventGroupWaitBits(
425 self.0,
426 mask,
427 pdFALSE,
428 pdFALSE,
429 timeout_ticks,
430 )
431 }
432 }
433
434 /// Deletes the event group and frees its resources.
435 ///
436 /// This function destroys the event group and releases any memory
437 /// allocated for it. After calling this, the event group should not
438 /// be used. The handle is set to null after deletion.
439 ///
440 /// # Safety
441 ///
442 /// Ensure no threads are waiting on this event group before deleting it.
443 ///
444 /// # Examples
445 ///
446 /// ```ignore
447 /// use osal_rs::os::{EventGroup, EventGroupFn};
448 ///
449 /// let mut events = EventGroup::new().unwrap();
450 /// // Use the event group...
451 /// events.delete();
452 /// ```
453 fn delete(&mut self) {
454 unsafe {
455 vEventGroupDelete(self.0);
456 self.0 = null_mut();
457 }
458 }
459}
460
461/// Automatically deletes the event group when it goes out of scope.
462///
463/// This ensures proper cleanup of FreeRTOS resources.
464impl Drop for EventGroup {
465 fn drop(&mut self) {
466 if self.0.is_null() {
467 return;
468 }
469 self.delete();
470 }
471}
472
473/// Allows dereferencing to the underlying FreeRTOS event group handle.
474impl Deref for EventGroup {
475 type Target = EventGroupHandle;
476
477 fn deref(&self) -> &Self::Target {
478 &self.0
479 }
480}
481
482/// Formats the event group for debugging purposes.
483impl Debug for EventGroup {
484 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
485 write!(f, "EventGroup {{ handle: {:?} }}", self.0)
486 }
487}
488
489/// Formats the event group for display purposes.
490impl Display for EventGroup {
491 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
492 write!(f, "EventGroup {{ handle: {:?} }}", self.0)
493 }
494}