since the module API is changing, it's a good time to const-ify the description(...
[asterisk/asterisk.git] / res / res_adsi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Includes code and algorithms from the Zapata library.
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief ADSI support 
24  *
25  * \author Mark Spencer <markster@digium.com> 
26  */
27
28 #include <time.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <math.h>
34 #include <errno.h>
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include "asterisk/ulaw.h"
41 #include "asterisk/alaw.h"
42 #include "asterisk/callerid.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/fskmodem.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/adsi.h"
47 #include "asterisk/module.h"
48 #include "asterisk/config.h"
49 #include "asterisk/file.h"
50
51 #define DEFAULT_ADSI_MAX_RETRIES 3
52
53 #define ADSI_MAX_INTRO 20
54 #define ADSI_MAX_SPEED_DIAL 6
55
56 #define ADSI_FLAG_DATAMODE      (1 << 8)
57
58 static int maxretries = DEFAULT_ADSI_MAX_RETRIES;
59
60 /* Asterisk ADSI button definitions */
61 #define ADSI_SPEED_DIAL         10      /* 10-15 are reserved for speed dial */
62
63 static char intro[ADSI_MAX_INTRO][20];
64 static int aligns[ADSI_MAX_INTRO];
65
66 static char speeddial[ADSI_MAX_SPEED_DIAL][3][20];
67
68 static int alignment = 0;
69
70 static int adsi_generate(unsigned char *buf, int msgtype, unsigned char *msg, int msglen, int msgnum, int last, int codec)
71 {
72         int sum;
73         int x;  
74         int bytes=0;
75         /* Initial carrier (imaginary) */
76         float cr = 1.0;
77         float ci = 0.0;
78         float scont = 0.0;
79
80         if (msglen > 255)
81                 msglen = 255;
82
83         /* If first message, Send 150ms of MARK's */
84         if (msgnum == 1) {
85                 for (x=0;x<150;x++)     /* was 150 */
86                         PUT_CLID_MARKMS;
87         }
88         /* Put message type */
89         PUT_CLID(msgtype);
90         sum = msgtype;
91
92         /* Put message length (plus one  for the message number) */
93         PUT_CLID(msglen + 1);
94         sum += msglen + 1;
95
96         /* Put message number */
97         PUT_CLID(msgnum);
98         sum += msgnum;
99
100         /* Put actual message */
101         for (x=0;x<msglen;x++) {
102                 PUT_CLID(msg[x]);
103                 sum += msg[x];
104         }
105
106         /* Put 2's compliment of sum */
107         PUT_CLID(256-(sum & 0xff));
108
109 #if 0
110         if (last) {
111                 /* Put trailing marks */
112                 for (x=0;x<50;x++)
113                         PUT_CLID_MARKMS;
114         }
115 #endif
116         return bytes;
117
118 }
119
120 static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int len, int *remainder)
121 {
122         /* Sends carefully on a full duplex channel by using reading for
123            timing */
124         struct ast_frame *inf, outf;
125         int amt;
126
127         /* Zero out our outgoing frame */
128         memset(&outf, 0, sizeof(outf));
129
130         if (remainder && *remainder) {
131                 amt = len;
132
133                 /* Send remainder if provided */
134                 if (amt > *remainder)
135                         amt = *remainder;
136                 else
137                         *remainder = *remainder - amt;
138                 outf.frametype = AST_FRAME_VOICE;
139                 outf.subclass = AST_FORMAT_ULAW;
140                 outf.data = buf;
141                 outf.datalen = amt;
142                 outf.samples = amt;
143                 if (ast_write(chan, &outf)) {
144                         ast_log(LOG_WARNING, "Failed to carefully write frame\n");
145                         return -1;
146                 }
147                 /* Update pointers and lengths */
148                 buf += amt;
149                 len -= amt;
150         }
151
152         while(len) {
153                 amt = len;
154                 /* If we don't get anything at all back in a second, forget
155                    about it */
156                 if (ast_waitfor(chan, 1000) < 1)
157                         return -1;
158                 inf = ast_read(chan);
159                 /* Detect hangup */
160                 if (!inf)
161                         return -1;
162                 if (inf->frametype == AST_FRAME_VOICE) {
163                         /* Read a voice frame */
164                         if (inf->subclass != AST_FORMAT_ULAW) {
165                                 ast_log(LOG_WARNING, "Channel not in ulaw?\n");
166                                 return -1;
167                         }
168                         /* Send no more than they sent us */
169                         if (amt > inf->datalen)
170                                 amt = inf->datalen;
171                         else if (remainder)
172                                 *remainder = inf->datalen - amt;
173                         outf.frametype = AST_FRAME_VOICE;
174                         outf.subclass = AST_FORMAT_ULAW;
175                         outf.data = buf;
176                         outf.datalen = amt;
177                         outf.samples = amt;
178                         if (ast_write(chan, &outf)) {
179                                 ast_log(LOG_WARNING, "Failed to carefully write frame\n");
180                                 return -1;
181                         }
182                         /* Update pointers and lengths */
183                         buf += amt;
184                         len -= amt;
185                 }
186                 ast_frfree(inf);
187         }
188         return 0;
189 }
190
191 static int __adsi_transmit_messages(struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype)
192 {
193         /* msglen must be no more than 256 bits, each */
194         unsigned char buf[24000 * 5];
195         int pos = 0, res;
196         int x;
197         int start=0;
198         int retries = 0;
199
200         char ack[3];
201
202         /* Wait up to 500 ms for initial ACK */
203         int waittime;
204         struct ast_frame *f;
205         int rem = 0;
206         int def;
207
208         if (chan->adsicpe == AST_ADSI_UNAVAILABLE) {
209                 /* Don't bother if we know they don't support ADSI */
210                 errno = ENOSYS;
211                 return -1;
212         }
213
214         while(retries < maxretries) {
215                 if (!(chan->adsicpe & ADSI_FLAG_DATAMODE)) {
216                         /* Generate CAS (no SAS) */
217                         ast_gen_cas(buf, 0, 680, AST_FORMAT_ULAW);
218                 
219                         /* Send CAS */
220                         if (adsi_careful_send(chan, buf, 680, NULL)) {
221                                 ast_log(LOG_WARNING, "Unable to send CAS\n");
222                         }
223                         /* Wait For DTMF result */
224                         waittime = 500;
225                         for(;;) {
226                                 if (((res = ast_waitfor(chan, waittime)) < 1)) {
227                                         /* Didn't get back DTMF A in time */
228                                         ast_log(LOG_DEBUG, "No ADSI CPE detected (%d)\n", res);
229                                         if (!chan->adsicpe)
230                                                 chan->adsicpe = AST_ADSI_UNAVAILABLE;
231                                         errno = ENOSYS;
232                                         return -1;
233                                 }
234                                 waittime = res;
235                                 f = ast_read(chan);
236                                 if (!f) {
237                                         ast_log(LOG_DEBUG, "Hangup in ADSI\n");
238                                         return -1;
239                                 }
240                                 if (f->frametype == AST_FRAME_DTMF) {
241                                         if (f->subclass == 'A') {
242                                                 /* Okay, this is an ADSI CPE.  Note this for future reference, too */
243                                                 if (!chan->adsicpe)
244                                                         chan->adsicpe = AST_ADSI_AVAILABLE;
245                                                 break;
246                                         } else {
247                                                 if (f->subclass == 'D')  {
248                                                         ast_log(LOG_DEBUG, "Off-hook capable CPE only, not ADSI\n");
249                                                 } else
250                                                         ast_log(LOG_WARNING, "Unknown ADSI response '%c'\n", f->subclass);
251                                                 if (!chan->adsicpe)
252                                                         chan->adsicpe = AST_ADSI_UNAVAILABLE;
253                                                 errno = ENOSYS;
254                                                 return -1;
255                                         }
256                                 }
257                                 ast_frfree(f);
258                         }
259
260                         ast_log(LOG_DEBUG, "ADSI Compatible CPE Detected\n");
261                 } else
262                         ast_log(LOG_DEBUG, "Already in data mode\n");
263
264                 x = 0;
265                 pos = 0;
266 #if 1
267                 def= ast_channel_defer_dtmf(chan);
268 #endif
269                 while((x < 6) && msg[x]) {
270                         res = adsi_generate(buf + pos, msgtype[x], msg[x], msglen[x], x+1 - start, (x == 5) || !msg[x+1], AST_FORMAT_ULAW);
271                         if (res < 0) {
272                                 ast_log(LOG_WARNING, "Failed to generate ADSI message %d on channel %s\n", x + 1, chan->name);
273                                 return -1;
274                         }
275                         ast_log(LOG_DEBUG, "Message %d, of %d input bytes, %d output bytes\n", 
276                                         x + 1, msglen[x], res);
277                         pos += res; 
278                         x++;
279                 }
280
281
282                 rem = 0;
283                 res = adsi_careful_send(chan, buf, pos, &rem); 
284                 if (!def)
285                         ast_channel_undefer_dtmf(chan);
286                 if (res)
287                         return -1;
288
289                 ast_log(LOG_DEBUG, "Sent total spill of %d bytes\n", pos);
290
291                 memset(ack, 0, sizeof(ack));
292                 /* Get real result */
293                 res = ast_readstring(chan, ack, 2, 1000, 1000, "");
294                 /* Check for hangup */
295                 if (res < 0)
296                         return -1;
297                 if (ack[0] == 'D') {
298                         ast_log(LOG_DEBUG, "Acked up to message %d\n", atoi(ack + 1));
299                         start += atoi(ack + 1);
300                         if (start >= x)
301                                 break;
302                         else {
303                                 retries++;
304                                 ast_log(LOG_DEBUG, "Retransmitting (%d), from %d\n", retries, start + 1);
305                         }
306                 } else {
307                         retries++;
308                         ast_log(LOG_WARNING, "Unexpected response to ack: %s (retry %d)\n", ack, retries);
309                 } 
310         }
311         if (retries >= maxretries) {
312                 ast_log(LOG_WARNING, "Maximum ADSI Retries (%d) exceeded\n", maxretries);
313                 errno = ETIMEDOUT;
314                 return -1;
315         }
316         return 0;
317         
318 }
319
320 int adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
321 {
322         int bytes;
323         unsigned char buf[256];
324         char ack[2];
325         bytes = 0;
326         /* Setup the resident soft key stuff, a piece at a time */
327         /* Upload what scripts we can for voicemail ahead of time */
328         bytes += adsi_download_connect(buf + bytes, service, fdn, sec, version);
329         if (adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DOWNLOAD, 0))
330                 return -1;
331         if (ast_readstring(chan, ack, 1, 10000, 10000, ""))
332                 return -1;
333         if (ack[0] == 'B')
334                 return 0;
335         ast_log(LOG_DEBUG, "Download was denied by CPE\n");
336         return -1;
337 }
338
339 int adsi_end_download(struct ast_channel *chan)
340 {
341         int bytes;
342         unsigned char buf[256];
343         bytes = 0;
344         /* Setup the resident soft key stuff, a piece at a time */
345         /* Upload what scripts we can for voicemail ahead of time */
346         bytes += adsi_download_disconnect(buf + bytes);
347         if (adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DOWNLOAD, 0))
348                 return -1;
349         return 0;
350 }
351
352 int adsi_transmit_message_full(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype, int dowait)
353 {
354         unsigned char *msgs[5] = { NULL, NULL, NULL, NULL, NULL };
355         int msglens[5];
356         int msgtypes[5];
357         int newdatamode;
358         int res;
359         int x;
360         int writeformat, readformat;
361         int waitforswitch = 0;
362
363         writeformat = chan->writeformat;
364         readformat = chan->readformat;
365
366         newdatamode = chan->adsicpe & ADSI_FLAG_DATAMODE;
367
368         for (x=0;x<msglen;x+=(msg[x+1]+2)) {
369                 if (msg[x] == ADSI_SWITCH_TO_DATA) {
370                         ast_log(LOG_DEBUG, "Switch to data is sent!\n");
371                         waitforswitch++;
372                         newdatamode = ADSI_FLAG_DATAMODE;
373                 }
374                 
375                 if (msg[x] == ADSI_SWITCH_TO_VOICE) {
376                         ast_log(LOG_DEBUG, "Switch to voice is sent!\n");
377                         waitforswitch++;
378                         newdatamode = 0;
379                 }
380         }
381         msgs[0] = msg;
382
383         msglens[0] = msglen;
384         msgtypes[0] = msgtype;
385
386         if (msglen > 253) {
387                 ast_log(LOG_WARNING, "Can't send ADSI message of %d bytes, too large\n", msglen);
388                 return -1;
389         }
390
391         ast_stopstream(chan);
392
393         if (ast_set_write_format(chan, AST_FORMAT_ULAW)) {
394                 ast_log(LOG_WARNING, "Unable to set write format to ULAW\n");
395                 return -1;
396         }
397
398         if (ast_set_read_format(chan, AST_FORMAT_ULAW)) {
399                 ast_log(LOG_WARNING, "Unable to set read format to ULAW\n");
400                 if (writeformat) {
401                         if (ast_set_write_format(chan, writeformat)) 
402                                 ast_log(LOG_WARNING, "Unable to restore write format to %d\n", writeformat);
403                 }
404                 return -1;
405         }
406         res = __adsi_transmit_messages(chan, msgs, msglens, msgtypes);
407
408         if (dowait) {
409                 ast_log(LOG_DEBUG, "Wait for switch is '%d'\n", waitforswitch);
410                 while(waitforswitch-- && ((res = ast_waitfordigit(chan, 1000)) > 0)) { res = 0; ast_log(LOG_DEBUG, "Waiting for 'B'...\n"); }
411         }
412         
413         if (!res)
414                 chan->adsicpe = (chan->adsicpe & ~ADSI_FLAG_DATAMODE) | newdatamode;
415
416         if (writeformat)
417                 ast_set_write_format(chan, writeformat);
418         if (readformat)
419                 ast_set_read_format(chan, readformat);
420
421         if (!res)
422                 res = ast_safe_sleep(chan, 100 );
423         return res;
424 }
425
426 int adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
427 {
428         return adsi_transmit_message_full(chan, msg, msglen, msgtype, 1);
429 }
430
431 static inline int ccopy(unsigned char *dst, unsigned char *src, int max)
432 {
433         int x=0;
434         /* Carefully copy the requested data */
435         while ((x < max) && src[x] && (src[x] != 0xff)) {
436                 dst[x] = src[x];
437                 x++;
438         }
439         return x;
440 }
441
442 int adsi_load_soft_key(unsigned char *buf, int key, char *llabel, char *slabel, char *ret, int data)
443 {
444         int bytes=0;
445
446         /* Abort if invalid key specified */
447         if ((key < 2) || (key > 33))
448                 return -1;
449         buf[bytes++] = ADSI_LOAD_SOFTKEY;
450         /* Reserve for length */
451         bytes++;
452         /* Which key */
453         buf[bytes++] = key;
454
455         /* Carefully copy long label */
456         bytes += ccopy(buf + bytes, (unsigned char *)llabel, 18);
457
458         /* Place delimiter */
459         buf[bytes++] = 0xff;
460
461         /* Short label */
462         bytes += ccopy(buf + bytes, (unsigned char *)slabel, 7);
463
464
465         /* If specified, copy return string */
466         if (ret) {
467                 /* Place delimiter */
468                 buf[bytes++] = 0xff;
469                 if (data)
470                         buf[bytes++] = ADSI_SWITCH_TO_DATA2;
471                 /* Carefully copy return string */
472                 bytes += ccopy(buf + bytes, (unsigned char *)ret, 20);
473
474         }
475         /* Replace parameter length */
476         buf[1] = bytes - 2;
477         return bytes;
478         
479 }
480
481 int adsi_connect_session(unsigned char *buf, unsigned char *fdn, int ver)
482 {
483         int bytes=0;
484         int x;
485
486         /* Message type */
487         buf[bytes++] = ADSI_CONNECT_SESSION;
488
489         /* Reserve space for length */
490         bytes++;
491
492         if (fdn) {
493                 for (x=0;x<4;x++)
494                         buf[bytes++] = fdn[x];
495                 if (ver > -1)
496                         buf[bytes++] = ver & 0xff;
497         }
498
499         buf[1] = bytes - 2;
500         return bytes;
501
502 }
503
504 int adsi_download_connect(unsigned char *buf, char *service,  unsigned char *fdn, unsigned char *sec, int ver)
505 {
506         int bytes=0;
507         int x;
508
509         /* Message type */
510         buf[bytes++] = ADSI_DOWNLOAD_CONNECT;
511
512         /* Reserve space for length */
513         bytes++;
514
515         /* Primary column */
516         bytes+= ccopy(buf + bytes, (unsigned char *)service, 18);
517
518         /* Delimiter */
519         buf[bytes++] = 0xff;
520         
521         for (x=0;x<4;x++) {
522                 buf[bytes++] = fdn[x];
523         }
524         for (x=0;x<4;x++)
525                 buf[bytes++] = sec[x];
526         buf[bytes++] = ver & 0xff;
527
528         buf[1] = bytes - 2;
529
530         return bytes;
531
532 }
533
534 int adsi_disconnect_session(unsigned char *buf)
535 {
536         int bytes=0;
537
538         /* Message type */
539         buf[bytes++] = ADSI_DISC_SESSION;
540
541         /* Reserve space for length */
542         bytes++;
543
544         buf[1] = bytes - 2;
545         return bytes;
546
547 }
548
549 int adsi_query_cpeid(unsigned char *buf)
550 {
551         int bytes = 0;
552         buf[bytes++] = ADSI_QUERY_CPEID;
553         /* Reserve space for length */
554         bytes++;
555         buf[1] = bytes - 2;
556         return bytes;
557 }
558
559 int adsi_query_cpeinfo(unsigned char *buf)
560 {
561         int bytes = 0;
562         buf[bytes++] = ADSI_QUERY_CONFIG;
563         /* Reserve space for length */
564         bytes++;
565         buf[1] = bytes - 2;
566         return bytes;
567 }
568
569 int adsi_read_encoded_dtmf(struct ast_channel *chan, unsigned char *buf, int maxlen)
570 {
571         int bytes = 0;
572         int res;
573         unsigned char current = 0;
574         int gotstar = 0;
575         int pos = 0;
576         memset(buf, 0, sizeof(buf));
577         while(bytes <= maxlen) {
578                 /* Wait up to a second for a digit */
579                 res = ast_waitfordigit(chan, 1000);
580                 if (!res)
581                         break;
582                 if (res == '*') {
583                         gotstar = 1;    
584                         continue;
585                 }
586                 /* Ignore anything other than a digit */
587                 if ((res < '0') || (res > '9'))
588                         continue;
589                 res -= '0';
590                 if (gotstar)
591                         res += 9;
592                 if (pos)  {
593                         pos = 0;
594                         buf[bytes++] = (res << 4) | current;
595                 } else {
596                         pos = 1;
597                         current = res;
598                 }
599                 gotstar = 0;
600         }
601         return bytes;
602 }
603
604 int adsi_get_cpeid(struct ast_channel *chan, unsigned char *cpeid, int voice)
605 {
606         unsigned char buf[256];
607         int bytes = 0;
608         int res;
609         bytes += adsi_data_mode(buf);
610         adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
611
612         bytes = 0;
613         bytes += adsi_query_cpeid(buf);
614         adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
615
616         /* Get response */
617         memset(buf, 0, sizeof(buf));
618         res = adsi_read_encoded_dtmf(chan, cpeid, 4);
619         if (res != 4) {
620                 ast_log(LOG_WARNING, "Got %d bytes back of encoded DTMF, expecting 4\n", res);
621                 res = 0;
622         } else {
623                 res = 1;
624         }
625
626         if (voice) {
627                 bytes = 0;
628                 bytes += adsi_voice_mode(buf, 0);
629                 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
630                 /* Ignore the resulting DTMF B announcing it's in voice mode */
631                 ast_waitfordigit(chan, 1000);
632         }
633         return res;
634 }
635
636 int adsi_get_cpeinfo(struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
637 {
638         unsigned char buf[256];
639         int bytes = 0;
640         int res;
641         bytes += adsi_data_mode(buf);
642         adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
643
644         bytes = 0;
645         bytes += adsi_query_cpeinfo(buf);
646         adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
647
648         /* Get width */
649         memset(buf, 0, sizeof(buf));
650         res = ast_readstring(chan, (char *)buf, 2, 1000, 500, "");
651         if (res < 0)
652                 return res;
653         if (strlen((char *)buf) != 2) {
654                 ast_log(LOG_WARNING, "Got %d bytes of width, expecting 2\n", res);
655                 res = 0;
656         } else {
657                 res = 1;
658         }
659         if (width)
660                 *width = atoi((char *)buf);
661         /* Get height */
662         memset(buf, 0, sizeof(buf));
663         if (res) {
664                 res = ast_readstring(chan, (char *)buf, 2, 1000, 500, "");
665                 if (res < 0)
666                         return res;
667                 if (strlen((char *)buf) != 2) {
668                         ast_log(LOG_WARNING, "Got %d bytes of height, expecting 2\n", res);
669                         res = 0;
670                 } else {
671                         res = 1;
672                 }       
673                 if (height)
674                         *height= atoi((char *)buf);
675         }
676         /* Get buttons */
677         memset(buf, 0, sizeof(buf));
678         if (res) {
679                 res = ast_readstring(chan, (char *)buf, 1, 1000, 500, "");
680                 if (res < 0)
681                         return res;
682                 if (strlen((char *)buf) != 1) {
683                         ast_log(LOG_WARNING, "Got %d bytes of buttons, expecting 1\n", res);
684                         res = 0;
685                 } else {
686                         res = 1;
687                 }       
688                 if (buttons)
689                         *buttons = atoi((char *)buf);
690         }
691         if (voice) {
692                 bytes = 0;
693                 bytes += adsi_voice_mode(buf, 0);
694                 adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
695                 /* Ignore the resulting DTMF B announcing it's in voice mode */
696                 ast_waitfordigit(chan, 1000);
697         }
698         return res;
699 }
700
701 int adsi_data_mode(unsigned char *buf)
702 {
703         int bytes=0;
704
705         /* Message type */
706         buf[bytes++] = ADSI_SWITCH_TO_DATA;
707
708         /* Reserve space for length */
709         bytes++;
710
711         buf[1] = bytes - 2;
712         return bytes;
713
714 }
715
716 int adsi_clear_soft_keys(unsigned char *buf)
717 {
718         int bytes=0;
719
720         /* Message type */
721         buf[bytes++] = ADSI_CLEAR_SOFTKEY;
722
723         /* Reserve space for length */
724         bytes++;
725
726         buf[1] = bytes - 2;
727         return bytes;
728
729 }
730
731 int adsi_clear_screen(unsigned char *buf)
732 {
733         int bytes=0;
734
735         /* Message type */
736         buf[bytes++] = ADSI_CLEAR_SCREEN;
737
738         /* Reserve space for length */
739         bytes++;
740
741         buf[1] = bytes - 2;
742         return bytes;
743
744 }
745
746 int adsi_voice_mode(unsigned char *buf, int when)
747 {
748         int bytes=0;
749
750         /* Message type */
751         buf[bytes++] = ADSI_SWITCH_TO_VOICE;
752
753         /* Reserve space for length */
754         bytes++;
755
756         buf[bytes++] = when & 0x7f;
757
758         buf[1] = bytes - 2;
759         return bytes;
760
761 }
762
763 int adsi_available(struct ast_channel *chan)
764 {
765         int cpe = chan->adsicpe & 0xff;
766         if ((cpe == AST_ADSI_AVAILABLE) ||
767             (cpe == AST_ADSI_UNKNOWN))
768                 return 1;
769         return 0;
770 }
771
772 int adsi_download_disconnect(unsigned char *buf)
773 {
774         int bytes=0;
775
776         /* Message type */
777         buf[bytes++] = ADSI_DOWNLOAD_DISC;
778
779         /* Reserve space for length */
780         bytes++;
781
782         buf[1] = bytes - 2;
783         return bytes;
784
785 }
786
787 int adsi_display(unsigned char *buf, int page, int line, int just, int wrap, 
788                  char *col1, char *col2)
789 {
790         int bytes=0;
791
792         /* Sanity check line number */
793
794         if (page) {
795                 if (line > 4) return -1;
796         } else {
797                 if (line > 33) return -1;
798         }
799
800         if (line < 1)
801                 return -1;
802         /* Parameter type */
803         buf[bytes++] = ADSI_LOAD_VIRTUAL_DISP;
804         
805         /* Reserve space for size */
806         bytes++;
807
808         /* Page and wrap indicator */
809         buf[bytes++] = ((page & 0x1) << 7) | ((wrap & 0x1) << 6) | (line & 0x3f);
810
811         /* Justification */
812         buf[bytes++] = (just & 0x3) << 5;
813
814         /* Omit highlight mode definition */
815         buf[bytes++] = 0xff;
816
817         /* Primary column */
818         bytes+= ccopy(buf + bytes, (unsigned char *)col1, 20);
819
820         /* Delimiter */
821         buf[bytes++] = 0xff;
822         
823         /* Secondary column */
824         bytes += ccopy(buf + bytes, (unsigned char *)col2, 20);
825
826         /* Update length */
827         buf[1] = bytes - 2;
828         
829         return bytes;
830
831 }
832
833 int adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
834 {
835         int bytes=0;
836
837         if (page) {
838                 if (line > 4) return -1;
839         } else {
840                 if (line > 33) return -1;
841         }
842
843         if (line < 1)
844                 return -1;
845
846         buf[bytes++] = ADSI_INPUT_CONTROL;
847         bytes++;
848         buf[bytes++] = ((page & 1) << 7) | (line & 0x3f);
849         buf[bytes++] = ((display & 1) << 7) | ((just & 0x3) << 4) | (format & 0x7);
850         
851         buf[1] = bytes - 2;
852         return bytes;
853
854 }
855
856 int adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
857 {
858         int bytes = 0;
859
860         if (!strlen((char *)format1))
861                 return -1;
862
863         buf[bytes++] = ADSI_INPUT_FORMAT;
864         bytes++;
865         buf[bytes++] = ((dir & 1) << 7) | ((wrap & 1) << 6) | (num & 0x7);
866         bytes += ccopy(buf + bytes, (unsigned char *)format1, 20);
867         buf[bytes++] = 0xff;
868         if (format2 && strlen((char *)format2)) {
869                 bytes += ccopy(buf + bytes, (unsigned char *)format2, 20);
870         }
871         buf[1] = bytes - 2;
872         return bytes;
873 }
874
875 int adsi_set_keys(unsigned char *buf, unsigned char *keys)
876 {
877         int bytes=0;
878         int x;
879         /* Message type */
880         buf[bytes++] = ADSI_INIT_SOFTKEY_LINE;
881         /* Space for size */
882         bytes++;
883         /* Key definitions */
884         for (x=0;x<6;x++)
885                 buf[bytes++] = (keys[x] & 0x3f) ? keys[x] : (keys[x] | 0x1);
886         buf[1] = bytes - 2;
887         return bytes;
888 }
889
890 int adsi_set_line(unsigned char *buf, int page, int line)
891 {
892         int bytes=0;
893
894         /* Sanity check line number */
895
896         if (page) {
897                 if (line > 4) return -1;
898         } else {
899                 if (line > 33) return -1;
900         }
901
902         if (line < 1)
903                 return -1;
904         /* Parameter type */
905         buf[bytes++] = ADSI_LINE_CONTROL;
906         
907         /* Reserve space for size */
908         bytes++;
909
910         /* Page and line */
911         buf[bytes++] = ((page & 0x1) << 7) | (line & 0x3f);
912
913         buf[1] = bytes - 2;
914         return bytes;
915
916 };
917
918 static int total = 0;
919 static int speeds = 0;
920
921 int adsi_channel_restore(struct ast_channel *chan)
922 {
923         unsigned char dsp[256];
924         int bytes;
925         int x;
926         unsigned char keyd[6];
927
928         memset(dsp, 0, sizeof(dsp));
929
930         /* Start with initial display setup */
931         bytes = 0;
932         bytes += adsi_set_line(dsp + bytes, ADSI_INFO_PAGE, 1);
933
934         /* Prepare key setup messages */
935
936         if (speeds) {
937                 memset(keyd, 0, sizeof(keyd));
938                 for (x=0;x<speeds;x++) {
939                         keyd[x] = ADSI_SPEED_DIAL + x;
940                 }
941                 bytes += adsi_set_keys(dsp + bytes, keyd);
942         }
943         adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0);
944         return 0;
945
946 }
947
948 int adsi_print(struct ast_channel *chan, char **lines, int *aligns, int voice)
949 {
950         unsigned char buf[4096];
951         int bytes=0;
952         int res;
953         int x;
954         for(x=0;lines[x];x++) 
955                 bytes += adsi_display(buf + bytes, ADSI_INFO_PAGE, x+1, aligns[x], 0, lines[x], "");
956         bytes += adsi_set_line(buf + bytes, ADSI_INFO_PAGE, 1);
957         if (voice) {
958                 bytes += adsi_voice_mode(buf + bytes, 0);
959         }
960         res = adsi_transmit_message_full(chan, buf, bytes, ADSI_MSG_DISPLAY, 0);
961         if (voice) {
962                 /* Ignore the resulting DTMF B announcing it's in voice mode */
963                 ast_waitfordigit(chan, 1000);
964         }
965         return res;
966 }
967
968 int adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
969 {
970         unsigned char dsp[256];
971         int bytes;
972         int res;
973         char resp[2];
974
975         memset(dsp, 0, sizeof(dsp));
976
977         /* Connect to session */
978         bytes = 0;
979         bytes += adsi_connect_session(dsp + bytes, app, ver);
980
981         if (data)
982                 bytes += adsi_data_mode(dsp + bytes);
983
984         /* Prepare key setup messages */
985         if (adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0))
986                 return -1;
987         if (app) {
988                 res = ast_readstring(chan, resp, 1, 1200, 1200, "");
989                 if (res < 0)
990                         return -1;
991                 if (res) {
992                         ast_log(LOG_DEBUG, "No response from CPE about version.  Assuming not there.\n");
993                         return 0;
994                 }
995                 if (!strcmp(resp, "B")) {
996                         ast_log(LOG_DEBUG, "CPE has script '%s' version %d already loaded\n", app, ver);
997                         return 1;
998                 } else if (!strcmp(resp, "A")) {
999                         ast_log(LOG_DEBUG, "CPE hasn't script '%s' version %d already loaded\n", app, ver);
1000                 } else {
1001                         ast_log(LOG_WARNING, "Unexpected CPE response to script query: %s\n", resp);
1002                 }
1003         } else
1004                 return 1;
1005         return 0;
1006
1007 }
1008
1009 int adsi_unload_session(struct ast_channel *chan)
1010 {
1011         unsigned char dsp[256];
1012         int bytes;
1013
1014         memset(dsp, 0, sizeof(dsp));
1015
1016         /* Connect to session */
1017         bytes = 0;
1018         bytes += adsi_disconnect_session(dsp + bytes);
1019         bytes += adsi_voice_mode(dsp + bytes, 0);
1020
1021         /* Prepare key setup messages */
1022         if (adsi_transmit_message_full(chan, dsp, bytes, ADSI_MSG_DISPLAY, 0))
1023                 return -1;
1024         return 0;
1025 }
1026
1027 static int str2align(char *s)
1028 {
1029         if (!strncasecmp(s, "l", 1))
1030                 return ADSI_JUST_LEFT;
1031         else if (!strncasecmp(s, "r", 1))
1032                 return ADSI_JUST_RIGHT;
1033         else if (!strncasecmp(s, "i", 1))
1034                 return ADSI_JUST_IND;
1035         else
1036                 return ADSI_JUST_CENT;
1037 }
1038
1039 static void init_state(void)
1040 {
1041         int x;
1042
1043         for (x=0;x<ADSI_MAX_INTRO;x++)
1044                 aligns[x] = ADSI_JUST_CENT;
1045         strncpy(intro[0], "Welcome to the", sizeof(intro[0]) - 1);
1046         strncpy(intro[1], "Asterisk", sizeof(intro[1]) - 1);
1047         strncpy(intro[2], "Open Source PBX", sizeof(intro[2]) - 1);
1048         total = 3;
1049         speeds = 0;
1050         for (x=3;x<ADSI_MAX_INTRO;x++)
1051                 intro[x][0] = '\0';
1052         memset(speeddial, 0, sizeof(speeddial));
1053         alignment = ADSI_JUST_CENT;
1054 }
1055
1056 static void adsi_load(void)
1057 {
1058         int x;
1059         struct ast_config *conf;
1060         struct ast_variable *v;
1061         char *name, *sname;
1062         init_state();
1063         conf = ast_config_load("adsi.conf");
1064         if (conf) {
1065                 x=0;
1066                 v = ast_variable_browse(conf, "intro");
1067                 while(v) {
1068                         if (!strcasecmp(v->name, "alignment"))
1069                                 alignment = str2align(v->value);
1070                         else if (!strcasecmp(v->name, "greeting")) {
1071                                 if (x < ADSI_MAX_INTRO) {
1072                                         aligns[x] = alignment;
1073                                         strncpy(intro[x], v->value, sizeof(intro[x]) - 1);
1074                                         intro[x][sizeof(intro[x]) - 1] = '\0';
1075                                         x++;
1076                                 }
1077                         } else if (!strcasecmp(v->name, "maxretries")) {
1078                                 if (atoi(v->value) > 0)
1079                                         maxretries = atoi(v->value);
1080                         }
1081                         v = v->next;
1082                 }
1083                 v = ast_variable_browse(conf, "speeddial");
1084                 if (x)
1085                         total = x;
1086                 x = 0;
1087                 while(v) {
1088                         char *stringp=NULL;
1089                         stringp=v->value;
1090                         name = strsep(&stringp, ",");
1091                         sname = strsep(&stringp, ",");
1092                         if (!sname) 
1093                                 sname = name;
1094                         if (x < ADSI_MAX_SPEED_DIAL) {
1095                                 /* Up to 20 digits */
1096                                 strncpy(speeddial[x][0], v->name, sizeof(speeddial[x][0]) - 1);
1097                                 strncpy(speeddial[x][1], name, 18);
1098                                 strncpy(speeddial[x][2], sname, 7);
1099                                 x++;
1100                         }
1101                         v = v->next;
1102                                 
1103                 }
1104                 if (x)
1105                         speeds = x;
1106                 ast_config_destroy(conf);
1107         }
1108 }
1109
1110 int reload(void)
1111 {
1112         adsi_load();
1113         return 0;
1114 }
1115
1116 int load_module(void)
1117 {
1118         adsi_load();
1119         return 0;
1120 }
1121
1122 int unload_module(void)
1123 {
1124         /* Can't unload this once we're loaded */
1125         return -1;
1126 }
1127
1128 const const char *description(void)
1129 {
1130         return "ADSI Resource";
1131 }
1132
1133 int usecount(void)
1134 {
1135         /* We should never be unloaded */
1136         return 1;
1137 }
1138
1139 const const char *key()
1140 {
1141         return ASTERISK_GPL_KEY;
1142 }