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/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