Path: blob/master/drivers/isdn/divert/isdn_divert.c
17686 views
/* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $1*2* DSS1 main diversion supplementary handling for i4l.3*4* Copyright 1999 by Werner Cornelius ([email protected])5*6* This software may be used and distributed according to the terms7* of the GNU General Public License, incorporated herein by reference.8*9*/1011#include <linux/proc_fs.h>12#include <linux/slab.h>13#include <linux/timer.h>14#include <linux/jiffies.h>1516#include "isdn_divert.h"1718/**********************************/19/* structure keeping calling info */20/**********************************/21struct call_struc22{ isdn_ctrl ics; /* delivered setup + driver parameters */23ulong divert_id; /* Id delivered to user */24unsigned char akt_state; /* actual state */25char deflect_dest[35]; /* deflection destination */26struct timer_list timer; /* timer control structure */27char info[90]; /* device info output */28struct call_struc *next; /* pointer to next entry */29struct call_struc *prev;30};313233/********************************************/34/* structure keeping deflection table entry */35/********************************************/36struct deflect_struc37{ struct deflect_struc *next,*prev;38divert_rule rule; /* used rule */39};404142/*****************************************/43/* variables for main diversion services */44/*****************************************/45/* diversion/deflection processes */46static struct call_struc *divert_head = NULL; /* head of remembered entrys */47static ulong next_id = 1; /* next info id */48static struct deflect_struc *table_head = NULL;49static struct deflect_struc *table_tail = NULL;50static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */5152DEFINE_SPINLOCK(divert_lock);5354/***************************/55/* timer callback function */56/***************************/57static void deflect_timer_expire(ulong arg)58{59unsigned long flags;60struct call_struc *cs = (struct call_struc *) arg;6162spin_lock_irqsave(&divert_lock, flags);63del_timer(&cs->timer); /* delete active timer */64spin_unlock_irqrestore(&divert_lock, flags);6566switch(cs->akt_state)67{ case DEFLECT_PROCEED:68cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */69divert_if.ll_cmd(&cs->ics);70spin_lock_irqsave(&divert_lock, flags);71cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */72cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);73add_timer(&cs->timer);74spin_unlock_irqrestore(&divert_lock, flags);75break;7677case DEFLECT_ALERT:78cs->ics.command = ISDN_CMD_REDIR; /* protocol */79strlcpy(cs->ics.parm.setup.phone, cs->deflect_dest, sizeof(cs->ics.parm.setup.phone));80strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed");81divert_if.ll_cmd(&cs->ics);82spin_lock_irqsave(&divert_lock, flags);83cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */84cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);85add_timer(&cs->timer);86spin_unlock_irqrestore(&divert_lock, flags);87break;8889case DEFLECT_AUTODEL:90default:91spin_lock_irqsave(&divert_lock, flags);92if (cs->prev)93cs->prev->next = cs->next; /* forward link */94else95divert_head = cs->next;96if (cs->next)97cs->next->prev = cs->prev; /* back link */98spin_unlock_irqrestore(&divert_lock, flags);99kfree(cs);100return;101102} /* switch */103} /* deflect_timer_func */104105106/*****************************************/107/* handle call forwarding de/activations */108/* 0 = deact, 1 = act, 2 = interrogate */109/*****************************************/110int cf_command(int drvid, int mode,111u_char proc, char *msn,112u_char service, char *fwd_nr, ulong *procid)113{ unsigned long flags;114int retval,msnlen;115int fwd_len;116char *p,*ielenp,tmp[60];117struct call_struc *cs;118119if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */120if ((proc & 0x7F) > 2) return(-EINVAL);121proc &= 3;122p = tmp;123*p++ = 0x30; /* enumeration */124ielenp = 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 */131132if (mode == 1)133{ if (!*fwd_nr) return(-EINVAL); /* destination missing */134if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */135fwd_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 */140strcpy(p,fwd_nr); /* copy number */141p += fwd_len; /* pointer beyond fwd */142} /* activate */143144msnlen = strlen(msn);145*p++ = 0x80; /* msn number */146if (msnlen > 1)147{ *p++ = msnlen; /* length */148strcpy(p,msn);149p += msnlen;150}151else *p++ = 0;152153*ielenp = p - ielenp - 1; /* set total IE length */154155/* allocate mem for information struct */156if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))157return(-ENOMEM); /* no memory */158init_timer(&cs->timer);159cs->info[0] = '\0';160cs->timer.function = deflect_timer_expire;161cs->timer.data = (ulong) cs; /* pointer to own structure */162cs->ics.driver = drvid;163cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */164cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */165cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */166cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */167cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */168cs->ics.parm.dss1_io.data = tmp; /* start of buffer */169170spin_lock_irqsave(&divert_lock, flags);171cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */172spin_unlock_irqrestore(&divert_lock, flags);173*procid = cs->ics.parm.dss1_io.ll_id;174175sprintf(cs->info,"%d 0x%lx %s%s 0 %s %02x %d%s%s\n",176(!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,177cs->ics.parm.dss1_io.ll_id,178(mode != 2) ? "" : "0 ",179divert_if.drv_to_name(cs->ics.driver),180msn,181service & 0xFF,182proc,183(mode != 1) ? "" : " 0 ",184(mode != 1) ? "" : fwd_nr);185186retval = divert_if.ll_cmd(&cs->ics); /* execute command */187188if (!retval)189{ cs->prev = NULL;190spin_lock_irqsave(&divert_lock, flags);191cs->next = divert_head;192divert_head = cs;193spin_unlock_irqrestore(&divert_lock, flags);194}195else196kfree(cs);197return(retval);198} /* cf_command */199200201/****************************************/202/* handle a external deflection command */203/****************************************/204int deflect_extern_action(u_char cmd, ulong callid, char *to_nr)205{ struct call_struc *cs;206isdn_ctrl ic;207unsigned long flags;208int i;209210if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */211cs = divert_head; /* start of parameter list */212while (cs)213{ if (cs->divert_id == callid) break; /* found */214cs = cs->next;215} /* search entry */216if (!cs) return(-EINVAL); /* invalid callid */217218ic.driver = cs->ics.driver;219ic.arg = cs->ics.arg;220i = -EINVAL;221if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */222switch (cmd & 0x7F)223{ case 0: /* hangup */224del_timer(&cs->timer);225ic.command = ISDN_CMD_HANGUP;226i = divert_if.ll_cmd(&ic);227spin_lock_irqsave(&divert_lock, flags);228cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */229cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);230add_timer(&cs->timer);231spin_unlock_irqrestore(&divert_lock, flags);232break;233234case 1: /* alert */235if (cs->akt_state == DEFLECT_ALERT) return(0);236cmd &= 0x7F; /* never wait */237del_timer(&cs->timer);238ic.command = ISDN_CMD_ALERT;239if ((i = divert_if.ll_cmd(&ic)))240{241spin_lock_irqsave(&divert_lock, flags);242cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */243cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);244add_timer(&cs->timer);245spin_unlock_irqrestore(&divert_lock, flags);246}247else248cs->akt_state = DEFLECT_ALERT;249break;250251case 2: /* redir */252del_timer(&cs->timer);253strlcpy(cs->ics.parm.setup.phone, to_nr, sizeof(cs->ics.parm.setup.phone));254strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");255ic.command = ISDN_CMD_REDIR;256if ((i = divert_if.ll_cmd(&ic)))257{258spin_lock_irqsave(&divert_lock, flags);259cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */260cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);261add_timer(&cs->timer);262spin_unlock_irqrestore(&divert_lock, flags);263}264else265cs->akt_state = DEFLECT_ALERT;266break;267268} /* switch */269return(i);270} /* deflect_extern_action */271272/********************************/273/* insert a new rule before idx */274/********************************/275int insertrule(int idx, divert_rule *newrule)276{ struct deflect_struc *ds,*ds1=NULL;277unsigned long flags;278279if (!(ds = kmalloc(sizeof(struct deflect_struc),280GFP_KERNEL)))281return(-ENOMEM); /* no memory */282283ds->rule = *newrule; /* set rule */284285spin_lock_irqsave(&divert_lock, flags);286287if (idx >= 0)288{ ds1 = table_head;289while ((ds1) && (idx > 0))290{ idx--;291ds1 = ds1->next;292}293if (!ds1) idx = -1;294}295296if (idx < 0)297{ ds->prev = table_tail; /* previous entry */298ds->next = NULL; /* end of chain */299if (ds->prev)300ds->prev->next = ds; /* last forward */301else302table_head = ds; /* is first entry */303table_tail = ds; /* end of queue */304}305else306{ ds->next = ds1; /* next entry */307ds->prev = ds1->prev; /* prev entry */308ds1->prev = ds; /* backward chain old element */309if (!ds->prev)310table_head = ds; /* first element */311}312313spin_unlock_irqrestore(&divert_lock, flags);314return(0);315} /* insertrule */316317/***********************************/318/* delete the rule at position idx */319/***********************************/320int deleterule(int idx)321{ struct deflect_struc *ds,*ds1;322unsigned long flags;323324if (idx < 0)325{ spin_lock_irqsave(&divert_lock, flags);326ds = table_head;327table_head = NULL;328table_tail = NULL;329spin_unlock_irqrestore(&divert_lock, flags);330while (ds)331{ ds1 = ds;332ds = ds->next;333kfree(ds1);334}335return(0);336}337338spin_lock_irqsave(&divert_lock, flags);339ds = table_head;340341while ((ds) && (idx > 0))342{ idx--;343ds = ds->next;344}345346if (!ds)347{348spin_unlock_irqrestore(&divert_lock, flags);349return(-EINVAL);350}351352if (ds->next)353ds->next->prev = ds->prev; /* backward chain */354else355table_tail = ds->prev; /* end of chain */356357if (ds->prev)358ds->prev->next = ds->next; /* forward chain */359else360table_head = ds->next; /* start of chain */361362spin_unlock_irqrestore(&divert_lock, flags);363kfree(ds);364return(0);365} /* deleterule */366367/*******************************************/368/* get a pointer to a specific rule number */369/*******************************************/370divert_rule *getruleptr(int idx)371{ struct deflect_struc *ds = table_head;372373if (idx < 0) return(NULL);374while ((ds) && (idx >= 0))375{ if (!(idx--))376{ return(&ds->rule);377break;378}379ds = ds->next;380}381return(NULL);382} /* getruleptr */383384/*************************************************/385/* called from common module on an incoming call */386/*************************************************/387static int isdn_divert_icall(isdn_ctrl *ic)388{ int retval = 0;389unsigned long flags;390struct call_struc *cs = NULL;391struct deflect_struc *dv;392char *p,*p1;393u_char accept;394395/* first check the internal deflection table */396for (dv = table_head; dv ; dv = dv->next )397{ /* scan table */398if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) ||399((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL)))400continue; /* call option check */401if (!(dv->rule.drvid & (1L << ic->driver)))402continue; /* driver not matching */403if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1))404continue; /* si1 not matching */405if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2))406continue; /* si2 not matching */407408p = dv->rule.my_msn;409p1 = ic->parm.setup.eazmsn;410accept = 0;411while (*p)412{ /* complete compare */413if (*p == '-')414{ accept = 1; /* call accepted */415break;416}417if (*p++ != *p1++)418break; /* not accepted */419if ((!*p) && (!*p1))420accept = 1;421} /* complete compare */422if (!accept) continue; /* not accepted */423424if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0]))425{ p = dv->rule.caller;426p1 = ic->parm.setup.phone;427accept = 0;428while (*p)429{ /* complete compare */430if (*p == '-')431{ accept = 1; /* call accepted */432break;433}434if (*p++ != *p1++)435break; /* not accepted */436if ((!*p) && (!*p1))437accept = 1;438} /* complete compare */439if (!accept) continue; /* not accepted */440}441442switch (dv->rule.action)443{ case DEFLECT_IGNORE:444return(0);445break;446447case DEFLECT_ALERT:448case DEFLECT_PROCEED:449case DEFLECT_REPORT:450case DEFLECT_REJECT:451if (dv->rule.action == DEFLECT_PROCEED)452if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))453return(0); /* no external deflection needed */454if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))455return(0); /* no memory */456init_timer(&cs->timer);457cs->info[0] = '\0';458cs->timer.function = deflect_timer_expire;459cs->timer.data = (ulong) cs; /* pointer to own structure */460461cs->ics = *ic; /* copy incoming data */462if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0");463if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0");464cs->ics.parm.setup.screen = dv->rule.screen;465if (dv->rule.waittime)466cs->timer.expires = jiffies + (HZ * dv->rule.waittime);467else468if (dv->rule.action == DEFLECT_PROCEED)469cs->timer.expires = jiffies + (HZ * extern_wait_max);470else471cs->timer.expires = 0;472cs->akt_state = dv->rule.action;473spin_lock_irqsave(&divert_lock, flags);474cs->divert_id = next_id++; /* new sequence number */475spin_unlock_irqrestore(&divert_lock, flags);476cs->prev = NULL;477if (cs->akt_state == DEFLECT_ALERT)478{ strcpy(cs->deflect_dest,dv->rule.to_nr);479if (!cs->timer.expires)480{ strcpy(ic->parm.setup.eazmsn,"Testtext direct");481ic->parm.setup.screen = dv->rule.screen;482strlcpy(ic->parm.setup.phone, dv->rule.to_nr, sizeof(ic->parm.setup.phone));483cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */484cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);485retval = 5;486}487else488retval = 1; /* alerting */489}490else491{ cs->deflect_dest[0] = '\0';492retval = 4; /* only proceed */493}494sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",495cs->akt_state,496cs->divert_id,497divert_if.drv_to_name(cs->ics.driver),498(ic->command == ISDN_STAT_ICALLW) ? "1":"0",499cs->ics.parm.setup.phone,500cs->ics.parm.setup.eazmsn,501cs->ics.parm.setup.si1,502cs->ics.parm.setup.si2,503cs->ics.parm.setup.screen,504dv->rule.waittime,505cs->deflect_dest);506if ((dv->rule.action == DEFLECT_REPORT) ||507(dv->rule.action == DEFLECT_REJECT))508{ put_info_buffer(cs->info);509kfree(cs); /* remove */510return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */511}512break;513514default:515return(0); /* ignore call */516break;517} /* switch action */518break;519} /* scan_table */520521if (cs)522{ cs->prev = NULL;523spin_lock_irqsave(&divert_lock, flags);524cs->next = divert_head;525divert_head = cs;526if (cs->timer.expires) add_timer(&cs->timer);527spin_unlock_irqrestore(&divert_lock, flags);528529put_info_buffer(cs->info);530return(retval);531}532else533return(0);534} /* isdn_divert_icall */535536537void deleteprocs(void)538{ struct call_struc *cs, *cs1;539unsigned long flags;540541spin_lock_irqsave(&divert_lock, flags);542cs = divert_head;543divert_head = NULL;544while (cs)545{ del_timer(&cs->timer);546cs1 = cs;547cs = cs->next;548kfree(cs1);549}550spin_unlock_irqrestore(&divert_lock, flags);551} /* deleteprocs */552553/****************************************************/554/* put a address including address type into buffer */555/****************************************************/556static int put_address(char *st, u_char *p, int len)557{ u_char retval = 0;558u_char adr_typ = 0; /* network standard */559560if (len < 2) return(retval);561if (*p == 0xA1)562{ retval = *(++p) + 2; /* total length */563if (retval > len) return(0); /* too short */564len = retval - 2; /* remaining length */565if (len < 3) return(0);566if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0);567adr_typ = *(++p);568len -= 3;569p++;570if (len < 2) return(0);571if (*p++ != 0x12) return(0);572if (*p > len) return(0); /* check number length */573len = *p++;574}575else576if (*p == 0x80)577{ retval = *(++p) + 2; /* total length */578if (retval > len) return(0);579len = retval - 2;580p++;581}582else583return(0); /* invalid address information */584585sprintf(st,"%d ",adr_typ);586st += strlen(st);587if (!len)588*st++ = '-';589else590while (len--)591*st++ = *p++;592*st = '\0';593return(retval);594} /* put_address */595596/*************************************/597/* report a successful interrogation */598/*************************************/599static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)600{ char *src = ic->parm.dss1_io.data;601int restlen = ic->parm.dss1_io.datalen;602int cnt = 1;603u_char n,n1;604char st[90], *p, *stp;605606if (restlen < 2) return(-100); /* frame too short */607if (*src++ != 0x30) return(-101);608if ((n = *src++) > 0x81) return(-102); /* invalid length field */609restlen -= 2; /* remaining bytes */610if (n == 0x80)611{ if (restlen < 2) return(-103);612if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);613restlen -= 2;614}615else616if ( n == 0x81)617{ n = *src++;618restlen--;619if (n > restlen) return(-105);620restlen = n;621}622else623if (n > restlen) return(-106);624else625restlen = n; /* standard format */626if (restlen < 3) return(-107); /* no procedure */627if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);628restlen -= 3;629if (restlen < 2) return(-109); /* list missing */630if (*src == 0x31)631{ src++;632if ((n = *src++) > 0x81) return(-110); /* invalid length field */633restlen -= 2; /* remaining bytes */634if (n == 0x80)635{ if (restlen < 2) return(-111);636if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);637restlen -= 2;638}639else640if ( n == 0x81)641{ n = *src++;642restlen--;643if (n > restlen) return(-113);644restlen = n;645}646else647if (n > restlen) return(-114);648else649restlen = n; /* standard format */650} /* result list header */651652while (restlen >= 2)653{ stp = st;654sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id,655cnt++,divert_if.drv_to_name(ic->driver));656stp += strlen(stp);657if (*src++ != 0x30) return(-115); /* invalid enum */658n = *src++;659restlen -= 2;660if (n > restlen) return(-116); /* enum length wrong */661restlen -= n;662p = src; /* one entry */663src += n;664if (!(n1 = put_address(stp,p,n & 0xFF))) continue;665stp += strlen(stp);666p += n1;667n -= n1;668if (n < 6) continue; /* no service and proc */669if ((*p++ != 0x0A) || (*p++ != 1)) continue;670sprintf(stp," 0x%02x ",(*p++) & 0xFF);671stp += strlen(stp);672if ((*p++ != 0x0A) || (*p++ != 1)) continue;673sprintf(stp,"%d ",(*p++) & 0xFF);674stp += strlen(stp);675n -= 6;676if (n > 2)677{ if (*p++ != 0x30) continue;678if (*p > (n-2)) continue;679n = *p++;680if (!(n1 = put_address(stp,p,n & 0xFF))) continue;681stp += strlen(stp);682}683sprintf(stp,"\n");684put_info_buffer(st);685} /* while restlen */686if (restlen) return(-117);687return(0);688} /* interrogate_success */689690/*********************************************/691/* callback for protocol specific extensions */692/*********************************************/693static int prot_stat_callback(isdn_ctrl *ic)694{ struct call_struc *cs, *cs1;695int i;696unsigned long flags;697698cs = divert_head; /* start of list */699cs1 = NULL;700while (cs)701{ if (ic->driver == cs->ics.driver)702{ switch (cs->ics.arg)703{ case DSS1_CMD_INVOKE:704if ((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:708sprintf(cs->info,"128 0x%lx 0x%x\n",709ic->parm.dss1_io.ll_id,710ic->parm.dss1_io.timeout);711put_info_buffer(cs->info);712break;713714case DSS1_STAT_INVOKE_RES:715switch (cs->ics.parm.dss1_io.proc)716{ case 7:717case 8:718put_info_buffer(cs->info);719break;720721case 11:722i = interrogate_success(ic,cs);723if (i)724sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT,725ic->parm.dss1_io.ll_id,i);726put_info_buffer(cs->info);727break;728729default:730printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);731break;732}733734735break;736737default:738printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);739break;740}741cs1 = cs; /* remember structure */742cs = NULL;743continue; /* abort search */744} /* id found */745break;746747case DSS1_CMD_INVOKE_ABORT:748printk(KERN_WARNING "dss1_divert unhandled invoke abort\n");749break;750751default:752printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg);753break;754} /* switch ics.arg */755cs = cs->next;756} /* driver ok */757}758759if (!cs1)760{ printk(KERN_WARNING "dss1_divert unhandled process\n");761return(0);762}763764if (cs1->ics.driver == -1)765{766spin_lock_irqsave(&divert_lock, flags);767del_timer(&cs1->timer);768if (cs1->prev)769cs1->prev->next = cs1->next; /* forward link */770else771divert_head = cs1->next;772if (cs1->next)773cs1->next->prev = cs1->prev; /* back link */774spin_unlock_irqrestore(&divert_lock, flags);775kfree(cs1);776}777778return(0);779} /* prot_stat_callback */780781782/***************************/783/* status callback from HL */784/***************************/785static int isdn_divert_stat_callback(isdn_ctrl *ic)786{ struct call_struc *cs, *cs1;787unsigned long flags;788int retval;789790retval = -1;791cs = divert_head; /* start of list */792while (cs)793{ if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg))794{ switch (ic->command)795{ case ISDN_STAT_DHUP:796sprintf(cs->info,"129 0x%lx\n",cs->divert_id);797del_timer(&cs->timer);798cs->ics.driver = -1;799break;800801case ISDN_STAT_CAUSE:802sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);803break;804805case ISDN_STAT_REDIR:806sprintf(cs->info,"131 0x%lx\n",cs->divert_id);807del_timer(&cs->timer);808cs->ics.driver = -1;809break;810811default:812sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));813break;814}815put_info_buffer(cs->info);816retval = 0;817}818cs1 = cs;819cs = cs->next;820if (cs1->ics.driver == -1)821{822spin_lock_irqsave(&divert_lock, flags);823if (cs1->prev)824cs1->prev->next = cs1->next; /* forward link */825else826divert_head = cs1->next;827if (cs1->next)828cs1->next->prev = cs1->prev; /* back link */829spin_unlock_irqrestore(&divert_lock, flags);830kfree(cs1);831}832}833return(retval); /* not found */834} /* isdn_divert_stat_callback */835836837/********************/838/* callback from ll */839/********************/840int ll_callback(isdn_ctrl *ic)841{842switch (ic->command)843{ case ISDN_STAT_ICALL:844case ISDN_STAT_ICALLW:845return(isdn_divert_icall(ic));846break;847848case ISDN_STAT_PROT:849if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)850{ if (ic->arg != DSS1_STAT_INVOKE_BRD)851return(prot_stat_callback(ic));852else853return(0); /* DSS1 invoke broadcast */854}855else856return(-1); /* protocol not euro */857858default:859return(isdn_divert_stat_callback(ic));860}861} /* ll_callback */862863864865