]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
chan_dahdi: Add DAHDI_CHANNEL function.
authorNaveen Albert <asterisk@phreaknet.org>
Thu, 11 Sep 2025 19:57:03 +0000 (15:57 -0400)
committerNaveen Albert <asterisk@phreaknet.org>
Mon, 22 Sep 2025 16:52:26 +0000 (16:52 +0000)
Add a dialplan function that can be used to get/set properties of
DAHDI channels (as opposed to Asterisk channels). This exposes
properties that were not previously available, allowing for certain
operations to now be performed in the dialplan.

Resolves: #1455

UserNote: The DAHDI_CHANNEL function allows for getting/setting
certain properties about DAHDI channels from the dialplan.

channels/chan_dahdi.c
channels/sig_analog.c

index a468650e364fb9c4f25141a41cd3e7524398309d..6ab7b22441a8a295a078a0e653bd554ccf0bf19c 100644 (file)
                        </example>
                </description>
        </function>
+       <function name="DAHDI_CHANNEL" language="en_US">
+               <synopsis>
+                       Set or get a property of a DAHDI channel.
+               </synopsis>
+               <syntax>
+                       <parameter name="property" required="true">
+                               <para>The property to set or get.</para>
+                               <enumlist>
+                                       <enum name="owner">
+                                               <para>R/O The name of the active channel on this DAHDI device.</para>
+                                       </enum>
+                                       <enum name="callwait">
+                                               <para>R/O The name of the call waiting channel on this DAHDI device.</para>
+                                       </enum>
+                                       <enum name="threeway">
+                                               <para>R/O The name of the three-way channel on this DAHDI device.</para>
+                                       </enum>
+                                       <enum name="polarity">
+                                               <para>R/W The polarity of the channel (0 or 1, idle or reverse can also be used for setting).</para>
+                                               <para>Only valid on FXO-signalled channels.</para>
+                                       </enum>
+                                       <enum name="dnd">
+                                               <para>R/W Whether Do Not Disturb is active.</para>
+                                               <para>Only valid on FXO-signalled channels.</para>
+                                       </enum>
+                                       <enum name="callforward">
+                                               <para>R/W The call forwarding target number.</para>
+                                               <para>Only valid on FXO-signalled channels.</para>
+                                       </enum>
+                                       <enum name="lastexten">
+                                               <para>R/W The last number dialed.</para>
+                                               <para>Only valid on FXO-signalled channels.</para>
+                                       </enum>
+                               </enumlist>
+                       </parameter>
+                       <parameter name="channel" required="false">
+                               <para>The DAHDI channel number.</para>
+                               <para>If not specified, the DAHDI channel number of the current channel
+                               will be used (in which case it must be a DAHDI channel).</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>The DAHDI_CHANNEL function can be used to set or get properties of DAHDI channels
+                       that are not specific to Asterisk channels.</para>
+                       <para>This function may also be called from non-DAHDI channels.</para>
+                       <example title="Set reverse polarity on current DAHDI channel">
+                       same => n,Set(DAHDI_CHANNEL(polarity)=1)
+                       same => n,NoOp(${DAHDI_CHANNEL(polarity)})
+                       </example>
+                       <example title="Set call forwarding target for channel 1">
+                       same => n,Set(DAHDI_CHANNEL(callforwardnumber,1)=5552368)
+                       same => n,NoOp(Calls now forwarding to ${DAHDI_CHANNEL(callforwardnumber,1)})
+                       </example>
+               </description>
+       </function>
        <info name="CHANNEL" language="en_US" tech="DAHDI">
                <enumlist>
                        <enum name="dahdi_channel">
@@ -2913,86 +2968,6 @@ static void my_hangup_polarityswitch(void *pvt)
        }
 }
 
-/*! \brief Return DAHDI pivot if channel is FXO signalled */
-static struct dahdi_pvt *fxo_pvt(struct ast_channel *chan)
-{
-       int res;
-       struct dahdi_params dahdip;
-       struct dahdi_pvt *pvt = NULL;
-
-       if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
-               ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan));
-               return NULL;
-       }
-
-       memset(&dahdip, 0, sizeof(dahdip));
-       res = ioctl(ast_channel_fd(chan, 0), DAHDI_GET_PARAMS, &dahdip);
-
-       if (res) {
-               ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", ast_channel_name(chan), strerror(errno));
-               return NULL;
-       }
-       if (!(dahdip.sigtype & __DAHDI_SIG_FXO)) {
-               ast_log(LOG_WARNING, "%s is not FXO signalled\n", ast_channel_name(chan));
-               return NULL;
-       }
-
-       pvt = ast_channel_tech_pvt(chan);
-       if (!dahdi_analog_lib_handles(pvt->sig, 0, 0)) {
-               ast_log(LOG_WARNING, "Channel signalling is not analog");
-               return NULL;
-       }
-
-       return pvt;
-}
-
-static int polarity_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)
-{
-       struct dahdi_pvt *pvt;
-
-       pvt = fxo_pvt(chan);
-       if (!pvt) {
-               return -1;
-       }
-
-       snprintf(buffer, buflen, "%d", pvt->polarity);
-
-       return 0;
-}
-
-static int polarity_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
-{
-       struct dahdi_pvt *pvt;
-       int polarity;
-
-       pvt = fxo_pvt(chan);
-       if (!pvt) {
-               return -1;
-       }
-
-       if (!strcasecmp(value, "idle")) {
-               polarity = POLARITY_IDLE;
-       } else if (!strcasecmp(value, "reverse")) {
-               polarity = POLARITY_REV;
-       } else {
-               polarity = atoi(value);
-       }
-
-       if (polarity != POLARITY_IDLE && polarity != POLARITY_REV) {
-               ast_log(LOG_WARNING, "Invalid polarity: '%s'\n", value);
-               return -1;
-       }
-
-       my_set_polarity(pvt, polarity);
-       return 0;
-}
-
-static struct ast_custom_function polarity_function = {
-       .name = "POLARITY",
-       .write = polarity_write,
-       .read = polarity_read,
-};
-
 static int my_start(void *pvt)
 {
        struct dahdi_pvt *p = pvt;
@@ -16905,6 +16880,206 @@ static struct dahdi_pvt *find_channel_from_str(const char *channel)
        return find_channel(chan_num);
 }
 
+static int print_subchannel(struct dahdi_pvt *p, int subchan, char *buffer, size_t len)
+{
+       if (!p->subs[subchan].owner) {
+               return -1;
+       }
+       ast_channel_lock(p->subs[subchan].owner);
+       snprintf(buffer, len, "%s", ast_channel_name(p->subs[subchan].owner));
+       ast_channel_unlock(p->subs[subchan].owner);
+       return 0;
+}
+
+#define REQUIRE_FXO_SIG() \
+       if (!(dahdip->sigtype & __DAHDI_SIG_FXO)) { \
+               ast_log(LOG_WARNING, "DAHDI channel %d is not FXO signalled\n", p->channel); \
+               return -1; \
+       }
+
+static int dahdichan_read_property(struct dahdi_pvt *p, struct dahdi_params *dahdip, const char *property, char *buffer, size_t len)
+{
+       struct analog_pvt *analog_p = p->sig_pvt;
+
+       /* R/O properties */
+       if (!strcasecmp(property, "owner")) {
+               return print_subchannel(p, SUB_REAL, buffer, len);
+       } else if (!strcasecmp(property, "callwait")) {
+               return print_subchannel(p, SUB_CALLWAIT, buffer, len);
+       } else if (!strcasecmp(property, "threeway")) {
+               return print_subchannel(p, SUB_THREEWAY, buffer, len);
+       /* R/W properties */
+       } else if (!strcasecmp(property, "polarity")) {
+               REQUIRE_FXO_SIG();
+               snprintf(buffer, len, "%d", p->polarity);
+       } else if (!strcasecmp(property, "dnd")) {
+               REQUIRE_FXO_SIG();
+               snprintf(buffer, len, "%d", analog_p->dnd);
+       } else if (!strcasecmp(property, "callforward")) {
+               REQUIRE_FXO_SIG();
+               snprintf(buffer, len, "%s", analog_p->call_forward);
+       } else if (!strcasecmp(property, "lastexten")) {
+               REQUIRE_FXO_SIG();
+               snprintf(buffer, len, "%s", analog_p->lastexten);
+       } else {
+               ast_log(LOG_ERROR, "Unknown DAHDI_CHANNEL property '%s'\n", property);
+               return -1;
+       }
+       return 0;
+}
+
+static int dahdichan_write_property(struct dahdi_pvt *p, struct dahdi_params *dahdip, const char *property, const char *value)
+{
+       struct analog_pvt *analog_p = p->sig_pvt;
+
+       /* We don't need to check ast_strlen_zero(value) because it's obviously not NULL.
+        * It may even be okay for it to be an empty string, but that's a per-setting thing. */
+
+       /* R/O properties */
+       if (!strcasecmp(property, "owner") || !strcasecmp(property, "callwait") || !strcasecmp(property, "threeway")) {
+               ast_log(LOG_ERROR, "DAHDI subchannel names are R/O\n");
+               return -1;
+       /* R/W properties */
+       } else if (!strcasecmp(property, "polarity")) {
+               int polarity = atoi(value);
+               REQUIRE_FXO_SIG();
+               if (polarity != POLARITY_IDLE && polarity != POLARITY_REV) {
+                       ast_log(LOG_ERROR, "Invalid polarity: '%s'\n", value);
+                       return -1;
+               }
+               my_set_polarity(p, polarity);
+       } else if (!strcasecmp(property, "dnd")) {
+               int dnd = atoi(value);
+               REQUIRE_FXO_SIG();
+               analog_dnd(analog_p, dnd ? 1 : 0);
+       } else if (!strcasecmp(property, "callforward")) {
+               REQUIRE_FXO_SIG();
+               if (strlen(value) >= sizeof(analog_p->call_forward) - 1) {
+                       ast_log(LOG_ERROR, "Provided call forwarding target '%s' is too long\n", value);
+               }
+               ast_copy_string(analog_p->call_forward, value, sizeof(analog_p->call_forward)); /* Could be empty to clear value */
+       } else if (!strcasecmp(property, "lastexten")) {
+               REQUIRE_FXO_SIG();
+               if (strlen(value) >= sizeof(analog_p->lastexten) - 1) {
+                       ast_log(LOG_ERROR, "Provided lastexten target '%s' is too long\n", value);
+               }
+               ast_copy_string(analog_p->lastexten, value, sizeof(analog_p->lastexten)); /* Could be empty to clear value */
+       } else {
+               ast_log(LOG_ERROR, "Unknown DAHDI_CHANNEL property '%s'\n", property);
+               return -1;
+       }
+       return 0;
+}
+#undef REQUIRE_FXO_SIG
+
+static int dahdichan_helper(struct ast_channel *chan, char *data, const char *value, char *buffer, size_t buflen)
+{
+       char *parse;
+       struct dahdi_pvt *pvt;
+       struct dahdi_params dahdip;
+       int res;
+       AST_DECLARE_APP_ARGS(args,
+               AST_APP_ARG(property);
+               AST_APP_ARG(dahdichan);
+       );
+
+       parse = ast_strdupa(data);
+       AST_STANDARD_APP_ARGS(args, parse);
+
+       if (buflen > 0) {
+               *buffer = '\0';
+       }
+
+       if (!ast_strlen_zero(args.dahdichan)) {
+               /* DAHDI channel number explicitly provided, find it. */
+               int channo = atoi(args.dahdichan);
+               pvt = find_channel(channo);
+               if (!pvt) {
+                       ast_log(LOG_ERROR, "DAHDI channel %d does not exist\n", channo);
+                       return -1;
+               }
+       } else {
+               /* No channel specified explicitly, so implicitly use the current channel, in which case it must be a DAHDI channel. */
+               if (!chan || !ast_channel_tech(chan) || strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
+                       ast_log(LOG_WARNING, "%s is not a DAHDI channel, and no DAHDI channel specified\n", ast_channel_name(chan));
+                       return -1;
+               }
+               pvt = ast_channel_tech_pvt(chan);
+       }
+
+       memset(&dahdip, 0, sizeof(dahdip));
+       if (ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &dahdip)) {
+               ast_log(LOG_WARNING, "Unable to get parameters of DAHDI channel %d: %s\n", pvt->channel, strerror(errno));
+               return -1;
+       }
+
+       /* We have the channel private to use. */
+       ast_mutex_lock(&pvt->lock);
+       if (value) {
+               res = dahdichan_write_property(pvt, &dahdip, args.property, value);
+       } else {
+               res = dahdichan_read_property(pvt, &dahdip, args.property, buffer, buflen);
+       }
+       ast_mutex_unlock(&pvt->lock);
+       return res;
+}
+
+static int dahdichan_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)
+{
+       return dahdichan_helper(chan, data, NULL, buffer, buflen);
+}
+
+static int dahdichan_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+       return dahdichan_helper(chan, data, value, NULL, 0);
+}
+
+static struct ast_custom_function dahdichan_function = {
+       .name = "DAHDI_CHANNEL",
+       .write = dahdichan_write,
+       .read = dahdichan_read,
+};
+
+/*! \todo The standalone POLARITY function can and should be deprecated/removed, since its functionality is now part of DAHDI_CHANNEL. */
+
+static int polarity_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)
+{
+       struct dahdi_params dahdip;
+       struct dahdi_pvt *pvt = ast_channel_tech_pvt(chan);
+       if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
+               ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan));
+               return -1;
+       }
+       memset(&dahdip, 0, sizeof(dahdip));
+       if (ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &dahdip)) {
+               ast_log(LOG_WARNING, "Unable to get parameters of DAHDI channel %d: %s\n", pvt->subs[SUB_REAL].dfd, strerror(errno));
+               return -1;
+       }
+       return dahdichan_read_property(ast_channel_tech_pvt(chan), &dahdip, "polarity", buffer, buflen);
+}
+
+static int polarity_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+       struct dahdi_params dahdip;
+       struct dahdi_pvt *pvt = ast_channel_tech_pvt(chan);
+       if (strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
+               ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", ast_channel_name(chan));
+               return -1;
+       }
+       memset(&dahdip, 0, sizeof(dahdip));
+       if (ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_GET_PARAMS, &dahdip)) {
+               ast_log(LOG_WARNING, "Unable to get parameters of DAHDI channel %d: %s\n", pvt->subs[SUB_REAL].dfd, strerror(errno));
+               return -1;
+       }
+       return dahdichan_write_property(ast_channel_tech_pvt(chan), &dahdip, "polarity", value);
+}
+
+static struct ast_custom_function polarity_function = {
+       .name = "POLARITY",
+       .write = polarity_write,
+       .read = polarity_read,
+};
+
 static int action_dahdidndon(struct mansession *s, const struct message *m)
 {
        struct dahdi_pvt *p;
@@ -18258,6 +18433,7 @@ static int __unload_module(void)
        ast_unregister_application(dahdi_accept_r2_call_app);
 #endif
 
+       ast_custom_function_unregister(&dahdichan_function);
        ast_custom_function_unregister(&polarity_function);
 
        ast_cli_unregister_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli));
@@ -20483,6 +20659,7 @@ static int load_module(void)
        ast_register_application_xml(dahdi_accept_r2_call_app, dahdi_accept_r2_call_exec);
 #endif
 
+       ast_custom_function_register(&dahdichan_function);
        ast_custom_function_register(&polarity_function);
 
        ast_cli_register_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli));
index 7eb222dba45a5aa870ab19ca186fb72d436d069c..580115c3db7a1278cce4cc2f3703a6fc755fae7a 100644 (file)
@@ -2452,7 +2452,9 @@ static void *__analog_ss_thread(void *data)
                                /* Last Number Redial */
                                if (!ast_strlen_zero(p->lastexten)) {
                                        ast_verb(4, "Redialing last number dialed on channel %d\n", p->channel);
+                                       analog_lock_private(p);
                                        ast_copy_string(exten, p->lastexten, sizeof(exten));
+                                       analog_unlock_private(p);
                                } else {
                                        ast_verb(3, "Last Number Redial not possible on channel %d (no saved number)\n", p->channel);
                                        res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
@@ -2465,8 +2467,10 @@ static void *__analog_ss_thread(void *data)
                                if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
                                        if (getforward) {
                                                /* Record this as the forwarding extension */
+                                               analog_lock_private(p);
                                                ast_copy_string(p->call_forward, exten, sizeof(p->call_forward));
-                                               ast_verb(3, "Setting call forward to '%s' on channel %d\n", p->call_forward, p->channel);
+                                               analog_unlock_private(p);
+                                               ast_verb(3, "Setting call forward to '%s' on channel %d\n", exten, p->channel);
                                                res = analog_play_tone(p, idx, ANALOG_TONE_DIALRECALL);
                                                if (res) {
                                                        break;