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/module.h> 13#include <linux/poll.h> 14#include <linux/slab.h> 15#ifdef CONFIG_PROC_FS 16#include <linux/proc_fs.h> 17#else 18#include <linux/fs.h> 19#endif 20#include <linux/sched.h> 21#include <linux/isdnif.h> 22#include <net/net_namespace.h> 23#include <linux/mutex.h> 24#include "isdn_divert.h" 25 26 27/*********************************/ 28/* Variables for interface queue */ 29/*********************************/ 30ulong if_used = 0; /* number of interface users */ 31static DEFINE_MUTEX(isdn_divert_mutex); 32static struct divert_info *divert_info_head = NULL; /* head of queue */ 33static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */ 34static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */ 35static wait_queue_head_t rd_queue; 36 37/*********************************/ 38/* put an info buffer into queue */ 39/*********************************/ 40void 41put_info_buffer(char *cp) 42{ 43 struct divert_info *ib; 44 unsigned long flags; 45 46 if (if_used <= 0) 47 return; 48 if (!cp) 49 return; 50 if (!*cp) 51 return; 52 if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC))) 53 return; /* no memory */ 54 strcpy(ib->info_start, cp); /* set output string */ 55 ib->next = NULL; 56 spin_lock_irqsave(&divert_info_lock, flags); 57 ib->usage_cnt = if_used; 58 if (!divert_info_head) 59 divert_info_head = ib; /* new head */ 60 else 61 divert_info_tail->next = ib; /* follows existing messages */ 62 divert_info_tail = ib; /* new tail */ 63 64 /* delete old entrys */ 65 while (divert_info_head->next) { 66 if ((divert_info_head->usage_cnt <= 0) && 67 (divert_info_head->next->usage_cnt <= 0)) { 68 ib = divert_info_head; 69 divert_info_head = divert_info_head->next; 70 kfree(ib); 71 } else 72 break; 73 } /* divert_info_head->next */ 74 spin_unlock_irqrestore(&divert_info_lock, flags); 75 wake_up_interruptible(&(rd_queue)); 76} /* put_info_buffer */ 77 78#ifdef CONFIG_PROC_FS 79 80/**********************************/ 81/* deflection device read routine */ 82/**********************************/ 83static ssize_t 84isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t *off) 85{ 86 struct divert_info *inf; 87 int len; 88 89 if (!(inf = *((struct divert_info **) file->private_data))) { 90 if (file->f_flags & O_NONBLOCK) 91 return -EAGAIN; 92 wait_event_interruptible(rd_queue, (inf = 93 *((struct divert_info **) file->private_data))); 94 } 95 if (!inf) 96 return (0); 97 98 inf->usage_cnt--; /* new usage count */ 99 file->private_data = &inf->next; /* next structure */ 100 if ((len = strlen(inf->info_start)) <= count) { 101 if (copy_to_user(buf, inf->info_start, len)) 102 return -EFAULT; 103 *off += len; 104 return (len); 105 } 106 return (0); 107} /* isdn_divert_read */ 108 109/**********************************/ 110/* deflection device write routine */ 111/**********************************/ 112static ssize_t 113isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t *off) 114{ 115 return (-ENODEV); 116} /* isdn_divert_write */ 117 118 119/***************************************/ 120/* select routines for various kernels */ 121/***************************************/ 122static unsigned int 123isdn_divert_poll(struct file *file, poll_table *wait) 124{ 125 unsigned int mask = 0; 126 127 poll_wait(file, &(rd_queue), wait); 128 /* mask = POLLOUT | POLLWRNORM; */ 129 if (*((struct divert_info **) file->private_data)) { 130 mask |= POLLIN | POLLRDNORM; 131 } 132 return mask; 133} /* isdn_divert_poll */ 134 135/****************/ 136/* Open routine */ 137/****************/ 138static int 139isdn_divert_open(struct inode *ino, struct file *filep) 140{ 141 unsigned long flags; 142 143 spin_lock_irqsave(&divert_info_lock, flags); 144 if_used++; 145 if (divert_info_head) 146 filep->private_data = &(divert_info_tail->next); 147 else 148 filep->private_data = &divert_info_head; 149 spin_unlock_irqrestore(&divert_info_lock, flags); 150 /* start_divert(); */ 151 return nonseekable_open(ino, filep); 152} /* isdn_divert_open */ 153 154/*******************/ 155/* close routine */ 156/*******************/ 157static int 158isdn_divert_close(struct inode *ino, struct file *filep) 159{ 160 struct divert_info *inf; 161 unsigned long flags; 162 163 spin_lock_irqsave(&divert_info_lock, flags); 164 if_used--; 165 inf = *((struct divert_info **) filep->private_data); 166 while (inf) { 167 inf->usage_cnt--; 168 inf = inf->next; 169 } 170 if (if_used <= 0) 171 while (divert_info_head) { 172 inf = divert_info_head; 173 divert_info_head = divert_info_head->next; 174 kfree(inf); 175 } 176 spin_unlock_irqrestore(&divert_info_lock, flags); 177 return (0); 178} /* isdn_divert_close */ 179 180/*********/ 181/* IOCTL */ 182/*********/ 183static int isdn_divert_ioctl_unlocked(struct file *file, uint cmd, ulong arg) 184{ 185 divert_ioctl dioctl; 186 int i; 187 unsigned long flags; 188 divert_rule *rulep; 189 char *cp; 190 191 if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl))) 192 return -EFAULT; 193 194 switch (cmd) { 195 case IIOCGETVER: 196 dioctl.drv_version = DIVERT_IIOC_VERSION; /* set version */ 197 break; 198 199 case IIOCGETDRV: 200 if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0) 201 return (-EINVAL); 202 break; 203 204 case IIOCGETNAM: 205 cp = divert_if.drv_to_name(dioctl.getid.drvid); 206 if (!cp) 207 return (-EINVAL); 208 if (!*cp) 209 return (-EINVAL); 210 strcpy(dioctl.getid.drvnam, cp); 211 break; 212 213 case IIOCGETRULE: 214 if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) 215 return (-EINVAL); 216 dioctl.getsetrule.rule = *rulep; /* copy data */ 217 break; 218 219 case IIOCMODRULE: 220 if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx))) 221 return (-EINVAL); 222 spin_lock_irqsave(&divert_lock, flags); 223 *rulep = dioctl.getsetrule.rule; /* copy data */ 224 spin_unlock_irqrestore(&divert_lock, flags); 225 return (0); /* no copy required */ 226 break; 227 228 case IIOCINSRULE: 229 return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule)); 230 break; 231 232 case IIOCDELRULE: 233 return (deleterule(dioctl.getsetrule.ruleidx)); 234 break; 235 236 case IIOCDODFACT: 237 return (deflect_extern_action(dioctl.fwd_ctrl.subcmd, 238 dioctl.fwd_ctrl.callid, 239 dioctl.fwd_ctrl.to_nr)); 240 241 case IIOCDOCFACT: 242 case IIOCDOCFDIS: 243 case IIOCDOCFINT: 244 if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid)) 245 return (-EINVAL); /* invalid driver */ 246 if (strnlen(dioctl.cf_ctrl.msn, sizeof(dioctl.cf_ctrl.msn)) == 247 sizeof(dioctl.cf_ctrl.msn)) 248 return -EINVAL; 249 if (strnlen(dioctl.cf_ctrl.fwd_nr, sizeof(dioctl.cf_ctrl.fwd_nr)) == 250 sizeof(dioctl.cf_ctrl.fwd_nr)) 251 return -EINVAL; 252 if ((i = cf_command(dioctl.cf_ctrl.drvid, 253 (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2, 254 dioctl.cf_ctrl.cfproc, 255 dioctl.cf_ctrl.msn, 256 dioctl.cf_ctrl.service, 257 dioctl.cf_ctrl.fwd_nr, 258 &dioctl.cf_ctrl.procid))) 259 return (i); 260 break; 261 262 default: 263 return (-EINVAL); 264 } /* switch cmd */ 265 return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0; 266} /* isdn_divert_ioctl */ 267 268static long isdn_divert_ioctl(struct file *file, uint cmd, ulong arg) 269{ 270 long ret; 271 272 mutex_lock(&isdn_divert_mutex); 273 ret = isdn_divert_ioctl_unlocked(file, cmd, arg); 274 mutex_unlock(&isdn_divert_mutex); 275 276 return ret; 277} 278 279static const struct file_operations isdn_fops = 280{ 281 .owner = THIS_MODULE, 282 .llseek = no_llseek, 283 .read = isdn_divert_read, 284 .write = isdn_divert_write, 285 .poll = isdn_divert_poll, 286 .unlocked_ioctl = isdn_divert_ioctl, 287 .open = isdn_divert_open, 288 .release = isdn_divert_close, 289}; 290 291/****************************/ 292/* isdn subdir in /proc/net */ 293/****************************/ 294static struct proc_dir_entry *isdn_proc_entry = NULL; 295static struct proc_dir_entry *isdn_divert_entry = NULL; 296#endif /* CONFIG_PROC_FS */ 297 298/***************************************************************************/ 299/* divert_dev_init must be called before the proc filesystem may be used */ 300/***************************************************************************/ 301int 302divert_dev_init(void) 303{ 304 305 init_waitqueue_head(&rd_queue); 306 307#ifdef CONFIG_PROC_FS 308 isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net); 309 if (!isdn_proc_entry) 310 return (-1); 311 isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO, 312 isdn_proc_entry, &isdn_fops); 313 if (!isdn_divert_entry) { 314 remove_proc_entry("isdn", init_net.proc_net); 315 return (-1); 316 } 317#endif /* CONFIG_PROC_FS */ 318 319 return (0); 320} /* divert_dev_init */ 321 322/***************************************************************************/ 323/* divert_dev_deinit must be called before leaving isdn when included as */ 324/* a module. */ 325/***************************************************************************/ 326int 327divert_dev_deinit(void) 328{ 329 330#ifdef CONFIG_PROC_FS 331 remove_proc_entry("divert", isdn_proc_entry); 332 remove_proc_entry("isdn", init_net.proc_net); 333#endif /* CONFIG_PROC_FS */ 334 335 return (0); 336} /* divert_dev_deinit */ 337