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