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