/* 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 "cpr_string.h" #include "cpr_errno.h" #include "phone.h" #include "phntask.h" #include "lsm.h" #include "ccapi.h" #include "phone_debug.h" #include "fim.h" #include "sdp.h" #include "debug.h" #include "gsm_sdp.h" #include "uiapi.h" #include "gsm.h" #include "prot_configmgr.h" #include "singly_link_list.h" typedef enum dcsm_state { DCSM_S_MIN = -1, DCSM_S_READY, DCSM_S_WAITING, DCSM_S_MAX } dcsm_state_e; #define DCSM_MAX_CALL_IDS (LSM_MAX_CALLS) static struct dcsm_icb_t { callid_t call_ids[DCSM_MAX_CALL_IDS]; line_t line; int gsm_state; sll_handle_t s_msg_list; dcsm_state_e state; } dcsm_cb; static const char *dcsm_state_names[] = { "DCSM_READY", "DCSM_WAITING" }; extern cc_int32_t g_dcsmDebug; static sm_rcs_t dcsm_wait_ev_feature_handling(void *event, int event_id); static sm_rcs_t dcsm_wait_ev_offhook_handling(void *event, int event_id); static sm_rcs_t dcsm_wait_ev_dialstring_handling(void *event, int event_id); typedef sm_rcs_t (*pdcsm_sm_evt_handler)(void *event, int event_id); typedef struct _dcsm_table_t { int min_state; int max_state; int min_event; int max_event; pdcsm_sm_evt_handler *table; } dcsm_table_t; static pdcsm_sm_evt_handler dcsm_function_table[DCSM_S_MAX][CC_MSG_MAX] = { /* DCSM_S_READY ------------------------------------------------------------ */ { /* DCSM_E_SETUP */ NULL, /* DCSM_E_SETUP_ACK */ NULL, /* DCSM_E_PROCEEDING */ NULL, /* DCSM_E_ALERTING */ NULL, /* DCSM_E_CONNECTED */ NULL, /* DCSM_E_CONNECTED_ACK */ NULL, /* DCSM_E_RELEASE */ NULL, /* DCSM_E_RELEASE_COMPLETE */ NULL, /* DCSM_E_FEATURE */ NULL, /* DCSM_E_FEATURE_ACK */ NULL, /* DCSM_E_OFFHOOK */ NULL, /* DCSM_E_ONHOOK */ NULL, /* DCSM_E_LINE */ NULL, /* DCSM_E_DIGIT_BEGIN */ NULL, /* DCSM_E_DIGIT_END */ NULL, /* DCSM_E_DIALSTRING */ NULL, /* DCSM_E_MWI */ NULL, /* DCSM_E_SESSION_AUDIT */ NULL }, /* DCSM_S_WAITING----------------------------------------------------- */ { /* DCSM_E_SETUP */ NULL, /* DCSM_E_SETUP_ACK */ NULL, /* DCSM_E_PROCEEDING */ NULL, /* DCSM_E_ALERTING */ NULL, /* DCSM_E_CONNECTED */ NULL, /* DCSM_E_CONNECTED_ACK */ NULL, /* DCSM_E_RELEASE */ NULL, /* DCSM_E_RELEASE_COMPLETE */ NULL, /* DCSM_E_FEATURE */ dcsm_wait_ev_feature_handling, /* DCSM_E_FEATURE_ACK */ NULL, /* DCSM_E_OFFHOOK */ dcsm_wait_ev_offhook_handling, /* DCSM_E_ONHOOK */ NULL, /* DCSM_E_LINE */ NULL, /* DCSM_E_DIGIT_BEGIN */ NULL, /* DCSM_E_DIGIT_END */ NULL, /* DCSM_E_DIALSTRING */ dcsm_wait_ev_dialstring_handling, /* DCSM_E_MWI */ NULL, /* DCSM_E_SESSION_AUDIT */ NULL }, }; static dcsm_table_t dcsm_sm_table; static dcsm_table_t *pdcsm_sm_table = &dcsm_sm_table; /* * Adds event to the dcsm queue. * * @param event - Call control event to add * * @return TRUE if the event is added. * */ static boolean dcsm_add_event_to_queue (void *event) { (void) sll_append(dcsm_cb.s_msg_list, event); return TRUE; } /* * Get a event from the queue. * * @param none * * @return pointer to the event. * */ static void * dcsm_get_first_event_from_queue (void) { void *msg_ptr = NULL; msg_ptr = sll_next(dcsm_cb.s_msg_list, NULL); sll_remove(dcsm_cb.s_msg_list, msg_ptr); return(msg_ptr); } /* * Get dcsm state name. * * @param int - state id * * @return ponter to the state name either * DCSM_READY or DCSM_WAITING. * */ const char *dcsm_get_state_name (int state) { if ((state <= DCSM_S_MIN) || (state >= DCSM_S_MAX)) { return (get_debug_string(GSM_UNDEFINED)); } return dcsm_state_names[state]; } /** * Feed the event to FIM state machine a direct function call * to process message retrieved from the queue. * * @param void * - pointer to the message to be processed * * @return none. */ static void dcsm_process_event_to_gsm (void *msg_ptr) { if (fim_process_event(msg_ptr, FALSE) == TRUE) { fim_free_event(msg_ptr); /* Release buffer too */ cpr_free(msg_ptr); } } /** * Process ready state events. * * @param none * * @return none. */ static void dcsm_do_ready_state_job (void) { static const char fname[] = "dcsm_do_ready_state_job"; void *msg_ptr; int event_id; cc_feature_t *feat_msg = NULL; callid_t call_id = CC_NO_CALL_ID; if (dcsm_cb.state != DCSM_S_READY) { DEF_DEBUG(DEB_F_PREFIX": not in ready state.\n", DEB_F_PREFIX_ARGS("DCSM", fname)); return; } msg_ptr = dcsm_get_first_event_from_queue(); /* Check if there is any msg available */ if (msg_ptr != NULL) { event_id = (int)(((cc_setup_t *)msg_ptr)->msg_id); if (event_id == CC_MSG_FEATURE) { feat_msg = (cc_feature_t *) msg_ptr; if (feat_msg != NULL) { call_id = feat_msg->call_id; } } DEF_DEBUG(DEB_F_PREFIX"%d: event (%s%s)\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id, cc_msg_name((cc_msgs_t)(event_id)), feat_msg ? cc_feature_name(feat_msg->feature_id):" "); dcsm_process_event_to_gsm(msg_ptr); } } /** * Function process events based on the state of DCSM. * * @param none * * @return none. */ void dcsm_process_jobs (void) { dcsm_do_ready_state_job(); } /** * Adds call_id to the list of call_id's which made DCSM to move * to waiting state. * * @param callid_t - call id that has to be added to the list * * @return none. */ static void dcsm_add_call_id_to_list (callid_t call_id) { static const char fname[] = "dcsm_add_call_id_to_list"; int i, loc = -1; for (i=0; i< DCSM_MAX_CALL_IDS; i++) { if (dcsm_cb.call_ids[i] == CC_NO_CALL_ID) { loc = i; } else if (dcsm_cb.call_ids[i] == call_id) { //Call_id already present so do not try to add again return; } } if (loc == -1) { /* Should never happen as there is a space to store call_id * for each calls */ DCSM_ERROR(DCSM_F_PREFIX"DCSM No space to store call_id.\n", DEB_F_PREFIX_ARGS("DCSM", fname)); return; } dcsm_cb.call_ids[loc] = call_id; } /** * Remove call_id from the list and see if the call_ids list is null. * * @param callid_t - call id that to be removed from the list. * * @return TRUE - if the call_ids list is empty. * FALSE - call_ids list is not empty. */ static boolean dcsm_remove_check_for_empty_list (callid_t call_id) { int i; boolean call_id_present = FALSE; for (i=0; i< DCSM_MAX_CALL_IDS; i++) { if (dcsm_cb.call_ids[i] == call_id) { dcsm_cb.call_ids[i] = CC_NO_CALL_ID; /* Found out that other call_id exist by setting * call_id_preset, so return true, don't have to * continue */ if (call_id_present == TRUE) { return FALSE; } } else if (dcsm_cb.call_ids[i] != CC_NO_CALL_ID) { call_id_present = TRUE; } } if (call_id_present == TRUE) { return(FALSE); } return(TRUE); } /* * The function responsible for setting gsm state machine. * * @param callid_t - call id of the call * state - current GSM state * * @return void * */ void dcsm_update_gsm_state (fsm_fcb_t *fcb, callid_t call_id, int state) { int last_state; static const char fname[] = "dcsm_update_gsm_state"; fsmdef_dcb_t *dcb; if (fcb->fsm_type != FSM_TYPE_DEF) { DEF_DEBUG(DEB_F_PREFIX"%d: Not handling for %s\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id, fsm_type_name(fcb->fsm_type)); return; } last_state = dcsm_cb.state; switch (state) { case FSMDEF_S_RELEASING: dcb = fsmdef_get_dcb_by_call_id(call_id); if (dcb && dcb->send_release == FALSE) { /* This call is already released from SIP persepctive. so no need to wait */ break; } case FSMDEF_S_CONNECTING: case FSMDEF_S_HOLD_PENDING: case FSMDEF_S_RESUME_PENDING: dcsm_add_call_id_to_list(call_id); dcsm_cb.state = DCSM_S_WAITING; break; case FSMDEF_S_MIN: case FSMDEF_S_IDLE: case FSMDEF_S_COLLECT_INFO: case FSMDEF_S_CALL_SENT: case FSMDEF_S_OUTGOING_PROCEEDING: case FSMDEF_S_KPML_COLLECT_INFO: case FSMDEF_S_OUTGOING_ALERTING: case FSMDEF_S_INCOMING_ALERTING: case FSMDEF_S_JOINING: case FSMDEF_S_CONNECTED: case FSMDEF_S_CONNECTED_MEDIA_PEND: case FSMDEF_S_HOLDING: case FSMDEF_S_PRESERVED: case FSMDEF_S_MAX: /* If there are no other call_id then move it to * ready state else, it will remain in waiting * state */ if (dcsm_remove_check_for_empty_list(call_id) == TRUE) { dcsm_cb.state = DCSM_S_READY; /* Check if there are any pending events in the queue * if so send a DCSM_EV_READY to the GSM so dcsm will * get a chance to execute. */ if (sll_count(dcsm_cb.s_msg_list) > 0 ) { if (gsm_send_msg(DCSM_EV_READY, NULL, 0) == CPR_FAILURE) { DCSM_ERROR(DCSM_F_PREFIX"send DCSM_EV_READY ERROR.\n", DEB_F_PREFIX_ARGS(DCSM, fname)); } } } break; default: break; } DEF_DEBUG(DEB_F_PREFIX"%d : %s --> %s\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id, dcsm_get_state_name(last_state), dcsm_get_state_name(dcsm_cb.state)); return; } /** * * Feature passed through when state machine is in waiting state * * @param sm_event_t * * @return sm_rcs_t * * @pre (none) */ static sm_rcs_t dcsm_wait_ev_offhook_handling (void *event, int event_id) { static const char fname[] = "dcsm_wait_ev_offhook_handling"; DEF_DEBUG(DEB_F_PREFIX": offhook\n", DEB_F_PREFIX_ARGS("DCSM", fname)); dcsm_add_event_to_queue(event); return (SM_RC_END); } /** * * Feature passed through when state machine is in waiting state * * @param sm_event_t * * @return sm_rcs_t * * @pre (none) */ static sm_rcs_t dcsm_wait_ev_feature_handling (void *event, int event_id) { static const char fname[] = "dcsm_wait_ev_feature_handling"; cc_feature_t *feat_msg = (cc_feature_t *) event; sm_rcs_t rc = SM_RC_END; cc_features_t ftr_id = CC_FEATURE_UNDEFINED; callid_t call_id = CC_NO_CALL_ID; if (feat_msg != NULL) { ftr_id = feat_msg->feature_id; call_id = feat_msg->call_id; } DEF_DEBUG(DEB_F_PREFIX"%d: id= %s%s\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id, cc_msg_name((cc_msgs_t)(event_id)), feat_msg ? cc_feature_name(feat_msg->feature_id):" "); switch (ftr_id) { case CC_FEATURE_ANSWER: case CC_FEATURE_NEW_CALL: case CC_FEATURE_REDIAL: case CC_FEATURE_RESUME: case CC_FEATURE_JOIN: dcsm_add_event_to_queue(event); DEF_DEBUG(DEB_F_PREFIX"%d: Event queued\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id); rc = SM_RC_END; break; default: DEF_DEBUG(DEB_F_PREFIX"%d: Feature msg not handled\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id); rc = SM_RC_CONT; break; } return (rc); } /** * * Feature passed through when state machine is in waiting state * * @param sm_event_t * * @return sm_rcs_t * * @pre (none) */ static sm_rcs_t dcsm_wait_ev_dialstring_handling (void *event, int event_id) { static const char fname[] = "dcsm_wait_ev_dialstring_handling"; DEF_DEBUG(DEB_F_PREFIX": dialstring\n", DEB_F_PREFIX_ARGS("DCSM", fname)); dcsm_add_event_to_queue(event); return (SM_RC_END); } /** * * Feature passed through when state machine is in waiting state * * @param sm_event_t * * @return sm_rcs_t * * @pre (none) */ sm_rcs_t dcsm_process_event (void *event, int event_id) { static const char fname[] = "dcsm_process_event"; callid_t call_id; int state_id; sm_rcs_t rc = SM_RC_CONT; fsm_fcb_t *fcb = (fsm_fcb_t *) event; cc_feature_t *feat_msg = NULL; pdcsm_sm_evt_handler hdlr; /* cached handler in order to compute its addr once */ call_id = fcb->call_id; if (event_id == CC_MSG_FEATURE) { feat_msg = (cc_feature_t *) event; if (feat_msg != NULL){ call_id = feat_msg->call_id; } } DEF_DEBUG(DEB_F_PREFIX"DCSM %-4d:(%s:%s%s)\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id, dcsm_get_state_name(dcsm_cb.state), cc_msg_name((cc_msgs_t)(event_id)), feat_msg ? cc_feature_name(feat_msg->feature_id):" "); state_id = dcsm_cb.state; // Get current state; /* * validate the state and event * and that there is a valid function for this state-event pair. */ if ((state_id > pdcsm_sm_table->min_state) && (state_id < pdcsm_sm_table->max_state) && (event_id > pdcsm_sm_table->min_event) && (event_id < pdcsm_sm_table->max_event)) { if ((hdlr = pdcsm_sm_table->table[pdcsm_sm_table->max_event * state_id + event_id]) != NULL) { DEF_DEBUG(DEB_F_PREFIX"%-4d: dcsm entry: (%s)\n", DEB_F_PREFIX_ARGS("DCSM", fname), call_id, cc_msg_name((cc_msgs_t)(event_id))); rc = hdlr(event, event_id); } } return (rc); } /* * Function responsible for searching the list. * * @param cac_data_t *key_p - pointer to the key. * @param cac_data_t *cac_data - cac data. * * @return void * */ static sll_match_e dcsm_match_event (void *key_p, void *msg_data) { return SLL_MATCH_FOUND; } /** * * Show function for DCSM * * @param argc - number of parameter passed * argv - argument passed * * @return 0 - if function successful */ cc_int32_t dcsm_show_cmd (cc_int32_t argc, const char *argv[]) { void *msg_ptr; int i; cc_setup_t *msg; cc_msgs_t msg_id; line_t line; callid_t call_id; cc_feature_t *feat_msg = NULL; /* * check if need help */ if ((argc == 2) && (argv[1][0] == '?')) { debugif_printf("%s", "show dcsm\n"); return (0); } if (dcsm_cb.s_msg_list == NULL) { return(0); } debugif_printf("%s", "\n-------------------------- DCSM Data --------------------------"); debugif_printf("\nDCSM State = %s",dcsm_get_state_name(dcsm_cb.state)); debugif_printf("%s", "\nDCSM waiting calls \n"); for (i=0; i< DCSM_MAX_CALL_IDS; i++) { if (dcsm_cb.call_ids[i] != CC_NO_CALL_ID) { debugif_printf("%d ", dcsm_cb.call_ids[i]); } } debugif_printf("%s", "\n"); debugif_printf("%s", "\nDCSM waiting events \n"); i = 0; msg_ptr = sll_next(dcsm_cb.s_msg_list, NULL); while (msg_ptr) { msg_ptr = sll_next(dcsm_cb.s_msg_list, msg_ptr); if (msg_ptr) { msg = (cc_setup_t *) msg_ptr; msg_id = msg->msg_id; call_id = msg->call_id; line = msg->line; if ((int)msg_id == CC_MSG_FEATURE) { feat_msg = (cc_feature_t *) msg_ptr; } debugif_printf("Event %d (%d/%d): (%s%s)\n", i++, line, call_id, cc_msg_name(msg_id), feat_msg ? cc_feature_name(feat_msg->feature_id):" "); } } debugif_printf("%s", "\n-------------------------- DCSM Data Done-----------------------"); return (0); } /** * * Initialize dcsm state machine. * * @param none * * @return none * * @pre none */ void dcsm_init (void) { static const char fname[] = "dcsm_init"; int i; /* * Initialize the state/event table. */ dcsm_sm_table.min_state = DCSM_S_MIN; dcsm_sm_table.max_state = DCSM_S_MAX; dcsm_sm_table.min_event = CC_MSG_MIN; dcsm_sm_table.max_event = CC_MSG_MAX; dcsm_sm_table.table = (&(dcsm_function_table[0][0])); dcsm_cb.state = DCSM_S_READY; for (i=0; i< DCSM_MAX_CALL_IDS; i++) { dcsm_cb.call_ids[i] = CC_NO_CALL_ID; } /* allocate and initialize cac list */ dcsm_cb.s_msg_list = sll_create((sll_match_e(*)(void *, void *)) dcsm_match_event); if (dcsm_cb.s_msg_list == NULL) { DCSM_ERROR(DCSM_F_PREFIX"DCSM CB creation failed.\n", DEB_F_PREFIX_ARGS("DCSM", fname)); } } /** * * Shut down routine for dcsm state machine. * * @param none * * @return none * * @pre none */ void dcsm_shutdown (void) { void *msg_ptr; if (dcsm_cb.s_msg_list == NULL) { return; } msg_ptr = sll_next(dcsm_cb.s_msg_list, NULL); while (msg_ptr) { msg_ptr = sll_next(dcsm_cb.s_msg_list, msg_ptr); if (msg_ptr) { fim_free_event(msg_ptr); /* Release buffer too */ cpr_free(msg_ptr); } } sll_destroy(dcsm_cb.s_msg_list); dcsm_cb.s_msg_list = NULL; }