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 /* 39 * btstack_run_loop.c 40 * 41 * Created by Matthias Ringwald on 6/6/09. 42 */ 43 44 #include "btstack_run_loop.h" 45 #include "btstack_run_loop_posix.h" 46 #include "btstack_linked_list.h" 47 #include "btstack_debug.h" 48 49 #ifdef _WIN32 50 #include "Winsock2.h" 51 #else 52 #include <sys/select.h> 53 #endif 54 #include <stdio.h> 55 #include <stdlib.h> 56 57 static void btstack_run_loop_posix_dump_timer(void); 58 static int btstack_run_loop_posix_timeval_compare(struct timeval *a, struct timeval *b); 59 static int btstack_run_loop_posix_timer_compare(btstack_timer_source_t *a, btstack_timer_source_t *b); 60 61 // the run loop 62 static btstack_linked_list_t data_sources; 63 static int data_sources_modified; 64 static btstack_linked_list_t timers; 65 static struct timeval init_tv; 66 67 /** 68 * Add data_source to run_loop 69 */ 70 static void btstack_run_loop_posix_add_data_source(btstack_data_source_t *ds){ 71 data_sources_modified = 1; 72 // log_info("btstack_run_loop_posix_add_data_source %x with fd %u\n", (int) ds, ds->fd); 73 btstack_linked_list_add(&data_sources, (btstack_linked_item_t *) ds); 74 } 75 76 /** 77 * Remove data_source from run loop 78 */ 79 static int btstack_run_loop_posix_remove_data_source(btstack_data_source_t *ds){ 80 data_sources_modified = 1; 81 // log_info("btstack_run_loop_posix_remove_data_source %x\n", (int) ds); 82 return btstack_linked_list_remove(&data_sources, (btstack_linked_item_t *) ds); 83 } 84 85 /** 86 * Add timer to run_loop (keep list sorted) 87 */ 88 static void btstack_run_loop_posix_add_timer(btstack_timer_source_t *ts){ 89 btstack_linked_item_t *it; 90 for (it = (btstack_linked_item_t *) &timers; it->next ; it = it->next){ 91 if ((btstack_timer_source_t *) it->next == ts){ 92 log_error( "btstack_run_loop_timer_add error: timer to add already in list!"); 93 return; 94 } 95 if (btstack_run_loop_posix_timer_compare( (btstack_timer_source_t *) it->next, ts) > 0) { 96 break; 97 } 98 } 99 ts->item.next = it->next; 100 it->next = (btstack_linked_item_t *) ts; 101 // log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 102 // btstack_run_loop_posix_dump_timer(); 103 } 104 105 /** 106 * Remove timer from run loop 107 */ 108 static int btstack_run_loop_posix_remove_timer(btstack_timer_source_t *ts){ 109 // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec); 110 // btstack_run_loop_posix_dump_timer(); 111 return btstack_linked_list_remove(&timers, (btstack_linked_item_t *) ts); 112 } 113 114 static void btstack_run_loop_posix_dump_timer(void){ 115 btstack_linked_item_t *it; 116 int i = 0; 117 for (it = (btstack_linked_item_t *) timers; it ; it = it->next){ 118 btstack_timer_source_t *ts = (btstack_timer_source_t*) it; 119 log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout.tv_sec); 120 } 121 } 122 123 static void btstack_run_loop_posix_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 124 ds->flags |= callback_types; 125 } 126 127 static void btstack_run_loop_posix_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 128 ds->flags &= ~callback_types; 129 } 130 131 /** 132 * Execute run_loop 133 */ 134 static void btstack_run_loop_posix_execute(void) { 135 fd_set descriptors_read; 136 fd_set descriptors_write; 137 138 btstack_timer_source_t *ts; 139 struct timeval current_tv; 140 struct timeval next_tv; 141 struct timeval *timeout; 142 btstack_linked_list_iterator_t it; 143 144 while (1) { 145 // collect FDs 146 FD_ZERO(&descriptors_read); 147 FD_ZERO(&descriptors_write); 148 int highest_fd = 0; 149 btstack_linked_list_iterator_init(&it, &data_sources); 150 while (btstack_linked_list_iterator_has_next(&it)){ 151 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 152 if (ds->fd < 0) continue; 153 if (ds->flags & DATA_SOURCE_CALLBACK_READ){ 154 FD_SET(ds->fd, &descriptors_read); 155 if (ds->fd > highest_fd) { 156 highest_fd = ds->fd; 157 } 158 } 159 if (ds->flags & DATA_SOURCE_CALLBACK_WRITE){ 160 FD_SET(ds->fd, &descriptors_write); 161 if (ds->fd > highest_fd) { 162 highest_fd = ds->fd; 163 } 164 } 165 } 166 167 // get next timeout 168 // pre: 0 <= tv_usec < 1000000 169 timeout = NULL; 170 if (timers) { 171 gettimeofday(¤t_tv, NULL); 172 ts = (btstack_timer_source_t *) timers; 173 next_tv.tv_usec = ts->timeout.tv_usec - current_tv.tv_usec; 174 next_tv.tv_sec = ts->timeout.tv_sec - current_tv.tv_sec; 175 while (next_tv.tv_usec < 0){ 176 next_tv.tv_usec += 1000000; 177 next_tv.tv_sec--; 178 } 179 if (next_tv.tv_sec < 0){ 180 next_tv.tv_sec = 0; 181 next_tv.tv_usec = 0; 182 } 183 timeout = &next_tv; 184 } 185 186 // wait for ready FDs 187 select( highest_fd+1 , &descriptors_read, &descriptors_write, NULL, timeout); 188 189 // process data sources very carefully 190 // bt_control.close() triggered from a client can remove a different data source 191 192 // log_info("btstack_run_loop_posix_execute: before ds check\n"); 193 data_sources_modified = 0; 194 btstack_linked_list_iterator_init(&it, &data_sources); 195 while (btstack_linked_list_iterator_has_next(&it) && !data_sources_modified){ 196 btstack_data_source_t *ds = (btstack_data_source_t*) btstack_linked_list_iterator_next(&it); 197 // log_info("btstack_run_loop_posix_execute: check %x with fd %u\n", (int) ds, ds->fd); 198 if (FD_ISSET(ds->fd, &descriptors_read)) { 199 // log_info("btstack_run_loop_posix_execute: process read %x with fd %u\n", (int) ds, ds->fd); 200 ds->process(ds, DATA_SOURCE_CALLBACK_READ); 201 } 202 if (FD_ISSET(ds->fd, &descriptors_write)) { 203 // log_info("btstack_run_loop_posix_execute: process write %x with fd %u\n", (int) ds, ds->fd); 204 ds->process(ds, DATA_SOURCE_CALLBACK_WRITE); 205 } 206 } 207 // log_info("btstack_run_loop_posix_execute: after ds check\n"); 208 209 // process timers 210 // pre: 0 <= tv_usec < 1000000 211 while (timers) { 212 gettimeofday(¤t_tv, NULL); 213 ts = (btstack_timer_source_t *) timers; 214 if (ts->timeout.tv_sec > current_tv.tv_sec) break; 215 if (ts->timeout.tv_sec == current_tv.tv_sec && ts->timeout.tv_usec > current_tv.tv_usec) break; 216 // log_info("btstack_run_loop_posix_execute: process times %x\n", (int) ts); 217 218 // remove timer before processing it to allow handler to re-register with run loop 219 btstack_run_loop_remove_timer(ts); 220 ts->process(ts); 221 } 222 } 223 } 224 225 // set timer 226 static void btstack_run_loop_posix_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ 227 gettimeofday(&a->timeout, NULL); 228 a->timeout.tv_sec += timeout_in_ms / 1000; 229 a->timeout.tv_usec += (timeout_in_ms % 1000) * 1000; 230 if (a->timeout.tv_usec > 1000000) { 231 a->timeout.tv_usec -= 1000000; 232 a->timeout.tv_sec++; 233 } 234 } 235 236 // compare timers - NULL is assumed to be before the Big Bang 237 // pre: 0 <= tv_usec < 1000000 238 static int btstack_run_loop_posix_timeval_compare(struct timeval *a, struct timeval *b){ 239 if (!a && !b) return 0; 240 if (!a) return -1; 241 if (!b) return 1; 242 243 if (a->tv_sec < b->tv_sec) { 244 return -1; 245 } 246 if (a->tv_sec > b->tv_sec) { 247 return 1; 248 } 249 250 if (a->tv_usec < b->tv_usec) { 251 return -1; 252 } 253 if (a->tv_usec > b->tv_usec) { 254 return 1; 255 } 256 257 return 0; 258 259 } 260 261 // compare timers - NULL is assumed to be before the Big Bang 262 // pre: 0 <= tv_usec < 1000000 263 static int btstack_run_loop_posix_timer_compare(btstack_timer_source_t *a, btstack_timer_source_t *b){ 264 if (!a && !b) return 0; 265 if (!a) return -1; 266 if (!b) return 1; 267 return btstack_run_loop_posix_timeval_compare(&a->timeout, &b->timeout); 268 } 269 270 static void btstack_run_loop_posix_init(void){ 271 data_sources = NULL; 272 timers = NULL; 273 gettimeofday(&init_tv, NULL); 274 } 275 276 /** 277 * @brief Queries the current time in ms since start 278 */ 279 static uint32_t btstack_run_loop_posix_get_time_ms(void){ 280 struct timeval current_tv; 281 gettimeofday(¤t_tv, NULL); 282 return (current_tv.tv_sec - init_tv.tv_sec) * 1000 283 + (current_tv.tv_usec - init_tv.tv_usec) / 1000; 284 } 285 286 287 static const btstack_run_loop_t btstack_run_loop_posix = { 288 &btstack_run_loop_posix_init, 289 &btstack_run_loop_posix_add_data_source, 290 &btstack_run_loop_posix_remove_data_source, 291 &btstack_run_loop_posix_enable_data_source_callbacks, 292 &btstack_run_loop_posix_disable_data_source_callbacks, 293 &btstack_run_loop_posix_set_timer, 294 &btstack_run_loop_posix_add_timer, 295 &btstack_run_loop_posix_remove_timer, 296 &btstack_run_loop_posix_execute, 297 &btstack_run_loop_posix_dump_timer, 298 &btstack_run_loop_posix_get_time_ms, 299 }; 300 301 /** 302 * Provide btstack_run_loop_posix instance 303 */ 304 const btstack_run_loop_t * btstack_run_loop_posix_get_instance(void){ 305 return &btstack_run_loop_posix; 306 } 307 308