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 btstack_linked_item_t *it; 126 int i = 0; 127 for (it = (btstack_linked_item_t *) timers; it ; it = it->next){ 128 btstack_timer_source_t *ts = (btstack_timer_source_t*) it; 129 log_info("timer %u (%p): timeout %u\n", i, ts, ts->timeout); 130 } 131 } 132 133 static void btstack_run_loop_posix_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 134 ds->flags |= callback_types; 135 } 136 137 static void btstack_run_loop_posix_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 138 ds->flags &= ~callback_types; 139 } 140 141 #ifdef _POSIX_MONOTONIC_CLOCK 142 /** 143 * @brief Returns the timespec which represents the time(stop - start). It might be negative 144 */ 145 static void timespec_diff(struct timespec *start, struct timespec *stop, struct timespec *result){ 146 result->tv_sec = stop->tv_sec - start->tv_sec; 147 if ((stop->tv_nsec - start->tv_nsec) < 0) { 148 result->tv_sec = stop->tv_sec - start->tv_sec - 1; 149 result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000; 150 } else { 151 result->tv_sec = stop->tv_sec - start->tv_sec; 152 result->tv_nsec = stop->tv_nsec - start->tv_nsec; 153 } 154 } 155 156 /** 157 * @brief Convert timespec to miliseconds, might overflow 158 */ 159 static uint64_t timespec_to_milliseconds(struct timespec *a){ 160 uint64_t ret = 0; 161 uint64_t sec_val = (uint64_t)(a->tv_sec); 162 uint64_t nsec_val = (uint64_t)(a->tv_nsec); 163 ret = (sec_val*1000) + (nsec_val/1000000); 164 return ret; 165 } 166 167 /** 168 * @brief Returns the milisecond value of (stop - start). Might overflow 169 */ 170 static uint64_t timespec_diff_milis(struct timespec* start, struct timespec* stop){ 171 struct timespec diff_ts; 172 timespec_diff(start, stop, &diff_ts); 173 return timespec_to_milliseconds(&diff_ts); 174 } 175 #endif 176 177 /** 178 * @brief Queries the current time in ms since start 179 */ 180 static uint32_t btstack_run_loop_posix_get_time_ms(void){ 181 uint32_t time_ms; 182 #ifdef _POSIX_MONOTONIC_CLOCK 183 struct timespec now_ts; 184 clock_gettime(CLOCK_MONOTONIC, &now_ts); 185 time_ms = (uint32_t) timespec_diff_milis(&init_ts, &now_ts); 186 #else 187 struct timeval tv; 188 gettimeofday(&tv, NULL); 189 time_ms = (uint32_t) ((tv.tv_sec - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000); 190 #endif 191 return time_ms; 192 } 193 194 /** 195 * Execute run_loop 196 */ 197 static void btstack_run_loop_posix_execute(void) { 198 fd_set descriptors_read; 199 fd_set descriptors_write; 200 201 btstack_timer_source_t *ts; 202 btstack_linked_list_iterator_t it; 203 struct timeval * timeout; 204 struct timeval tv; 205 uint32_t now_ms; 206 207 #ifdef _POSIX_MONOTONIC_CLOCK 208 log_info("POSIX run loop with monotonic clock"); 209 #else 210 log_info("POSIX run loop using ettimeofday fallback."); 211 #endif 212 213 while (1) { 214 // collect FDs 215 FD_ZERO(&descriptors_read); 216 FD_ZERO(&descriptors_write); 217 int highest_fd = -1; 218 btstack_linked_list_iterator_init(&it, &data_sources); 219 while (btstack_linked_list_iterator_has_next(&it)){ 220 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 221 if (ds->source.fd < 0) continue; 222 if (ds->flags & DATA_SOURCE_CALLBACK_READ){ 223 FD_SET(ds->source.fd, &descriptors_read); 224 if (ds->source.fd > highest_fd) { 225 highest_fd = ds->source.fd; 226 } 227 log_debug("btstack_run_loop_execute adding fd %u for read", ds->source.fd); 228 } 229 if (ds->flags & DATA_SOURCE_CALLBACK_WRITE){ 230 FD_SET(ds->source.fd, &descriptors_write); 231 if (ds->source.fd > highest_fd) { 232 highest_fd = ds->source.fd; 233 } 234 log_debug("btstack_run_loop_execute adding fd %u for write", ds->source.fd); 235 } 236 } 237 238 // get next timeout 239 timeout = NULL; 240 if (timers) { 241 ts = (btstack_timer_source_t *) timers; 242 timeout = &tv; 243 uint32_t list_timeout = ts->timeout; 244 now_ms = btstack_run_loop_posix_get_time_ms(); 245 int32_t delta = btstack_time_delta(list_timeout, now_ms); 246 if (delta < 0){ 247 delta = 0; 248 } 249 tv.tv_sec = delta / 1000; 250 tv.tv_usec = (int) (delta - (tv.tv_sec * 1000)) * 1000; 251 log_debug("btstack_run_loop_execute next timeout in %u ms", delta); 252 } 253 254 // wait for ready FDs 255 select( highest_fd+1 , &descriptors_read, &descriptors_write, NULL, timeout); 256 257 258 data_sources_modified = 0; 259 btstack_linked_list_iterator_init(&it, &data_sources); 260 while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){ 261 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 262 log_debug("btstack_run_loop_posix_execute: check ds %p with fd %u\n", ds, ds->source.fd); 263 if (FD_ISSET(ds->source.fd, &descriptors_read)) { 264 log_debug("btstack_run_loop_posix_execute: process read ds %p with fd %u\n", ds, ds->source.fd); 265 ds->process(ds, DATA_SOURCE_CALLBACK_READ); 266 } 267 if (data_sources_modified) break; 268 if (FD_ISSET(ds->source.fd, &descriptors_write)) { 269 log_debug("btstack_run_loop_posix_execute: process write ds %p with fd %u\n", ds, ds->source.fd); 270 ds->process(ds, DATA_SOURCE_CALLBACK_WRITE); 271 } 272 } 273 log_debug("btstack_run_loop_posix_execute: after ds check\n"); 274 275 // process timers 276 now_ms = btstack_run_loop_posix_get_time_ms(); 277 while (timers) { 278 ts = (btstack_timer_source_t *) timers; 279 int32_t delta = btstack_time_delta(ts->timeout, now_ms); 280 if (delta > 0) break; 281 log_debug("btstack_run_loop_posix_execute: process timer %p\n", ts); 282 283 // remove timer before processing it to allow handler to re-register with run loop 284 btstack_run_loop_posix_remove_timer(ts); 285 ts->process(ts); 286 } 287 } 288 } 289 290 // set timer 291 static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ 292 uint32_t time_ms = btstack_run_loop_posix_get_time_ms(); 293 a->timeout = time_ms + timeout_in_ms; 294 log_debug("btstack_run_loop_posix_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms); 295 } 296 297 static void btstack_run_loop_posix_init(void){ 298 data_sources = NULL; 299 timers = NULL; 300 #ifdef _POSIX_MONOTONIC_CLOCK 301 clock_gettime(CLOCK_MONOTONIC, &init_ts); 302 init_ts.tv_nsec = 0; 303 #else 304 // just assume that we started at tv_usec == 0 305 gettimeofday(&init_tv, NULL); 306 init_tv.tv_usec = 0; 307 #endif 308 } 309 310 311 static const btstack_run_loop_t btstack_run_loop_posix = { 312 &btstack_run_loop_posix_init, 313 &btstack_run_loop_posix_add_data_source, 314 &btstack_run_loop_posix_remove_data_source, 315 &btstack_run_loop_posix_enable_data_source_callbacks, 316 &btstack_run_loop_posix_disable_data_source_callbacks, 317 &btstack_run_loop_posix_set_timer, 318 &btstack_run_loop_posix_add_timer, 319 &btstack_run_loop_posix_remove_timer, 320 &btstack_run_loop_posix_execute, 321 &btstack_run_loop_posix_dump_timer, 322 &btstack_run_loop_posix_get_time_ms, 323 }; 324 325 /** 326 * Provide btstack_run_loop_posix instance 327 */ 328 const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){ 329 return &btstack_run_loop_posix; 330 } 331 332