2 * Asterisk -- A telephony toolkit for Linux.
6 * Copyright (C) 2001, Linux Support Services, Inc.
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License.
13 * Includes code and algorithms from the Zapata library.
24 #include <asterisk/ulaw.h>
25 #include <asterisk/alaw.h>
26 #include <asterisk/callerid.h>
27 #include <asterisk/logger.h>
28 #include <asterisk/fskmodem.h>
29 #include <asterisk/channel.h>
30 #include <asterisk/adsi.h>
31 #include <asterisk/module.h>
32 #include <asterisk/config.h>
33 #include <asterisk/file.h>
35 #define DEFAULT_ADSI_MAX_RETRIES 3
37 #define ADSI_MAX_INTRO 20
38 #define ADSI_MAX_SPEED_DIAL 6
40 #define ADSI_FLAG_DATAMODE (1 << 8)
42 static int maxretries = DEFAULT_ADSI_MAX_RETRIES;
44 /* Asterisk ADSI button definitions */
45 #define ADSI_SPEED_DIAL 10 /* 10-15 are reserved for speed dial */
47 static char intro[ADSI_MAX_INTRO][20];
48 static int aligns[ADSI_MAX_INTRO];
50 static char speeddial[ADSI_MAX_SPEED_DIAL][3][20];
52 static int alignment = 0;
54 static int adsi_generate(unsigned char *buf, int msgtype, char *msg, int msglen, int msgnum, int last, int codec)
59 /* Initial carrier (imaginary) */
67 /* If first message, Send 150ms of MARK's */
69 for (x=0;x<150;x++) /* was 150 */
72 /* Put message type */
76 /* Put message length (plus one for the message number) */
80 /* Put message number */
84 /* Put actual message */
85 for (x=0;x<msglen;x++) {
90 /* Put 2's compliment of sum */
91 PUT_CLID(256-(sum & 0xff));
95 /* Put trailing marks */
104 static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int len, int *remainder)
106 /* Sends carefully on a full duplex channel by using reading for
108 struct ast_frame *inf, outf;
111 /* Zero out our outgoing frame */
112 memset(&outf, 0, sizeof(outf));
114 if (remainder && *remainder) {
117 /* Send remainder if provided */
118 if (amt > *remainder)
121 *remainder = *remainder - amt;
122 outf.frametype = AST_FRAME_VOICE;
123 outf.subclass = AST_FORMAT_ULAW;
127 if (ast_write(chan, &outf)) {
128 ast_log(LOG_WARNING, "Failed to carefully write frame\n");
131 /* Update pointers and lengths */
138 /* If we don't get anything at all back in a second, forget
140 if (ast_waitfor(chan, 1000) < 1)
142 inf = ast_read(chan);
146 if (inf->frametype == AST_FRAME_VOICE) {
147 /* Read a voice frame */
148 if (inf->subclass != AST_FORMAT_ULAW) {
149 ast_log(LOG_WARNING, "Channel not in ulaw?\n");
152 /* Send no more than they sent us */
153 if (amt > inf->datalen)
156 *remainder = inf->datalen - amt;
157 outf.frametype = AST_FRAME_VOICE;
158 outf.subclass = AST_FORMAT_ULAW;
162 if (ast_write(chan, &outf)) {
163 ast_log(LOG_WARNING, "Failed to carefully write frame\n");
166 /* Update pointers and lengths */
175 static int __adsi_transmit_messages(struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype)
177 /* msglen must be no more than 256 bits, each */
178 unsigned char buf[24000 * 5];
186 /* Wait up to 500 ms for initial ACK */
192 if (chan->adsicpe == AST_ADSI_UNAVAILABLE) {
193 /* Don't bother if we know they don't support ADSI */
198 while(retries < maxretries) {
199 if (!(chan->adsicpe & ADSI_FLAG_DATAMODE)) {
200 /* Generate CAS (no SAS) */
201 ast_gen_cas(buf, 0, 680, AST_FORMAT_ULAW);
204 if (adsi_careful_send(chan, buf, 680, NULL)) {
205 ast_log(LOG_WARNING, "Unable to send CAS\n");
207 /* Wait For DTMF result */
210 if (((res = ast_waitfor(chan, waittime)) < 1)) {
211 /* Didn't get back DTMF A in time */
212 ast_log(LOG_DEBUG, "No ADSI CPE detected (%d)\n", res);
214 chan->adsicpe = AST_ADSI_UNAVAILABLE;
221 ast_log(LOG_DEBUG, "Hangup in ADSI\n");
224 if (f->frametype == AST_FRAME_DTMF) {
225 if (f->subclass == 'A') {
226 /* Okay, this is an ADSI CPE. Note this for future reference, too */
228 chan->adsicpe = AST_ADSI_AVAILABLE;
231 if (f->subclass == 'D') {
232 ast_log(LOG_DEBUG, "Off-hook capable CPE only, not ADSI\n");
234 ast_log(LOG_WARNING, "Unknown ADSI response '%c'\n", f->subclass);
236 chan->adsicpe = AST_ADSI_UNAVAILABLE;
244 ast_log(LOG_DEBUG, "ADSI Compatible CPE Detected\n");
246 ast_log(LOG_DEBUG, "Already in data mode\n");
251 def= ast_channel_defer_dtmf(chan);
253 while((x < 6) && msg[x]) {
254 res = adsi_generate(buf + pos, msgtype[x], msg[x], msglen[x], x+1 - start, (x == 5) || !msg[x+1], AST_FORMAT_ULAW);
256 ast_log(LOG_WARNING, "Failed to generate ADSI message %d on channel %s\n", x + 1, chan->name);
259 ast_log(LOG_DEBUG, "Message %d, of %d input bytes, %d output bytes\n",
260 x + 1, msglen[x], res);
267 res = adsi_careful_send(chan, buf, pos, &rem);
269 ast_channel_undefer_dtmf(chan);
273 ast_log(LOG_DEBUG, "Sent total spill of %d bytes\n", pos);
275 memset(ack, 0, sizeof(ack));
276 /* Get real result */
277 res = ast_readstring(chan, ack, 2, 1000, 1000, "");
278 /* Check for hangup */
282 ast_log(LOG_DEBUG, "Acked up to message %d\n", atoi(ack + 1));
283 start += atoi(ack + 1);
288 ast_log(LOG_DEBUG, "Retransmitting (%d), from %d\n", retries, start + 1);
292 ast_log(LOG_WARNING, "Unexpected response to ack: %s (retry %d)\n", ack, retries);
295 if (retries >= maxretries) {
296 ast_log(LOG_WARNING, "Maximum ADSI Retries (%d) exceeded\n", maxretries);
304 int adsi_begin_download(struct ast_channel *chan, char *service, char *fdn, char *sec, int version)
307 unsigned char buf[256];
310 /* Setup the resident soft key stuff, a piece at a time */
311 /* Upload what scripts we can for voicemail ahead of time */
312 bytes += adsi_download_connect(buf + bytes, service, fdn, sec, version);
313 if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD))
315 if (ast_readstring(chan, ack, 1, 10000, 10000, ""))
319 ast_log(LOG_DEBUG, "Download was denied by CPE\n");
323 int adsi_end_download(struct ast_channel *chan)
326 unsigned char buf[256];
328 /* Setup the resident soft key stuff, a piece at a time */
329 /* Upload what scripts we can for voicemail ahead of time */
330 bytes += adsi_download_disconnect(buf + bytes);
331 if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD))
336 int adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
338 unsigned char *msgs[5] = { NULL, NULL, NULL, NULL, NULL };
344 int writeformat, readformat;
346 writeformat = chan->writeformat;
347 readformat = chan->readformat;
349 newdatamode = chan->adsicpe & ADSI_FLAG_DATAMODE;
351 for (x=0;x<msglen;x+=(msg[x+1]+2)) {
352 if (msg[x] == ADSI_SWITCH_TO_DATA)
353 newdatamode = ADSI_FLAG_DATAMODE;
355 if (msg[x] == ADSI_SWITCH_TO_VOICE)
361 msgtypes[0] = msgtype;
364 ast_log(LOG_WARNING, "Can't send ADSI message of %d bytes, too large\n", msglen);
368 ast_stopstream(chan);
370 if (ast_set_write_format(chan, AST_FORMAT_ULAW)) {
371 ast_log(LOG_WARNING, "Unable to set write format to ULAW\n");
375 if (ast_set_read_format(chan, AST_FORMAT_ULAW)) {
376 ast_log(LOG_WARNING, "Unable to set read format to ULAW\n");
378 if (ast_set_write_format(chan, writeformat))
379 ast_log(LOG_WARNING, "Unable to restore write format to %d\n", writeformat);
383 res = __adsi_transmit_messages(chan, msgs, msglens, msgtypes);
385 chan->adsicpe = (chan->adsicpe & ~ADSI_FLAG_DATAMODE) | newdatamode;
388 ast_set_write_format(chan, writeformat);
390 ast_set_read_format(chan, readformat);
393 res = ast_safe_sleep(chan, 100 );
397 static inline int ccopy(unsigned char *dst, unsigned char *src, int max)
400 /* Carefully copy the requested data */
401 while ((x < max) && src[x] && (src[x] != 0xff)) {
408 int adsi_load_soft_key(unsigned char *buf, int key, unsigned char *llabel, unsigned char *slabel, unsigned char *ret, int data)
412 /* Abort if invalid key specified */
413 if ((key < 2) || (key > 33))
415 buf[bytes++] = ADSI_LOAD_SOFTKEY;
416 /* Reserve for length */
421 /* Carefully copy long label */
422 bytes += ccopy(buf + bytes, llabel, 18);
424 /* Place delimiter */
428 bytes += ccopy(buf + bytes, slabel, 7);
431 /* If specified, copy return string */
433 /* Place delimiter */
436 buf[bytes++] = ADSI_SWITCH_TO_DATA2;
437 /* Carefully copy return string */
438 bytes += ccopy(buf + bytes, ret, 20);
441 /* Replace parameter length */
447 int adsi_connect_session(unsigned char *buf, unsigned char *fdn, int ver)
453 buf[bytes++] = ADSI_CONNECT_SESSION;
455 /* Reserve space for length */
460 buf[bytes++] = fdn[x];
462 buf[bytes++] = ver & 0xff;
470 int adsi_download_connect(unsigned char *buf, unsigned char *service, unsigned char *fdn, unsigned char *sec, int ver)
476 buf[bytes++] = ADSI_DOWNLOAD_CONNECT;
478 /* Reserve space for length */
482 bytes+= ccopy(buf + bytes, service, 18);
488 buf[bytes++] = fdn[x];
491 buf[bytes++] = sec[x];
492 buf[bytes++] = ver & 0xff;
500 int adsi_disconnect_session(unsigned char *buf)
505 buf[bytes++] = ADSI_DISC_SESSION;
507 /* Reserve space for length */
515 int adsi_query_cpeid(unsigned char *buf)
518 buf[bytes++] = ADSI_QUERY_CPEID;
519 /* Reserve space for length */
525 int adsi_query_cpeinfo(unsigned char *buf)
528 buf[bytes++] = ADSI_QUERY_CONFIG;
529 /* Reserve space for length */
535 int adsi_read_encoded_dtmf(struct ast_channel *chan, unsigned char *buf, int maxlen)
539 unsigned char current = 0;
542 memset(buf, 0, sizeof(buf));
543 while(bytes <= maxlen) {
544 /* Wait up to a second for a digit */
545 res = ast_waitfordigit(chan, 1000);
552 /* Ignore anything other than a digit */
553 if ((res < '0') || (res > '9'))
560 buf[bytes++] = (res << 4) | current;
570 int adsi_get_cpeid(struct ast_channel *chan, unsigned char *cpeid, int voice)
575 bytes += adsi_data_mode(buf);
576 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
579 bytes += adsi_query_cpeid(buf);
580 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
583 memset(buf, 0, sizeof(buf));
584 res = adsi_read_encoded_dtmf(chan, cpeid, 4);
586 ast_log(LOG_WARNING, "Got %d bytes back of encoded DTMF, expecting 4\n", res);
594 bytes += adsi_voice_mode(buf, 0);
595 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
596 /* Ignore the resulting DTMF B announcing it's in voice mode */
597 ast_waitfordigit(chan, 1000);
602 int adsi_get_cpeinfo(struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
607 bytes += adsi_data_mode(buf);
608 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
611 bytes += adsi_query_cpeinfo(buf);
612 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
615 memset(buf, 0, sizeof(buf));
616 res = ast_readstring(chan, buf, 2, 1000, 500, "");
619 if (strlen(buf) != 2) {
620 ast_log(LOG_WARNING, "Got %d bytes of width, expecting 2\n", res);
628 memset(buf, 0, sizeof(buf));
630 res = ast_readstring(chan, buf, 2, 1000, 500, "");
633 if (strlen(buf) != 2) {
634 ast_log(LOG_WARNING, "Got %d bytes of height, expecting 2\n", res);
643 memset(buf, 0, sizeof(buf));
645 res = ast_readstring(chan, buf, 1, 1000, 500, "");
648 if (strlen(buf) != 1) {
649 ast_log(LOG_WARNING, "Got %d bytes of buttons, expecting 1\n", res);
655 *buttons = atoi(buf);
659 bytes += adsi_voice_mode(buf, 0);
660 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
661 /* Ignore the resulting DTMF B announcing it's in voice mode */
662 ast_waitfordigit(chan, 1000);
667 int adsi_data_mode(unsigned char *buf)
672 buf[bytes++] = ADSI_SWITCH_TO_DATA;
674 /* Reserve space for length */
682 int adsi_clear_soft_keys(unsigned char *buf)
687 buf[bytes++] = ADSI_CLEAR_SOFTKEY;
689 /* Reserve space for length */
697 int adsi_clear_screen(unsigned char *buf)
702 buf[bytes++] = ADSI_CLEAR_SCREEN;
704 /* Reserve space for length */
712 int adsi_voice_mode(unsigned char *buf, int when)
717 buf[bytes++] = ADSI_SWITCH_TO_VOICE;
719 /* Reserve space for length */
722 buf[bytes++] = when & 0x7f;
729 int adsi_available(struct ast_channel *chan)
731 int cpe = chan->adsicpe & 0xff;
732 if ((cpe == AST_ADSI_AVAILABLE) ||
733 (cpe == AST_ADSI_UNKNOWN))
738 int adsi_download_disconnect(unsigned char *buf)
743 buf[bytes++] = ADSI_DOWNLOAD_DISC;
745 /* Reserve space for length */
753 int adsi_display(unsigned char *buf, int page, int line, int just, int wrap,
754 unsigned char *col1, unsigned char *col2)
758 /* Sanity check line number */
761 if (line > 4) return -1;
763 if (line > 33) return -1;
769 buf[bytes++] = ADSI_LOAD_VIRTUAL_DISP;
771 /* Reserve space for size */
774 /* Page and wrap indicator */
775 buf[bytes++] = ((page & 0x1) << 7) | ((wrap & 0x1) << 6) | (line & 0x3f);
778 buf[bytes++] = (just & 0x3) << 5;
780 /* Omit highlight mode definition */
784 bytes+= ccopy(buf + bytes, col1, 20);
789 /* Secondary column */
790 bytes += ccopy(buf + bytes, col2, 20);
799 int adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
804 if (line > 4) return -1;
806 if (line > 33) return -1;
812 buf[bytes++] = ADSI_INPUT_CONTROL;
814 buf[bytes++] = ((page & 1) << 7) | (line & 0x3f);
815 buf[bytes++] = ((display & 1) << 7) | ((just & 0x3) << 4) | (format & 0x7);
822 int adsi_input_format(unsigned char *buf, int num, int dir, int wrap, unsigned char *format1, unsigned char *format2)
826 if (!strlen(format1))
829 buf[bytes++] = ADSI_INPUT_FORMAT;
831 buf[bytes++] = ((dir & 1) << 7) | ((wrap & 1) << 6) | (num & 0x7);
832 bytes += ccopy(buf + bytes, format1, 20);
834 if (format2 && strlen(format2)) {
835 bytes += ccopy(buf + bytes, format2, 20);
841 int adsi_set_keys(unsigned char *buf, unsigned char *keys)
846 buf[bytes++] = ADSI_INIT_SOFTKEY_LINE;
849 /* Key definitions */
851 buf[bytes++] = (keys[x] & 0x3f) ? keys[x] : (keys[x] | 0x1);
856 int adsi_set_line(unsigned char *buf, int page, int line)
860 /* Sanity check line number */
863 if (line > 4) return -1;
865 if (line > 33) return -1;
871 buf[bytes++] = ADSI_LINE_CONTROL;
873 /* Reserve space for size */
877 buf[bytes++] = ((page & 0x1) << 7) | (line & 0x3f);
884 static int total = 0;
885 static int speeds = 0;
887 int adsi_channel_restore(struct ast_channel *chan)
892 unsigned char keyd[6];
894 memset(dsp, 0, sizeof(dsp));
896 /* Start with initial display setup */
898 bytes += adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
900 /* Prepare key setup messages */
903 memset(keyd, 0, sizeof(keyd));
904 for (x=0;x<speeds;x++) {
905 keyd[x] = ADSI_SPEED_DIAL + x;
907 bytes += adsi_set_keys(dsp + bytes, keyd);
909 adsi_transmit_message(chan, dsp, bytes, ADSI_MSG_DISPLAY);
914 int adsi_print(struct ast_channel *chan, char **lines, int *aligns, int voice)
920 for(x=0;lines[x];x++)
921 bytes += adsi_display(buf + bytes, ADSI_INFO_PAGE, x+1, aligns[x],0, lines[x], "");
922 bytes += adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
924 bytes += adsi_voice_mode(buf + bytes, 0);
926 res = adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
928 /* Ignore the resulting DTMF B announcing it's in voice mode */
929 ast_waitfordigit(chan, 1000);
934 int adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
941 memset(dsp, 0, sizeof(dsp));
943 /* Connect to session */
945 bytes += adsi_connect_session(dsp + bytes, app,ver);
948 bytes += adsi_data_mode(dsp + bytes);
950 /* Prepare key setup messages */
951 if (adsi_transmit_message(chan, dsp, bytes, ADSI_MSG_DISPLAY))
954 res = ast_readstring(chan, resp, 1, 1200, 1200, "");
958 ast_log(LOG_DEBUG, "No response from CPE about version. Assuming not there.\n");
961 if (!strcmp(resp, "B")) {
962 ast_log(LOG_DEBUG, "CPE has script '%s' version %d already loaded\n", app, ver);
964 } else if (!strcmp(resp, "A")) {
965 ast_log(LOG_DEBUG, "CPE hasn't script '%s' version %d already loaded\n", app, ver);
967 ast_log(LOG_WARNING, "Unexpected CPE response to script query: %s\n", resp);
975 int adsi_unload_session(struct ast_channel *chan)
980 memset(dsp, 0, sizeof(dsp));
982 /* Connect to session */
984 bytes += adsi_disconnect_session(dsp + bytes);
985 bytes += adsi_voice_mode(dsp + bytes, 0);
987 /* Prepare key setup messages */
988 if (adsi_transmit_message(chan, dsp, bytes, ADSI_MSG_DISPLAY))
993 static int str2align(char *s)
995 if (!strncasecmp(s, "l", 1))
996 return ADSI_JUST_LEFT;
997 else if (!strncasecmp(s, "r", 1))
998 return ADSI_JUST_RIGHT;
999 else if (!strncasecmp(s, "i", 1))
1000 return ADSI_JUST_IND;
1002 return ADSI_JUST_CENT;
1005 static void init_state(void)
1009 for (x=0;x<ADSI_MAX_INTRO;x++)
1010 aligns[x] = ADSI_JUST_CENT;
1011 strncpy(intro[0], "Welcome to the", sizeof(intro[0]) - 1);
1012 strncpy(intro[1], "Asterisk", sizeof(intro[1]) - 1);
1013 strncpy(intro[2], "Open Source PBX", sizeof(intro[2]) - 1);
1016 for (x=3;x<ADSI_MAX_INTRO;x++)
1018 memset(speeddial, 0, sizeof(speeddial));
1019 alignment = ADSI_JUST_CENT;
1022 static void adsi_load(void)
1025 struct ast_config *conf;
1026 struct ast_variable *v;
1029 conf = ast_config_load("adsi.conf");
1032 v = ast_variable_browse(conf, "intro");
1034 if (!strcasecmp(v->name, "alignment"))
1035 alignment = str2align(v->value);
1036 else if (!strcasecmp(v->name, "greeting")) {
1037 if (x < ADSI_MAX_INTRO) {
1038 aligns[x] = alignment;
1039 strncpy(intro[x], v->value, sizeof(intro[x]) - 1);
1040 intro[x][sizeof(intro[x]) - 1] = '\0';
1043 } else if (!strcasecmp(v->name, "maxretries")) {
1044 if (atoi(v->value) > 0)
1045 maxretries = atoi(v->value);
1049 v = ast_variable_browse(conf, "speeddial");
1056 name = strsep(&stringp, ",");
1057 sname = strsep(&stringp, ",");
1060 if (x < ADSI_MAX_SPEED_DIAL) {
1061 /* Up to 20 digits */
1062 strncpy(speeddial[x][0], v->name, sizeof(speeddial[x][0]) - 1);
1063 strncpy(speeddial[x][1], name, 18);
1064 strncpy(speeddial[x][2], sname, 7);
1072 ast_config_destroy(conf);
1082 int load_module(void)
1088 int unload_module(void)
1090 /* Can't unload this once we're loaded */
1094 char *description(void)
1096 return "ADSI Resource";
1101 /* We should never be unloaded */
1107 return ASTERISK_GPL_KEY;