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