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