[Asterisk-code-review] chan_dahdi: Add on-hook ADSI script download support. (asterisk[master])

N A asteriskteam at digium.com
Thu Dec 22 10:26:14 CST 2022


N A has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/19749 )


Change subject: chan_dahdi: Add on-hook ADSI script download support.
......................................................................

chan_dahdi: Add on-hook ADSI script download support.

GetCPEID alludes to storing the CPEID for on-hook ADSI
script downloads, except there has never been any code to do this.

This adds code to save the CPE, as well as makes it possible
to initiate automatic feature downloads to idle (on-hook) CPE
on DAHDI FXS channels. As part of this change, GetCPEID now
saves CPEID for DAHDI channels, and ADSIProg now supports an
option that permits automatic feature downloads for on-hook CPE.
An AMI action and CLI command are added that allow an administrator
to spawn downloads to CPE on DAHDI channels.

ASTERISK-30374 #close

Change-Id: I3ca4d0037291ee824eceb88c58e07884bbbd8575
---
M apps/app_adsiprog.c
M apps/app_getcpeid.c
M channels/chan_dahdi.c
M channels/sig_analog.c
M channels/sig_analog.h
A doc/CHANGES-staging/adsionhook.txt
M include/asterisk/callerid.h
M main/callerid.c
8 files changed, 465 insertions(+), 17 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/49/19749/1

diff --git a/apps/app_adsiprog.c b/apps/app_adsiprog.c
index 26fe16b..7781d27 100644
--- a/apps/app_adsiprog.c
+++ b/apps/app_adsiprog.c
@@ -52,24 +52,53 @@
 #include "asterisk/adsi.h"
 #include "asterisk/utils.h"
 #include "asterisk/lock.h"
+#include "asterisk/app.h"
 
 static const char app[] = "ADSIProg";
 
 /*** DOCUMENTATION
 	<application name="ADSIProg" language="en_US">
 		<synopsis>
-			Load Asterisk ADSI Scripts into phone
+			Loads an ADSI script into a Type III (ADSI-capable) CPE.
 		</synopsis>
 		<syntax>
 			<parameter name="script" required="false">
-				<para>adsi script to use. If not given uses the default script <filename>asterisk.adsi</filename></para>
+				<para>ADSI script to use. If not given uses the default script <filename>asterisk.adsi</filename></para>
+			</parameter>
+			<parameter name="options" required="false">
+				<optionlist>
+					<option name="a">
+						<para>Automatic script upload (on-hook programming capability).</para>
+						<para>To use this capability, you should use the <literal>DAHDIAutoADSIProg</literal>
+						AMI action or the <literal>dahdi send adsiprog</literal> CLI command to queue an
+						ADSI script for an automatic download to your CPE when it is idle.
+						The <literal>adsi</literal> extension in your CPE's incoming context should call
+						ADSIProg with the a option in order to transact the automatic script upload.
+						Your CPE will download the program and then go back on-hook automatically
+						once this has completed. No user intervention is required.</para>
+						<note><para>This is ONLY compatible with <literal>chan_dahdi</literal>.</para></note>
+					</option>
+				</optionlist>
 			</parameter>
 		</syntax>
 		<description>
-			<para>This application programs an ADSI Phone with the given script</para>
+			<para>This application programs an ADSI Phone with the given script.</para>
+			<para>This application can be used for off-hook programming of ADSI phones as well
+			as automatic feature downloads to idle (on-hook) CPE.</para>
+			<variablelist>
+				<variable name="ADSIPROGSTATUS">
+					<value name="FAILURE">
+						Failed to upload the ADSI script
+					</value>
+					<value name="SUCCESS">
+						Successfully uploaded the ADSI script
+					</value>
+				</variable>
+			</variablelist>
 		</description>
 		<see-also>
 			<ref type="application">GetCPEID</ref>
+			<ref type="manager">DAHDIAutoADSIProg</ref>
 			<ref type="filename">adsi.conf</ref>
 		</see-also>
 	</application>
@@ -1459,7 +1488,8 @@
 }
 #endif
 
-static int adsi_prog(struct ast_channel *chan, const char *script)
+/*! \param automatic 0 for off-hook downloads, 1 for automatic feature downloads */
+static int adsi_prog(struct ast_channel *chan, const char *script, int automatic)
 {
 	struct adsi_script *scr;
 	int x, bytes;
@@ -1468,9 +1498,13 @@
 	if (!(scr = compile_script(script)))
 		return -1;
 
-	/* Start an empty ADSI Session */
-	if (ast_adsi_load_session(chan, NULL, 0, 1) < 1)
-		return -1;
+	/* If this is an automatic feature download, begin the download straight away (SR-3034, 3.3). */
+	if (!automatic) {
+		/* Start an empty ADSI Session */
+		if (ast_adsi_load_session(chan, NULL, 0, 1) < 1) {
+			return -1;
+		}
+	}
 
 	/* Now begin the download attempt */
 	if (ast_adsi_begin_download(chan, scr->desc, scr->fdn, scr->sec, scr->ver)) {
@@ -1566,24 +1600,43 @@
 		return -1;
 	}
 	ast_free(scr);
-	ast_adsi_unload_session(chan);
+	if (!automatic) {
+		ast_adsi_unload_session(chan);
+	}
 	return 0;
 }
 
 static int adsi_exec(struct ast_channel *chan, const char *data)
 {
-	int res = 0;
+	char *parse;
+	int automatic = 0, res = 0;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(script);
+		AST_APP_ARG(options);
+	);
 
-	if (ast_strlen_zero(data))
-		data = "asterisk.adsi";
+	parse = ast_strdupa(S_OR(data, ""));
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (ast_strlen_zero(args.script)) {
+		args.script = "asterisk.adsi";
+	}
+	if (!ast_strlen_zero(args.options)) {
+		automatic = strchr(args.options, 'a') ? 1 : 0;
+		if (automatic && strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
+			ast_log(LOG_ERROR, "Automatic downloads only possible with DAHDI channels. Aborting!\n");
+			return -1;
+		}
+	}
 
 	if (!ast_adsi_available(chan)) {
 		ast_verb(3, "ADSI Unavailable on CPE.  Not bothering to try.\n");
 	} else {
 		ast_verb(3, "ADSI Available on CPE.  Attempting Upload.\n");
-		res = adsi_prog(chan, data);
+		res = adsi_prog(chan, args.script, automatic);
 	}
 
+	pbx_builtin_setvar_helper(chan, "ADSIPROGSTATUS", res ? "FAILURE" : "SUCCESS");
 	return res;
 }
 
diff --git a/apps/app_getcpeid.c b/apps/app_getcpeid.c
index abd9a82..4d4081a 100644
--- a/apps/app_getcpeid.c
+++ b/apps/app_getcpeid.c
@@ -32,6 +32,13 @@
 
 #include "asterisk.h"
 
+#ifdef HAVE_DAHDI
+#include <dahdi/user.h>
+#include "asterisk/frame.h"
+#include "../channels/sig_analog.h"
+#include "../channels/chan_dahdi.h"
+#endif
+
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -47,10 +54,27 @@
 		<syntax />
 		<description>
 			<para>Obtains and displays ADSI CPE ID and other information in order
-			to properly setup <filename>dahdi.conf</filename> for on-hook operations.</para>
+			to properly setup <literal>chan_dahdi</literal> for on-hook operations.</para>
+			<para>If this application is called on a DAHDI FXS channel (FXO signalled),
+			then the CPEID received will be stored on the channel for future automatic
+			feature downloads to the CPE. When a CPE accepts an automatic download,
+			it will execute the <literal>adsi</literal> extension in the DAHDI channel's
+			configured context, which should be as follows:
+			<literal>exten => adsi,1,ADSIProg(${ADSIAUTOSCRIPT},a)</literal></para>
+			<variablelist>
+				<variable name="CPEID">
+					<para>The CPE ID if successfully received.</para>
+				</variable>
+			</variablelist>
 		</description>
+		<see-also>
+			<ref type="application">ADSIProg</ref>
+			<ref type="manager">DAHDIAutoADSIProg</ref>
+			<ref type="filename">adsi.conf</ref>
+		</see-also>
 	</application>
  ***/
+
 static char *app = "GetCPEID";
 
 static int cpeid_setstatus(struct ast_channel *chan, char *stuff[], int voice)
@@ -100,11 +124,37 @@
 			}
 		}
 		if (res > -1) {
-			if (gotcpeid)
-				snprintf(data[1], 80, "CPEID: %02hhx:%02hhx:%02hhx:%02hhx",
-					cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
-			else
+			if (gotcpeid) {
+				char cpeidstr[12]; /* XX:XX:XX:XX */
+				snprintf(cpeidstr, sizeof(cpeidstr), "%02hhx:%02hhx:%02hhx:%02hhx", cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
+				snprintf(data[1], 80, "CPEID: %s", cpeidstr);
+				pbx_builtin_setvar_helper(chan, "CPEID", cpeidstr);
+#ifdef HAVE_DAHDI
+				if (!strcasecmp(ast_channel_tech(chan)->type, "DAHDI")) {
+					struct dahdi_pvt *pvt;
+					struct analog_pvt *analog_p;
+					struct dahdi_params dahdip;
+
+					memset(&dahdip, 0, sizeof(dahdip));
+					if (ioctl(ast_channel_fd(chan, 0), DAHDI_GET_PARAMS, &dahdip)) {
+						ast_log(LOG_WARNING, "Unable to get parameters of %s: %s\n", ast_channel_name(chan), strerror(errno));
+						return -1;
+					}
+					if (dahdip.sigtype & __DAHDI_SIG_FXO) {
+						/* If we're a DAHDI channel, save the CPE ID for on-hook programming use. */
+						pvt = ast_channel_tech_pvt(chan);
+						if (pvt->adsi) {
+							analog_p = pvt->sig_pvt;
+							memcpy(analog_p->cpeid, cpeid, sizeof(analog_p->cpeid));
+							ast_debug(1, "Saved CPE ID for DAHDI channel %d: '%02hhx:%02hhx:%02hhx:%02hhx'\n",
+								pvt->channel, cpeid[0], cpeid[1], cpeid[2], cpeid[3]);
+						}
+					}
+				}
+#endif
+			} else {
 				strcpy(data[1], "CPEID Unknown");
+			}
 			if (gotgeometry)
 				snprintf(data[2], 80, "Geom: %dx%d, %d buttons", width, height, buttons);
 			else
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 5607eb0..7f7886b 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -393,6 +393,48 @@
 			<note><para>Feature only supported by analog channels.</para></note>
 		</description>
 	</manager>
+	<manager name="DAHDIAutoADSIProg" language="en_US">
+		<synopsis>
+			Initiate an automatic ADSI feature download to an idle (on-hook) CPE
+		</synopsis>
+		<syntax>
+			<parameter name="DAHDIChannel" required="true">
+				<para>DAHDI channel number on which to initiate download.</para>
+				<para>You must have a Type III (ADSI-capable) CPE connected on this channel.</para>
+			</parameter>
+			<parameter name="Script" required="false">
+				<para>ADSI script to download.</para>
+				<para>The default, if not specified, is <literal>asterisk.adsi</literal>.</para>
+			</parameter>
+			<parameter name="CPEID" required="false">
+				<para>The CPEID to use for the on-hook download attempt.</para>
+				<para>If not specified, the last CPEID retrieved from the GetCPEID() application
+				will be used.</para>
+				<para>If GetCPEID() has not been called on this channel while Asterisk is running,
+				this argument must be provided or the action will fail. The CPEID must match the
+				CPEID of a CPE on the channel or programming will fail.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Equivalent to the CLI command "dahdi send adsiprog <variable>channel</variable> <variable>script</variable> <variable>CPEID</variable>".</para>
+			<para>If this action is successful, that does not necessarily mean the feature download was successful. It only means that Asterisk will
+			attempt to begin the download. When the CPE accepts the download, it will go off-hook and execute the <literal>adsi</literal> extension
+			in the dialplan (in the phone's incoming context). You should call <literal>exten => adsi,1,ADSIProg(${ADSIAUTOSCRIPT},a)</literal>
+			to actually send the script to the CPE. The CPE will automatically go back on-hook once the download has completed.</para>
+			<para>The CPEID of the CPE must be known in advance in order to use this capability.
+			This can be done by calling GetCPEID() in the dialplan, or by manually providing
+			the CPEID to this action in the appropriate parameter.</para>
+			<para>Asterisk will only attempt to initiate an automatic feature download if the channel has been idle for at least 45 seconds.</para>
+			<para>Additionally, automatic feature downloads are only supported for scripts that CPE have previously requested during an off-hook session.
+			Attempts to upload completely new scripts that have not been previously seen by the phone will be rejected by the CPE.</para>
+			<para>CPE may need to go off-hook between the initial download of a script and a future attempt to automatically update it.</para>
+			<note><para>Feature only supported by analog channels.</para></note>
+		</description>
+		<see-also>
+			<ref type="application">ADSIProg</ref>
+			<ref type="application">GetCPEID</ref>
+		</see-also>
+	</manager>
 	<manager name="DAHDIShowChannels" language="en_US">
 		<synopsis>
 			Show status of DAHDI channels.
@@ -2766,6 +2808,110 @@
 	return pvt;
 }
 
+#define adsi_err(fd, s, m, ...) \
+	if (s && m) astman_send_error(s, m, "Failed to queue ADSI on-hook program"); \
+	else ast_cli(fd, __VA_ARGS__);
+
+static int adsi_onhook_init(struct dahdi_pvt *p, const char *cpeid, const char *script, int fd, struct mansession *s, const struct message *m)
+{
+	int x;
+	unsigned char cpeid_bytes[4];
+	struct analog_pvt *analog_p = p->sig_pvt;
+
+	/* Only FXS ports, please. */
+	if (p->sig != SIG_FXOLS && p->sig != SIG_FXOGS && p->sig != SIG_FXOKS) {
+		adsi_err(fd, s, m, "DAHDI channel %d is not an FXS channel\n", p->channel);
+		return -1;
+	}
+
+	memset(cpeid_bytes, 0, sizeof(cpeid_bytes));
+
+	/* We must know the CPE's CPE ID, since the CPE will compare it for a match.
+	 * If we don't already know it and the user didn't provide one, abort. */
+	if (ast_strlen_zero(cpeid)) {
+		/* Try to look up the CPEID on the pivot, from a previous invocation of GetCPEID(). */
+		memcpy(cpeid_bytes, analog_p->cpeid, sizeof(cpeid_bytes));
+		/* If we don't have a CPEID, we must abort, because without it, the on-hook alerting will fail. */
+		ast_debug(1, "Cached CPEID for channel %d: '%02hhx:%02hhx:%02hhx:%02hhx'\n",
+			p->channel, cpeid_bytes[0], cpeid_bytes[1], cpeid_bytes[2], cpeid_bytes[3]);
+		if (cpeid_bytes[0] == 0x00) {
+			adsi_err(fd, s, m, "No CPEID provided and none found in cache for DAHDI channel %d\n", p->channel);
+			return -1;
+		}
+	} else {
+		/* We are expecting a string like 20:06:cc:c3 (this is what GetCPEID() outputs to the CLI)
+		 * Need to convert it back into bytes. */
+		if (sscanf(cpeid, "%02hhx:%02hhx:%02hhx:%02hhx", &cpeid_bytes[0], &cpeid_bytes[1], &cpeid_bytes[2], &cpeid_bytes[3]) != 4) {
+			adsi_err(fd, s, m, "Invalid CPE ID: %s\n", cpeid);
+			return -1;
+		}
+	}
+
+	ast_debug(1, "Using CPEID '%02hhx:%02hhx:%02hhx:%02hhx'\n", cpeid_bytes[0], cpeid_bytes[1], cpeid_bytes[2], cpeid_bytes[3]);
+
+	/* Only allow on-hook downloads on a quiescent FXS port. */
+	if (analog_p->fxsoffhookstate || p->owner || analog_p->subs[SUB_REAL].owner) {
+		adsi_err(fd, s, m, "DAHDI channel %d is not currently idle\n", p->channel);
+		return -1;
+	}
+
+	while (time(NULL) < analog_p->onhooktime + 45) {
+		/* CPE seem to ignore any on-hook programming attempts if they were in use
+		 * as recently as 45 seconds ago (probably because it's highly likely
+		 * during this time the phone could transition to non-idle... but why
+		 * can't it just rely on us to make the right call? Ugh.)
+		 * So, we may as well bail now. */
+		adsi_err(fd, s, m, "DAHDI channel %d was in use too recently (%d s ago)\n", p->channel, time(NULL) - analog_p->onhooktime);
+		return -1;
+	}
+
+	if (p->cidspill) {
+		adsi_err(fd, s, m, "CID spill already active on DAHDI channel %d\n", p->channel);
+		return -1;
+	}
+
+	/* Okay, we should be able to actually send the spill. */
+	p->mwisend_data.mwisend_current = MWI_SEND_SPILL;
+	p->mwisendactive = 1;
+
+	p->cidspill = ast_calloc(1, MAX_CALLERID_SIZE);
+	if (!p->cidspill) {
+		p->mwisendactive = 0;
+		return -1;
+	}
+
+	x = DAHDI_FLUSH_BOTH;
+	ioctl(p->subs[SUB_REAL].dfd, DAHDI_FLUSH, &x);
+	x = 3000;
+	ioctl(p->subs[SUB_REAL].dfd, DAHDI_ONHOOKTRANSFER, &x);
+
+	/* ADSI Automatic Feature Download (SR-2461 2.2.2.2)
+	 * ADSI On-Hook Alerting (SR-2461 3.3.3.1, SR-3004 Sec 7, SR-3034 3.3, 2.5.5, 4.2.2)
+	 *
+	 * This is a completely unsolicited spill, not preceded by any ringing (so basically like MWI). */
+
+	/* Generate the FSK spill. */
+	p->cidlen = ast_callerid_adsi_alert_generate(p->cidspill, cpeid_bytes, AST_LAW(p));
+	p->cidpos = 0;
+
+	/* At this point, the spill has been queued, and mwi_send_process_buffer will actually
+	 * do the work of ensuring the spill gets sent. Thank you very much!
+	 * Yes, I know this isn't MWI, but it's a spill of the exact same type that needs to be sent,
+	 * so we can let it do our work for us since it's just what we need. */
+
+	/* The CPE will go off-hook once it gets the spill.
+	 * At the point, we can simply call ADSIProg(), since that will send the CAS, receive the ACK, and continue.
+	 * Basically, our job here is just to get the phone to go off-hook on its own.
+	 * Then it can talk to the ADSI programming application, with the script to download as an argument
+	 * Afterwards, it will hang up again on its own. */
+
+	/* If the CPE goes off-hook within the next 2 seconds, consider this a positive response to the FSK spill.
+	 * So, keep track of when we sent the spill. */
+	gettimeofday(&analog_p->adsionhooktime, NULL);
+	ast_copy_string(analog_p->adsionhookscript, S_OR(script, ""), sizeof(analog_p->adsionhookscript));
+	return 0;
+}
+
 static int polarity_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)
 {
 	struct dahdi_pvt *pvt;
@@ -16377,6 +16523,50 @@
 	return CLI_SUCCESS;
 }
 
+static char *dahdi_send_adsionhookprog(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	const char *script, *cpeid;
+	struct dahdi_pvt *dahdi_chan = NULL;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "dahdi send adsiprog";
+		e->usage =
+			"Usage: dahdi send adsiprog <chan#> <script> [<cpeid>]\n"
+			"	Initiates an automatic ADSI program download\n"
+			"	to a CPE that is currently on-hook.\n"
+			"	<chan num> is the channel number\n"
+			" 	<cpeid> CPEID override\n"
+			;
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 5 && a->argc != 6) {
+		return CLI_SHOWUSAGE;
+	}
+
+	script = a->argv[4];
+	cpeid = a->argc == 6 ? a->argv[5] : NULL;
+
+	dahdi_chan = find_channel_from_str(a->argv[3]);
+	if (!dahdi_chan) {
+		ast_cli(a->fd, "Unable to find given channel %s\n", a->argv[3]);
+		return CLI_FAILURE;
+	} else if (!dahdi_chan->adsi) {
+		ast_cli(a->fd, "ADSI not enabled for channel %d\n", dahdi_chan->channel);
+		return CLI_FAILURE;
+	}
+
+	if (adsi_onhook_init(dahdi_chan, cpeid, script, a->fd, NULL, NULL)) {
+		return CLI_FAILURE;
+	}
+
+	ast_cli(a->fd, "ADSI Automatic Download initiated\n");
+	return CLI_SUCCESS;
+}
+
 static struct ast_cli_entry dahdi_cli[] = {
 	AST_CLI_DEFINE(handle_dahdi_show_cadences, "List cadences"),
 	AST_CLI_DEFINE(dahdi_show_channels, "Show active DAHDI channels"),
@@ -16389,6 +16579,7 @@
 	AST_CLI_DEFINE(dahdi_set_hwgain, "Set hardware gain on a channel"),
 	AST_CLI_DEFINE(dahdi_set_swgain, "Set software gain on a channel"),
 	AST_CLI_DEFINE(dahdi_set_dnd, "Sets/resets DND (Do Not Disturb) mode on a channel"),
+	AST_CLI_DEFINE(dahdi_send_adsionhookprog, "Initiate an automatic ADSI script download on a channel"),
 };
 
 #define TRANSFER	0
@@ -16561,6 +16752,36 @@
 	return 0;
 }
 
+static int action_adsionhookprog(struct mansession *s, const struct message *m)
+{
+	struct dahdi_pvt *p;
+	const char *cpeid, *script, *channel;
+
+	channel = astman_get_header(m, "DAHDIChannel");
+	cpeid = astman_get_header(m, "CPEID"); /* Potentially okay if NULL */
+	script = astman_get_header(m, "Script"); /* Potentially okay if NULL */
+
+	if (ast_strlen_zero(channel)) {
+		astman_send_error(s, m, "No channel specified");
+		return 0;
+	}
+
+	p = find_channel_from_str(channel);
+	if (!p) {
+		astman_send_error(s, m, "No such channel");
+		return 0;
+	} else if (!p->adsi) {
+		astman_send_error(s, m, "ADSI not enabled for channel %d", p->channel);
+		return 0;
+	}
+	if (adsi_onhook_init(p, cpeid, script, -1, s, m)) {
+		return 0;
+	}
+	/* It won't happen immediately, just return success. */
+	astman_send_ack(s, m, "ADSI Automatic Download initiated");
+	return 0;
+}
+
 static int action_dahdishowchannels(struct mansession *s, const struct message *m)
 {
 	struct dahdi_pvt *tmp = NULL;
@@ -17738,6 +17959,7 @@
 	ast_manager_unregister("DAHDITransfer");
 	ast_manager_unregister("DAHDIDNDoff");
 	ast_manager_unregister("DAHDIDNDon");
+	ast_manager_unregister("DAHDIAutoADSIProg");
 	ast_manager_unregister("DAHDIShowChannels");
 	ast_manager_unregister("DAHDIRestart");
 #if defined(HAVE_PRI)
@@ -19929,6 +20151,7 @@
 	ast_manager_register_xml("DAHDIDialOffhook", 0, action_dahdidialoffhook);
 	ast_manager_register_xml("DAHDIDNDon", 0, action_dahdidndon);
 	ast_manager_register_xml("DAHDIDNDoff", 0, action_dahdidndoff);
+	ast_manager_register_xml("DAHDIAutoADSIProg", 0, action_adsionhookprog);
 	ast_manager_register_xml("DAHDIShowChannels", 0, action_dahdishowchannels);
 	ast_manager_register_xml("DAHDIRestart", 0, action_dahdirestart);
 #if defined(HAVE_PRI)
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index b694a96..62dd116 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -2133,6 +2133,36 @@
 	case ANALOG_SIG_FXOLS:
 	case ANALOG_SIG_FXOGS:
 	case ANALOG_SIG_FXOKS:
+		if (p->subs[ANALOG_SUB_REAL].owner) {
+			/* Check if this is CPE going off hook to download an incoming ADSI script.
+			 * If the CPE went off-hook in response, then program it.
+			 * (It doesn't matter whether immediate=yes or not).
+			 * Note we don't have access to the DAHDI pivot, so we can't check p->adsi
+			 * (that's on the DAHDI pivot, not the analog sig pivot)
+			 */
+			unsigned int ms_since_alert = ast_tvdiff_ms(ast_tvnow(), p->adsionhooktime);
+			memset(&p->adsionhooktime, 0, sizeof(p->adsionhooktime)); /* Reset */
+			/* CPE must go off-hook within 500 ms.
+			 * However, there is a delay between when we queued the FSK spill,
+			 * and it actually gets sent and received. So tack on an extra 2000ms for that. */
+			if (ms_since_alert < 2500) {
+				ast_verb(3, "Automatic ADSI download of %s initiated on %s\n", p->adsionhookscript, ast_channel_name(p->subs[ANALOG_SUB_REAL].owner));
+				ast_setstate(chan, AST_STATE_UP);
+				analog_set_echocanceller(p, 1);
+				/* We just want to execute ADSIProg(p->adsionhookscript), really, but we have to start the PBX,
+				 * so send the channel to the "adsi" extension and set ADSIAUTOSCRIPT so it knows what to do. */
+				ast_channel_exten_set(p->subs[ANALOG_SUB_REAL].owner, "adsi");
+				pbx_builtin_setvar_helper(p->subs[ANALOG_SUB_REAL].owner, "ADSIAUTOSCRIPT", p->adsionhookscript);
+				res = ast_pbx_run(chan);
+				if (res) {
+					ast_log(LOG_WARNING, "PBX exited non-zero\n");
+					res = analog_play_tone(p, idx, ANALOG_TONE_CONGESTION);
+				}
+				goto quit;
+			} else {
+				ast_debug(1, "Elapsed time since ADSI on-hook spill is %u\n", ms_since_alert);
+			}
+		}
 		/* 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 7e9acda..aa186d9 100644
--- a/channels/sig_analog.h
+++ b/channels/sig_analog.h
@@ -353,6 +353,11 @@
 	char *origcid_name;				/*!< malloced original callerid */
 	char call_forward[AST_MAX_EXTENSION];
 
+	/* ADSI on-hook alerting */
+	unsigned char cpeid[4];
+	char adsionhookscript[64];
+	struct timeval adsionhooktime;
+
 	/* Ast channel to pass to __ss_analog_thread */
 	struct ast_channel *ss_astchan;
 
diff --git a/doc/CHANGES-staging/adsionhook.txt b/doc/CHANGES-staging/adsionhook.txt
new file mode 100644
index 0000000..82622d2
--- /dev/null
+++ b/doc/CHANGES-staging/adsionhook.txt
@@ -0,0 +1,4 @@
+Subject: chan_dahdi
+
+ADSI on-hook downloads (automatic feature downloads to
+idle ADSI CPE) are now supported.
diff --git a/include/asterisk/callerid.h b/include/asterisk/callerid.h
index 16a3df1..a85487e 100644
--- a/include/asterisk/callerid.h
+++ b/include/asterisk/callerid.h
@@ -236,6 +236,14 @@
 int ast_callerid_vmwi_generate(unsigned char *buf, int active, int type, struct ast_format *codec, const char *name,
 	const char *number, int flags);
 
+/*!
+ * \brief Generate an ADSI on-hook alerting signal for automatic feature download offer
+ * \param buf
+ * \param cpeid The CPEID of the CPE targeted for the download
+ * \param codec
+ */
+int ast_callerid_adsi_alert_generate(unsigned char *buf, unsigned char *cpeid, struct ast_format *codec);
+
 /*! \brief Generate Caller-ID spill but in a format suitable for Call Waiting(tm)'s Caller*ID(tm)
  * \see ast_callerid_generate() for other details
  */
diff --git a/main/callerid.c b/main/callerid.c
index c6c29a0..4c970c8 100644
--- a/main/callerid.c
+++ b/main/callerid.c
@@ -938,6 +938,59 @@
 	return bytes;
 }
 
+int ast_callerid_adsi_alert_generate(unsigned char *buf, unsigned char *cpeid, struct ast_format *codec)
+{
+	char msg[256];
+	int len = 0;
+	int sum;
+	int x;
+	int bytes = 0;
+	float cr = 1.0;
+	float ci = 0.0;
+	float scont = 0.0;
+
+	/* MDMF */
+	msg[len++] = 0x80; /* SR-3363 3.3 */
+	/* Length is 6 */
+	msg[len++] = 6;
+	/* "CPE ID Parameter" */
+	msg[len++] = 0xc;
+	/* Length of IE is 4 */
+	msg[len++] = 4;
+	/* CPE ID: 4 bytes */
+	msg[len++] = cpeid[0];
+	msg[len++] = cpeid[1];
+	msg[len++] = cpeid[2];
+	msg[len++] = cpeid[3];
+
+	sum = 0;
+	for (x = 0; x < len; x++) {
+		sum += msg[x];
+	}
+	sum = (256 - (sum & 255));
+	msg[len++] = sum;
+	/* Wait a half a second */
+	for (x = 0; x < 4000; x++) {
+		PUT_BYTE(0x7f);
+	}
+	/* Transmit 30 0x55's (looks like a square wave) for channel seizure */
+	for (x = 0; x < 30; x++) {
+		PUT_CLID(0x55);
+	}
+	/* Send 170ms of callerid marks */
+	for (x = 0; x < 170; x++) {
+		PUT_CLID_MARKMS;
+	}
+	for (x = 0; x < len; x++) {
+		PUT_CLID(msg[x]);
+	}
+	/* Send 50 more ms of marks */
+	for (x = 0; x < 50; x++) {
+		PUT_CLID_MARKMS;
+	}
+	return bytes;
+}
+
 int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, struct ast_format *codec)
 {
 	return callerid_full_generate(buf, number, name, NULL, -1, flags, CID_TYPE_MDMF, callwaiting, codec);

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/19749
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: I3ca4d0037291ee824eceb88c58e07884bbbd8575
Gerrit-Change-Number: 19749
Gerrit-PatchSet: 1
Gerrit-Owner: N A <asterisk at phreaknet.org>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20221222/23b359d6/attachment-0001.html>


More information about the asterisk-code-review mailing list