osal_rs/freertos/event_group.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/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};
36use crate::xEventGroupGetBits;
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 pub fn wait_with_to_tick(&self, mask: EventBits, timeout_ticks: impl ToTick) -> EventBits {
171 self.wait(mask, timeout_ticks.to_ticks())
172 }
173}
174
175impl EventGroupFn for EventGroup {
176 fn new() -> Result<Self> {
177 let handle = unsafe { xEventGroupCreate() };
178 if handle.is_null() {
179 Err(Error::OutOfMemory)
180 } else {
181 Ok(Self (handle))
182 }
183 }
184
185 fn set(&self, bits: EventBits) -> EventBits {
186 unsafe { xEventGroupSetBits(self.0, bits) }
187 }
188
189 fn set_from_isr(&self, bits: EventBits) -> Result<()> {
190
191 let mut higher_priority_task_woken: BaseType = pdFALSE;
192
193 let ret = unsafe { xEventGroupSetBitsFromISR(self.0, bits, &mut higher_priority_task_woken) };
194 if ret != pdFAIL {
195
196 System::yield_from_isr(higher_priority_task_woken);
197
198 Ok(())
199 } else {
200 Err(Error::QueueFull)
201 }
202 }
203
204 fn get(&self) -> EventBits {
205 xEventGroupGetBits!(self.0)
206 }
207
208 fn get_from_isr(&self) -> EventBits{
209 unsafe { xEventGroupGetBitsFromISR(self.0) }
210 }
211
212
213 fn clear(&self, bits: EventBits) -> EventBits {
214 unsafe { xEventGroupClearBits(self.0, bits) }
215 }
216
217 fn clear_from_isr(&self, bits: EventBits) -> Result<()> {
218 let ret = unsafe { xEventGroupClearBitsFromISR(self.0, bits) };
219 if ret != pdFAIL {
220 Ok(())
221 } else {
222 Err(Error::QueueFull)
223 }
224 }
225
226 fn wait(&self, mask: EventBits, timeout_ticks: TickType) -> EventBits {
227 unsafe {
228 crate::freertos::ffi::xEventGroupWaitBits(
229 self.0,
230 mask,
231 pdFALSE,
232 pdFALSE,
233 timeout_ticks,
234 )
235 }
236 }
237
238 fn delete(&mut self) {
239 unsafe {
240 vEventGroupDelete(self.0);
241 self.0 = null_mut();
242 }
243 }
244}
245
246impl Drop for EventGroup {
247 fn drop(&mut self) {
248 if self.0.is_null() {
249 return;
250 }
251 self.delete();
252 }
253}
254
255impl Deref for EventGroup {
256 type Target = EventGroupHandle;
257
258 fn deref(&self) -> &Self::Target {
259 &self.0
260 }
261}
262
263impl Debug for EventGroup {
264 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
265 write!(f, "EventGroup {{ handle: {:?} }}", self.0)
266 }
267}
268
269impl Display for EventGroup {
270 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
271 write!(f, "EventGroup {{ handle: {:?} }}", self.0)
272 }
273}