|  | /* | 
|  | * 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 <zephyr/kernel.h> | 
|  | #include <zephyr/kernel_structs.h> | 
|  |  | 
|  | #include <zephyr/toolchain.h> | 
|  | #include <zephyr/sys/dlist.h> | 
|  | #include <zephyr/init.h> | 
|  | #include <zephyr/internal/syscall_handler.h> | 
|  | #include <zephyr/tracing/tracing.h> | 
|  | #include <zephyr/sys/check.h> | 
|  | /* private kernel APIs */ | 
|  | #include <wait_q.h> | 
|  | #include <ksched.h> | 
|  |  | 
|  | #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_OPTION_RESET  0x02   /* Reset events prior to waiting */ | 
|  | #define K_EVENT_OPTION_CLEAR  0x04   /* Clear events that are received */ | 
|  |  | 
|  | struct event_walk_data { | 
|  | struct k_thread  *head; | 
|  | uint32_t events; | 
|  | uint32_t clear_events; | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_OBJ_CORE_EVENT | 
|  | static struct k_obj_type obj_type_event; | 
|  | #endif /* CONFIG_OBJ_CORE_EVENT */ | 
|  |  | 
|  | void z_impl_k_event_init(struct k_event *event) | 
|  | { | 
|  | __ASSERT_NO_MSG(!arch_is_in_isr()); | 
|  |  | 
|  | event->events = 0; | 
|  | event->lock = (struct k_spinlock) {}; | 
|  |  | 
|  | SYS_PORT_TRACING_OBJ_INIT(k_event, event); | 
|  |  | 
|  | z_waitq_init(&event->wait_q); | 
|  |  | 
|  | k_object_init(event); | 
|  |  | 
|  | #ifdef CONFIG_OBJ_CORE_EVENT | 
|  | k_obj_core_init_and_link(K_OBJ_CORE(event), &obj_type_event); | 
|  | #endif /* CONFIG_OBJ_CORE_EVENT */ | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | void z_vrfy_k_event_init(struct k_event *event) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ_NEVER_INIT(event, K_OBJ_EVENT)); | 
|  | z_impl_k_event_init(event); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_init_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | /** | 
|  | * @brief determine the set of events that have 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. | 
|  | * | 
|  | * @return event bits that satisfy the wait condition or zero | 
|  | */ | 
|  | static uint32_t 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) && (match != desired)) { | 
|  | /* special case for K_EVENT_WAIT_ALL */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* return the matched events for any wait condition */ | 
|  | return match; | 
|  | } | 
|  |  | 
|  | static int event_walk_op(struct k_thread *thread, void *data) | 
|  | { | 
|  | uint32_t match; | 
|  | unsigned int wait_condition; | 
|  | struct event_walk_data *event_data = data; | 
|  |  | 
|  | wait_condition = thread->event_options & K_EVENT_WAIT_MASK; | 
|  |  | 
|  | match = are_wait_conditions_met(thread->events, event_data->events, | 
|  | wait_condition); | 
|  | if (match != 0) { | 
|  | /* | 
|  | * The wait conditions have been satisfied. So, set the | 
|  | * received events and then add this thread to the list | 
|  | * of threads to unpend. | 
|  | * | 
|  | * NOTE: thread event options can consume an event | 
|  | */ | 
|  | thread->events = match; | 
|  | if (thread->event_options & K_EVENT_OPTION_CLEAR) { | 
|  | event_data->clear_events |= match; | 
|  | } | 
|  | thread->next_event_link = event_data->head; | 
|  | event_data->head = thread; | 
|  |  | 
|  | /* | 
|  | * Events create a list of threads to wake up. We do | 
|  | * not want z_thread_timeout to wake these threads; they | 
|  | * will be woken up by k_event_post_internal once they | 
|  | * have been processed. | 
|  | */ | 
|  | thread->no_wake_on_timeout = true; | 
|  | #ifdef CONFIG_SYS_CLOCK_EXISTS | 
|  | z_abort_timeout(&thread->base.timeout); | 
|  | #endif /* CONFIG_SYS_CLOCK_EXISTS */ | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static uint32_t k_event_post_internal(struct k_event *event, uint32_t events, | 
|  | uint32_t events_mask) | 
|  | { | 
|  | k_spinlock_key_t  key; | 
|  | struct k_thread  *thread; | 
|  | struct event_walk_data data; | 
|  | uint32_t previous_events; | 
|  |  | 
|  | data.head = NULL; | 
|  | key = k_spin_lock(&event->lock); | 
|  |  | 
|  | SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_event, post, event, events, | 
|  | events_mask); | 
|  |  | 
|  | previous_events = event->events & events_mask; | 
|  | events = (event->events & ~events_mask) | | 
|  | (events & events_mask); | 
|  |  | 
|  | /* | 
|  | * Posting an event has the potential to wake multiple pended threads. | 
|  | * It is desirable to unpend all affected threads simultaneously. This | 
|  | * is done in three steps: | 
|  | * | 
|  | * 1. Walk the waitq and 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 | 
|  | */ | 
|  |  | 
|  | data.events = events; | 
|  | data.clear_events = 0; | 
|  | z_sched_waitq_walk(&event->wait_q, event_walk_op, &data); | 
|  |  | 
|  | if (data.head != NULL) { | 
|  | thread = data.head; | 
|  | struct k_thread *next; | 
|  | do { | 
|  | arch_thread_return_value_set(thread, 0); | 
|  | next = thread->next_event_link; | 
|  | z_sched_wake_thread(thread, false); | 
|  | thread = next; | 
|  | } while (thread != NULL); | 
|  | } | 
|  |  | 
|  | /* stash any events not consumed */ | 
|  | event->events = data.events & ~data.clear_events; | 
|  |  | 
|  | z_reschedule(&event->lock, key); | 
|  |  | 
|  | SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_event, post, event, events, | 
|  | events_mask); | 
|  |  | 
|  | return previous_events; | 
|  | } | 
|  |  | 
|  | uint32_t z_impl_k_event_post(struct k_event *event, uint32_t events) | 
|  | { | 
|  | return k_event_post_internal(event, events, events); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | uint32_t z_vrfy_k_event_post(struct k_event *event, uint32_t events) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_post(event, events); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_post_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | uint32_t z_impl_k_event_set(struct k_event *event, uint32_t events) | 
|  | { | 
|  | return k_event_post_internal(event, events, ~0); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | uint32_t z_vrfy_k_event_set(struct k_event *event, uint32_t events) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_set(event, events); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_set_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | uint32_t z_impl_k_event_set_masked(struct k_event *event, uint32_t events, | 
|  | uint32_t events_mask) | 
|  | { | 
|  | return k_event_post_internal(event, events, events_mask); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | uint32_t z_vrfy_k_event_set_masked(struct k_event *event, uint32_t events, | 
|  | uint32_t events_mask) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_set_masked(event, events, events_mask); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_set_masked_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | uint32_t z_impl_k_event_clear(struct k_event *event, uint32_t events) | 
|  | { | 
|  | return k_event_post_internal(event, 0, events); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | uint32_t z_vrfy_k_event_clear(struct k_event *event, uint32_t events) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_clear(event, events); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_clear_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | 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 = k_sched_current_thread_query(); | 
|  |  | 
|  | k_spinlock_key_t  key = k_spin_lock(&event->lock); | 
|  |  | 
|  | if (options & K_EVENT_OPTION_RESET) { | 
|  | event->events = 0; | 
|  | } | 
|  |  | 
|  | /* Test if the wait conditions have already been met. */ | 
|  | rv = are_wait_conditions_met(events, event->events, wait_condition); | 
|  | if (rv != 0) { | 
|  | /* clear the events that are matched */ | 
|  | if (options & K_EVENT_OPTION_CLEAR) { | 
|  | event->events &= ~rv; | 
|  | } | 
|  |  | 
|  | k_spin_unlock(&event->lock, key); | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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_OPTION_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) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_wait(event, events, reset, timeout); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_wait_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | /** | 
|  | * 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_OPTION_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) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_wait_all(event, events, reset, timeout); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_wait_all_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | uint32_t z_impl_k_event_wait_safe(struct k_event *event, uint32_t events, | 
|  | bool reset, k_timeout_t timeout) | 
|  | { | 
|  | uint32_t options = reset ? (K_EVENT_OPTION_CLEAR | K_EVENT_OPTION_RESET) | 
|  | : K_EVENT_OPTION_CLEAR; | 
|  |  | 
|  | return k_event_wait_internal(event, events, options, timeout); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | uint32_t z_vrfy_k_event_wait_safe(struct k_event *event, uint32_t events, | 
|  | bool reset, k_timeout_t timeout) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_wait_safe(event, events, reset, timeout); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_wait_safe_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | uint32_t z_impl_k_event_wait_all_safe(struct k_event *event, uint32_t events, | 
|  | bool reset, k_timeout_t timeout) | 
|  | { | 
|  | uint32_t options = reset ? (K_EVENT_OPTION_CLEAR | | 
|  | K_EVENT_OPTION_RESET | K_EVENT_WAIT_ALL) | 
|  | : (K_EVENT_OPTION_CLEAR | K_EVENT_WAIT_ALL); | 
|  |  | 
|  | return k_event_wait_internal(event, events, options, timeout); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_USERSPACE | 
|  | uint32_t z_vrfy_k_event_wait_all_safe(struct k_event *event, uint32_t events, | 
|  | bool reset, k_timeout_t timeout) | 
|  | { | 
|  | K_OOPS(K_SYSCALL_OBJ(event, K_OBJ_EVENT)); | 
|  | return z_impl_k_event_wait_all_safe(event, events, reset, timeout); | 
|  | } | 
|  | #include <zephyr/syscalls/k_event_wait_all_safe_mrsh.c> | 
|  | #endif /* CONFIG_USERSPACE */ | 
|  |  | 
|  | #ifdef CONFIG_OBJ_CORE_EVENT | 
|  | static int init_event_obj_core_list(void) | 
|  | { | 
|  | /* Initialize condvar object type */ | 
|  |  | 
|  | z_obj_type_init(&obj_type_event, K_OBJ_TYPE_EVENT_ID, | 
|  | offsetof(struct k_event, obj_core)); | 
|  |  | 
|  | /* Initialize and link statically defined condvars */ | 
|  |  | 
|  | STRUCT_SECTION_FOREACH(k_event, event) { | 
|  | k_obj_core_init_and_link(K_OBJ_CORE(event), &obj_type_event); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | SYS_INIT(init_event_obj_core_list, PRE_KERNEL_1, | 
|  | CONFIG_KERNEL_INIT_PRIORITY_OBJECTS); | 
|  | #endif /* CONFIG_OBJ_CORE_EVENT */ |