1 /* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $
3 * DSS1 main diversion supplementary handling for i4l.
5 * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
7 * This software may be used and distributed according to the terms
8 * of the GNU General Public License, incorporated herein by reference.
12 #include <linux/version.h>
13 #include <linux/proc_fs.h>
14 #include "isdn_divert.h"
16 /**********************************/
17 /* structure keeping calling info */
18 /**********************************/
20 { isdn_ctrl ics; /* delivered setup + driver parameters */
21 ulong divert_id; /* Id delivered to user */
22 unsigned char akt_state; /* actual state */
23 char deflect_dest[35]; /* deflection destination */
24 struct timer_list timer; /* timer control structure */
25 char info[90]; /* device info output */
26 struct call_struc *next; /* pointer to next entry */
27 struct call_struc *prev;
31 /********************************************/
32 /* structure keeping deflection table entry */
33 /********************************************/
35 { struct deflect_struc *next,*prev;
36 divert_rule rule; /* used rule */
40 /*****************************************/
41 /* variables for main diversion services */
42 /*****************************************/
43 /* diversion/deflection processes */
44 static struct call_struc *divert_head = NULL; /* head of remembered entrys */
45 static ulong next_id = 1; /* next info id */
46 static struct deflect_struc *table_head = NULL;
47 static struct deflect_struc *table_tail = NULL;
48 static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */
50 /***************************/
51 /* timer callback function */
52 /***************************/
53 static void deflect_timer_expire(ulong arg)
54 { unsigned long flags;
55 struct call_struc *cs = (struct call_struc *) arg;
59 del_timer(&cs->timer); /* delete active timer */
63 { case DEFLECT_PROCEED:
64 cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
65 divert_if.ll_cmd(&cs->ics);
68 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
69 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
70 add_timer(&cs->timer);
75 cs->ics.command = ISDN_CMD_REDIR; /* protocol */
76 strcpy(cs->ics.parm.setup.phone,cs->deflect_dest);
77 strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed");
78 divert_if.ll_cmd(&cs->ics);
81 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
82 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
83 add_timer(&cs->timer);
92 cs->prev->next = cs->next; /* forward link */
94 divert_head = cs->next;
96 cs->next->prev = cs->prev; /* back link */
102 } /* deflect_timer_func */
105 /*****************************************/
106 /* handle call forwarding de/activations */
107 /* 0 = deact, 1 = act, 2 = interrogate */
108 /*****************************************/
109 int cf_command(int drvid, int mode,
110 u_char proc, char *msn,
111 u_char service, char *fwd_nr, ulong *procid)
112 { unsigned long flags;
115 char *p,*ielenp,tmp[60];
116 struct call_struc *cs;
118 if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */
119 if ((proc & 0x7F) > 2) return(-EINVAL);
122 *p++ = 0x30; /* enumeration */
123 ielenp = p++; /* remember total length position */
124 *p++ = 0xa; /* proc tag */
125 *p++ = 1; /* length */
126 *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */
127 *p++ = 0xa; /* service tag */
128 *p++ = 1; /* length */
129 *p++ = service; /* service to handle */
132 { if (!*fwd_nr) return(-EINVAL); /* destination missing */
133 if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */
134 fwd_len = strlen(fwd_nr);
135 *p++ = 0x30; /* number enumeration */
136 *p++ = fwd_len + 2; /* complete forward to len */
137 *p++ = 0x80; /* fwd to nr */
138 *p++ = fwd_len; /* length of number */
139 strcpy(p,fwd_nr); /* copy number */
140 p += fwd_len; /* pointer beyond fwd */
143 msnlen = strlen(msn);
144 *p++ = 0x80; /* msn number */
146 { *p++ = msnlen; /* length */
152 *ielenp = p - ielenp - 1; /* set total IE length */
154 /* allocate mem for information struct */
155 if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
156 return(-ENOMEM); /* no memory */
157 init_timer(&cs->timer);
159 cs->timer.function = deflect_timer_expire;
160 cs->timer.data = (ulong) cs; /* pointer to own structure */
161 cs->ics.driver = drvid;
162 cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */
163 cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */
164 cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */
165 cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */
166 cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */
167 cs->ics.parm.dss1_io.data = tmp; /* start of buffer */
171 cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */
172 restore_flags(flags);
173 *procid = cs->ics.parm.dss1_io.ll_id;
175 sprintf(cs->info,"%d 0x%lx %s%s 0 %s %02x %d%s%s\n",
176 (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,
177 cs->ics.parm.dss1_io.ll_id,
178 (mode != 2) ? "" : "0 ",
179 divert_if.drv_to_name(cs->ics.driver),
183 (mode != 1) ? "" : " 0 ",
184 (mode != 1) ? "" : fwd_nr);
186 retval = divert_if.ll_cmd(&cs->ics); /* excute command */
192 cs->next = divert_head;
194 restore_flags(flags);
202 /****************************************/
203 /* handle a external deflection command */
204 /****************************************/
205 int deflect_extern_action(u_char cmd, ulong callid, char *to_nr)
206 { struct call_struc *cs;
211 if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */
212 cs = divert_head; /* start of parameter list */
214 { if (cs->divert_id == callid) break; /* found */
217 if (!cs) return(-EINVAL); /* invalid callid */
219 ic.driver = cs->ics.driver;
220 ic.arg = cs->ics.arg;
222 if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */
224 { case 0: /* hangup */
225 del_timer(&cs->timer);
226 ic.command = ISDN_CMD_HANGUP;
227 i = divert_if.ll_cmd(&ic);
230 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
231 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
232 add_timer(&cs->timer);
233 restore_flags(flags);
237 if (cs->akt_state == DEFLECT_ALERT) return(0);
238 cmd &= 0x7F; /* never wait */
239 del_timer(&cs->timer);
240 ic.command = ISDN_CMD_ALERT;
241 if ((i = divert_if.ll_cmd(&ic)))
244 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
245 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
246 add_timer(&cs->timer);
247 restore_flags(flags);
250 cs->akt_state = DEFLECT_ALERT;
254 del_timer(&cs->timer);
255 strcpy(cs->ics.parm.setup.phone, to_nr);
256 strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");
257 ic.command = ISDN_CMD_REDIR;
258 if ((i = divert_if.ll_cmd(&ic)))
261 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
262 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
263 add_timer(&cs->timer);
264 restore_flags(flags);
267 cs->akt_state = DEFLECT_ALERT;
272 } /* deflect_extern_action */
274 /********************************/
275 /* insert a new rule before idx */
276 /********************************/
277 int insertrule(int idx, divert_rule *newrule)
278 { struct deflect_struc *ds,*ds1=NULL;
281 if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc),
283 return(-ENOMEM); /* no memory */
285 ds->rule = *newrule; /* set rule */
292 while ((ds1) && (idx > 0))
300 { ds->prev = table_tail; /* previous entry */
301 ds->next = NULL; /* end of chain */
303 ds->prev->next = ds; /* last forward */
305 table_head = ds; /* is first entry */
306 table_tail = ds; /* end of queue */
309 { ds->next = ds1; /* next entry */
310 ds->prev = ds1->prev; /* prev entry */
311 ds1->prev = ds; /* backward chain old element */
313 table_head = ds; /* first element */
316 restore_flags(flags);
320 /***********************************/
321 /* delete the rule at position idx */
322 /***********************************/
323 int deleterule(int idx)
324 { struct deflect_struc *ds,*ds1;
333 restore_flags(flags);
346 while ((ds) && (idx > 0))
352 { restore_flags(flags);
357 ds->next->prev = ds->prev; /* backward chain */
359 table_tail = ds->prev; /* end of chain */
362 ds->prev->next = ds->next; /* forward chain */
364 table_head = ds->next; /* start of chain */
366 restore_flags(flags);
371 /*******************************************/
372 /* get a pointer to a specific rule number */
373 /*******************************************/
374 divert_rule *getruleptr(int idx)
375 { struct deflect_struc *ds = table_head;
377 if (idx < 0) return(NULL);
378 while ((ds) && (idx >= 0))
388 /*************************************************/
389 /* called from common module on an incoming call */
390 /*************************************************/
391 int isdn_divert_icall(isdn_ctrl *ic)
394 struct call_struc *cs = NULL;
395 struct deflect_struc *dv;
399 /* first check the internal deflection table */
400 for (dv = table_head; dv ; dv = dv->next )
402 if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) ||
403 ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL)))
404 continue; /* call option check */
405 if (!(dv->rule.drvid & (1L << ic->driver)))
406 continue; /* driver not matching */
407 if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1))
408 continue; /* si1 not matching */
409 if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2))
410 continue; /* si2 not matching */
413 p1 = ic->parm.setup.eazmsn;
416 { /* complete compare */
418 { accept = 1; /* call accepted */
422 break; /* not accepted */
425 } /* complete compare */
426 if (!accept) continue; /* not accepted */
428 if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0]))
429 { p = dv->rule.caller;
430 p1 = ic->parm.setup.phone;
433 { /* complete compare */
435 { accept = 1; /* call accepted */
439 break; /* not accepted */
442 } /* complete compare */
443 if (!accept) continue; /* not accepted */
446 switch (dv->rule.action)
447 { case DEFLECT_IGNORE:
452 case DEFLECT_PROCEED:
455 if (dv->rule.action == DEFLECT_PROCEED)
456 if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))
457 return(0); /* no external deflection needed */
458 if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
459 return(0); /* no memory */
460 init_timer(&cs->timer);
462 cs->timer.function = deflect_timer_expire;
463 cs->timer.data = (ulong) cs; /* pointer to own structure */
465 cs->ics = *ic; /* copy incoming data */
466 if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0");
467 if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0");
468 cs->ics.parm.setup.screen = dv->rule.screen;
469 if (dv->rule.waittime)
470 cs->timer.expires = jiffies + (HZ * dv->rule.waittime);
472 if (dv->rule.action == DEFLECT_PROCEED)
473 cs->timer.expires = jiffies + (HZ * extern_wait_max);
475 cs->timer.expires = 0;
476 cs->akt_state = dv->rule.action;
479 cs->divert_id = next_id++; /* new sequence number */
480 restore_flags(flags);
482 if (cs->akt_state == DEFLECT_ALERT)
483 { strcpy(cs->deflect_dest,dv->rule.to_nr);
484 if (!cs->timer.expires)
485 { strcpy(ic->parm.setup.eazmsn,"Testtext direct");
486 ic->parm.setup.screen = dv->rule.screen;
487 strcpy(ic->parm.setup.phone,dv->rule.to_nr);
488 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
489 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
493 retval = 1; /* alerting */
496 { cs->deflect_dest[0] = '\0';
497 retval = 4; /* only proceed */
499 sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
502 divert_if.drv_to_name(cs->ics.driver),
503 (ic->command == ISDN_STAT_ICALLW) ? "1":"0",
504 cs->ics.parm.setup.phone,
505 cs->ics.parm.setup.eazmsn,
506 cs->ics.parm.setup.si1,
507 cs->ics.parm.setup.si2,
508 cs->ics.parm.setup.screen,
511 if ((dv->rule.action == DEFLECT_REPORT) ||
512 (dv->rule.action == DEFLECT_REJECT))
513 { put_info_buffer(cs->info);
514 kfree(cs); /* remove */
515 return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */
520 return(0); /* ignore call */
522 } /* switch action */
530 cs->next = divert_head;
532 if (cs->timer.expires) add_timer(&cs->timer);
533 restore_flags(flags);
535 put_info_buffer(cs->info);
540 } /* isdn_divert_icall */
543 void deleteprocs(void)
544 { struct call_struc *cs, *cs1;
552 { del_timer(&cs->timer);
557 restore_flags(flags);
560 /****************************************************/
561 /* put a address including address type into buffer */
562 /****************************************************/
563 int put_address(char *st, u_char *p, int len)
565 u_char adr_typ = 0; /* network standard */
567 if (len < 2) return(retval);
569 { retval = *(++p) + 2; /* total length */
570 if (retval > len) return(0); /* too short */
571 len = retval - 2; /* remaining length */
572 if (len < 3) return(0);
573 if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0);
577 if (len < 2) return(0);
578 if (*p++ != 0x12) return(0);
579 if (*p > len) return(0); /* check number length */
584 { retval = *(++p) + 2; /* total length */
585 if (retval > len) return(0);
590 return(0); /* invalid address information */
592 sprintf(st,"%d ",adr_typ);
603 /*************************************/
604 /* report a succesfull interrogation */
605 /*************************************/
606 int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
607 { char *src = ic->parm.dss1_io.data;
608 int restlen = ic->parm.dss1_io.datalen;
611 char st[90], *p, *stp;
613 if (restlen < 2) return(-100); /* frame too short */
614 if (*src++ != 0x30) return(-101);
615 if ((n = *src++) > 0x81) return(-102); /* invalid length field */
616 restlen -= 2; /* remaining bytes */
618 { if (restlen < 2) return(-103);
619 if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);
626 if (n > restlen) return(-105);
630 if (n > restlen) return(-106);
632 restlen = n; /* standard format */
633 if (restlen < 3) return(-107); /* no procedure */
634 if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);
636 if (restlen < 2) return(-109); /* list missing */
639 if ((n = *src++) > 0x81) return(-110); /* invalid length field */
640 restlen -= 2; /* remaining bytes */
642 { if (restlen < 2) return(-111);
643 if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);
650 if (n > restlen) return(-113);
654 if (n > restlen) return(-114);
656 restlen = n; /* standard format */
657 } /* result list header */
661 sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id,
662 cnt++,divert_if.drv_to_name(ic->driver));
664 if (*src++ != 0x30) return(-115); /* invalid enum */
667 if (n > restlen) return(-116); /* enum length wrong */
669 p = src; /* one entry */
671 if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
675 if (n < 6) continue; /* no service and proc */
676 if ((*p++ != 0x0A) || (*p++ != 1)) continue;
677 sprintf(stp," 0x%02x ",(*p++) & 0xFF);
679 if ((*p++ != 0x0A) || (*p++ != 1)) continue;
680 sprintf(stp,"%d ",(*p++) & 0xFF);
684 { if (*p++ != 0x30) continue;
685 if (*p > (n-2)) continue;
687 if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
692 } /* while restlen */
693 if (restlen) return(-117);
695 } /* interrogate_success */
697 /*********************************************/
698 /* callback for protocol specific extensions */
699 /*********************************************/
700 int prot_stat_callback(isdn_ctrl *ic)
701 { struct call_struc *cs, *cs1;
705 cs = divert_head; /* start of list */
708 { if (ic->driver == cs->ics.driver)
709 { switch (cs->ics.arg)
710 { case DSS1_CMD_INVOKE:
711 if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) &&
712 (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id))
714 { case DSS1_STAT_INVOKE_ERR:
715 sprintf(cs->info,"128 0x%lx 0x%x\n",
716 ic->parm.dss1_io.ll_id,
717 ic->parm.dss1_io.timeout);
718 put_info_buffer(cs->info);
721 case DSS1_STAT_INVOKE_RES:
722 switch (cs->ics.parm.dss1_io.proc)
725 put_info_buffer(cs->info);
729 i = interrogate_success(ic,cs);
731 sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT,
732 ic->parm.dss1_io.ll_id,i);
733 put_info_buffer(cs->info);
737 printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);
745 printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);
748 cs1 = cs; /* remember structure */
750 continue; /* abort search */
754 case DSS1_CMD_INVOKE_ABORT:
755 printk(KERN_WARNING "dss1_divert unhandled invoke abort\n");
759 printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg);
761 } /* switch ics.arg */
767 { printk(KERN_WARNING "dss1_divert unhandled process\n");
771 if (cs1->ics.driver == -1)
774 del_timer(&cs1->timer);
776 cs1->prev->next = cs1->next; /* forward link */
778 divert_head = cs1->next;
780 cs1->next->prev = cs1->prev; /* back link */
781 restore_flags(flags);
786 } /* prot_stat_callback */
789 /***************************/
790 /* status callback from HL */
791 /***************************/
792 int isdn_divert_stat_callback(isdn_ctrl *ic)
793 { struct call_struc *cs, *cs1;
798 cs = divert_head; /* start of list */
800 { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg))
801 { switch (ic->command)
802 { case ISDN_STAT_DHUP:
803 sprintf(cs->info,"129 0x%lx\n",cs->divert_id);
804 del_timer(&cs->timer);
808 case ISDN_STAT_CAUSE:
809 sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);
812 case ISDN_STAT_REDIR:
813 sprintf(cs->info,"131 0x%lx\n",cs->divert_id);
814 del_timer(&cs->timer);
819 sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));
822 put_info_buffer(cs->info);
827 if (cs1->ics.driver == -1)
832 cs1->prev->next = cs1->next; /* forward link */
834 divert_head = cs1->next;
836 cs1->next->prev = cs1->prev; /* back link */
837 restore_flags(flags);
841 return(retval); /* not found */
842 } /* isdn_divert_stat_callback */
845 /********************/
846 /* callback from ll */
847 /********************/
848 int ll_callback(isdn_ctrl *ic)
851 { case ISDN_STAT_ICALL:
852 case ISDN_STAT_ICALLW:
853 return(isdn_divert_icall(ic));
857 if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)
858 { if (ic->arg != DSS1_STAT_INVOKE_BRD)
859 return(prot_stat_callback(ic));
861 return(0); /* DSS1 invoke broadcast */
864 return(-1); /* protocol not euro */
867 return(isdn_divert_stat_callback(ic));