| /* |
| * 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. |
| * current->t_prio == rdy->t_prio means that current task |
| * was already put in semaphore waiting list and context |
| * switch may already started, in that case start next |
| * context switch. In worst case scenario second task |
| * switch interrupt will check that there is no need |
| * for switching. |
| */ |
| 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; |
| } |