Skip to main content

osal_rs/freertos/
event_group.rs

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