ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / isdn / divert / isdn_divert.c
1 /* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $
2  *
3  * DSS1 main diversion supplementary handling for i4l.
4  *
5  * Copyright 1999       by Werner Cornelius (werner@isdn4linux.de)
6  * 
7  * This software may be used and distributed according to the terms
8  * of the GNU General Public License, incorporated herein by reference.
9  *
10  */
11
12 #include <linux/version.h>
13 #include <linux/proc_fs.h>
14 #include "isdn_divert.h"
15
16 /**********************************/
17 /* structure keeping calling info */
18 /**********************************/
19 struct call_struc
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;
28   };
29
30
31 /********************************************/
32 /* structure keeping deflection table entry */
33 /********************************************/
34 struct deflect_struc
35   { struct deflect_struc *next,*prev; 
36     divert_rule rule; /* used rule */
37   };
38
39
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 */ 
49
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;
56
57   save_flags(flags);
58   cli();
59   del_timer(&cs->timer); /* delete active timer */
60   restore_flags(flags);
61
62   switch(cs->akt_state)
63    { case DEFLECT_PROCEED:
64        cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
65        divert_if.ll_cmd(&cs->ics);                        
66        save_flags(flags);
67        cli();
68        cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
69        cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
70        add_timer(&cs->timer);
71        restore_flags(flags); 
72        break;
73
74      case DEFLECT_ALERT:
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);                        
79        save_flags(flags);
80        cli();
81        cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
82        cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
83        add_timer(&cs->timer);
84        restore_flags(flags); 
85        break;
86
87      case DEFLECT_AUTODEL:
88      default:
89        save_flags(flags);
90        cli();
91        if (cs->prev) 
92          cs->prev->next = cs->next; /* forward link */
93         else
94          divert_head = cs->next;
95        if (cs->next)
96          cs->next->prev = cs->prev; /* back link */           
97        restore_flags(flags); 
98        kfree(cs);
99        return;
100
101    } /* switch */
102 } /* deflect_timer_func */
103
104
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;
113   int retval,msnlen;
114   int fwd_len;
115   char *p,*ielenp,tmp[60];
116   struct call_struc *cs;
117
118   if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */
119   if ((proc & 0x7F) > 2) return(-EINVAL);
120   proc &= 3;
121   p = tmp;
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 */
130
131   if (mode == 1) 
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 */
141    } /* activate */
142
143   msnlen = strlen(msn);
144   *p++ = 0x80; /* msn number */
145   if (msnlen > 1)
146    { *p++ = msnlen; /* length */
147      strcpy(p,msn);
148      p += msnlen;
149    }
150   else *p++ = 0;
151
152   *ielenp = p - ielenp - 1; /* set total IE length */ 
153
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);
158   cs->info[0] = '\0';
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 */
168   
169   save_flags(flags);
170   cli();
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;  
174
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),
180           msn,
181           service & 0xFF,
182           proc,
183           (mode != 1) ? "" : " 0 ",
184           (mode != 1) ? "" : fwd_nr);
185  
186   retval = divert_if.ll_cmd(&cs->ics); /* excute command */
187
188   if (!retval)
189    { cs->prev = NULL;
190      save_flags(flags);
191      cli();
192      cs->next = divert_head;
193      divert_head = cs; 
194      restore_flags(flags);
195    }
196   else
197    kfree(cs);
198   return(retval); 
199 } /* cf_command */
200
201
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;
207   isdn_ctrl ic;
208   unsigned long flags;
209   int i;
210
211   if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */
212   cs = divert_head; /* start of parameter list */
213   while (cs)
214    { if (cs->divert_id == callid) break; /* found */
215      cs = cs->next;  
216    } /* search entry */
217   if (!cs) return(-EINVAL); /* invalid callid */
218
219   ic.driver = cs->ics.driver;
220   ic.arg = cs->ics.arg;
221   i = -EINVAL;
222   if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */
223   switch (cmd & 0x7F)
224    { case 0: /* hangup */
225        del_timer(&cs->timer); 
226        ic.command = ISDN_CMD_HANGUP;
227        i = divert_if.ll_cmd(&ic);                         
228        save_flags(flags);
229        cli();
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); 
234      break;      
235
236      case 1: /* alert */
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)))
242         { save_flags(flags);
243           cli();
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);
248         }
249        else
250           cs->akt_state = DEFLECT_ALERT; 
251      break;      
252
253      case 2: /* redir */
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)))
259         { save_flags(flags);
260           cli();
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);
265         }
266        else
267           cs->akt_state = DEFLECT_ALERT; 
268      break;      
269
270    } /* switch */
271   return(i);
272 } /* deflect_extern_action */
273
274 /********************************/
275 /* insert a new rule before idx */
276 /********************************/
277 int insertrule(int idx, divert_rule *newrule)
278 { struct deflect_struc *ds,*ds1=NULL;
279   unsigned long flags;
280
281   if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc), 
282                                               GFP_KERNEL))) 
283     return(-ENOMEM); /* no memory */
284
285   ds->rule = *newrule; /* set rule */
286
287   save_flags(flags);
288   cli();
289
290   if (idx >= 0)
291    { ds1 = table_head;
292      while ((ds1) && (idx > 0))
293       { idx--;
294         ds1 = ds1->next;
295       } 
296      if (!ds1) idx = -1; 
297    }
298
299   if (idx < 0)
300    { ds->prev = table_tail; /* previous entry */
301      ds->next = NULL; /* end of chain */
302      if (ds->prev) 
303        ds->prev->next = ds; /* last forward */
304       else
305         table_head = ds; /* is first entry */
306      table_tail = ds; /* end of queue */
307    }
308   else
309     { ds->next = ds1; /* next entry */
310       ds->prev = ds1->prev; /* prev entry */
311       ds1->prev = ds; /* backward chain old element */
312       if (!ds->prev)
313         table_head = ds; /* first element */
314    }
315
316   restore_flags(flags);
317   return(0);
318 } /* insertrule */
319
320 /***********************************/
321 /* delete the rule at position idx */
322 /***********************************/
323 int deleterule(int idx)
324 { struct deflect_struc *ds,*ds1;
325   unsigned long flags;
326   
327   if (idx < 0) 
328    { save_flags(flags);
329      cli();
330      ds = table_head;
331      table_head = NULL;
332      table_tail = NULL;
333      restore_flags(flags);
334      while (ds)
335       { ds1 = ds; 
336         ds = ds->next;
337         kfree(ds1);
338       } 
339      return(0); 
340    }
341
342   save_flags(flags);
343   cli();
344   ds = table_head;
345
346   while ((ds) && (idx > 0))
347    { idx--; 
348      ds = ds->next;  
349    }
350
351   if (!ds) 
352    { restore_flags(flags);
353      return(-EINVAL);
354    }  
355
356   if (ds->next) 
357     ds->next->prev = ds->prev; /* backward chain */
358    else
359      table_tail = ds->prev; /* end of chain */
360
361   if (ds->prev)
362     ds->prev->next = ds->next; /* forward chain */
363    else
364      table_head = ds->next; /* start of chain */      
365   
366   restore_flags(flags);
367   kfree(ds);
368   return(0);
369 } /* deleterule */
370
371 /*******************************************/
372 /* get a pointer to a specific rule number */
373 /*******************************************/
374 divert_rule *getruleptr(int idx)
375 { struct deflect_struc *ds = table_head;
376   
377   if (idx < 0) return(NULL);
378   while ((ds) && (idx >= 0))
379    { if (!(idx--)) 
380       { return(&ds->rule);
381         break;
382       }
383      ds = ds->next;  
384    }
385   return(NULL);
386 } /* getruleptr */
387
388 /*************************************************/
389 /* called from common module on an incoming call */
390 /*************************************************/
391 int isdn_divert_icall(isdn_ctrl *ic)
392 { int retval = 0;
393   unsigned long flags;
394   struct call_struc *cs = NULL; 
395   struct deflect_struc *dv;
396   char *p,*p1;
397   u_char accept;
398
399   /* first check the internal deflection table */
400   for (dv = table_head; dv ; dv = dv->next )
401    { /* scan table */
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 */
411
412      p = dv->rule.my_msn;
413      p1 = ic->parm.setup.eazmsn;
414      accept = 0;
415      while (*p)
416       { /* complete compare */
417         if (*p == '-')
418           { accept = 1; /* call accepted */
419             break;
420           }
421         if (*p++ != *p1++) 
422           break; /* not accepted */
423         if ((!*p) && (!*p1))
424           accept = 1;
425       } /* complete compare */
426      if (!accept) continue; /* not accepted */
427  
428      if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0]))
429       { p = dv->rule.caller;
430         p1 = ic->parm.setup.phone;
431         accept = 0;
432         while (*p)
433          { /* complete compare */
434            if (*p == '-')
435             { accept = 1; /* call accepted */
436               break;
437             }
438            if (*p++ != *p1++) 
439              break; /* not accepted */
440            if ((!*p) && (!*p1))
441              accept = 1;
442          } /* complete compare */
443         if (!accept) continue; /* not accepted */
444       }  
445
446      switch (dv->rule.action)
447        { case DEFLECT_IGNORE:
448            return(0);
449            break;
450
451          case DEFLECT_ALERT:
452          case DEFLECT_PROCEED:
453          case DEFLECT_REPORT:
454          case DEFLECT_REJECT:
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);
461            cs->info[0] = '\0';
462            cs->timer.function = deflect_timer_expire;
463            cs->timer.data = (ulong) cs; /* pointer to own structure */
464            
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);
471            else
472             if (dv->rule.action == DEFLECT_PROCEED)
473               cs->timer.expires = jiffies + (HZ * extern_wait_max); 
474             else  
475               cs->timer.expires = 0;
476            cs->akt_state = dv->rule.action;                
477            save_flags(flags);
478            cli();
479            cs->divert_id = next_id++; /* new sequence number */
480            restore_flags(flags);
481            cs->prev = NULL;
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);
490                    retval = 5; 
491                  }
492                else
493                  retval = 1; /* alerting */                 
494              }
495            else
496              { cs->deflect_dest[0] = '\0';
497                retval = 4; /* only proceed */
498              }  
499            sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
500                    cs->akt_state,
501                    cs->divert_id,
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,
509                    dv->rule.waittime,
510                    cs->deflect_dest);
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 */ 
516             }              
517            break;
518   
519          default:
520            return(0); /* ignore call */
521            break;
522        } /* switch action */    
523      break; 
524    } /* scan_table */
525
526   if (cs) 
527    { cs->prev = NULL;
528      save_flags(flags);
529      cli();
530      cs->next = divert_head;
531      divert_head = cs; 
532      if (cs->timer.expires) add_timer(&cs->timer);
533      restore_flags(flags);
534
535      put_info_buffer(cs->info); 
536      return(retval);
537    }
538   else
539      return(0);
540 } /* isdn_divert_icall */
541
542
543 void deleteprocs(void)
544 { struct call_struc *cs, *cs1; 
545   unsigned long flags;
546
547   save_flags(flags);
548   cli();
549   cs = divert_head;
550   divert_head = NULL;
551   while (cs)
552    { del_timer(&cs->timer);
553      cs1 = cs;
554      cs = cs->next;
555      kfree(cs1);
556    } 
557   restore_flags(flags);
558 } /* deleteprocs */
559
560 /****************************************************/
561 /* put a address including address type into buffer */
562 /****************************************************/
563 int put_address(char *st, u_char *p, int len)
564 { u_char retval = 0;
565   u_char adr_typ = 0; /* network standard */
566
567   if (len < 2) return(retval);
568   if (*p == 0xA1)
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);
574      adr_typ = *(++p);
575      len -= 3;
576      p++;
577      if (len < 2) return(0);
578      if (*p++ != 0x12) return(0);
579      if (*p > len) return(0); /* check number length */
580      len = *p++;
581    }   
582   else
583    if (*p == 0x80)
584     { retval = *(++p) + 2; /* total length */
585       if (retval > len) return(0);
586       len = retval - 2;
587       p++;
588     }
589    else  
590     return(0); /* invalid address information */
591
592   sprintf(st,"%d ",adr_typ);
593   st += strlen(st);
594   if (!len) 
595     *st++ = '-';
596   else
597    while (len--)
598      *st++ = *p++;
599   *st = '\0';
600   return(retval);
601 } /* put_address */
602
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;
609   int cnt = 1;
610   u_char n,n1;
611   char st[90], *p, *stp;
612
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 */
617   if (n == 0x80)
618    { if (restlen < 2) return(-103);
619      if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);
620      restlen -= 2;
621    }
622   else
623    if ( n == 0x81)
624     { n = *src++;
625       restlen--;
626       if (n > restlen) return(-105);
627       restlen = n;
628     }
629    else
630     if (n > restlen) return(-106);
631      else 
632       restlen = n; /* standard format */   
633   if (restlen < 3) return(-107); /* no procedure */
634   if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);
635   restlen -= 3; 
636   if (restlen < 2) return(-109); /* list missing */
637   if (*src == 0x31)
638    { src++; 
639      if ((n = *src++) > 0x81) return(-110); /* invalid length field */
640      restlen -= 2; /* remaining bytes */
641      if (n == 0x80)
642       { if (restlen < 2) return(-111);
643         if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);
644         restlen -= 2;
645       }
646      else
647       if ( n == 0x81)
648        { n = *src++;
649          restlen--;
650          if (n > restlen) return(-113);
651          restlen = n;
652        }
653       else
654        if (n > restlen) return(-114);
655         else 
656          restlen = n; /* standard format */   
657    } /* result list header */ 
658
659   while (restlen >= 2)
660    { stp = st;
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));
663      stp += strlen(stp);
664      if (*src++ != 0x30) return(-115); /* invalid enum */
665      n = *src++;
666      restlen -= 2;
667      if (n > restlen) return(-116); /* enum length wrong */
668      restlen -= n;
669      p = src; /* one entry */
670      src += n;
671      if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
672      stp += strlen(stp);
673      p += n1;
674      n -= n1;
675      if (n < 6) continue; /* no service and proc */
676      if ((*p++ != 0x0A) || (*p++ != 1)) continue;
677      sprintf(stp," 0x%02x ",(*p++) & 0xFF);
678      stp += strlen(stp);
679      if ((*p++ != 0x0A) || (*p++ != 1)) continue;
680      sprintf(stp,"%d ",(*p++) & 0xFF);
681      stp += strlen(stp);
682      n -= 6;
683      if (n > 2)
684       { if (*p++ != 0x30) continue;
685         if (*p > (n-2)) continue;
686         n = *p++;
687         if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
688         stp += strlen(stp);
689       }
690      sprintf(stp,"\n");
691      put_info_buffer(st);
692    } /* while restlen */
693   if (restlen) return(-117);
694   return(0);   
695 } /* interrogate_success */
696
697 /*********************************************/
698 /* callback for protocol specific extensions */
699 /*********************************************/
700 int prot_stat_callback(isdn_ctrl *ic)
701 { struct call_struc *cs, *cs1;
702   int i;
703   unsigned long flags;
704
705   cs = divert_head; /* start of list */
706   cs1 = NULL;
707   while (cs)
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))
713               { switch (ic->arg)
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);
719                    break;
720                    
721                    case DSS1_STAT_INVOKE_RES:
722                      switch (cs->ics.parm.dss1_io.proc)
723                       {  case  7:
724                          case  8:
725                             put_info_buffer(cs->info); 
726                            break;
727                        
728                          case  11:
729                            i = interrogate_success(ic,cs);
730                            if (i)
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); 
734                            break;
735                        
736                          default: 
737                            printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);
738                            break;
739                       } 
740
741
742                    break;
743  
744                    default:
745                      printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);
746                    break;  
747                  } 
748                 cs1 = cs; /* remember structure */
749                 cs = NULL; 
750                 continue; /* abort search */
751               } /* id found */ 
752            break;
753    
754            case DSS1_CMD_INVOKE_ABORT:
755              printk(KERN_WARNING "dss1_divert unhandled invoke abort\n"); 
756            break;   
757          
758            default:
759              printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg); 
760            break; 
761          } /* switch ics.arg */ 
762         cs = cs->next; 
763       } /* driver ok */
764    }  
765    
766   if (!cs1) 
767    { printk(KERN_WARNING "dss1_divert unhandled process\n");
768      return(0);
769    }  
770
771   if (cs1->ics.driver == -1)
772    { save_flags(flags);
773      cli();
774      del_timer(&cs1->timer);
775      if (cs1->prev) 
776        cs1->prev->next = cs1->next; /* forward link */
777      else
778        divert_head = cs1->next;
779      if (cs1->next)
780        cs1->next->prev = cs1->prev; /* back link */           
781      restore_flags(flags); 
782      kfree(cs1);
783    } 
784
785   return(0);
786 } /* prot_stat_callback */
787
788
789 /***************************/
790 /* status callback from HL */
791 /***************************/
792 int isdn_divert_stat_callback(isdn_ctrl *ic)
793 { struct call_struc *cs, *cs1;
794   unsigned long flags;
795   int retval;
796
797   retval = -1;
798   cs = divert_head; /* start of list */
799      while (cs)
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);
805                 cs->ics.driver = -1;
806                 break;
807
808               case ISDN_STAT_CAUSE:
809                 sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);
810                 break;
811
812               case ISDN_STAT_REDIR:
813                 sprintf(cs->info,"131 0x%lx\n",cs->divert_id);
814                 del_timer(&cs->timer);
815                 cs->ics.driver = -1;
816                 break; 
817
818               default:
819                 sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));
820                 break; 
821             }
822           put_info_buffer(cs->info);
823           retval = 0; 
824          }
825         cs1 = cs; 
826         cs = cs->next;
827         if (cs1->ics.driver == -1)
828           { 
829             save_flags(flags);
830             cli();
831             if (cs1->prev) 
832               cs1->prev->next = cs1->next; /* forward link */
833             else
834               divert_head = cs1->next;
835             if (cs1->next)
836               cs1->next->prev = cs1->prev; /* back link */           
837             restore_flags(flags); 
838             kfree(cs1);
839           } 
840       }  
841   return(retval); /* not found */
842 } /* isdn_divert_stat_callback */ 
843
844
845 /********************/
846 /* callback from ll */
847 /********************/ 
848 int ll_callback(isdn_ctrl *ic)
849 {
850   switch (ic->command)
851    { case ISDN_STAT_ICALL:
852      case ISDN_STAT_ICALLW:
853        return(isdn_divert_icall(ic));
854      break;
855
856      case ISDN_STAT_PROT:
857        if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)
858         { if (ic->arg != DSS1_STAT_INVOKE_BRD)
859             return(prot_stat_callback(ic));
860           else
861             return(0); /* DSS1 invoke broadcast */
862         }
863        else
864          return(-1); /* protocol not euro */    
865
866      default:
867        return(isdn_divert_stat_callback(ic));
868    }
869 } /* ll_callback */
870