2012-12-17 20:15:23 -05:00

708 lines
17 KiB
C
Executable File

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "cpr_types.h"
#include "cpr_stdlib.h"
#include "cpr_stdio.h"
#include "phntask.h"
#include "fsm.h"
#include "fim.h"
#include "lsm.h"
#include "sm.h"
#include "gsm.h"
#include "ccapi.h"
#include "phone_debug.h"
#include "debug.h"
#include "text_strings.h"
#include "sip_interface_regmgr.h"
#include "resource_manager.h"
#include "singly_link_list.h"
#include "platform_api.h"
#define CAC_FAILURE_TIMEOUT 5
cc_int32_t g_cacDebug = 0;
/* CAC key */
typedef struct {
callid_t call_id;
} cac_key_t;
typedef enum {
FSM_CAC_IDLE = 0,
FSM_CAC_REQ_PENDING = 1,
FSM_CAC_REQ_RESP = 2
} fsm_cac_state_e;
/* CAC structure to hold the data
*/
typedef struct cac_data_t {
void *msg_ptr;
callid_t call_id;
void *cac_fail_timer;
fsm_cac_state_e cac_state;
uint32_t sessions;
} cac_data_t;
static sll_handle_t s_cac_list = NULL;
/*
* Function responsible for searching the list waiting for
* bandwidth allocation.
*
* @param cac_data_t *key_p - pointer to the key.
* @param cac_data_t *cac_data - cac data.
*
* @return void
*
*/
static sll_match_e
fsm_cac_match_call_id (cac_data_t *key_p, cac_data_t *cac_data)
{
if (cac_data->call_id == key_p->call_id) {
return SLL_MATCH_FOUND;
}
return SLL_MATCH_NOT_FOUND;
}
/*
* Function responsible to create new data information
* for cac.
*
* @param none.
*
* @return cac_data_t *
*
*/
static cac_data_t *
fsm_get_new_cac_data (void)
{
static const char *fname="fsm_get_new_cac_data";
cac_data_t *cac_mem;
cac_mem = (cac_data_t *) cpr_malloc(sizeof(cac_data_t));
if (cac_mem == NULL) {
CAC_ERROR(CAC_F_PREFIX"No memory for CAC data.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
return (NULL);
}
memset(cac_mem, 0, sizeof(cac_data_t));
return (cac_mem);
}
/**
*
* Release all cac related data. This includes timer, cac_data
* and message buffers
*
* @param cac_data cac data structure
*
* @return none.
*
* @pre (cac_data not_eq NULL)
*/
static void
fsm_clear_cac_data (cac_data_t *cac_data)
{
if (cac_data->cac_fail_timer) {
(void) cprCancelTimer(cac_data->cac_fail_timer);
(void) cprDestroyTimer(cac_data->cac_fail_timer);
}
(void) sll_remove(s_cac_list, cac_data);
fim_free_event(cac_data->msg_ptr);
/* Release buffer too */
cpr_free(cac_data->msg_ptr);
cpr_free(cac_data);
}
/**
*
* Notifies the SIP stack and UI that CAC has failed.
*
* @param cac_data cac data structure
*
* @return none.
*
* @pre (cac_data not_eq NULL)
*/
static void fsm_cac_notify_failure (cac_data_t *cac_data)
{
const char fname[] = "fsm_cac_notify_failure";
cc_setup_t *msg = (cc_setup_t *) cac_data->msg_ptr;
cc_msgs_t msg_id = msg->msg_id;
callid_t call_id = msg->call_id;
line_t line = msg->line;
int event_id = msg_id;
cc_srcs_t src_id = msg->src_id;
/* Notify UI about the failure */
lsm_ui_display_notify_str_index(STR_INDEX_NO_BAND_WIDTH);
/* Send response from network side regarding the failure */
if (event_id == CC_MSG_SETUP &&
src_id == CC_SRC_SIP) {
DEF_DEBUG(DEB_F_PREFIX"Send CAC failure to SIP %d.\n",
DEB_F_PREFIX_ARGS("CAC", fname), cac_data->call_id);
cc_int_release(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
CC_CAUSE_CONGESTION, NULL, NULL);
} else {
/* If the cac failed, GSM is not spinning yet, so just send the
* information to UI in this case. Other case, where GSM receives event
* will send the information from GSM.
* If the UI is not cleaned up, session infomation is not cleared.
*/
ui_call_state(evOnHook, line, call_id, CC_CAUSE_CONGESTION);
}
}
/**
*
* Initialize the cac timer. This timer is responsible for cleanup if the
* cac response is not received from lower layer.
*
* @param cac_data cac data structure
* timeout specify the time out in sec
*
* @return true if the timer is created scuccessfully.
* false if the timer is not created.
*
* @pre (cac_data not_eq NULL)
*/
static boolean
fsm_init_cac_failure_timer(cac_data_t *cac_data, uint32_t timeout)
{
const char fname[] = "fsm_init_cac_failure_timer";
CAC_DEBUG(DEB_F_PREFIX"cac_data call_id=%x\n",
DEB_F_PREFIX_ARGS("CAC", fname),
cac_data->call_id);
cac_data->cac_fail_timer =
cprCreateTimer("CAC failure timer", GSM_CAC_FAILURE_TIMER, TIMER_EXPIRATION,
gsm_msg_queue);
if (cac_data->cac_fail_timer == NULL) {
CAC_ERROR(CAC_F_PREFIX"CAC Timer allocation failed.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
return(FALSE);
}
(void) cprStartTimer(cac_data->cac_fail_timer, timeout * 1000,
(void *)(long)cac_data->call_id);
return(TRUE);
}
/**
*
* Serches through cac link list and returns cac_data
* based on call_id. This search is a singly link list search.
*
* @param call_id call_id of the call
*
* @return cac_data if found in the list
* NULL if there is no cac_data
*
* @pre (call_id not_eq CC_NO_CALL_ID)
*/
static cac_data_t *
fsm_cac_get_data_by_call_id (callid_t call_id)
{
const char fname[] = "fsm_cac_get_data_by_call_id";
cac_data_t *cac_data;
cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
while (cac_data != NULL) {
if (cac_data->call_id == call_id) {
CAC_DEBUG(DEB_F_PREFIX"cac_data found call_id=%x\n",
DEB_F_PREFIX_ARGS("CAC", fname),
cac_data->call_id);
return(cac_data);
}
cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
}
CAC_DEBUG(DEB_F_PREFIX"cac_data NOT found.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
return(NULL);
}
/**
*
* Initialize cac module by enabling debugs and creating a cac list.
*
* @param void
*
* @return void
*
* @pre (NULL)
*/
void fsm_cac_init (void)
{
const char fname[] = "fsm_cac_init";
/* allocate and initialize cac list */
s_cac_list = sll_create((sll_match_e(*)(void *, void *))
fsm_cac_match_call_id);
if (s_cac_list == NULL) {
CAC_ERROR(CAC_F_PREFIX"CAC list creation failed.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
}
}
/**
*
* clears all the entries in the cac list
*
* @param void
*
* @return void
*
* @pre (NULL)
*/
void fsm_cac_clear_list (void)
{
const char fname[] = "fsm_cac_clear_list";
cac_data_t *cac_data;
cac_data_t *prev_cac_data;
DEF_DEBUG(DEB_F_PREFIX"Clear all pending CAC dat.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
while (cac_data != NULL) {
prev_cac_data = cac_data;
cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
fsm_cac_notify_failure(prev_cac_data);
fsm_clear_cac_data(prev_cac_data);
}
}
/**
*
* Shutdown cac module, clears all the pending cac requests
*
* @param void
*
* @return void
*
* @pre (NULL)
*/
void fsm_cac_shutdown (void)
{
fsm_cac_clear_list();
sll_destroy(s_cac_list);
s_cac_list = NULL;
}
/**
*
* Check if there are pending CAC requests
*
* @param none
*
* @return cac_data returns first pending request.
*
* @pre (NULL)
*/
static cac_data_t *
fsm_cac_check_if_pending_req (void)
{
cac_data_t *cac_data;
cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
while (cac_data != NULL) {
if (cac_data->cac_state == FSM_CAC_REQ_PENDING ||
cac_data->cac_state == FSM_CAC_IDLE) {
return(cac_data);
}
cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
}
return(NULL);
}
/**
*
* Check if there are pending CAC requests
*
* @param none
*
* @return cac_data returns first pending request.
*
* @pre (NULL)
*/
static cc_causes_t
fsm_cac_process_bw_allocation (cac_data_t *cac_data)
{
const char fname[] = "fsm_cac_process_bw_allocation";
if (lsm_allocate_call_bandwidth(cac_data->call_id, cac_data->sessions) ==
CC_CAUSE_CONGESTION) {
DEF_DEBUG(DEB_F_PREFIX"CAC Allocation failed.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
fsm_cac_notify_failure(cac_data);
fsm_clear_cac_data(cac_data);
return(CC_CAUSE_CONGESTION);
}
cac_data->cac_state = FSM_CAC_REQ_PENDING;
return(CC_CAUSE_OK);
}
/**
*
* Check if there are pending CAC requests
*
* @param call_id request a cac for this call_id
* sessions number of sessions in the request
* msg ccapi msg, that is held to process
* till cac response is received.
*
* @return CC_CAUSE_BW_OK if the bandwidth is received.
* CC_CAUSE_Ok Call returned successfully, not sure about BW yet
* CC_CAUSE_ERROR: Call returned with failure.
*
* @pre (NULL)
*/
cc_causes_t
fsm_cac_call_bandwidth_req (callid_t call_id, uint32_t sessions,
void *msg)
{
const char fname[] = "fsm_cac_call_bandwidth_req";
cac_data_t *cac_data, *cac_pend_data;
/* If wlan not connected return OK */
cac_data = fsm_get_new_cac_data();
if (cac_data == NULL) {
return(CC_CAUSE_CONGESTION);
}
cac_data->msg_ptr = msg;
cac_data->call_id = call_id;
cac_data->cac_state = FSM_CAC_IDLE;
cac_data->sessions = sessions;
fsm_init_cac_failure_timer(cac_data, CAC_FAILURE_TIMEOUT);
/* Make sure there is no pending requests before submitting
* another one
*/
if ((cac_pend_data = fsm_cac_check_if_pending_req()) == NULL) {
/*
* Make sure sufficient bandwidth available to make a outgoing call. This
* should be done before allocating other resources.
*/
DEF_DEBUG(DEB_F_PREFIX"CAC request for %d sessions %d.\n",
DEB_F_PREFIX_ARGS("CAC", fname), call_id, sessions);
if (fsm_cac_process_bw_allocation(cac_data) == CC_CAUSE_CONGESTION) {
return(CC_CAUSE_CONGESTION);
}
cac_data->cac_state = FSM_CAC_REQ_PENDING;
} else if (cac_pend_data->cac_state == FSM_CAC_IDLE) {
if (fsm_cac_process_bw_allocation(cac_pend_data) ==
CC_CAUSE_CONGESTION) {
/* Clear all remaining data */
fsm_cac_clear_list();
return(CC_CAUSE_CONGESTION);
}
}
(void) sll_append(s_cac_list, cac_data);
return(CC_CAUSE_OK);
}
/**
*
* This is called by gsm to cleanup the cac data. If there are any
* pending CAC requests and far end cancels the call, the pending
* request has to be canceled.
*
* @param call_id - call_id of the request
*
* @return none.
*
* @pre (NULL)
*/
void fsm_cac_call_release_cleanup (callid_t call_id)
{
cac_data_t *cac_data;
cac_data = fsm_cac_get_data_by_call_id(call_id);
if (cac_data) {
sll_remove(s_cac_list, cac_data);
fsm_clear_cac_data(cac_data);
}
}
/**
*
* Called when the bandwidth response with available bw is received. This
* also process held ccapi messages through fim event chain
*
* @param none
*
* @return CC_CAUSE_NO_RESOURCE No bandwidth
* CC_CAUSE_OK if ok
*
*
* @pre (NULL)
*/
cc_causes_t
fsm_cac_process_bw_avail_resp (void)
{
const char fname[] = "fsm_cac_process_bw_avail_resp";
cac_data_t *cac_data = NULL;
cac_data_t *next_cac_data = NULL;
cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
if (cac_data != NULL) {
switch (cac_data->cac_state) {
default:
case FSM_CAC_IDLE:
DEF_DEBUG(DEB_F_PREFIX"No Pending CAC request.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
/*
* Make sure sufficient bandwidth available to make a outgoing call. This
* should be done before allocating other resources.
*/
if (fsm_cac_process_bw_allocation(cac_data) == CC_CAUSE_CONGESTION) {
sll_remove(s_cac_list, cac_data);
return(CC_CAUSE_NO_RESOURCE);
}
break;
case FSM_CAC_REQ_PENDING:
next_cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
sll_remove(s_cac_list, cac_data);
/* Request for the next bandwidth */
DEF_DEBUG(DEB_F_PREFIX"Process pending responses %d.\n",
DEB_F_PREFIX_ARGS("CAC", fname), cac_data->call_id);
/* Let GSM process completed request */
fim_process_event(cac_data->msg_ptr, TRUE);
fsm_clear_cac_data(cac_data);
if (next_cac_data != NULL) {
/*
* Make sure sufficient bandwidth available to make a outgoing call. This
* should be done before allocating other resources.
*/
DEF_DEBUG(DEB_F_PREFIX"Requesting next allocation %d.\n",
DEB_F_PREFIX_ARGS("CAC", fname), next_cac_data->call_id);
if (fsm_cac_process_bw_allocation(next_cac_data) ==
CC_CAUSE_CONGESTION) {
/* If the next data was in idle state and the request fialed
* then clean up the remaining list
*/
if (next_cac_data->cac_state == FSM_CAC_IDLE) {
/* Clear all remaining data */
fsm_cac_clear_list();
} else {
sll_remove(s_cac_list, next_cac_data);
}
return(CC_CAUSE_NO_RESOURCE);
}
}
break;
}
}
return(CC_CAUSE_NO_RESOURCE);
}
/**
*
* Called when the bandwidth response with failed bw is received. This
* also process held ccapi messages through fim event chain
*
* @param none
*
* @return CC_CAUSE_NO_RESOURCE No bandwidth
* CC_CAUSE_OK if ok
*
*
* @pre (NULL)
*/
cc_causes_t
fsm_cac_process_bw_failed_resp (void)
{
const char fname[] = "fsm_cac_process_bw_avail_resp";
cac_data_t *cac_data = NULL;
cac_data_t *next_cac_data = NULL;
cac_data = (cac_data_t *) sll_next(s_cac_list, NULL);
if (cac_data != NULL) {
switch (cac_data->cac_state) {
default:
case FSM_CAC_IDLE:
DEF_DEBUG(DEB_F_PREFIX"No Pending request.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
/*
* Make sure sufficient bandwidth available to make a outgoing call. This
* should be done before allocating other resources.
*/
if (fsm_cac_process_bw_allocation(cac_data) == CC_CAUSE_CONGESTION) {
sll_remove(s_cac_list, cac_data);
return(CC_CAUSE_NO_RESOURCE);
}
break;
case FSM_CAC_REQ_PENDING:
next_cac_data = (cac_data_t *) sll_next(s_cac_list, cac_data);
sll_remove(s_cac_list, cac_data);
/* Request for the next bandwidth */
DEF_DEBUG(DEB_F_PREFIX"Process pending responses even after failure.\n",
DEB_F_PREFIX_ARGS("CAC", fname));
/* Let GSM process completed request */
fsm_cac_notify_failure(cac_data);
fsm_clear_cac_data(cac_data);
if (next_cac_data != NULL) {
/*
* Make sure sufficient bandwidth available to make a outgoing call. This
* should be done before allocating other resources.
*/
if (fsm_cac_process_bw_allocation(next_cac_data) == CC_CAUSE_CONGESTION) {
/* If the next data was in idle state and the request fialed
* then clean up the remaining list
*/
if (next_cac_data->cac_state == FSM_CAC_IDLE) {
/* Clear all remaining data */
fsm_cac_clear_list();
} else {
sll_remove(s_cac_list, next_cac_data);
}
return(CC_CAUSE_NO_RESOURCE);
}
}
break;
}
}
return(CC_CAUSE_NO_RESOURCE);
}
/**
*
* Process time-out event. This cause cac data to send failure notifications.
*
* @param void *tmr_data - timer data
*
* @return none
*
*
* @pre (NULL)
*/
void
fsm_cac_process_bw_fail_timer (void *tmr_data)
{
const char fname[] = "fsm_cac_process_bw_fail_timer";
DEF_DEBUG(DEB_F_PREFIX"CAC request timedout %d.\n",
DEB_F_PREFIX_ARGS("CAC", fname), (callid_t)(long)tmr_data);
/* Time-out causes same set of processing as bw failure
*/
fsm_cac_process_bw_failed_resp();
}