root/drivers/isdn/mISDN/timerdev.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mISDN_open
  2. mISDN_close
  3. mISDN_read
  4. mISDN_poll
  5. dev_expire_timer
  6. misdn_add_timer
  7. misdn_del_timer
  8. mISDN_ioctl
  9. mISDN_inittimer
  10. mISDN_timer_cleanup

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *
   4  * general timer device for using in ISDN stacks
   5  *
   6  * Author       Karsten Keil <kkeil@novell.com>
   7  *
   8  * Copyright 2008  by Karsten Keil <kkeil@novell.com>
   9  */
  10 
  11 #include <linux/poll.h>
  12 #include <linux/vmalloc.h>
  13 #include <linux/slab.h>
  14 #include <linux/timer.h>
  15 #include <linux/miscdevice.h>
  16 #include <linux/module.h>
  17 #include <linux/mISDNif.h>
  18 #include <linux/mutex.h>
  19 #include <linux/sched/signal.h>
  20 
  21 #include "core.h"
  22 
  23 static DEFINE_MUTEX(mISDN_mutex);
  24 static u_int    *debug;
  25 
  26 
  27 struct mISDNtimerdev {
  28         int                     next_id;
  29         struct list_head        pending;
  30         struct list_head        expired;
  31         wait_queue_head_t       wait;
  32         u_int                   work;
  33         spinlock_t              lock; /* protect lists */
  34 };
  35 
  36 struct mISDNtimer {
  37         struct list_head        list;
  38         struct  mISDNtimerdev   *dev;
  39         struct timer_list       tl;
  40         int                     id;
  41 };
  42 
  43 static int
  44 mISDN_open(struct inode *ino, struct file *filep)
  45 {
  46         struct mISDNtimerdev    *dev;
  47 
  48         if (*debug & DEBUG_TIMER)
  49                 printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
  50         dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
  51         if (!dev)
  52                 return -ENOMEM;
  53         dev->next_id = 1;
  54         INIT_LIST_HEAD(&dev->pending);
  55         INIT_LIST_HEAD(&dev->expired);
  56         spin_lock_init(&dev->lock);
  57         dev->work = 0;
  58         init_waitqueue_head(&dev->wait);
  59         filep->private_data = dev;
  60         return nonseekable_open(ino, filep);
  61 }
  62 
  63 static int
  64 mISDN_close(struct inode *ino, struct file *filep)
  65 {
  66         struct mISDNtimerdev    *dev = filep->private_data;
  67         struct list_head        *list = &dev->pending;
  68         struct mISDNtimer       *timer, *next;
  69 
  70         if (*debug & DEBUG_TIMER)
  71                 printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
  72 
  73         spin_lock_irq(&dev->lock);
  74         while (!list_empty(list)) {
  75                 timer = list_first_entry(list, struct mISDNtimer, list);
  76                 spin_unlock_irq(&dev->lock);
  77                 del_timer_sync(&timer->tl);
  78                 spin_lock_irq(&dev->lock);
  79                 /* it might have been moved to ->expired */
  80                 list_del(&timer->list);
  81                 kfree(timer);
  82         }
  83         spin_unlock_irq(&dev->lock);
  84 
  85         list_for_each_entry_safe(timer, next, &dev->expired, list) {
  86                 kfree(timer);
  87         }
  88         kfree(dev);
  89         return 0;
  90 }
  91 
  92 static ssize_t
  93 mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
  94 {
  95         struct mISDNtimerdev    *dev = filep->private_data;
  96         struct list_head *list = &dev->expired;
  97         struct mISDNtimer       *timer;
  98         int     ret = 0;
  99 
 100         if (*debug & DEBUG_TIMER)
 101                 printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
 102                        filep, buf, (int)count, off);
 103 
 104         if (count < sizeof(int))
 105                 return -ENOSPC;
 106 
 107         spin_lock_irq(&dev->lock);
 108         while (list_empty(list) && (dev->work == 0)) {
 109                 spin_unlock_irq(&dev->lock);
 110                 if (filep->f_flags & O_NONBLOCK)
 111                         return -EAGAIN;
 112                 wait_event_interruptible(dev->wait, (dev->work ||
 113                                                      !list_empty(list)));
 114                 if (signal_pending(current))
 115                         return -ERESTARTSYS;
 116                 spin_lock_irq(&dev->lock);
 117         }
 118         if (dev->work)
 119                 dev->work = 0;
 120         if (!list_empty(list)) {
 121                 timer = list_first_entry(list, struct mISDNtimer, list);
 122                 list_del(&timer->list);
 123                 spin_unlock_irq(&dev->lock);
 124                 if (put_user(timer->id, (int __user *)buf))
 125                         ret = -EFAULT;
 126                 else
 127                         ret = sizeof(int);
 128                 kfree(timer);
 129         } else {
 130                 spin_unlock_irq(&dev->lock);
 131         }
 132         return ret;
 133 }
 134 
 135 static __poll_t
 136 mISDN_poll(struct file *filep, poll_table *wait)
 137 {
 138         struct mISDNtimerdev    *dev = filep->private_data;
 139         __poll_t                mask = EPOLLERR;
 140 
 141         if (*debug & DEBUG_TIMER)
 142                 printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
 143         if (dev) {
 144                 poll_wait(filep, &dev->wait, wait);
 145                 mask = 0;
 146                 if (dev->work || !list_empty(&dev->expired))
 147                         mask |= (EPOLLIN | EPOLLRDNORM);
 148                 if (*debug & DEBUG_TIMER)
 149                         printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
 150                                dev->work, list_empty(&dev->expired));
 151         }
 152         return mask;
 153 }
 154 
 155 static void
 156 dev_expire_timer(struct timer_list *t)
 157 {
 158         struct mISDNtimer *timer = from_timer(timer, t, tl);
 159         u_long                  flags;
 160 
 161         spin_lock_irqsave(&timer->dev->lock, flags);
 162         if (timer->id >= 0)
 163                 list_move_tail(&timer->list, &timer->dev->expired);
 164         wake_up_interruptible(&timer->dev->wait);
 165         spin_unlock_irqrestore(&timer->dev->lock, flags);
 166 }
 167 
 168 static int
 169 misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
 170 {
 171         int                     id;
 172         struct mISDNtimer       *timer;
 173 
 174         if (!timeout) {
 175                 dev->work = 1;
 176                 wake_up_interruptible(&dev->wait);
 177                 id = 0;
 178         } else {
 179                 timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
 180                 if (!timer)
 181                         return -ENOMEM;
 182                 timer->dev = dev;
 183                 timer_setup(&timer->tl, dev_expire_timer, 0);
 184                 spin_lock_irq(&dev->lock);
 185                 id = timer->id = dev->next_id++;
 186                 if (dev->next_id < 0)
 187                         dev->next_id = 1;
 188                 list_add_tail(&timer->list, &dev->pending);
 189                 timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
 190                 add_timer(&timer->tl);
 191                 spin_unlock_irq(&dev->lock);
 192         }
 193         return id;
 194 }
 195 
 196 static int
 197 misdn_del_timer(struct mISDNtimerdev *dev, int id)
 198 {
 199         struct mISDNtimer       *timer;
 200 
 201         spin_lock_irq(&dev->lock);
 202         list_for_each_entry(timer, &dev->pending, list) {
 203                 if (timer->id == id) {
 204                         list_del_init(&timer->list);
 205                         timer->id = -1;
 206                         spin_unlock_irq(&dev->lock);
 207                         del_timer_sync(&timer->tl);
 208                         kfree(timer);
 209                         return id;
 210                 }
 211         }
 212         spin_unlock_irq(&dev->lock);
 213         return 0;
 214 }
 215 
 216 static long
 217 mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
 218 {
 219         struct mISDNtimerdev    *dev = filep->private_data;
 220         int                     id, tout, ret = 0;
 221 
 222 
 223         if (*debug & DEBUG_TIMER)
 224                 printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
 225                        filep, cmd, arg);
 226         mutex_lock(&mISDN_mutex);
 227         switch (cmd) {
 228         case IMADDTIMER:
 229                 if (get_user(tout, (int __user *)arg)) {
 230                         ret = -EFAULT;
 231                         break;
 232                 }
 233                 id = misdn_add_timer(dev, tout);
 234                 if (*debug & DEBUG_TIMER)
 235                         printk(KERN_DEBUG "%s add %d id %d\n", __func__,
 236                                tout, id);
 237                 if (id < 0) {
 238                         ret = id;
 239                         break;
 240                 }
 241                 if (put_user(id, (int __user *)arg))
 242                         ret = -EFAULT;
 243                 break;
 244         case IMDELTIMER:
 245                 if (get_user(id, (int __user *)arg)) {
 246                         ret = -EFAULT;
 247                         break;
 248                 }
 249                 if (*debug & DEBUG_TIMER)
 250                         printk(KERN_DEBUG "%s del id %d\n", __func__, id);
 251                 id = misdn_del_timer(dev, id);
 252                 if (put_user(id, (int __user *)arg))
 253                         ret = -EFAULT;
 254                 break;
 255         default:
 256                 ret = -EINVAL;
 257         }
 258         mutex_unlock(&mISDN_mutex);
 259         return ret;
 260 }
 261 
 262 static const struct file_operations mISDN_fops = {
 263         .owner          = THIS_MODULE,
 264         .read           = mISDN_read,
 265         .poll           = mISDN_poll,
 266         .unlocked_ioctl = mISDN_ioctl,
 267         .open           = mISDN_open,
 268         .release        = mISDN_close,
 269         .llseek         = no_llseek,
 270 };
 271 
 272 static struct miscdevice mISDNtimer = {
 273         .minor  = MISC_DYNAMIC_MINOR,
 274         .name   = "mISDNtimer",
 275         .fops   = &mISDN_fops,
 276 };
 277 
 278 int
 279 mISDN_inittimer(u_int *deb)
 280 {
 281         int     err;
 282 
 283         debug = deb;
 284         err = misc_register(&mISDNtimer);
 285         if (err)
 286                 printk(KERN_WARNING "mISDN: Could not register timer device\n");
 287         return err;
 288 }
 289 
 290 void mISDN_timer_cleanup(void)
 291 {
 292         misc_deregister(&mISDNtimer);
 293 }

/* [<][>][^][v][top][bottom][index][help] */