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