blob: 7c7197fafbd5060f7f106f7c16e9cc30f50caec4 [file] [log] [blame]
/***********************************************************************************************//**
* \file cyabs_rtos_zephyr.c
*
* \brief
* Implementation for Zephyr RTOS abstraction
*
***************************************************************************************************
* \copyright
* Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
* an affiliate of Cypress Semiconductor Corporation
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**************************************************************************************************/
#include <stdlib.h>
#include <string.h>
#include <cy_utils.h>
#include <cyabs_rtos.h>
#if defined(__cplusplus)
extern "C" {
#endif
/*
* Error Converter
*/
/* Last received error status */
static cy_rtos_error_t dbgErr;
cy_rtos_error_t cy_rtos_last_error(void)
{
return dbgErr;
}
/* Converts internal error type to external error type */
static cy_rslt_t error_converter(cy_rtos_error_t internalError)
{
cy_rslt_t value;
switch (internalError) {
case 0:
value = CY_RSLT_SUCCESS;
break;
case -EAGAIN:
value = CY_RTOS_TIMEOUT;
break;
case -EINVAL:
value = CY_RTOS_BAD_PARAM;
break;
case -ENOMEM:
value = CY_RTOS_NO_MEMORY;
break;
default:
value = CY_RTOS_GENERAL_ERROR;
break;
}
/* Update the last known error status */
dbgErr = internalError;
return value;
}
static void free_thead_obj(cy_thread_t *thread)
{
/* Free allocated stack buffer */
if ((*thread)->memptr != NULL) {
k_free((*thread)->memptr);
}
/* Free object */
k_free(*thread);
}
/*
* Threads
*/
static void thread_entry_function_wrapper(void *p1, void *p2, void *p3)
{
CY_UNUSED_PARAMETER(p3);
((cy_thread_entry_fn_t)p2)(p1);
}
cy_rslt_t cy_rtos_create_thread(cy_thread_t *thread, cy_thread_entry_fn_t entry_function,
const char *name, void *stack, uint32_t stack_size,
cy_thread_priority_t priority, cy_thread_arg_t arg)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
cy_rtos_error_t status_internal = 0;
if ((thread == NULL) || (stack_size < CY_RTOS_MIN_STACK_SIZE)) {
status = CY_RTOS_BAD_PARAM;
} else if ((stack != NULL) && (0 != (((uint32_t)stack) & CY_RTOS_ALIGNMENT_MASK))) {
status = CY_RTOS_ALIGNMENT_ERROR;
} else {
void *stack_alloc = NULL;
k_tid_t my_tid = NULL;
/* Allocate cy_thread_t object */
*thread = k_malloc(sizeof(k_thread_wrapper_t));
/* Allocate stack if NULL was passed */
if ((uint32_t *)stack == NULL) {
stack_alloc = k_aligned_alloc(Z_KERNEL_STACK_OBJ_ALIGN,
Z_KERNEL_STACK_SIZE_ADJUST(stack_size));
/* Store pointer to allocated stack,
* NULL if not allocated by cy_rtos_thread_create (passed by application).
*/
(*thread)->memptr = stack_alloc;
} else {
stack_alloc = stack;
(*thread)->memptr = NULL;
}
if (stack_alloc == NULL) {
status = CY_RTOS_NO_MEMORY;
} else {
/* Create thread */
my_tid = k_thread_create(&(*thread)->z_thread, stack_alloc, stack_size,
thread_entry_function_wrapper,
(cy_thread_arg_t)arg, (void *)entry_function, NULL,
priority, 0, K_NO_WAIT);
/* Set thread name */
status_internal = k_thread_name_set(my_tid, name);
/* NOTE: k_thread_name_set returns -ENOSYS if thread name
* configuration option not enabled.
*/
}
if ((my_tid == NULL) || ((status_internal != 0) && (status_internal != -ENOSYS))) {
status = CY_RTOS_GENERAL_ERROR;
/* Free allocated thread object and stack buffer */
free_thead_obj(thread);
}
}
return status;
}
cy_rslt_t cy_rtos_exit_thread(void)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (k_is_in_isr()) {
status = CY_RTOS_GENERAL_ERROR;
} else {
cy_thread_t thread = (cy_thread_t)k_current_get();
/* Abort thread */
k_thread_abort((k_tid_t)&(thread)->z_thread);
CODE_UNREACHABLE;
}
return status;
}
cy_rslt_t cy_rtos_terminate_thread(cy_thread_t *thread)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (thread == NULL) {
status = CY_RTOS_BAD_PARAM;
} else if (k_is_in_isr()) {
status = CY_RTOS_GENERAL_ERROR;
} else {
/* Abort thread */
k_thread_abort((k_tid_t)&(*thread)->z_thread);
/* Free allocated stack buffer */
if ((*thread)->memptr != NULL) {
k_free((*thread)->memptr);
}
}
return status;
}
cy_rslt_t cy_rtos_is_thread_running(cy_thread_t *thread, bool *running)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((thread == NULL) || (running == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
*running = (k_current_get() == &(*thread)->z_thread) ? true : false;
}
return status;
}
cy_rslt_t cy_rtos_get_thread_state(cy_thread_t *thread, cy_thread_state_t *state)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((thread == NULL) || (state == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
if (k_is_in_isr()) {
*state = CY_THREAD_STATE_UNKNOWN;
} else if (k_current_get() == &(*thread)->z_thread) {
*state = CY_THREAD_STATE_RUNNING;
}
if (((*thread)->z_thread.base.thread_state & _THREAD_DEAD) != 0) {
*state = CY_THREAD_STATE_TERMINATED;
} else {
switch ((*thread)->z_thread.base.thread_state) {
case _THREAD_DUMMY:
*state = CY_THREAD_STATE_UNKNOWN;
break;
case _THREAD_PRESTART:
*state = CY_THREAD_STATE_INACTIVE;
break;
case _THREAD_SUSPENDED:
case _THREAD_PENDING:
*state = CY_THREAD_STATE_BLOCKED;
break;
case _THREAD_QUEUED:
*state = CY_THREAD_STATE_READY;
break;
default:
*state = CY_THREAD_STATE_UNKNOWN;
break;
}
}
}
return status;
}
cy_rslt_t cy_rtos_join_thread(cy_thread_t *thread)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
cy_rtos_error_t status_internal;
if (thread == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
if (*thread != NULL) {
/* Sleep until a thread exits */
status_internal = k_thread_join(&(*thread)->z_thread, K_FOREVER);
status = error_converter(status_internal);
if (status == CY_RSLT_SUCCESS) {
/* Free allocated thread object and stack buffer */
free_thead_obj(thread);
}
}
}
return status;
}
cy_rslt_t cy_rtos_get_thread_handle(cy_thread_t *thread)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (thread == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
*thread = (cy_thread_t)k_current_get();
}
return status;
}
cy_rslt_t cy_rtos_wait_thread_notification(cy_time_t timeout_ms)
{
cy_rtos_error_t status_internal;
cy_rslt_t status = CY_RSLT_SUCCESS;
if (timeout_ms == CY_RTOS_NEVER_TIMEOUT) {
status_internal = k_sleep(K_FOREVER);
} else {
status_internal = k_msleep((int32_t)timeout_ms);
if (status_internal == 0) {
status = CY_RTOS_TIMEOUT;
}
}
if ((status_internal < 0) && (status_internal != K_TICKS_FOREVER)) {
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_thread_set_notification(cy_thread_t *thread, bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rslt_t status = CY_RSLT_SUCCESS;
if (thread == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
k_wakeup(&(*thread)->z_thread);
}
return status;
}
/*
* Mutexes
*/
cy_rslt_t cy_rtos_init_mutex2(cy_mutex_t *mutex, bool recursive)
{
cy_rtos_error_t status_internal;
cy_rslt_t status;
/* Non recursive mutex is not supported by Zephyr */
CY_UNUSED_PARAMETER(recursive);
if (mutex == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Initialize a mutex object */
status_internal = k_mutex_init(mutex);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_get_mutex(cy_mutex_t *mutex, cy_time_t timeout_ms)
{
cy_rtos_error_t status_internal;
cy_rslt_t status;
if (mutex == NULL) {
status = CY_RTOS_BAD_PARAM;
} else if (k_is_in_isr()) {
/* Mutexes may not be locked in ISRs */
status = CY_RTOS_GENERAL_ERROR;
} else {
/* Convert timeout value */
k_timeout_t k_timeout =
(timeout_ms == CY_RTOS_NEVER_TIMEOUT) ? K_FOREVER : K_MSEC(timeout_ms);
/* Lock a mutex */
status_internal = k_mutex_lock(mutex, k_timeout);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_set_mutex(cy_mutex_t *mutex)
{
cy_rtos_error_t status_internal;
cy_rslt_t status;
if (mutex == NULL) {
status = CY_RTOS_BAD_PARAM;
} else if (k_is_in_isr()) {
/* Mutexes may not be unlocked in ISRs */
status = CY_RTOS_GENERAL_ERROR;
} else {
/* Unlock a mutex */
status_internal = k_mutex_unlock(mutex);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_deinit_mutex(cy_mutex_t *mutex)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (mutex == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Force unlock mutex, do not care about return */
(void)k_mutex_unlock(mutex);
}
return status;
}
/*
* Semaphores
*/
cy_rslt_t cy_rtos_init_semaphore(cy_semaphore_t *semaphore, uint32_t maxcount, uint32_t initcount)
{
cy_rtos_error_t status_internal;
cy_rslt_t status;
if (semaphore == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Initialize a semaphore object */
status_internal = k_sem_init(semaphore, initcount, maxcount);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_get_semaphore(cy_semaphore_t *semaphore, cy_time_t timeout_ms, bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rtos_error_t status_internal;
cy_rslt_t status;
if (semaphore == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Convert timeout value */
k_timeout_t k_timeout;
if (k_is_in_isr()) {
/* NOTE: Based on Zephyr documentation when k_sem_take
* is called from ISR timeout must be set to K_NO_WAIT.
*/
k_timeout = K_NO_WAIT;
} else if (timeout_ms == CY_RTOS_NEVER_TIMEOUT) {
k_timeout = K_FOREVER;
} else {
k_timeout = K_MSEC(timeout_ms);
}
/* Take a semaphore */
status_internal = k_sem_take(semaphore, k_timeout);
status = error_converter(status_internal);
if (k_is_in_isr() && (status_internal == -EBUSY)) {
status = CY_RSLT_SUCCESS;
}
}
return status;
}
cy_rslt_t cy_rtos_set_semaphore(cy_semaphore_t *semaphore, bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rslt_t status = CY_RSLT_SUCCESS;
if (semaphore == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Give a semaphore */
k_sem_give(semaphore);
}
return status;
}
cy_rslt_t cy_rtos_get_count_semaphore(cy_semaphore_t *semaphore, size_t *count)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((semaphore == NULL) || (count == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
*count = k_sem_count_get(semaphore);
}
return status;
}
cy_rslt_t cy_rtos_deinit_semaphore(cy_semaphore_t *semaphore)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (semaphore == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
k_sem_reset(semaphore);
}
return status;
}
/*
* Events
*/
cy_rslt_t cy_rtos_init_event(cy_event_t *event)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (event == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Initialize an event object */
k_event_init(event);
}
return status;
}
cy_rslt_t cy_rtos_setbits_event(cy_event_t *event, uint32_t bits, bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rslt_t status = CY_RSLT_SUCCESS;
if (event == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Post the new bits */
k_event_post(event, bits);
}
return status;
}
cy_rslt_t cy_rtos_clearbits_event(cy_event_t *event, uint32_t bits, bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rslt_t status = CY_RSLT_SUCCESS;
if (event == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
uint32_t current_bits;
/* Reads events value */
status = cy_rtos_getbits_event(event, &current_bits);
/* Set masked value */
k_event_set(event, (~bits & current_bits));
}
return status;
}
cy_rslt_t cy_rtos_getbits_event(cy_event_t *event, uint32_t *bits)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((event == NULL) || (bits == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
/* NOTE: Zephyr does not provide function for get bits,
* retrieve it from event object.
*/
*bits = event->events;
}
return status;
}
cy_rslt_t cy_rtos_waitbits_event(cy_event_t *event, uint32_t *bits, bool clear, bool all,
cy_time_t timeout)
{
cy_rslt_t status;
if ((event == NULL) || (bits == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
uint32_t wait_for = *bits;
k_timeout_t k_timeout =
(timeout == CY_RTOS_NEVER_TIMEOUT) ? K_FOREVER : K_MSEC(timeout);
if (all) {
/* Wait for all of the specified events */
*bits = k_event_wait_all(event, wait_for, false, k_timeout);
} else {
/* Wait for any of the specified events */
*bits = k_event_wait(event, wait_for, false, k_timeout);
}
/* Check timeout */
status = (*bits == 0) ? CY_RTOS_TIMEOUT : CY_RSLT_SUCCESS;
/* Return full current events */
cy_rtos_getbits_event(event, bits);
/* Crear bits if required */
if ((status == CY_RSLT_SUCCESS) && (clear == true)) {
cy_rtos_clearbits_event(event, wait_for, false);
}
}
return status;
}
cy_rslt_t cy_rtos_deinit_event(cy_event_t *event)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (event != NULL) {
/* Clear event */
k_event_set(event, 0);
} else {
status = CY_RTOS_BAD_PARAM;
}
return status;
}
/*
* Queues
*/
cy_rslt_t cy_rtos_init_queue(cy_queue_t *queue, size_t length, size_t itemsize)
{
cy_rtos_error_t status_internal;
cy_rslt_t status;
if (queue == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Initialize a message queue */
status_internal = k_msgq_alloc_init(queue, itemsize, length);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_put_queue(cy_queue_t *queue, const void *item_ptr, cy_time_t timeout_ms,
bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rtos_error_t status_internal;
cy_rslt_t status;
if ((queue == NULL) || (item_ptr == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Convert timeout value */
k_timeout_t k_timeout;
if (k_is_in_isr()) {
k_timeout = K_NO_WAIT;
} else if (timeout_ms == CY_RTOS_NEVER_TIMEOUT) {
k_timeout = K_FOREVER;
} else {
k_timeout = K_MSEC(timeout_ms);
}
/* Send a message to a message queue */
status_internal = k_msgq_put(queue, item_ptr, k_timeout);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_get_queue(cy_queue_t *queue, void *item_ptr, cy_time_t timeout_ms, bool in_isr)
{
CY_UNUSED_PARAMETER(in_isr);
cy_rtos_error_t status_internal;
cy_rslt_t status;
if ((queue == NULL) || (item_ptr == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Convert timeout value */
k_timeout_t k_timeout;
if (k_is_in_isr()) {
k_timeout = K_NO_WAIT;
} else if (timeout_ms == CY_RTOS_NEVER_TIMEOUT) {
k_timeout = K_FOREVER;
} else {
k_timeout = K_MSEC(timeout_ms);
}
/* Receive a message from a message queue */
status_internal = k_msgq_get(queue, item_ptr, k_timeout);
status = error_converter(status_internal);
}
return status;
}
cy_rslt_t cy_rtos_count_queue(cy_queue_t *queue, size_t *num_waiting)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((queue == NULL) || (num_waiting == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Get the number of messages in a message queue */
*num_waiting = k_msgq_num_used_get(queue);
}
return status;
}
cy_rslt_t cy_rtos_space_queue(cy_queue_t *queue, size_t *num_spaces)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((queue == NULL) || (num_spaces == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Get the amount of free space in a message queue */
*num_spaces = k_msgq_num_free_get(queue);
}
return status;
}
cy_rslt_t cy_rtos_reset_queue(cy_queue_t *queue)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (queue == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Reset a message queue */
k_msgq_purge(queue);
}
return status;
}
cy_rslt_t cy_rtos_deinit_queue(cy_queue_t *queue)
{
cy_rtos_error_t status_internal;
cy_rslt_t status;
if (queue == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Reset a message queue */
status = cy_rtos_reset_queue(queue);
if (status == CY_RSLT_SUCCESS) {
/* Release allocated buffer for a queue */
status_internal = k_msgq_cleanup(queue);
status = error_converter(status_internal);
}
}
return status;
}
/*
* Timers
*/
static void zephyr_timer_event_handler(struct k_timer *timer)
{
cy_timer_t *_timer = (cy_timer_t *)timer;
((cy_timer_callback_t)_timer->callback_function)(
(cy_timer_callback_arg_t)_timer->arg);
}
cy_rslt_t cy_rtos_init_timer(cy_timer_t *timer, cy_timer_trigger_type_t type,
cy_timer_callback_t fun, cy_timer_callback_arg_t arg)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((timer == NULL) || (fun == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
timer->callback_function = (void *)fun;
timer->trigger_type = (uint32_t)type;
timer->arg = (void *)arg;
k_timer_init(&timer->z_timer, zephyr_timer_event_handler, NULL);
}
return status;
}
cy_rslt_t cy_rtos_start_timer(cy_timer_t *timer, cy_time_t num_ms)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (timer == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
if ((cy_timer_trigger_type_t)timer->trigger_type == CY_TIMER_TYPE_ONCE) {
/* Called once only */
k_timer_start(&timer->z_timer, K_MSEC(num_ms), K_NO_WAIT);
} else {
/* Called periodically until stopped */
k_timer_start(&timer->z_timer, K_MSEC(num_ms), K_MSEC(num_ms));
}
}
return status;
}
cy_rslt_t cy_rtos_stop_timer(cy_timer_t *timer)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (timer == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Stop timer */
k_timer_stop(&timer->z_timer);
}
return status;
}
cy_rslt_t cy_rtos_is_running_timer(cy_timer_t *timer, bool *state)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if ((timer == NULL) || (state == NULL)) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Check if running */
*state = (k_timer_remaining_get(&timer->z_timer) != 0u);
}
return status;
}
cy_rslt_t cy_rtos_deinit_timer(cy_timer_t *timer)
{
cy_rslt_t status;
if (timer == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
bool running;
/* Get current timer state */
status = cy_rtos_is_running_timer(timer, &running);
/* Check if running */
if ((status == CY_RSLT_SUCCESS) && (running)) {
/* Stop timer */
status = cy_rtos_stop_timer(timer);
}
}
return status;
}
/*
* Time
*/
cy_rslt_t cy_rtos_get_time(cy_time_t *tval)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (tval == NULL) {
status = CY_RTOS_BAD_PARAM;
} else {
/* Get system uptime (32-bit version) */
*tval = k_uptime_get_32();
}
return status;
}
cy_rslt_t cy_rtos_delay_milliseconds(cy_time_t num_ms)
{
cy_rslt_t status = CY_RSLT_SUCCESS;
if (k_is_in_isr()) {
status = CY_RTOS_GENERAL_ERROR;
} else {
k_msleep(num_ms);
}
return status;
}
#if defined(__cplusplus)
}
#endif