linux/drivers/isdn/divert/divert_procfs.c
<<
>>
Prefs
   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#ifdef CONFIG_PROC_FS
  15#include <linux/proc_fs.h>
  16#else
  17#include <linux/fs.h>
  18#endif
  19#include <linux/isdnif.h>
  20#include <net/net_namespace.h>
  21#include "isdn_divert.h"
  22
  23
  24/*********************************/
  25/* Variables for interface queue */
  26/*********************************/
  27ulong if_used = 0;              /* number of interface users */
  28static struct divert_info *divert_info_head = NULL;     /* head of queue */
  29static struct divert_info *divert_info_tail = NULL;     /* pointer to last entry */
  30static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */
  31static wait_queue_head_t rd_queue;
  32
  33/*********************************/
  34/* put an info buffer into queue */
  35/*********************************/
  36void
  37put_info_buffer(char *cp)
  38{
  39        struct divert_info *ib;
  40        unsigned long flags;
  41
  42        if (if_used <= 0)
  43                return;
  44        if (!cp)
  45                return;
  46        if (!*cp)
  47                return;
  48        if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
  49                 return;        /* no memory */
  50        strcpy(ib->info_start, cp);     /* set output string */
  51        ib->next = NULL;
  52        spin_lock_irqsave( &divert_info_lock, flags );
  53        ib->usage_cnt = if_used;
  54        if (!divert_info_head)
  55                divert_info_head = ib;  /* new head */
  56        else
  57                divert_info_tail->next = ib;    /* follows existing messages */
  58        divert_info_tail = ib;  /* new tail */
  59
  60        /* delete old entrys */
  61        while (divert_info_head->next) {
  62                if ((divert_info_head->usage_cnt <= 0) &&
  63                    (divert_info_head->next->usage_cnt <= 0)) {
  64                        ib = divert_info_head;
  65                        divert_info_head = divert_info_head->next;
  66                        kfree(ib);
  67                } else
  68                        break;
  69        }                       /* divert_info_head->next */
  70        spin_unlock_irqrestore( &divert_info_lock, flags );
  71        wake_up_interruptible(&(rd_queue));
  72}                               /* put_info_buffer */
  73
  74#ifdef CONFIG_PROC_FS
  75
  76/**********************************/
  77/* deflection device read routine */
  78/**********************************/
  79static ssize_t
  80isdn_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/**********************************/
 107static ssize_t
 108isdn_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/***************************************/
 117static unsigned int
 118isdn_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/****************/
 133static int
 134isdn_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/*******************/
 152static int
 153isdn_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/*********/
 178static int
 179isdn_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
 259static const struct file_operations isdn_fops =
 260{
 261        .owner          = THIS_MODULE,
 262        .llseek         = no_llseek,
 263        .read           = isdn_divert_read,
 264        .write          = isdn_divert_write,
 265        .poll           = isdn_divert_poll,
 266        .ioctl          = isdn_divert_ioctl,
 267        .open           = isdn_divert_open,
 268        .release        = isdn_divert_close,                                      
 269};
 270
 271/****************************/
 272/* isdn subdir in /proc/net */
 273/****************************/
 274static struct proc_dir_entry *isdn_proc_entry = NULL;
 275static struct proc_dir_entry *isdn_divert_entry = NULL;
 276#endif  /* CONFIG_PROC_FS */
 277
 278/***************************************************************************/
 279/* divert_dev_init must be called before the proc filesystem may be used   */
 280/***************************************************************************/
 281int
 282divert_dev_init(void)
 283{
 284
 285        init_waitqueue_head(&rd_queue);
 286
 287#ifdef CONFIG_PROC_FS
 288        isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net);
 289        if (!isdn_proc_entry)
 290                return (-1);
 291        isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO,
 292                                        isdn_proc_entry, &isdn_fops);
 293        if (!isdn_divert_entry) {
 294                remove_proc_entry("isdn", init_net.proc_net);
 295                return (-1);
 296        }
 297#endif  /* CONFIG_PROC_FS */
 298
 299        return (0);
 300}                               /* divert_dev_init */
 301
 302/***************************************************************************/
 303/* divert_dev_deinit must be called before leaving isdn when included as   */
 304/* a module.                                                               */
 305/***************************************************************************/
 306int
 307divert_dev_deinit(void)
 308{
 309
 310#ifdef CONFIG_PROC_FS
 311        remove_proc_entry("divert", isdn_proc_entry);
 312        remove_proc_entry("isdn", init_net.proc_net);
 313#endif  /* CONFIG_PROC_FS */
 314
 315        return (0);
 316}                               /* divert_dev_deinit */
 317
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.