sig_analog: Add Called Subscriber Held capability.

This adds support for Called Subscriber Held for FXS
lines, which allows users to go on hook when receiving
a call and resume the call later from another phone on
the same line, without disconnecting the call. This is
a convenience mechanism that most real PSTN telephone
switches support.

ASTERISK-30372 #close

Resolves: #240

UserNote: Called Subscriber Held is now supported for analog
FXS channels, using the calledsubscriberheld option. This allows
a station  user to go on hook when receiving an incoming call
and resume from another phone on the same line by going on hook,
without disconnecting the call.

(cherry picked from commit 52283301e8)
This commit is contained in:
Naveen Albert 2023-08-09 22:04:23 +00:00 committed by Asterisk Development Team
parent 8bf9b1d966
commit 534269e3e6
5 changed files with 60 additions and 0 deletions

View File

@ -12949,6 +12949,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
tmp->callwaitingcallerid = conf->chan.callwaitingcallerid;
tmp->threewaycalling = conf->chan.threewaycalling;
tmp->threewaysilenthold = conf->chan.threewaysilenthold;
tmp->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Not used in chan_dahdi.c, just analog pvt, but must exist on the DAHDI pvt anyways */
tmp->adsi = conf->chan.adsi;
tmp->use_smdi = conf->chan.use_smdi;
tmp->permhidecallerid = conf->chan.hidecallerid;
@ -13247,6 +13248,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
analog_p->ani_wink_time = conf->chan.ani_wink_time;
analog_p->hanguponpolarityswitch = conf->chan.hanguponpolarityswitch;
analog_p->permcallwaiting = conf->chan.callwaiting; /* permcallwaiting possibly modified in analog_config_complete */
analog_p->calledsubscriberheld = conf->chan.calledsubscriberheld; /* Only actually used in analog pvt, not DAHDI pvt */
analog_p->callreturn = conf->chan.callreturn;
analog_p->cancallforward = conf->chan.cancallforward;
analog_p->canpark = conf->chan.canpark;
@ -18341,6 +18343,8 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
confp->chan.busycount = atoi(v->value);
} else if (!strcasecmp(v->name, "busypattern")) {
parse_busy_pattern(v, &confp->chan.busy_cadence);
} else if (!strcasecmp(v->name, "calledsubscriberheld")) {
confp->chan.calledsubscriberheld = ast_true(v->value);
} else if (!strcasecmp(v->name, "callprogress")) {
confp->chan.callprogress &= ~CALLPROGRESS_PROGRESS;
if (ast_true(v->value))

View File

@ -204,6 +204,13 @@ struct dahdi_pvt {
* \note Set from the "busydetect" value read in from chan_dahdi.conf
*/
unsigned int busydetect:1;
/*!
* \brief TRUE if Called Subscriber held is enabled.
* This allows a single incoming call to hold a DAHDI channel up,
* allowing a recipient to hang up an extension and pick up another
* phone on the same line without disconnecting the call.
*/
unsigned int calledsubscriberheld:1;
/*!
* \brief TRUE if call return is enabled.
* (*69, if your dialplan doesn't catch this first)

View File

@ -805,6 +805,11 @@ int analog_available(struct analog_pvt *p)
return 0;
}
/* If line is being held, definitely not (don't allow call waitings to an on-hook phone) */
if (p->cshactive) {
return 0;
}
/* If no owner definitely available */
if (!p->owner) {
offhook = analog_is_off_hook(p);
@ -1300,6 +1305,7 @@ int analog_hangup(struct analog_pvt *p, struct ast_channel *ast)
p->channel, idx, p->subs[ANALOG_SUB_REAL].allocd, p->subs[ANALOG_SUB_CALLWAIT].allocd, p->subs[ANALOG_SUB_THREEWAY].allocd);
if (idx > -1) {
/* Real channel, do some fixup */
p->cshactive = 0;
p->subs[idx].owner = NULL;
p->polarity = POLARITY_IDLE;
analog_set_linear_mode(p, idx, 0);
@ -2933,6 +2939,34 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
analog_get_and_handle_alarms(p);
cause_code->ast_cause = AST_CAUSE_NETWORK_OUT_OF_ORDER;
case ANALOG_EVENT_ONHOOK:
if (p->calledsubscriberheld && (p->sig == ANALOG_SIG_FXOLS || p->sig == ANALOG_SIG_FXOGS || p->sig == ANALOG_SIG_FXOKS) && idx == ANALOG_SUB_REAL) {
ast_debug(4, "Channel state on %s is %d\n", ast_channel_name(ast), ast_channel_state(ast));
/* Called Subscriber Held: don't let the called party hang up on an incoming call immediately (if it's the only call). */
if (p->subs[ANALOG_SUB_CALLWAIT].owner || p->subs[ANALOG_SUB_THREEWAY].owner) {
ast_debug(2, "Letting this call hang up normally, since it's not the only call\n");
} else if (!p->owner || !p->subs[ANALOG_SUB_REAL].owner || ast_channel_state(ast) != AST_STATE_UP) {
ast_debug(2, "Called Subscriber Held does not apply: channel state is %d\n", ast_channel_state(ast));
} else if (!p->owner || !p->subs[ANALOG_SUB_REAL].owner || strcmp(ast_channel_appl(p->subs[ANALOG_SUB_REAL].owner), "AppDial")) {
/* Called Subscriber held only applies to incoming calls, not outgoing calls.
* We can't use p->outgoing because that is always true, for both incoming and outgoing calls, so it's not accurate.
* We can check the channel application/data instead.
* For incoming calls to the channel, it will look like: AppDial / (Outgoing Line)
* We only want this behavior for regular calls anyways (and not, say, Queue),
* so this would actually work great. But accessing ast_channel_appl can cause a crash if there are no calls left,
* so this check must occur AFTER we confirm the channel state *is* still UP.
*/
ast_debug(2, "Called Subscriber Held does not apply: not an incoming call\n");
} else if (analog_is_off_hook(p)) {
ast_log(LOG_WARNING, "Got ONHOOK but channel %d is off hook?\n", p->channel); /* Shouldn't happen */
} else {
ast_verb(3, "Holding incoming call %s for channel %d\n", ast_channel_name(ast), p->channel);
/* Inhibit dahdi_hangup from getting called, and do nothing else now.
* When the DAHDI channel goes off hook again, it'll just get reconnected with the incoming call,
* to which, as far as its concerned, nothing has happened. */
p->cshactive = 1; /* Keep track that this DAHDI channel is currently being held by an incoming call. */
break;
}
}
ast_queue_control_data(ast, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
ast_channel_hangupcause_hash_set(ast, cause_code, data_size);
switch (p->sig) {
@ -3809,6 +3843,7 @@ void *analog_handle_init_event(struct analog_pvt *i, int event)
case ANALOG_SIG_FXOKS:
res = analog_off_hook(i);
i->fxsoffhookstate = 1;
i->cshactive = 0;
if (res && (errno == EBUSY)) {
break;
}

View File

@ -289,6 +289,7 @@ struct analog_pvt {
unsigned int ani_wink_time:16; /* Safe wait time before we wink to start ANI spill */
unsigned int answeronpolarityswitch:1;
unsigned int calledsubscriberheld:1; /*!< TRUE if a single incoming call can hold an FXS channel */
unsigned int callreturn:1;
unsigned int cancallforward:1;
unsigned int canpark:1;
@ -330,6 +331,7 @@ struct analog_pvt {
/* XXX: All variables after this are internal */
unsigned int callwaiting:1; /*!< TRUE if call waiting is enabled. (Active option) */
unsigned int cshactive:1; /*!< TRUE if FXS channel is currently held by an incoming call */
unsigned int dialednone:1;
unsigned int dialing:1; /*!< TRUE if in the process of dialing digits or sending something */
unsigned int dnd:1; /*!< TRUE if Do-Not-Disturb is enabled. */

View File

@ -755,6 +755,18 @@ usecallingpres=yes
;
callwaitingcallerid=yes
;
; Whether or not to allow users to go on-hook when receiving an incoming call
; without disconnecting it. Users can later resume the call from any phone
; on the same physical phone line (the same DAHDI channel).
; This setting only has an effect on FXS (FXO-signalled) channels where there
; is only a single incoming call to the DAHDI channel, using the Dial application.
; (This is a convenience mechanism to avoid users wishing to resume a conversation
; at a different phone from leaving a phone off the hook, resuming elsewhere,
; and forgetting to restore the original phone on hook afterwards.)
; Default is no.
;
;calledsubscriberheld=yes
;
; Support three-way calling
;
threewaycalling=yes