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