blob: ae629d7c54714c391b78bb079416e0da11142cdc [file] [log] [blame]
/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation, nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <sys/time.h>
#include <string.h>
#include <pthread.h>
#include <rpc/rpc.h>
#include <loc_api_rpc_glue.h>
#include "loc_api_sync_call.h"
/* Logging */
#define LOG_TAG "LocSvc_api_rpc_glue"
// #define LOG_NDDEBUG 0
#ifndef USE_GLIB
#include <utils/Log.h>
#endif /* USE_GLIB */
/***************************************************************************
* DATA FOR ASYNCHRONOUS RPC PROCESSING
**************************************************************************/
loc_sync_call_slot_array_s_type loc_sync_data;
pthread_mutex_t loc_sync_call_mutex = PTHREAD_MUTEX_INITIALIZER;
boolean loc_sync_call_inited = 0;
/*===========================================================================
FUNCTION loc_api_sync_call_init
DESCRIPTION
Initialize this module
DEPENDENCIES
N/A
RETURN VALUE
none
SIDE EFFECTS
N/A
===========================================================================*/
void loc_api_sync_call_init()
{
pthread_mutex_lock(&loc_sync_call_mutex);
if (loc_sync_call_inited == 1) {
pthread_mutex_unlock(&loc_sync_call_mutex);
return;
}
loc_sync_call_inited = 1;
loc_sync_data.num_of_slots = LOC_SYNC_CALL_SLOTS_MAX;
int i;
for (i = 0; i < loc_sync_data.num_of_slots; i++)
{
loc_sync_call_slot_s_type *slot = &loc_sync_data.slots[i];
pthread_mutex_init(&slot->lock, NULL);
pthread_cond_init(&slot->loc_cb_arrived_cond, NULL);
slot->not_available = 0;
slot->in_use = 0;
slot->loc_handle = -1;
slot->loc_cb_wait_event_mask = 0; /* event to wait */
slot->loc_cb_received_event_mask = 0; /* received event */
}
pthread_mutex_unlock(&loc_sync_call_mutex);
}
/*===========================================================================
FUNCTION loc_api_sync_call_destroy
DESCRIPTION
Initialize this module
DEPENDENCIES
N/A
RETURN VALUE
none
SIDE EFFECTS
N/A
===========================================================================*/
void loc_api_sync_call_destroy()
{
int i;
pthread_mutex_lock(&loc_sync_call_mutex);
if (loc_sync_call_inited == 0) {
pthread_mutex_unlock(&loc_sync_call_mutex);
return;
}
loc_sync_call_inited = 0;
for (i = 0; i < loc_sync_data.num_of_slots; i++)
{
loc_sync_call_slot_s_type *slot = &loc_sync_data.slots[i];
pthread_mutex_lock(&slot->lock);
slot->not_available = 1;
pthread_mutex_unlock(&slot->lock);
pthread_cond_destroy(&slot->loc_cb_arrived_cond);
pthread_mutex_destroy(&slot->lock);
}
pthread_mutex_unlock(&loc_sync_call_mutex);
}
/*===========================================================================
FUNCTION loc_match_callback
DESCRIPTION
Checks if an awaited event has arrived
RETURN VALUE
TRUE arrived
FALSE not matching
===========================================================================*/
static boolean loc_match_callback(
rpc_loc_event_mask_type wait_mask,
rpc_loc_ioctl_e_type wait_ioctl,
rpc_loc_event_mask_type event_mask,
const rpc_loc_event_payload_u_type *callback_payload
)
{
if ((event_mask & wait_mask) == 0) return FALSE;
if (event_mask != RPC_LOC_EVENT_IOCTL_REPORT || wait_ioctl == 0 ||
( (callback_payload != NULL) &&
callback_payload->rpc_loc_event_payload_u_type_u.ioctl_report.type == wait_ioctl) )
return TRUE;
return FALSE;
}
/*===========================================================================
FUNCTION loc_api_callback_process_sync_call
DESCRIPTION
Wakes up blocked API calls to check if the needed callback has arrived
DEPENDENCIES
N/A
RETURN VALUE
none
SIDE EFFECTS
N/A
===========================================================================*/
void loc_api_callback_process_sync_call(
rpc_loc_client_handle_type loc_handle, /* handle of the client */
rpc_loc_event_mask_type loc_event, /* event mask */
const rpc_loc_event_payload_u_type* loc_event_payload /* payload */
)
{
int i;
ALOGV("loc_handle = 0x%lx, loc_event = 0x%lx", loc_handle, loc_event);
for (i = 0; i < loc_sync_data.num_of_slots; i++)
{
loc_sync_call_slot_s_type *slot = &loc_sync_data.slots[i];
pthread_mutex_lock(&slot->lock);
if (slot->in_use &&
slot->signal_sent == 0 &&
slot->loc_handle == loc_handle &&
loc_match_callback(slot->loc_cb_wait_event_mask, slot->ioctl_type, loc_event, loc_event_payload))
{
memcpy(&slot->loc_cb_received_payload, loc_event_payload, sizeof (rpc_loc_event_payload_u_type));
slot->loc_cb_received_event_mask = loc_event;
ALOGV("signal slot %d in_use %d, loc_handle 0x%lx, event_mask 0x%1x, ioctl_type %d", i, slot->in_use, slot->loc_handle, (int) slot->loc_cb_wait_event_mask, (int) slot->ioctl_type);
pthread_cond_signal(&slot->loc_cb_arrived_cond);
slot->signal_sent = 1;
pthread_mutex_unlock(&slot->lock);
break;
} else {
/* do nothing */
}
pthread_mutex_unlock(&slot->lock);
}
}
/*===========================================================================
FUNCTION loc_lock_a_slot
DESCRIPTION
Allocates a buffer slot for the synchronous API call
DEPENDENCIES
N/A
RETURN VALUE
Select ID (>=0) : successful
-1 : buffer full
SIDE EFFECTS
N/A
===========================================================================*/
static int loc_lock_a_slot()
{
int i, select_id = -1; /* no free buffer */
for (i = 0; i < loc_sync_data.num_of_slots; i++)
{
loc_sync_call_slot_s_type *slot = &loc_sync_data.slots[i];
if (pthread_mutex_trylock(&slot->lock) == EBUSY)
{
ALOGV("trylock EBUSY : %d", i);
continue;
}
if (!slot->in_use && !slot->not_available)
{
select_id = i;
/* Return from here and leave the mutex locked.
* will unlock it in loc_unlock_slot()
*/
break;
}
/* ALOGV("slot %d in_use = %d, not_available = %d : %d", i, slot->in_use, slot->not_available, i); */
pthread_mutex_unlock(&slot->lock);
}
return select_id;
}
/*===========================================================================
FUNCTION loc_unlock_slot
DESCRIPTION
Unlocks a buffer slot
DEPENDENCIES
N/A
RETURN VALUE
None
SIDE EFFECTS
N/A
===========================================================================*/
static void loc_unlock_slot(int select_id)
{
pthread_mutex_unlock(&loc_sync_data.slots[select_id].lock);
}
/*===========================================================================
FUNCTION loc_lock_slot
DESCRIPTION
Locks a specific slot that was previously locked from loc_lock_a_slot
DEPENDENCIES
N/A
RETURN VALUE
None
SIDE EFFECTS
N/A
===========================================================================*/
static void loc_lock_slot(int select_id)
{
pthread_mutex_lock(&loc_sync_data.slots[select_id].lock);
}
/*===========================================================================
FUNCTION loc_set_slot_in_use
DESCRIPTION
Sets the in_use flag of slot to true or false.
Should be called only after the slot is locked
DEPENDENCIES
N/A
RETURN VALUE
None
SIDE EFFECTS
N/A
===========================================================================*/
static void loc_set_slot_in_use(int select_id, boolean in_use)
{
loc_sync_data.slots[select_id].in_use = in_use;
if (in_use == 1)
loc_sync_data.slots[select_id].signal_sent = 0;
}
/*===========================================================================
FUNCTION loc_api_save_callback
DESCRIPTION
Selects which callback or IOCTL event to wait for.
The event_mask specifies the event(s). If it is RPC_LOC_EVENT_IOCTL_REPORT,
then ioctl_type specifies the IOCTL event.
If ioctl_type is non-zero, RPC_LOC_EVENT_IOCTL_REPORT is automatically added.
DEPENDENCIES
N/A
RETURN VALUE
Select ID (>=0) : successful
-1 : out of buffer
SIDE EFFECTS
N/A
===========================================================================*/
static void loc_api_save_callback(
int select_id, /* Selected slot */
rpc_loc_client_handle_type loc_handle, /* Client handle */
rpc_loc_event_mask_type event_mask, /* Event mask to wait for */
rpc_loc_ioctl_e_type ioctl_type /* IOCTL type to wait for */
)
{
loc_sync_call_slot_s_type *slot = &loc_sync_data.slots[select_id];
slot->loc_handle = loc_handle;
slot->loc_cb_wait_event_mask = event_mask;
slot->ioctl_type = ioctl_type;
if (ioctl_type) slot->loc_cb_wait_event_mask |= RPC_LOC_EVENT_IOCTL_REPORT;
return;
}
/*===========================================================================
FUNCTION loc_save_user_payload
DESCRIPTION
Saves received payload into user data structures
RETURN VALUE
None
===========================================================================*/
static void loc_save_user_payload(
rpc_loc_event_payload_u_type *user_cb_payload,
rpc_loc_ioctl_callback_s_type *user_ioctl_buffer,
const rpc_loc_event_payload_u_type *received_cb_payload
)
{
if (user_cb_payload)
{
memcpy(user_cb_payload, received_cb_payload,
sizeof (rpc_loc_event_payload_u_type));
}
if (user_ioctl_buffer)
{
memcpy(user_ioctl_buffer,
&received_cb_payload->rpc_loc_event_payload_u_type_u.ioctl_report,
sizeof *user_ioctl_buffer);
}
}
/*===========================================================================
FUNCTION loc_api_wait_callback
DESCRIPTION
Waits for a selected callback. The wait expires in timeout_seconds seconds.
If the function is called before an existing wait has finished, it will
immediately return EBUSY.
DEPENDENCIES
N/A
RETURN VALUE
RPC_LOC_API_SUCCESS if successful (0)
RPC_LOC_API_TIMEOUT if timed out
RPC_LOC_API_ENGINE_BUSY if already in a wait
RPC_LOC_API_INVALID_PARAMETER if callback is not yet selected
SIDE EFFECTS
N/A
===========================================================================*/
static int loc_api_wait_callback(
int select_id, /* ID from loc_select_callback() */
int timeout_seconds, /* Timeout in this number of seconds */
rpc_loc_event_payload_u_type *callback_payload, /* Pointer to callback payload buffer, can be NULL */
rpc_loc_ioctl_callback_s_type *ioctl_payload /* Pointer to IOCTL payload, can be NULL */
)
{
int ret_val = RPC_LOC_API_SUCCESS; /* the return value of this function: 0 = no error */
int rc = 0; /* return code from pthread calls */
struct timespec expire_time;
loc_sync_call_slot_s_type *slot = &loc_sync_data.slots[select_id];
clock_gettime(CLOCK_REALTIME, &expire_time);
expire_time.tv_sec += timeout_seconds;
/* Waiting */
while (slot->signal_sent == 0 && rc != ETIMEDOUT) {
rc = pthread_cond_timedwait(&slot->loc_cb_arrived_cond,
&slot->lock, &expire_time);
}
if (rc == ETIMEDOUT)
{
ret_val = RPC_LOC_API_TIMEOUT; /* Timed out */
ALOGE("TIMEOUT: %d", select_id);
}
else {
/* Obtained the first awaited callback */
ret_val = RPC_LOC_API_SUCCESS; /* Successful */
loc_save_user_payload(callback_payload, ioctl_payload, &slot->loc_cb_received_payload);
}
return ret_val;
}
/*===========================================================================
FUNCTION loc_api_sync_ioctl
DESCRIPTION
Synchronous IOCTL call (reentrant version)
DEPENDENCIES
N/A
RETURN VALUE
Loc API error code (0 = success)
SIDE EFFECTS
N/A
===========================================================================*/
int loc_api_sync_ioctl
(
rpc_loc_client_handle_type handle,
rpc_loc_ioctl_e_type ioctl_type,
rpc_loc_ioctl_data_u_type* ioctl_data_ptr,
uint32 timeout_msec,
rpc_loc_ioctl_callback_s_type *cb_data_ptr
)
{
int rc = -1;
int select_id;
rpc_loc_ioctl_callback_s_type callback_data;
select_id = loc_lock_a_slot();
if (select_id < 0 || select_id >= loc_sync_data.num_of_slots)
{
ALOGE("slot not available ioctl_type = %s",
loc_get_ioctl_type_name(ioctl_type));
return rc;
}
loc_set_slot_in_use(select_id, 1); // set slot in use to true
// Select the callback we are waiting for
loc_api_save_callback(select_id, handle, 0, ioctl_type);
loc_unlock_slot(select_id); // slot is unlocked, but in_use is still true
// we want to avoid keeping the slot locked during the loc_ioctl because the rpc
// framework will also lock a different mutex during this call, and typically
// locking two different mutexes at the same time can lead to deadlock.
rc = loc_ioctl(handle, ioctl_type, ioctl_data_ptr);
loc_lock_slot(select_id);
if (rc != RPC_LOC_API_SUCCESS)
{
ALOGE("loc_ioctl failed select_id = %d, ioctl_type %s, returned %s",
select_id, loc_get_ioctl_type_name(ioctl_type), loc_get_ioctl_status_name(rc));
}
else {
ALOGV("select_id = %d, ioctl_type %d, returned RPC_LOC_API_SUCCESS",
select_id, ioctl_type);
// Wait for the callback of loc_ioctl
if ((rc = loc_api_wait_callback(select_id, timeout_msec / 1000, NULL, &callback_data)) != 0)
{
// Callback waiting failed
ALOGE("callback wait failed select_id = %d, ioctl_type %s, returned %s",
select_id, loc_get_ioctl_type_name(ioctl_type), loc_get_ioctl_status_name(rc));
}
else
{
if (cb_data_ptr) memcpy(cb_data_ptr, &callback_data, sizeof *cb_data_ptr);
if (callback_data.status != RPC_LOC_API_SUCCESS)
{
rc = callback_data.status;
ALOGE("callback status failed select_id = %d, ioctl_type %s, returned %s",
select_id, loc_get_ioctl_type_name(ioctl_type), loc_get_ioctl_status_name(rc));
} else {
ALOGV("callback status success select_id = %d, ioctl_type %d, returned %d",
select_id, ioctl_type, rc);
}
} /* wait callback */
} /* loc_ioctl */
loc_set_slot_in_use(select_id, 0); // set slot in use to false
loc_unlock_slot(select_id);
return rc;
}