brintos

brintos / linux-shallow public Read only

0
0
Text · 6.4 KiB · 7cfa8c6 Raw
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