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/proc_fs.h>
13#include <linux/slab.h>
14#include <linux/timer.h>
15#include <linux/jiffies.h>
16
17#include "isdn_divert.h"
18
19/**********************************/
20/* structure keeping calling info */
21/**********************************/
22struct call_struc
23  { isdn_ctrl ics; /* delivered setup + driver parameters */
24    ulong divert_id; /* Id delivered to user */
25    unsigned char akt_state; /* actual state */
26    char deflect_dest[35]; /* deflection destination */
27    struct timer_list timer; /* timer control structure */
28    char info[90]; /* device info output */
29    struct call_struc *next; /* pointer to next entry */
30    struct call_struc *prev;
31  };
32
33
34/********************************************/
35/* structure keeping deflection table entry */
36/********************************************/
37struct deflect_struc
38  { struct deflect_struc *next,*prev;
39    divert_rule rule; /* used rule */
40  };
41
42
43/*****************************************/
44/* variables for main diversion services */
45/*****************************************/
46/* diversion/deflection processes */
47static struct call_struc *divert_head = NULL; /* head of remembered entrys */
48static ulong next_id = 1; /* next info id */
49static struct deflect_struc *table_head = NULL;
50static struct deflect_struc *table_tail = NULL;
51static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */
52
53DEFINE_SPINLOCK(divert_lock);
54
55/***************************/
56/* timer callback function */
57/***************************/
58static void deflect_timer_expire(ulong arg)
59{
60  unsigned long flags;
61  struct call_struc *cs = (struct call_struc *) arg;
62
63  spin_lock_irqsave(&divert_lock, flags);
64  del_timer(&cs->timer); /* delete active timer */
65  spin_unlock_irqrestore(&divert_lock, flags);
66
67  switch(cs->akt_state)
68   { case DEFLECT_PROCEED:
69       cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
70       divert_if.ll_cmd(&cs->ics);
71       spin_lock_irqsave(&divert_lock, flags);
72       cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
73       cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
74       add_timer(&cs->timer);
75       spin_unlock_irqrestore(&divert_lock, flags);
76       break;
77
78     case DEFLECT_ALERT:
79       cs->ics.command = ISDN_CMD_REDIR; /* protocol */
80       strlcpy(cs->ics.parm.setup.phone, cs->deflect_dest, sizeof(cs->ics.parm.setup.phone));
81       strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed");
82       divert_if.ll_cmd(&cs->ics);
83       spin_lock_irqsave(&divert_lock, flags);
84       cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
85       cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
86       add_timer(&cs->timer);
87       spin_unlock_irqrestore(&divert_lock, flags);
88       break;
89
90     case DEFLECT_AUTODEL:
91     default:
92       spin_lock_irqsave(&divert_lock, flags);
93       if (cs->prev)
94         cs->prev->next = cs->next; /* forward link */
95        else
96         divert_head = cs->next;
97       if (cs->next)
98         cs->next->prev = cs->prev; /* back link */
99       spin_unlock_irqrestore(&divert_lock, flags);
100       kfree(cs);
101       return;
102
103   } /* switch */
104} /* deflect_timer_func */
105
106
107/*****************************************/
108/* handle call forwarding de/activations */
109/* 0 = deact, 1 = act, 2 = interrogate   */
110/*****************************************/
111int cf_command(int drvid, int mode,
112               u_char proc, char *msn,
113               u_char service, char *fwd_nr, ulong *procid)
114{ unsigned long flags;
115  int retval,msnlen;
116  int fwd_len;
117  char *p,*ielenp,tmp[60];
118  struct call_struc *cs;
119
120  if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */
121  if ((proc & 0x7F) > 2) return(-EINVAL);
122  proc &= 3;
123  p = tmp;
124  *p++ = 0x30; /* enumeration */
125  ielenp = p++; /* remember total length position */
126  *p++ = 0xa; /* proc tag */
127  *p++ = 1;   /* length */
128  *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */
129  *p++ = 0xa; /* service tag */
130  *p++ = 1;   /* length */
131  *p++ = service; /* service to handle */
132
133  if (mode == 1)
134   { if (!*fwd_nr) return(-EINVAL); /* destination missing */
135     if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */
136     fwd_len = strlen(fwd_nr);
137     *p++ = 0x30; /* number enumeration */
138     *p++ = fwd_len + 2; /* complete forward to len */
139     *p++ = 0x80; /* fwd to nr */
140     *p++ = fwd_len; /* length of number */
141     strcpy(p,fwd_nr); /* copy number */
142     p += fwd_len; /* pointer beyond fwd */
143   } /* activate */
144
145  msnlen = strlen(msn);
146  *p++ = 0x80; /* msn number */
147  if (msnlen > 1)
148   { *p++ = msnlen; /* length */
149     strcpy(p,msn);
150     p += msnlen;
151   }
152  else *p++ = 0;
153
154  *ielenp = p - ielenp - 1; /* set total IE length */
155
156  /* allocate mem for information struct */
157  if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
158             return(-ENOMEM); /* no memory */
159  init_timer(&cs->timer);
160  cs->info[0] = '\0';
161  cs->timer.function = deflect_timer_expire;
162  cs->timer.data = (ulong) cs; /* pointer to own structure */
163  cs->ics.driver = drvid;
164  cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */
165  cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */
166  cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */
167  cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */
168  cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */
169  cs->ics.parm.dss1_io.data = tmp; /* start of buffer */
170
171  spin_lock_irqsave(&divert_lock, flags);
172  cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */
173  spin_unlock_irqrestore(&divert_lock, flags);
174  *procid = cs->ics.parm.dss1_io.ll_id;
175
176  sprintf(cs->info,"%d 0x%lx %s%s 0 %s %02x %d%s%s\n",
177	  (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,
178          cs->ics.parm.dss1_io.ll_id,
179          (mode != 2) ? "" : "0 ",
180          divert_if.drv_to_name(cs->ics.driver),
181          msn,
182          service & 0xFF,
183          proc,
184          (mode != 1) ? "" : " 0 ",
185          (mode != 1) ? "" : fwd_nr);
186
187  retval = divert_if.ll_cmd(&cs->ics); /* execute command */
188
189  if (!retval)
190   { cs->prev = NULL;
191     spin_lock_irqsave(&divert_lock, flags);
192     cs->next = divert_head;
193     divert_head = cs;
194     spin_unlock_irqrestore(&divert_lock, flags);
195   }
196  else
197   kfree(cs);
198  return(retval);
199} /* cf_command */
200
201
202/****************************************/
203/* handle a external deflection command */
204/****************************************/
205int 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       spin_lock_irqsave(&divert_lock, flags);
229       cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
230       cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
231       add_timer(&cs->timer);
232       spin_unlock_irqrestore(&divert_lock, flags);
233     break;
234
235     case 1: /* alert */
236       if (cs->akt_state == DEFLECT_ALERT) return(0);
237       cmd &= 0x7F; /* never wait */
238       del_timer(&cs->timer);
239       ic.command = ISDN_CMD_ALERT;
240       if ((i = divert_if.ll_cmd(&ic)))
241	{
242          spin_lock_irqsave(&divert_lock, flags);
243          cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
244          cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
245          add_timer(&cs->timer);
246          spin_unlock_irqrestore(&divert_lock, flags);
247        }
248       else
249          cs->akt_state = DEFLECT_ALERT;
250     break;
251
252     case 2: /* redir */
253       del_timer(&cs->timer);
254       strlcpy(cs->ics.parm.setup.phone, to_nr, sizeof(cs->ics.parm.setup.phone));
255       strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");
256       ic.command = ISDN_CMD_REDIR;
257       if ((i = divert_if.ll_cmd(&ic)))
258	{
259          spin_lock_irqsave(&divert_lock, flags);
260          cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
261          cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
262          add_timer(&cs->timer);
263          spin_unlock_irqrestore(&divert_lock, flags);
264        }
265       else
266          cs->akt_state = DEFLECT_ALERT;
267     break;
268
269   } /* switch */
270  return(i);
271} /* deflect_extern_action */
272
273/********************************/
274/* insert a new rule before idx */
275/********************************/
276int insertrule(int idx, divert_rule *newrule)
277{ struct deflect_struc *ds,*ds1=NULL;
278  unsigned long flags;
279
280  if (!(ds = kmalloc(sizeof(struct deflect_struc),
281                                              GFP_KERNEL)))
282    return(-ENOMEM); /* no memory */
283
284  ds->rule = *newrule; /* set rule */
285
286  spin_lock_irqsave(&divert_lock, flags);
287
288  if (idx >= 0)
289   { ds1 = table_head;
290     while ((ds1) && (idx > 0))
291      { idx--;
292        ds1 = ds1->next;
293      }
294     if (!ds1) idx = -1;
295   }
296
297  if (idx < 0)
298   { ds->prev = table_tail; /* previous entry */
299     ds->next = NULL; /* end of chain */
300     if (ds->prev)
301       ds->prev->next = ds; /* last forward */
302      else
303        table_head = ds; /* is first entry */
304     table_tail = ds; /* end of queue */
305   }
306  else
307    { ds->next = ds1; /* next entry */
308      ds->prev = ds1->prev; /* prev entry */
309      ds1->prev = ds; /* backward chain old element */
310      if (!ds->prev)
311        table_head = ds; /* first element */
312   }
313
314  spin_unlock_irqrestore(&divert_lock, flags);
315  return(0);
316} /* insertrule */
317
318/***********************************/
319/* delete the rule at position idx */
320/***********************************/
321int deleterule(int idx)
322{ struct deflect_struc *ds,*ds1;
323  unsigned long flags;
324
325  if (idx < 0)
326   { spin_lock_irqsave(&divert_lock, flags);
327     ds = table_head;
328     table_head = NULL;
329     table_tail = NULL;
330     spin_unlock_irqrestore(&divert_lock, flags);
331     while (ds)
332      { ds1 = ds;
333        ds = ds->next;
334        kfree(ds1);
335      }
336     return(0);
337   }
338
339  spin_lock_irqsave(&divert_lock, flags);
340  ds = table_head;
341
342  while ((ds) && (idx > 0))
343   { idx--;
344     ds = ds->next;
345   }
346
347  if (!ds)
348   {
349     spin_unlock_irqrestore(&divert_lock, flags);
350     return(-EINVAL);
351   }
352
353  if (ds->next)
354    ds->next->prev = ds->prev; /* backward chain */
355   else
356     table_tail = ds->prev; /* end of chain */
357
358  if (ds->prev)
359    ds->prev->next = ds->next; /* forward chain */
360   else
361     table_head = ds->next; /* start of chain */
362
363  spin_unlock_irqrestore(&divert_lock, flags);
364  kfree(ds);
365  return(0);
366} /* deleterule */
367
368/*******************************************/
369/* get a pointer to a specific rule number */
370/*******************************************/
371divert_rule *getruleptr(int idx)
372{ struct deflect_struc *ds = table_head;
373
374  if (idx < 0) return(NULL);
375  while ((ds) && (idx >= 0))
376   { if (!(idx--))
377      { return(&ds->rule);
378        break;
379      }
380     ds = ds->next;
381   }
382  return(NULL);
383} /* getruleptr */
384
385/*************************************************/
386/* called from common module on an incoming call */
387/*************************************************/
388static int isdn_divert_icall(isdn_ctrl *ic)
389{ int retval = 0;
390  unsigned long flags;
391  struct call_struc *cs = NULL;
392  struct deflect_struc *dv;
393  char *p,*p1;
394  u_char accept;
395
396  /* first check the internal deflection table */
397  for (dv = table_head; dv ; dv = dv->next )
398   { /* scan table */
399     if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) ||
400         ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL)))
401       continue; /* call option check */
402     if (!(dv->rule.drvid & (1L << ic->driver)))
403       continue; /* driver not matching */
404     if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1))
405       continue; /* si1 not matching */
406     if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2))
407       continue; /* si2 not matching */
408
409     p = dv->rule.my_msn;
410     p1 = ic->parm.setup.eazmsn;
411     accept = 0;
412     while (*p)
413      { /* complete compare */
414        if (*p == '-')
415	  { accept = 1; /* call accepted */
416            break;
417          }
418        if (*p++ != *p1++)
419          break; /* not accepted */
420        if ((!*p) && (!*p1))
421          accept = 1;
422      } /* complete compare */
423     if (!accept) continue; /* not accepted */
424
425     if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0]))
426      { p = dv->rule.caller;
427        p1 = ic->parm.setup.phone;
428        accept = 0;
429        while (*p)
430	 { /* complete compare */
431           if (*p == '-')
432	    { accept = 1; /* call accepted */
433              break;
434            }
435           if (*p++ != *p1++)
436             break; /* not accepted */
437           if ((!*p) && (!*p1))
438             accept = 1;
439         } /* complete compare */
440        if (!accept) continue; /* not accepted */
441      }
442
443     switch (dv->rule.action)
444       { case DEFLECT_IGNORE:
445           return(0);
446           break;
447
448         case DEFLECT_ALERT:
449         case DEFLECT_PROCEED:
450         case DEFLECT_REPORT:
451         case DEFLECT_REJECT:
452           if (dv->rule.action == DEFLECT_PROCEED)
453	    if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))
454              return(0); /* no external deflection needed */
455           if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
456             return(0); /* no memory */
457           init_timer(&cs->timer);
458           cs->info[0] = '\0';
459           cs->timer.function = deflect_timer_expire;
460           cs->timer.data = (ulong) cs; /* pointer to own structure */
461
462           cs->ics = *ic; /* copy incoming data */
463           if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0");
464           if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0");
465	   cs->ics.parm.setup.screen = dv->rule.screen;
466           if (dv->rule.waittime)
467             cs->timer.expires = jiffies + (HZ * dv->rule.waittime);
468           else
469            if (dv->rule.action == DEFLECT_PROCEED)
470              cs->timer.expires = jiffies + (HZ * extern_wait_max);
471            else
472              cs->timer.expires = 0;
473           cs->akt_state = dv->rule.action;
474           spin_lock_irqsave(&divert_lock, flags);
475           cs->divert_id = next_id++; /* new sequence number */
476           spin_unlock_irqrestore(&divert_lock, flags);
477           cs->prev = NULL;
478           if (cs->akt_state == DEFLECT_ALERT)
479             { strcpy(cs->deflect_dest,dv->rule.to_nr);
480               if (!cs->timer.expires)
481		 { strcpy(ic->parm.setup.eazmsn,"Testtext direct");
482                   ic->parm.setup.screen = dv->rule.screen;
483                   strlcpy(ic->parm.setup.phone, dv->rule.to_nr, sizeof(ic->parm.setup.phone));
484                   cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
485                   cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
486                   retval = 5;
487                 }
488               else
489                 retval = 1; /* alerting */
490             }
491           else
492             { cs->deflect_dest[0] = '\0';
493	       retval = 4; /* only proceed */
494             }
495           sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
496                   cs->akt_state,
497                   cs->divert_id,
498                   divert_if.drv_to_name(cs->ics.driver),
499                   (ic->command == ISDN_STAT_ICALLW) ? "1":"0",
500                   cs->ics.parm.setup.phone,
501                   cs->ics.parm.setup.eazmsn,
502                   cs->ics.parm.setup.si1,
503                   cs->ics.parm.setup.si2,
504                   cs->ics.parm.setup.screen,
505                   dv->rule.waittime,
506                   cs->deflect_dest);
507           if ((dv->rule.action == DEFLECT_REPORT) ||
508               (dv->rule.action == DEFLECT_REJECT))
509	    { put_info_buffer(cs->info);
510	      kfree(cs); /* remove */
511              return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */
512            }
513           break;
514
515         default:
516           return(0); /* ignore call */
517           break;
518       } /* switch action */
519     break;
520   } /* scan_table */
521
522  if (cs)
523   { cs->prev = NULL;
524     spin_lock_irqsave(&divert_lock, flags);
525     cs->next = divert_head;
526     divert_head = cs;
527     if (cs->timer.expires) add_timer(&cs->timer);
528     spin_unlock_irqrestore(&divert_lock, flags);
529
530     put_info_buffer(cs->info);
531     return(retval);
532   }
533  else
534     return(0);
535} /* isdn_divert_icall */
536
537
538void deleteprocs(void)
539{ struct call_struc *cs, *cs1;
540  unsigned long flags;
541
542  spin_lock_irqsave(&divert_lock, flags);
543  cs = divert_head;
544  divert_head = NULL;
545  while (cs)
546   { del_timer(&cs->timer);
547     cs1 = cs;
548     cs = cs->next;
549     kfree(cs1);
550   }
551  spin_unlock_irqrestore(&divert_lock, flags);
552} /* deleteprocs */
553
554/****************************************************/
555/* put a address including address type into buffer */
556/****************************************************/
557static int put_address(char *st, u_char *p, int len)
558{ u_char retval = 0;
559  u_char adr_typ = 0; /* network standard */
560
561  if (len < 2) return(retval);
562  if (*p == 0xA1)
563   { retval = *(++p) + 2; /* total length */
564     if (retval > len) return(0); /* too short */
565     len = retval - 2; /* remaining length */
566     if (len < 3) return(0);
567     if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0);
568     adr_typ = *(++p);
569     len -= 3;
570     p++;
571     if (len < 2) return(0);
572     if (*p++ != 0x12) return(0);
573     if (*p > len) return(0); /* check number length */
574     len = *p++;
575   }
576  else
577   if (*p == 0x80)
578    { retval = *(++p) + 2; /* total length */
579      if (retval > len) return(0);
580      len = retval - 2;
581      p++;
582    }
583   else
584    return(0); /* invalid address information */
585
586  sprintf(st,"%d ",adr_typ);
587  st += strlen(st);
588  if (!len)
589    *st++ = '-';
590  else
591   while (len--)
592     *st++ = *p++;
593  *st = '\0';
594  return(retval);
595} /* put_address */
596
597/*************************************/
598/* report a successful interrogation */
599/*************************************/
600static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
601{ char *src = ic->parm.dss1_io.data;
602  int restlen = ic->parm.dss1_io.datalen;
603  int cnt = 1;
604  u_char n,n1;
605  char st[90], *p, *stp;
606
607  if (restlen < 2) return(-100); /* frame too short */
608  if (*src++ != 0x30) return(-101);
609  if ((n = *src++) > 0x81) return(-102); /* invalid length field */
610  restlen -= 2; /* remaining bytes */
611  if (n == 0x80)
612   { if (restlen < 2) return(-103);
613     if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);
614     restlen -= 2;
615   }
616  else
617   if ( n == 0x81)
618    { n = *src++;
619      restlen--;
620      if (n > restlen) return(-105);
621      restlen = n;
622    }
623   else
624    if (n > restlen) return(-106);
625     else
626      restlen = n; /* standard format */
627  if (restlen < 3) return(-107); /* no procedure */
628  if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);
629  restlen -= 3;
630  if (restlen < 2) return(-109); /* list missing */
631  if (*src == 0x31)
632   { src++;
633     if ((n = *src++) > 0x81) return(-110); /* invalid length field */
634     restlen -= 2; /* remaining bytes */
635     if (n == 0x80)
636      { if (restlen < 2) return(-111);
637        if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);
638        restlen -= 2;
639      }
640     else
641      if ( n == 0x81)
642       { n = *src++;
643         restlen--;
644         if (n > restlen) return(-113);
645         restlen = n;
646       }
647      else
648       if (n > restlen) return(-114);
649        else
650         restlen = n; /* standard format */
651   } /* result list header */
652
653  while (restlen >= 2)
654   { stp = st;
655     sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id,
656                 cnt++,divert_if.drv_to_name(ic->driver));
657     stp += strlen(stp);
658     if (*src++ != 0x30) return(-115); /* invalid enum */
659     n = *src++;
660     restlen -= 2;
661     if (n > restlen) return(-116); /* enum length wrong */
662     restlen -= n;
663     p = src; /* one entry */
664     src += n;
665     if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
666     stp += strlen(stp);
667     p += n1;
668     n -= n1;
669     if (n < 6) continue; /* no service and proc */
670     if ((*p++ != 0x0A) || (*p++ != 1)) continue;
671     sprintf(stp," 0x%02x ",(*p++) & 0xFF);
672     stp += strlen(stp);
673     if ((*p++ != 0x0A) || (*p++ != 1)) continue;
674     sprintf(stp,"%d ",(*p++) & 0xFF);
675     stp += strlen(stp);
676     n -= 6;
677     if (n > 2)
678      { if (*p++ != 0x30) continue;
679        if (*p > (n-2)) continue;
680        n = *p++;
681        if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
682        stp += strlen(stp);
683      }
684     sprintf(stp,"\n");
685     put_info_buffer(st);
686   } /* while restlen */
687  if (restlen) return(-117);
688  return(0);
689} /* interrogate_success */
690
691/*********************************************/
692/* callback for protocol specific extensions */
693/*********************************************/
694static int prot_stat_callback(isdn_ctrl *ic)
695{ struct call_struc *cs, *cs1;
696  int i;
697  unsigned long flags;
698
699  cs = divert_head; /* start of list */
700  cs1 = NULL;
701  while (cs)
702   { if (ic->driver == cs->ics.driver)
703      { switch (cs->ics.arg)
704	 { case DSS1_CMD_INVOKE:
705             if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) &&
706                 (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id))
707	      { switch (ic->arg)
708		{  case DSS1_STAT_INVOKE_ERR:
709                     sprintf(cs->info,"128 0x%lx 0x%x\n",
710                             ic->parm.dss1_io.ll_id,
711                             ic->parm.dss1_io.timeout);
712                     put_info_buffer(cs->info);
713                   break;
714
715                   case DSS1_STAT_INVOKE_RES:
716                     switch (cs->ics.parm.dss1_io.proc)
717		      {  case  7:
718                         case  8:
719                            put_info_buffer(cs->info);
720                           break;
721
722                         case  11:
723                           i = interrogate_success(ic,cs);
724                           if (i)
725                             sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT,
726                                     ic->parm.dss1_io.ll_id,i);
727                           put_info_buffer(cs->info);
728                           break;
729
730		         default:
731                           printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);
732                           break;
733                      }
734
735
736                   break;
737
738		   default:
739                     printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);
740                   break;
741                 }
742                cs1 = cs; /* remember structure */
743                cs = NULL;
744                continue; /* abort search */
745              } /* id found */
746           break;
747
748	   case DSS1_CMD_INVOKE_ABORT:
749             printk(KERN_WARNING "dss1_divert unhandled invoke abort\n");
750           break;
751
752	   default:
753             printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg);
754           break;
755         } /* switch ics.arg */
756        cs = cs->next;
757      } /* driver ok */
758   }
759
760  if (!cs1)
761   { printk(KERN_WARNING "dss1_divert unhandled process\n");
762     return(0);
763   }
764
765  if (cs1->ics.driver == -1)
766   {
767     spin_lock_irqsave(&divert_lock, flags);
768     del_timer(&cs1->timer);
769     if (cs1->prev)
770       cs1->prev->next = cs1->next; /* forward link */
771     else
772       divert_head = cs1->next;
773     if (cs1->next)
774       cs1->next->prev = cs1->prev; /* back link */
775     spin_unlock_irqrestore(&divert_lock, flags);
776     kfree(cs1);
777   }
778
779  return(0);
780} /* prot_stat_callback */
781
782
783/***************************/
784/* status callback from HL */
785/***************************/
786static int isdn_divert_stat_callback(isdn_ctrl *ic)
787{ struct call_struc *cs, *cs1;
788  unsigned long flags;
789  int retval;
790
791  retval = -1;
792  cs = divert_head; /* start of list */
793     while (cs)
794      { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg))
795         { switch (ic->command)
796	    { case ISDN_STAT_DHUP:
797                sprintf(cs->info,"129 0x%lx\n",cs->divert_id);
798                del_timer(&cs->timer);
799                cs->ics.driver = -1;
800                break;
801
802	      case ISDN_STAT_CAUSE:
803                sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);
804                break;
805
806	      case ISDN_STAT_REDIR:
807                sprintf(cs->info,"131 0x%lx\n",cs->divert_id);
808                del_timer(&cs->timer);
809                cs->ics.driver = -1;
810                break;
811
812	      default:
813                sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));
814                break;
815            }
816          put_info_buffer(cs->info);
817          retval = 0;
818         }
819        cs1 = cs;
820        cs = cs->next;
821        if (cs1->ics.driver == -1)
822          {
823            spin_lock_irqsave(&divert_lock, flags);
824            if (cs1->prev)
825              cs1->prev->next = cs1->next; /* forward link */
826            else
827              divert_head = cs1->next;
828            if (cs1->next)
829              cs1->next->prev = cs1->prev; /* back link */
830            spin_unlock_irqrestore(&divert_lock, flags);
831            kfree(cs1);
832          }
833      }
834  return(retval); /* not found */
835} /* isdn_divert_stat_callback */
836
837
838/********************/
839/* callback from ll */
840/********************/
841int ll_callback(isdn_ctrl *ic)
842{
843  switch (ic->command)
844   { case ISDN_STAT_ICALL:
845     case ISDN_STAT_ICALLW:
846       return(isdn_divert_icall(ic));
847     break;
848
849     case ISDN_STAT_PROT:
850       if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)
851	{ if (ic->arg != DSS1_STAT_INVOKE_BRD)
852            return(prot_stat_callback(ic));
853          else
854            return(0); /* DSS1 invoke broadcast */
855        }
856       else
857         return(-1); /* protocol not euro */
858
859     default:
860       return(isdn_divert_stat_callback(ic));
861   }
862} /* ll_callback */
863
864