* better
[mascara-docs.git] / i386 / linux-2.3.21 / drivers / isdn / divert / divert_procfs.c
blob035aa363613a808e1d9868703edb32e785f4f1c3
1 /*
2 * $Id: divert_procfs.c,v 1.4 1999/08/06 07:42:48 calle Exp $
4 * Filesystem handling for the diversion supplementary services.
6 * Copyright 1998 by Werner Cornelius (werner@isdn4linux.de)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * $Log: divert_procfs.c,v $
23 * Revision 1.4 1999/08/06 07:42:48 calle
24 * Added COMPAT_HAS_NEW_WAITQ for rd_queue for newer kernels.
26 * Revision 1.3 1999/07/05 20:21:41 werner
27 * changes to use diversion sources for all kernel versions.
28 * removed static device, only proc filesystem used
30 * Revision 1.2 1999/07/04 21:37:31 werner
31 * Ported from kernel version 2.0
37 #include <linux/config.h>
38 #define __NO_VERSION__
39 #include <linux/module.h>
40 #include <linux/version.h>
41 #if (LINUX_VERSION_CODE >= 0x020117)
42 #include <linux/poll.h>
43 #endif
44 #ifdef CONFIG_PROC_FS
45 #include <linux/proc_fs.h>
46 #else
47 #include <linux/fs.h>
48 #endif
49 #include <linux/isdnif.h>
50 #include <linux/isdn_compat.h>
51 #include "isdn_divert.h"
53 /*********************************/
54 /* Variables for interface queue */
55 /*********************************/
56 ulong if_used = 0; /* number of interface users */
57 static struct divert_info *divert_info_head = NULL; /* head of queue */
58 static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */
59 #ifdef COMPAT_HAS_NEW_WAITQ
60 static wait_queue_head_t rd_queue;
61 #else
62 static struct wait_queue *rd_queue = 0; /* Queue IO */
63 #endif
65 /*********************************/
66 /* put an info buffer into queue */
67 /*********************************/
68 void put_info_buffer(char *cp)
69 { struct divert_info *ib;
70 int flags;
72 if (if_used <= 0) return;
73 if (!cp) return;
74 if (!*cp) return;
75 if (!(ib = (struct divert_info *) kmalloc(sizeof(struct divert_info)+strlen(cp), GFP_ATOMIC))) return; /* no memory */
76 strcpy(ib->info_start,cp); /* set output string */
77 ib->next = NULL;
78 save_flags(flags);
79 cli();
80 ib->usage_cnt = if_used;
81 if (!divert_info_head)
82 divert_info_head = ib; /* new head */
83 else
84 divert_info_tail->next = ib; /* follows existing messages */
85 divert_info_tail = ib; /* new tail */
86 restore_flags(flags);
88 /* delete old entrys */
89 while (divert_info_head->next)
90 { if ((divert_info_head->usage_cnt <= 0) &&
91 (divert_info_head->next->usage_cnt <= 0))
92 { ib = divert_info_head;
93 divert_info_head = divert_info_head->next;
94 kfree(ib);
96 else break;
97 } /* divert_info_head->next */
98 wake_up_interruptible(&(rd_queue));
99 } /* put_info_buffer */
101 /**********************************/
102 /* deflection device read routine */
103 /**********************************/
104 #if (LINUX_VERSION_CODE < 0x020117)
105 static int isdn_divert_read(struct inode *inode, struct file *file, char *buf, RWARG count)
106 #else
107 static ssize_t isdn_divert_read(struct file *file, char *buf, size_t count, loff_t *off)
108 #endif
109 { struct divert_info *inf;
110 int len;
112 if (!*((struct divert_info **)file->private_data))
113 { if (file->f_flags & O_NONBLOCK)
114 return -EAGAIN;
115 interruptible_sleep_on(&(rd_queue));
117 if (!(inf = *((struct divert_info **)file->private_data))) return(0);
119 inf->usage_cnt--; /* new usage count */
120 (struct divert_info **)file->private_data = &inf->next; /* next structure */
121 if ((len = strlen(inf->info_start)) <= count)
122 { if (copy_to_user(buf, inf->info_start, len))
123 return -EFAULT;
124 file->f_pos += len;
125 return(len);
127 return(0);
128 } /* isdn_divert_read */
130 /**********************************/
131 /* deflection device write routine */
132 /**********************************/
133 #if (LINUX_VERSION_CODE < 0x020117)
134 static int isdn_divert_write(struct inode *inode, struct file *file, const char *buf, RWARG count)
135 #else
136 static ssize_t isdn_divert_write(struct file *file, const char *buf, size_t count, loff_t *off)
137 #endif
139 return(-ENODEV);
140 } /* isdn_divert_write */
143 /***************************************/
144 /* select routines for various kernels */
145 /***************************************/
146 #if (LINUX_VERSION_CODE < 0x020117)
147 static int isdn_divert_select(struct inode *inode, struct file *file, int type, select_table * st)
149 if (*((struct divert_info **)file->private_data))
150 return 1;
151 else
152 { if (st) select_wait(&(rd_queue), st);
153 return 0;
155 } /* isdn_divert_select */
156 #else
157 static unsigned int isdn_divert_poll(struct file *file, poll_table * wait)
158 { unsigned int mask = 0;
160 poll_wait(file, &(rd_queue), wait);
161 /* mask = POLLOUT | POLLWRNORM; */
162 if (*((struct divert_info **)file->private_data))
163 { mask |= POLLIN | POLLRDNORM;
165 return mask;
166 } /* isdn_divert_poll */
167 #endif
169 /****************/
170 /* Open routine */
171 /****************/
172 static int isdn_divert_open(struct inode *ino, struct file *filep)
173 { int flags;
175 MOD_INC_USE_COUNT;
176 save_flags(flags);
177 cli();
178 if_used++;
179 if (divert_info_head)
180 (struct divert_info **)filep->private_data = &(divert_info_tail->next);
181 else
182 (struct divert_info **)filep->private_data = &divert_info_head;
183 restore_flags(flags);
184 /* start_divert(); */
185 return(0);
186 } /* isdn_divert_open */
188 /*******************/
189 /* close routine */
190 /*******************/
191 #if (LINUX_VERSION_CODE < 0x020117)
192 static void isdn_divert_close(struct inode *ino, struct file *filep)
193 #else
194 static int isdn_divert_close(struct inode *ino, struct file *filep)
195 #endif
196 { struct divert_info *inf;
197 int flags;
199 save_flags(flags);
200 cli();
201 if_used--;
202 inf = *((struct divert_info **)filep->private_data);
203 while (inf)
204 { inf->usage_cnt--;
205 inf = inf->next;
207 restore_flags(flags);
208 if (if_used <= 0)
209 while (divert_info_head)
210 { inf = divert_info_head;
211 divert_info_head = divert_info_head->next;
212 kfree(inf);
214 MOD_DEC_USE_COUNT;
215 #if (LINUX_VERSION_CODE < 0x020117)
216 #else
217 return(0);
218 #endif
219 } /* isdn_divert_close */
221 /*********/
222 /* IOCTL */
223 /*********/
224 static int isdn_divert_ioctl(struct inode *inode, struct file *file,
225 uint cmd, ulong arg)
226 { divert_ioctl dioctl;
227 int i, flags;
228 divert_rule *rulep;
229 char *cp;
231 if ((i = copy_from_user(&dioctl,(char *) arg, sizeof(dioctl))))
232 return(i);
234 switch (cmd)
236 case IIOCGETVER:
237 dioctl.drv_version = DIVERT_IIOC_VERSION ; /* set version */
238 break;
240 case IIOCGETDRV:
241 if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
242 return(-EINVAL);
243 break;
245 case IIOCGETNAM:
246 cp = divert_if.drv_to_name(dioctl.getid.drvid);
247 if (!cp) return(-EINVAL);
248 if (!*cp) return(-EINVAL);
249 strcpy(dioctl.getid.drvnam,cp);
250 break;
252 case IIOCGETRULE:
253 if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
254 return(-EINVAL);
255 dioctl.getsetrule.rule = *rulep; /* copy data */
256 break;
258 case IIOCMODRULE:
259 if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
260 return(-EINVAL);
261 save_flags(flags);
262 cli();
263 *rulep = dioctl.getsetrule.rule; /* copy data */
264 restore_flags(flags);
265 return(0); /* no copy required */
266 break;
268 case IIOCINSRULE:
269 return(insertrule(dioctl.getsetrule.ruleidx,&dioctl.getsetrule.rule));
270 break;
272 case IIOCDELRULE:
273 return(deleterule(dioctl.getsetrule.ruleidx));
274 break;
276 case IIOCDODFACT:
277 return(deflect_extern_action(dioctl.fwd_ctrl.subcmd,
278 dioctl.fwd_ctrl.callid,
279 dioctl.fwd_ctrl.to_nr));
281 case IIOCDOCFACT:
282 case IIOCDOCFDIS:
283 case IIOCDOCFINT:
284 if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))
285 return(-EINVAL); /* invalid driver */
286 if ((i = cf_command(dioctl.cf_ctrl.drvid,
287 (cmd == IIOCDOCFACT) ? 1: (cmd == IIOCDOCFDIS) ? 0:2,
288 dioctl.cf_ctrl.cfproc,
289 dioctl.cf_ctrl.msn,
290 dioctl.cf_ctrl.service,
291 dioctl.cf_ctrl.fwd_nr,
292 &dioctl.cf_ctrl.procid)))
293 return(i);
294 break;
296 default:
297 return(-EINVAL);
298 } /* switch cmd */
299 return(copy_to_user((char *) arg, &dioctl, sizeof(dioctl))); /* success */
300 } /* isdn_divert_ioctl */
303 #ifdef CONFIG_PROC_FS
304 #if (LINUX_VERSION_CODE < 0x020117)
305 static LSTYPE
306 isdn_divert_lseek(struct inode *inode, struct file *file, LSARG offset, int orig)
307 #else
308 static loff_t
309 isdn_divert_lseek(struct file *file, loff_t offset, int orig)
310 #endif
312 return -ESPIPE;
315 #if (LINUX_VERSION_CODE < 0x020117)
316 static struct file_operations isdn_fops =
318 isdn_divert_lseek,
319 isdn_divert_read,
320 isdn_divert_write,
321 NULL, /* isdn_readdir */
322 isdn_divert_select, /* isdn_select */
323 isdn_divert_ioctl, /* isdn_ioctl */
324 NULL, /* isdn_mmap */
325 isdn_divert_open,
326 isdn_divert_close,
327 NULL /* fsync */
330 #else
332 static struct file_operations isdn_fops =
334 isdn_divert_lseek,
335 isdn_divert_read,
336 isdn_divert_write,
337 NULL, /* isdn_readdir */
338 isdn_divert_poll, /* isdn_poll */
339 isdn_divert_ioctl, /* isdn_ioctl */
340 NULL, /* isdn_mmap */
341 isdn_divert_open,
342 NULL, /* flush */
343 isdn_divert_close,
344 NULL /* fsync */
346 #endif /* kernel >= 2.1 */
350 * proc directories can do almost nothing..
352 struct inode_operations proc_isdn_inode_ops = {
353 &isdn_fops, /* isdn divert special file-ops */
354 NULL, /* create */
355 NULL, /* lookup */
356 NULL, /* link */
357 NULL, /* unlink */
358 NULL, /* symlink */
359 NULL, /* mkdir */
360 NULL, /* rmdir */
361 NULL, /* mknod */
362 NULL, /* rename */
363 NULL, /* readlink */
364 NULL, /* follow_link */
365 NULL, /* readpage */
366 NULL, /* writepage */
367 NULL, /* bmap */
368 NULL, /* truncate */
369 NULL /* permission */
372 /****************************/
373 /* isdn subdir in /proc/net */
374 /****************************/
375 static struct proc_dir_entry isdn_proc_entry =
376 { 0, 4, "isdn", S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0,
377 &proc_dir_inode_operations,NULL,NULL,NULL,NULL,NULL
380 static struct proc_dir_entry isdn_divert_entry =
381 { 0, 6, "divert",S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_isdn_inode_ops,
382 NULL
385 /*****************************************************************/
386 /* variables used for automatic determining existence of proc fs */
387 /*****************************************************************/
388 static int (*proc_reg_dynamic)(struct proc_dir_entry *, struct proc_dir_entry *) = NULL;
389 static int (*proc_unreg)(struct proc_dir_entry *, int) = NULL;
391 #endif CONFIG_PROC_FS
393 /***************************************************************************/
394 /* divert_dev_init must be called before the proc filesystem may be used */
395 /***************************************************************************/
396 int divert_dev_init(void)
397 { int i;
399 #ifdef COMPAT_HAS_NEW_WAITQ
400 init_waitqueue_head(&rd_queue);
401 #endif
403 #ifdef CONFIG_PROC_FS
404 #if (LINUX_VERSION_CODE < 0x020117)
405 (void *) proc_reg_dynamic = get_module_symbol("","proc_register_dynamic");
406 (void *) proc_unreg = get_module_symbol("","proc_unregister");
407 if (proc_unreg)
408 { i = proc_reg_dynamic(&proc_net,&isdn_proc_entry);
409 if (i) return(i);
410 i = proc_reg_dynamic(&isdn_proc_entry,&isdn_divert_entry);
411 if (i)
412 { proc_unreg(&proc_net,isdn_proc_entry.low_ino);
413 return(i);
415 } /* proc exists */
416 #else
417 (void *) proc_reg_dynamic = get_module_symbol("","proc_register");
418 (void *) proc_unreg = get_module_symbol("","proc_unregister");
419 if (proc_unreg)
420 { i = proc_reg_dynamic(proc_net,&isdn_proc_entry);
421 if (i) return(i);
422 i = proc_reg_dynamic(&isdn_proc_entry,&isdn_divert_entry);
423 if (i)
424 { proc_unreg(proc_net,isdn_proc_entry.low_ino);
425 return(i);
427 } /* proc exists */
428 #endif
429 #endif CONFIG_PROC_FS
431 return(0);
432 } /* divert_dev_init */
434 /***************************************************************************/
435 /* divert_dev_deinit must be called before leaving isdn when included as */
436 /* a module. */
437 /***************************************************************************/
438 int divert_dev_deinit(void)
439 { int i;
441 #ifdef CONFIG_PROC_FS
442 if (proc_unreg)
443 { i = proc_unreg(&isdn_proc_entry,isdn_divert_entry.low_ino);
444 if (i) return(i);
445 #if (LINUX_VERSION_CODE < 0x020117)
446 i = proc_unreg(&proc_net,isdn_proc_entry.low_ino);
447 #else
448 i = proc_unreg(proc_net,isdn_proc_entry.low_ino);
449 #endif
450 if (i) return(i);
451 } /* proc exists */
452 #endif CONFIG_PROC_FS
454 return(0);
455 } /* divert_dev_deinit */