1/* 2 * Copyright (C) 2009-2012 by Matthias Ringwald 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 MATTHIAS RINGWALD 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 [email protected] 34 * 35 */ 36 37#define __BTSTACK_FILE__ "btstack_run_loop_corefoundation.c" 38 39/* 40 * btstack_run_loop_corefoundation.c 41 * 42 * Created by Matthias Ringwald on 8/2/09. 43 */ 44 45#include "btstack_run_loop.h" 46#include "btstack_debug.h" 47 48#import <Foundation/Foundation.h> 49#import <CoreFoundation/CoreFoundation.h> 50 51#include <stdio.h> 52#include <stdlib.h> 53#include <sys/time.h> 54#include <unistd.h> 55#include <pthread.h> 56 57static struct timeval init_tv; 58static CFAbsoluteTime init_cf; 59static CFRunLoopRef current_runloop; 60 61// to trigger run loop from other thread 62static int run_loop_pipe_fd; 63static btstack_data_source_t run_loop_pipe_ds; 64static pthread_mutex_t run_loop_callback_mutex = PTHREAD_MUTEX_INITIALIZER; 65 66typedef struct { 67 CFSocketRef socket; 68 CFRunLoopSourceRef socket_run_loop; 69} btstack_corefoundation_data_source_helper_t; 70 71static void theCFRunLoopTimerCallBack (CFRunLoopTimerRef timer,void *info){ 72 btstack_timer_source_t * ts = (btstack_timer_source_t*)info; 73 ts->process(ts); 74} 75 76static void socketDataCallback ( 77 CFSocketRef s, 78 CFSocketCallBackType callbackType, 79 CFDataRef address, 80 const void *data, 81 void *info) { 82 if (!info) return; 83 btstack_data_source_t * ds = (btstack_data_source_t *) info; 84 85 if ((callbackType == kCFSocketReadCallBack) && (ds->flags & DATA_SOURCE_CALLBACK_READ)){ 86 // printf("btstack_run_loop_corefoundation_ds %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) ds, ds->source.fd, (int) s, (int) ds->item.next); 87 ds->process(ds, DATA_SOURCE_CALLBACK_READ); 88 } 89 if ((callbackType == kCFSocketWriteCallBack) && (ds->flags & DATA_SOURCE_CALLBACK_WRITE)){ 90 // printf("btstack_run_loop_corefoundation_ds %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) ds, ds->source.fd, (int) s, (int) ds->item.next); 91 ds->process(ds, DATA_SOURCE_CALLBACK_WRITE); 92 } 93} 94 95static CFOptionFlags btstack_run_loop_corefoundation_option_flags_for_callback_types(uint16_t callback_types){ 96 uint16_t option_flags = 0; 97 if (callback_types & DATA_SOURCE_CALLBACK_READ){ 98 option_flags |= kCFSocketReadCallBack; 99 } 100 if (callback_types & DATA_SOURCE_CALLBACK_WRITE){ 101 option_flags |= kCFSocketWriteCallBack; 102 } 103 return option_flags; 104} 105 106static void btstack_run_loop_corefoundation_add_data_source(btstack_data_source_t *data_source){ 107 108 // add fd as CFSocket 109 110 // allocate memory for Core Foundation references 111 btstack_corefoundation_data_source_helper_t * references = malloc(sizeof(btstack_corefoundation_data_source_helper_t)); 112 if (!references){ 113 log_error("btstack_run_loop_corefoundation_add_data_source could not allocate btstack_corefoundation_data_source_helper_t"); 114 return; 115 } 116 117 // store our data_source in socket context 118 CFSocketContext socketContext; 119 memset(&socketContext, 0, sizeof(CFSocketContext)); 120 socketContext.info = data_source; 121 122 // create CFSocket from file descriptor 123 uint16_t callback_types = btstack_run_loop_corefoundation_option_flags_for_callback_types(data_source->flags); 124 CFSocketRef socket = CFSocketCreateWithNative ( 125 kCFAllocatorDefault, 126 data_source->source.fd, 127 callback_types, 128 socketDataCallback, 129 &socketContext 130 ); 131 132 // configure options: 133 // - don't close native fd on CFSocketInvalidate 134 CFOptionFlags socket_options = CFSocketGetSocketFlags(socket); 135 socket_options &= ~kCFSocketCloseOnInvalidate; 136 CFSocketSetSocketFlags(socket, socket_options); 137 138 // create run loop source 139 CFRunLoopSourceRef socket_run_loop = CFSocketCreateRunLoopSource ( kCFAllocatorDefault, socket, 0); 140 141 // store CFSocketRef and CFRunLoopSource in struct on heap 142 memset(references, 0, sizeof(btstack_corefoundation_data_source_helper_t)); 143 references->socket = socket; 144 references->socket_run_loop = socket_run_loop; 145 146 // hack: store btstack_corefoundation_data_source_helper_t in "next" of btstack_linked_item_t 147 data_source->item.next = (void *) references; 148 149 // add to run loop 150 CFRunLoopAddSource( CFRunLoopGetCurrent(), socket_run_loop, kCFRunLoopCommonModes); 151 // printf("btstack_run_loop_corefoundation_add_data_source %x - fd %u - CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->source.fd, (int) socket, (int) socket_run_loop); 152 153} 154 155static void btstack_run_loop_corefoundation_enable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 156 btstack_corefoundation_data_source_helper_t * references = (btstack_corefoundation_data_source_helper_t *) ds->item.next; 157 uint16_t option_flags = btstack_run_loop_corefoundation_option_flags_for_callback_types(callback_types); 158 CFSocketEnableCallBacks(references->socket, option_flags); 159} 160 161static void btstack_run_loop_corefoundation_disable_data_source_callbacks(btstack_data_source_t * ds, uint16_t callback_types){ 162 btstack_corefoundation_data_source_helper_t * references = (btstack_corefoundation_data_source_helper_t *) ds->item.next; 163 uint16_t option_flags = btstack_run_loop_corefoundation_option_flags_for_callback_types(callback_types); 164 CFSocketDisableCallBacks(references->socket, option_flags); 165} 166 167static bool btstack_run_loop_corefoundation_remove_data_source(btstack_data_source_t *ds){ 168 btstack_corefoundation_data_source_helper_t * references = (btstack_corefoundation_data_source_helper_t *) ds->item.next; 169 // printf("btstack_run_loop_corefoundation_remove_data_source %x - fd %u, CFSocket %x, CFRunLoopSource %x\n", (int) dataSource, dataSource->source.fd, (int) dataSource->item.next, (int) dataSource->item.user_data); 170 CFRunLoopRemoveSource( CFRunLoopGetCurrent(), references->socket_run_loop, kCFRunLoopCommonModes); 171 CFRelease(references->socket_run_loop); 172 173 CFSocketInvalidate(references->socket); 174 CFRelease(references->socket); 175 176 free(references); 177 return true; 178} 179 180static void btstack_run_loop_corefoundation_add_timer(btstack_timer_source_t * ts) 181{ 182 CFAbsoluteTime fireDate = init_cf + ts->timeout; 183 CFRunLoopTimerContext timerContext = {0, ts, NULL, NULL, NULL}; 184 CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate (kCFAllocatorDefault,fireDate,0,0,0,theCFRunLoopTimerCallBack,&timerContext); 185 CFRetain(timerRef); 186 187 // hack: store CFRunLoopTimerRef in next pointer of linked_item 188 ts->item.next = (void *)timerRef; 189 // printf("btstack_run_loop_corefoundation_add_timer %x -> %x now %f, then %f\n", (int) ts, (int) ts->item.next, CFAbsoluteTimeGetCurrent(),fireDate); 190 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerRef, kCFRunLoopCommonModes); 191} 192 193static bool btstack_run_loop_corefoundation_remove_timer(btstack_timer_source_t * ts){ 194 // printf("btstack_run_loop_corefoundation_remove_timer %x -> %x\n", (int) ts, (int) ts->item.next); 195 if (ts->item.next != NULL) { 196 CFRunLoopTimerInvalidate((CFRunLoopTimerRef) ts->item.next); // also removes timer from run loops + releases it 197 CFRelease((CFRunLoopTimerRef) ts->item.next); 198 return true; 199 } 200 return false; 201} 202 203/** 204 * @brief Queries the current time in ms since start 205 */ 206static uint32_t btstack_run_loop_corefoundation_get_time_ms(void){ 207 struct timeval tv; 208 gettimeofday(&tv, NULL); 209 uint32_t time_ms = ((tv.tv_sec - init_tv.tv_sec) * 1000) + (tv.tv_usec / 1000); 210 log_debug("btstack_run_loop_corefoundation_get_time_ms: %u <- %u / %u", time_ms, (int) tv.tv_sec, (int) tv.tv_usec); 211 return time_ms; 212} 213 214// set timer 215static void btstack_run_loop_corefoundation_set_timer(btstack_timer_source_t *a, uint32_t timeout_in_ms){ 216 uint32_t time_ms = btstack_run_loop_corefoundation_get_time_ms(); 217 a->timeout = time_ms + timeout_in_ms; 218 log_debug("btstack_run_loop_corefoundation_set_timer to %u ms (now %u, timeout %u)", a->timeout, time_ms, timeout_in_ms); 219} 220 221static void btstack_run_loop_corefoundation_pipe_process(btstack_data_source_t * ds, btstack_data_source_callback_type_t callback_type){ 222 UNUSED(callback_type); 223 uint8_t buffer[1]; 224 (void) read(ds->source.fd, buffer, 1); 225 // execute callbacks - protect list with mutex 226 while (1){ 227 pthread_mutex_lock(&run_loop_callback_mutex); 228 btstack_context_callback_registration_t * callback_registration = (btstack_context_callback_registration_t *) btstack_linked_list_pop(&btstack_run_loop_base_callbacks); 229 pthread_mutex_unlock(&run_loop_callback_mutex); 230 if (callback_registration == NULL){ 231 break; 232 } 233 (*callback_registration->callback)(callback_registration->context); 234 } 235} 236 237static void btstack_run_loop_corefoundation_execute_on_main_thread(btstack_context_callback_registration_t * callback_registration){ 238 // protect list with mutex 239 pthread_mutex_lock(&run_loop_callback_mutex); 240 btstack_run_loop_base_add_callback(callback_registration); 241 pthread_mutex_unlock(&run_loop_callback_mutex); 242 // trigger run loop 243 if (run_loop_pipe_fd >= 0){ 244 const uint8_t x = (uint8_t) 'x'; 245 (void) write(run_loop_pipe_fd, &x, 1); 246 } 247} 248 249static void btstack_run_loop_corefoundation_init(void){ 250 gettimeofday(&init_tv, NULL); 251 // calc CFAbsoluteTime for init_tv 252 // note: timeval uses unix time: seconds since Jan 1st 1970, CF uses Jan 1st 2001 as reference date 253 init_cf = ((double)init_tv.tv_sec) + (((double)init_tv.tv_usec)/1000000.0) - kCFAbsoluteTimeIntervalSince1970; // unix time - since Jan 1st 1970 254 255 // create pipe and register as data source 256 int fildes[2]; 257 int status = pipe(fildes); 258 if (status == 0 ) { 259 run_loop_pipe_fd = fildes[1]; 260 run_loop_pipe_ds.source.fd = fildes[0]; 261 run_loop_pipe_ds.flags = DATA_SOURCE_CALLBACK_READ; 262 run_loop_pipe_ds.process = &btstack_run_loop_corefoundation_pipe_process; 263 btstack_run_loop_base_add_data_source(&run_loop_pipe_ds); 264 log_info("Pipe in %u, out %u", run_loop_pipe_fd, fildes[0]); 265 } else { 266 run_loop_pipe_fd = -1; 267 log_error("pipe() failed"); 268 } 269} 270 271static void btstack_run_loop_corefoundation_execute(void) { 272 current_runloop = CFRunLoopGetCurrent(); 273 CFRunLoopRun(); 274} 275 276static void btstack_run_loop_corefoundation_trigger_exit(void){ 277 if (current_runloop != NULL){ 278 CFRunLoopStop(current_runloop); 279 current_runloop = NULL; 280 } 281} 282static void btstack_run_loop_corefoundation_dump_timer(void){ 283 log_error("WARNING: btstack_run_loop_dump_timer not implemented!"); 284 return; 285} 286 287static const btstack_run_loop_t btstack_run_loop_corefoundation = { 288 &btstack_run_loop_corefoundation_init, 289 &btstack_run_loop_corefoundation_add_data_source, 290 &btstack_run_loop_corefoundation_remove_data_source, 291 &btstack_run_loop_corefoundation_enable_data_source_callbacks, 292 &btstack_run_loop_corefoundation_disable_data_source_callbacks, 293 &btstack_run_loop_corefoundation_set_timer, 294 &btstack_run_loop_corefoundation_add_timer, 295 &btstack_run_loop_corefoundation_remove_timer, 296 &btstack_run_loop_corefoundation_execute, 297 &btstack_run_loop_corefoundation_dump_timer, 298 &btstack_run_loop_corefoundation_get_time_ms, 299 NULL, /* poll data sources from irq */ 300 &btstack_run_loop_corefoundation_execute_on_main_thread, 301 &btstack_run_loop_corefoundation_trigger_exit, 302}; 303 304/** 305 * Provide btstack_run_loop_corefoundation instance 306 */ 307const btstack_run_loop_t * btstack_run_loop_corefoundation_get_instance(void){ 308 return &btstack_run_loop_corefoundation; 309} 310