aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/isdn/divert/isdn_divert.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn/divert/isdn_divert.c')
-rw-r--r--drivers/isdn/divert/isdn_divert.c861
1 files changed, 861 insertions, 0 deletions
diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c
new file mode 100644
index 000000000000..1eb112213f0c
--- /dev/null
+++ b/drivers/isdn/divert/isdn_divert.c
@@ -0,0 +1,861 @@
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/version.h>
13#include <linux/proc_fs.h>
14
15#include "isdn_divert.h"
16
17/**********************************/
18/* structure keeping calling info */
19/**********************************/
20struct call_struc
21 { isdn_ctrl ics; /* delivered setup + driver parameters */
22 ulong divert_id; /* Id delivered to user */
23 unsigned char akt_state; /* actual state */
24 char deflect_dest[35]; /* deflection destination */
25 struct timer_list timer; /* timer control structure */
26 char info[90]; /* device info output */
27 struct call_struc *next; /* pointer to next entry */
28 struct call_struc *prev;
29 };
30
31
32/********************************************/
33/* structure keeping deflection table entry */
34/********************************************/
35struct deflect_struc
36 { struct deflect_struc *next,*prev;
37 divert_rule rule; /* used rule */
38 };
39
40
41/*****************************************/
42/* variables for main diversion services */
43/*****************************************/
44/* diversion/deflection processes */
45static struct call_struc *divert_head = NULL; /* head of remembered entrys */
46static ulong next_id = 1; /* next info id */
47static struct deflect_struc *table_head = NULL;
48static struct deflect_struc *table_tail = NULL;
49static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */
50
51DEFINE_SPINLOCK(divert_lock);
52
53/***************************/
54/* timer callback function */
55/***************************/
56static void deflect_timer_expire(ulong arg)
57{
58 unsigned long flags;
59 struct call_struc *cs = (struct call_struc *) arg;
60
61 spin_lock_irqsave(&divert_lock, flags);
62 del_timer(&cs->timer); /* delete active timer */
63 spin_unlock_irqrestore(&divert_lock, flags);
64
65 switch(cs->akt_state)
66 { case DEFLECT_PROCEED:
67 cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
68 divert_if.ll_cmd(&cs->ics);
69 spin_lock_irqsave(&divert_lock, flags);
70 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
71 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
72 add_timer(&cs->timer);
73 spin_unlock_irqrestore(&divert_lock, flags);
74 break;
75
76 case DEFLECT_ALERT:
77 cs->ics.command = ISDN_CMD_REDIR; /* protocol */
78 strcpy(cs->ics.parm.setup.phone,cs->deflect_dest);
79 strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed");
80 divert_if.ll_cmd(&cs->ics);
81 spin_lock_irqsave(&divert_lock, flags);
82 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
83 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
84 add_timer(&cs->timer);
85 spin_unlock_irqrestore(&divert_lock, flags);
86 break;
87
88 case DEFLECT_AUTODEL:
89 default:
90 spin_lock_irqsave(&divert_lock, flags);
91 if (cs->prev)
92 cs->prev->next = cs->next; /* forward link */
93 else
94 divert_head = cs->next;
95 if (cs->next)
96 cs->next->prev = cs->prev; /* back link */
97 spin_unlock_irqrestore(&divert_lock, flags);
98 kfree(cs);
99 return;
100
101 } /* switch */
102} /* deflect_timer_func */
103
104
105/*****************************************/
106/* handle call forwarding de/activations */
107/* 0 = deact, 1 = act, 2 = interrogate */
108/*****************************************/
109int cf_command(int drvid, int mode,
110 u_char proc, char *msn,
111 u_char service, char *fwd_nr, ulong *procid)
112{ unsigned long flags;
113 int retval,msnlen;
114 int fwd_len;
115 char *p,*ielenp,tmp[60];
116 struct call_struc *cs;
117
118 if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */
119 if ((proc & 0x7F) > 2) return(-EINVAL);
120 proc &= 3;
121 p = tmp;
122 *p++ = 0x30; /* enumeration */
123 ielenp = p++; /* remember total length position */
124 *p++ = 0xa; /* proc tag */
125 *p++ = 1; /* length */
126 *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */
127 *p++ = 0xa; /* service tag */
128 *p++ = 1; /* length */
129 *p++ = service; /* service to handle */
130
131 if (mode == 1)
132 { if (!*fwd_nr) return(-EINVAL); /* destination missing */
133 if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */
134 fwd_len = strlen(fwd_nr);
135 *p++ = 0x30; /* number enumeration */
136 *p++ = fwd_len + 2; /* complete forward to len */
137 *p++ = 0x80; /* fwd to nr */
138 *p++ = fwd_len; /* length of number */
139 strcpy(p,fwd_nr); /* copy number */
140 p += fwd_len; /* pointer beyond fwd */
141 } /* activate */
142
143 msnlen = strlen(msn);
144 *p++ = 0x80; /* msn number */
145 if (msnlen > 1)
146 { *p++ = msnlen; /* length */
147 strcpy(p,msn);
148 p += msnlen;
149 }
150 else *p++ = 0;
151
152 *ielenp = p - ielenp - 1; /* set total IE length */
153
154 /* allocate mem for information struct */
155 if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
156 return(-ENOMEM); /* no memory */
157 init_timer(&cs->timer);
158 cs->info[0] = '\0';
159 cs->timer.function = deflect_timer_expire;
160 cs->timer.data = (ulong) cs; /* pointer to own structure */
161 cs->ics.driver = drvid;
162 cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */
163 cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */
164 cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */
165 cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */
166 cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */
167 cs->ics.parm.dss1_io.data = tmp; /* start of buffer */
168
169 spin_lock_irqsave(&divert_lock, flags);
170 cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */
171 spin_unlock_irqrestore(&divert_lock, flags);
172 *procid = cs->ics.parm.dss1_io.ll_id;
173
174 sprintf(cs->info,"%d 0x%lx %s%s 0 %s %02x %d%s%s\n",
175 (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,
176 cs->ics.parm.dss1_io.ll_id,
177 (mode != 2) ? "" : "0 ",
178 divert_if.drv_to_name(cs->ics.driver),
179 msn,
180 service & 0xFF,
181 proc,
182 (mode != 1) ? "" : " 0 ",
183 (mode != 1) ? "" : fwd_nr);
184
185 retval = divert_if.ll_cmd(&cs->ics); /* excute command */
186
187 if (!retval)
188 { cs->prev = NULL;
189 spin_lock_irqsave(&divert_lock, flags);
190 cs->next = divert_head;
191 divert_head = cs;
192 spin_unlock_irqrestore(&divert_lock, flags);
193 }
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{ struct call_struc *cs;
205 isdn_ctrl ic;
206 unsigned long flags;
207 int i;
208
209 if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */
210 cs = divert_head; /* start of parameter list */
211 while (cs)
212 { if (cs->divert_id == callid) break; /* found */
213 cs = cs->next;
214 } /* search entry */
215 if (!cs) return(-EINVAL); /* invalid callid */
216
217 ic.driver = cs->ics.driver;
218 ic.arg = cs->ics.arg;
219 i = -EINVAL;
220 if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */
221 switch (cmd & 0x7F)
222 { case 0: /* hangup */
223 del_timer(&cs->timer);
224 ic.command = ISDN_CMD_HANGUP;
225 i = divert_if.ll_cmd(&ic);
226 spin_lock_irqsave(&divert_lock, flags);
227 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
228 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
229 add_timer(&cs->timer);
230 spin_unlock_irqrestore(&divert_lock, flags);
231 break;
232
233 case 1: /* alert */
234 if (cs->akt_state == DEFLECT_ALERT) return(0);
235 cmd &= 0x7F; /* never wait */
236 del_timer(&cs->timer);
237 ic.command = ISDN_CMD_ALERT;
238 if ((i = divert_if.ll_cmd(&ic)))
239 {
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 }
246 else
247 cs->akt_state = DEFLECT_ALERT;
248 break;
249
250 case 2: /* redir */
251 del_timer(&cs->timer);
252 strcpy(cs->ics.parm.setup.phone, to_nr);
253 strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");
254 ic.command = ISDN_CMD_REDIR;
255 if ((i = divert_if.ll_cmd(&ic)))
256 {
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 }
263 else
264 cs->akt_state = DEFLECT_ALERT;
265 break;
266
267 } /* switch */
268 return(i);
269} /* deflect_extern_action */
270
271/********************************/
272/* insert a new rule before idx */
273/********************************/
274int insertrule(int idx, divert_rule *newrule)
275{ struct deflect_struc *ds,*ds1=NULL;
276 unsigned long flags;
277
278 if (!(ds = (struct deflect_struc *) kmalloc(sizeof(struct deflect_struc),
279 GFP_KERNEL)))
280 return(-ENOMEM); /* no memory */
281
282 ds->rule = *newrule; /* set rule */
283
284 spin_lock_irqsave(&divert_lock, flags);
285
286 if (idx >= 0)
287 { ds1 = table_head;
288 while ((ds1) && (idx > 0))
289 { idx--;
290 ds1 = ds1->next;
291 }
292 if (!ds1) idx = -1;
293 }
294
295 if (idx < 0)
296 { ds->prev = table_tail; /* previous entry */
297 ds->next = NULL; /* end of chain */
298 if (ds->prev)
299 ds->prev->next = ds; /* last forward */
300 else
301 table_head = ds; /* is first entry */
302 table_tail = ds; /* end of queue */
303 }
304 else
305 { ds->next = ds1; /* next entry */
306 ds->prev = ds1->prev; /* prev entry */
307 ds1->prev = ds; /* backward chain old element */
308 if (!ds->prev)
309 table_head = ds; /* first element */
310 }
311
312 spin_unlock_irqrestore(&divert_lock, flags);
313 return(0);
314} /* insertrule */
315
316/***********************************/
317/* delete the rule at position idx */
318/***********************************/
319int deleterule(int idx)
320{ struct deflect_struc *ds,*ds1;
321 unsigned long flags;
322
323 if (idx < 0)
324 { spin_lock_irqsave(&divert_lock, flags);
325 ds = table_head;
326 table_head = NULL;
327 table_tail = NULL;
328 spin_unlock_irqrestore(&divert_lock, flags);
329 while (ds)
330 { ds1 = ds;
331 ds = ds->next;
332 kfree(ds1);
333 }
334 return(0);
335 }
336
337 spin_lock_irqsave(&divert_lock, flags);
338 ds = table_head;
339
340 while ((ds) && (idx > 0))
341 { idx--;
342 ds = ds->next;
343 }
344
345 if (!ds)
346 {
347 spin_unlock_irqrestore(&divert_lock, flags);
348 return(-EINVAL);
349 }
350
351 if (ds->next)
352 ds->next->prev = ds->prev; /* backward chain */
353 else
354 table_tail = ds->prev; /* end of chain */
355
356 if (ds->prev)
357 ds->prev->next = ds->next; /* forward chain */
358 else
359 table_head = ds->next; /* start of chain */
360
361 spin_unlock_irqrestore(&divert_lock, flags);
362 kfree(ds);
363 return(0);
364} /* deleterule */
365
366/*******************************************/
367/* get a pointer to a specific rule number */
368/*******************************************/
369divert_rule *getruleptr(int idx)
370{ struct deflect_struc *ds = table_head;
371
372 if (idx < 0) return(NULL);
373 while ((ds) && (idx >= 0))
374 { if (!(idx--))
375 { return(&ds->rule);
376 break;
377 }
378 ds = ds->next;
379 }
380 return(NULL);
381} /* getruleptr */
382
383/*************************************************/
384/* called from common module on an incoming call */
385/*************************************************/
386int isdn_divert_icall(isdn_ctrl *ic)
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")) || (ic->parm.setup.phone[0]))
424 { p = dv->rule.caller;
425 p1 = ic->parm.setup.phone;
426 accept = 0;
427 while (*p)
428 { /* complete compare */
429 if (*p == '-')
430 { accept = 1; /* call accepted */
431 break;
432 }
433 if (*p++ != *p1++)
434 break; /* not accepted */
435 if ((!*p) && (!*p1))
436 accept = 1;
437 } /* complete compare */
438 if (!accept) continue; /* not accepted */
439 }
440
441 switch (dv->rule.action)
442 { case DEFLECT_IGNORE:
443 return(0);
444 break;
445
446 case DEFLECT_ALERT:
447 case DEFLECT_PROCEED:
448 case DEFLECT_REPORT:
449 case DEFLECT_REJECT:
450 if (dv->rule.action == DEFLECT_PROCEED)
451 if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))
452 return(0); /* no external deflection needed */
453 if (!(cs = (struct call_struc *) kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
454 return(0); /* no memory */
455 init_timer(&cs->timer);
456 cs->info[0] = '\0';
457 cs->timer.function = deflect_timer_expire;
458 cs->timer.data = (ulong) cs; /* pointer to own structure */
459
460 cs->ics = *ic; /* copy incoming data */
461 if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0");
462 if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0");
463 cs->ics.parm.setup.screen = dv->rule.screen;
464 if (dv->rule.waittime)
465 cs->timer.expires = jiffies + (HZ * dv->rule.waittime);
466 else
467 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,"Testtext direct");
480 ic->parm.setup.screen = dv->rule.screen;
481 strcpy(ic->parm.setup.phone,dv->rule.to_nr);
482 cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
483 cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
484 retval = 5;
485 }
486 else
487 retval = 1; /* alerting */
488 }
489 else
490 { cs->deflect_dest[0] = '\0';
491 retval = 4; /* only proceed */
492 }
493 sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
494 cs->akt_state,
495 cs->divert_id,
496 divert_if.drv_to_name(cs->ics.driver),
497 (ic->command == ISDN_STAT_ICALLW) ? "1":"0",
498 cs->ics.parm.setup.phone,
499 cs->ics.parm.setup.eazmsn,
500 cs->ics.parm.setup.si1,
501 cs->ics.parm.setup.si2,
502 cs->ics.parm.setup.screen,
503 dv->rule.waittime,
504 cs->deflect_dest);
505 if ((dv->rule.action == DEFLECT_REPORT) ||
506 (dv->rule.action == DEFLECT_REJECT))
507 { put_info_buffer(cs->info);
508 kfree(cs); /* remove */
509 return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */
510 }
511 break;
512
513 default:
514 return(0); /* ignore call */
515 break;
516 } /* switch action */
517 break;
518 } /* scan_table */
519
520 if (cs)
521 { cs->prev = NULL;
522 spin_lock_irqsave(&divert_lock, flags);
523 cs->next = divert_head;
524 divert_head = cs;
525 if (cs->timer.expires) add_timer(&cs->timer);
526 spin_unlock_irqrestore(&divert_lock, flags);
527
528 put_info_buffer(cs->info);
529 return(retval);
530 }
531 else
532 return(0);
533} /* isdn_divert_icall */
534
535
536void deleteprocs(void)
537{ struct call_struc *cs, *cs1;
538 unsigned long flags;
539
540 spin_lock_irqsave(&divert_lock, flags);
541 cs = divert_head;
542 divert_head = NULL;
543 while (cs)
544 { del_timer(&cs->timer);
545 cs1 = cs;
546 cs = cs->next;
547 kfree(cs1);
548 }
549 spin_unlock_irqrestore(&divert_lock, flags);
550} /* deleteprocs */
551
552/****************************************************/
553/* put a address including address type into buffer */
554/****************************************************/
555int put_address(char *st, u_char *p, int len)
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 }
574 else
575 if (*p == 0x80)
576 { retval = *(++p) + 2; /* total length */
577 if (retval > len) return(0);
578 len = retval - 2;
579 p++;
580 }
581 else
582 return(0); /* invalid address information */
583
584 sprintf(st,"%d ",adr_typ);
585 st += strlen(st);
586 if (!len)
587 *st++ = '-';
588 else
589 while (len--)
590 *st++ = *p++;
591 *st = '\0';
592 return(retval);
593} /* put_address */
594
595/*************************************/
596/* report a succesfull interrogation */
597/*************************************/
598int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
599{ char *src = ic->parm.dss1_io.data;
600 int restlen = ic->parm.dss1_io.datalen;
601 int cnt = 1;
602 u_char n,n1;
603 char st[90], *p, *stp;
604
605 if (restlen < 2) return(-100); /* frame too short */
606 if (*src++ != 0x30) return(-101);
607 if ((n = *src++) > 0x81) return(-102); /* invalid length field */
608 restlen -= 2; /* remaining bytes */
609 if (n == 0x80)
610 { if (restlen < 2) return(-103);
611 if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);
612 restlen -= 2;
613 }
614 else
615 if ( n == 0x81)
616 { n = *src++;
617 restlen--;
618 if (n > restlen) return(-105);
619 restlen = n;
620 }
621 else
622 if (n > restlen) return(-106);
623 else
624 restlen = n; /* standard format */
625 if (restlen < 3) return(-107); /* no procedure */
626 if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);
627 restlen -= 3;
628 if (restlen < 2) return(-109); /* list missing */
629 if (*src == 0x31)
630 { src++;
631 if ((n = *src++) > 0x81) return(-110); /* invalid length field */
632 restlen -= 2; /* remaining bytes */
633 if (n == 0x80)
634 { if (restlen < 2) return(-111);
635 if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);
636 restlen -= 2;
637 }
638 else
639 if ( n == 0x81)
640 { n = *src++;
641 restlen--;
642 if (n > restlen) return(-113);
643 restlen = n;
644 }
645 else
646 if (n > restlen) return(-114);
647 else
648 restlen = n; /* standard format */
649 } /* result list header */
650
651 while (restlen >= 2)
652 { stp = st;
653 sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id,
654 cnt++,divert_if.drv_to_name(ic->driver));
655 stp += strlen(stp);
656 if (*src++ != 0x30) return(-115); /* invalid enum */
657 n = *src++;
658 restlen -= 2;
659 if (n > restlen) return(-116); /* enum length wrong */
660 restlen -= n;
661 p = src; /* one entry */
662 src += n;
663 if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
664 stp += strlen(stp);
665 p += n1;
666 n -= n1;
667 if (n < 6) continue; /* no service and proc */
668 if ((*p++ != 0x0A) || (*p++ != 1)) continue;
669 sprintf(stp," 0x%02x ",(*p++) & 0xFF);
670 stp += strlen(stp);
671 if ((*p++ != 0x0A) || (*p++ != 1)) continue;
672 sprintf(stp,"%d ",(*p++) & 0xFF);
673 stp += strlen(stp);
674 n -= 6;
675 if (n > 2)
676 { if (*p++ != 0x30) continue;
677 if (*p > (n-2)) continue;
678 n = *p++;
679 if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
680 stp += strlen(stp);
681 }
682 sprintf(stp,"\n");
683 put_info_buffer(st);
684 } /* while restlen */
685 if (restlen) return(-117);
686 return(0);
687} /* interrogate_success */
688
689/*********************************************/
690/* callback for protocol specific extensions */
691/*********************************************/
692int prot_stat_callback(isdn_ctrl *ic)
693{ struct call_struc *cs, *cs1;
694 int i;
695 unsigned long flags;
696
697 cs = divert_head; /* start of list */
698 cs1 = NULL;
699 while (cs)
700 { if (ic->driver == cs->ics.driver)
701 { switch (cs->ics.arg)
702 { case DSS1_CMD_INVOKE:
703 if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) &&
704 (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id))
705 { switch (ic->arg)
706 { case DSS1_STAT_INVOKE_ERR:
707 sprintf(cs->info,"128 0x%lx 0x%x\n",
708 ic->parm.dss1_io.ll_id,
709 ic->parm.dss1_io.timeout);
710 put_info_buffer(cs->info);
711 break;
712
713 case DSS1_STAT_INVOKE_RES:
714 switch (cs->ics.parm.dss1_io.proc)
715 { case 7:
716 case 8:
717 put_info_buffer(cs->info);
718 break;
719
720 case 11:
721 i = interrogate_success(ic,cs);
722 if (i)
723 sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT,
724 ic->parm.dss1_io.ll_id,i);
725 put_info_buffer(cs->info);
726 break;
727
728 default:
729 printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);
730 break;
731 }
732
733
734 break;
735
736 default:
737 printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);
738 break;
739 }
740 cs1 = cs; /* remember structure */
741 cs = NULL;
742 continue; /* abort search */
743 } /* id found */
744 break;
745
746 case DSS1_CMD_INVOKE_ABORT:
747 printk(KERN_WARNING "dss1_divert unhandled invoke abort\n");
748 break;
749
750 default:
751 printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg);
752 break;
753 } /* switch ics.arg */
754 cs = cs->next;
755 } /* driver ok */
756 }
757
758 if (!cs1)
759 { printk(KERN_WARNING "dss1_divert unhandled process\n");
760 return(0);
761 }
762
763 if (cs1->ics.driver == -1)
764 {
765 spin_lock_irqsave(&divert_lock, flags);
766 del_timer(&cs1->timer);
767 if (cs1->prev)
768 cs1->prev->next = cs1->next; /* forward link */
769 else
770 divert_head = cs1->next;
771 if (cs1->next)
772 cs1->next->prev = cs1->prev; /* back link */
773 spin_unlock_irqrestore(&divert_lock, flags);
774 kfree(cs1);
775 }
776
777 return(0);
778} /* prot_stat_callback */
779
780
781/***************************/
782/* status callback from HL */
783/***************************/
784int isdn_divert_stat_callback(isdn_ctrl *ic)
785{ struct call_struc *cs, *cs1;
786 unsigned long flags;
787 int retval;
788
789 retval = -1;
790 cs = divert_head; /* start of list */
791 while (cs)
792 { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg))
793 { switch (ic->command)
794 { case ISDN_STAT_DHUP:
795 sprintf(cs->info,"129 0x%lx\n",cs->divert_id);
796 del_timer(&cs->timer);
797 cs->ics.driver = -1;
798 break;
799
800 case ISDN_STAT_CAUSE:
801 sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);
802 break;
803
804 case ISDN_STAT_REDIR:
805 sprintf(cs->info,"131 0x%lx\n",cs->divert_id);
806 del_timer(&cs->timer);
807 cs->ics.driver = -1;
808 break;
809
810 default:
811 sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));
812 break;
813 }
814 put_info_buffer(cs->info);
815 retval = 0;
816 }
817 cs1 = cs;
818 cs = cs->next;
819 if (cs1->ics.driver == -1)
820 {
821 spin_lock_irqsave(&divert_lock, flags);
822 if (cs1->prev)
823 cs1->prev->next = cs1->next; /* forward link */
824 else
825 divert_head = cs1->next;
826 if (cs1->next)
827 cs1->next->prev = cs1->prev; /* back link */
828 spin_unlock_irqrestore(&divert_lock, flags);
829 kfree(cs1);
830 }
831 }
832 return(retval); /* not found */
833} /* isdn_divert_stat_callback */
834
835
836/********************/
837/* callback from ll */
838/********************/
839int ll_callback(isdn_ctrl *ic)
840{
841 switch (ic->command)
842 { case ISDN_STAT_ICALL:
843 case ISDN_STAT_ICALLW:
844 return(isdn_divert_icall(ic));
845 break;
846
847 case ISDN_STAT_PROT:
848 if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)
849 { if (ic->arg != DSS1_STAT_INVOKE_BRD)
850 return(prot_stat_callback(ic));
851 else
852 return(0); /* DSS1 invoke broadcast */
853 }
854 else
855 return(-1); /* protocol not euro */
856
857 default:
858 return(isdn_divert_stat_callback(ic));
859 }
860} /* ll_callback */
861