| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 | #include <linux/proc_fs.h> | 
| Al Viro | d7fe0f2 | 2006-12-03 23:15:30 -0500 | [diff] [blame] | 13 | #include <linux/timer.h> | 
 | 14 | #include <linux/jiffies.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 15 |  | 
 | 16 | #include "isdn_divert.h" | 
 | 17 |  | 
 | 18 | /**********************************/ | 
 | 19 | /* structure keeping calling info */ | 
 | 20 | /**********************************/ | 
 | 21 | struct 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 | /********************************************/ | 
 | 36 | struct 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 */ | 
 | 46 | static struct call_struc *divert_head = NULL; /* head of remembered entrys */ | 
 | 47 | static ulong next_id = 1; /* next info id */    | 
 | 48 | static struct deflect_struc *table_head = NULL; | 
 | 49 | static struct deflect_struc *table_tail = NULL;  | 
 | 50 | static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */  | 
 | 51 |  | 
 | 52 | DEFINE_SPINLOCK(divert_lock); | 
 | 53 |  | 
 | 54 | /***************************/ | 
 | 55 | /* timer callback function */ | 
 | 56 | /***************************/ | 
 | 57 | static 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 | /*****************************************/ | 
 | 110 | int 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 */   | 
| Robert P. J. Day | 5cbded5 | 2006-12-13 00:35:56 -0800 | [diff] [blame] | 156 |   if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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); /* excute 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 | /****************************************/ | 
 | 204 | int 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 | /********************************/ | 
 | 275 | int insertrule(int idx, divert_rule *newrule) | 
 | 276 | { struct deflect_struc *ds,*ds1=NULL; | 
 | 277 |   unsigned long flags; | 
 | 278 |  | 
| Robert P. J. Day | 5cbded5 | 2006-12-13 00:35:56 -0800 | [diff] [blame] | 279 |   if (!(ds = kmalloc(sizeof(struct deflect_struc), | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | /***********************************/ | 
 | 320 | int 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 | /*******************************************/ | 
 | 370 | divert_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 | /*************************************************/ | 
| Adrian Bunk | 408b664 | 2005-05-01 08:59:29 -0700 | [diff] [blame] | 387 | static int isdn_divert_icall(isdn_ctrl *ic) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 */   | 
| Robert P. J. Day | 5cbded5 | 2006-12-13 00:35:56 -0800 | [diff] [blame] | 454 |            if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC))) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
 | 537 | void 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 | /****************************************************/ | 
| Adrian Bunk | 408b664 | 2005-05-01 08:59:29 -0700 | [diff] [blame] | 556 | static int put_address(char *st, u_char *p, int len) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | /*************************************/ | 
| Andreas Mohr | d6e05ed | 2006-06-26 18:35:02 +0200 | [diff] [blame] | 597 | /* report a successful interrogation */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 598 | /*************************************/ | 
| Adrian Bunk | 408b664 | 2005-05-01 08:59:29 -0700 | [diff] [blame] | 599 | static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | /*********************************************/ | 
| Adrian Bunk | 408b664 | 2005-05-01 08:59:29 -0700 | [diff] [blame] | 693 | static int prot_stat_callback(isdn_ctrl *ic) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | /***************************/ | 
| Adrian Bunk | 408b664 | 2005-05-01 08:59:29 -0700 | [diff] [blame] | 785 | static int isdn_divert_stat_callback(isdn_ctrl *ic) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | /********************/  | 
 | 840 | int 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 |  |