1b7d596c1SMatthias Ringwald /* 2b7d596c1SMatthias Ringwald * Copyright (C) 2014 BlueKitchen GmbH 3b7d596c1SMatthias Ringwald * 4b7d596c1SMatthias Ringwald * Redistribution and use in source and binary forms, with or without 5b7d596c1SMatthias Ringwald * modification, are permitted provided that the following conditions 6b7d596c1SMatthias Ringwald * are met: 7b7d596c1SMatthias Ringwald * 8b7d596c1SMatthias Ringwald * 1. Redistributions of source code must retain the above copyright 9b7d596c1SMatthias Ringwald * notice, this list of conditions and the following disclaimer. 10b7d596c1SMatthias Ringwald * 2. Redistributions in binary form must reproduce the above copyright 11b7d596c1SMatthias Ringwald * notice, this list of conditions and the following disclaimer in the 12b7d596c1SMatthias Ringwald * documentation and/or other materials provided with the distribution. 13b7d596c1SMatthias Ringwald * 3. Neither the name of the copyright holders nor the names of 14b7d596c1SMatthias Ringwald * contributors may be used to endorse or promote products derived 15b7d596c1SMatthias Ringwald * from this software without specific prior written permission. 16b7d596c1SMatthias Ringwald * 4. Any redistribution, use, or modification is done solely for 17b7d596c1SMatthias Ringwald * personal benefit and not for any commercial purpose or for 18b7d596c1SMatthias Ringwald * monetary gain. 19b7d596c1SMatthias Ringwald * 20b7d596c1SMatthias Ringwald * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21b7d596c1SMatthias Ringwald * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22b7d596c1SMatthias Ringwald * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23b7d596c1SMatthias Ringwald * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS 24b7d596c1SMatthias Ringwald * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25b7d596c1SMatthias Ringwald * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26b7d596c1SMatthias Ringwald * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27b7d596c1SMatthias Ringwald * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28b7d596c1SMatthias Ringwald * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29b7d596c1SMatthias Ringwald * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30b7d596c1SMatthias Ringwald * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31b7d596c1SMatthias Ringwald * SUCH DAMAGE. 32b7d596c1SMatthias Ringwald * 33b7d596c1SMatthias Ringwald * Please inquire about commercial licensing options at 34b7d596c1SMatthias Ringwald * [email protected] 35b7d596c1SMatthias Ringwald * 36b7d596c1SMatthias Ringwald */ 37b7d596c1SMatthias Ringwald 38b7d596c1SMatthias Ringwald /* 394ab92137SMatthias Ringwald * btstack_run_loop.c 40b7d596c1SMatthias Ringwald * 41b7d596c1SMatthias Ringwald * Created by Matthias Ringwald on 6/6/09. 42b7d596c1SMatthias Ringwald */ 43b7d596c1SMatthias Ringwald 44b7d596c1SMatthias Ringwald #include "btstack_run_loop.h" 458f2a52f4SMatthias Ringwald #include "btstack_run_loop_posix.h" 46b7d596c1SMatthias Ringwald #include "btstack_linked_list.h" 47b7d596c1SMatthias Ringwald #include "btstack_debug.h" 48b7d596c1SMatthias Ringwald 49b7d596c1SMatthias Ringwald #ifdef _WIN32 50b7d596c1SMatthias Ringwald #include "Winsock2.h" 51b7d596c1SMatthias Ringwald #else 52b7d596c1SMatthias Ringwald #include <sys/select.h> 53b7d596c1SMatthias Ringwald #endif 54b7d596c1SMatthias Ringwald #include <stdio.h> 55b7d596c1SMatthias Ringwald #include <stdlib.h> 56b7d596c1SMatthias Ringwald 57528a4a3bSMatthias Ringwald static void btstack_run_loop_posix_dump_timer(void); 58528a4a3bSMatthias Ringwald static int btstack_run_loop_posix_timeval_compare(struct timeval *a, struct timeval *b); 59*ec820d77SMatthias Ringwald static int btstack_run_loop_posix_timer_compare(btstack_timer_source_t *a, btstack_timer_source_t *b); 60b7d596c1SMatthias Ringwald 61b7d596c1SMatthias Ringwald // the run loop 628f2a52f4SMatthias Ringwald static btstack_linked_list_t data_sources; 63b7d596c1SMatthias Ringwald static int data_sources_modified; 648f2a52f4SMatthias Ringwald static btstack_linked_list_t timers; 65b7d596c1SMatthias Ringwald static struct timeval init_tv; 66b7d596c1SMatthias Ringwald 67b7d596c1SMatthias Ringwald /** 68b7d596c1SMatthias Ringwald * Add data_source to run_loop 69b7d596c1SMatthias Ringwald */ 70*ec820d77SMatthias Ringwald static void btstack_run_loop_posix_add_data_source(btstack_data_source_t *ds){ 71b7d596c1SMatthias Ringwald data_sources_modified = 1; 72528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_add_data_source %x with fd %u\n", (int) ds, ds->fd); 73b7d596c1SMatthias Ringwald btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds); 74b7d596c1SMatthias Ringwald } 75b7d596c1SMatthias Ringwald 76b7d596c1SMatthias Ringwald /** 77b7d596c1SMatthias Ringwald * Remove data_source from run loop 78b7d596c1SMatthias Ringwald */ 79*ec820d77SMatthias Ringwald static int btstack_run_loop_posix_remove_data_source(btstack_data_source_t *ds){ 80b7d596c1SMatthias Ringwald data_sources_modified = 1; 81528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_remove_data_source %x\n", (int) ds); 82b7d596c1SMatthias Ringwald return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds); 83b7d596c1SMatthias Ringwald } 84b7d596c1SMatthias Ringwald 85b7d596c1SMatthias Ringwald /** 86b7d596c1SMatthias Ringwald * Add timer to run_loop (keep list sorted) 87b7d596c1SMatthias Ringwald */ 88*ec820d77SMatthias Ringwald static void btstack_run_loop_posix_add_timer(btstack_timer_source_t *ts){ 89b7d596c1SMatthias Ringwald btstack_linked_item_t *it; 90b7d596c1SMatthias Ringwald for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){ 91*ec820d77SMatthias Ringwald if ((btstack_timer_source_t *) it->next == ts){ 92528a4a3bSMatthias Ringwald log_error( "btstack_run_loop_timer_add error: timer to add already in list!"); 93b7d596c1SMatthias Ringwald return; 94b7d596c1SMatthias Ringwald } 95*ec820d77SMatthias Ringwald if (btstack_run_loop_posix_timer_compare( (btstack_timer_source_t *) it->next, ts) > 0) { 96b7d596c1SMatthias Ringwald break; 97b7d596c1SMatthias Ringwald } 98b7d596c1SMatthias Ringwald } 99b7d596c1SMatthias Ringwald ts->item.next = it->next; 100b7d596c1SMatthias Ringwald it->next = (btstack_linked_item_t *) ts; 101b7d596c1SMatthias Ringwald // log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 102528a4a3bSMatthias Ringwald // btstack_run_loop_posix_dump_timer(); 103b7d596c1SMatthias Ringwald } 104b7d596c1SMatthias Ringwald 105b7d596c1SMatthias Ringwald /** 106b7d596c1SMatthias Ringwald * Remove timer from run loop 107b7d596c1SMatthias Ringwald */ 108*ec820d77SMatthias Ringwald static int btstack_run_loop_posix_remove_timer(btstack_timer_source_t *ts){ 109b7d596c1SMatthias Ringwald // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 110528a4a3bSMatthias Ringwald // btstack_run_loop_posix_dump_timer(); 111b7d596c1SMatthias Ringwald return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts); 112b7d596c1SMatthias Ringwald } 113b7d596c1SMatthias Ringwald 114528a4a3bSMatthias Ringwald static void btstack_run_loop_posix_dump_timer(void){ 115b7d596c1SMatthias Ringwald btstack_linked_item_t *it; 116b7d596c1SMatthias Ringwald int i = 0; 117b7d596c1SMatthias Ringwald for (it = (btstack_linked_item_t *) timers; it ; it = it->next){ 118*ec820d77SMatthias Ringwald btstack_timer_source_t *ts = (btstack_timer_source_t*) it; 119b7d596c1SMatthias Ringwald log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout.tv_sec); 120b7d596c1SMatthias Ringwald } 121b7d596c1SMatthias Ringwald } 122b7d596c1SMatthias Ringwald 123b7d596c1SMatthias Ringwald /** 124b7d596c1SMatthias Ringwald * Execute run_loop 125b7d596c1SMatthias Ringwald */ 126528a4a3bSMatthias Ringwald static void btstack_run_loop_posix_execute(void) { 127b7d596c1SMatthias Ringwald fd_set descriptors; 128b7d596c1SMatthias Ringwald 129*ec820d77SMatthias Ringwald btstack_timer_source_t *ts; 130b7d596c1SMatthias Ringwald struct timeval current_tv; 131b7d596c1SMatthias Ringwald struct timeval next_tv; 132b7d596c1SMatthias Ringwald struct timeval *timeout; 133b7d596c1SMatthias Ringwald btstack_linked_list_iterator_t it; 134b7d596c1SMatthias Ringwald 135b7d596c1SMatthias Ringwald while (1) { 136b7d596c1SMatthias Ringwald // collect FDs 137b7d596c1SMatthias Ringwald FD_ZERO(&descriptors); 138b7d596c1SMatthias Ringwald int highest_fd = 0; 139b7d596c1SMatthias Ringwald btstack_linked_list_iterator_init(&it, &data_sources); 140b7d596c1SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it)){ 141*ec820d77SMatthias Ringwald btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 142b7d596c1SMatthias Ringwald if (ds->fd >= 0) { 143b7d596c1SMatthias Ringwald FD_SET(ds->fd, &descriptors); 144b7d596c1SMatthias Ringwald if (ds->fd > highest_fd) { 145b7d596c1SMatthias Ringwald highest_fd = ds->fd; 146b7d596c1SMatthias Ringwald } 147b7d596c1SMatthias Ringwald } 148b7d596c1SMatthias Ringwald } 149b7d596c1SMatthias Ringwald 150b7d596c1SMatthias Ringwald // get next timeout 151b7d596c1SMatthias Ringwald // pre: 0 <= tv_usec < 1000000 152b7d596c1SMatthias Ringwald timeout = NULL; 153b7d596c1SMatthias Ringwald if (timers) { 154b7d596c1SMatthias Ringwald gettimeofday(¤t_tv, NULL); 155*ec820d77SMatthias Ringwald ts = (btstack_timer_source_t *) timers; 156b7d596c1SMatthias Ringwald next_tv.tv_usec = ts->timeout.tv_usec - current_tv.tv_usec; 157b7d596c1SMatthias Ringwald next_tv.tv_sec = ts->timeout.tv_sec - current_tv.tv_sec; 158b7d596c1SMatthias Ringwald while (next_tv.tv_usec < 0){ 159b7d596c1SMatthias Ringwald next_tv.tv_usec += 1000000; 160b7d596c1SMatthias Ringwald next_tv.tv_sec--; 161b7d596c1SMatthias Ringwald } 162b7d596c1SMatthias Ringwald if (next_tv.tv_sec < 0){ 163b7d596c1SMatthias Ringwald next_tv.tv_sec = 0; 164b7d596c1SMatthias Ringwald next_tv.tv_usec = 0; 165b7d596c1SMatthias Ringwald } 166b7d596c1SMatthias Ringwald timeout = &next_tv; 167b7d596c1SMatthias Ringwald } 168b7d596c1SMatthias Ringwald 169b7d596c1SMatthias Ringwald // wait for ready FDs 170b7d596c1SMatthias Ringwald select( highest_fd+1 , &descriptors, NULL, NULL, timeout); 171b7d596c1SMatthias Ringwald 172b7d596c1SMatthias Ringwald // process data sources very carefully 173b7d596c1SMatthias Ringwald // bt_control.close() triggered from a client can remove a different data source 174b7d596c1SMatthias Ringwald 175528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_execute: before ds check\n"); 176b7d596c1SMatthias Ringwald data_sources_modified = 0; 177b7d596c1SMatthias Ringwald btstack_linked_list_iterator_init(&it, &data_sources); 178b7d596c1SMatthias Ringwald while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){ 179*ec820d77SMatthias Ringwald btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 180528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_execute: check %x with fd %u\n", (int) ds, ds->fd); 181b7d596c1SMatthias Ringwald if (FD_ISSET(ds->fd, &descriptors)) { 182528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_execute: process %x with fd %u\n", (int) ds, ds->fd); 183b7d596c1SMatthias Ringwald ds->process(ds); 184b7d596c1SMatthias Ringwald } 185b7d596c1SMatthias Ringwald } 186528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_execute: after ds check\n"); 187b7d596c1SMatthias Ringwald 188b7d596c1SMatthias Ringwald // process timers 189b7d596c1SMatthias Ringwald // pre: 0 <= tv_usec < 1000000 190b7d596c1SMatthias Ringwald while (timers) { 191b7d596c1SMatthias Ringwald gettimeofday(¤t_tv, NULL); 192*ec820d77SMatthias Ringwald ts = (btstack_timer_source_t *) timers; 193b7d596c1SMatthias Ringwald if (ts->timeout.tv_sec > current_tv.tv_sec) break; 194b7d596c1SMatthias Ringwald if (ts->timeout.tv_sec == current_tv.tv_sec && ts->timeout.tv_usec > current_tv.tv_usec) break; 195528a4a3bSMatthias Ringwald // log_info("btstack_run_loop_posix_execute: process times %x\n", (int) ts); 196b7d596c1SMatthias Ringwald 197b7d596c1SMatthias Ringwald // remove timer before processing it to allow handler to re-register with run loop 198528a4a3bSMatthias Ringwald btstack_run_loop_remove_timer(ts); 199b7d596c1SMatthias Ringwald ts->process(ts); 200b7d596c1SMatthias Ringwald } 201b7d596c1SMatthias Ringwald } 202b7d596c1SMatthias Ringwald } 203b7d596c1SMatthias Ringwald 204b7d596c1SMatthias Ringwald // set timer 205*ec820d77SMatthias Ringwald static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ 206b7d596c1SMatthias Ringwald gettimeofday(&a->timeout, NULL); 207b7d596c1SMatthias Ringwald a->timeout.tv_sec += timeout_in_ms / 1000; 208b7d596c1SMatthias Ringwald a->timeout.tv_usec += (timeout_in_ms % 1000) * 1000; 209b7d596c1SMatthias Ringwald if (a->timeout.tv_usec > 1000000) { 210b7d596c1SMatthias Ringwald a->timeout.tv_usec -= 1000000; 211b7d596c1SMatthias Ringwald a->timeout.tv_sec++; 212b7d596c1SMatthias Ringwald } 213b7d596c1SMatthias Ringwald } 214b7d596c1SMatthias Ringwald 215b7d596c1SMatthias Ringwald // compare timers - NULL is assumed to be before the Big Bang 216b7d596c1SMatthias Ringwald // pre: 0 <= tv_usec < 1000000 217528a4a3bSMatthias Ringwald static int btstack_run_loop_posix_timeval_compare(struct timeval *a, struct timeval *b){ 218b7d596c1SMatthias Ringwald if (!a && !b) return 0; 219b7d596c1SMatthias Ringwald if (!a) return -1; 220b7d596c1SMatthias Ringwald if (!b) return 1; 221b7d596c1SMatthias Ringwald 222b7d596c1SMatthias Ringwald if (a->tv_sec < b->tv_sec) { 223b7d596c1SMatthias Ringwald return -1; 224b7d596c1SMatthias Ringwald } 225b7d596c1SMatthias Ringwald if (a->tv_sec > b->tv_sec) { 226b7d596c1SMatthias Ringwald return 1; 227b7d596c1SMatthias Ringwald } 228b7d596c1SMatthias Ringwald 229b7d596c1SMatthias Ringwald if (a->tv_usec < b->tv_usec) { 230b7d596c1SMatthias Ringwald return -1; 231b7d596c1SMatthias Ringwald } 232b7d596c1SMatthias Ringwald if (a->tv_usec > b->tv_usec) { 233b7d596c1SMatthias Ringwald return 1; 234b7d596c1SMatthias Ringwald } 235b7d596c1SMatthias Ringwald 236b7d596c1SMatthias Ringwald return 0; 237b7d596c1SMatthias Ringwald 238b7d596c1SMatthias Ringwald } 239b7d596c1SMatthias Ringwald 240b7d596c1SMatthias Ringwald // compare timers - NULL is assumed to be before the Big Bang 241b7d596c1SMatthias Ringwald // pre: 0 <= tv_usec < 1000000 242*ec820d77SMatthias Ringwald static int btstack_run_loop_posix_timer_compare(btstack_timer_source_t *a, btstack_timer_source_t *b){ 243b7d596c1SMatthias Ringwald if (!a && !b) return 0; 244b7d596c1SMatthias Ringwald if (!a) return -1; 245b7d596c1SMatthias Ringwald if (!b) return 1; 246528a4a3bSMatthias Ringwald return btstack_run_loop_posix_timeval_compare(&a->timeout, &b->timeout); 247b7d596c1SMatthias Ringwald } 248b7d596c1SMatthias Ringwald 249528a4a3bSMatthias Ringwald static void btstack_run_loop_posix_init(void){ 250b7d596c1SMatthias Ringwald data_sources = NULL; 251b7d596c1SMatthias Ringwald timers = NULL; 252b7d596c1SMatthias Ringwald gettimeofday(&init_tv, NULL); 253b7d596c1SMatthias Ringwald } 254b7d596c1SMatthias Ringwald 255b7d596c1SMatthias Ringwald /** 256b7d596c1SMatthias Ringwald * @brief Queries the current time in ms since start 257b7d596c1SMatthias Ringwald */ 258528a4a3bSMatthias Ringwald static uint32_t btstack_run_loop_posix_get_time_ms(void){ 259b7d596c1SMatthias Ringwald struct timeval current_tv; 260b7d596c1SMatthias Ringwald gettimeofday(¤t_tv, NULL); 261b7d596c1SMatthias Ringwald return (current_tv.tv_sec - init_tv.tv_sec) * 1000 262b7d596c1SMatthias Ringwald + (current_tv.tv_usec - init_tv.tv_usec) / 1000; 263b7d596c1SMatthias Ringwald } 264b7d596c1SMatthias Ringwald 265b7d596c1SMatthias Ringwald 266528a4a3bSMatthias Ringwald static const btstack_run_loop_t btstack_run_loop_posix = { 267528a4a3bSMatthias Ringwald &btstack_run_loop_posix_init, 268528a4a3bSMatthias Ringwald &btstack_run_loop_posix_add_data_source, 269528a4a3bSMatthias Ringwald &btstack_run_loop_posix_remove_data_source, 270528a4a3bSMatthias Ringwald &btstack_run_loop_posix_set_timer, 271528a4a3bSMatthias Ringwald &btstack_run_loop_posix_add_timer, 272528a4a3bSMatthias Ringwald &btstack_run_loop_posix_remove_timer, 273528a4a3bSMatthias Ringwald &btstack_run_loop_posix_execute, 274528a4a3bSMatthias Ringwald &btstack_run_loop_posix_dump_timer, 275528a4a3bSMatthias Ringwald &btstack_run_loop_posix_get_time_ms, 276b7d596c1SMatthias Ringwald }; 277b7d596c1SMatthias Ringwald 278b7d596c1SMatthias Ringwald /** 279528a4a3bSMatthias Ringwald * Provide btstack_run_loop_posix instance 280b7d596c1SMatthias Ringwald */ 281528a4a3bSMatthias Ringwald const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){ 282528a4a3bSMatthias Ringwald return &btstack_run_loop_posix; 283b7d596c1SMatthias Ringwald } 284b7d596c1SMatthias Ringwald 285