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