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
This commit is contained in:
Naveen Albert
2025-08-18 08:56:23 -04:00
committed by github-actions[bot]
parent 38cab43227
commit fe7be89a3c
4 changed files with 64 additions and 14 deletions

View File

@@ -277,11 +277,16 @@
</enum>
<enum name="dialmode">
<para>R/W Pulse and tone dialing mode of the channel.</para>
<para>Disabling tone dialing using this option will not disable the DSP used for DTMF detection.
<para>Disabling tone dialing using this option will not automatically disable the DSP used for DTMF detection.
To do that, also set the <literal>digitdetect</literal> option. If digit detection is disabled,
DTMF will not be detected, regardless of the <literal>dialmode</literal> setting.
The <literal>digitdetect</literal> setting has no impact on pulse dialing detection.</para>
<para>If set, overrides the setting in <literal>chan_dahdi.conf</literal> for that channel.</para>
<para>The <literal>dialmode</literal> setting applies to the DAHDI channel as a whole, but is reset for each call,
so modifications made using the <literal>CHANNEL</literal> function apply temporarily per-call.
The <literal>digitdetect</literal> setting applies to the entire DAHDI channel,
so any changes made to this setting will affect all calls concurrently on the same DAHDI channel.
<literal>digitdetect</literal> is reset once all calls on the line have cleared.</para>
<enumlist>
<enum name="both" />
<enum name="pulse" />
@@ -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;
@@ -18838,13 +18854,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;

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */