/* * Copyright (c) 2021 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ /** * @file event objects library * * Event objects are used to signal one or more threads that a custom set of * events has occurred. Threads wait on event objects until another thread or * ISR posts the desired set of events to the event object. Each time events * are posted to an event object, all threads waiting on that event object are * processed to determine if there is a match. All threads that whose wait * conditions match the current set of events now belonging to the event object * are awakened. * * Threads waiting on an event object have the option of either waking once * any or all of the events it desires have been posted to the event object. * * @brief Kernel event object */ #include #include #include #include #include #include #include #include #include #include #define K_EVENT_WAIT_ANY 0x00 /* Wait for any events */ #define K_EVENT_WAIT_ALL 0x01 /* Wait for all events */ #define K_EVENT_WAIT_MASK 0x01 #define K_EVENT_WAIT_RESET 0x02 /* Reset events prior to waiting */ void z_impl_k_event_init(struct k_event *event) { event->events = 0; event->lock = (struct k_spinlock) {}; SYS_PORT_TRACING_OBJ_INIT(k_event, event); z_waitq_init(&event->wait_q); z_object_init(event); } #ifdef CONFIG_USERSPACE void z_vrfy_k_event_init(struct k_event *event) { Z_OOPS(Z_SYSCALL_OBJ_NEVER_INIT(event, K_OBJ_EVENT)); z_impl_k_event_init(event); } #include #endif /** * @brief determine if desired set of events been satisfied * * This routine determines if the current set of events satisfies the desired * set of events. If @a wait_condition is K_EVENT_WAIT_ALL, then at least * all the desired events must be present to satisfy the request. If @a * wait_condition is not K_EVENT_WAIT_ALL, it is assumed to be K_EVENT_WAIT_ANY. * In the K_EVENT_WAIT_ANY case, the request is satisfied when any of the * current set of events are present in the desired set of events. */ static bool are_wait_conditions_met(uint32_t desired, uint32_t current, unsigned int wait_condition) { uint32_t match = current & desired; if (wait_condition == K_EVENT_WAIT_ALL) { return match == desired; } /* wait_condition assumed to be K_EVENT_WAIT_ANY */ return match != 0; } static void k_event_post_internal(struct k_event *event, uint32_t events, bool accumulate) { k_spinlock_key_t key; struct k_thread *thread; unsigned int wait_condition; struct k_thread *head = NULL; key = k_spin_lock(&event->lock); SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_event, post, event, events, accumulate); if (accumulate) { events |= event->events; } event->events = events; /* * Posting an event has the potential to wake multiple pended threads. * It is desirable to unpend all affected threads simultaneously. To * do so, this must be done in three steps as it is unsafe to unpend * threads from within the _WAIT_Q_FOR_EACH() loop. * * 1. Create a linked list of threads to unpend. * 2. Unpend each of the threads in the linked list * 3. Ready each of the threads in the linked list */ _WAIT_Q_FOR_EACH(&event->wait_q, thread) { wait_condition = thread->event_options & K_EVENT_WAIT_MASK; if (are_wait_conditions_met(thread->events, events, wait_condition)) { /* * The wait conditions have been satisfied. Add this * thread to the list of threads to unpend. */ thread->next_event_link = head; head = thread; } } if (head != NULL) { thread = head; do { z_unpend_thread(thread); arch_thread_return_value_set(thread, 0); thread->events = events; z_ready_thread(thread); thread = thread->next_event_link; } while (thread != NULL); } z_reschedule(&event->lock, key); SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_event, post, event, events, accumulate); } void z_impl_k_event_post(struct k_event *event, uint32_t events) { k_event_post_internal(event, events, true); } #ifdef CONFIG_USERSPACE void z_vrfy_k_event_post(struct k_event *event, uint32_t events) { Z_OOPS(Z_SYSCALL_OBJ(event, K_OBJ_EVENT)); z_impl_k_event_post(event, events); } #include #endif void z_impl_k_event_set(struct k_event *event, uint32_t events) { k_event_post_internal(event, events, false); } #ifdef CONFIG_USERSPACE void z_vrfy_k_event_set(struct k_event *event, uint32_t events) { Z_OOPS(Z_SYSCALL_OBJ(event, K_OBJ_EVENT)); z_impl_k_event_set(event, events); } #include #endif static uint32_t k_event_wait_internal(struct k_event *event, uint32_t events, unsigned int options, k_timeout_t timeout) { uint32_t rv = 0; unsigned int wait_condition; struct k_thread *thread; __ASSERT(((arch_is_in_isr() == false) || K_TIMEOUT_EQ(timeout, K_NO_WAIT)), ""); SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_event, wait, event, events, options, timeout); if (events == 0) { SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_event, wait, event, events, 0); return 0; } wait_condition = options & K_EVENT_WAIT_MASK; thread = z_current_get(); k_spinlock_key_t key = k_spin_lock(&event->lock); if (options & K_EVENT_WAIT_RESET) { event->events = 0; } /* Test if the wait conditions have already been met. */ if (are_wait_conditions_met(events, event->events, wait_condition)) { rv = event->events; k_spin_unlock(&event->lock, key); goto out; } /* Match conditions have not been met. */ if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) { k_spin_unlock(&event->lock, key); goto out; } /* * The caller must pend to wait for the match. Save the desired * set of events in the k_thread structure. */ thread->events = events; thread->event_options = options; SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_event, wait, event, events, options, timeout); if (z_pend_curr(&event->lock, key, &event->wait_q, timeout) == 0) { /* Retrieve the set of events that woke the thread */ rv = thread->events; } out: SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_event, wait, event, events, rv & events); return rv & events; } /** * Wait for any of the specified events */ uint32_t z_impl_k_event_wait(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout) { uint32_t options = reset ? K_EVENT_WAIT_RESET : 0; return k_event_wait_internal(event, events, options, timeout); } #ifdef CONFIG_USERSPACE uint32_t z_vrfy_k_event_wait(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout) { Z_OOPS(Z_SYSCALL_OBJ(event, K_OBJ_EVENT)); return z_impl_k_event_wait(event, events, reset, timeout); } #include #endif /** * Wait for all of the specified events */ uint32_t z_impl_k_event_wait_all(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout) { uint32_t options = reset ? (K_EVENT_WAIT_RESET | K_EVENT_WAIT_ALL) : K_EVENT_WAIT_ALL; return k_event_wait_internal(event, events, options, timeout); } #ifdef CONFIG_USERSPACE uint32_t z_vrfy_k_event_wait_all(struct k_event *event, uint32_t events, bool reset, k_timeout_t timeout) { Z_OOPS(Z_SYSCALL_OBJ(event, K_OBJ_EVENT)); return z_impl_k_event_wait_all(event, events, reset, timeout); } #include #endif