Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/isdn/mISDN/timerdev.c
15111 views
1
/*
2
*
3
* general timer device for using in ISDN stacks
4
*
5
* Author Karsten Keil <[email protected]>
6
*
7
* Copyright 2008 by Karsten Keil <[email protected]>
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
__module_get(THIS_MODULE);
68
return nonseekable_open(ino, filep);
69
}
70
71
static int
72
mISDN_close(struct inode *ino, struct file *filep)
73
{
74
struct mISDNtimerdev *dev = filep->private_data;
75
struct mISDNtimer *timer, *next;
76
77
if (*debug & DEBUG_TIMER)
78
printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
79
list_for_each_entry_safe(timer, next, &dev->pending, list) {
80
del_timer(&timer->tl);
81
kfree(timer);
82
}
83
list_for_each_entry_safe(timer, next, &dev->expired, list) {
84
kfree(timer);
85
}
86
kfree(dev);
87
module_put(THIS_MODULE);
88
return 0;
89
}
90
91
static ssize_t
92
mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
93
{
94
struct mISDNtimerdev *dev = filep->private_data;
95
struct mISDNtimer *timer;
96
u_long flags;
97
int ret = 0;
98
99
if (*debug & DEBUG_TIMER)
100
printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
101
filep, buf, (int)count, off);
102
103
if (list_empty(&dev->expired) && (dev->work == 0)) {
104
if (filep->f_flags & O_NONBLOCK)
105
return -EAGAIN;
106
wait_event_interruptible(dev->wait, (dev->work ||
107
!list_empty(&dev->expired)));
108
if (signal_pending(current))
109
return -ERESTARTSYS;
110
}
111
if (count < sizeof(int))
112
return -ENOSPC;
113
if (dev->work)
114
dev->work = 0;
115
if (!list_empty(&dev->expired)) {
116
spin_lock_irqsave(&dev->lock, flags);
117
timer = (struct mISDNtimer *)dev->expired.next;
118
list_del(&timer->list);
119
spin_unlock_irqrestore(&dev->lock, flags);
120
if (put_user(timer->id, (int __user *)buf))
121
ret = -EFAULT;
122
else
123
ret = sizeof(int);
124
kfree(timer);
125
}
126
return ret;
127
}
128
129
static unsigned int
130
mISDN_poll(struct file *filep, poll_table *wait)
131
{
132
struct mISDNtimerdev *dev = filep->private_data;
133
unsigned int mask = POLLERR;
134
135
if (*debug & DEBUG_TIMER)
136
printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
137
if (dev) {
138
poll_wait(filep, &dev->wait, wait);
139
mask = 0;
140
if (dev->work || !list_empty(&dev->expired))
141
mask |= (POLLIN | POLLRDNORM);
142
if (*debug & DEBUG_TIMER)
143
printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
144
dev->work, list_empty(&dev->expired));
145
}
146
return mask;
147
}
148
149
static void
150
dev_expire_timer(unsigned long data)
151
{
152
struct mISDNtimer *timer = (void *)data;
153
u_long flags;
154
155
spin_lock_irqsave(&timer->dev->lock, flags);
156
list_move_tail(&timer->list, &timer->dev->expired);
157
spin_unlock_irqrestore(&timer->dev->lock, flags);
158
wake_up_interruptible(&timer->dev->wait);
159
}
160
161
static int
162
misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
163
{
164
int id;
165
u_long flags;
166
struct mISDNtimer *timer;
167
168
if (!timeout) {
169
dev->work = 1;
170
wake_up_interruptible(&dev->wait);
171
id = 0;
172
} else {
173
timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
174
if (!timer)
175
return -ENOMEM;
176
spin_lock_irqsave(&dev->lock, flags);
177
timer->id = dev->next_id++;
178
if (dev->next_id < 0)
179
dev->next_id = 1;
180
list_add_tail(&timer->list, &dev->pending);
181
spin_unlock_irqrestore(&dev->lock, flags);
182
timer->dev = dev;
183
timer->tl.data = (long)timer;
184
timer->tl.function = dev_expire_timer;
185
init_timer(&timer->tl);
186
timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
187
add_timer(&timer->tl);
188
id = timer->id;
189
}
190
return id;
191
}
192
193
static int
194
misdn_del_timer(struct mISDNtimerdev *dev, int id)
195
{
196
u_long flags;
197
struct mISDNtimer *timer;
198
int ret = 0;
199
200
spin_lock_irqsave(&dev->lock, flags);
201
list_for_each_entry(timer, &dev->pending, list) {
202
if (timer->id == id) {
203
list_del_init(&timer->list);
204
/* RED-PEN AK: race -- timer can be still running on
205
* other CPU. Needs reference count I think
206
*/
207
del_timer(&timer->tl);
208
ret = timer->id;
209
kfree(timer);
210
goto unlock;
211
}
212
}
213
unlock:
214
spin_unlock_irqrestore(&dev->lock, flags);
215
return ret;
216
}
217
218
static long
219
mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
220
{
221
struct mISDNtimerdev *dev = filep->private_data;
222
int id, tout, ret = 0;
223
224
225
if (*debug & DEBUG_TIMER)
226
printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
227
filep, cmd, arg);
228
mutex_lock(&mISDN_mutex);
229
switch (cmd) {
230
case IMADDTIMER:
231
if (get_user(tout, (int __user *)arg)) {
232
ret = -EFAULT;
233
break;
234
}
235
id = misdn_add_timer(dev, tout);
236
if (*debug & DEBUG_TIMER)
237
printk(KERN_DEBUG "%s add %d id %d\n", __func__,
238
tout, id);
239
if (id < 0) {
240
ret = id;
241
break;
242
}
243
if (put_user(id, (int __user *)arg))
244
ret = -EFAULT;
245
break;
246
case IMDELTIMER:
247
if (get_user(id, (int __user *)arg)) {
248
ret = -EFAULT;
249
break;
250
}
251
if (*debug & DEBUG_TIMER)
252
printk(KERN_DEBUG "%s del id %d\n", __func__, id);
253
id = misdn_del_timer(dev, id);
254
if (put_user(id, (int __user *)arg))
255
ret = -EFAULT;
256
break;
257
default:
258
ret = -EINVAL;
259
}
260
mutex_unlock(&mISDN_mutex);
261
return ret;
262
}
263
264
static const struct file_operations mISDN_fops = {
265
.read = mISDN_read,
266
.poll = mISDN_poll,
267
.unlocked_ioctl = mISDN_ioctl,
268
.open = mISDN_open,
269
.release = mISDN_close,
270
.llseek = no_llseek,
271
};
272
273
static struct miscdevice mISDNtimer = {
274
.minor = MISC_DYNAMIC_MINOR,
275
.name = "mISDNtimer",
276
.fops = &mISDN_fops,
277
};
278
279
int
280
mISDN_inittimer(u_int *deb)
281
{
282
int err;
283
284
debug = deb;
285
err = misc_register(&mISDNtimer);
286
if (err)
287
printk(KERN_WARNING "mISDN: Could not register timer device\n");
288
return err;
289
}
290
291
void mISDN_timer_cleanup(void)
292
{
293
misc_deregister(&mISDNtimer);
294
}
295
296