ftmod_libpri: Implement channel hunting in NT-mode.
Hunt for a free channel for incoming calls that do not preselect a channel (pevent->ring.channel == -1). Verify the preselected channel for calls that do specify a channel and in case the channel is already taken, hunt for a free one, or abort with an error message (if the preselection was exclusive). TE-mode channel selection is the same as before (there's still room for improvement, though, but i'll save that for later). The MSN/DDI filter code is moved into the TE-mode section (only useful there). The duplicate ring detection had to be reworked. We now store the call reference (CRV) in caller_data->call_reference of the selected channel and do a CRV -> channel look up with find_channel_by_cref() at the top of on_ring(). NOTE: This is only lightly tested (NT/TE mode), i'd either have to write a lot of custom code to check it toroughly or the need for a scriptable ISDN stack... Signed-off-by: Stefan Knoblich <stkn@openisdn.net>
This commit is contained in:
parent
7bc19b0c1b
commit
e8d0845376
|
@ -1538,6 +1538,107 @@ out:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up FreeTDM channel by call reference value
|
||||||
|
* \param[in] span Span object
|
||||||
|
* \param[in] crv CRV to search for
|
||||||
|
* \return Channel on success, NULL otherwise
|
||||||
|
*/
|
||||||
|
static ftdm_channel_t *find_channel_by_cref(ftdm_span_t *span, const int cref)
|
||||||
|
{
|
||||||
|
ftdm_iterator_t *iter = NULL;
|
||||||
|
ftdm_channel_t *chan = NULL;
|
||||||
|
|
||||||
|
if (!span || cref <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ftdm_mutex_lock(span->mutex);
|
||||||
|
|
||||||
|
/* Iterate over all channels on this span */
|
||||||
|
for (iter = ftdm_span_get_chan_iterator(span, NULL); iter; iter = ftdm_iterator_next(iter)) {
|
||||||
|
ftdm_channel_t *cur = ftdm_iterator_current(iter);
|
||||||
|
ftdm_caller_data_t *caller_data = NULL;
|
||||||
|
|
||||||
|
if (ftdm_channel_get_type(cur) != FTDM_CHAN_TYPE_B)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
caller_data = ftdm_channel_get_caller_data(cur);
|
||||||
|
|
||||||
|
if (caller_data->call_reference == cref) {
|
||||||
|
chan = cur;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdm_iterator_free(iter);
|
||||||
|
ftdm_mutex_unlock(span->mutex);
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hunt for free channel (NT-mode only)
|
||||||
|
* \param[in] span Span to hunt on
|
||||||
|
* \param[in] hint Channel ID hint (preferred by remote end)
|
||||||
|
* \param[in] excl Is the hint exclusive (or preferred)?
|
||||||
|
* \param[out] chan Selected channel
|
||||||
|
* \retval FTDM_SUCCESS A free channel has been found
|
||||||
|
* \retval FTDM_FAIL No free channels could be found on the span
|
||||||
|
* \retval FTDM_EBUSY The channel indicated in the exclusive hint is already in use
|
||||||
|
*/
|
||||||
|
static ftdm_status_t hunt_channel(ftdm_span_t *span, const int hint, const ftdm_bool_t excl, ftdm_channel_t **chan)
|
||||||
|
{
|
||||||
|
ftdm_iterator_t *iter = NULL;
|
||||||
|
ftdm_channel_t *tmp = NULL;
|
||||||
|
int ret = FTDM_FAIL;
|
||||||
|
|
||||||
|
/* lock span */
|
||||||
|
ftdm_mutex_lock(span->mutex);
|
||||||
|
|
||||||
|
/* Check hint */
|
||||||
|
if (hint > 0) {
|
||||||
|
tmp = ftdm_span_get_channel(span, hint);
|
||||||
|
if (!tmp) {
|
||||||
|
ftdm_log(FTDM_LOG_NOTICE, "Invalid channel hint '%d' given (out of bounds)\n", hint);
|
||||||
|
}
|
||||||
|
else if (!ftdm_test_flag(tmp, FTDM_CHANNEL_INUSE) && ftdm_channel_get_type(tmp) == FTDM_CHAN_TYPE_B) {
|
||||||
|
ftdm_log(FTDM_LOG_DEBUG, "Using channel '%d' from hint\n", ftdm_channel_get_id(tmp));
|
||||||
|
ftdm_channel_use(tmp);
|
||||||
|
ret = FTDM_SUCCESS;
|
||||||
|
*chan = tmp;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
else if (excl) {
|
||||||
|
ftdm_log(FTDM_LOG_NOTICE, "Channel '%d' in exclusive hint is not available\n",
|
||||||
|
ftdm_channel_get_id(tmp));
|
||||||
|
ret = FTDM_EBUSY;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate over all channels on this span */
|
||||||
|
for (iter = ftdm_span_get_chan_iterator(span, NULL); iter; iter = ftdm_iterator_next(iter)) {
|
||||||
|
tmp = ftdm_iterator_current(iter);
|
||||||
|
|
||||||
|
if (ftdm_channel_get_type(tmp) != FTDM_CHAN_TYPE_B)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ftdm_test_flag(tmp, FTDM_CHANNEL_INUSE)) {
|
||||||
|
ftdm_channel_use(tmp);
|
||||||
|
ret = FTDM_SUCCESS;
|
||||||
|
*chan = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdm_iterator_free(iter);
|
||||||
|
out:
|
||||||
|
ftdm_mutex_unlock(span->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Handler for libpri ring event
|
* \brief Handler for libpri ring event
|
||||||
* \param spri Pri wrapper structure (libpri, span, dchan)
|
* \param spri Pri wrapper structure (libpri, span, dchan)
|
||||||
|
@ -1553,76 +1654,96 @@ static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
|
||||||
ftdm_caller_data_t *caller_data = NULL;
|
ftdm_caller_data_t *caller_data = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (pevent->ring.channel == -1) {
|
/*
|
||||||
|
* Check if call has an associated channel (duplicate ring event)
|
||||||
|
*/
|
||||||
|
if ((chan = find_channel_by_cref(span, pevent->ring.cref))) {
|
||||||
|
ftdm_log_chan_msg(chan, FTDM_LOG_NOTICE, "-- Duplicate ring received (ignored)\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdn_data->mode == PRI_NETWORK) {
|
||||||
|
/*
|
||||||
|
* Always hunt for a free channel in NT-mode,
|
||||||
|
* but use the pre-selected one as hint
|
||||||
|
*/
|
||||||
|
switch (hunt_channel(span, pevent->ring.channel, !pevent->ring.flexible, &chan)) {
|
||||||
|
case FTDM_SUCCESS: /* OK channel found */
|
||||||
|
break;
|
||||||
|
case FTDM_EBUSY: /* Exclusive channel hint is not available */
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "-- New call without channel on span '%s' [NOTE: Initial SETUP w/o channel selection is not supported by FreeTDM]\n",
|
||||||
|
ftdm_span_get_name(span));
|
||||||
|
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_CHANNEL_UNACCEPTABLE);
|
||||||
|
return ret;
|
||||||
|
default:
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "-- New call without channel on span '%s' [NOTE: Initial SETUP w/o channel selection is not supported by FreeTDM]\n",
|
||||||
|
ftdm_span_get_name(span));
|
||||||
|
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_NORMAL_CIRCUIT_CONGESTION);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdm_channel_lock(chan);
|
||||||
|
|
||||||
|
} else if (pevent->ring.channel == -1) {
|
||||||
|
/*
|
||||||
|
* TE-mode incoming call without channel selection (not supported)
|
||||||
|
*/
|
||||||
ftdm_log(FTDM_LOG_ERROR, "-- New call without channel on span '%s' [NOTE: Initial SETUP w/o channel selection is not supported by FreeTDM]\n",
|
ftdm_log(FTDM_LOG_ERROR, "-- New call without channel on span '%s' [NOTE: Initial SETUP w/o channel selection is not supported by FreeTDM]\n",
|
||||||
ftdm_span_get_name(span));
|
ftdm_span_get_name(span));
|
||||||
pri_destroycall(spri->pri, pevent->ring.call);
|
pri_destroycall(spri->pri, pevent->ring.call);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
chan = ftdm_span_get_channel(span, pevent->ring.channel);
|
|
||||||
if (!chan) {
|
|
||||||
ftdm_log(FTDM_LOG_ERROR, "-- Unable to get channel %d:%d\n",
|
|
||||||
ftdm_span_get_id(span), pevent->ring.channel);
|
|
||||||
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_DESTINATION_OUT_OF_ORDER);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check MSN filter */
|
|
||||||
if (!msn_filter_match(isdn_data, pevent->ring.callednum)) {
|
|
||||||
ftdm_log(FTDM_LOG_INFO, "-- MSN filter not matching incoming DNIS '%s', ignoring call\n",
|
|
||||||
pevent->ring.callednum);
|
|
||||||
pri_destroycall(spri->pri, pevent->ring.call);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ftdm_channel_lock(chan);
|
|
||||||
|
|
||||||
if (chan->call_data) {
|
|
||||||
/* we could drop the incoming call, but most likely the pointer is just a ghost of the past,
|
|
||||||
* this check is just to detect potentially unreleased pointers */
|
|
||||||
ftdm_log_chan(chan, FTDM_LOG_ERROR, "channel already has call %p!\n", chan->call_data);
|
|
||||||
chan->call_data = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ftdm_channel_get_state(chan) != FTDM_CHANNEL_STATE_DOWN || ftdm_test_flag(chan, FTDM_CHANNEL_INUSE)) {
|
|
||||||
ftdm_log(FTDM_LOG_WARNING, "-- Duplicate Ring on channel %d:%d (ignored)\n", ftdm_span_get_id(span), pevent->ring.channel);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((pevent->ring.progressmask & PRI_PROG_INBAND_AVAILABLE)) {
|
|
||||||
/* Open channel if inband information is available */
|
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, opening B-Channel %d:%d\n",
|
|
||||||
ftdm_channel_get_span_id(chan),
|
|
||||||
ftdm_channel_get_id(chan));
|
|
||||||
|
|
||||||
if (!ftdm_test_flag(chan, FTDM_CHANNEL_OPEN) && ftdm_channel_open_chan(chan) != FTDM_SUCCESS) {
|
|
||||||
// ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan);
|
|
||||||
|
|
||||||
ftdm_log(FTDM_LOG_WARNING, "-- Error opening channel %d:%d (ignored)\n",
|
|
||||||
ftdm_channel_get_span_id(chan),
|
|
||||||
ftdm_channel_get_id(chan));
|
|
||||||
|
|
||||||
// caller_data->hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER;
|
|
||||||
// ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_TERMINATING);
|
|
||||||
// goto done;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* Reserve channel, don't open it yet */
|
/*
|
||||||
if (ftdm_channel_use(chan) != FTDM_SUCCESS) {
|
* TE-mode, check MSN filter, ignore calls that aren't for this PTMP terminal
|
||||||
ftdm_log(FTDM_LOG_WARNING, "-- Error reserving channel %d:%d (ignored)\n",
|
*/
|
||||||
|
if (!msn_filter_match(isdn_data, pevent->ring.callednum)) {
|
||||||
|
ftdm_log(FTDM_LOG_INFO, "-- MSN filter not matching incoming DNIS '%s', ignoring call\n",
|
||||||
|
pevent->ring.callednum);
|
||||||
|
pri_destroycall(spri->pri, pevent->ring.call);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TE-mode channel selection, use whatever the NT tells us to
|
||||||
|
*/
|
||||||
|
chan = ftdm_span_get_channel(span, pevent->ring.channel);
|
||||||
|
if (!chan) {
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "-- Unable to get channel %d:%d\n",
|
||||||
ftdm_span_get_id(span), pevent->ring.channel);
|
ftdm_span_get_id(span), pevent->ring.channel);
|
||||||
|
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_DESTINATION_OUT_OF_ORDER);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdm_channel_lock(chan);
|
||||||
|
|
||||||
|
if (ftdm_channel_get_state(chan) != FTDM_CHANNEL_STATE_DOWN || ftdm_test_flag(chan, FTDM_CHANNEL_INUSE)) {
|
||||||
|
ftdm_log_chan_msg(chan, FTDM_LOG_ERROR, "-- Selected channel is already in use\n");
|
||||||
|
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_DESTINATION_OUT_OF_ORDER);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reserve channel */
|
||||||
|
if (ftdm_channel_use(chan) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log_chan_msg(chan, FTDM_LOG_ERROR, "-- Error reserving channel\n");
|
||||||
|
pri_hangup(spri->pri, pevent->ring.call, PRI_CAUSE_DESTINATION_OUT_OF_ORDER);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ftdm_log(FTDM_LOG_NOTICE, "-- Ring on channel %d:%d (from %s to %s)\n", ftdm_span_get_id(span), pevent->ring.channel,
|
if (chan->call_data) {
|
||||||
pevent->ring.callingnum, pevent->ring.callednum);
|
/* we could drop the incoming call, but most likely the pointer is just a ghost of the past,
|
||||||
|
* this check is just to detect potentially unreleased pointers */
|
||||||
|
ftdm_log_chan(chan, FTDM_LOG_WARNING, "Channel already has call %p!\n", chan->call_data);
|
||||||
|
chan->call_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
caller_data = ftdm_channel_get_caller_data(chan);
|
caller_data = ftdm_channel_get_caller_data(chan);
|
||||||
|
|
||||||
memset(caller_data, 0, sizeof(*caller_data));
|
memset(caller_data, 0, sizeof(*caller_data));
|
||||||
|
|
||||||
|
/* Save CRV, so we can do proper duplicate RING detection */
|
||||||
|
caller_data->call_reference = pevent->ring.cref;
|
||||||
|
|
||||||
ftdm_set_string(caller_data->cid_num.digits, (char *)pevent->ring.callingnum);
|
ftdm_set_string(caller_data->cid_num.digits, (char *)pevent->ring.callingnum);
|
||||||
ftdm_set_string(caller_data->ani.digits, (char *)pevent->ring.callingani);
|
ftdm_set_string(caller_data->ani.digits, (char *)pevent->ring.callingani);
|
||||||
ftdm_set_string(caller_data->dnis.digits, (char *)pevent->ring.callednum);
|
ftdm_set_string(caller_data->dnis.digits, (char *)pevent->ring.callednum);
|
||||||
|
@ -1642,7 +1763,30 @@ static int on_ring(lpwrap_pri_t *spri, lpwrap_pri_event_t event_type, pri_event
|
||||||
/* hurr, this is valid as along as nobody releases the call */
|
/* hurr, this is valid as along as nobody releases the call */
|
||||||
chan->call_data = pevent->ring.call;
|
chan->call_data = pevent->ring.call;
|
||||||
|
|
||||||
/* only go to RING state if we have the complete called number (indicated via pevent->complete flag) */
|
/* Open Channel if inband information is available */
|
||||||
|
if ((pevent->ring.progressmask & PRI_PROG_INBAND_AVAILABLE)) {
|
||||||
|
/* Open channel if inband information is available */
|
||||||
|
ftdm_log(FTDM_LOG_DEBUG, "-- In-band information available, opening B-Channel %d:%d\n",
|
||||||
|
ftdm_channel_get_span_id(chan),
|
||||||
|
ftdm_channel_get_id(chan));
|
||||||
|
|
||||||
|
if (!ftdm_test_flag(chan, FTDM_CHANNEL_OPEN) && ftdm_channel_open_chan(chan) != FTDM_SUCCESS) {
|
||||||
|
// ftdm_caller_data_t *caller_data = ftdm_channel_get_caller_data(chan);
|
||||||
|
|
||||||
|
ftdm_log(FTDM_LOG_WARNING, "-- Error opening channel %d:%d (ignored)\n",
|
||||||
|
ftdm_channel_get_span_id(chan),
|
||||||
|
ftdm_channel_get_id(chan));
|
||||||
|
|
||||||
|
// caller_data->hangup_cause = FTDM_CAUSE_DESTINATION_OUT_OF_ORDER;
|
||||||
|
// ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_TERMINATING);
|
||||||
|
// goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ftdm_log(FTDM_LOG_NOTICE, "-- Ring on channel %d:%d (from %s to %s)\n", ftdm_span_get_id(span), pevent->ring.channel,
|
||||||
|
pevent->ring.callingnum, pevent->ring.callednum);
|
||||||
|
|
||||||
|
/* Only go to RING state if we have the complete called number (indicated via pevent->complete flag) */
|
||||||
if (!pevent->ring.complete && (isdn_data->overlap & FTMOD_LIBPRI_OVERLAP_RECEIVE)) {
|
if (!pevent->ring.complete && (isdn_data->overlap & FTMOD_LIBPRI_OVERLAP_RECEIVE)) {
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "RING event without complete indicator, waiting for more digits\n");
|
ftdm_log(FTDM_LOG_DEBUG, "RING event without complete indicator, waiting for more digits\n");
|
||||||
ftdm_set_state(chan, FTDM_CHANNEL_STATE_COLLECT);
|
ftdm_set_state(chan, FTDM_CHANNEL_STATE_COLLECT);
|
||||||
|
|
Loading…
Reference in New Issue