diff options
Diffstat (limited to 'drivers/isdn/divert/isdn_divert.c')
-rw-r--r-- | drivers/isdn/divert/isdn_divert.c | 861 |
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 | /**********************************/ | ||
20 | struct 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 | /********************************************/ | ||
35 | struct 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 */ | ||
45 | static struct call_struc *divert_head = NULL; /* head of remembered entrys */ | ||
46 | static ulong next_id = 1; /* next info id */ | ||
47 | static struct deflect_struc *table_head = NULL; | ||
48 | static struct deflect_struc *table_tail = NULL; | ||
49 | static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */ | ||
50 | |||
51 | DEFINE_SPINLOCK(divert_lock); | ||
52 | |||
53 | /***************************/ | ||
54 | /* timer callback function */ | ||
55 | /***************************/ | ||
56 | static 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 | /*****************************************/ | ||
109 | int 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 | /****************************************/ | ||
203 | int 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 | /********************************/ | ||
274 | int 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 | /***********************************/ | ||
319 | int 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 | /*******************************************/ | ||
369 | divert_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 | /*************************************************/ | ||
386 | int 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 | |||
536 | void 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 | /****************************************************/ | ||
555 | int 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 | /*************************************/ | ||
598 | int 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 | /*********************************************/ | ||
692 | int 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 | /***************************/ | ||
784 | int 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 | /********************/ | ||
839 | int 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 | |||