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