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