|  | /* | 
|  | * Copyright (c) 2016 Intel Corporation | 
|  | * Copyright (c) 2016 Wind River Systems, Inc. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @file | 
|  | * | 
|  | * Workqueue support functions | 
|  | */ | 
|  |  | 
|  | #include <kernel_structs.h> | 
|  | #include <wait_q.h> | 
|  | #include <spinlock.h> | 
|  | #include <errno.h> | 
|  | #include <stdbool.h> | 
|  |  | 
|  | #define WORKQUEUE_THREAD_NAME	"workqueue" | 
|  |  | 
|  | extern void z_work_q_main(void *work_q_ptr, void *p2, void *p3); | 
|  |  | 
|  | void k_work_q_start(struct k_work_q *work_q, k_thread_stack_t *stack, | 
|  | size_t stack_size, int prio) | 
|  | { | 
|  | k_queue_init(&work_q->queue); | 
|  | (void)k_thread_create(&work_q->thread, stack, stack_size, z_work_q_main, | 
|  | work_q, NULL, NULL, prio, 0, 0); | 
|  |  | 
|  | k_thread_name_set(&work_q->thread, WORKQUEUE_THREAD_NAME); | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_SYS_CLOCK_EXISTS | 
|  | static void work_timeout(struct _timeout *t) | 
|  | { | 
|  | struct k_delayed_work *w = CONTAINER_OF(t, struct k_delayed_work, | 
|  | timeout); | 
|  |  | 
|  | /* submit work to workqueue */ | 
|  | k_work_submit_to_queue(w->work_q, &w->work); | 
|  | } | 
|  |  | 
|  | void k_delayed_work_init(struct k_delayed_work *work, k_work_handler_t handler) | 
|  | { | 
|  | k_work_init(&work->work, handler); | 
|  | _init_timeout(&work->timeout, work_timeout); | 
|  | work->work_q = NULL; | 
|  | } | 
|  |  | 
|  | static int work_cancel(struct k_delayed_work *work) | 
|  | { | 
|  | __ASSERT(work->work_q != NULL, ""); | 
|  |  | 
|  | if (k_work_pending(&work->work)) { | 
|  | /* Remove from the queue if already submitted */ | 
|  | if (!k_queue_remove(&work->work_q->queue, &work->work)) { | 
|  | return -EINVAL; | 
|  | } | 
|  | } else { | 
|  | (void)_abort_timeout(&work->timeout); | 
|  | } | 
|  |  | 
|  | /* Detach from workqueue */ | 
|  | work->work_q = NULL; | 
|  |  | 
|  | atomic_clear_bit(work->work.flags, K_WORK_STATE_PENDING); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int k_delayed_work_submit_to_queue(struct k_work_q *work_q, | 
|  | struct k_delayed_work *work, | 
|  | s32_t delay) | 
|  | { | 
|  | k_spinlock_key_t key = k_spin_lock(&work_q->lock); | 
|  | int err; | 
|  |  | 
|  | /* Work cannot be active in multiple queues */ | 
|  | if (work->work_q && work->work_q != work_q) { | 
|  | err = -EADDRINUSE; | 
|  | goto done; | 
|  | } | 
|  |  | 
|  | /* Cancel if work has been submitted */ | 
|  | if (work->work_q == work_q) { | 
|  | err = work_cancel(work); | 
|  | if (err < 0) { | 
|  | goto done; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Attach workqueue so the timeout callback can submit it */ | 
|  | work->work_q = work_q; | 
|  |  | 
|  | if (!delay) { | 
|  | /* Submit work if no ticks is 0 */ | 
|  | k_work_submit_to_queue(work_q, &work->work); | 
|  | } else { | 
|  | /* Add timeout */ | 
|  | _add_timeout(&work->timeout, work_timeout, | 
|  | _TICK_ALIGN + _ms_to_ticks(delay)); | 
|  | } | 
|  |  | 
|  | err = 0; | 
|  |  | 
|  | done: | 
|  | k_spin_unlock(&work_q->lock, key); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | int k_delayed_work_cancel(struct k_delayed_work *work) | 
|  | { | 
|  | if (!work->work_q) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | struct k_spinlock *lock = &work->work_q->lock; | 
|  | k_spinlock_key_t key = k_spin_lock(lock); | 
|  | int ret = work_cancel(work); | 
|  |  | 
|  | k_spin_unlock(lock, key); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_SYS_CLOCK_EXISTS */ |