From 7e686596160f15d74625a3928fce2e26c20a09b3 Mon Sep 17 00:00:00 2001 From: Naveen Albert Date: Mon, 18 Aug 2025 08:56:23 -0400 Subject: [PATCH] chan_dahdi: Fix erroneously persistent dialmode. It is possible to modify the dialmode setting in the chan_dahdi/sig_analog private using the CHANNEL function, to modify it during calls. However, it was not being reset between calls, meaning that if, for example, tone dialing was disabled, it would never work again unless explicitly enabled. This fixes the setting by pairing it with a "perm" version of the setting, as a few other features have, so that it can be reset to the permanent setting between calls. The documentation is also clarified to explain the interaction of this setting and the digitdetect setting more clearly. Resolves: #1378 --- channels/chan_dahdi.c | 42 +++++++++++++++++++++++++++++------------- channels/chan_dahdi.h | 2 +- channels/sig_analog.c | 33 +++++++++++++++++++++++++++++++++ channels/sig_analog.h | 1 + 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 2b0b14f471..a468650e36 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -277,11 +277,16 @@ R/W Pulse and tone dialing mode of the channel. - Disabling tone dialing using this option will not disable the DSP used for DTMF detection. + Disabling tone dialing using this option will not automatically disable the DSP used for DTMF detection. To do that, also set the digitdetect option. If digit detection is disabled, DTMF will not be detected, regardless of the dialmode setting. The digitdetect setting has no impact on pulse dialing detection. If set, overrides the setting in chan_dahdi.conf for that channel. + The dialmode setting applies to the DAHDI channel as a whole, but is reset for each call, + so modifications made using the CHANNEL function apply temporarily per-call. + The digitdetect setting applies to the entire DAHDI channel, + so any changes made to this setting will affect all calls concurrently on the same DAHDI channel. + digitdetect is reset once all calls on the line have cleared. @@ -1144,7 +1149,7 @@ static struct dahdi_chan_conf dahdi_chan_conf_default(void) .mohsuggest = "", .parkinglot = "", .transfertobusy = 1, - .dialmode = 0, + .permdialmode = ANALOG_DIALMODE_BOTH, .ani_info_digits = 2, .ani_wink_time = 1000, @@ -6691,6 +6696,16 @@ static int dahdi_hangup(struct ast_channel *ast) p->callwaitcas = 0; p->callwaiting = p->permcallwaiting; p->hidecallerid = p->permhidecallerid; + if (dahdi_analog_lib_handles(p->sig, p->radio, 0) && !p->owner) { + /* The code in sig_analog handles resetting to permdialmode on originations; + * this addresses the edge case of multiple calls that do not involve + * origination inbetween, i.e. multiple incoming calls. */ + struct analog_pvt *analog_p = p->sig_pvt; + /* If no calls remain, reset dialmode. + * This way, if the next call is an incoming call, + * it's already been reset. */ + analog_p->dialmode = analog_p->permdialmode; + } p->waitingfordt.tv_sec = 0; p->dialing = 0; p->rdnis[0] = '\0'; @@ -7377,13 +7392,13 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char } /* analog pvt is used for pulse dialing, so update both */ if (!strcasecmp(value, "pulse")) { - p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_PULSE; + analog_p->dialmode = ANALOG_DIALMODE_PULSE; } else if (!strcasecmp(value, "dtmf") || !strcasecmp(value, "tone")) { - p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_DTMF; + analog_p->dialmode = ANALOG_DIALMODE_DTMF; } else if (!strcasecmp(value, "none")) { - p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_NONE; + analog_p->dialmode = ANALOG_DIALMODE_NONE; } else if (!strcasecmp(value, "both")) { - p->dialmode = analog_p->dialmode = ANALOG_DIALMODE_BOTH; + analog_p->dialmode = ANALOG_DIALMODE_BOTH; } else { ast_log(LOG_WARNING, "'%s' is an invalid setting for %s\n", value, data); res = -1; @@ -9279,6 +9294,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast) f = &p->subs[idx].f; if (f) { + struct analog_pvt *analog_p = p->sig_pvt; switch (f->frametype) { case AST_FRAME_DTMF_BEGIN: case AST_FRAME_DTMF_END: @@ -9287,7 +9303,7 @@ static struct ast_frame *dahdi_read(struct ast_channel *ast) } else { dahdi_handle_dtmf(ast, idx, &f); } - if (!(p->dialmode == ANALOG_DIALMODE_BOTH || p->dialmode == ANALOG_DIALMODE_DTMF)) { + if (!(analog_p->dialmode == ANALOG_DIALMODE_BOTH || analog_p->dialmode == ANALOG_DIALMODE_DTMF)) { if (f->frametype == AST_FRAME_DTMF_END) { /* only show this message when the key is let go of */ ast_debug(1, "Dropping DTMF digit '%c' because tone dialing is disabled\n", f->subclass.integer); } @@ -13127,7 +13143,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, tmp->immediate = conf->chan.immediate; tmp->immediatering = conf->chan.immediatering; tmp->transfertobusy = conf->chan.transfertobusy; - tmp->dialmode = conf->chan.dialmode; + tmp->permdialmode = conf->chan.permdialmode; if (chan_sig & __DAHDI_SIG_FXS) { tmp->mwimonitor_fsk = conf->chan.mwimonitor_fsk; tmp->mwimonitor_neon = conf->chan.mwimonitor_neon; @@ -13470,7 +13486,7 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf, analog_p->threewaycalling = conf->chan.threewaycalling; analog_p->transfer = conf->chan.transfer; analog_p->transfertobusy = conf->chan.transfertobusy; - analog_p->dialmode = conf->chan.dialmode; + analog_p->permdialmode = conf->chan.permdialmode; analog_p->use_callerid = tmp->use_callerid; analog_p->usedistinctiveringdetection = tmp->usedistinctiveringdetection; analog_p->use_smdi = tmp->use_smdi; @@ -18851,13 +18867,13 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct confp->chan.transfertobusy = ast_true(v->value); } else if (!strcasecmp(v->name, "dialmode")) { if (!strcasecmp(v->value, "pulse")) { - confp->chan.dialmode = ANALOG_DIALMODE_PULSE; + confp->chan.permdialmode = ANALOG_DIALMODE_PULSE; } else if (!strcasecmp(v->value, "dtmf") || !strcasecmp(v->value, "tone")) { - confp->chan.dialmode = ANALOG_DIALMODE_DTMF; + confp->chan.permdialmode = ANALOG_DIALMODE_DTMF; } else if (!strcasecmp(v->value, "none")) { - confp->chan.dialmode = ANALOG_DIALMODE_NONE; + confp->chan.permdialmode = ANALOG_DIALMODE_NONE; } else { - confp->chan.dialmode = ANALOG_DIALMODE_BOTH; + confp->chan.permdialmode = ANALOG_DIALMODE_BOTH; } } else if (!strcasecmp(v->name, "mwimonitor")) { confp->chan.mwimonitor_neon = 0; diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h index 43c89d93b4..051cc2313f 100644 --- a/channels/chan_dahdi.h +++ b/channels/chan_dahdi.h @@ -146,7 +146,7 @@ struct dahdi_pvt { * \note Set to a couple of nonzero values but it is only tested like a boolean. */ int radio; - int dialmode; /*!< Dialing Modes Allowed (Pulse/Tone) */ + enum analog_dialmode permdialmode; /*!< Dialing Modes Allowed (Pulse/Tone). Used for reading in chan_dahdi.conf only, the perm setting pair is in sig_analog */ int outsigmod; /*!< Outbound Signalling style (modifier) */ int oprmode; /*!< "Operator Services" mode */ struct dahdi_pvt *oprpeer; /*!< "Operator Services" peer tech_pvt ptr */ diff --git a/channels/sig_analog.c b/channels/sig_analog.c index b623f25019..7acd3f52e3 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -862,6 +862,16 @@ int analog_available(struct analog_pvt *p) static int analog_stop_callwait(struct analog_pvt *p) { p->callwaitcas = 0; + + /* There are 3 scenarios in which we need to reset the dialmode to permdialmode. + * 1) When placing a new outgoing call (either the first or a three-way) + * 2) When receiving a new incoming call + * 2A) If it's the first incoming call (not a call waiting), we reset + * in dahdi_hangup. + * 2B ) If it's a call waiting we've answered, either by swapping calls + * or having it ring through, we call analog_stop_callwait. That's this! */ + p->dialmode = p->permdialmode; + if (analog_callbacks.stop_callwait) { return analog_callbacks.stop_callwait(p->chan_pvt); } @@ -2372,6 +2382,29 @@ static void *__analog_ss_thread(void *data) */ p->hidecallerid = p->permhidecallerid; + /* Set the default dial mode. + * As with Caller ID, this is independent for each call, + * and changes made using the CHANNEL function are only temporary. + * This reset ensures temporary changes are discarded when a new call is originated. + * + * XXX There is a slight edge case in that because the dialmode is reset to permdialmode, + * assuming permdialmode=both, if a user disables dtmf during call 1, then flashes and + * starts call 2, this will set dialmode back to permcallmode on the private, + * allowing tone dialing to (correctly) work on call 2. + * If the user flashes back to call 1, however, tone dialing will again work on call 1. + * + * This problem does not exist with the other settings that involve a "permanent" + * and "transient" settings (e.g. hidecallerid, callwaiting), because hidecallerid + * only matters when originating a call, so as soon as it's been placed, it doesn't + * matter if it gets reset. For callwaiting, the setting is supposed to be common + * to the entire channel private (all subchannels), which is NOT the case with this setting. + * + * The correct and probably only fix for this edge case is to move dialmode out of the channel private + * (which is shared by all subchannels), and into the Asterisk channel structure. Just using an array for + * each chan_dahdi subchannel won't work because the indices change as calls flip around. + */ + p->dialmode = p->permdialmode; + /* Read the first digit */ timeout = analog_get_firstdigit_timeout(p); /* If starting a threeway call, never timeout on the first digit so someone diff --git a/channels/sig_analog.h b/channels/sig_analog.h index ae909ebba6..2d2e7e5ce0 100644 --- a/channels/sig_analog.h +++ b/channels/sig_analog.h @@ -301,6 +301,7 @@ struct analog_pvt { unsigned int permcallwaiting:1; /*!< TRUE if call waiting is enabled. (Configured option) */ unsigned int callwaitingdeluxe:1; /*!< TRUE if Call Waiting Deluxe options are available */ unsigned int permhidecallerid:1; /*!< Whether to hide our outgoing caller ID or not */ + enum analog_dialmode permdialmode; /*!< Which of pulse and/or tone dialing to support */ unsigned int pulse:1; unsigned int threewaycalling:1; unsigned int threewaysilenthold:1; /*!< Whether to time out a three-way dial tone to silence */