diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/divert |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/divert')
-rw-r--r-- | drivers/isdn/divert/Makefile | 9 | ||||
-rw-r--r-- | drivers/isdn/divert/divert_init.c | 83 | ||||
-rw-r--r-- | drivers/isdn/divert/divert_procfs.c | 319 | ||||
-rw-r--r-- | drivers/isdn/divert/isdn_divert.c | 861 | ||||
-rw-r--r-- | drivers/isdn/divert/isdn_divert.h | 132 |
5 files changed, 1404 insertions, 0 deletions
diff --git a/drivers/isdn/divert/Makefile b/drivers/isdn/divert/Makefile new file mode 100644 index 000000000000..dd4a202e0bc2 --- /dev/null +++ b/drivers/isdn/divert/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # Makefile for the dss1_divert ISDN module | ||
2 | |||
3 | # Each configuration option enables a list of files. | ||
4 | |||
5 | obj-$(CONFIG_ISDN_DIVERSION) += dss1_divert.o | ||
6 | |||
7 | # Multipart objects. | ||
8 | |||
9 | dss1_divert-y := isdn_divert.o divert_procfs.o divert_init.o | ||
diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c new file mode 100644 index 000000000000..434e684f5dbb --- /dev/null +++ b/drivers/isdn/divert/divert_init.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* $Id divert_init.c,v 1.5.6.2 2001/01/24 22:18:17 kai Exp $ | ||
2 | * | ||
3 | * Module init for DSS1 diversion services 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/module.h> | ||
13 | #include <linux/version.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/kernel.h> | ||
16 | |||
17 | #include "isdn_divert.h" | ||
18 | |||
19 | MODULE_DESCRIPTION("ISDN4Linux: Call diversion support"); | ||
20 | MODULE_AUTHOR("Werner Cornelius"); | ||
21 | MODULE_LICENSE("GPL"); | ||
22 | |||
23 | /****************************************/ | ||
24 | /* structure containing interface to hl */ | ||
25 | /****************************************/ | ||
26 | isdn_divert_if divert_if = | ||
27 | { DIVERT_IF_MAGIC, /* magic value */ | ||
28 | DIVERT_CMD_REG, /* register cmd */ | ||
29 | ll_callback, /* callback routine from ll */ | ||
30 | NULL, /* command still not specified */ | ||
31 | NULL, /* drv_to_name */ | ||
32 | NULL, /* name_to_drv */ | ||
33 | }; | ||
34 | |||
35 | /*************************/ | ||
36 | /* Module interface code */ | ||
37 | /* no cmd line parms */ | ||
38 | /*************************/ | ||
39 | static int __init divert_init(void) | ||
40 | { int i; | ||
41 | |||
42 | if (divert_dev_init()) | ||
43 | { printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n"); | ||
44 | return(-EIO); | ||
45 | } | ||
46 | if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) | ||
47 | { divert_dev_deinit(); | ||
48 | printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n",i); | ||
49 | return(-EIO); | ||
50 | } | ||
51 | printk(KERN_INFO "dss1_divert module successfully installed\n"); | ||
52 | return(0); | ||
53 | } | ||
54 | |||
55 | /**********************/ | ||
56 | /* Module deinit code */ | ||
57 | /**********************/ | ||
58 | static void __exit divert_exit(void) | ||
59 | { | ||
60 | unsigned long flags; | ||
61 | int i; | ||
62 | |||
63 | spin_lock_irqsave(&divert_lock, flags); | ||
64 | divert_if.cmd = DIVERT_CMD_REL; /* release */ | ||
65 | if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) | ||
66 | { printk(KERN_WARNING "dss1_divert: error %d releasing module\n",i); | ||
67 | spin_unlock_irqrestore(&divert_lock, flags); | ||
68 | return; | ||
69 | } | ||
70 | if (divert_dev_deinit()) | ||
71 | { printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n"); | ||
72 | spin_unlock_irqrestore(&divert_lock, flags); | ||
73 | return; | ||
74 | } | ||
75 | spin_unlock_irqrestore(&divert_lock, flags); | ||
76 | deleterule(-1); /* delete all rules and free mem */ | ||
77 | deleteprocs(); | ||
78 | printk(KERN_INFO "dss1_divert module successfully removed \n"); | ||
79 | } | ||
80 | |||
81 | module_init(divert_init); | ||
82 | module_exit(divert_exit); | ||
83 | |||
diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c new file mode 100644 index 000000000000..e1f0d87de0eb --- /dev/null +++ b/drivers/isdn/divert/divert_procfs.c | |||
@@ -0,0 +1,319 @@ | |||
1 | /* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $ | ||
2 | * | ||
3 | * Filesystem handling for the diversion supplementary services. | ||
4 | * | ||
5 | * Copyright 1998 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/config.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/version.h> | ||
15 | #include <linux/poll.h> | ||
16 | #include <linux/smp_lock.h> | ||
17 | #ifdef CONFIG_PROC_FS | ||
18 | #include <linux/proc_fs.h> | ||
19 | #else | ||
20 | #include <linux/fs.h> | ||
21 | #endif | ||
22 | #include <linux/isdnif.h> | ||
23 | #include "isdn_divert.h" | ||
24 | |||
25 | |||
26 | /*********************************/ | ||
27 | /* Variables for interface queue */ | ||
28 | /*********************************/ | ||
29 | ulong if_used = 0; /* number of interface users */ | ||
30 | static struct divert_info *divert_info_head = NULL; /* head of queue */ | ||
31 | static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */ | ||
32 | static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */ | ||
33 | static wait_queue_head_t rd_queue; | ||
34 | |||
35 | /*********************************/ | ||
36 | /* put an info buffer into queue */ | ||
37 | /*********************************/ | ||
38 | void | ||
39 | put_info_buffer(char *cp) | ||
40 | { | ||
41 | struct divert_info *ib; | ||
42 | unsigned long flags; | ||
43 | |||
44 | if (if_used <= 0) | ||
45 | return; | ||
46 | if (!cp) | ||
47 | return; | ||
48 | if (!*cp) | ||
49 | return; | ||
50 | if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC))) | ||
51 | return; /* no memory */ | ||
52 | strcpy(ib->info_start, cp); /* set output string */ | ||
53 | ib->next = NULL; | ||
54 | spin_lock_irqsave( &divert_info_lock, flags ); | ||
55 | ib->usage_cnt = if_used; | ||
56 | if (!divert_info_head) | ||
57 | divert_info_head = ib; /* new head */ | ||
58 | else | ||
59 | divert_info_tail->next = ib; /* follows existing messages */ | ||
60 | divert_info_tail = ib; /* new tail */ | ||
61 | |||
62 | /* delete old entrys */ | ||
63 | while (divert_info_head->next) { | ||
64 | if ((divert_info_head->usage_cnt <= 0) && | ||
65 | (divert_info_head->next->usage_cnt <= 0)) { | ||
66 | ib = divert_info_head; | ||
67 | divert_info_head = divert_info_head->next; | ||
68 | kfree(ib); | ||
69 | } else | ||
70 | break; | ||
71 | } /* divert_info_head->next */ | ||
72 | spin_unlock_irqrestore( &divert_info_lock, flags ); | ||
73 | wake_up_interruptible(&(rd_queue)); | ||
74 | } /* put_info_buffer */ | ||
75 | |||
76 | /**********************************/ | ||
77 | /* deflection device read routine */ | ||
78 | /**********************************/ | ||
79 | static ssize_t | ||
80 | isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t * off) | ||
81 | { | ||
82 | struct divert_info *inf; | ||
83 | int len; | ||
84 | |||
85 | if (!*((struct divert_info **) file->private_data)) { | ||
86 | if (file->f_flags & O_NONBLOCK) | ||
87 | return -EAGAIN; | ||
88 | interruptible_sleep_on(&(rd_queue)); | ||
89 | } | ||
90 | if (!(inf = *((struct divert_info **) file->private_data))) | ||
91 | return (0); | ||
92 | |||
93 | inf->usage_cnt--; /* new usage count */ | ||
94 | file->private_data = &inf->next; /* next structure */ | ||
95 | if ((len = strlen(inf->info_start)) <= count) { | ||
96 | if (copy_to_user(buf, inf->info_start, len)) | ||
97 | return -EFAULT; | ||
98 | *off += len; | ||
99 | return (len); | ||
100 | } | ||
101 | return (0); | ||
102 | } /* isdn_divert_read */ | ||
103 | |||
104 | /**********************************/ | ||
105 | /* deflection device write routine */ | ||
106 | /**********************************/ | ||
107 | static ssize_t | ||
108 | isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t * off) | ||
109 | { | ||
110 | return (-ENODEV); | ||
111 | } /* isdn_divert_write */ | ||
112 | |||
113 | |||
114 | /***************************************/ | ||
115 | /* select routines for various kernels */ | ||
116 | /***************************************/ | ||
117 | static unsigned int | ||
118 | isdn_divert_poll(struct file *file, poll_table * wait) | ||
119 | { | ||
120 | unsigned int mask = 0; | ||
121 | |||
122 | poll_wait(file, &(rd_queue), wait); | ||
123 | /* mask = POLLOUT | POLLWRNORM; */ | ||
124 | if (*((struct divert_info **) file->private_data)) { | ||
125 | mask |= POLLIN | POLLRDNORM; | ||
126 | } | ||
127 | return mask; | ||
128 | } /* isdn_divert_poll */ | ||
129 | |||
130 | /****************/ | ||
131 | /* Open routine */ | ||
132 | /****************/ | ||
133 | static int | ||
134 | isdn_divert_open(struct inode *ino, struct file *filep) | ||
135 | { | ||
136 | unsigned long flags; | ||
137 | |||
138 | spin_lock_irqsave( &divert_info_lock, flags ); | ||
139 | if_used++; | ||
140 | if (divert_info_head) | ||
141 | filep->private_data = &(divert_info_tail->next); | ||
142 | else | ||
143 | filep->private_data = &divert_info_head; | ||
144 | spin_unlock_irqrestore( &divert_info_lock, flags ); | ||
145 | /* start_divert(); */ | ||
146 | return nonseekable_open(ino, filep); | ||
147 | } /* isdn_divert_open */ | ||
148 | |||
149 | /*******************/ | ||
150 | /* close routine */ | ||
151 | /*******************/ | ||
152 | static int | ||
153 | isdn_divert_close(struct inode *ino, struct file *filep) | ||
154 | { | ||
155 | struct divert_info *inf; | ||
156 | unsigned long flags; | ||
157 | |||
158 | spin_lock_irqsave( &divert_info_lock, flags ); | ||
159 | if_used--; | ||
160 | inf = *((struct divert_info **) filep->private_data); | ||
161 | while (inf) { | ||
162 | inf->usage_cnt--; | ||
163 | inf = inf->next; | ||
164 | } | ||
165 | if (if_used <= 0) | ||
166 | while (divert_info_head) { | ||
167 | inf = divert_info_head; | ||
168 | divert_info_head = divert_info_head->next; | ||
169 | kfree(inf); | ||
170 | } | ||
171 | spin_unlock_irqrestore( &divert_info_lock, flags ); | ||
172 | return (0); | ||
173 | } /* isdn_divert_close */ | ||
174 | |||
175 | /*********/ | ||
176 | /* IOCTL */ | ||
177 | /*********/ | ||
178 | static int | ||
179 | isdn_divert_ioctl(struct inode *inode, struct file *file, | ||
180 | uint cmd, ulong arg) | ||
181 | { | ||
182 | divert_ioctl dioctl; | ||
183 | int i; | ||
184 | unsigned long flags; | ||
185 | divert_rule *rulep; | ||
186 | char *cp; | ||
187 | |||
188 | if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl))) | ||
189 | return -EFAULT; | ||
190 | |||
191 | switch (cmd) { | ||
192 | case IIOCGETVER: | ||
193 | dioctl.drv_version = DIVERT_IIOC_VERSION; /* set version */ | ||
194 | break; | ||
195 | |||
196 | case IIOCGETDRV: | ||
197 | if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0) | ||
198 | return (-EINVAL); | ||
199 | break; | ||
200 | |||
201 | case IIOCGETNAM: | ||
202 | cp = divert_if.drv_to_name(dioctl.getid.drvid); | ||
203 | if (!cp) | ||
204 | return (-EINVAL); | ||
205 | if (!*cp) | ||
206 | return (-EINVAL); | ||
207 | strcpy(dioctl.getid.drvnam, cp); | ||
208 | break; | ||
209 | |||
210 | case IIOCGETRULE: | ||
211 | if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) | ||
212 | return (-EINVAL); | ||
213 | dioctl.getsetrule.rule = *rulep; /* copy data */ | ||
214 | break; | ||
215 | |||
216 | case IIOCMODRULE: | ||
217 | if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) | ||
218 | return (-EINVAL); | ||
219 | spin_lock_irqsave(&divert_lock, flags); | ||
220 | *rulep = dioctl.getsetrule.rule; /* copy data */ | ||
221 | spin_unlock_irqrestore(&divert_lock, flags); | ||
222 | return (0); /* no copy required */ | ||
223 | break; | ||
224 | |||
225 | case IIOCINSRULE: | ||
226 | return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule)); | ||
227 | break; | ||
228 | |||
229 | case IIOCDELRULE: | ||
230 | return (deleterule(dioctl.getsetrule.ruleidx)); | ||
231 | break; | ||
232 | |||
233 | case IIOCDODFACT: | ||
234 | return (deflect_extern_action(dioctl.fwd_ctrl.subcmd, | ||
235 | dioctl.fwd_ctrl.callid, | ||
236 | dioctl.fwd_ctrl.to_nr)); | ||
237 | |||
238 | case IIOCDOCFACT: | ||
239 | case IIOCDOCFDIS: | ||
240 | case IIOCDOCFINT: | ||
241 | if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid)) | ||
242 | return (-EINVAL); /* invalid driver */ | ||
243 | if ((i = cf_command(dioctl.cf_ctrl.drvid, | ||
244 | (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2, | ||
245 | dioctl.cf_ctrl.cfproc, | ||
246 | dioctl.cf_ctrl.msn, | ||
247 | dioctl.cf_ctrl.service, | ||
248 | dioctl.cf_ctrl.fwd_nr, | ||
249 | &dioctl.cf_ctrl.procid))) | ||
250 | return (i); | ||
251 | break; | ||
252 | |||
253 | default: | ||
254 | return (-EINVAL); | ||
255 | } /* switch cmd */ | ||
256 | return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0; | ||
257 | } /* isdn_divert_ioctl */ | ||
258 | |||
259 | |||
260 | #ifdef CONFIG_PROC_FS | ||
261 | static struct file_operations isdn_fops = | ||
262 | { | ||
263 | .owner = THIS_MODULE, | ||
264 | .llseek = no_llseek, | ||
265 | .read = isdn_divert_read, | ||
266 | .write = isdn_divert_write, | ||
267 | .poll = isdn_divert_poll, | ||
268 | .ioctl = isdn_divert_ioctl, | ||
269 | .open = isdn_divert_open, | ||
270 | .release = isdn_divert_close, | ||
271 | }; | ||
272 | |||
273 | /****************************/ | ||
274 | /* isdn subdir in /proc/net */ | ||
275 | /****************************/ | ||
276 | static struct proc_dir_entry *isdn_proc_entry = NULL; | ||
277 | static struct proc_dir_entry *isdn_divert_entry = NULL; | ||
278 | #endif /* CONFIG_PROC_FS */ | ||
279 | |||
280 | /***************************************************************************/ | ||
281 | /* divert_dev_init must be called before the proc filesystem may be used */ | ||
282 | /***************************************************************************/ | ||
283 | int | ||
284 | divert_dev_init(void) | ||
285 | { | ||
286 | |||
287 | init_waitqueue_head(&rd_queue); | ||
288 | |||
289 | #ifdef CONFIG_PROC_FS | ||
290 | isdn_proc_entry = create_proc_entry("isdn", S_IFDIR | S_IRUGO | S_IXUGO, proc_net); | ||
291 | if (!isdn_proc_entry) | ||
292 | return (-1); | ||
293 | isdn_divert_entry = create_proc_entry("divert", S_IFREG | S_IRUGO, isdn_proc_entry); | ||
294 | if (!isdn_divert_entry) { | ||
295 | remove_proc_entry("isdn", proc_net); | ||
296 | return (-1); | ||
297 | } | ||
298 | isdn_divert_entry->proc_fops = &isdn_fops; | ||
299 | isdn_divert_entry->owner = THIS_MODULE; | ||
300 | #endif /* CONFIG_PROC_FS */ | ||
301 | |||
302 | return (0); | ||
303 | } /* divert_dev_init */ | ||
304 | |||
305 | /***************************************************************************/ | ||
306 | /* divert_dev_deinit must be called before leaving isdn when included as */ | ||
307 | /* a module. */ | ||
308 | /***************************************************************************/ | ||
309 | int | ||
310 | divert_dev_deinit(void) | ||
311 | { | ||
312 | |||
313 | #ifdef CONFIG_PROC_FS | ||
314 | remove_proc_entry("divert", isdn_proc_entry); | ||
315 | remove_proc_entry("isdn", proc_net); | ||
316 | #endif /* CONFIG_PROC_FS */ | ||
317 | |||
318 | return (0); | ||
319 | } /* divert_dev_deinit */ | ||
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 | |||
diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h new file mode 100644 index 000000000000..19439a6176a9 --- /dev/null +++ b/drivers/isdn/divert/isdn_divert.h | |||
@@ -0,0 +1,132 @@ | |||
1 | /* $Id: isdn_divert.h,v 1.5.6.1 2001/09/23 22:24:36 kai Exp $ | ||
2 | * | ||
3 | * Header for the diversion supplementary ioctl interface. | ||
4 | * | ||
5 | * Copyright 1998 by Werner Cornelius (werner@ikt.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/ioctl.h> | ||
13 | #include <linux/types.h> | ||
14 | |||
15 | /******************************************/ | ||
16 | /* IOCTL codes for interface to user prog */ | ||
17 | /******************************************/ | ||
18 | #define DIVERT_IIOC_VERSION 0x01 /* actual version */ | ||
19 | #define IIOCGETVER _IO('I', 1) /* get version of interface */ | ||
20 | #define IIOCGETDRV _IO('I', 2) /* get driver number */ | ||
21 | #define IIOCGETNAM _IO('I', 3) /* get driver name */ | ||
22 | #define IIOCGETRULE _IO('I', 4) /* read one rule */ | ||
23 | #define IIOCMODRULE _IO('I', 5) /* modify/replace a rule */ | ||
24 | #define IIOCINSRULE _IO('I', 6) /* insert/append one rule */ | ||
25 | #define IIOCDELRULE _IO('I', 7) /* delete a rule */ | ||
26 | #define IIOCDODFACT _IO('I', 8) /* hangup/reject/alert/immediately deflect a call */ | ||
27 | #define IIOCDOCFACT _IO('I', 9) /* activate control forwarding in PBX */ | ||
28 | #define IIOCDOCFDIS _IO('I',10) /* deactivate control forwarding in PBX */ | ||
29 | #define IIOCDOCFINT _IO('I',11) /* interrogate control forwarding in PBX */ | ||
30 | |||
31 | /*************************************/ | ||
32 | /* states reported through interface */ | ||
33 | /*************************************/ | ||
34 | #define DEFLECT_IGNORE 0 /* ignore incoming call */ | ||
35 | #define DEFLECT_REPORT 1 /* only report */ | ||
36 | #define DEFLECT_PROCEED 2 /* deflect when externally triggered */ | ||
37 | #define DEFLECT_ALERT 3 /* alert and deflect after delay */ | ||
38 | #define DEFLECT_REJECT 4 /* reject immediately */ | ||
39 | #define DIVERT_ACTIVATE 5 /* diversion activate */ | ||
40 | #define DIVERT_DEACTIVATE 6 /* diversion deactivate */ | ||
41 | #define DIVERT_REPORT 7 /* interrogation result */ | ||
42 | #define DEFLECT_AUTODEL 255 /* only for internal use */ | ||
43 | |||
44 | #define DEFLECT_ALL_IDS 0xFFFFFFFF /* all drivers selected */ | ||
45 | |||
46 | typedef struct | ||
47 | { ulong drvid; /* driver ids, bit mapped */ | ||
48 | char my_msn[35]; /* desired msn, subaddr allowed */ | ||
49 | char caller[35]; /* caller id, partial string with * + subaddr allowed */ | ||
50 | char to_nr[35]; /* deflected to number incl. subaddress */ | ||
51 | u_char si1,si2; /* service indicators, si1=bitmask, si1+2 0 = all */ | ||
52 | u_char screen; /* screening: 0 = no info, 1 = info, 2 = nfo with nr */ | ||
53 | u_char callopt; /* option for call handling: | ||
54 | 0 = all calls | ||
55 | 1 = only non waiting calls | ||
56 | 2 = only waiting calls */ | ||
57 | u_char action; /* desired action: | ||
58 | 0 = don't report call -> ignore | ||
59 | 1 = report call, do not allow/proceed for deflection | ||
60 | 2 = report call, send proceed, wait max waittime secs | ||
61 | 3 = report call, alert and deflect after waittime | ||
62 | 4 = report call, reject immediately | ||
63 | actions 1-2 only take place if interface is opened | ||
64 | */ | ||
65 | u_char waittime; /* maximum wait time for proceeding */ | ||
66 | } divert_rule; | ||
67 | |||
68 | typedef union | ||
69 | { int drv_version; /* return of driver version */ | ||
70 | struct | ||
71 | { int drvid; /* id of driver */ | ||
72 | char drvnam[30]; /* name of driver */ | ||
73 | } getid; | ||
74 | struct | ||
75 | { int ruleidx; /* index of rule */ | ||
76 | divert_rule rule; /* rule parms */ | ||
77 | } getsetrule; | ||
78 | struct | ||
79 | { u_char subcmd; /* 0 = hangup/reject, | ||
80 | 1 = alert, | ||
81 | 2 = deflect */ | ||
82 | ulong callid; /* id of call delivered by ascii output */ | ||
83 | char to_nr[35]; /* destination when deflect, | ||
84 | else uus1 string (maxlen 31), | ||
85 | data from rule used if empty */ | ||
86 | } fwd_ctrl; | ||
87 | struct | ||
88 | { int drvid; /* id of driver */ | ||
89 | u_char cfproc; /* cfu = 0, cfb = 1, cfnr = 2 */ | ||
90 | ulong procid; /* process id returned when no error */ | ||
91 | u_char service; /* basically coded service, 0 = all */ | ||
92 | char msn[25]; /* desired msn, empty = all */ | ||
93 | char fwd_nr[35];/* forwarded to number + subaddress */ | ||
94 | } cf_ctrl; | ||
95 | } divert_ioctl; | ||
96 | |||
97 | #ifdef __KERNEL__ | ||
98 | |||
99 | #include <linux/isdnif.h> | ||
100 | #include <linux/isdn_divertif.h> | ||
101 | |||
102 | #define AUTODEL_TIME 30 /* timeout in s to delete internal entries */ | ||
103 | |||
104 | /**************************************************/ | ||
105 | /* structure keeping ascii info for device output */ | ||
106 | /**************************************************/ | ||
107 | struct divert_info | ||
108 | { struct divert_info *next; | ||
109 | ulong usage_cnt; /* number of files still to work */ | ||
110 | char info_start[2]; /* info string start */ | ||
111 | }; | ||
112 | |||
113 | |||
114 | /**************/ | ||
115 | /* Prototypes */ | ||
116 | /**************/ | ||
117 | extern spinlock_t divert_lock; | ||
118 | |||
119 | extern ulong if_used; /* number of interface users */ | ||
120 | extern int divert_dev_deinit(void); | ||
121 | extern int divert_dev_init(void); | ||
122 | extern void put_info_buffer(char *); | ||
123 | extern int ll_callback(isdn_ctrl *); | ||
124 | extern isdn_divert_if divert_if; | ||
125 | extern divert_rule *getruleptr(int); | ||
126 | extern int insertrule(int, divert_rule *); | ||
127 | extern int deleterule(int); | ||
128 | extern void deleteprocs(void); | ||
129 | extern int deflect_extern_action(u_char, ulong, char *); | ||
130 | extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *); | ||
131 | |||
132 | #endif /* __KERNEL__ */ | ||