1 /* 2 * Copyright (C) 2014 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24 * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "btstack_run_loop_posix.c" 39 40 /* 41 * btstack_run_loop.c 42 * 43 * Created by Matthias Ringwald on 6/6/09. 44 */ 45 46 // enable POSIX functions (needed for -std=c99) 47 #define _POSIX_C_SOURCE 200809 48 49 #include "btstack_run_loop_posix.h" 50 51 #include "btstack_run_loop.h" 52 #include "btstack_util.h" 53 #include "btstack_linked_list.h" 54 #include "btstack_debug.h" 55 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <sys/select.h> 59 #include <sys/time.h> 60 #include <time.h> 61 #include <unistd.h> 62 63 static void btstack_run_loop_posix_dump_timer(void); 64 65 // the run loop 66 static btstack_linked_list_t data_sources; 67 static int data_sources_modified; 68 static btstack_linked_list_t timers; 69 70 // start time. tv_usec/tv_nsec = 0 71 #ifdef _POSIX_MONOTONIC_CLOCK 72 // use monotonic clock if available 73 static struct timespec init_ts; 74 #else 75 // fallback to gettimeofday 76 static struct timeval init_tv; 77 #endif 78 79 /** 80 * Add data_source to run_loop 81 */ 82 static void btstack_run_loop_posix_add_data_source(btstack_data_source_t *ds){ 83 data_sources_modified = 1; 84 // log_info("btstack_run_loop_posix_add_data_source %x with fd %u\n", (int) ds, ds->source.fd); 85 btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds); 86 } 87 88 /** 89 * Remove data_source from run loop 90 */ 91 static bool btstack_run_loop_posix_remove_data_source(btstack_data_source_t *ds){ 92 data_sources_modified = 1; 93 log_debug("btstack_run_loop_posix_remove_data_source %p\n", ds); 94 return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds); 95 } 96 97 /** 98 * Add timer to run_loop (keep list sorted) 99 */ 100 static void btstack_run_loop_posix_add_timer(btstack_timer_source_t *ts){ 101 btstack_linked_item_t *it; 102 for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){ 103 btstack_timer_source_t * next = (btstack_timer_source_t *) it->next; 104 btstack_assert(next != ts); 105 // exit if new timeout before list timeout 106 int32_t delta = btstack_time_delta(ts->timeout, next->timeout); 107 if (delta < 0) break; 108 } 109 ts->item.next = it->next; 110 it->next = (btstack_linked_item_t *) ts; 111 log_debug("Added timer %p at %u\n", ts, ts->timeout); 112 // btstack_run_loop_posix_dump_timer(); 113 } 114 115 /** 116 * Remove timer from run loop 117 */ 118 static bool btstack_run_loop_posix_remove_timer(btstack_timer_source_t *ts){ 119 // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 120 // btstack_run_loop_posix_dump_timer(); 121 return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts); 122 } 123 124 static void btstack_run_loop_posix_dump_timer(void){ 125 #ifdef ENABLE_LOG_INFO 126 btstack_linked_item_t *it; 127 int i = 0; 128 for (it = (btstack_linked_item_t *) timers; it ; it = it->next){ 129 btstack_timer_source_t *ts = (btstack_timer_source_t*) it; 130 log_info("timer %u (%p): timeout %u\n", i, ts, ts->timeout); 131 } 132 #endif 133 } 134 135 static void btstack_run_loop_posix_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 136 ds->flags |= callback_types; 137 } 138 139 static void btstack_run_loop_posix_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 140 ds->flags &= ~callback_types; 141 } 142 143 #ifdef _POSIX_MONOTONIC_CLOCK 144 /** 145 * @brief Returns the timespec which represents the time(stop - start). It might be negative 146 */ 147 static void timespec_diff(struct timespec *start, struct timespec *stop, struct timespec *result){ 148 result->tv_sec = stop->tv_sec - start->tv_sec; 149 if ((stop->tv_nsec - start->tv_nsec) < 0) { 150 result->tv_sec = stop->tv_sec - start->tv_sec - 1; 151 result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000; 152 } else { 153 result->tv_sec = stop->tv_sec - start->tv_sec; 154 result->tv_nsec = stop->tv_nsec - start->tv_nsec; 155 } 156 } 157 158 /** 159 * @brief Convert timespec to miliseconds, might overflow 160 */ 161 static uint64_t timespec_to_milliseconds(struct timespec *a){ 162 uint64_t ret = 0; 163 uint64_t sec_val = (uint64_t)(a->tv_sec); 164 uint64_t nsec_val = (uint64_t)(a->tv_nsec); 165 ret = (sec_val*1000) + (nsec_val/1000000); 166 return ret; 167 } 168 169 /** 170 * @brief Returns the milisecond value of (stop - start). Might overflow 171 */ 172 static uint64_t timespec_diff_milis(struct timespec* start, struct timespec* stop){ 173 struct timespec diff_ts; 174 timespec_diff(start, stop, &diff_ts); 175 return timespec_to_milliseconds(&diff_ts); 176 } 177 #endif 178 179 /** 180 * @brief Queries the current time in ms since start 181 */ 182 static uint32_t btstack_run_loop_posix_get_time_ms(void){ 183 uint32_t time_ms; 184 #ifdef _POSIX_MONOTONIC_CLOCK 185 struct timespec now_ts; 186 clock_gettime(CLOCK_MONOTONIC, &now_ts); 187 time_ms = (uint32_t) timespec_diff_milis(&init_ts, &now_ts); 188 #else 189 struct timeval tv; 190 gettimeofday(&tv, NULL); 191 time_ms = (uint32_t) ((tv.tv_sec - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000); 192 #endif 193 return time_ms; 194 } 195 196 /** 197 * Execute run_loop 198 */ 199 static void btstack_run_loop_posix_execute(void) { 200 fd_set descriptors_read; 201 fd_set descriptors_write; 202 203 btstack_timer_source_t *ts; 204 btstack_linked_list_iterator_t it; 205 struct timeval * timeout; 206 struct timeval tv; 207 uint32_t now_ms; 208 209 #ifdef _POSIX_MONOTONIC_CLOCK 210 log_info("POSIX run loop with monotonic clock"); 211 #else 212 log_info("POSIX run loop using ettimeofday fallback."); 213 #endif 214 215 while (true) { 216 // collect FDs 217 FD_ZERO(&descriptors_read); 218 FD_ZERO(&descriptors_write); 219 int highest_fd = -1; 220 btstack_linked_list_iterator_init(&it, &data_sources); 221 while (btstack_linked_list_iterator_has_next(&it)){ 222 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 223 if (ds->source.fd < 0) continue; 224 if (ds->flags & DATA_SOURCE_CALLBACK_READ){ 225 FD_SET(ds->source.fd, &descriptors_read); 226 if (ds->source.fd > highest_fd) { 227 highest_fd = ds->source.fd; 228 } 229 log_debug("btstack_run_loop_execute adding fd %u for read", ds->source.fd); 230 } 231 if (ds->flags & DATA_SOURCE_CALLBACK_WRITE){ 232 FD_SET(ds->source.fd, &descriptors_write); 233 if (ds->source.fd > highest_fd) { 234 highest_fd = ds->source.fd; 235 } 236 log_debug("btstack_run_loop_execute adding fd %u for write", ds->source.fd); 237 } 238 } 239 240 // get next timeout 241 timeout = NULL; 242 if (timers) { 243 ts = (btstack_timer_source_t *) timers; 244 timeout = &tv; 245 uint32_t list_timeout = ts->timeout; 246 now_ms = btstack_run_loop_posix_get_time_ms(); 247 int32_t delta = btstack_time_delta(list_timeout, now_ms); 248 if (delta < 0){ 249 delta = 0; 250 } 251 tv.tv_sec = delta / 1000; 252 tv.tv_usec = (int) (delta - (tv.tv_sec * 1000)) * 1000; 253 log_debug("btstack_run_loop_execute next timeout in %u ms", delta); 254 } 255 256 // wait for ready FDs 257 select( highest_fd+1 , &descriptors_read, &descriptors_write, NULL, timeout); 258 259 260 data_sources_modified = 0; 261 btstack_linked_list_iterator_init(&it, &data_sources); 262 while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){ 263 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 264 log_debug("btstack_run_loop_posix_execute: check ds %p with fd %u\n", ds, ds->source.fd); 265 if (FD_ISSET(ds->source.fd, &descriptors_read)) { 266 log_debug("btstack_run_loop_posix_execute: process read ds %p with fd %u\n", ds, ds->source.fd); 267 ds->process(ds, DATA_SOURCE_CALLBACK_READ); 268 } 269 if (data_sources_modified) break; 270 if (FD_ISSET(ds->source.fd, &descriptors_write)) { 271 log_debug("btstack_run_loop_posix_execute: process write ds %p with fd %u\n", ds, ds->source.fd); 272 ds->process(ds, DATA_SOURCE_CALLBACK_WRITE); 273 } 274 } 275 log_debug("btstack_run_loop_posix_execute: after ds check\n"); 276 277 // process timers 278 now_ms = btstack_run_loop_posix_get_time_ms(); 279 while (timers) { 280 ts = (btstack_timer_source_t *) timers; 281 int32_t delta = btstack_time_delta(ts->timeout, now_ms); 282 if (delta > 0) break; 283 log_debug("btstack_run_loop_posix_execute: process timer %p\n", ts); 284 285 // remove timer before processing it to allow handler to re-register with run loop 286 btstack_run_loop_posix_remove_timer(ts); 287 ts->process(ts); 288 } 289 } 290 } 291 292 // set timer 293 static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ 294 uint32_t time_ms = btstack_run_loop_posix_get_time_ms(); 295 a->timeout = time_ms + timeout_in_ms; 296 log_debug("btstack_run_loop_posix_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms); 297 } 298 299 static void btstack_run_loop_posix_init(void){ 300 data_sources = NULL; 301 timers = NULL; 302 #ifdef _POSIX_MONOTONIC_CLOCK 303 clock_gettime(CLOCK_MONOTONIC, &init_ts); 304 init_ts.tv_nsec = 0; 305 #else 306 // just assume that we started at tv_usec == 0 307 gettimeofday(&init_tv, NULL); 308 init_tv.tv_usec = 0; 309 #endif 310 } 311 312 313 static const btstack_run_loop_t btstack_run_loop_posix = { 314 &btstack_run_loop_posix_init, 315 &btstack_run_loop_posix_add_data_source, 316 &btstack_run_loop_posix_remove_data_source, 317 &btstack_run_loop_posix_enable_data_source_callbacks, 318 &btstack_run_loop_posix_disable_data_source_callbacks, 319 &btstack_run_loop_posix_set_timer, 320 &btstack_run_loop_posix_add_timer, 321 &btstack_run_loop_posix_remove_timer, 322 &btstack_run_loop_posix_execute, 323 &btstack_run_loop_posix_dump_timer, 324 &btstack_run_loop_posix_get_time_ms, 325 }; 326 327 /** 328 * Provide btstack_run_loop_posix instance 329 */ 330 const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){ 331 return &btstack_run_loop_posix; 332 } 333 334