Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *
0004  * general timer device for using in ISDN stacks
0005  *
0006  * Author   Karsten Keil <kkeil@novell.com>
0007  *
0008  * Copyright 2008  by Karsten Keil <kkeil@novell.com>
0009  */
0010 
0011 #include <linux/poll.h>
0012 #include <linux/vmalloc.h>
0013 #include <linux/slab.h>
0014 #include <linux/timer.h>
0015 #include <linux/miscdevice.h>
0016 #include <linux/module.h>
0017 #include <linux/mISDNif.h>
0018 #include <linux/mutex.h>
0019 #include <linux/sched/signal.h>
0020 
0021 #include "core.h"
0022 
0023 static DEFINE_MUTEX(mISDN_mutex);
0024 static u_int    *debug;
0025 
0026 
0027 struct mISDNtimerdev {
0028     int         next_id;
0029     struct list_head    pending;
0030     struct list_head    expired;
0031     wait_queue_head_t   wait;
0032     u_int           work;
0033     spinlock_t      lock; /* protect lists */
0034 };
0035 
0036 struct mISDNtimer {
0037     struct list_head    list;
0038     struct  mISDNtimerdev   *dev;
0039     struct timer_list   tl;
0040     int         id;
0041 };
0042 
0043 static int
0044 mISDN_open(struct inode *ino, struct file *filep)
0045 {
0046     struct mISDNtimerdev    *dev;
0047 
0048     if (*debug & DEBUG_TIMER)
0049         printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
0050     dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
0051     if (!dev)
0052         return -ENOMEM;
0053     dev->next_id = 1;
0054     INIT_LIST_HEAD(&dev->pending);
0055     INIT_LIST_HEAD(&dev->expired);
0056     spin_lock_init(&dev->lock);
0057     dev->work = 0;
0058     init_waitqueue_head(&dev->wait);
0059     filep->private_data = dev;
0060     return nonseekable_open(ino, filep);
0061 }
0062 
0063 static int
0064 mISDN_close(struct inode *ino, struct file *filep)
0065 {
0066     struct mISDNtimerdev    *dev = filep->private_data;
0067     struct list_head    *list = &dev->pending;
0068     struct mISDNtimer   *timer, *next;
0069 
0070     if (*debug & DEBUG_TIMER)
0071         printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
0072 
0073     spin_lock_irq(&dev->lock);
0074     while (!list_empty(list)) {
0075         timer = list_first_entry(list, struct mISDNtimer, list);
0076         spin_unlock_irq(&dev->lock);
0077         del_timer_sync(&timer->tl);
0078         spin_lock_irq(&dev->lock);
0079         /* it might have been moved to ->expired */
0080         list_del(&timer->list);
0081         kfree(timer);
0082     }
0083     spin_unlock_irq(&dev->lock);
0084 
0085     list_for_each_entry_safe(timer, next, &dev->expired, list) {
0086         kfree(timer);
0087     }
0088     kfree(dev);
0089     return 0;
0090 }
0091 
0092 static ssize_t
0093 mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
0094 {
0095     struct mISDNtimerdev    *dev = filep->private_data;
0096     struct list_head *list = &dev->expired;
0097     struct mISDNtimer   *timer;
0098     int ret = 0;
0099 
0100     if (*debug & DEBUG_TIMER)
0101         printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
0102                filep, buf, (int)count, off);
0103 
0104     if (count < sizeof(int))
0105         return -ENOSPC;
0106 
0107     spin_lock_irq(&dev->lock);
0108     while (list_empty(list) && (dev->work == 0)) {
0109         spin_unlock_irq(&dev->lock);
0110         if (filep->f_flags & O_NONBLOCK)
0111             return -EAGAIN;
0112         wait_event_interruptible(dev->wait, (dev->work ||
0113                              !list_empty(list)));
0114         if (signal_pending(current))
0115             return -ERESTARTSYS;
0116         spin_lock_irq(&dev->lock);
0117     }
0118     if (dev->work)
0119         dev->work = 0;
0120     if (!list_empty(list)) {
0121         timer = list_first_entry(list, struct mISDNtimer, list);
0122         list_del(&timer->list);
0123         spin_unlock_irq(&dev->lock);
0124         if (put_user(timer->id, (int __user *)buf))
0125             ret = -EFAULT;
0126         else
0127             ret = sizeof(int);
0128         kfree(timer);
0129     } else {
0130         spin_unlock_irq(&dev->lock);
0131     }
0132     return ret;
0133 }
0134 
0135 static __poll_t
0136 mISDN_poll(struct file *filep, poll_table *wait)
0137 {
0138     struct mISDNtimerdev    *dev = filep->private_data;
0139     __poll_t        mask = EPOLLERR;
0140 
0141     if (*debug & DEBUG_TIMER)
0142         printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
0143     if (dev) {
0144         poll_wait(filep, &dev->wait, wait);
0145         mask = 0;
0146         if (dev->work || !list_empty(&dev->expired))
0147             mask |= (EPOLLIN | EPOLLRDNORM);
0148         if (*debug & DEBUG_TIMER)
0149             printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
0150                    dev->work, list_empty(&dev->expired));
0151     }
0152     return mask;
0153 }
0154 
0155 static void
0156 dev_expire_timer(struct timer_list *t)
0157 {
0158     struct mISDNtimer *timer = from_timer(timer, t, tl);
0159     u_long          flags;
0160 
0161     spin_lock_irqsave(&timer->dev->lock, flags);
0162     if (timer->id >= 0)
0163         list_move_tail(&timer->list, &timer->dev->expired);
0164     wake_up_interruptible(&timer->dev->wait);
0165     spin_unlock_irqrestore(&timer->dev->lock, flags);
0166 }
0167 
0168 static int
0169 misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
0170 {
0171     int         id;
0172     struct mISDNtimer   *timer;
0173 
0174     if (!timeout) {
0175         dev->work = 1;
0176         wake_up_interruptible(&dev->wait);
0177         id = 0;
0178     } else {
0179         timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
0180         if (!timer)
0181             return -ENOMEM;
0182         timer->dev = dev;
0183         timer_setup(&timer->tl, dev_expire_timer, 0);
0184         spin_lock_irq(&dev->lock);
0185         id = timer->id = dev->next_id++;
0186         if (dev->next_id < 0)
0187             dev->next_id = 1;
0188         list_add_tail(&timer->list, &dev->pending);
0189         timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
0190         add_timer(&timer->tl);
0191         spin_unlock_irq(&dev->lock);
0192     }
0193     return id;
0194 }
0195 
0196 static int
0197 misdn_del_timer(struct mISDNtimerdev *dev, int id)
0198 {
0199     struct mISDNtimer   *timer;
0200 
0201     spin_lock_irq(&dev->lock);
0202     list_for_each_entry(timer, &dev->pending, list) {
0203         if (timer->id == id) {
0204             list_del_init(&timer->list);
0205             timer->id = -1;
0206             spin_unlock_irq(&dev->lock);
0207             del_timer_sync(&timer->tl);
0208             kfree(timer);
0209             return id;
0210         }
0211     }
0212     spin_unlock_irq(&dev->lock);
0213     return 0;
0214 }
0215 
0216 static long
0217 mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
0218 {
0219     struct mISDNtimerdev    *dev = filep->private_data;
0220     int         id, tout, ret = 0;
0221 
0222 
0223     if (*debug & DEBUG_TIMER)
0224         printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
0225                filep, cmd, arg);
0226     mutex_lock(&mISDN_mutex);
0227     switch (cmd) {
0228     case IMADDTIMER:
0229         if (get_user(tout, (int __user *)arg)) {
0230             ret = -EFAULT;
0231             break;
0232         }
0233         id = misdn_add_timer(dev, tout);
0234         if (*debug & DEBUG_TIMER)
0235             printk(KERN_DEBUG "%s add %d id %d\n", __func__,
0236                    tout, id);
0237         if (id < 0) {
0238             ret = id;
0239             break;
0240         }
0241         if (put_user(id, (int __user *)arg))
0242             ret = -EFAULT;
0243         break;
0244     case IMDELTIMER:
0245         if (get_user(id, (int __user *)arg)) {
0246             ret = -EFAULT;
0247             break;
0248         }
0249         if (*debug & DEBUG_TIMER)
0250             printk(KERN_DEBUG "%s del id %d\n", __func__, id);
0251         id = misdn_del_timer(dev, id);
0252         if (put_user(id, (int __user *)arg))
0253             ret = -EFAULT;
0254         break;
0255     default:
0256         ret = -EINVAL;
0257     }
0258     mutex_unlock(&mISDN_mutex);
0259     return ret;
0260 }
0261 
0262 static const struct file_operations mISDN_fops = {
0263     .owner      = THIS_MODULE,
0264     .read       = mISDN_read,
0265     .poll       = mISDN_poll,
0266     .unlocked_ioctl = mISDN_ioctl,
0267     .open       = mISDN_open,
0268     .release    = mISDN_close,
0269     .llseek     = no_llseek,
0270 };
0271 
0272 static struct miscdevice mISDNtimer = {
0273     .minor  = MISC_DYNAMIC_MINOR,
0274     .name   = "mISDNtimer",
0275     .fops   = &mISDN_fops,
0276 };
0277 
0278 int
0279 mISDN_inittimer(u_int *deb)
0280 {
0281     int err;
0282 
0283     debug = deb;
0284     err = misc_register(&mISDNtimer);
0285     if (err)
0286         printk(KERN_WARNING "mISDN: Could not register timer device\n");
0287     return err;
0288 }
0289 
0290 void mISDN_timer_cleanup(void)
0291 {
0292     misc_deregister(&mISDNtimer);
0293 }