293 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *4 * general timer device for using in ISDN stacks5 *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 23static DEFINE_MUTEX(mISDN_mutex);24static u_int *debug;25 26 27struct 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 36struct mISDNtimer {37 struct list_head list;38 struct mISDNtimerdev *dev;39 struct timer_list tl;40 int id;41};42 43static int44mISDN_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 63static int64mISDN_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 timer_shutdown_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 92static ssize_t93mISDN_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 else127 ret = sizeof(int);128 kfree(timer);129 } else {130 spin_unlock_irq(&dev->lock);131 }132 return ret;133}134 135static __poll_t136mISDN_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 155static void156dev_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 168static int169misdn_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 196static int197misdn_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 timer_shutdown_sync(&timer->tl);208 kfree(timer);209 return id;210 }211 }212 spin_unlock_irq(&dev->lock);213 return 0;214}215 216static long217mISDN_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 262static 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};270 271static struct miscdevice mISDNtimer = {272 .minor = MISC_DYNAMIC_MINOR,273 .name = "mISDNtimer",274 .fops = &mISDN_fops,275};276 277int278mISDN_inittimer(u_int *deb)279{280 int err;281 282 debug = deb;283 err = misc_register(&mISDNtimer);284 if (err)285 printk(KERN_WARNING "mISDN: Could not register timer device\n");286 return err;287}288 289void mISDN_timer_cleanup(void)290{291 misc_deregister(&mISDNtimer);292}293