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)
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)
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>
45 #include <linux/proc_fs.h>
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
;
62 static struct wait_queue
*rd_queue
= 0; /* Queue IO */
65 /*********************************/
66 /* put an info buffer into queue */
67 /*********************************/
68 void put_info_buffer(char *cp
)
69 { struct divert_info
*ib
;
72 if (if_used
<= 0) 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 */
80 ib
->usage_cnt
= if_used
;
81 if (!divert_info_head
)
82 divert_info_head
= ib
; /* new head */
84 divert_info_tail
->next
= ib
; /* follows existing messages */
85 divert_info_tail
= ib
; /* new tail */
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
;
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
)
107 static ssize_t
isdn_divert_read(struct file
*file
, char *buf
, size_t count
, loff_t
*off
)
109 { struct divert_info
*inf
;
112 if (!*((struct divert_info
**)file
->private_data
))
113 { if (file
->f_flags
& O_NONBLOCK
)
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
))
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
)
136 static ssize_t
isdn_divert_write(struct file
*file
, const char *buf
, size_t count
, loff_t
*off
)
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
))
152 { if (st
) select_wait(&(rd_queue
), st
);
155 } /* isdn_divert_select */
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
;
166 } /* isdn_divert_poll */
172 static int isdn_divert_open(struct inode
*ino
, struct file
*filep
)
179 if (divert_info_head
)
180 (struct divert_info
**)filep
->private_data
= &(divert_info_tail
->next
);
182 (struct divert_info
**)filep
->private_data
= &divert_info_head
;
183 restore_flags(flags
);
184 /* start_divert(); */
186 } /* isdn_divert_open */
188 /*******************/
190 /*******************/
191 #if (LINUX_VERSION_CODE < 0x020117)
192 static void isdn_divert_close(struct inode
*ino
, struct file
*filep
)
194 static int isdn_divert_close(struct inode
*ino
, struct file
*filep
)
196 { struct divert_info
*inf
;
202 inf
= *((struct divert_info
**)filep
->private_data
);
207 restore_flags(flags
);
209 while (divert_info_head
)
210 { inf
= divert_info_head
;
211 divert_info_head
= divert_info_head
->next
;
215 #if (LINUX_VERSION_CODE < 0x020117)
219 } /* isdn_divert_close */
224 static int isdn_divert_ioctl(struct inode
*inode
, struct file
*file
,
226 { divert_ioctl dioctl
;
231 if ((i
= copy_from_user(&dioctl
,(char *) arg
, sizeof(dioctl
))))
237 dioctl
.drv_version
= DIVERT_IIOC_VERSION
; /* set version */
241 if ((dioctl
.getid
.drvid
= divert_if
.name_to_drv(dioctl
.getid
.drvnam
)) < 0)
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
);
253 if (!(rulep
= getruleptr(dioctl
.getsetrule
.ruleidx
)))
255 dioctl
.getsetrule
.rule
= *rulep
; /* copy data */
259 if (!(rulep
= getruleptr(dioctl
.getsetrule
.ruleidx
)))
263 *rulep
= dioctl
.getsetrule
.rule
; /* copy data */
264 restore_flags(flags
);
265 return(0); /* no copy required */
269 return(insertrule(dioctl
.getsetrule
.ruleidx
,&dioctl
.getsetrule
.rule
));
273 return(deleterule(dioctl
.getsetrule
.ruleidx
));
277 return(deflect_extern_action(dioctl
.fwd_ctrl
.subcmd
,
278 dioctl
.fwd_ctrl
.callid
,
279 dioctl
.fwd_ctrl
.to_nr
));
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
,
290 dioctl
.cf_ctrl
.service
,
291 dioctl
.cf_ctrl
.fwd_nr
,
292 &dioctl
.cf_ctrl
.procid
)))
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)
306 isdn_divert_lseek(struct inode
*inode
, struct file
*file
, LSARG offset
, int orig
)
309 isdn_divert_lseek(struct file
*file
, loff_t offset
, int orig
)
315 #if (LINUX_VERSION_CODE < 0x020117)
316 static struct file_operations isdn_fops
=
321 NULL
, /* isdn_readdir */
322 isdn_divert_select
, /* isdn_select */
323 isdn_divert_ioctl
, /* isdn_ioctl */
324 NULL
, /* isdn_mmap */
332 static struct file_operations isdn_fops
=
337 NULL
, /* isdn_readdir */
338 isdn_divert_poll
, /* isdn_poll */
339 isdn_divert_ioctl
, /* isdn_ioctl */
340 NULL
, /* isdn_mmap */
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 */
364 NULL
, /* follow_link */
366 NULL
, /* writepage */
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
,
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)
399 #ifdef COMPAT_HAS_NEW_WAITQ
400 init_waitqueue_head(&rd_queue
);
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");
408 { i
= proc_reg_dynamic(&proc_net
,&isdn_proc_entry
);
410 i
= proc_reg_dynamic(&isdn_proc_entry
,&isdn_divert_entry
);
412 { proc_unreg(&proc_net
,isdn_proc_entry
.low_ino
);
417 (void *) proc_reg_dynamic
= get_module_symbol("","proc_register");
418 (void *) proc_unreg
= get_module_symbol("","proc_unregister");
420 { i
= proc_reg_dynamic(proc_net
,&isdn_proc_entry
);
422 i
= proc_reg_dynamic(&isdn_proc_entry
,&isdn_divert_entry
);
424 { proc_unreg(proc_net
,isdn_proc_entry
.low_ino
);
429 #endif CONFIG_PROC_FS
432 } /* divert_dev_init */
434 /***************************************************************************/
435 /* divert_dev_deinit must be called before leaving isdn when included as */
437 /***************************************************************************/
438 int divert_dev_deinit(void)
441 #ifdef CONFIG_PROC_FS
443 { i
= proc_unreg(&isdn_proc_entry
,isdn_divert_entry
.low_ino
);
445 #if (LINUX_VERSION_CODE < 0x020117)
446 i
= proc_unreg(&proc_net
,isdn_proc_entry
.low_ino
);
448 i
= proc_unreg(proc_net
,isdn_proc_entry
.low_ino
);
452 #endif CONFIG_PROC_FS
455 } /* divert_dev_deinit */