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_full(chan, buf, bytes, ADSI_MSG_DOWNLOAD, 0))
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_full(chan, buf, bytes, ADSI_MSG_DOWNLOAD, 0))
336 int adsi_transmit_message_full(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype, int dowait)
338 unsigned char *msgs[5] = { NULL, NULL, NULL, NULL, NULL };
344 int writeformat, readformat;
345 int waitforswitch = 0;
347 writeformat = chan->writeformat;
348 readformat = chan->readformat;
350 newdatamode = chan->adsicpe & ADSI_FLAG_DATAMODE;
352 for (x=0;x<msglen;x+=(msg[x+1]+2)) {
353 if (msg[x] == ADSI_SWITCH_TO_DATA) {
354 ast_log(LOG_DEBUG, "Switch to data is sent!\n");
356 newdatamode = ADSI_FLAG_DATAMODE;
359 if (msg[x] == ADSI_SWITCH_TO_VOICE) {
360 ast_log(LOG_DEBUG, "Switch to voice is sent!\n");
368 msgtypes[0] = msgtype;
371 ast_log(LOG_WARNING, "Can't send ADSI message of %d bytes, too large\n", msglen);
375 ast_stopstream(chan);
377 if (ast_set_write_format(chan, AST_FORMAT_ULAW)) {
378 ast_log(LOG_WARNING, "Unable to set write format to ULAW\n");
382 if (ast_set_read_format(chan, AST_FORMAT_ULAW)) {
383 ast_log(LOG_WARNING, "Unable to set read format to ULAW\n");
385 if (ast_set_write_format(chan, writeformat))
386 ast_log(LOG_WARNING, "Unable to restore write format to %d\n", writeformat);
390 res = __adsi_transmit_messages(chan, msgs, msglens, msgtypes);
393 ast_log(LOG_DEBUG, "Wait for switch is '%d'\n", waitforswitch);
394 while(waitforswitch-- && ((res = ast_waitfordigit(chan, 1000)) > 0)) { res = 0; ast_log(LOG_DEBUG, "Waiting for 'B'...\n"); }
398 chan->adsicpe = (chan->adsicpe & ~ADSI_FLAG_DATAMODE) | newdatamode;
401 ast_set_write_format(chan, writeformat);
403 ast_set_read_format(chan, readformat);
406 res = ast_safe_sleep(chan, 100 );
410 int adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
412 return adsi_transmit_message_full(chan, msg, msglen, msgtype, 1);
415 static inline int ccopy(unsigned char *dst, unsigned char *src, int max)
418 /* Carefully copy the requested data */
419 while ((x < max) && src[x] && (src[x] != 0xff)) {
426 int adsi_load_soft_key(unsigned char *buf, int key, unsigned char *llabel, unsigned char *slabel, unsigned char *ret, int data)
430 /* Abort if invalid key specified */
431 if ((key < 2) || (key > 33))
433 buf[bytes++] = ADSI_LOAD_SOFTKEY;
434 /* Reserve for length */
439 /* Carefully copy long label */
440 bytes += ccopy(buf + bytes, llabel, 18);
442 /* Place delimiter */
446 bytes += ccopy(buf + bytes, slabel, 7);
449 /* If specified, copy return string */
451 /* Place delimiter */
454 buf[bytes++] = ADSI_SWITCH_TO_DATA2;
455 /* Carefully copy return string */
456 bytes += ccopy(buf + bytes, ret, 20);
459 /* Replace parameter length */
465 int adsi_connect_session(unsigned char *buf, unsigned char *fdn, int ver)
471 buf[bytes++] = ADSI_CONNECT_SESSION;
473 /* Reserve space for length */
478 buf[bytes++] = fdn[x];
480 buf[bytes++] = ver & 0xff;
488 int adsi_download_connect(unsigned char *buf, unsigned char *service, unsigned char *fdn, unsigned char *sec, int ver)
494 buf[bytes++] = ADSI_DOWNLOAD_CONNECT;
496 /* Reserve space for length */
500 bytes+= ccopy(buf + bytes, service, 18);
506 buf[bytes++] = fdn[x];
509 buf[bytes++] = sec[x];
510 buf[bytes++] = ver & 0xff;
518 int adsi_disconnect_session(unsigned char *buf)
523 buf[bytes++] = ADSI_DISC_SESSION;
525 /* Reserve space for length */
533 int adsi_query_cpeid(unsigned char *buf)
536 buf[bytes++] = ADSI_QUERY_CPEID;
537 /* Reserve space for length */
543 int adsi_query_cpeinfo(unsigned char *buf)
546 buf[bytes++] = ADSI_QUERY_CONFIG;
547 /* Reserve space for length */
553 int adsi_read_encoded_dtmf(struct ast_channel *chan, unsigned char *buf, int maxlen)
557 unsigned char current = 0;
560 memset(buf, 0, sizeof(buf));
561 while(bytes <= maxlen) {
562 /* Wait up to a second for a digit */
563 res = ast_waitfordigit(chan, 1000);
570 /* Ignore anything other than a digit */
571 if ((res < '0') || (res > '9'))
578 buf[bytes++] = (res << 4) | current;
588 int adsi_get_cpeid(struct ast_channel *chan, unsigned char *cpeid, int voice)
593 bytes += adsi_data_mode(buf);
594 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
597 bytes += adsi_query_cpeid(buf);
598 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
601 memset(buf, 0, sizeof(buf));
602 res = adsi_read_encoded_dtmf(chan, cpeid, 4);
604 ast_log(LOG_WARNING, "Got %d bytes back of encoded DTMF, expecting 4\n", res);
612 bytes += adsi_voice_mode(buf, 0);
613 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
614 /* Ignore the resulting DTMF B announcing it's in voice mode */
615 ast_waitfordigit(chan, 1000);
620 int adsi_get_cpeinfo(struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
625 bytes += adsi_data_mode(buf);
626 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
629 bytes += adsi_query_cpeinfo(buf);
630 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
633 memset(buf, 0, sizeof(buf));
634 res = ast_readstring(chan, buf, 2, 1000, 500, "");
637 if (strlen(buf) != 2) {
638 ast_log(LOG_WARNING, "Got %d bytes of width, expecting 2\n", res);
646 memset(buf, 0, sizeof(buf));
648 res = ast_readstring(chan, buf, 2, 1000, 500, "");
651 if (strlen(buf) != 2) {
652 ast_log(LOG_WARNING, "Got %d bytes of height, expecting 2\n", res);
661 memset(buf, 0, sizeof(buf));
663 res = ast_readstring(chan, buf, 1, 1000, 500, "");
666 if (strlen(buf) != 1) {
667 ast_log(LOG_WARNING, "Got %d bytes of buttons, expecting 1\n", res);
673 *buttons = atoi(buf);
677 bytes += adsi_voice_mode(buf, 0);
678 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
679 /* Ignore the resulting DTMF B announcing it's in voice mode */
680 ast_waitfordigit(chan, 1000);
685 int adsi_data_mode(unsigned char *buf)
690 buf[bytes++] = ADSI_SWITCH_TO_DATA;
692 /* Reserve space for length */
700 int adsi_clear_soft_keys(unsigned char *buf)
705 buf[bytes++] = ADSI_CLEAR_SOFTKEY;
707 /* Reserve space for length */
715 int adsi_clear_screen(unsigned char *buf)
720 buf[bytes++] = ADSI_CLEAR_SCREEN;
722 /* Reserve space for length */
730 int adsi_voice_mode(unsigned char *buf, int when)
735 buf[bytes++] = ADSI_SWITCH_TO_VOICE;
737 /* Reserve space for length */
740 buf[bytes++] = when & 0x7f;
747 int adsi_available(struct ast_channel *chan)
749 int cpe = chan->adsicpe & 0xff;
750 if ((cpe == AST_ADSI_AVAILABLE) ||
751 (cpe == AST_ADSI_UNKNOWN))
756 int adsi_download_disconnect(unsigned char *buf)
761 buf[bytes++] = ADSI_DOWNLOAD_DISC;
763 /* Reserve space for length */
771 int adsi_display(unsigned char *buf, int page, int line, int just, int wrap,
772 unsigned char *col1, unsigned char *col2)
776 /* Sanity check line number */
779 if (line > 4) return -1;
781 if (line > 33) return -1;
787 buf[bytes++] = ADSI_LOAD_VIRTUAL_DISP;
789 /* Reserve space for size */
792 /* Page and wrap indicator */
793 buf[bytes++] = ((page & 0x1) << 7) | ((wrap & 0x1) << 6) | (line & 0x3f);
796 buf[bytes++] = (just & 0x3) << 5;
798 /* Omit highlight mode definition */
802 bytes+= ccopy(buf + bytes, col1, 20);
807 /* Secondary column */
808 bytes += ccopy(buf + bytes, col2, 20);
817 int adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
822 if (line > 4) return -1;
824 if (line > 33) return -1;
830 buf[bytes++] = ADSI_INPUT_CONTROL;
832 buf[bytes++] = ((page & 1) << 7) | (line & 0x3f);
833 buf[bytes++] = ((display & 1) << 7) | ((just & 0x3) << 4) | (format & 0x7);
840 int adsi_input_format(unsigned char *buf, int num, int dir, int wrap, unsigned char *format1, unsigned char *format2)
844 if (!strlen(format1))
847 buf[bytes++] = ADSI_INPUT_FORMAT;
849 buf[bytes++] = ((dir & 1) << 7) | ((wrap & 1) << 6) | (num & 0x7);
850 bytes += ccopy(buf + bytes, format1, 20);
852 if (format2 && strlen(format2)) {
853 bytes += ccopy(buf + bytes, format2, 20);
859 int adsi_set_keys(unsigned char *buf, unsigned char *keys)
864 buf[bytes++] = ADSI_INIT_SOFTKEY_LINE;
867 /* Key definitions */
869 buf[bytes++] = (keys[x] & 0x3f) ? keys[x] : (keys[x] | 0x1);
874 int adsi_set_line(unsigned char *buf, int page, int line)
878 /* Sanity check line number */
881 if (line > 4) return -1;
883 if (line > 33) return -1;
889 buf[bytes++] = ADSI_LINE_CONTROL;
891 /* Reserve space for size */
895 buf[bytes++] = ((page & 0x1) << 7) | (line & 0x3f);
902 static int total = 0;
903 static int speeds = 0;
905 int adsi_channel_restore(struct ast_channel *chan)
910 unsigned char keyd[6];
912 memset(dsp, 0, sizeof(dsp));
914 /* Start with initial display setup */
916 bytes += adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
918 /* Prepare key setup messages */
921 memset(keyd, 0, sizeof(keyd));
922 for (x=0;x<speeds;x++) {
923 keyd[x] = ADSI_SPEED_DIAL + x;
925 bytes += adsi_set_keys(dsp + bytes, keyd);
927 adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0);
932 int adsi_print(struct ast_channel *chan, char **lines, int *aligns, int voice)
938 for(x=0;lines[x];x++)
939 bytes += adsi_display(buf + bytes, ADSI_INFO_PAGE, x+1, aligns[x],0, lines[x], "");
940 bytes += adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
942 bytes += adsi_voice_mode(buf + bytes, 0);
944 res = adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
946 /* Ignore the resulting DTMF B announcing it's in voice mode */
947 ast_waitfordigit(chan, 1000);
952 int adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
959 memset(dsp, 0, sizeof(dsp));
961 /* Connect to session */
963 bytes += adsi_connect_session(dsp + bytes, app,ver);
966 bytes += adsi_data_mode(dsp + bytes);
968 /* Prepare key setup messages */
969 if (adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0))
972 res = ast_readstring(chan, resp, 1, 1200, 1200, "");
976 ast_log(LOG_DEBUG, "No response from CPE about version. Assuming not there.\n");
979 if (!strcmp(resp, "B")) {
980 ast_log(LOG_DEBUG, "CPE has script '%s' version %d already loaded\n", app, ver);
982 } else if (!strcmp(resp, "A")) {
983 ast_log(LOG_DEBUG, "CPE hasn't script '%s' version %d already loaded\n", app, ver);
985 ast_log(LOG_WARNING, "Unexpected CPE response to script query: %s\n", resp);
993 int adsi_unload_session(struct ast_channel *chan)
998 memset(dsp, 0, sizeof(dsp));
1000 /* Connect to session */
1002 bytes += adsi_disconnect_session(dsp + bytes);
1003 bytes += adsi_voice_mode(dsp + bytes, 0);
1005 /* Prepare key setup messages */
1006 if (adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0))
1011 static int str2align(char *s)
1013 if (!strncasecmp(s, "l", 1))
1014 return ADSI_JUST_LEFT;
1015 else if (!strncasecmp(s, "r", 1))
1016 return ADSI_JUST_RIGHT;
1017 else if (!strncasecmp(s, "i", 1))
1018 return ADSI_JUST_IND;
1020 return ADSI_JUST_CENT;
1023 static void init_state(void)
1027 for (x=0;x<ADSI_MAX_INTRO;x++)
1028 aligns[x] = ADSI_JUST_CENT;
1029 strncpy(intro[0], "Welcome to the", sizeof(intro[0]) - 1);
1030 strncpy(intro[1], "Asterisk", sizeof(intro[1]) - 1);
1031 strncpy(intro[2], "Open Source PBX", sizeof(intro[2]) - 1);
1034 for (x=3;x<ADSI_MAX_INTRO;x++)
1036 memset(speeddial, 0, sizeof(speeddial));
1037 alignment = ADSI_JUST_CENT;
1040 static void adsi_load(void)
1043 struct ast_config *conf;
1044 struct ast_variable *v;
1047 conf = ast_config_load("adsi.conf");
1050 v = ast_variable_browse(conf, "intro");
1052 if (!strcasecmp(v->name, "alignment"))
1053 alignment = str2align(v->value);
1054 else if (!strcasecmp(v->name, "greeting")) {
1055 if (x < ADSI_MAX_INTRO) {
1056 aligns[x] = alignment;
1057 strncpy(intro[x], v->value, sizeof(intro[x]) - 1);
1058 intro[x][sizeof(intro[x]) - 1] = '\0';
1061 } else if (!strcasecmp(v->name, "maxretries")) {
1062 if (atoi(v->value) > 0)
1063 maxretries = atoi(v->value);
1067 v = ast_variable_browse(conf, "speeddial");
1074 name = strsep(&stringp, ",");
1075 sname = strsep(&stringp, ",");
1078 if (x < ADSI_MAX_SPEED_DIAL) {
1079 /* Up to 20 digits */
1080 strncpy(speeddial[x][0], v->name, sizeof(speeddial[x][0]) - 1);
1081 strncpy(speeddial[x][1], name, 18);
1082 strncpy(speeddial[x][2], sname, 7);
1090 ast_config_destroy(conf);
1100 int load_module(void)
1106 int unload_module(void)
1108 /* Can't unload this once we're loaded */
1112 char *description(void)
1114 return "ADSI Resource";
1119 /* We should never be unloaded */
1125 return ASTERISK_GPL_KEY;