| /* |
| * 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 <string.h> |
| #include "syscfg/syscfg.h" |
| #if !MYNEWT_VAL(OS_SYSVIEW_TRACE_CALLOUT) |
| #define OS_TRACE_DISABLE_FILE_API |
| #endif |
| #include "os/mynewt.h" |
| #include "os_priv.h" |
| |
| struct os_callout_list g_callout_list; |
| |
| void os_callout_init(struct os_callout *c, struct os_eventq *evq, |
| os_event_fn *ev_cb, void *ev_arg) |
| { |
| os_trace_api_u32x2(OS_TRACE_ID_CALLOUT_INIT, (uint32_t)c, (uint32_t)evq); |
| |
| memset(c, 0, sizeof(*c)); |
| c->c_ev.ev_cb = ev_cb; |
| c->c_ev.ev_arg = ev_arg; |
| c->c_evq = evq; |
| |
| os_trace_api_ret(OS_TRACE_ID_CALLOUT_INIT); |
| } |
| |
| void |
| os_callout_stop(struct os_callout *c) |
| { |
| os_sr_t sr; |
| |
| os_trace_api_u32(OS_TRACE_ID_CALLOUT_STOP, (uint32_t)c); |
| |
| OS_ENTER_CRITICAL(sr); |
| |
| if (os_callout_queued(c)) { |
| TAILQ_REMOVE(&g_callout_list, c, c_next); |
| c->c_next.tqe_prev = NULL; |
| } |
| |
| if (c->c_evq) { |
| os_eventq_remove(c->c_evq, &c->c_ev); |
| } |
| |
| OS_EXIT_CRITICAL(sr); |
| |
| os_trace_api_ret(OS_TRACE_ID_CALLOUT_STOP); |
| } |
| |
| int |
| os_callout_reset(struct os_callout *c, os_time_t ticks) |
| { |
| struct os_callout *entry; |
| os_sr_t sr; |
| int ret; |
| |
| os_trace_api_u32x2(OS_TRACE_ID_CALLOUT_RESET, (uint32_t)c, (uint32_t)ticks); |
| |
| if (ticks > INT32_MAX) { |
| ret = OS_EINVAL; |
| goto err; |
| } |
| |
| OS_ENTER_CRITICAL(sr); |
| |
| os_callout_stop(c); |
| |
| if (ticks == 0) { |
| ticks = 1; |
| } |
| |
| c->c_ticks = os_time_get() + ticks; |
| |
| entry = NULL; |
| TAILQ_FOREACH(entry, &g_callout_list, c_next) { |
| if (OS_TIME_TICK_LT(c->c_ticks, entry->c_ticks)) { |
| break; |
| } |
| } |
| |
| if (entry) { |
| TAILQ_INSERT_BEFORE(entry, c, c_next); |
| } else { |
| TAILQ_INSERT_TAIL(&g_callout_list, c, c_next); |
| } |
| |
| OS_EXIT_CRITICAL(sr); |
| |
| ret = OS_OK; |
| |
| err: |
| os_trace_api_ret_u32(OS_TRACE_ID_CALLOUT_RESET, (uint32_t)ret); |
| return ret; |
| } |
| |
| |
| /** |
| * This function is called by the OS in the time tick. It searches the list |
| * of callouts, and sees if any of them are ready to run. If they are ready |
| * to run, it posts an event for each callout that's ready to run, |
| * to the event queue provided to os_callout_init(). |
| */ |
| void |
| os_callout_tick(void) |
| { |
| os_sr_t sr; |
| struct os_callout *c; |
| uint32_t now; |
| |
| os_trace_api_void(OS_TRACE_ID_CALLOUT_TICK); |
| |
| now = os_time_get(); |
| |
| while (1) { |
| OS_ENTER_CRITICAL(sr); |
| c = TAILQ_FIRST(&g_callout_list); |
| if (c) { |
| if (OS_TIME_TICK_GEQ(now, c->c_ticks)) { |
| TAILQ_REMOVE(&g_callout_list, c, c_next); |
| c->c_next.tqe_prev = NULL; |
| } else { |
| c = NULL; |
| } |
| } |
| OS_EXIT_CRITICAL(sr); |
| |
| if (c) { |
| if (c->c_evq) { |
| os_eventq_put(c->c_evq, &c->c_ev); |
| } else { |
| c->c_ev.ev_cb(&c->c_ev); |
| } |
| } else { |
| break; |
| } |
| } |
| |
| os_trace_api_ret(OS_TRACE_ID_CALLOUT_TICK); |
| } |
| |
| /* |
| * Returns the number of ticks to the first pending callout. If there are no |
| * pending callouts then return OS_TIMEOUT_NEVER instead. |
| * |
| * @param now The time now |
| * |
| * @return Number of ticks to first pending callout |
| */ |
| os_time_t |
| os_callout_wakeup_ticks(os_time_t now) |
| { |
| os_time_t rt; |
| struct os_callout *c; |
| |
| OS_ASSERT_CRITICAL(); |
| |
| c = TAILQ_FIRST(&g_callout_list); |
| if (c != NULL) { |
| if (OS_TIME_TICK_GEQ(c->c_ticks, now)) { |
| rt = c->c_ticks - now; |
| } else { |
| rt = 0; /* callout time is in the past */ |
| } |
| } else { |
| rt = OS_TIMEOUT_NEVER; |
| } |
| |
| return (rt); |
| } |
| |
| |
| os_time_t |
| os_callout_remaining_ticks(struct os_callout *c, os_time_t now) |
| { |
| os_sr_t sr; |
| os_time_t rt; |
| |
| OS_ENTER_CRITICAL(sr); |
| |
| if (OS_TIME_TICK_GEQ(c->c_ticks, now)) { |
| rt = c->c_ticks - now; |
| } else { |
| rt = 0; /* callout time is in the past */ |
| } |
| |
| OS_EXIT_CRITICAL(sr); |
| |
| return rt; |
| } |
| |