Commit 7d585413e9 for asterisk.org

commit 7d585413e95842f5102b82cb7cb7e97fd153824f
Author: Naveen Albert <asterisk@phreaknet.org>
Date:   Fri Sep 11 09:26:43 2026 -0400

    chan_dahdi: Avoid transitioning device state from INUSE to RINGING.

    By default, DAHDI relies on the Asterisk code to provide most device
    state, especially for analog lines, in which case it relies entirely
    on the core. When an incoming call arrives to a DAHDI line, it
    immediately asks the core to update device state for the channel.

    This can sometimes lead to unoptimal outcomes; for example, when
    ringing an FXS line (station), the device state is momentarily
    "INUSE" and then almost immediately transitions to "RINGING". However,
    this is incompatible with the state machines of some CPE. For example,
    Polycom IP phones' BLFs will ignore any "RINGING" received after an
    "INUSE". This makes sense because a line can't ring after being in use
    without first being made idle. (The reverse case, RINGING followed by
    INUSE, is allowed, since one can answer a ringing phone.)

    Thus, to ensure that BLFs accurately display the status of DAHDI
    channels, we need to avoid the momentary "INUSE" device state
    for ringing channels.

    Resolves: #2159

diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 84dc76bc87..e308ba21d0 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -9826,12 +9826,22 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
 	ast_jb_configure(tmp, &global_jbconf);

 	/* Set initial device state */
-	ast_copy_string(device_name, ast_channel_name(tmp), sizeof(device_name));
-	dashptr = strrchr(device_name, '-');
-	if (dashptr) {
-		*dashptr = '\0';
+	if (IS_FXO_SIG(i) && idx == SUB_REAL && state == AST_STATE_RESERVED && !startpbx && requestor) {
+		/* In the case that an analog line is receiving a call, temporarily suppress device state updates.
+		 * If we updated the device state right now, the calculated device state would be "INUSE",
+		 * even though almost instantly, it will become "RINGING". This is problematic,
+		 * because many IP phones ignore INUSE -> RINGING transitions since that's not technically possible.
+		 * To prevent such a bogus transition, if this line is about to ring and is not already in use, skip the update.
+		 * The next update will correctly start device state at RINGING so everything works as devices expect. */
+		ast_debug(3, "Suppressing immediate device state update to prevent INUSE -> RINGING transition\n");
+	} else {
+		ast_copy_string(device_name, ast_channel_name(tmp), sizeof(device_name));
+		dashptr = strrchr(device_name, '-');
+		if (dashptr) {
+			*dashptr = '\0';
+		}
+		ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, device_name);
 	}
-	ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, AST_DEVSTATE_CACHABLE, device_name);

 	for (v = i->vars ; v ; v = v->next)
 		pbx_builtin_setvar_helper(tmp, v->name, v->value);