blob: e338b80d2012854d7babe7e1e9656639ec6369cd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <assert.h>
#include "syscfg/syscfg.h"
#if !MYNEWT_VAL(OS_SYSVIEW_TRACE_SEM)
#define OS_TRACE_DISABLE_FILE_API
#endif
#include "os/mynewt.h"
/* XXX:
* 1) Should I check to see if we are within an ISR for some of these?
* 2) Would I do anything different for os_sem_release() if we were in an
* ISR when this was called?
*/
os_error_t
os_sem_init(struct os_sem *sem, uint16_t tokens)
{
os_error_t ret;
os_trace_api_u32x2(OS_TRACE_ID_SEM_INIT, (uint32_t)sem, (uint32_t)tokens);
if (!sem) {
ret = OS_INVALID_PARM;
goto done;
}
sem->sem_tokens = tokens;
SLIST_FIRST(&sem->sem_head) = NULL;
ret = OS_OK;
done:
os_trace_api_ret_u32(OS_TRACE_ID_SEM_INIT, (uint32_t)ret);
return ret;
}
os_error_t
os_sem_release(struct os_sem *sem)
{
int resched;
os_sr_t sr;
struct os_task *current;
struct os_task *rdy;
os_error_t ret;
os_trace_api_u32(OS_TRACE_ID_SEM_RELEASE, (uint32_t)sem);
/* OS must be started to release semaphores */
if (!g_os_started) {
ret = OS_NOT_STARTED;
goto done;
}
/* Check for valid semaphore */
if (!sem) {
ret = OS_INVALID_PARM;
goto done;
}
/* Get current task */
resched = 0;
current = os_sched_get_current_task();
OS_ENTER_CRITICAL(sr);
/* Check if tasks are waiting for the semaphore */
rdy = SLIST_FIRST(&sem->sem_head);
if (rdy) {
/* Clear flag that we are waiting on the semaphore; wake up task */
rdy->t_flags &= ~OS_TASK_FLAG_SEM_WAIT;
os_sched_wakeup(rdy);
/* Schedule if waiting task higher priority */
if (current->t_prio > rdy->t_prio) {
resched = 1;
}
} else {
/* Add to the number of tokens */
sem->sem_tokens++;
}
OS_EXIT_CRITICAL(sr);
/* Re-schedule if needed */
if (resched) {
os_sched(rdy);
}
ret = OS_OK;
done:
os_trace_api_ret_u32(OS_TRACE_ID_SEM_RELEASE, (uint32_t)ret);
return ret;
}
os_error_t
os_sem_pend(struct os_sem *sem, os_time_t timeout)
{
os_sr_t sr;
int sched;
struct os_task *current;
struct os_task *entry;
struct os_task *last;
os_error_t ret;
os_trace_api_u32x2(OS_TRACE_ID_SEM_PEND, (uint32_t)sem, (uint32_t)timeout);
/* Check if OS is started */
if (!g_os_started) {
ret = OS_NOT_STARTED;
goto done;
}
/* Check for valid semaphore */
if (!sem) {
ret = OS_INVALID_PARM;
goto done;
}
/* Assume we dont have to put task to sleep; get current task */
sched = 0;
current = os_sched_get_current_task();
OS_ENTER_CRITICAL(sr);
/*
* If there is a token available, take it. If no token, either return
* with error if timeout was 0 or put this task to sleep.
*/
if (sem->sem_tokens != 0) {
sem->sem_tokens--;
ret = OS_OK;
} else if (timeout == 0) {
ret = OS_TIMEOUT;
} else {
/* Silence gcc maybe-uninitialized warning. */
ret = OS_OK;
/* Link current task to tasks waiting for semaphore */
current->t_obj = sem;
current->t_flags |= OS_TASK_FLAG_SEM_WAIT;
last = NULL;
if (!SLIST_EMPTY(&sem->sem_head)) {
/* Insert in priority order */
SLIST_FOREACH(entry, &sem->sem_head, t_obj_list) {
if (current->t_prio < entry->t_prio) {
break;
}
last = entry;
}
}
if (last) {
SLIST_INSERT_AFTER(last, current, t_obj_list);
} else {
SLIST_INSERT_HEAD(&sem->sem_head, current, t_obj_list);
}
/* We will put this task to sleep */
sched = 1;
os_sched_sleep(current, timeout);
}
OS_EXIT_CRITICAL(sr);
if (sched) {
os_sched(NULL);
/* Check if we timed out or got the semaphore */
if (current->t_flags & OS_TASK_FLAG_SEM_WAIT) {
OS_ENTER_CRITICAL(sr);
current->t_flags &= ~OS_TASK_FLAG_SEM_WAIT;
OS_EXIT_CRITICAL(sr);
ret = OS_TIMEOUT;
} else {
ret = OS_OK;
}
}
done:
os_trace_api_ret_u32(OS_TRACE_ID_SEM_PEND, (uint32_t)ret);
return ret;
}