Path: blob/master/drivers/isdn/divert/divert_procfs.c
17627 views
/* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $1*2* Filesystem handling for the diversion supplementary services.3*4* Copyright 1998 by Werner Cornelius ([email protected])5*6* This software may be used and distributed according to the terms7* of the GNU General Public License, incorporated herein by reference.8*9*/1011#include <linux/module.h>12#include <linux/poll.h>13#include <linux/slab.h>14#ifdef CONFIG_PROC_FS15#include <linux/proc_fs.h>16#else17#include <linux/fs.h>18#endif19#include <linux/sched.h>20#include <linux/isdnif.h>21#include <net/net_namespace.h>22#include <linux/mutex.h>23#include "isdn_divert.h"242526/*********************************/27/* Variables for interface queue */28/*********************************/29ulong if_used = 0; /* number of interface users */30static DEFINE_MUTEX(isdn_divert_mutex);31static struct divert_info *divert_info_head = NULL; /* head of queue */32static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */33static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */34static wait_queue_head_t rd_queue;3536/*********************************/37/* put an info buffer into queue */38/*********************************/39void40put_info_buffer(char *cp)41{42struct divert_info *ib;43unsigned long flags;4445if (if_used <= 0)46return;47if (!cp)48return;49if (!*cp)50return;51if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))52return; /* no memory */53strcpy(ib->info_start, cp); /* set output string */54ib->next = NULL;55spin_lock_irqsave( &divert_info_lock, flags );56ib->usage_cnt = if_used;57if (!divert_info_head)58divert_info_head = ib; /* new head */59else60divert_info_tail->next = ib; /* follows existing messages */61divert_info_tail = ib; /* new tail */6263/* delete old entrys */64while (divert_info_head->next) {65if ((divert_info_head->usage_cnt <= 0) &&66(divert_info_head->next->usage_cnt <= 0)) {67ib = divert_info_head;68divert_info_head = divert_info_head->next;69kfree(ib);70} else71break;72} /* divert_info_head->next */73spin_unlock_irqrestore( &divert_info_lock, flags );74wake_up_interruptible(&(rd_queue));75} /* put_info_buffer */7677#ifdef CONFIG_PROC_FS7879/**********************************/80/* deflection device read routine */81/**********************************/82static ssize_t83isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t * off)84{85struct divert_info *inf;86int len;8788if (!*((struct divert_info **) file->private_data)) {89if (file->f_flags & O_NONBLOCK)90return -EAGAIN;91interruptible_sleep_on(&(rd_queue));92}93if (!(inf = *((struct divert_info **) file->private_data)))94return (0);9596inf->usage_cnt--; /* new usage count */97file->private_data = &inf->next; /* next structure */98if ((len = strlen(inf->info_start)) <= count) {99if (copy_to_user(buf, inf->info_start, len))100return -EFAULT;101*off += len;102return (len);103}104return (0);105} /* isdn_divert_read */106107/**********************************/108/* deflection device write routine */109/**********************************/110static ssize_t111isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t * off)112{113return (-ENODEV);114} /* isdn_divert_write */115116117/***************************************/118/* select routines for various kernels */119/***************************************/120static unsigned int121isdn_divert_poll(struct file *file, poll_table * wait)122{123unsigned int mask = 0;124125poll_wait(file, &(rd_queue), wait);126/* mask = POLLOUT | POLLWRNORM; */127if (*((struct divert_info **) file->private_data)) {128mask |= POLLIN | POLLRDNORM;129}130return mask;131} /* isdn_divert_poll */132133/****************/134/* Open routine */135/****************/136static int137isdn_divert_open(struct inode *ino, struct file *filep)138{139unsigned long flags;140141spin_lock_irqsave( &divert_info_lock, flags );142if_used++;143if (divert_info_head)144filep->private_data = &(divert_info_tail->next);145else146filep->private_data = &divert_info_head;147spin_unlock_irqrestore( &divert_info_lock, flags );148/* start_divert(); */149return nonseekable_open(ino, filep);150} /* isdn_divert_open */151152/*******************/153/* close routine */154/*******************/155static int156isdn_divert_close(struct inode *ino, struct file *filep)157{158struct divert_info *inf;159unsigned long flags;160161spin_lock_irqsave( &divert_info_lock, flags );162if_used--;163inf = *((struct divert_info **) filep->private_data);164while (inf) {165inf->usage_cnt--;166inf = inf->next;167}168if (if_used <= 0)169while (divert_info_head) {170inf = divert_info_head;171divert_info_head = divert_info_head->next;172kfree(inf);173}174spin_unlock_irqrestore( &divert_info_lock, flags );175return (0);176} /* isdn_divert_close */177178/*********/179/* IOCTL */180/*********/181static int isdn_divert_ioctl_unlocked(struct file *file, uint cmd, ulong arg)182{183divert_ioctl dioctl;184int i;185unsigned long flags;186divert_rule *rulep;187char *cp;188189if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl)))190return -EFAULT;191192switch (cmd) {193case IIOCGETVER:194dioctl.drv_version = DIVERT_IIOC_VERSION; /* set version */195break;196197case IIOCGETDRV:198if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)199return (-EINVAL);200break;201202case IIOCGETNAM:203cp = divert_if.drv_to_name(dioctl.getid.drvid);204if (!cp)205return (-EINVAL);206if (!*cp)207return (-EINVAL);208strcpy(dioctl.getid.drvnam, cp);209break;210211case IIOCGETRULE:212if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))213return (-EINVAL);214dioctl.getsetrule.rule = *rulep; /* copy data */215break;216217case IIOCMODRULE:218if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))219return (-EINVAL);220spin_lock_irqsave(&divert_lock, flags);221*rulep = dioctl.getsetrule.rule; /* copy data */222spin_unlock_irqrestore(&divert_lock, flags);223return (0); /* no copy required */224break;225226case IIOCINSRULE:227return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule));228break;229230case IIOCDELRULE:231return (deleterule(dioctl.getsetrule.ruleidx));232break;233234case IIOCDODFACT:235return (deflect_extern_action(dioctl.fwd_ctrl.subcmd,236dioctl.fwd_ctrl.callid,237dioctl.fwd_ctrl.to_nr));238239case IIOCDOCFACT:240case IIOCDOCFDIS:241case IIOCDOCFINT:242if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))243return (-EINVAL); /* invalid driver */244if ((i = cf_command(dioctl.cf_ctrl.drvid,245(cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2,246dioctl.cf_ctrl.cfproc,247dioctl.cf_ctrl.msn,248dioctl.cf_ctrl.service,249dioctl.cf_ctrl.fwd_nr,250&dioctl.cf_ctrl.procid)))251return (i);252break;253254default:255return (-EINVAL);256} /* switch cmd */257return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0;258} /* isdn_divert_ioctl */259260static long isdn_divert_ioctl(struct file *file, uint cmd, ulong arg)261{262long ret;263264mutex_lock(&isdn_divert_mutex);265ret = isdn_divert_ioctl_unlocked(file, cmd, arg);266mutex_unlock(&isdn_divert_mutex);267268return ret;269}270271static const struct file_operations isdn_fops =272{273.owner = THIS_MODULE,274.llseek = no_llseek,275.read = isdn_divert_read,276.write = isdn_divert_write,277.poll = isdn_divert_poll,278.unlocked_ioctl = isdn_divert_ioctl,279.open = isdn_divert_open,280.release = isdn_divert_close,281};282283/****************************/284/* isdn subdir in /proc/net */285/****************************/286static struct proc_dir_entry *isdn_proc_entry = NULL;287static struct proc_dir_entry *isdn_divert_entry = NULL;288#endif /* CONFIG_PROC_FS */289290/***************************************************************************/291/* divert_dev_init must be called before the proc filesystem may be used */292/***************************************************************************/293int294divert_dev_init(void)295{296297init_waitqueue_head(&rd_queue);298299#ifdef CONFIG_PROC_FS300isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net);301if (!isdn_proc_entry)302return (-1);303isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO,304isdn_proc_entry, &isdn_fops);305if (!isdn_divert_entry) {306remove_proc_entry("isdn", init_net.proc_net);307return (-1);308}309#endif /* CONFIG_PROC_FS */310311return (0);312} /* divert_dev_init */313314/***************************************************************************/315/* divert_dev_deinit must be called before leaving isdn when included as */316/* a module. */317/***************************************************************************/318int319divert_dev_deinit(void)320{321322#ifdef CONFIG_PROC_FS323remove_proc_entry("divert", isdn_proc_entry);324remove_proc_entry("isdn", init_net.proc_net);325#endif /* CONFIG_PROC_FS */326327return (0);328} /* divert_dev_deinit */329330331