/* * 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 #include #include #include #include #include #include #include "nimble/nimble_npl.h" #ifndef CONFIG_NIMBLE_CALLOUT_THREAD_STACKSIZE #define CONFIG_NIMBLE_CALLOUT_THREAD_STACKSIZE 1024 #endif struct ble_npl_callout *pending_callout = NULL; bool thread_started = false; pthread_t callout_thread; pthread_mutex_t callout_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t callout_cond = PTHREAD_COND_INITIALIZER; static pthread_addr_t callout_handler(pthread_addr_t arg) { struct ble_npl_callout *c; pthread_mutex_lock(&callout_mutex); while (!pending_callout) { pthread_cond_wait(&callout_cond, &callout_mutex); } c = pending_callout; pending_callout = NULL; pthread_mutex_unlock(&callout_mutex); /* Invoke callback */ if (c->c_evq) { ble_npl_eventq_put(c->c_evq, &c->c_ev); } else { c->c_ev.ev_cb(&c->c_ev); } return NULL; } static void ble_npl_callout_timer_cb(union sigval sv) { struct ble_npl_callout *c = (struct ble_npl_callout *)sv.sival_ptr; assert(c); pthread_mutex_lock(&callout_mutex); pending_callout = c; pthread_cond_signal(&callout_cond); pthread_mutex_unlock(&callout_mutex); } void ble_npl_callout_init(struct ble_npl_callout *c, struct ble_npl_eventq *evq, ble_npl_event_fn *ev_cb, void *ev_arg) { struct sigevent event; if (!thread_started) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, CONFIG_NIMBLE_CALLOUT_THREAD_STACKSIZE); pthread_create(&callout_thread, &attr, callout_handler, NULL); thread_started = true; } /* Initialize the callout. */ memset(c, 0, sizeof(*c)); c->c_ev.ev_cb = ev_cb; c->c_ev.ev_arg = ev_arg; c->c_evq = evq; c->c_active = false; event.sigev_notify = SIGEV_THREAD; event.sigev_value.sival_ptr = c; // put callout obj in signal args event.sigev_notify_function = ble_npl_callout_timer_cb; event.sigev_notify_attributes = NULL; timer_create(CLOCK_REALTIME, &event, &c->c_timer); } bool ble_npl_callout_is_active(struct ble_npl_callout *c) { /* TODO: seek native posix method to determine whether timer_t is active. TODO: fix bug where one-shot timer is still active after fired. */ return c->c_active; } int ble_npl_callout_inited(struct ble_npl_callout *c) { return (c->c_timer != NULL); } ble_npl_error_t ble_npl_callout_reset(struct ble_npl_callout *c, ble_npl_time_t ticks) { struct itimerspec its; if (ticks < 0) { return BLE_NPL_EINVAL; } if (ticks == 0) { ticks = 1; } c->c_ticks = ble_npl_time_get() + ticks; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; /* one shot */ its.it_value.tv_sec = (ticks / 1000); its.it_value.tv_nsec = (ticks % 1000) * 1000000; /* expiration */ its.it_value.tv_nsec %= 1000000000; c->c_active = true; timer_settime(c->c_timer, 0, &its, NULL); return BLE_NPL_OK; } int ble_npl_callout_queued(struct ble_npl_callout *c) { struct itimerspec its; timer_gettime(c->c_timer, &its); return ((its.it_value.tv_sec > 0) || (its.it_value.tv_nsec > 0)); } void ble_npl_callout_stop(struct ble_npl_callout *c) { if (!ble_npl_callout_inited(c)) { return; } struct itimerspec its; its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; its.it_value.tv_sec = 0; its.it_value.tv_nsec = 0; timer_settime(c->c_timer, 0, &its, NULL); c->c_active = false; } ble_npl_time_t ble_npl_callout_get_ticks(struct ble_npl_callout *co) { return co->c_ticks; } void ble_npl_callout_set_arg(struct ble_npl_callout *co, void *arg) { co->c_ev.ev_arg = arg; } uint32_t ble_npl_callout_remaining_ticks(struct ble_npl_callout *co, ble_npl_time_t now) { ble_npl_time_t rt; uint32_t exp; struct itimerspec its; timer_gettime(co->c_timer, &its); exp = its.it_value.tv_sec * 1000; if (exp > now) { rt = exp - now; } else { rt = 0; } return rt; }