diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index 6bd486e49b..b0398ef90c 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -235,6 +235,7 @@ ftmod_sangoma_isdn_la_SOURCES = \ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c \ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cntrl.c \ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c \ + $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c \ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c \ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_cntrl.c \ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_cfg.c \ diff --git a/libs/freetdm/conf/freetdm.conf.xml b/libs/freetdm/conf/freetdm.conf.xml index 7d5de5a189..29232d927b 100644 --- a/libs/freetdm/conf/freetdm.conf.xml +++ b/libs/freetdm/conf/freetdm.conf.xml @@ -18,6 +18,11 @@ with the signaling protocols that you can run on top of your I/O interfaces. --> + + diff --git a/libs/freetdm/docs/async.txt b/libs/freetdm/docs/async.txt index 04069ebebd..c5d706ecba 100644 --- a/libs/freetdm/docs/async.txt +++ b/libs/freetdm/docs/async.txt @@ -2,6 +2,7 @@ APIs that result in an event when the API returns FTDM_SUCCESS ftdm_channel_call_answer() ftdm_channel_call_indicate() +ftdm_channel_call_transfer() FTDM_SIGEVENT_INDICATION_COMPLETED *note that FTDM_SIGEVENT_INDICATION_COMPLETED has associated data to indicate the result of the indication *note this event is only delivered on non-blocking channels diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 618f2501ff..731893a2f6 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -67,7 +67,8 @@ typedef enum { TFLAG_CODEC = (1 << 2), TFLAG_BREAK = (1 << 3), TFLAG_HOLD = (1 << 4), - TFLAG_DEAD = (1 << 5) + TFLAG_DEAD = (1 << 5), + TFLAG_TRANSFER = (1 << 6), } TFLAGS; static struct { @@ -87,7 +88,9 @@ static struct { analog_option_t analog_options; switch_hash_t *ss7_configs; int sip_headers; - int crash_on_assert; + uint8_t crash_on_assert; + uint8_t fail_on_error; + uint8_t config_error; } globals; /* private data attached to each fs session */ @@ -880,10 +883,30 @@ static switch_status_t channel_receive_message_b(switch_core_session_t *session, ftdm_channel_call_answer(tech_pvt->ftdmchan); } break; + case SWITCH_MESSAGE_INDICATE_REDIRECT: + case SWITCH_MESSAGE_INDICATE_DEFLECT: + { + ftdm_usrmsg_t usrmsg; + const char *val = NULL; + + memset(&usrmsg, 0, sizeof(usrmsg)); + + if ((val = switch_channel_get_variable(channel, "freetdm_transfer_data"))) { + ftdm_usrmsg_add_var(&usrmsg, "transfer_data", val); + } + + switch_set_flag(tech_pvt, TFLAG_TRANSFER); + if (ftdm_channel_call_transfer_ex(tech_pvt->ftdmchan, msg->string_arg, &usrmsg) != FTDM_SUCCESS) { + switch_clear_flag(tech_pvt, TFLAG_TRANSFER); + } + while (switch_test_flag(tech_pvt, TFLAG_TRANSFER)) { + switch_yield(100000); + } + } default: break; } - + return SWITCH_STATUS_SUCCESS; } @@ -1747,11 +1770,13 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session static FIO_SIGNAL_CB_FUNCTION(on_common_signal) { - switch_event_t *event = NULL; - ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE; uint32_t chanid, spanid; + switch_event_t *event = NULL; + ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE; + chanid = ftdm_channel_get_id(sigmsg->channel); spanid = ftdm_channel_get_span_id(sigmsg->channel); + switch (sigmsg->event_id) { case FTDM_SIGEVENT_ALARM_CLEAR: @@ -1786,14 +1811,44 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal) } return FTDM_SUCCESS; } + case FTDM_SIGEVENT_TRANSFER_COMPLETED: + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + private_t *tech_pvt = NULL; - case FTDM_SIGEVENT_RELEASED: + if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + channel = switch_core_session_get_channel(session); + tech_pvt = switch_core_session_get_private(session); + + switch_clear_flag_locked(tech_pvt, TFLAG_TRANSFER); + switch_channel_set_variable(channel, "freetdm_transfer_response", ftdm_transfer_response2str(sigmsg->ev_data.transfer_completed.response)); + switch_core_session_rwunlock(session); + } + return FTDM_SUCCESS; + } + break; + case FTDM_SIGEVENT_RELEASED: case FTDM_SIGEVENT_INDICATION_COMPLETED: case FTDM_SIGEVENT_DIALING: - { + { /* Swallow these events */ return FTDM_BREAK; - } + } + break; + case FTDM_SIGEVENT_STOP: + case FTDM_SIGEVENT_RESTART: + { + switch_core_session_t *session = NULL; + private_t *tech_pvt = NULL; + while((session = ftdm_channel_get_session(sigmsg->channel, 0))) { + tech_pvt = switch_core_session_get_private(session); + + switch_clear_flag_locked(tech_pvt, TFLAG_TRANSFER); + switch_core_session_rwunlock(session); + return FTDM_SUCCESS; + } + } break; default: return FTDM_SUCCESS; @@ -2348,6 +2403,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) break; case FTDM_SIGEVENT_PROCEED: case FTDM_SIGEVENT_FACILITY: + case FTDM_SIGEVENT_TRANSFER_COMPLETED: /* FS does not have handlers for these messages, so ignore them for now */ break; default: @@ -2427,6 +2483,10 @@ static uint32_t enable_analog_option(const char *str, uint32_t current_options) } +#define CONFIG_ERROR(...) do { \ + ftdm_log(FTDM_LOG_ERROR, __VA_ARGS__); \ + globals.config_error = 1; \ + } while(0) /* create ftdm_conf_node_t tree based on a fixed pattern XML configuration list * last 2 args are for limited aka dumb recursivity * */ @@ -2619,7 +2679,7 @@ static int add_profile_parameters(switch_xml_t cfg, const char *profname, ftdm_c profnode = switch_xml_child(cfg, "config_profiles"); if (!profnode) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot find profile '%s', there is no 'config_profiles' XML section\n", profname); + CONFIG_ERROR("cannot find profile '%s', there is no 'config_profiles' XML section\n", profname); return 0; } @@ -2635,7 +2695,7 @@ static int add_profile_parameters(switch_xml_t cfg, const char *profname, ftdm_c } if (!profile) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to find profile '%s'\n", profname); + CONFIG_ERROR("failed to find profile '%s'\n", profname); return 0; } @@ -2670,7 +2730,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans) unsigned paramindex = 0; if (!name && !id) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "sangoma isdn span missing required attribute 'id' or 'name', skipping ...\n"); + CONFIG_ERROR("sangoma isdn span missing required attribute 'id' or 'name', skipping ...\n"); continue; } @@ -2688,7 +2748,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans) } if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); continue; } @@ -2716,7 +2776,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans) char *val = (char *) switch_xml_attr_soft(param, "value"); if (ftdm_array_len(spanparameters) - 1 == paramindex) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for ss7 span, ignoring any parameter after %s\n", var); + CONFIG_ERROR("Too many parameters for ss7 span, ignoring any parameter after %s\n", var); break; } @@ -2746,10 +2806,10 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans) int calls; int seconds; if (sscanf(val, "%d/%d", &calls, &seconds) != 2) { - ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var); + CONFIG_ERROR("Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var); } else { if (calls < 1 || seconds < 1) { - ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, minimum call limit must be 1 per second\n", var); + CONFIG_ERROR("Invalid %s parameter value, minimum call limit must be 1 per second\n", var); } else { SPAN_CONFIG[span_id].limit_calls = calls; SPAN_CONFIG[span_id].limit_seconds = seconds; @@ -2759,7 +2819,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans) if (!strcasecmp(val, "answer")) { SPAN_CONFIG[span_id].limit_reset_event = FTDM_LIMIT_RESET_ON_ANSWER; } else { - ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, only accepted event is 'answer'\n", var); + CONFIG_ERROR("Invalid %s parameter value, only accepted event is 'answer'\n", var); } } else { spanparameters[paramindex].var = var; @@ -2772,7 +2832,7 @@ static void parse_bri_pri_spans(switch_xml_t cfg, switch_xml_t spans) "sangoma_isdn", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring Sangoma ISDN FreeTDM span %d\n", span_id); + CONFIG_ERROR("Error configuring Sangoma ISDN FreeTDM span %d\n", span_id); continue; } SPAN_CONFIG[span_id].span = span; @@ -2789,15 +2849,11 @@ static switch_status_t load_config(void) const char *cf = "freetdm.conf"; switch_xml_t cfg, xml, settings, param, spans, myspan; ftdm_conf_node_t *ss7confnode = NULL; - ftdm_span_t *boost_spans[FTDM_MAX_PHYSICAL_SPANS_PER_LOGICAL_SPAN]; - ftdm_span_t *boost_span = NULL; - unsigned boosti = 0; unsigned int i = 0; ftdm_channel_t *fchan = NULL; ftdm_iterator_t *chaniter = NULL; ftdm_iterator_t *curr = NULL; - memset(boost_spans, 0, sizeof(boost_spans)); memset(&globals, 0, sizeof(globals)); switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool); if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { @@ -2816,6 +2872,8 @@ static switch_status_t load_config(void) switch_set_string(globals.hold_music, val); } else if (!strcasecmp(var, "crash-on-assert")) { globals.crash_on_assert = switch_true(val); + } else if (!strcasecmp(var, "fail-on-error")) { + globals.fail_on_error = switch_true(val); } else if (!strcasecmp(var, "sip-headers")) { globals.sip_headers = switch_true(val); } else if (!strcasecmp(var, "enable-analog-option")) { @@ -2846,11 +2904,11 @@ static switch_status_t load_config(void) uint32_t span_id = 0; unsigned paramindex = 0; if (!name && !id) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ss7 span missing required attribute 'id' or 'name', skipping ...\n"); + CONFIG_ERROR("ss7 span missing required attribute 'id' or 'name', skipping ...\n"); continue; } if (!configname) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ss7 span missing required attribute, skipping ...\n"); + CONFIG_ERROR("ss7 span missing required attribute, skipping ...\n"); continue; } if (name) { @@ -2867,7 +2925,7 @@ static switch_status_t load_config(void) } if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); continue; } @@ -2877,7 +2935,7 @@ static switch_status_t load_config(void) ss7confnode = get_ss7_config_node(cfg, configname); if (!ss7confnode) { - ftdm_log(FTDM_LOG_ERROR, "Error finding ss7config '%s' for FreeTDM span id: %s\n", configname, switch_str_nil(id)); + CONFIG_ERROR("Error finding ss7config '%s' for FreeTDM span id: %s\n", configname, switch_str_nil(id)); continue; } @@ -2891,7 +2949,7 @@ static switch_status_t load_config(void) char *val = (char *) switch_xml_attr_soft(param, "value"); if (ftdm_array_len(spanparameters) - 1 == paramindex) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for ss7 span, ignoring any parameter after %s\n", var); + CONFIG_ERROR("Too many parameters for ss7 span, ignoring any parameter after %s\n", var); break; } @@ -2910,7 +2968,7 @@ static switch_status_t load_config(void) "sangoma_ss7", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring ss7 FreeTDM span %d\n", span_id); + CONFIG_ERROR("Error configuring ss7 FreeTDM span %d\n", span_id); continue; } SPAN_CONFIG[span_id].span = span; @@ -2961,7 +3019,7 @@ static switch_status_t load_config(void) } if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); continue; } @@ -2995,10 +3053,10 @@ static switch_status_t load_config(void) int calls; int seconds; if (sscanf(val, "%d/%d", &calls, &seconds) != 2) { - ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var); + CONFIG_ERROR("Invalid %s parameter, format example: 3/1 for 3 calls per second\n", var); } else { if (calls < 1 || seconds < 1) { - ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, minimum call limit must be 1 per second\n", var); + CONFIG_ERROR("Invalid %s parameter value, minimum call limit must be 1 per second\n", var); } else { SPAN_CONFIG[span_id].limit_calls = calls; SPAN_CONFIG[span_id].limit_seconds = seconds; @@ -3008,7 +3066,7 @@ static switch_status_t load_config(void) if (!strcasecmp(val, "answer")) { SPAN_CONFIG[span_id].limit_reset_event = FTDM_LIMIT_RESET_ON_ANSWER; } else { - ftdm_log(FTDM_LOG_ERROR, "Invalid %s parameter value, only accepted event is 'answer'\n", var); + CONFIG_ERROR("Invalid %s parameter value, only accepted event is 'answer'\n", var); } } else if (!strcasecmp(var, "dial-regex")) { dial_regex = val; @@ -3036,7 +3094,7 @@ static switch_status_t load_config(void) } if (!id && !name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + CONFIG_ERROR("span missing required param 'id'\n"); continue; } @@ -3066,7 +3124,7 @@ static switch_status_t load_config(void) } if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); continue; } @@ -3086,7 +3144,7 @@ static switch_status_t load_config(void) "callwaiting", &callwaiting, "wait_dialtone_timeout", &dialtone_timeout, FTDM_TAG_END) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span)); + CONFIG_ERROR("Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span)); continue; } @@ -3162,7 +3220,7 @@ static switch_status_t load_config(void) } if (!id && !name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required param 'id'\n"); + CONFIG_ERROR("span missing required param 'id'\n"); continue; } @@ -3194,7 +3252,7 @@ static switch_status_t load_config(void) } if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); + CONFIG_ERROR("Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); continue; } @@ -3208,7 +3266,7 @@ static switch_status_t load_config(void) "digit_timeout", &to, "max_dialstr", &max, FTDM_TAG_END) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d\n", span_id); + CONFIG_ERROR("Error starting FreeTDM span %d\n", span_id); continue; } @@ -3245,7 +3303,7 @@ static switch_status_t load_config(void) uint32_t span_id = 0; if (!name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n"); + CONFIG_ERROR("span missing required attribute 'name'\n"); continue; } @@ -3256,7 +3314,7 @@ static switch_status_t load_config(void) char *val = (char *) switch_xml_attr_soft(param, "value"); if (ftdm_array_len(spanparameters) - 1 == paramindex) { - ftdm_log(FTDM_LOG_ERROR, "Too many parameters for pri span '%s', ignoring everything after '%s'\n", name, var); + CONFIG_ERROR("Too many parameters for pri span '%s', ignoring everything after '%s'\n", name, var); break; } @@ -3278,13 +3336,13 @@ static switch_status_t load_config(void) zstatus = ftdm_span_find_by_name(name, &span); if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name); + CONFIG_ERROR("Error finding FreeTDM span %s\n", name); continue; } span_id = ftdm_span_get_id(span); if (ftdm_configure_span_signaling(span, "isdn", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name); + CONFIG_ERROR("Error configuring FreeTDM span %s\n", name); continue; } @@ -3311,7 +3369,7 @@ static switch_status_t load_config(void) int span_id = 0; if (!name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n"); + CONFIG_ERROR("span missing required attribute 'name'\n"); continue; } @@ -3322,7 +3380,7 @@ static switch_status_t load_config(void) char *val = (char *) switch_xml_attr_soft(param, "value"); if (ftdm_array_len(spanparameters) - 1 == paramindex) { - ftdm_log(FTDM_LOG_ERROR, "Too many parameters for pritap span '%s', ignoring everything after '%s'\n", name, var); + CONFIG_ERROR("Too many parameters for pritap span '%s', ignoring everything after '%s'\n", name, var); break; } @@ -3339,13 +3397,13 @@ static switch_status_t load_config(void) zstatus = ftdm_span_find_by_name(name, &span); if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name); + CONFIG_ERROR("Error finding FreeTDM span %s\n", name); continue; } span_id = ftdm_span_get_id(span); if (ftdm_configure_span_signaling(span, "pritap", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name); + CONFIG_ERROR("Error configuring FreeTDM span %s\n", name); continue; } @@ -3371,7 +3429,7 @@ static switch_status_t load_config(void) uint32_t span_id = 0; if (!name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "span missing required attribute 'name'\n"); + CONFIG_ERROR("span missing required attribute 'name'\n"); continue; } @@ -3382,7 +3440,7 @@ static switch_status_t load_config(void) char *val = (char *) switch_xml_attr_soft(param, "value"); if (ftdm_array_len(spanparameters) - 1 == paramindex) { - ftdm_log(FTDM_LOG_ERROR, "Too many parameters for libpri span, ignoring everything after '%s'\n", var); + CONFIG_ERROR("Too many parameters for libpri span, ignoring everything after '%s'\n", var); break; } @@ -3404,13 +3462,13 @@ static switch_status_t load_config(void) zstatus = ftdm_span_find_by_name(name, &span); if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span %s\n", name); + CONFIG_ERROR("Error finding FreeTDM span %s\n", name); continue; } span_id = ftdm_span_get_id(span); if (ftdm_configure_span_signaling(span, "libpri", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM span %s\n", name); + CONFIG_ERROR("Error configuring FreeTDM span %s\n", name); continue; } @@ -3423,86 +3481,6 @@ static switch_status_t load_config(void) } } - if ((spans = switch_xml_child(cfg, "boost_spans"))) { - for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { - char *id = (char *) switch_xml_attr(myspan, "id"); - char *name = (char *) switch_xml_attr(myspan, "name"); - char *sigmod = (char *) switch_xml_attr(myspan, "sigmod"); - ftdm_status_t zstatus = FTDM_FAIL; - const char *context = "default"; - const char *dialplan = "XML"; - uint32_t span_id = 0; - ftdm_span_t *span = NULL; - ftdm_conf_parameter_t spanparameters[30]; - unsigned paramindex = 0; - - if (!id && !name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "boost span requires an id or name as attribute: \n"); - continue; - } - memset(spanparameters, 0, sizeof(spanparameters)); - if (sigmod) { - spanparameters[paramindex].var = "sigmod"; - spanparameters[paramindex].val = sigmod; - paramindex++; - } - - for (param = switch_xml_child(myspan, "param"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - char *val = (char *) switch_xml_attr_soft(param, "value"); - - if (ftdm_array_len(spanparameters) - 1 == paramindex) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Too many parameters for boost span, ignoring any parameter after %s\n", var); - break; - } - - if (!strcasecmp(var, "context")) { - context = val; - } else if (!strcasecmp(var, "dialplan")) { - dialplan = val; - } else { - spanparameters[paramindex].var = var; - spanparameters[paramindex].val = val; - paramindex++; - } - } - - if (name) { - zstatus = ftdm_span_find_by_name(name, &span); - } else { - if (switch_is_number(id)) { - span_id = atoi(id); - zstatus = ftdm_span_find(span_id, &span); - } - - if (zstatus != FTDM_SUCCESS) { - zstatus = ftdm_span_find_by_name(id, &span); - } - } - - if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM span id:%s name:%s\n", switch_str_nil(id), switch_str_nil(name)); - continue; - } - - if (!span_id) { - span_id = ftdm_span_get_id(span); - } - - if (ftdm_configure_span_signaling(span, "sangoma_boost", on_clear_channel_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d error: %s\n", span_id, ftdm_span_get_last_error(span)); - continue; - } - - SPAN_CONFIG[span_id].span = span; - switch_copy_string(SPAN_CONFIG[span_id].context, context, sizeof(SPAN_CONFIG[span_id].context)); - switch_copy_string(SPAN_CONFIG[span_id].dialplan, dialplan, sizeof(SPAN_CONFIG[span_id].dialplan)); - - switch_copy_string(SPAN_CONFIG[span_id].type, "Sangoma (boost)", sizeof(SPAN_CONFIG[span_id].type)); - boost_spans[boosti++] = span; - } - } - if ((spans = switch_xml_child(cfg, "r2_spans"))) { for (myspan = switch_xml_child(spans, "span"); myspan; myspan = myspan->next) { char *name = (char *) switch_xml_attr(myspan, "name"); @@ -3521,7 +3499,7 @@ static switch_status_t load_config(void) unsigned paramindex = 0; if (!name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "'name' attribute required for R2 spans!\n"); + CONFIG_ERROR("'name' attribute required for R2 spans!\n"); continue; } @@ -3556,14 +3534,13 @@ static switch_status_t load_config(void) zstatus = ftdm_span_find_by_name(name, &span); if (zstatus != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error finding FreeTDM R2 Span '%s'\n", name); + CONFIG_ERROR("Error finding FreeTDM R2 Span '%s'\n", name); continue; } span_id = ftdm_span_get_id(span); if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, spanparameters) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM R2 span %s, error: %s\n", - name, ftdm_span_get_last_error(span)); + CONFIG_ERROR("Error configuring FreeTDM R2 span %s, error: %s\n", name, ftdm_span_get_last_error(span)); continue; } @@ -3581,24 +3558,12 @@ static switch_status_t load_config(void) switch_copy_string(SPAN_CONFIG[span_id].type, "R2", sizeof(SPAN_CONFIG[span_id].type)); if (ftdm_span_start(span) == FTDM_FAIL) { - ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM R2 span %s, error: %s\n", name, ftdm_span_get_last_error(span)); + CONFIG_ERROR("Error starting FreeTDM R2 span %s, error: %s\n", name, ftdm_span_get_last_error(span)); continue; } } } - /* start all boost spans now that we're done configuring. Unfortunately at this point boost modules have the limitation - * of needing all spans to be configured before starting them */ - for (i=0 ; i < boosti; i++) { - boost_span = boost_spans[i]; - ftdm_log(FTDM_LOG_DEBUG, "Starting boost span %d\n", ftdm_span_get_id(boost_span)); - if (ftdm_span_start(boost_span) == FTDM_FAIL) { - ftdm_log(FTDM_LOG_ERROR, "Error starting boost FreeTDM span %d, error: %s\n", - ftdm_span_get_id(boost_span), ftdm_span_get_last_error(boost_span)); - continue; - } - } - if (globals.crash_on_assert) { ftdm_log(FTDM_LOG_WARNING, "Crash on assert enabled\n"); ftdm_global_set_crash_policy(FTDM_CRASH_ON_ASSERT); @@ -3606,6 +3571,11 @@ static switch_status_t load_config(void) switch_xml_free(xml); + if (globals.fail_on_error && globals.config_error) { + ftdm_log(FTDM_LOG_ERROR, "Refusing to load module with errors\n"); + return SWITCH_STATUS_TERM; + } + return SWITCH_STATUS_SUCCESS; } diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index b57292c5b0..93729f0a50 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -58,6 +58,8 @@ struct tm *localtime_r(const time_t *clock, struct tm *result); #define FTDM_READ_TRACE_INDEX 0 #define FTDM_WRITE_TRACE_INDEX 1 #define MAX_CALLIDS 6000 +#define FTDM_HALF_DTMF_PAUSE 500 +#define FTDM_FULL_DTMF_PAUSE 1000 ftdm_time_t time_last_throttle_log = 0; ftdm_time_t time_current_throttle_log = 0; @@ -306,7 +308,10 @@ FTDM_ENUM_NAMES(CALLING_PARTY_CATEGORY_NAMES, CALLING_PARTY_CATEGORY_STRINGS) FTDM_STR2ENUM(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t, CALLING_PARTY_CATEGORY_NAMES, FTDM_CPC_INVALID) FTDM_ENUM_NAMES(INDICATION_NAMES, INDICATION_STRINGS) -FTDM_STR2ENUM(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID) +FTDM_STR2ENUM(ftdm_str2ftdm_channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID) + +FTDM_ENUM_NAMES(TRANSFER_RESPONSE_NAMES, TRANSFER_RESPONSE_STRINGS) +FTDM_STR2ENUM(ftdm_str2ftdm_transfer_response, ftdm_transfer_response2str, ftdm_transfer_response_t, TRANSFER_RESPONSE_NAMES, FTDM_TRANSFER_RESPONSE_INVALID) static ftdm_status_t ftdm_group_add_channels(ftdm_span_t* span, int currindex, const char* name); @@ -1191,6 +1196,10 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read_event(ftdm_channel_t *ftdmchan, ftdm goto done; } + if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT)) { + ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT); + } + status = span->fio->channel_next_event(ftdmchan, event); if (status != FTDM_SUCCESS) { goto done; @@ -1952,6 +1961,7 @@ FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, f if (!citer) { return FTDM_ENOMEM; } + for (curr = citer ; curr; curr = ftdm_iterator_next(curr)) { fchan = ftdm_iterator_current(curr); if (enabled) { @@ -2076,12 +2086,12 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status) { ftdm_sigmsg_t msg; - + if (!ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) { return; } - ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n", + ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n", ftdm_channel_indication2str(indication), ftdm_channel_state2str(fchan->state), status); ftdm_clear_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING); memset(&msg, 0, sizeof(msg)); @@ -2155,11 +2165,36 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char return status; } +FT_DECLARE(ftdm_status_t) _ftdm_channel_call_transfer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, const char* arg, ftdm_usrmsg_t *usrmsg) +{ + ftdm_status_t status; + ftdm_usrmsg_t *msg = NULL; + ftdm_bool_t free_msg = FTDM_FALSE; + + if (!usrmsg) { + msg = ftdm_calloc(1, sizeof(*msg)); + ftdm_assert_return(msg, FTDM_FAIL, "Failed to allocate usr msg"); + memset(msg, 0, sizeof(*msg)); + free_msg = FTDM_TRUE; + } else { + msg = usrmsg; + } + + ftdm_usrmsg_add_var(msg, "transfer_arg", arg); + /* we leave the locking up to ftdm_channel_call_indicate, DO NOT lock here since ftdm_channel_call_indicate expects + * the lock recursivity to be 1 */ + status = _ftdm_channel_call_indicate(file, func, line, ftdmchan, FTDM_CHANNEL_INDICATE_TRANSFER, msg); + if (free_msg == FTDM_TRUE) { + ftdm_safe_free(msg); + } + return status; +} + /* lock must be acquired by the caller! */ static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char *func, int line, ftdm_channel_t *chan, ftdm_usrmsg_t *usrmsg) { ftdm_status_t status = FTDM_SUCCESS; - + if (chan->state != FTDM_CHANNEL_STATE_DOWN) { if (chan->state == FTDM_CHANNEL_STATE_HANGUP) { /* make user's life easier, and just ignore double hangup requests */ @@ -2351,6 +2386,14 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch case FTDM_CHANNEL_INDICATE_ANSWER: status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan, usrmsg); break; + case FTDM_CHANNEL_INDICATE_TRANSFER: + if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_TRANSFER)) { + ftdm_log_chan_ex_msg(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Transfer not supported\n"); + status = FTDM_EINVAL; + goto done; + } + status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_TRANSFER, 1, usrmsg); + break; default: /* See if signalling module can provide this indication */ status = ftdm_channel_sig_indicate(ftdmchan, indication, usrmsg); @@ -2720,10 +2763,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_close(ftdm_channel_t **ftdmchan) return status; } - static ftdm_status_t ftdmchan_activate_dtmf_buffer(ftdm_channel_t *ftdmchan) { - if (!ftdmchan->dtmf_buffer) { if (ftdm_buffer_create(&ftdmchan->dtmf_buffer, 1024, 3192, 0) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_ERROR, "Failed to allocate DTMF Buffer!\n"); @@ -2752,10 +2793,29 @@ static ftdm_status_t ftdmchan_activate_dtmf_buffer(ftdm_channel_t *ftdmchan) return FTDM_SUCCESS; } +/* + * ftdmchan_activate_dtmf_buffer to initialize ftdmchan->dtmf_buffer should be called prior to + * calling ftdm_insert_dtmf_pause + */ +static ftdm_status_t ftdm_insert_dtmf_pause(ftdm_channel_t *ftdmchan, ftdm_size_t pausems) +{ + void *data = NULL; + unsigned int datalen = pausems * sizeof(uint16_t); + + data = ftdm_malloc(datalen); + ftdm_assert(data, "Failed to allocate memory\n"); + + memset(data, FTDM_SILENCE_VALUE(ftdmchan), datalen); + + ftdm_buffer_write(ftdmchan->dtmf_buffer, data, datalen); + ftdm_safe_free(data); + return FTDM_SUCCESS; +} + FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_command_t command, void *obj) { ftdm_status_t status = FTDM_FAIL; - + ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n"); ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n"); @@ -3519,13 +3579,18 @@ skipdebug: status = ftdm_buffer_write(ftdmchan->digit_buffer, dtmf, wr) ? FTDM_SUCCESS : FTDM_FAIL; ftdm_mutex_unlock(ftdmchan->mutex); - + return status; } -static FIO_WRITE_FUNCTION(ftdm_raw_write) +FIO_WRITE_FUNCTION(ftdm_raw_write) { int dlen = (int) *datalen; + + if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE)) { + ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE); + } + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED)) { ftdmchan->txdrops++; if (ftdmchan->txdrops <= 10) { @@ -3545,11 +3610,16 @@ static FIO_WRITE_FUNCTION(ftdm_raw_write) return ftdmchan->fio->write(ftdmchan, data, datalen); } -static FIO_READ_FUNCTION(ftdm_raw_read) +FIO_READ_FUNCTION(ftdm_raw_read) { - ftdm_status_t status = ftdmchan->fio->read(ftdmchan, data, datalen); + ftdm_status_t status; + + if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) { + ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ); + } + status = ftdmchan->fio->read(ftdmchan, data, datalen); - if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN) + if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN) && (ftdmchan->native_codec == FTDM_CODEC_ALAW || ftdmchan->native_codec == FTDM_CODEC_ULAW)) { ftdm_size_t i = 0; unsigned char *rdata = data; @@ -3616,30 +3686,31 @@ static ftdm_status_t handle_tone_generation(ftdm_channel_t *ftdmchan) if (ftdm_buffer_read(ftdmchan->gen_dtmf_buffer, digits, dblen) && !ftdm_strlen_zero_buf(digits)) { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Generating DTMF [%s]\n", digits); - + cur = digits; - if (*cur == 'F') { - ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL); - cur++; - } - for (; *cur; cur++) { - if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) { - ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2); - x++; + if (*cur == 'F') { + ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL); + } else if (*cur == 'w') { + ftdm_insert_dtmf_pause(ftdmchan, FTDM_HALF_DTMF_PAUSE); + } else if (*cur == 'W') { + ftdm_insert_dtmf_pause(ftdmchan, FTDM_FULL_DTMF_PAUSE); } else { - ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Problem adding DTMF sequence [%s]\n", digits); - return FTDM_FAIL; + if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) { + ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2); + x++; + } else { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Problem adding DTMF sequence [%s]\n", digits); + return FTDM_FAIL; + } + } + if (x) { + ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4; } - } - - if (x) { - ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4; } } } - if (!ftdmchan->buffer_delay || --ftdmchan->buffer_delay == 0) { /* time to pick a buffer, either the dtmf or fsk buffer */ @@ -3699,6 +3770,7 @@ static ftdm_status_t handle_tone_generation(ftdm_channel_t *ftdmchan) } + FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor) { int16_t x; @@ -3720,49 +3792,12 @@ FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint } } -FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen) +FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen) { ftdm_status_t status = FTDM_FAIL; fio_codec_t codec_func = NULL; ftdm_size_t max = *datalen; - ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n"); - ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n"); - - ftdm_channel_lock(ftdmchan); - - if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel that is not open\n"); - status = FTDM_FAIL; - goto done; - } - - if (!ftdmchan->fio->read) { - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "read method not implemented\n"); - status = FTDM_FAIL; - goto done; - } - - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) { - ftdmchan->rxdrops++; - if (ftdmchan->rxdrops <= 10) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel with rx disabled\n"); - } - if (ftdmchan->rxdrops == 10) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "too many rx drops, not logging anymore\n"); - } - status = FTDM_FAIL; - goto done; - } - - status = ftdm_raw_read(ftdmchan, data, datalen); - if (status != FTDM_SUCCESS) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read filed\n"); - goto done; - } - handle_tone_generation(ftdmchan); if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DIGITAL_MEDIA)) { @@ -3789,8 +3824,10 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data } } - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) || + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) || + ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT)) { + uint8_t sln_buf[1024] = {0}; int16_t *sln; ftdm_size_t slen = 0; @@ -3894,8 +3931,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data } } } - - + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) && !ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) { teletone_dtmf_detect(&ftdmchan->dtmf_detect, sln, (int)slen); teletone_dtmf_get(&ftdmchan->dtmf_detect, digit_str, sizeof(digit_str)); @@ -3904,7 +3940,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) { ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++; } else { - ftdm_channel_queue_dtmf(ftdmchan, digit_str); + if (!ftdmchan->span->sig_dtmf || (ftdmchan->span->sig_dtmf(ftdmchan, (const char*)digit_str) != FTDM_BREAK)) { + ftdm_channel_queue_dtmf(ftdmchan, digit_str); + } if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF)) { ftdmchan->skip_read_frames = 20; @@ -3915,20 +3953,19 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data } if (ftdmchan->skip_read_frames > 0 || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MUTE)) { - + ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); if (ftdmchan->pre_buffer && ftdm_buffer_inuse(ftdmchan->pre_buffer)) { ftdm_buffer_zero(ftdmchan->pre_buffer); } ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex); - memset(data, FTDM_SILENCE_VALUE(ftdmchan), *datalen); if (ftdmchan->skip_read_frames > 0) { ftdmchan->skip_read_frames--; } - } else { + } else { ftdm_mutex_lock(ftdmchan->pre_buffer_mutex); if (ftdmchan->pre_buffer_size && ftdmchan->pre_buffer) { ftdm_buffer_write(ftdmchan->pre_buffer, data, *datalen); @@ -3942,9 +3979,55 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data } done: + return FTDM_SUCCESS; +} + +FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen) +{ + + ftdm_status_t status = FTDM_FAIL; + + ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n"); + ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n"); + + ftdm_channel_lock(ftdmchan); + + if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel that is not open\n"); + status = FTDM_FAIL; + goto done; + } + + if (!ftdmchan->fio->read) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "read method not implemented\n"); + status = FTDM_FAIL; + goto done; + } + + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) { + ftdmchan->rxdrops++; + if (ftdmchan->rxdrops <= 10) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel with rx disabled\n"); + } + if (ftdmchan->rxdrops == 10) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "too many rx drops, not logging anymore\n"); + } + status = FTDM_FAIL; + goto done; + } + status = ftdm_raw_read(ftdmchan, data, datalen); + if (status != FTDM_SUCCESS) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read filed\n"); + goto done; + } + + status = ftdm_channel_process_media(ftdmchan, data, datalen); + if (status != FTDM_SUCCESS) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to process media\n"); + } +done: ftdm_channel_unlock(ftdmchan); - return status; } @@ -3972,14 +4055,12 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *dat if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot write in channel not open\n"); - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open"); status = FTDM_FAIL; goto done; } if (!ftdmchan->fio->write) { ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "write method not implemented\n"); - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); status = FTDM_FAIL; goto done; } @@ -4003,8 +4084,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_write(ftdm_channel_t *ftdmchan, void *dat status = codec_func(data, max, datalen); } else { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Do not know how to handle transcoding from %d to %d\n", - ftdmchan->effective_codec, ftdmchan->native_codec); - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!"); + ftdmchan->effective_codec, ftdmchan->native_codec); status = FTDM_FAIL; goto done; } diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c index 3cf4876070..2a0fa83706 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.c @@ -44,6 +44,7 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj); static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span); static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span); +static ftdm_status_t ftdm_sangoma_isdn_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf); ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event); static void ftdm_sangoma_isdn_poll_events(ftdm_span_t *span); @@ -53,10 +54,13 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan); static void ftdm_sangoma_isdn_dchan_set_queue_size(ftdm_channel_t *ftdmchan); -static ftdm_io_interface_t g_sngisdn_io_interface; +static ftdm_io_interface_t g_sngisdn_io_interface; static sng_isdn_event_interface_t g_sngisdn_event_interface; -ftdm_sngisdn_data_t g_sngisdn_data; +ftdm_sngisdn_data_t g_sngisdn_data; + +FTDM_ENUM_NAMES(SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_TYPE_STRINGS) +FTDM_STR2ENUM(ftdm_str2sngisdn_transfer_type, sngisdn_transfer_type2str, sngisdn_transfer_type_t, SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_INVALID) ftdm_state_map_t sangoma_isdn_state_map = { { @@ -126,8 +130,7 @@ ftdm_state_map_t sangoma_isdn_state_map = { ZSD_INBOUND, ZSM_UNACCEPTABLE, {FTDM_CHANNEL_STATE_PROCEED, FTDM_END}, - {FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, - FTDM_CHANNEL_STATE_UP, FTDM_END} + {FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_TRANSFER, FTDM_END} }, { ZSD_INBOUND, @@ -151,7 +154,14 @@ ftdm_state_map_t sangoma_isdn_state_map = { ZSD_INBOUND, ZSM_UNACCEPTABLE, {FTDM_CHANNEL_STATE_UP, FTDM_END}, - {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + {FTDM_CHANNEL_STATE_TRANSFER, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}, + }, + + { + ZSD_INBOUND, + ZSM_UNACCEPTABLE, + {FTDM_CHANNEL_STATE_TRANSFER, FTDM_END}, + {FTDM_CHANNEL_STATE_PROCEED, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_TERMINATING,FTDM_END}, }, { ZSD_INBOUND, @@ -352,49 +362,96 @@ static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan) return; } -static void *ftdm_sangoma_isdn_dchan_run(ftdm_thread_t *me, void *obj) +static void *ftdm_sangoma_isdn_io_run(ftdm_thread_t *me, void *obj) { uint8_t data[1000]; + unsigned i; ftdm_status_t status = FTDM_SUCCESS; ftdm_wait_flag_t wflags = FTDM_READ; ftdm_span_t *span = (ftdm_span_t*) obj; - ftdm_channel_t *dchan = ((sngisdn_span_data_t*)span->signal_data)->dchan; ftdm_size_t len = 0; - - ftdm_channel_set_feature(dchan, FTDM_CHANNEL_FEATURE_IO_STATS); - ftdm_sangoma_isdn_dchan_set_queue_size(dchan); + ftdm_channel_t *ftdmchan = NULL; + unsigned waitms = 10000; + ftdm_iterator_t *chaniter = NULL; + ftdm_iterator_t *citer = NULL; + short *poll_events = ftdm_malloc(sizeof(short) * span->chan_count); + + /* Initialize the d-channel */ + ftdm_assert(((sngisdn_span_data_t*)span->signal_data)->dchan, "Span does not have a dchannel"); + ftdm_channel_set_feature(((sngisdn_span_data_t*)span->signal_data)->dchan, FTDM_CHANNEL_FEATURE_IO_STATS); + ftdm_sangoma_isdn_dchan_set_queue_size(((sngisdn_span_data_t*)span->signal_data)->dchan); + ftdm_channel_open_chan(((sngisdn_span_data_t*)span->signal_data)->dchan); + + chaniter = ftdm_span_get_chan_iterator(span, NULL); + if (!chaniter) { + ftdm_log(FTDM_LOG_CRIT, "Failed to allocate channel iterator for span %s!\n", span->name); + goto done; + } - ftdm_assert(dchan, "Span does not have a dchannel"); - ftdm_channel_open_chan(dchan); - while (ftdm_running() && !(ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD))) { + len = 1000; + waitms = 1000; wflags = FTDM_READ; - status = ftdm_channel_wait(dchan, &wflags, 10000); - switch(status) { + memset(poll_events, 0, sizeof(short)*span->chan_count); + + for (i = 0, citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer), i++) { + ftdmchan = ftdm_iterator_current(citer); + + if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) { + poll_events[i] |= FTDM_READ; + waitms = 20; + } + } else { + /* We always read the d-channel */ + poll_events[i] |= FTDM_READ; + } + } + + status = ftdm_span_poll_event(span, waitms, poll_events); + switch (status) { case FTDM_FAIL: - ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Failed to wait for d-channel\n"); + ftdm_log(FTDM_LOG_CRIT, "Failed to poll span for IO\n"); break; case FTDM_TIMEOUT: break; case FTDM_SUCCESS: - if ((wflags & FTDM_READ)) { + for (citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer)) { len = 1000; - status = ftdm_channel_read(dchan, data, &len); - if (status == FTDM_SUCCESS) { - sngisdn_snd_data(dchan, data, len); + ftdmchan = ftdm_iterator_current(citer); + if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) { + if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) { + if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) { + status = ftdm_raw_read(ftdmchan, data, &len); + if (status != FTDM_SUCCESS) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read failed\n"); + continue; + } + + status = ftdm_channel_process_media(ftdmchan, data, &len); + if (status != FTDM_SUCCESS) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to process media\n"); + continue; + } + } + } } else { - ftdm_log_chan_msg(dchan, FTDM_LOG_WARNING, "Failed to read from channel \n"); + if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) { + status = ftdm_channel_read(ftdmchan, data, &len); + if (status == FTDM_SUCCESS) { + sngisdn_snd_data(ftdmchan, data, len); + } + } } -#ifndef WIN32 /* It is valid on WIN32 for poll to return without errors, but no flags set */ - } else { - ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Failed to poll for d-channel\n"); -#endif } break; default: - ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Unhandled IO event\n"); + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Unhandled IO event\n"); } - } + } +done: + ftdm_iterator_free(chaniter); + ftdm_safe_free(poll_events); return NULL; } @@ -600,10 +657,10 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev /* this function is called with the channel already locked by the core */ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan) { - ftdm_sigmsg_t sigev; - ftdm_channel_state_t initial_state; - sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; - uint8_t state_change = 0; + ftdm_sigmsg_t sigev; + ftdm_channel_state_t initial_state; + sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; + uint8_t state_change = 0; memset(&sigev, 0, sizeof(sigev)); @@ -632,18 +689,15 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm break; case FTDM_CHANNEL_STATE_GET_CALLERID: { - if (!sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) { - /* By default, we do not send a progress indicator in the proceed */ - ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID}; - - sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED); - sngisdn_snd_proceed(ftdmchan, prog_ind); - } - /* Wait in this state until we get FACILITY msg */ + /* By default, we do not send a progress indicator in the proceed */ + ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID}; + sngisdn_snd_proceed(ftdmchan, prog_ind); + + /* Wait in this state until we get FACILITY msg */ } break; case FTDM_CHANNEL_STATE_RING: /* incoming call request */ - { + { ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending incoming call from %s to %s to FTDM core\n", ftdmchan->caller_data.ani.digits, ftdmchan->caller_data.dnis.digits); /* we have enough information to inform FTDM of the call*/ @@ -678,13 +732,8 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA); } } else { - if (!sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) { - /* By default, we do not send a progress indicator in the proceed */ - ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID}; - sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED); - - sngisdn_snd_proceed(ftdmchan, prog_ind); - } + ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID}; + sngisdn_snd_proceed(ftdmchan, prog_ind); } } break; @@ -846,7 +895,6 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm ftdm_channel_close(&close_chan); } if (glare) { - ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Glare detected, processing saved call\n"); /* We are calling sngisdn_rcv_con_ind with ftdmchan->mutex being locked, so no other threads will be able to touch this channel. The next time we will @@ -856,11 +904,17 @@ static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdm } } break; + case FTDM_CHANNEL_STATE_TRANSFER: + { + /* sngisdn_transfer function will always result in a state change */ + sngisdn_transfer(ftdmchan); + state_change++; + } + break; case FTDM_CHANNEL_STATE_RESTART: { /* IMPLEMENT ME */ } - break; case FTDM_CHANNEL_STATE_SUSPENDED: { @@ -984,6 +1038,21 @@ static FIO_SPAN_SET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_set_span_sig_status) return FTDM_NOTIMPL; } +static ftdm_status_t ftdm_sangoma_isdn_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf) +{ + sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; + switch(sngisdn_info->transfer_data.type) { + case SNGISDN_TRANSFER_ATT_COURTESY_VRU: + case SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA: + return sngisdn_att_transfer_process_dtmf(ftdmchan, dtmf); + default: + /* We do not care about DTMF events, do nothing */ + break; + } + + return FTDM_SUCCESS; +} + static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span) { sngisdn_span_data_t *signal_data = span->signal_data; @@ -992,6 +1061,7 @@ static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span) ftdm_log(FTDM_LOG_CRIT, "Failed to start span %s\n", span->name); return FTDM_FAIL; } + /* clear the monitor thread stop flag */ ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD); ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD); @@ -1015,7 +1085,7 @@ static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span) } /*start the dchan monitor thread*/ - if (ftdm_thread_create_detached(ftdm_sangoma_isdn_dchan_run, span) != FTDM_SUCCESS) { + if (ftdm_thread_create_detached(ftdm_sangoma_isdn_io_run, span) != FTDM_SUCCESS) { ftdm_log(FTDM_LOG_CRIT,"Failed to start Sangoma ISDN d-channel Monitor Thread!\n"); return FTDM_FAIL; } @@ -1106,6 +1176,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config) span->indicate = ftdm_sangoma_isdn_indicate; span->channel_request = NULL; span->signal_cb = sig_cb; + span->sig_dtmf = ftdm_sangoma_isdn_dtmf; span->get_channel_sig_status = ftdm_sangoma_isdn_get_chan_sig_status; span->set_channel_sig_status = ftdm_sangoma_isdn_set_chan_sig_status; span->get_span_sig_status = ftdm_sangoma_isdn_get_span_sig_status; @@ -1117,10 +1188,9 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config) ftdm_set_flag(span, FTDM_SPAN_USE_PROCEED_STATE); ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES); ftdm_set_flag(span, FTDM_SPAN_NON_STOPPABLE); + ftdm_set_flag(span, FTDM_SPAN_USE_TRANSFER); - if (span->trunk_type == FTDM_TRUNK_BRI_PTMP || - span->trunk_type == FTDM_TRUNK_BRI) { - + if (FTDM_SPAN_IS_BRI(span)) { sngisdn_set_span_avail_rate(span, SNGISDN_AVAIL_PWR_SAVING); } @@ -1156,10 +1226,9 @@ static FIO_SIG_LOAD_FUNCTION(ftdm_sangoma_isdn_init) g_sngisdn_event_interface.cc.sng_fac_ind = sngisdn_rcv_fac_ind; g_sngisdn_event_interface.cc.sng_sta_cfm = sngisdn_rcv_sta_cfm; g_sngisdn_event_interface.cc.sng_srv_ind = sngisdn_rcv_srv_ind; - g_sngisdn_event_interface.cc.sng_srv_ind = sngisdn_rcv_srv_cfm; - g_sngisdn_event_interface.cc.sng_rst_ind = sngisdn_rcv_rst_cfm; + g_sngisdn_event_interface.cc.sng_srv_cfm = sngisdn_rcv_srv_cfm; g_sngisdn_event_interface.cc.sng_rst_ind = sngisdn_rcv_rst_ind; - g_sngisdn_event_interface.cc.sng_rst_cfm = sngisdn_rcv_rst_cfm; + g_sngisdn_event_interface.cc.sng_rst_cfm = sngisdn_rcv_rst_cfm; g_sngisdn_event_interface.lg.sng_log = sngisdn_rcv_sng_log; g_sngisdn_event_interface.lg.sng_assert = sngisdn_rcv_sng_assert; @@ -1298,6 +1367,9 @@ static FIO_API_FUNCTION(ftdm_sangoma_isdn_api) status = sngisdn_check_free_ids(); goto done; } + if (!strcasecmp(argv[0], "check_mem")) { + sngisdn_get_memory_info(); + } done: switch (status) { case FTDM_SUCCESS: @@ -1319,6 +1391,7 @@ done: return status; } + static FIO_IO_LOAD_FUNCTION(ftdm_sangoma_isdn_io_init) { memset(&g_sngisdn_io_interface, 0, sizeof(g_sngisdn_io_interface)); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h index ac69fa8d66..68e1371b79 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn.h @@ -82,6 +82,8 @@ typedef enum { FLAG_MEDIA_READY = (1 << 11), /* Set when we already sent a Channel ID IE */ FLAG_SENT_CHAN_ID = (1 << 12), + /* Set when we already sent a Connect */ + FLAG_SENT_CONNECT = (1 << 13), } sngisdn_flag_t; @@ -152,10 +154,11 @@ typedef struct ftdm_sngisdn_prog_ind { } ftdm_sngisdn_progind_t; /* Only timers that can be cancelled are listed here */ -#define SNGISDN_NUM_TIMERS 1 +#define SNGISDN_NUM_TIMERS 2 /* Increase NUM_TIMERS as number of ftdm_sngisdn_timer_t increases */ typedef enum { SNGISDN_TIMER_FACILITY = 0, + SNGISDN_TIMER_ATT_TRANSFER, } ftdm_sngisdn_timer_t; typedef struct sngisdn_glare_data { @@ -165,8 +168,35 @@ typedef struct sngisdn_glare_data { int16_t dChan; ConEvnt setup; uint8_t ces; -}sngisdn_glare_data_t; +} sngisdn_glare_data_t; +typedef enum { + SNGISDN_TRANSFER_NONE = 0, /* Default value, no transfer being done */ + SNGISDN_TRANSFER_ATT_COURTESY_VRU, + SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA, + SNGISDN_TRANSFER_INVALID, +} sngisdn_transfer_type_t; +#define SNGISDN_TRANSFER_TYPE_STRINGS "NONE", "ATT_COURTESY_VRU", "ATT_COURTERY_VRU_DATA", "INVALID" +FTDM_STR2ENUM_P(ftdm_str2sngisdn_transfer_type, sngisdn_transfer_type2str, sngisdn_transfer_type_t) + +/* From section 4.2 of TR50075, max length of data is 100 when single UUI is sent */ +#define COURTESY_TRANSFER_MAX_DATA_SIZE 100 + +typedef struct _att_courtesy_vru +{ + char dtmf_digits [20]; + char data[COURTESY_TRANSFER_MAX_DATA_SIZE]; +} att_courtesy_vru_t; + +typedef struct _sngisdn_transfer_data +{ + sngisdn_transfer_type_t type; /* Specifies which type of transfer is being used */ + ftdm_transfer_response_t response; + union + { + att_courtesy_vru_t att_courtesy_vru; + } tdata; +} sngisdn_transfer_data_t; /* Channel specific data */ typedef struct sngisdn_chan_data { @@ -180,7 +210,8 @@ typedef struct sngisdn_chan_data { uint8_t globalFlg; sngisdn_glare_data_t glare; - ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS]; + ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS]; + sngisdn_transfer_data_t transfer_data; /* variables saved here will be sent to the user application on next SIGEVENT_XXX */ @@ -211,8 +242,10 @@ typedef struct sngisdn_span_data { uint8_t facility_ie_decode; uint8_t facility; int8_t facility_timeout; + uint8_t att_remove_dtmf; + int32_t transfer_timeout; uint8_t num_local_numbers; - uint8_t ignore_cause_value; + uint8_t ignore_cause_value; uint8_t trace_q931; /* TODO: combine with trace_flags */ uint8_t trace_q921; /* TODO: combine with trace_flags */ uint8_t raw_trace_q931; /* TODO: combine with trace_flags */ @@ -394,7 +427,7 @@ void sngisdn_trace_interpreted_q931(sngisdn_span_data_t *signal_data, ftdm_trace void sngisdn_trace_raw_q921(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t dir, uint8_t *data, uint32_t data_len); void sngisdn_trace_raw_q931(sngisdn_span_data_t *signal_data, ftdm_trace_dir_t dir, uint8_t *data, uint32_t data_len); -void get_memory_info(void); +void sngisdn_get_memory_info(void); ftdm_status_t sng_isdn_activate_trace(ftdm_span_t *span, sngisdn_tracetype_t trace_opt); ftdm_status_t sngisdn_check_free_ids(void); @@ -432,18 +465,23 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId); ftdm_status_t set_restart_ind_ie(ftdm_channel_t *ftdmchan, RstInd *rstInd); ftdm_status_t set_facility_ie(ftdm_channel_t *ftdmchan, FacilityStr *facilityStr); ftdm_status_t set_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t *data_len); +ftdm_status_t set_user_to_user_ie(ftdm_channel_t *ftdmchan, UsrUsr *usrUsr); +ftdm_status_t set_cause_ie(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn); ftdm_status_t sngisdn_add_var(sngisdn_chan_data_t *sngisdn_info, const char* var, const char* val); ftdm_status_t sngisdn_add_raw_data(sngisdn_chan_data_t *sngisdn_info, uint8_t* data, ftdm_size_t data_len); ftdm_status_t sngisdn_clear_data(sngisdn_chan_data_t *sngisdn_info); void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t event_id); - + uint8_t sngisdn_get_infoTranCap_from_user(ftdm_bearer_cap_t bearer_capability); uint8_t sngisdn_get_usrInfoLyr1Prot_from_user(ftdm_user_layer1_prot_t layer1_prot); ftdm_bearer_cap_t sngisdn_get_infoTranCap_from_stack(uint8_t bearer_capability); ftdm_user_layer1_prot_t sngisdn_get_usrInfoLyr1Prot_from_stack(uint8_t layer1_prot); +ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan); +ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf); + static __inline__ uint32_t sngisdn_test_flag(sngisdn_chan_data_t *sngisdn_info, sngisdn_flag_t flag) { return (uint32_t) sngisdn_info->flags & flag; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c index 49b07fff33..ec688eb2ba 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c @@ -285,7 +285,9 @@ ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_ signal_data->timer_t3 = 8; signal_data->restart_opt = SNGISDN_OPT_DEFAULT; signal_data->link_id = span->span_id; - + signal_data->transfer_timeout = 20000; + signal_data->att_remove_dtmf = 1; + span->default_caller_data.dnis.plan = FTDM_NPI_INVALID; span->default_caller_data.dnis.type = FTDM_TON_INVALID; span->default_caller_data.cid_num.plan = FTDM_NPI_INVALID; @@ -365,6 +367,13 @@ ftdm_status_t ftmod_isdn_parse_cfg(ftdm_conf_parameter_t *ftdm_parameters, ftdm_ if (signal_data->facility_timeout < 0) { signal_data->facility_timeout = 0; } + } else if (!strcasecmp(var, "transfer-timeout")) { + signal_data->transfer_timeout = atoi(val); + if (signal_data->transfer_timeout < 0) { + signal_data->transfer_timeout = 0; + } + } else if (!strcasecmp(var, "att-remove-dtmf")) { + parse_yesno(var, val, &signal_data->att_remove_dtmf); } else if (!strcasecmp(var, "facility-ie-decode")) { parse_yesno(var, val, &signal_data->facility_ie_decode); } else if (!strcasecmp(var, "ignore-cause-value")) { diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c index 872cf51bc4..c5256d08fc 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_hndl.c @@ -35,6 +35,7 @@ #include "ftmod_sangoma_isdn.h" static ftdm_status_t sngisdn_cause_val_requires_disconnect(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn); static void sngisdn_process_restart_confirm(ftdm_channel_t *ftdmchan); +static ftdm_status_t sngisdn_force_down(ftdm_channel_t *ftdmchan); /* Remote side transmit a SETUP */ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event) @@ -191,8 +192,7 @@ void sngisdn_process_con_ind (sngisdn_event_data_t *sngisdn_event) strcpy(ftdmchan->caller_data.cid_name, retrieved_str); } } -#endif - +#endif if (signal_data->overlap_dial == SNGISDN_OPT_TRUE && !conEvnt->sndCmplt.eh.pres) { ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT); } else { @@ -318,8 +318,9 @@ void sngisdn_process_con_cfm (sngisdn_event_data_t *sngisdn_event) } } else { switch(ftdmchan->state) { + case FTDM_CHANNEL_STATE_TRANSFER: case FTDM_CHANNEL_STATE_UP: - /* This is the only valid state we should get a CONNECT ACK on */ + /* These are the only valid states we should get a CONNECT ACK on */ /* do nothing */ break; case FTDM_CHANNEL_STATE_HANGUP_COMPLETE: @@ -926,36 +927,8 @@ void sngisdn_process_sta_cfm (sngisdn_event_data_t *sngisdn_event) 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; + if (sngisdn_force_down(ftdmchan) != FTDM_SUCCESS) { + 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; case 1: @@ -1165,6 +1138,48 @@ static void sngisdn_process_restart_confirm(ftdm_channel_t *ftdmchan) return; } +static ftdm_status_t sngisdn_force_down(ftdm_channel_t *ftdmchan) +{ + sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data; + ftdm_status_t status = FTDM_SUCCESS; + + ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Forcing channel to DOWN state (%s)\n", ftdm_channel_state2str(ftdmchan->state)); + switch (ftdmchan->state) { + case FTDM_CHANNEL_STATE_DOWN: + /* Do nothing */ + break; + case FTDM_CHANNEL_STATE_RESET: + ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN); + break; + 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 already waiting for usr to respond to SIGEVENT stop. + FreeTDM already scheduled a timout in case the User does respond to + SIGEVENT_STOP, no need to do anything here */ + 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; + default: + status = FTDM_FAIL; + + } + return status; +} void sngisdn_process_rst_cfm (sngisdn_event_data_t *sngisdn_event) { @@ -1177,12 +1192,12 @@ void sngisdn_process_rst_cfm (sngisdn_event_data_t *sngisdn_event) sngisdn_span_data_t *signal_data = g_sngisdn_data.dchans[dChan].spans[1]; if (!signal_data) { - ftdm_log(FTDM_LOG_CRIT, "Received RESTART on unconfigured span (suId:%d)\n", suId); + ftdm_log(FTDM_LOG_CRIT, "Received RESTART CFM on unconfigured span (suId:%d)\n", suId); return; } - if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) { - ftdm_log(FTDM_LOG_DEBUG, "Receved RESTART, but Restart Indicator IE not present\n"); + if (rstEvnt->rstInd.eh.pres != PRSNT_NODEF && rstEvnt->rstInd.rstClass.pres != PRSNT_NODEF) { + ftdm_log(FTDM_LOG_DEBUG, "Received RESTART, but Restart Indicator IE not present\n"); return; } @@ -1239,24 +1254,105 @@ void sngisdn_process_rst_cfm (sngisdn_event_data_t *sngisdn_event) } +/* The remote side sent us a RESTART Msg. Trillium automatically acks with RESTART ACK, but + we need to clear our call states if there is a call on this channel */ void sngisdn_process_rst_ind (sngisdn_event_data_t *sngisdn_event) { + uint8_t chan_no = 0; int16_t suId = sngisdn_event->suId; int16_t dChan = sngisdn_event->dChan; uint8_t ces = sngisdn_event->ces; uint8_t evntType = sngisdn_event->evntType; + Rst *rstEvnt = NULL; + sngisdn_span_data_t *signal_data = NULL; ISDN_FUNC_TRACE_ENTER(__FUNCTION__); - /* Function does not require any info from ssHlEvnt struct for now */ - /*Rst *rstEvnt = &sngisdn_event->event.rstEvnt;*/ - - ftdm_log(FTDM_LOG_DEBUG, "Processing RESTART CFM (suId:%u dChan:%d ces:%d %s)\n", suId, dChan, ces, + rstEvnt = &sngisdn_event->event.rstEvnt; + + /* TODO: readjust this when NFAS is implemented as signal_data will not always be the first + * span for that d-channel */ + if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) { + ftdm_log(FTDM_LOG_DEBUG, "Received RESTART IND, but Restart Indicator IE not present\n"); + return; + } + + signal_data = g_sngisdn_data.dchans[dChan].spans[1]; + + if (!signal_data) { + ftdm_log(FTDM_LOG_CRIT, "Received RESTART IND on unconfigured span (suId:%d)\n", suId); + return; + } + + ftdm_log(FTDM_LOG_DEBUG, "Processing RESTART IND (suId:%u dChan:%d ces:%d %s)\n", suId, dChan, ces, (evntType == IN_LNK_DWN)?"LNK_DOWN": (evntType == IN_LNK_UP)?"LNK_UP": (evntType == IN_INDCHAN)?"b-channel": (evntType == IN_LNK_DWN_DM_RLS)?"NFAS service procedures": (evntType == IN_SWCHD_BU_DCHAN)?"NFAS switchover to backup":"Unknown"); + + if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) { + ftdm_log(FTDM_LOG_DEBUG, "Received RESTART IND, but Restart Indicator IE not present\n"); + return; + } + + switch(rstEvnt->rstInd.rstClass.val) { + case IN_CL_INDCHAN: /* Indicated b-channel */ + if (rstEvnt->chanId.eh.pres) { + if (rstEvnt->chanId.intType.val == IN_IT_BASIC) { + if (rstEvnt->chanId.infoChanSel.pres == PRSNT_NODEF) { + chan_no = rstEvnt->chanId.infoChanSel.val; + } + } else if (rstEvnt->chanId.intType.val == IN_IT_OTHER) { + if (rstEvnt->chanId.chanNmbSlotMap.pres == PRSNT_NODEF) { + chan_no = rstEvnt->chanId.chanNmbSlotMap.val[0]; + } + } + } + if (!chan_no) { + ftdm_log(FTDM_LOG_CRIT, "Failed to determine channel from RESTART\n"); + return; + } + break; + case IN_CL_SNGINT: /* Single interface */ + case IN_CL_ALLINT: /* All interfaces */ + /* In case restart class indicates all interfaces, we will duplicate + this event on each span associated to this d-channel in sngisdn_rcv_rst_cfm, + so treat it as a single interface anyway */ + break; + default: + ftdm_log(FTDM_LOG_CRIT, "Invalid restart indicator class:%d\n", rstEvnt->rstInd.rstClass.val); + return; + } + + if (chan_no) { /* For a single channel */ + if (chan_no > ftdm_span_get_chan_count(signal_data->ftdm_span)) { + ftdm_log(FTDM_LOG_CRIT, "Received RESTART IND on invalid channel:%d\n", chan_no); + } else { + ftdm_iterator_t *chaniter = NULL; + ftdm_iterator_t *curr = NULL; + + chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL); + for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) { + ftdm_channel_t *ftdmchan = (ftdm_channel_t*)ftdm_iterator_current(curr); + if (ftdmchan->physical_chan_id == chan_no) { + sngisdn_force_down(ftdmchan); + } + } + ftdm_iterator_free(chaniter); + } + } else { /* for all channels */ + ftdm_iterator_t *chaniter = NULL; + ftdm_iterator_t *curr = NULL; + + chaniter = ftdm_span_get_chan_iterator(signal_data->ftdm_span, NULL); + for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) { + sngisdn_force_down((ftdm_channel_t*)ftdm_iterator_current(curr)); + } + ftdm_iterator_free(chaniter); + } + + ISDN_FUNC_TRACE_EXIT(__FUNCTION__); return; } diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c index 674e642719..9a1e66fe51 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c @@ -146,13 +146,18 @@ void sngisdn_snd_proceed(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_i sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; - if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) { + if (sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) { + return; + } + sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED); + + if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending PROGRESS, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId); sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); return; } - + memset(&cnStEvnt, 0, sizeof(cnStEvnt)); /* Indicate channel ID only in first response */ @@ -161,7 +166,7 @@ void sngisdn_snd_proceed(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_i } set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind); set_facility_ie(ftdmchan, &cnStEvnt.facilityStr); - + ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending PROCEED (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces); if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, MI_CALLPROC, signal_data->dchan_id, sngisdn_info->ces)) { @@ -230,12 +235,17 @@ void sngisdn_snd_alert(ftdm_channel_t *ftdmchan, ftdm_sngisdn_progind_t prog_ind void sngisdn_snd_connect(ftdm_channel_t *ftdmchan) { - CnStEvnt cnStEvnt; + CnStEvnt cnStEvnt; sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data; sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_NETE_ISDN}; - if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) { + if (sngisdn_test_flag(sngisdn_info, FLAG_SENT_CONNECT)) { + return; + } + sngisdn_set_flag(sngisdn_info, FLAG_SENT_CONNECT); + + if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) { ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending CONNECT, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId); sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT); ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING); @@ -353,22 +363,12 @@ void sngisdn_snd_disconnect(ftdm_channel_t *ftdmchan) ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE); return; } - - memset(&discEvnt, 0, sizeof(discEvnt)); - - /* Fill discEvnt here */ - /* TODO move this to set_cause_ie function */ - discEvnt.causeDgn[0].eh.pres = PRSNT_NODEF; - discEvnt.causeDgn[0].location.pres = PRSNT_NODEF; - discEvnt.causeDgn[0].location.val = IN_LOC_PRIVNETLU; - discEvnt.causeDgn[0].codeStand3.pres = PRSNT_NODEF; - discEvnt.causeDgn[0].codeStand3.val = IN_CSTD_CCITT; - discEvnt.causeDgn[0].causeVal.pres = PRSNT_NODEF; - discEvnt.causeDgn[0].causeVal.val = ftdmchan->caller_data.hangup_cause; - discEvnt.causeDgn[0].recommend.pres = NOTPRSNT; - discEvnt.causeDgn[0].dgnVal.pres = NOTPRSNT; - set_facility_ie(ftdmchan, &discEvnt.facilityStr); + memset(&discEvnt, 0, sizeof(discEvnt)); + + set_cause_ie(ftdmchan, &discEvnt.causeDgn[0]); + set_facility_ie(ftdmchan, &discEvnt.facilityStr); + set_user_to_user_ie(ftdmchan, &discEvnt.usrUsr); ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId); if (sng_isdn_disc_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &discEvnt)) { diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c index 2a523c7cb9..e2f93f0e86 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_rcv.c @@ -712,10 +712,12 @@ void sngisdn_rcv_q921_ind(BdMngmt *status) } void sngisdn_rcv_q931_ind(InMngmt *status) { +#ifndef WIN32 if (status->t.usta.alarm.cause == 287) { - get_memory_info(); + sngisdn_get_memory_info(); return; } +#endif switch (status->t.usta.alarm.event) { case LCM_EVENT_UP: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c index 61f340205d..b0c3efe271 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c @@ -47,7 +47,6 @@ static uint8_t get_trillium_val(ftdm2trillium_t *vals, uint8_t ftdm_val, uint8_t static uint8_t get_ftdm_val(ftdm2trillium_t *vals, uint8_t trillium_val, uint8_t default_val); extern ftdm_sngisdn_data_t g_sngisdn_data; -void get_memory_info(void); ftdm2trillium_t npi_codes[] = { {FTDM_NPI_UNKNOWN, IN_NP_UNK}, @@ -105,6 +104,8 @@ void clear_call_data(sngisdn_chan_data_t *sngisdn_info) sngisdn_info->spInstId = 0; sngisdn_info->globalFlg = 0; sngisdn_info->flags = 0; + sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE; + ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].mutex); return; } @@ -846,6 +847,41 @@ ftdm_status_t set_prog_ind_ie(ftdm_channel_t *ftdmchan, ProgInd *progInd, ftdm_s return FTDM_SUCCESS; } +ftdm_status_t set_user_to_user_ie(ftdm_channel_t *ftdmchan, UsrUsr *usrUsr) +{ + sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; + + if (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) { + usrUsr->eh.pres = PRSNT_NODEF; + + usrUsr->protocolDisc.pres = PRSNT_NODEF; + usrUsr->protocolDisc.val = 0x08; + usrUsr->usrInfo.pres = PRSNT_NODEF; + usrUsr->usrInfo.len = strlen(sngisdn_info->transfer_data.tdata.att_courtesy_vru.data); + memcpy(usrUsr->usrInfo.val, sngisdn_info->transfer_data.tdata.att_courtesy_vru.data, usrUsr->usrInfo.len); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending AT&T Transfer data len:%d\n", usrUsr->usrInfo.len); + + return FTDM_SUCCESS; + } + + return FTDM_SUCCESS; +} + +ftdm_status_t set_cause_ie(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn) +{ + + causeDgn->eh.pres = PRSNT_NODEF; + causeDgn->location.pres = PRSNT_NODEF; + causeDgn->location.val = IN_LOC_PRIVNETLU; + causeDgn->codeStand3.pres = PRSNT_NODEF; + causeDgn->codeStand3.val = IN_CSTD_CCITT; + causeDgn->causeVal.pres = PRSNT_NODEF; + causeDgn->causeVal.val = ftdmchan->caller_data.hangup_cause; + causeDgn->recommend.pres = NOTPRSNT; + causeDgn->dgnVal.pres = NOTPRSNT; + return FTDM_SUCCESS; +} + ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId) { sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data; @@ -854,7 +890,7 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId) } ftdm_set_flag(sngisdn_info, FLAG_SENT_CHAN_ID); - + chanId->eh.pres = PRSNT_NODEF; chanId->prefExc.pres = PRSNT_NODEF; chanId->prefExc.val = IN_PE_EXCLSVE; @@ -863,8 +899,7 @@ ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId) chanId->intIdentPres.pres = PRSNT_NODEF; chanId->intIdentPres.val = IN_IIP_IMPLICIT; - if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI || - ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP) { + if (FTDM_SPAN_IS_BRI(ftdmchan->span)) { /* BRI only params */ chanId->intType.pres = PRSNT_NODEF; @@ -1085,10 +1120,17 @@ ftdm_status_t sngisdn_check_free_ids(void) return FTDM_SUCCESS; } -void get_memory_info(void) +void sngisdn_get_memory_info(void) { +#ifdef WIN32 + /* SRegInfoShow is not formally supported by Trillium with Windows */ + ftdm_log(FTDM_LOG_WARNING, "SRegInfoShow not supported on Windows\n"); +#else + /* SRegInfoShow is not formally supported by Trillium in Linux either, but + * it seems like its working fine so far */ U32 availmen = 0; SRegInfoShow(S_REG, &availmen); +#endif return; } @@ -1288,10 +1330,13 @@ void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t sigev.raw.data = sngisdn_info->raw_data; sigev.raw.len = sngisdn_info->raw_data_len; - + sngisdn_info->raw_data = NULL; sngisdn_info->raw_data_len = 0; } + if (event_id == FTDM_SIGEVENT_TRANSFER_COMPLETED) { + sigev.ev_data.transfer_completed.response = sngisdn_info->transfer_data.response; + } ftdm_span_send_signal(ftdmchan->span, &sigev); } diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c index d455fc06d0..f44f1032a3 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c @@ -757,7 +757,7 @@ void print_hex_dump(char* str, uint32_t *str_len, uint8_t* data, uint32_t index_ { uint32_t k; *str_len += sprintf(&str[*str_len], " [ "); - for(k=index_start; k <= index_end; k++) { + for(k=index_start; k < index_end; k++) { if (k && !(k%32)) { *str_len += sprintf(&str[*str_len], "\n "); } @@ -917,7 +917,7 @@ static ftdm_status_t sngisdn_map_call(sngisdn_span_data_t *signal_data, sngisdn_ case PROT_Q931_MSGTYPE_USER_INFO: case PROT_Q931_MSGTYPE_DISCONNECT: case PROT_Q931_MSGTYPE_RELEASE: - case PROT_Q931_MSGTYPE_RELEASE_ACK: + case PROT_Q931_MSGTYPE_RESTART_ACK: case PROT_Q931_MSGTYPE_RELEASE_COMPLETE: case PROT_Q931_MSGTYPE_FACILITY: case PROT_Q931_MSGTYPE_NOTIFY: diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h index db33cf8083..ca6683d4e3 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.h @@ -211,7 +211,7 @@ struct code2str dcodQ931CallRefLoTable[] = { #define PROT_Q931_MSGTYPE_DISCONNECT 69 #define PROT_Q931_MSGTYPE_RESTART 70 #define PROT_Q931_MSGTYPE_RELEASE 77 -#define PROT_Q931_MSGTYPE_RELEASE_ACK 78 +#define PROT_Q931_MSGTYPE_RESTART_ACK 78 #define PROT_Q931_MSGTYPE_RELEASE_COMPLETE 90 #define PROT_Q931_MSGTYPE_SEGMENT 96 #define PROT_Q931_MSGTYPE_FACILITY 98 @@ -240,7 +240,7 @@ struct code2str dcodQ931MsgTypeTable[] = { {PROT_Q931_MSGTYPE_DISCONNECT, "DISCONNECT"}, {PROT_Q931_MSGTYPE_RESTART, "RESTART"}, {PROT_Q931_MSGTYPE_RELEASE, "RELEASE"}, - {PROT_Q931_MSGTYPE_RELEASE_ACK, "RELEASR ACK"}, + {PROT_Q931_MSGTYPE_RESTART_ACK, "RESTART ACK"}, {PROT_Q931_MSGTYPE_RELEASE_COMPLETE, "RELEASE COMPLETE"}, {PROT_Q931_MSGTYPE_SEGMENT, "SEGMENT"}, {PROT_Q931_MSGTYPE_FACILITY, "FACILITY"}, diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c new file mode 100644 index 0000000000..f00087a061 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2011, Sangoma Technologies + * David Yat Sin + * 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 original author; nor the names of any contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE 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 "ftmod_sangoma_isdn.h" + +#define TRANSFER_FUNC(name) ftdm_status_t (name)(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* target) + +#define SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF "**1" +#define SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED "**5" +#define SNGISDN_ATT_TRANSFER_RESPONSE_OK "**6" +#define SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM "**7" +#define SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND "**8" + + +void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response); +void att_courtesy_transfer_timeout(void* p_sngisdn_info); +static ftdm_status_t att_courtesy_vru(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* target); + +typedef struct transfer_interfaces { + const char *name; + sngisdn_transfer_type_t type; + TRANSFER_FUNC(*func); +}transfer_interface_t; + +static transfer_interface_t transfer_interfaces[] = { + /* AT&T TR-50075 Courtesy Transfer - VRU -- No Data (Section 4.3) */ + { "ATT_COURTESY_TRANSFER_V", SNGISDN_TRANSFER_ATT_COURTESY_VRU, att_courtesy_vru}, + /* AT&T TR-50075 Courtesy Transfer - VRU --Data (Section 4.4) */ + { "ATT_COURTESY_TRANSFER_V_DATA", SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA, att_courtesy_vru}, +}; + +void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response) +{ + ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan; + ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED); + ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DTMF_DETECT, NULL); + + sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE; + sngisdn_info->transfer_data.response = response; + + ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Transfer Complete:%s\n", ftdm_transfer_response2str(sngisdn_info->transfer_data.response)); + sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_TRANSFER_COMPLETED); + + ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_RX_BUFFERS, NULL); + return; +} + +void att_courtesy_transfer_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 (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_NONE) { + /* Call was already cleared */ + ftdm_mutex_unlock(ftdmchan->mutex); + return; + } + + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "AT&T Courtesy Transfer timeout (%d)\n", signal_data->transfer_timeout); + att_courtesy_transfer_complete(sngisdn_info, FTDM_TRANSFER_RESPONSE_TIMEOUT); + ftdm_mutex_unlock(ftdmchan->mutex); + 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:%d 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_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; +} + + +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 /\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; +} + +ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf) +{ + ftdm_status_t status = FTDM_SUCCESS; + sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data; + sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data; + char *dtmf_digits = sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits; + ftdm_size_t dtmf_digits_len = strlen(dtmf_digits); + + dtmf_digits_len += sprintf(&dtmf_digits[dtmf_digits_len], "%s", dtmf); + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer response digits:%s\n", dtmf_digits); + if (dtmf_digits_len == 3) { + if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF)) { + sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_CP_DROP_OFF; + } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED)) { + sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED; + } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_OK)) { + sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_OK; + } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM)) { + sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_NUM; + } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND)) { + sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_COMMAND; + } else { + sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID; + } + if (signal_data->transfer_timeout) { + ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_TIMER_ATT_TRANSFER]); + } + + if (sngisdn_info->transfer_data.response == FTDM_TRANSFER_RESPONSE_OK && + sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) { + sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC); + ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING; + sngisdn_snd_disconnect(ftdmchan); + } + /* Network side will send disconnect in case of NO-DATA Transfer */ + att_courtesy_transfer_complete(sngisdn_info, sngisdn_info->transfer_data.response); + } + + if (signal_data->att_remove_dtmf) { + /* If we return FTDM_BREAK, dtmf event is not queue'ed to user */ + status = FTDM_BREAK; + } + return status; +} diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c index 2bf9f6bc2e..6c945e1512 100644 --- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c @@ -1193,12 +1193,26 @@ FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) #else if (pfds[i-1].revents & POLLPRI) { #endif - ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT); + ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT); ftdmchan->last_event_time = ftdm_current_time_in_ms(); k++; } +#ifdef LIBSANGOMA_VERSION + if (outflags[i-1] & POLLIN) { +#else + if (pfds[i-1].revents & POLLIN) { +#endif + ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ); + } +#ifdef LIBSANGOMA_VERSION + if (outflags[i-1] & POLLOUT) { +#else + if (pfds[i-1].revents & POLLOUT) { +#endif + ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE); + } } - /* when k is 0 it might be that an async wanpipe device signal was delivered */ + /* when k is 0 it might be that an async wanpipe device signal was delivered */ return FTDM_SUCCESS; } @@ -1445,14 +1459,9 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(wanpipe_channel_next_event) wanpipe_tdm_api_t tdm_api; ftdm_span_t *span = ftdmchan->span; - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) { - ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT); - } - memset(&tdm_api, 0, sizeof(tdm_api)); status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api); if (status != FTDM_SUCCESS) { - snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to read event from channel: %s\n", strerror(errno)); return FTDM_FAIL; } @@ -1488,7 +1497,7 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event) for(i = 1; i <= span->chan_count; i++) { /* as a hack for wink/flash detection, wanpipe_poll_event overrides the timeout parameter * to force the user to call this function each 5ms or so to detect the timeout of our wink/flash */ - if (span->channels[i]->last_event_time && !ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) { + if (span->channels[i]->last_event_time && !ftdm_test_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT)) { ftdm_time_t diff = ftdm_current_time_in_ms() - span->channels[i]->last_event_time; /* XX printf("%u %u %u\n", diff, (unsigned)ftdm_current_time_in_ms(), (unsigned)span->channels[i]->last_event_time); */ if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_WINK)) { @@ -1520,13 +1529,13 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_span_next_event) goto event; } } - } - if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) { + } + if (ftdm_test_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT)) { ftdm_status_t status; wanpipe_tdm_api_t tdm_api; ftdm_channel_t *ftdmchan = span->channels[i]; memset(&tdm_api, 0, sizeof(tdm_api)); - ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT); + ftdm_clear_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT); err = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api); if (err != FTDM_SUCCESS) { diff --git a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c index 9c01b41299..fa9f76cd3f 100644 --- a/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c +++ b/libs/freetdm/src/ftmod/ftmod_zt/ftmod_zt.c @@ -976,13 +976,19 @@ FIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event) snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); return FTDM_FAIL; } - + for(i = 1; i <= span->chan_count; i++) { if (pfds[i-1].revents & POLLPRI) { - ftdm_set_flag(span->channels[i], FTDM_CHANNEL_EVENT); + ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT); span->channels[i]->last_event_time = ftdm_current_time_in_ms(); k++; } + if (pfds[i-1].revents & POLLIN) { + ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_READ); + } + if (pfds[i-1].revents & POLLOUT) { + ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_WRITE); + } } if (!k) { @@ -1106,8 +1112,8 @@ FIO_CHANNEL_NEXT_EVENT_FUNCTION(zt_channel_next_event) zt_event_t zt_event_id = 0; ftdm_span_t *span = ftdmchan->span; - if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) { - ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT); + if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT)) { + ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT); } if (ioctl(ftdmchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) { @@ -1143,8 +1149,8 @@ FIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event) for(i = 1; i <= span->chan_count; i++) { ftdm_channel_t *fchan = span->channels[i]; - if (ftdm_test_flag(fchan, FTDM_CHANNEL_EVENT)) { - ftdm_clear_flag(fchan, FTDM_CHANNEL_EVENT); + if (ftdm_test_io_flag(fchan, FTDM_CHANNEL_IO_EVENT)) { + ftdm_clear_io_flag(fchan, FTDM_CHANNEL_IO_EVENT); if (ioctl(fchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) { snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno)); return FTDM_FAIL; diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index 253f256ffa..b792ed4ede 100755 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -30,9 +30,10 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Contributors: + * Contributors: * * Moises Silva + * David Yat Sin * */ @@ -317,6 +318,19 @@ typedef enum { #define CALLING_PARTY_CATEGORY_STRINGS "unknown", "operator", "operator-french", "operator-english", "operator-german", "operator-russian", "operator-spanish", "ordinary", "priority", "data-call", "test-call", "payphone", "invalid" FTDM_STR2ENUM_P(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t) +/*! Network responses to transfer requests */ +typedef enum { + FTDM_TRANSFER_RESPONSE_OK, /* Call is being transferred */ + FTDM_TRANSFER_RESPONSE_CP_DROP_OFF, /* Calling Party drop off */ + FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED, /* Cannot redirect, limits exceeded */ + FTDM_TRANSFER_RESPONSE_INVALID_NUM, /* Network did not receive or recognize dialed number */ + FTDM_TRANSFER_RESPONSE_INVALID_COMMAND, /* Network received an invalid command */ + FTDM_TRANSFER_RESPONSE_TIMEOUT, /* We did not receive a response from Network */ + FTDM_TRANSFER_RESPONSE_INVALID, +} ftdm_transfer_response_t; +#define TRANSFER_RESPONSE_STRINGS "transfer-ok", "cp-drop-off", "limits-exceeded", "invalid-num", "invalid-command", "timeout", "invalid" +FTDM_STR2ENUM_P(ftdm_str2ftdm_transfer_response, ftdm_transfer_response2str, ftdm_transfer_response_t) + /*! \brief Digit limit used in DNIS/ANI */ #define FTDM_DIGITS_LIMIT 25 @@ -436,13 +450,14 @@ typedef enum { FTDM_SIGEVENT_TRACE, /*!alarm_flags &= ~(flag) #define ftdm_test_alarm_flag(obj, flag) ((obj)->alarm_flags & flag) +#define ftdm_set_io_flag(obj, flag) (obj)->io_flags |= (flag) +#define ftdm_clear_io_flag(obj, flag) (obj)->io_flags &= ~(flag) +#define ftdm_test_io_flag(obj, flag) ((obj)->io_flags & flag) + /*! \brief Set a flag on an arbitrary object \command obj the object to set the flags on @@ -399,6 +403,7 @@ struct ftdm_channel { uint64_t flags; uint32_t pflags; uint32_t sflags; + uint8_t io_flags; ftdm_alarm_flag_t alarm_flags; ftdm_channel_feature_t features; ftdm_codec_t effective_codec; @@ -503,6 +508,7 @@ struct ftdm_span { ftdm_span_stop_t stop; ftdm_channel_sig_read_t sig_read; ftdm_channel_sig_write_t sig_write; + ftdm_channel_sig_dtmf_t sig_dtmf; ftdm_channel_state_processor_t state_processor; /*!< This guy is called whenever state processing is required */ void *io_data; /*!< Private I/O data per span. Do not touch unless you are an I/O module */ char *type; @@ -591,6 +597,10 @@ FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indi FT_DECLARE(ftdm_iterator_t *) ftdm_get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter); +FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen); + +FIO_WRITE_FUNCTION(ftdm_raw_write); +FIO_READ_FUNCTION(ftdm_raw_read); /*! * \brief Retrieves an event from the span diff --git a/libs/freetdm/src/include/private/ftdm_state.h b/libs/freetdm/src/include/private/ftdm_state.h index 191f8aef82..e11a18d590 100644 --- a/libs/freetdm/src/include/private/ftdm_state.h +++ b/libs/freetdm/src/include/private/ftdm_state.h @@ -67,6 +67,7 @@ typedef enum { FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, + FTDM_CHANNEL_STATE_TRANSFER, FTDM_CHANNEL_STATE_IDLE, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_CANCEL, @@ -78,7 +79,7 @@ typedef enum { } ftdm_channel_state_t; #define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \ "RING", "RINGING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \ - "RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", \ + "RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "TRANSFER", "IDLE", "TERMINATING", "CANCEL", \ "HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID" FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t) diff --git a/libs/freetdm/src/include/private/ftdm_types.h b/libs/freetdm/src/include/private/ftdm_types.h index e4d2b6900d..2ed9fd04c5 100755 --- a/libs/freetdm/src/include/private/ftdm_types.h +++ b/libs/freetdm/src/include/private/ftdm_types.h @@ -189,6 +189,8 @@ typedef enum { /* If this flag is set, then this span cannot be stopped individually, it can only be stopped on freetdm unload */ FTDM_SPAN_NON_STOPPABLE = (1 << 13), + /* If this flag is set, then this span supports TRANSFER state */ + FTDM_SPAN_USE_TRANSFER = (1 << 14), } ftdm_span_flag_t; /*! \brief Channel supported features */ @@ -206,6 +208,13 @@ typedef enum { FTDM_CHANNEL_FEATURE_MF_GENERATE = (1<<10), /*!< Channel can generate R2 MF tones (read-only) */ } ftdm_channel_feature_t; +/*! \brief Channel IO pending flags */ +typedef enum { + FTDM_CHANNEL_IO_EVENT = (1 << 0), + FTDM_CHANNEL_IO_READ = (1 << 1), + FTDM_CHANNEL_IO_WRITE = (1 << 2), +} ftdm_channel_io_flags_t; + /*!< Channel flags. This used to be an enum but we reached the 32bit limit for enums, is safer this way */ #define FTDM_CHANNEL_CONFIGURED (1ULL << 0) #define FTDM_CHANNEL_READY (1ULL << 1) @@ -214,7 +223,6 @@ typedef enum { #define FTDM_CHANNEL_SUPRESS_DTMF (1ULL << 4) #define FTDM_CHANNEL_TRANSCODE (1ULL << 5) #define FTDM_CHANNEL_BUFFER (1ULL << 6) -#define FTDM_CHANNEL_EVENT (1ULL << 7) #define FTDM_CHANNEL_INTHREAD (1ULL << 8) #define FTDM_CHANNEL_WINK (1ULL << 9) #define FTDM_CHANNEL_FLASH (1ULL << 10) @@ -331,10 +339,11 @@ typedef ftdm_status_t (*ftdm_span_start_t)(ftdm_span_t *span); typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span); typedef ftdm_status_t (*ftdm_channel_sig_read_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size); typedef ftdm_status_t (*ftdm_channel_sig_write_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size); +typedef ftdm_status_t (*ftdm_channel_sig_dtmf_t)(ftdm_channel_t *ftdmchan, const char *dtmf); typedef enum { FTDM_ITERATOR_VARS = 1, - FTDM_ITERATOR_CHANS, + FTDM_ITERATOR_CHANS, } ftdm_iterator_type_t; struct ftdm_iterator {