| /* |
| * 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_MUTEX) |
| #define OS_TRACE_DISABLE_FILE_API |
| #endif |
| #include "os/mynewt.h" |
| |
| os_error_t |
| os_mutex_init(struct os_mutex *mu) |
| { |
| os_error_t ret; |
| |
| if (!mu) { |
| ret = OS_INVALID_PARM; |
| goto done; |
| } |
| |
| os_trace_api_u32(OS_TRACE_ID_MUTEX_INIT, (uint32_t)mu); |
| |
| /* Initialize to 0 */ |
| mu->mu_prio = 0; |
| mu->mu_level = 0; |
| mu->mu_owner = NULL; |
| SLIST_FIRST(&mu->mu_head) = NULL; |
| |
| ret = OS_OK; |
| |
| done: |
| os_trace_api_ret_u32(OS_TRACE_ID_MUTEX_INIT, (uint32_t)ret); |
| return ret; |
| } |
| |
| os_error_t |
| os_mutex_release(struct os_mutex *mu) |
| { |
| 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_MUTEX_RELEASE, (uint32_t)mu); |
| |
| /* Check if OS is started */ |
| if (!g_os_started) { |
| ret = OS_NOT_STARTED; |
| goto done; |
| } |
| |
| /* Check for valid mutex */ |
| if (!mu) { |
| ret = OS_INVALID_PARM; |
| goto done; |
| } |
| |
| /* We better own this mutex! */ |
| current = os_sched_get_current_task(); |
| if ((mu->mu_level == 0) || (mu->mu_owner != current)) { |
| ret = OS_BAD_MUTEX; |
| goto done; |
| } |
| |
| /* Decrement nesting level by 1. If not zero, nested (so dont release!) */ |
| --mu->mu_level; |
| if (mu->mu_level != 0) { |
| ret = OS_OK; |
| goto done; |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| |
| /* Restore owner task's priority; resort list if different */ |
| if (current->t_prio != mu->mu_prio) { |
| current->t_prio = mu->mu_prio; |
| os_sched_resort(current); |
| } |
| |
| /* Check if tasks are waiting for the mutex */ |
| rdy = SLIST_FIRST(&mu->mu_head); |
| if (rdy) { |
| /* There is one waiting. Wake it up */ |
| assert(rdy->t_obj); |
| os_sched_wakeup(rdy); |
| |
| /* Set mutex internals */ |
| mu->mu_level = 1; |
| mu->mu_prio = rdy->t_prio; |
| } |
| |
| /* Set new owner of mutex (or NULL if not owned) */ |
| mu->mu_owner = rdy; |
| if (rdy) { |
| rdy->t_lockcnt++; |
| } |
| --current->t_lockcnt; |
| |
| /* Do we need to re-schedule? */ |
| resched = 0; |
| rdy = os_sched_next_task(); |
| if (rdy != current) { |
| resched = 1; |
| } |
| 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_MUTEX_RELEASE, (uint32_t)ret); |
| return ret; |
| } |
| |
| os_error_t |
| os_mutex_pend(struct os_mutex *mu, os_time_t timeout) |
| { |
| os_sr_t sr; |
| os_error_t ret; |
| struct os_task *current; |
| struct os_task *entry; |
| struct os_task *last; |
| |
| os_trace_api_u32x2(OS_TRACE_ID_MUTEX_PEND, (uint32_t)mu, (uint32_t)timeout); |
| |
| /* OS must be started when calling this function */ |
| if (!g_os_started) { |
| ret = OS_NOT_STARTED; |
| goto done; |
| } |
| |
| /* Check for valid mutex */ |
| if (!mu) { |
| ret = OS_INVALID_PARM; |
| goto done; |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| |
| /* Is this owned? */ |
| current = os_sched_get_current_task(); |
| if (mu->mu_level == 0) { |
| mu->mu_owner = current; |
| mu->mu_prio = current->t_prio; |
| current->t_lockcnt++; |
| mu->mu_level = 1; |
| OS_EXIT_CRITICAL(sr); |
| ret = OS_OK; |
| goto done; |
| } |
| |
| /* Are we owner? */ |
| if (mu->mu_owner == current) { |
| ++mu->mu_level; |
| OS_EXIT_CRITICAL(sr); |
| ret = OS_OK; |
| goto done; |
| } |
| |
| /* Mutex is not owned by us. If timeout is 0, return immediately */ |
| if (timeout == 0) { |
| OS_EXIT_CRITICAL(sr); |
| ret = OS_TIMEOUT; |
| goto done; |
| } |
| |
| /* Change priority of owner if needed */ |
| if (mu->mu_owner->t_prio > current->t_prio) { |
| mu->mu_owner->t_prio = current->t_prio; |
| os_sched_resort(mu->mu_owner); |
| } |
| |
| /* Link current task to tasks waiting for mutex */ |
| last = NULL; |
| if (!SLIST_EMPTY(&mu->mu_head)) { |
| /* Insert in priority order */ |
| SLIST_FOREACH(entry, &mu->mu_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(&mu->mu_head, current, t_obj_list); |
| } |
| |
| /* Set mutex pointer in task */ |
| current->t_obj = mu; |
| current->t_flags |= OS_TASK_FLAG_MUTEX_WAIT; |
| os_sched_sleep(current, timeout); |
| OS_EXIT_CRITICAL(sr); |
| |
| os_sched(NULL); |
| |
| OS_ENTER_CRITICAL(sr); |
| current->t_flags &= ~OS_TASK_FLAG_MUTEX_WAIT; |
| OS_EXIT_CRITICAL(sr); |
| |
| /* If we are owner we did not time out. */ |
| if (mu->mu_owner == current) { |
| ret = OS_OK; |
| } else { |
| ret = OS_TIMEOUT; |
| } |
| |
| done: |
| os_trace_api_ret_u32(OS_TRACE_ID_MUTEX_PEND, (uint32_t)ret); |
| return ret; |
| } |
| |