]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
sig_analog: Add Called Subscriber Held capability.
authorNaveen Albert <asterisk@phreaknet.org>
Wed, 9 Aug 2023 22:04:23 +0000 (22:04 +0000)
committerNaveen Albert <asterisk@phreaknet.org>
Tue, 22 Aug 2023 13:30:26 +0000 (13:30 +0000)
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.

channels/chan_dahdi.c
channels/chan_dahdi.h
channels/sig_analog.c
channels/sig_analog.h
configs/samples/chan_dahdi.conf.sample

index 5823a4eac4e696d7b3964d6cf51a927e314bba86..3b3f12474c08bd050fdd896a918560ce3fb366d2 100644 (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))
index 31aea7f1f6bf15c4b4ddd9cdf6c0f1f3628a3796..edf28e1ebaed86ded92d8db1ead3fdde518918bd 100644 (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)
index 816735effb8776299577844e480ff09bbaabbef6..92fc47eda6c950aad8f74cebbe75f2b17bfdd495 100644 (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;
                        }
index 673767933fd323a82c6bc371bdf108d9346737b2..81043f39a58c4119ac7a3483fb043945a9165798 100644 (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. */
index 7df30c034b87a3094ac9813772def6182e9b110d..c9f5aa4fd07da7903fc058513dca3dc107d0ee61 100644 (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