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