void on_wat_con_sts(unsigned char span_id, uint8_t call_id, wat_con_status_t *status) { ftdm_span_t *span = NULL; //ftdm_status_t ftdm_status = FTDM_FAIL; ftdm_gsm_span_data_t *gsm_data = NULL; if(!(span = GetSpanByID(span_id, &gsm_data))) { return; } switch(status->type) { case WAT_CON_STATUS_TYPE_RINGING: ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - WAT_CON_STATUS_TYPE_RINGING\r\n"); ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_RINGING); break; case WAT_CON_STATUS_TYPE_ANSWER: ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - WAT_CON_STATUS_TYPE_ANSWER\r\n"); ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); break; default: ftdm_log(FTDM_LOG_INFO, "on_wat_con_sts - Unhandled state %d\n", span_id); }; return; }
void sngisdn_process_disc_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; DiscEvnt *discEvnt = &sngisdn_event->event.discEvnt; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing DISCONNECT (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_RING: case FTDM_CHANNEL_STATE_DIALING: case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_UP: if (discEvnt->causeDgn[0].eh.pres && discEvnt->causeDgn[0].causeVal.pres) { ftdmchan->caller_data.hangup_cause = discEvnt->causeDgn[0].causeVal.val; } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "DISCONNECT did not have a cause code\n"); ftdmchan->caller_data.hangup_cause = 0; } sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_REL); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; case FTDM_CHANNEL_STATE_COLLECT: case FTDM_CHANNEL_STATE_GET_CALLERID: ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); break; case FTDM_CHANNEL_STATE_DOWN: /* somehow we are in down, nothing we can do locally */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Received DISCONNECT but we are in DOWN state\n"); break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* This is a race condition. We just sent a DISCONNECT, on this channel */ /* Do nothing */ break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Received DISCONNECT in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); /* start reset procedure */ /* Start the release procedure */ ftdm_set_flag(sngisdn_info, FLAG_REMOTE_REL); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan) { const char* args; char *p; char *type = NULL; char *target = NULL; ftdm_status_t status = FTDM_FAIL; sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; unsigned i; args = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_arg"); if (ftdm_strlen_zero(args)) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because call_transfer_arg variable is not set\n"); goto done; } type = ftdm_strdup(args); if ((p = strchr(type, '/'))) { target = ftdm_strdup(p+1); *p = '\0'; } if (ftdm_strlen_zero(type) || ftdm_strlen_zero(target)) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid parameters for transfer %s, expected <type>/<target>\n", args); goto done; } if (sngisdn_info->transfer_data.type != SNGISDN_TRANSFER_NONE) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because an existing transfer transfer is pending (%s)\n", sngisdn_transfer_type2str(sngisdn_info->transfer_data.type)); goto done; } ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer requested type:%s target:%s\n", type, target); for (i = 0; i < ftdm_array_len(transfer_interfaces); i++ ) { if (!strcasecmp(transfer_interfaces[i].name, type)) { /* Depending on the transfer type, the transfer function may change the * channel state to UP, or last_state, but the transfer function will always result in * an immediate state change if FTDM_SUCCESS is returned */ status = transfer_interfaces[i].func(ftdmchan, transfer_interfaces[i].type, target); goto done; } } ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid transfer type:%s\n", type); done: if (status != FTDM_SUCCESS) { ftdm_set_state(ftdmchan, ftdmchan->last_state); } ftdm_safe_free(type); ftdm_safe_free(target); return status; }
void on_wat_rel_cfm(unsigned char span_id, uint8_t call_id) { ftdm_log(FTDM_LOG_INFO, "s%d: Call hangup complete (id:%d)\n", span_id, call_id); ftdm_span_t *span = NULL; //ftdm_status_t ftdm_status = FTDM_FAIL; ftdm_gsm_span_data_t *gsm_data = NULL; if(!(span = GetSpanByID(span_id, &gsm_data))) { return; } switch(gsm_data->dchan->state) { case FTDM_CHANNEL_STATE_UP: ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_HANGUP); break; default: ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_DOWN); break; } }
void sngisdn_process_fac_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; FacEvnt *facEvnt = &sngisdn_event->event.facEvnt; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing FACILITY IND (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_GET_CALLERID: /* Update the caller ID Name */ if (facEvnt->facElmt.facStr.pres) { char retrieved_str[255]; /* return values for "sng_isdn_retrieve_facility_information_following": If there will be no information following, or fails to decode IE, returns -1 If there will be no information following, but current FACILITY IE contains a caller name, returns 0 If there will be information following, returns 1 */ if (sng_isdn_retrieve_facility_caller_name(&facEvnt->facElmt.facStr.val[2], facEvnt->facElmt.facStr.len, retrieved_str) == 0) { strcpy(ftdmchan->caller_data.cid_name, retrieved_str); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to retrieve Caller Name from Facility IE\n"); } if (signal_data->facility_timeout) { /* Cancel facility timeout */ ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_TIMER_FACILITY]); } } ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); break; case FTDM_CHANNEL_STATE_RING: /* We received the caller ID Name in FACILITY, but its too late, facility-timeout already occurred */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "FACILITY received, but we already proceeded with call\n"); break; default: /* We do not support other FACILITY types for now, so do nothing */ break; } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
void on_wat_con_ind(unsigned char span_id, uint8_t call_id, wat_con_event_t *con_event) { //fprintf(stdout, "s%d: Incoming call (id:%d) Calling Number:%s Calling Name:\"%s\" type:%d plan:%d\n", span_id, call_id, con_event->calling_num.digits, con_event->calling_name, con_event->calling_num.type, con_event->calling_num.plan); ftdm_log(FTDM_LOG_INFO, "s%d: Incoming call (id:%d) Calling Number:%s Calling Name:\"%s\" type:%d plan:%d\n", span_id, call_id, con_event->calling_num.digits, con_event->calling_name, con_event->calling_num.type, con_event->calling_num.plan); ftdm_span_t *span = NULL; //ftdm_status_t ftdm_status = FTDM_FAIL; ftdm_gsm_span_data_t *gsm_data = NULL; if(!(span = GetSpanByID(span_id, &gsm_data))) { return; } gsm_data->call_id = call_id; #define ZERO_ARRAY(arr) memset(arr, 0, sizeof(arr)) // cid date { time_t t; struct tm *tmp; t = time(NULL); tmp = localtime(&t); if (tmp == NULL) { ZERO_ARRAY(gsm_data->bchan->caller_data.cid_date); strftime(gsm_data->bchan->caller_data.cid_date, sizeof(gsm_data->bchan->caller_data.cid_date), "%y/%m/%d", tmp); } } // cid name ZERO_ARRAY(gsm_data->bchan->caller_data.cid_name); strncpy(gsm_data->bchan->caller_data.cid_name, con_event->calling_name,sizeof(gsm_data->bchan->caller_data.cid_name)); // dnis ZERO_ARRAY(gsm_data->bchan->caller_data.dnis.digits); strncpy(gsm_data->bchan->caller_data.dnis.digits, con_event->calling_num.digits, FTDM_DIGITS_LIMIT); //collected ZERO_ARRAY(gsm_data->bchan->caller_data.collected); strncpy(gsm_data->bchan->caller_data.collected, con_event->calling_num.digits, FTDM_DIGITS_LIMIT); ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_RING); if (ftdm_channel_open_chan(gsm_data->bchan) != FTDM_SUCCESS) { ftdm_log_chan(gsm_data->bchan, FTDM_LOG_ERROR, "Failed to open GSM b-channel of span %s!\n", span->name); } }
void on_wat_rel_ind(unsigned char span_id, uint8_t call_id, wat_rel_event_t *rel_event) { ftdm_log(FTDM_LOG_INFO, "s%d: Call hangup (id:%d) cause:%d\n", span_id, call_id, rel_event->cause); ftdm_span_t *span = NULL; //ftdm_status_t ftdm_status = FTDM_FAIL; ftdm_gsm_span_data_t *gsm_data = NULL; if(!(span = GetSpanByID(span_id, &gsm_data))) { return; } ftdm_set_state(gsm_data->bchan, FTDM_CHANNEL_STATE_HANGUP); }
void sngisdn_facility_timeout(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_mutex_lock(ftdmchan->mutex); if (ftdmchan->state == FTDM_CHANNEL_STATE_GET_CALLERID) { ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Facility timeout reached proceeding with call (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->spInstId, sngisdn_info->suInstId); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); } ftdm_mutex_unlock(ftdmchan->mutex); return; }
void sngisdn_t3_timeout(void* p_sngisdn_info) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Timer T3 expired (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); ftdm_mutex_lock(ftdmchan->mutex); if (ftdm_test_flag(sngisdn_info, FLAG_ACTIVATING)){ /* PHY layer timed-out, need to clear the call */ ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Failed to Wake-Up line (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->glare.spInstId, sngisdn_info->glare.suInstId); ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NO_ROUTE_DESTINATION; ftdm_clear_flag(sngisdn_info, FLAG_ACTIVATING); ftdm_set_flag(sngisdn_info, FLAG_LOCAL_ABORT); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); } ftdm_mutex_unlock(ftdmchan->mutex); }
void sngisdn_process_sta_cfm (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; StaEvnt *staEvnt = &sngisdn_event->event.staEvnt; uint8_t call_state = 0; if (staEvnt->callSte.eh.pres && staEvnt->callSte.callGlblSte.pres) { call_state = staEvnt->callSte.callGlblSte.val; } ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing STATUS CONFIRM (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); if (staEvnt->causeDgn[0].eh.pres && staEvnt->causeDgn[0].causeVal.pres) { if (staEvnt->callSte.eh.pres && staEvnt->callSte.callGlblSte.pres) { call_state = staEvnt->callSte.callGlblSte.val; } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Received STATUS without call state\n"); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } switch (staEvnt->causeDgn[0].causeVal.val) { case FTDM_CAUSE_RESPONSE_TO_STATUS_ENQUIRY: ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Status Check OK:%d", call_state); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; case FTDM_CAUSE_WRONG_CALL_STATE: ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Message incompatible with call state (call_state:%d channel-state:%s cause:%d) (suId:%u suInstId:%u spInstId:%u)\n", call_state, ftdm_channel_state2str(ftdmchan->state), staEvnt->causeDgn[0].causeVal.val, suId, suInstId, spInstId); break; case FTDM_CAUSE_RECOVERY_ON_TIMER_EXPIRE: ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Recovery on timer expire (call_state:%d channel-state:%s cause:%d) (suId:%u suInstId:%u spInstId:%u)\n", call_state, ftdm_channel_state2str(ftdmchan->state), staEvnt->causeDgn[0].causeVal.val, suId, suInstId, spInstId); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "STATUS CONFIRM (call_state:%d channel-state:%s cause:%d) (suId:%u suInstId:%u spInstId:%u)\n", call_state, ftdm_channel_state2str(ftdmchan->state), staEvnt->causeDgn[0].causeVal.val, suId, suInstId, spInstId); break; } /* Section 4.3.30 from INT Interface - Service Definition */ ftdmchan->caller_data.hangup_cause = staEvnt->causeDgn[0].causeVal.val; switch(call_state) { /* Sere ITU-T Q931 for definition of call states */ case 0: /* Remote switch thinks there are no calls on this channel */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_COLLECT: case FTDM_CHANNEL_STATE_DIALING: case FTDM_CHANNEL_STATE_UP: sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; case FTDM_CHANNEL_STATE_TERMINATING: /* We are in the process of clearing local states, just make sure we will not send any messages to remote switch */ sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); break; case FTDM_CHANNEL_STATE_HANGUP: /* This cannot happen, state_advance always sets ftdmchan to STATE_HANGUP_COMPLETE when in STATE_HANGUP and we called check_for_state_change earlier so something is very wrong here!!! */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "How can we we in FTDM_CHANNEL_STATE_HANGUP after checking for state change?\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* We were waiting for remote switch to send RELEASE COMPLETE but this will not happen, so just clear local state */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); break; case FTDM_CHANNEL_STATE_DOWN: /* If our local state is down as well, then there is nothing to do */ break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 1: switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_UP: /* Remote side is still waiting for our CONNECT message */ if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_connect", 1, sngisdn_delayed_connect, (void*) sngisdn_info, NULL); break; } /* Fall-through */ default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 2: /* overlap sending/receiving */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_COLLECT: /* T302 Timeout reached */ /* Send the call to user, and see if they accept it */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "T302 Timer expired, proceeding with call\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); break; case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Remote switch expecting OVERLAP receive, but we are already PROCEEDING\n"); sngisdn_snd_disconnect(ftdmchan); break; case FTDM_CHANNEL_STATE_DOWN: case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* We hung up locally, but remote switch doesn't know send disconnect again*/ sngisdn_snd_disconnect(ftdmchan); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 3: switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_PROGRESS: /* T310 timer has expired */ ftdmchan->caller_data.hangup_cause = staEvnt->causeDgn[0].causeVal.val; ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "T310 Timer expired, hanging up call\n"); sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; case FTDM_CHANNEL_STATE_UP: /* Remote side is still waiting for our CONNECT message */ if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_connect", 1, sngisdn_delayed_connect, (void*) sngisdn_info, NULL); break; } /* Fall-through */ default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 8: /* Remote switch is in "Connect Request state" */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_UP: /* This is ok. We sent a Connect, and we are waiting for a connect ack */ /* Do nothing */ break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* We hung up locally, but remote switch doesn't know send disconnect again*/ sngisdn_snd_disconnect(ftdmchan); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 9: /* Remote switch is in "Incoming call proceeding" state */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_GET_CALLERID: /* Do nothing */ break; case FTDM_CHANNEL_STATE_UP: /* Remote switch missed our CONNECT message, re-send */ ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_connect", 1, sngisdn_delayed_connect, (void*) sngisdn_info, NULL); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 10: /* Remote switch is in active state */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_UP: /* This is ok, they are in active state and we are in active state */ /* Do nothing */ break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* We sent a disconnect message, but remote side missed it ? */ ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "delayed_disconnect", 1, sngisdn_delayed_disconnect, (void*) sngisdn_info, NULL); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); break; } break; case 22: switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_UP: /* Stack is in the process of clearing the call*/ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* Do nothing as we will get a RELEASE COMPLETE */ break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); //ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RESTART); break; } default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Don't know how to handle incompatible state. remote call state:%d our state:%s\n", call_state, ftdm_channel_state2str(ftdmchan->state)); //ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RESTART); break; } } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
void sngisdn_process_rel_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; RelEvnt *relEvnt = &sngisdn_event->event.relEvnt; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing RELEASE/RELEASE COMPLETE (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); if ((suInstId && (sngisdn_info->glare.suInstId == suInstId)) || (spInstId && (sngisdn_info->glare.spInstId == spInstId))) { /* This hangup is for a glared saved call */ ftdm_clear_flag(sngisdn_info, FLAG_DELAYED_REL); clear_call_glare_data(sngisdn_info); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } /* check whether the ftdm channel is in a state to accept a call */ switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* go to DOWN */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); break; case FTDM_CHANNEL_STATE_DOWN: /* do nothing, just drop the message */ break; case FTDM_CHANNEL_STATE_DIALING: /* Remote side rejected our SETUP message on outbound call */ if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP)) { sng_isdn_set_avail_rate(ftdmchan->span, SNGISDN_AVAIL_DOWN); } /* fall-through */ case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_UP: case FTDM_CHANNEL_STATE_RING: /* If we previously had a glare on this channel, this RELEASE could be for the previous call. Confirm whether call_data has not changed while we were waiting for ftdmchan->mutex by comparing suInstId's */ if (((sngisdn_chan_data_t*)ftdmchan->call_data)->suInstId == suInstId || ((sngisdn_chan_data_t*)ftdmchan->call_data)->spInstId == spInstId) { if (relEvnt->causeDgn[0].eh.pres && relEvnt->causeDgn[0].causeVal.pres) { ftdmchan->caller_data.hangup_cause = relEvnt->causeDgn[0].causeVal.val; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "cause:%d\n", ftdmchan->caller_data.hangup_cause); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "RELEASE COMPLETE did not have a cause code\n"); ftdmchan->caller_data.hangup_cause = 0; } sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); } else { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "RELEASE was for previous call (suInstId:%u spInstId:%u)\n", suInstId, spInstId); } break; case FTDM_CHANNEL_STATE_COLLECT: case FTDM_CHANNEL_STATE_GET_CALLERID: ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); break; case FTDM_CHANNEL_STATE_TERMINATING: if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE) && sngisdn_info->glare.suInstId != suInstId) { /* This release if for the outbound call that we already started clearing */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Received RELEASE for local glared call\n"); sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Received release before we could clear local call\n"); /* FS core took too long to respond to the SIG STOP event */ sngisdn_set_flag(sngisdn_info, FLAG_REMOTE_ABORT); /* set abort flag so that we do not transmit another release complete on this channel once FS core is done */ } break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Received RELEASE in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); break; } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
/* Remote side transmit a SETUP */ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; int16_t dChan = sngisdn_event->dChan; uint8_t ces = sngisdn_event->ces; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ConEvnt *conEvnt = &sngisdn_event->event.conEvnt; ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); ftdm_log_chan(sngisdn_info->ftdmchan, FTDM_LOG_DEBUG, "Processing SETUP (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); switch (ftdmchan->state) { case FTDM_CHANNEL_STATE_DOWN: /* Proper state to receive a SETUP */ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_INUSE) || ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Received SETUP but channel is in USE, saving call for later processing\n"); /* the flag the channel as having a collision */ sngisdn_set_flag(sngisdn_info, FLAG_GLARE); /* save the SETUP for processing once the channel has gone to DOWN */ memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt)); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */ sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; break; } sngisdn_info->suInstId = get_unique_suInstId(suId); sngisdn_info->spInstId = spInstId; /* If this is a glared call that was previously saved, we moved all the info to the current call, so clear the glared saved data */ if (sngisdn_info->glare.spInstId == spInstId) { clear_call_glare_data(sngisdn_info); } ftdm_mutex_lock(g_sngisdn_data.ccs[suId].mutex); g_sngisdn_data.ccs[suId].active_suInstIds[sngisdn_info->suInstId] = sngisdn_info; ftdm_mutex_unlock(g_sngisdn_data.ccs[suId].mutex); ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND); if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP && signal_data->signalling == SNGISDN_SIGNALING_NET) { sngisdn_info->ces = ces; } /* try to open the channel */ if (ftdm_channel_open_chan(ftdmchan) != FTDM_SUCCESS) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Failed to open channel"); sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_REL); ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_TEMPORARY_FAILURE; ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_CANCEL); break; } #if 0 /* Export ftdmchan variables here if we need to */ ftdm_channel_add_var(ftdmchan, "isdn_specific_var", "1"); ftdm_channel_add_var(ftdmchan, "isdn_crap", "morecrap"); ftdm_channel_add_var(ftdmchan, "isdn_stuff", "s"); ftdm_channel_add_var(ftdmchan, "isdn_d", "asdsadasdasdsad"); #endif /* Fill in call information */ cpy_calling_num_from_stack(&ftdmchan->caller_data, &conEvnt->cgPtyNmb); cpy_called_num_from_stack(&ftdmchan->caller_data, &conEvnt->cdPtyNmb); cpy_calling_name_from_stack(&ftdmchan->caller_data, &conEvnt->display); if (conEvnt->bearCap[0].eh.pres) { ftdmchan->caller_data.bearer_layer1 = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].usrInfoLyr1Prot.val); ftdmchan->caller_data.bearer_capability = sngisdn_get_infoTranCap_from_stack(conEvnt->bearCap[0].infoTranCap.val); } if (signal_data->switchtype == SNGISDN_SWITCH_NI2) { if (conEvnt->shift11.eh.pres && conEvnt->ni2OctStr.eh.pres) { if (conEvnt->ni2OctStr.str.len == 4 && conEvnt->ni2OctStr.str.val[0] == 0x37) { snprintf(ftdmchan->caller_data.aniII, 5, "%.2d", conEvnt->ni2OctStr.str.val[3]); } } if (signal_data->facility == SNGISDN_OPT_TRUE && conEvnt->facilityStr.eh.pres) { /* Verify whether the Caller Name will come in a subsequent FACILITY message */ uint16_t ret_val; char retrieved_str[255]; ret_val = sng_isdn_retrieve_facility_caller_name(conEvnt->facilityStr.facilityStr.val, conEvnt->facilityStr.facilityStr.len, retrieved_str); /* return values for "sng_isdn_retrieve_facility_information_following": If there will be no information following, or fails to decode IE, returns -1 If there will be no information following, but current FACILITY IE contains a caller name, returns 0 If there will be information following, returns 1 */ if (ret_val == 1) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Expecting Caller name in FACILITY\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_GET_CALLERID); /* Launch timer in case we never get a FACILITY msg */ if (signal_data->facility_timeout) { ftdm_sched_timer(signal_data->sched, "facility_timeout", signal_data->facility_timeout, sngisdn_facility_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_TIMER_FACILITY]); } break; } else if (ret_val == 0) { strcpy(ftdmchan->caller_data.cid_name, retrieved_str); } } } if (signal_data->overlap_dial == SNGISDN_OPT_TRUE && !conEvnt->sndCmplt.eh.pres) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); } else { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); } break; case FTDM_CHANNEL_STATE_TERMINATING: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Processing SETUP in TERMINATING state, saving SETUP info for later processing\n"); ftdm_assert(!sngisdn_test_flag(sngisdn_info, FLAG_GLARE), "Trying to save GLARE info, but we already had a glare\n"); sngisdn_set_flag(sngisdn_info, FLAG_GLARE); /* save the SETUP for processing once the channel has gone to DOWN */ memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt)); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */ sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; break; case FTDM_CHANNEL_STATE_DIALING: /* glare */ if (signal_data->signalling == SNGISDN_SIGNALING_NET) { /* Save inbound call info so we can send a RELEASE when this channel goes to a different state */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Processing SETUP in DIALING state, rejecting inbound call\n"); sngisdn_set_flag(sngisdn_info, FLAG_DELAYED_REL); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = get_unique_suInstId(suId); sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; ftdmchan->caller_data.hangup_cause = 0x2C; /* Channel requested not available */ ftdm_sched_timer(signal_data->sched, "delayed_release", 1, sngisdn_delayed_release, (void*) sngisdn_info, NULL); } else { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "Processing SETUP in DIALING state, saving SETUP info for later processing\n"); /* the flag the channel as having a collision */ ftdm_assert(!sngisdn_test_flag(sngisdn_info, FLAG_GLARE), "Trying to save GLARE info, but we already had a glare"); sngisdn_set_flag(sngisdn_info, FLAG_GLARE); /* save the SETUP for processing once the channel has gone to DOWN */ memcpy(&sngisdn_info->glare.setup, conEvnt, sizeof(*conEvnt)); sngisdn_info->glare.suId = suId; sngisdn_info->glare.suInstId = suInstId; /* Do not generate a suInstId now, we will generate when glared call gets extracted */ sngisdn_info->glare.spInstId = spInstId; sngisdn_info->glare.dChan = dChan; sngisdn_info->glare.ces = ces; ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); } break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing SETUP in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); break; } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
void sngisdn_process_cnst_ind (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; uint8_t ces = sngisdn_event->ces; uint8_t evntType = sngisdn_event->evntType; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; CnStEvnt *cnStEvnt = &sngisdn_event->event.cnStEvnt; ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing %s (suId:%u suInstId:%u spInstId:%u ces:%d)\n", (evntType == MI_ALERTING)?"ALERT": (evntType == MI_CALLPROC)?"PROCEED": (evntType == MI_PROGRESS)?"PROGRESS": (evntType == MI_SETUPACK)?"SETUP ACK": (evntType == MI_INFO)?"INFO":"UNKNOWN", suId, suInstId, spInstId, ces); switch(evntType) { case MI_PROGRESS: if (signal_data->switchtype == SNGISDN_SWITCH_NI2 && cnStEvnt->causeDgn[0].eh.pres && cnStEvnt->causeDgn[0].causeVal.pres) { switch(cnStEvnt->causeDgn[0].causeVal.val) { case 17: /* User Busy */ case 18: /* No User responding */ case 19: /* User alerting, no answer */ case 21: /* Call rejected, the called party does not with to accept this call */ case 27: /* Destination out of order */ case 31: /* Normal, unspecified */ case 34: /* Circuit/Channel congestion */ case 41: /* Temporary failure */ case 42: /* Switching equipment is experiencing a period of high traffic */ case 47: /* Resource unavailable */ case 58: /* Bearer Capability not available */ case 63: /* Service or option not available */ case 65: /* Bearer Cap not implemented, not supported */ case 79: /* Service or option not implemented, unspecified */ ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Cause requires disconnect (cause:%d)\n", cnStEvnt->causeDgn[0].causeVal.val); ftdmchan->caller_data.hangup_cause = cnStEvnt->causeDgn[0].causeVal.val; sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); goto sngisdn_process_cnst_ind_end; } } /* fall-through */ case MI_ALERTING: case MI_CALLPROC: switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_DIALING: if (evntType == MI_PROGRESS || (cnStEvnt->progInd.eh.pres && cnStEvnt->progInd.progDesc.val == IN_PD_IBAVAIL)) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); } else { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS); } break; case FTDM_CHANNEL_STATE_PROGRESS: if (evntType == MI_PROGRESS || (cnStEvnt->progInd.eh.pres && cnStEvnt->progInd.progDesc.val == IN_PD_IBAVAIL)) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); } break; case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: /* We are already in progress media, we can't go to any higher state except up */ /* Do nothing */ break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing ALERT/PROCEED/PROGRESS in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); /* Start the disconnect procedure */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; } break; case MI_SETUPACK: ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Processing SETUP_ACK, but overlap sending not implemented (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); break; case MI_INFO: ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing INFO (suId:%u suInstId:%u spInstId:%u)\n", suId, suInstId, spInstId); if (cnStEvnt->cdPtyNmb.eh.pres) { switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_COLLECT: { ftdm_size_t min_digits = ((sngisdn_span_data_t*)ftdmchan->span->signal_data)->min_digits; ftdm_size_t num_digits; cpy_called_num_from_stack(&ftdmchan->caller_data, &cnStEvnt->cdPtyNmb); num_digits = strlen(ftdmchan->caller_data.dnis.digits); if (cnStEvnt->sndCmplt.eh.pres || num_digits >= min_digits) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING); } else { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "received %d of %d digits\n", num_digits, min_digits); } } break; case FTDM_CHANNEL_STATE_RING: case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_UP: ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Receiving more digits %s, but we already proceeded with call\n", cnStEvnt->cdPtyNmb.nmbDigits.val); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "\n", suId, suInstId, spInstId); break; } } break; default: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Unhandled STATUS event\n"); break; } sngisdn_process_cnst_ind_end: ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
/* Remote side transmit a CONNECT or CONNECT ACK */ void sngisdn_process_con_cfm (sngisdn_event_data_t *sngisdn_event) { ISDN_FUNC_TRACE_ENTER(__FUNCTION__); int16_t suId = sngisdn_event->suId; uint32_t suInstId = sngisdn_event->suInstId; uint32_t spInstId = sngisdn_event->spInstId; uint8_t ces = sngisdn_event->ces; sngisdn_chan_data_t *sngisdn_info = sngisdn_event->sngisdn_info; ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; /* Function does not require any info from conStEvnt struct for now */ /* CnStEvnt *cnStEvnt = &sngisdn_event->event.cnStEvnt; */ ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "State change flag pending\n"); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Processing CONNECT/CONNECT ACK (suId:%u suInstId:%u spInstId:%u ces:%d)\n", suId, suInstId, spInstId, ces); if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP && ((sngisdn_span_data_t*)ftdmchan->span->signal_data)->signalling == SNGISDN_SIGNALING_NET) { if(sngisdn_info->ces == CES_MNGMNT) { /* We assign the call to the first TE */ sngisdn_info->ces = ces; } else { /* We already assigned this call, do nothing */ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call already assigned, ignoring connect\n"); ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } } if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: case FTDM_CHANNEL_STATE_DIALING: ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP); break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing CONNECT/CONNECT ACK in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); /* Start the disconnect procedure */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; } } else { switch(ftdmchan->state) { case FTDM_CHANNEL_STATE_UP: /* This is the only valid state we should get a CONNECT ACK on */ /* do nothing */ break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: /* We just hung up an incoming call right after we sent a CONNECT so ignore this message */ break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Processing CONNECT/CONNECT ACK in an invalid state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); /* Start the disconnect procedure */ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); break; } } ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; }
static ftdm_status_t att_courtesy_vru(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* args) { char dtmf_digits[64]; ftdm_status_t status = FTDM_FAIL; sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; char *p = args; uint8_t forced_answer = 0; switch (signal_data->switchtype) { case SNGISDN_SWITCH_5ESS: case SNGISDN_SWITCH_4ESS: break; default: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "AT&T Courtesy Transfer not supported for switchtype\n"); return FTDM_FAIL; } while (!ftdm_strlen_zero(p)) { if (!isdigit(*p) && *p != 'w' && *p != 'W') { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot transfer to non-numeric number:%s\n", args); return FTDM_FAIL; } p++; } ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Performing AT&T Courtesy Transfer-VRU%s to %s\n", (type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) ?"--data" : "", args); sprintf(dtmf_digits, "*8w%s", args); ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending digits %s\n", dtmf_digits); switch (ftdmchan->last_state) { case FTDM_CHANNEL_STATE_PROCEED: case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: /* Call has to be in answered state - so send a CONNECT message if we did not answer this call yet */ forced_answer++; sngisdn_snd_connect(ftdmchan); /* fall-through */ case FTDM_CHANNEL_STATE_UP: memset(&sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits, 0, sizeof(sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits)); sngisdn_info->transfer_data.type = type; /* We will be polling the channel for IO so that we can receive the DTMF events, * Disable user RX otherwise it is a race between who calls channel_read */ ftdm_set_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED); ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL); ftdm_channel_command(ftdmchan, FTDM_COMMAND_SEND_DTMF, dtmf_digits); if (type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) { /* We need to save transfer data, so we can send it in the disconnect msg */ const char *val = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_data"); if (ftdm_strlen_zero(val)) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform data transfer because transfer_data variable is not set\n"); goto done; } if (strlen(val) > COURTESY_TRANSFER_MAX_DATA_SIZE) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Data exceeds max size (len:%"FTDM_SIZE_FMT" max:%d), cannot perform transfer\n", strlen(val), COURTESY_TRANSFER_MAX_DATA_SIZE); goto done; } memcpy(sngisdn_info->transfer_data.tdata.att_courtesy_vru.data, val, strlen(val)); } ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP); if (forced_answer) { /* Notify the user that we answered the call */ sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_UP); } if (signal_data->transfer_timeout) { ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "courtesy_transfer_timeout", signal_data->transfer_timeout, att_courtesy_transfer_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_CHAN_TIMER_ATT_TRANSFER]); } status = FTDM_SUCCESS; break; default: ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer in state %s\n", ftdm_channel_state2str(ftdmchan->state)); break; } done: return status; }
static ftdm_status_t ftdm_gsm_state_advance(ftdm_channel_t *ftdmchan) { ftdm_log_chan(ftdmchan, STATE_ADVANCE_LOG_LEVEL , "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state)); ftdm_channel_complete_state(ftdmchan); switch (ftdmchan->state) { /* starting an incoming call */ case FTDM_CHANNEL_STATE_COLLECT: { } break; /* starting an outgoing call */ case FTDM_CHANNEL_STATE_DIALING: { uint32_t interval = 0; ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting outgoing call with interval %d\n", interval); { ftdm_gsm_span_data_t *gsm_data; gsm_data = ftdmchan->span->signal_data; gsm_data->call_id = g_outbound_call_id++; wat_con_event_t con_event; memset(&con_event, 0, sizeof(con_event)); ftdm_set_string(con_event.called_num.digits, ftdmchan->caller_data.dnis.digits); ftdm_log(FTDM_LOG_DEBUG, "Dialing number %s\n", con_event.called_num.digits); wat_con_req(ftdmchan->span->span_id, gsm_data->call_id , &con_event); SEND_STATE_SIGNAL(FTDM_SIGEVENT_DIALING); } } break; /* incoming call was offered */ case FTDM_CHANNEL_STATE_RING: /* notify the user about the new call */ ftdm_log(FTDM_LOG_INFO, "Answering Incomming Call\r\n"); SEND_STATE_SIGNAL(FTDM_SIGEVENT_START); break; /* the call is making progress */ case FTDM_CHANNEL_STATE_PROGRESS: case FTDM_CHANNEL_STATE_PROGRESS_MEDIA: { SEND_STATE_SIGNAL(FTDM_SIGEVENT_PROGRESS_MEDIA); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP); } break; /* the call was answered */ case FTDM_CHANNEL_STATE_UP: { if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) { SEND_STATE_SIGNAL(FTDM_SIGEVENT_UP); } else { ftdm_gsm_span_data_t *gsm_data; gsm_data = ftdmchan->span->signal_data; wat_con_cfm(ftdmchan->span->span_id, gsm_data->call_id); } } break; /* just got hangup */ case FTDM_CHANNEL_STATE_HANGUP: { ftdm_gsm_span_data_t *gsm_data; gsm_data = ftdmchan->span->signal_data; wat_rel_req(ftdmchan->span->span_id, gsm_data->call_id); gsm_data->call_id = 0; SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); } break; case FTDM_CHANNEL_STATE_TERMINATING: { SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); } break; /* finished call for good */ case FTDM_CHANNEL_STATE_DOWN: { ftdm_channel_t *closed_chan; closed_chan = ftdmchan; ftdm_channel_close(&closed_chan); ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "State processing ended.\n"); SEND_STATE_SIGNAL(FTDM_SIGEVENT_STOP); } break; /* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */ case FTDM_CHANNEL_STATE_RINGING: ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n"); SEND_STATE_SIGNAL(FTDM_SIGEVENT_RINGING); break; /* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */ case FTDM_CHANNEL_STATE_RESET: { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n"); ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); } break; default: { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state)); } break; } return FTDM_SUCCESS; }
static void handle_pri_passive_event(pritap_t *pritap, pri_event *e) { passive_call_t *pcall = NULL; passive_call_t *peerpcall = NULL; ftdm_channel_t *fchan = NULL; ftdm_channel_t *peerfchan = NULL; int layer1, transcap = 0; int crv = 0; pritap_t *peertap = pritap->peerspan->signal_data; switch (e->e) { case PRI_EVENT_RING: /* we cannot use ftdm_channel_t because we still dont know which channel will be used * (ie, flexible channel was requested), thus, we need our own list of call references */ crv = tap_pri_get_crv(pritap->pri, e->ring.call); ftdm_log(FTDM_LOG_DEBUG, "Ring on channel %s:%d:%d with callref %d\n", pritap->span->name, PRI_SPAN(e->ring.channel), PRI_CHANNEL(e->ring.channel), crv); pcall = tap_pri_get_pcall_bycrv(pritap, crv); if (pcall) { ftdm_log(FTDM_LOG_WARNING, "There is a call with callref %d already, ignoring duplicated ring event\n", crv); break; } /* Try to get a recycled call (ie, e->ring.call is a call that the PRI stack allocated previously and then * re-used for the next RING event because we did not destroy it fast enough) */ pcall = tap_pri_get_pcall(pritap, e->ring.call); if (!pcall) { /* ok so the call is really not known to us, let's get a new one */ pcall = tap_pri_get_pcall(pritap, NULL); if (!pcall) { ftdm_log(FTDM_LOG_ERROR, "Failed to get a free passive PRI call slot for callref %d, this is a bug!\n", crv); break; } } pcall->callref = e->ring.call; ftdm_set_string(pcall->callingnum.digits, e->ring.callingnum); ftdm_set_string(pcall->callingani.digits, e->ring.callingani); ftdm_set_string(pcall->callednum.digits, e->ring.callednum); ftdm_set_string(pcall->callingname, e->ring.callingname); break; case PRI_EVENT_PROGRESS: crv = tap_pri_get_crv(pritap->pri, e->proceeding.call); ftdm_log(FTDM_LOG_DEBUG, "Progress on channel %s:%d:%d with callref %d\n", pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv); break; case PRI_EVENT_PROCEEDING: crv = tap_pri_get_crv(pritap->pri, e->proceeding.call); /* at this point we should know the real b chan that will be used and can therefore proceed to notify about the call, but * only if a couple of call tests are passed first */ ftdm_log(FTDM_LOG_DEBUG, "Proceeding on channel %s:%d:%d with callref %d\n", pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv); /* check that we already know about this call in the peer PRI (which was the one receiving the PRI_EVENT_RING event) */ if (!(pcall = tap_pri_get_pcall_bycrv(peertap, crv))) { ftdm_log(FTDM_LOG_DEBUG, "ignoring proceeding in channel %s:%d:%d for callref %d since we don't know about it\n", pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv); break; } if (pcall->proceeding) { ftdm_log(FTDM_LOG_DEBUG, "Ignoring duplicated proceeding with callref %d\n", crv); break; } pcall->proceeding = 1; /* This call should not be known to this PRI yet ... */ if ((peerpcall = tap_pri_get_pcall_bycrv(pritap, crv))) { ftdm_log(FTDM_LOG_ERROR, "ignoring proceeding in channel %s:%d:%d for callref %d, dup???\n", pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv); break; } /* Check if the call pointer is being recycled */ peerpcall = tap_pri_get_pcall(pritap, e->proceeding.call); if (!peerpcall) { peerpcall = tap_pri_get_pcall(pritap, NULL); if (!peerpcall) { ftdm_log(FTDM_LOG_ERROR, "Failed to get a free peer PRI passive call slot for callref %d in span %s, this is a bug!\n", crv, pritap->span->name); break; } peerpcall->callref = e->proceeding.call; } /* check that the layer 1 and trans capability are supported */ layer1 = pri_get_layer1(peertap->pri, pcall->callref); transcap = pri_get_transcap(peertap->pri, pcall->callref); if (PRI_LAYER_1_ULAW != layer1 && PRI_LAYER_1_ALAW != layer1) { ftdm_log(FTDM_LOG_NOTICE, "Not monitoring callref %d with unsupported layer 1 format %d\n", crv, layer1); break; } if (transcap != PRI_TRANS_CAP_SPEECH && transcap != PRI_TRANS_CAP_3_1K_AUDIO && transcap != PRI_TRANS_CAP_7K_AUDIO) { ftdm_log(FTDM_LOG_NOTICE, "Not monitoring callref %d with unsupported capability %d\n", crv, transcap); break; } fchan = tap_pri_get_fchan(pritap, pcall, e->proceeding.channel); if (!fchan) { ftdm_log(FTDM_LOG_ERROR, "Proceeding requested on odd/unavailable channel %s:%d:%d for callref %d\n", pritap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv); break; } peerfchan = tap_pri_get_fchan(peertap, pcall, e->proceeding.channel); if (!peerfchan) { ftdm_log(FTDM_LOG_ERROR, "Proceeding requested on odd/unavailable channel %s:%d:%d for callref %d\n", peertap->span->name, PRI_SPAN(e->proceeding.channel), PRI_CHANNEL(e->proceeding.channel), crv); break; } pcall->fchan = fchan; peerpcall->fchan = fchan; ftdm_log_chan(fchan, FTDM_LOG_NOTICE, "Starting new tapped call with callref %d\n", crv); ftdm_channel_lock(fchan); fchan->call_data = peerfchan; ftdm_set_state(fchan, FTDM_CHANNEL_STATE_RING); ftdm_channel_unlock(fchan); ftdm_channel_lock(peerfchan); peerfchan->call_data = fchan; ftdm_channel_unlock(peerfchan); break; case PRI_EVENT_ANSWER: crv = tap_pri_get_crv(pritap->pri, e->answer.call); ftdm_log(FTDM_LOG_DEBUG, "Answer on channel %s:%d:%d with callref %d\n", pritap->span->name, PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), crv); if (!(pcall = tap_pri_get_pcall_bycrv(pritap, crv))) { ftdm_log(FTDM_LOG_DEBUG, "ignoring answer in channel %s:%d:%d for callref %d since we don't know about it\n", pritap->span->name, PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->proceeding.channel), crv); break; } if (!pcall->fchan) { ftdm_log(FTDM_LOG_ERROR, "Received answer in channel %s:%d:%d for callref %d but we never got a channel\n", pritap->span->name, PRI_SPAN(e->answer.channel), PRI_CHANNEL(e->answer.channel), crv); break; } ftdm_channel_lock(pcall->fchan); ftdm_log_chan(pcall->fchan, FTDM_LOG_NOTICE, "Tapped call was answered in state %s\n", ftdm_channel_state2str(pcall->fchan->state)); ftdm_set_pflag(pcall->fchan, PRITAP_NETWORK_ANSWER); ftdm_set_state(pcall->fchan, FTDM_CHANNEL_STATE_UP); ftdm_channel_unlock(pcall->fchan); break; case PRI_EVENT_HANGUP_REQ: crv = tap_pri_get_crv(pritap->pri, e->hangup.call); ftdm_log(FTDM_LOG_DEBUG, "Hangup on channel %s:%d:%d with callref %d\n", pritap->span->name, PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), crv); if (!(pcall = tap_pri_get_pcall_bycrv(pritap, crv))) { ftdm_log(FTDM_LOG_DEBUG, "ignoring hangup in channel %s:%d:%d for callref %d since we don't know about it", pritap->span->name, PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), crv); break; } if (pcall->fchan) { fchan = pcall->fchan; ftdm_channel_lock(fchan); if (fchan->state < FTDM_CHANNEL_STATE_TERMINATING) { ftdm_set_state(fchan, FTDM_CHANNEL_STATE_TERMINATING); } pcall->fchan = NULL; /* after this event we're not supposed to need to do anything with the channel anymore */ ftdm_channel_unlock(fchan); } tap_pri_put_pcall(pritap, e->hangup.call); tap_pri_put_pcall(peertap, e->hangup.call); break; case PRI_EVENT_HANGUP_ACK: crv = tap_pri_get_crv(pritap->pri, e->hangup.call); ftdm_log(FTDM_LOG_DEBUG, "Hangup ack on channel %s:%d:%d with callref %d\n", pritap->span->name, PRI_SPAN(e->hangup.channel), PRI_CHANNEL(e->hangup.channel), crv); tap_pri_put_pcall(pritap, e->hangup.call); tap_pri_put_pcall(peertap, e->hangup.call); break; default: ftdm_log(FTDM_LOG_DEBUG, "Ignoring passive event %s on span %s\n", pri_event2str(e->gen.e), pritap->span->name); break; } }