diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 5823a4eac4..3b3f12474c 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -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)) diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h index 31aea7f1f6..edf28e1eba 100644 --- a/channels/chan_dahdi.h +++ b/channels/chan_dahdi.h @@ -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) diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 816735effb..92fc47eda6 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -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; } diff --git a/channels/sig_analog.h b/channels/sig_analog.h index 673767933f..81043f39a5 100644 --- a/channels/sig_analog.h +++ b/channels/sig_analog.h @@ -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. */ diff --git a/configs/samples/chan_dahdi.conf.sample b/configs/samples/chan_dahdi.conf.sample index 7df30c034b..c9f5aa4fd0 100644 --- a/configs/samples/chan_dahdi.conf.sample +++ b/configs/samples/chan_dahdi.conf.sample @@ -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