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}