1 /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions are 5 * met: 6 * * Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * * Redistributions in binary form must reproduce the above 9 * copyright notice, this list of conditions and the following 10 * disclaimer in the documentation and/or other materials provided 11 * with the distribution. 12 * * Neither the name of The Linux Foundation, nor the names of its 13 * contributors may be used to endorse or promote products derived 14 * from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 #include <stdio.h> 29 #include <assert.h> 30 #include <errno.h> 31 #include <sys/time.h> 32 #include <string.h> 33 #include <pthread.h> 34 #include <stdbool.h> 35 #include <stdint.h> 36 #include <loc_cfg.h> 37 #include "loc_api_v02_client.h" 38 #include "loc_api_sync_req.h" 39 40 /* Logging */ 41 // Uncomment to log verbose logs 42 #define LOG_NDEBUG 1 43 44 // log debug logs 45 #define LOG_NDDEBUG 1 46 #define LOG_TAG "LocSvc_api_v02" 47 #include "loc_util_log.h" 48 49 #define LOC_SYNC_REQ_BUFFER_SIZE 8 50 #define GPS_CONF_FILE "/etc/gps.conf" 51 pthread_mutex_t loc_sync_call_mutex = PTHREAD_MUTEX_INITIALIZER; 52 53 static bool loc_sync_call_initialized = false; 54 55 typedef struct { 56 pthread_mutex_t sync_req_lock; 57 58 /* Client ID */ 59 locClientHandleType client_handle; 60 61 /* waiting conditional variable */ 62 pthread_cond_t ind_arrived_cond; 63 64 /* Callback waiting data block, protected by loc_cb_data_mutex */ 65 bool ind_is_selected; /* is cb selected? */ 66 bool ind_is_waiting; /* is waiting? */ 67 bool ind_has_arrived; /* callback has arrived */ 68 uint32_t req_id; /* sync request */ 69 void *recv_ind_payload_ptr; /* received payload */ 70 uint32_t recv_ind_id; /* received ind */ 71 72 } loc_sync_req_data_s_type; 73 74 typedef struct { 75 bool in_use; /* at least one sync call is active */ 76 bool slot_in_use[LOC_SYNC_REQ_BUFFER_SIZE]; 77 loc_sync_req_data_s_type slots[LOC_SYNC_REQ_BUFFER_SIZE]; 78 } loc_sync_req_array_s_type; 79 80 /*************************************************************************** 81 * DATA FOR ASYNCHRONOUS RPC PROCESSING 82 **************************************************************************/ 83 loc_sync_req_array_s_type loc_sync_array; 84 85 /*=========================================================================== 86 87 FUNCTION loc_sync_req_init 88 89 DESCRIPTION 90 Initialize this module 91 92 DEPENDENCIES 93 N/A 94 95 RETURN VALUE 96 none 97 98 SIDE EFFECTS 99 N/A 100 101 ===========================================================================*/ 102 void loc_sync_req_init() 103 { 104 LOC_LOGV(" %s:%d]:\n", __func__, __LINE__); 105 UTIL_READ_CONF_DEFAULT(GPS_CONF_FILE); 106 pthread_mutex_lock(&loc_sync_call_mutex); 107 if(true == loc_sync_call_initialized) 108 { 109 LOC_LOGD("%s:%d]:already initialized\n", __func__, __LINE__); 110 pthread_mutex_unlock(&loc_sync_call_mutex); 111 return; 112 } 113 114 loc_sync_array.in_use = false; 115 116 memset(loc_sync_array.slot_in_use, 0, sizeof(loc_sync_array.slot_in_use)); 117 118 int i; 119 for (i = 0; i < LOC_SYNC_REQ_BUFFER_SIZE; i++) 120 { 121 loc_sync_req_data_s_type *slot = &loc_sync_array.slots[i]; 122 123 pthread_mutex_init(&slot->sync_req_lock, NULL); 124 pthread_cond_init(&slot->ind_arrived_cond, NULL); 125 126 slot->client_handle = LOC_CLIENT_INVALID_HANDLE_VALUE; 127 slot->ind_is_selected = false; /* is ind selected? */ 128 slot->ind_is_waiting = false; /* is waiting? */ 129 slot->ind_has_arrived = false; /* callback has arrived */ 130 slot->recv_ind_id = 0; /* ind to wait for */ 131 slot->recv_ind_payload_ptr = NULL; 132 slot->req_id = 0; /* req id */ 133 } 134 135 loc_sync_call_initialized = true; 136 pthread_mutex_unlock(&loc_sync_call_mutex); 137 } 138 139 140 /*=========================================================================== 141 142 FUNCTION loc_sync_process_ind 143 144 DESCRIPTION 145 Wakes up blocked API calls to check if the needed callback has arrived 146 147 DEPENDENCIES 148 N/A 149 150 RETURN VALUE 151 none 152 153 SIDE EFFECTS 154 N/A 155 156 ===========================================================================*/ 157 void loc_sync_process_ind( 158 locClientHandleType client_handle, /* handle of the client */ 159 uint32_t ind_id , /* ind id */ 160 void *ind_payload_ptr /* payload */ 161 ) 162 { 163 164 LOC_LOGV("%s:%d]: received indication, handle = %p ind_id = %u \n", 165 __func__,__LINE__, client_handle, ind_id); 166 167 pthread_mutex_lock(&loc_sync_call_mutex); 168 169 if (!loc_sync_array.in_use) 170 { 171 LOC_LOGD("%s:%d]: loc_sync_array not in use \n", 172 __func__, __LINE__); 173 pthread_mutex_unlock(&loc_sync_call_mutex); 174 return; 175 } 176 177 bool in_use = false, consumed = false; 178 int i; 179 180 for (i = 0; i < LOC_SYNC_REQ_BUFFER_SIZE && !consumed; i++) 181 { 182 loc_sync_req_data_s_type *slot = &loc_sync_array.slots[i]; 183 184 in_use |= loc_sync_array.slot_in_use[i]; 185 186 pthread_mutex_lock(&slot->sync_req_lock); 187 188 if ( (loc_sync_array.slot_in_use[i]) && (slot->client_handle == client_handle) 189 && (ind_id == slot->recv_ind_id) && (!slot->ind_has_arrived)) 190 { 191 // copy the payload to the slot waiting for this ind 192 size_t payload_size = 0; 193 194 LOC_LOGV("%s:%d]: found slot %d selected for ind %u \n", 195 __func__, __LINE__, i, ind_id); 196 197 if(true == locClientGetSizeByRespIndId(ind_id, &payload_size) && 198 NULL != slot->recv_ind_payload_ptr && NULL != ind_payload_ptr) 199 { 200 LOC_LOGV("%s:%d]: copying ind payload size = %u \n", 201 __func__, __LINE__, payload_size); 202 203 memcpy(slot->recv_ind_payload_ptr, ind_payload_ptr, payload_size); 204 205 consumed = true; 206 207 } 208 /* Received a callback while waiting, wake up thread to check it */ 209 if (slot->ind_is_waiting) 210 { 211 slot->recv_ind_id = ind_id; 212 213 pthread_cond_signal(&slot->ind_arrived_cond); 214 } 215 else 216 { 217 /* If callback arrives before wait, remember it */ 218 LOC_LOGV("%s:%d]: ind %u arrived before wait was called \n", 219 __func__, __LINE__, ind_id); 220 221 slot->ind_has_arrived = true; 222 } 223 } 224 pthread_mutex_unlock(&slot->sync_req_lock); 225 } 226 227 if (!in_use) { 228 loc_sync_array.in_use = false; 229 } 230 231 pthread_mutex_unlock(&loc_sync_call_mutex); 232 } 233 234 /*=========================================================================== 235 236 FUNCTION loc_alloc_slot 237 238 DESCRIPTION 239 Allocates a buffer slot for the synchronous API call 240 241 DEPENDENCIES 242 N/A 243 244 RETURN VALUE 245 Select ID (>=0) : successful 246 -1 : buffer full 247 248 SIDE EFFECTS 249 N/A 250 251 ===========================================================================*/ 252 static int loc_alloc_slot() 253 { 254 int i, select_id = -1; /* no free buffer */ 255 256 pthread_mutex_lock(&loc_sync_call_mutex); 257 258 for (i = 0; i < LOC_SYNC_REQ_BUFFER_SIZE; i++) 259 { 260 if (!loc_sync_array.slot_in_use[i]) 261 { 262 select_id = i; 263 loc_sync_array.slot_in_use[i] = 1; 264 loc_sync_array.in_use = true; 265 break; 266 } 267 } 268 269 pthread_mutex_unlock(&loc_sync_call_mutex); 270 LOC_LOGV("%s:%d]: returning slot %d\n", 271 __func__, __LINE__, select_id); 272 return select_id; 273 } 274 275 /*=========================================================================== 276 277 FUNCTION loc_free_slot 278 279 DESCRIPTION 280 Frees a buffer slot after the synchronous API call 281 282 DEPENDENCIES 283 N/A 284 285 RETURN VALUE 286 None 287 288 SIDE EFFECTS 289 N/A 290 291 ===========================================================================*/ 292 static void loc_free_slot(int select_id) 293 { 294 int i; 295 loc_sync_req_data_s_type *slot; 296 297 pthread_mutex_lock(&loc_sync_call_mutex); 298 299 LOC_LOGD("%s:%d]: freeing slot %d\n", __func__, __LINE__, select_id); 300 301 loc_sync_array.slot_in_use[select_id] = 0; 302 303 slot = &loc_sync_array.slots[select_id]; 304 305 slot->client_handle = LOC_CLIENT_INVALID_HANDLE_VALUE; 306 slot->ind_is_selected = false; /* is ind selected? */ 307 slot->ind_is_waiting = false; /* is waiting? */ 308 slot->ind_has_arrived = false; /* callback has arrived */ 309 slot->recv_ind_id = 0; /* ind to wait for */ 310 slot->recv_ind_payload_ptr = NULL; 311 slot->req_id = 0; 312 313 // check if all slots are now free 314 for (i = 0; i < LOC_SYNC_REQ_BUFFER_SIZE; i++) 315 { 316 if (loc_sync_array.slot_in_use[i]) break; 317 } 318 319 if (i >= LOC_SYNC_REQ_BUFFER_SIZE) 320 { 321 loc_sync_array.in_use = false; 322 } 323 324 pthread_mutex_unlock(&loc_sync_call_mutex); 325 } 326 327 /*=========================================================================== 328 329 FUNCTION loc_sync_select_ind 330 331 DESCRIPTION 332 Selects which indication to wait for. 333 334 335 DEPENDENCIES 336 N/A 337 338 RETURN VALUE 339 Select ID (>=0) : successful 340 -ENOMEM : out of buffer 341 342 SIDE EFFECTS 343 N/A 344 345 ===========================================================================*/ 346 static int loc_sync_select_ind( 347 locClientHandleType client_handle, /* Client handle */ 348 uint32_t ind_id, /* ind Id wait for */ 349 uint32_t req_id, /* req id */ 350 void * ind_payload_ptr /* ptr where payload should be copied to*/ 351 ) 352 { 353 int select_id = loc_alloc_slot(); 354 355 LOC_LOGV("%s:%d]: client handle %p, ind_id %u, req_id %u \n", 356 __func__, __LINE__, client_handle, ind_id, req_id); 357 358 if (select_id < 0) 359 { 360 LOC_LOGE("%s:%d]: buffer full for this synchronous req %s \n", 361 __func__, __LINE__, loc_get_v02_event_name(req_id)); 362 return -ENOMEM; 363 } 364 365 loc_sync_req_data_s_type *slot = &loc_sync_array.slots[select_id]; 366 367 pthread_mutex_lock(&slot->sync_req_lock); 368 369 slot->client_handle = client_handle; 370 slot->ind_is_selected = true; 371 slot->ind_is_waiting = false; 372 slot->ind_has_arrived = false; 373 374 slot->recv_ind_id = ind_id; 375 slot->req_id = req_id; 376 slot->recv_ind_payload_ptr = ind_payload_ptr; //store the payload ptr 377 378 pthread_mutex_unlock(&slot->sync_req_lock); 379 380 return select_id; 381 } 382 383 384 /*=========================================================================== 385 386 FUNCTION loc_sync_wait_for_ind 387 388 DESCRIPTION 389 Waits for a selected indication. The wait expires in timeout_seconds seconds. 390 If the function is called before an existing wait has finished, it will 391 immediately return error. 392 393 DEPENDENCIES 394 N/A 395 396 RETURN VALUE 397 0 on SUCCESS, -ve value on failure 398 399 SIDE EFFECTS 400 N/A 401 402 ===========================================================================*/ 403 static int loc_sync_wait_for_ind( 404 int select_id, /* ID from loc_sync_select_ind() */ 405 int timeout_seconds, /* Timeout in this number of seconds */ 406 uint32_t ind_id 407 ) 408 { 409 if (select_id < 0 || select_id >= LOC_SYNC_REQ_BUFFER_SIZE || !loc_sync_array.slot_in_use[select_id]) 410 { 411 LOC_LOGE("%s:%d]: invalid select_id: %d \n", 412 __func__, __LINE__, select_id); 413 414 return (-EINVAL); 415 } 416 417 loc_sync_req_data_s_type *slot = &loc_sync_array.slots[select_id]; 418 419 int ret_val = 0; /* the return value of this function: 0 = no error */ 420 int rc; /* return code from pthread calls */ 421 422 struct timeval present_time; 423 struct timespec expire_time; 424 425 pthread_mutex_lock(&slot->sync_req_lock); 426 427 do 428 { 429 if (slot->ind_has_arrived) 430 { 431 ret_val = 0; /* success */ 432 break; 433 } 434 435 if (slot->ind_is_waiting) 436 { 437 LOC_LOGW("%s:%d]: already waiting in this slot %d\n", __func__, 438 __LINE__, select_id); 439 ret_val = -EBUSY; // busy 440 break; 441 } 442 443 /* Calculate absolute expire time */ 444 gettimeofday(&present_time, NULL); 445 expire_time.tv_sec = present_time.tv_sec; 446 expire_time.tv_nsec = present_time.tv_usec * 1000; 447 expire_time.tv_sec += timeout_seconds; 448 449 /* Take new wait request */ 450 slot->ind_is_waiting = true; 451 452 /* Waiting */ 453 rc = pthread_cond_timedwait(&slot->ind_arrived_cond, 454 &slot->sync_req_lock, &expire_time); 455 456 slot->ind_is_waiting = false; 457 458 if(rc == ETIMEDOUT) 459 { 460 LOC_LOGE("%s:%d]: slot %d, timed out for ind_id %s\n", 461 __func__, __LINE__, select_id, loc_get_v02_event_name(ind_id)); 462 ret_val = -ETIMEDOUT; //time out 463 } 464 465 } while (0); 466 467 pthread_mutex_unlock(&slot->sync_req_lock); 468 loc_free_slot(select_id); 469 470 return ret_val; 471 } 472 473 /*=========================================================================== 474 475 FUNCTION loc_sync_send_req 476 477 DESCRIPTION 478 Synchronous req call (thread safe) 479 480 DEPENDENCIES 481 N/A 482 483 RETURN VALUE 484 Loc API 2.0 status 485 486 SIDE EFFECTS 487 N/A 488 489 ===========================================================================*/ 490 locClientStatusEnumType loc_sync_send_req 491 ( 492 locClientHandleType client_handle, 493 uint32_t req_id, /* req id */ 494 locClientReqUnionType req_payload, 495 uint32_t timeout_msec, 496 uint32_t ind_id, //ind ID to block for, usually the same as req_id */ 497 void *ind_payload_ptr /* can be NULL*/ 498 ) 499 { 500 locClientStatusEnumType status = eLOC_CLIENT_SUCCESS ; 501 int select_id; 502 int rc = 0; 503 504 // Select the callback we are waiting for 505 select_id = loc_sync_select_ind(client_handle, ind_id, req_id, 506 ind_payload_ptr); 507 508 if (select_id >= 0) 509 { 510 status = locClientSendReq (client_handle, req_id, req_payload); 511 LOC_LOGV("%s:%d]: select_id = %d,locClientSendReq returned %d\n", 512 __func__, __LINE__, select_id, status); 513 514 if (status != eLOC_CLIENT_SUCCESS ) 515 { 516 loc_free_slot(select_id); 517 } 518 else 519 { 520 // Wait for the indication callback 521 if (( rc = loc_sync_wait_for_ind( select_id, 522 timeout_msec / 1000, 523 ind_id) ) < 0) 524 { 525 if ( rc == -ETIMEDOUT) 526 status = eLOC_CLIENT_FAILURE_TIMEOUT; 527 else 528 status = eLOC_CLIENT_FAILURE_INTERNAL; 529 530 // Callback waiting failed 531 LOC_LOGE("%s:%d]: loc_api_wait_for_ind failed, err %d, " 532 "select id %d, status %s", __func__, __LINE__, rc , 533 select_id, loc_get_v02_client_status_name(status)); 534 } 535 else 536 { 537 status = eLOC_CLIENT_SUCCESS; 538 LOC_LOGV("%s:%d]: success (select id %d)\n", 539 __func__, __LINE__, select_id); 540 } 541 } 542 } /* select id */ 543 544 return status; 545 } 546 547 548