]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 LT |
2 | /* |
3 | * Timers abstract layer | |
c1017a4c | 4 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4 LT |
5 | */ |
6 | ||
1da177e4 LT |
7 | #include <linux/delay.h> |
8 | #include <linux/init.h> | |
1da177e4 LT |
9 | #include <linux/slab.h> |
10 | #include <linux/time.h> | |
1a60d4c5 | 11 | #include <linux/mutex.h> |
51990e82 | 12 | #include <linux/device.h> |
65a77217 | 13 | #include <linux/module.h> |
543537bd | 14 | #include <linux/string.h> |
174cd4b1 | 15 | #include <linux/sched/signal.h> |
1da177e4 LT |
16 | #include <sound/core.h> |
17 | #include <sound/timer.h> | |
18 | #include <sound/control.h> | |
19 | #include <sound/info.h> | |
20 | #include <sound/minors.h> | |
21 | #include <sound/initval.h> | |
22 | #include <linux/kmod.h> | |
1da177e4 | 23 | |
9f8a7658 TI |
24 | /* internal flags */ |
25 | #define SNDRV_TIMER_IFLG_PAUSED 0x00010000 | |
fe1b26c9 | 26 | #define SNDRV_TIMER_IFLG_DEAD 0x00020000 |
9f8a7658 | 27 | |
8eeaa2f9 | 28 | #if IS_ENABLED(CONFIG_SND_HRTIMER) |
109fef9e | 29 | #define DEFAULT_TIMER_LIMIT 4 |
1da177e4 LT |
30 | #else |
31 | #define DEFAULT_TIMER_LIMIT 1 | |
32 | #endif | |
33 | ||
34 | static int timer_limit = DEFAULT_TIMER_LIMIT; | |
b751eef1 | 35 | static int timer_tstamp_monotonic = 1; |
c1017a4c | 36 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); |
1da177e4 LT |
37 | MODULE_DESCRIPTION("ALSA timer interface"); |
38 | MODULE_LICENSE("GPL"); | |
39 | module_param(timer_limit, int, 0444); | |
40 | MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); | |
b751eef1 JK |
41 | module_param(timer_tstamp_monotonic, int, 0444); |
42 | MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); | |
1da177e4 | 43 | |
03cfe6f5 KS |
44 | MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); |
45 | MODULE_ALIAS("devname:snd/timer"); | |
46 | ||
53d2f744 TI |
47 | struct snd_timer_user { |
48 | struct snd_timer_instance *timeri; | |
6b172a85 | 49 | int tread; /* enhanced read with timestamps and events */ |
1da177e4 LT |
50 | unsigned long ticks; |
51 | unsigned long overrun; | |
52 | int qhead; | |
53 | int qtail; | |
54 | int qused; | |
55 | int queue_size; | |
230323da | 56 | bool disconnected; |
53d2f744 TI |
57 | struct snd_timer_read *queue; |
58 | struct snd_timer_tread *tqueue; | |
1da177e4 LT |
59 | spinlock_t qlock; |
60 | unsigned long last_resolution; | |
61 | unsigned int filter; | |
62 | struct timespec tstamp; /* trigger tstamp */ | |
63 | wait_queue_head_t qchange_sleep; | |
64 | struct fasync_struct *fasync; | |
af368027 | 65 | struct mutex ioctl_lock; |
53d2f744 | 66 | }; |
1da177e4 LT |
67 | |
68 | /* list of timers */ | |
69 | static LIST_HEAD(snd_timer_list); | |
70 | ||
71 | /* list of slave instances */ | |
72 | static LIST_HEAD(snd_timer_slave_list); | |
73 | ||
74 | /* lock for slave active lists */ | |
75 | static DEFINE_SPINLOCK(slave_active_lock); | |
76 | ||
1a60d4c5 | 77 | static DEFINE_MUTEX(register_mutex); |
1da177e4 | 78 | |
53d2f744 TI |
79 | static int snd_timer_free(struct snd_timer *timer); |
80 | static int snd_timer_dev_free(struct snd_device *device); | |
81 | static int snd_timer_dev_register(struct snd_device *device); | |
c461482c | 82 | static int snd_timer_dev_disconnect(struct snd_device *device); |
1da177e4 | 83 | |
53d2f744 | 84 | static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); |
1da177e4 LT |
85 | |
86 | /* | |
87 | * create a timer instance with the given owner string. | |
88 | * when timer is not NULL, increments the module counter | |
89 | */ | |
53d2f744 TI |
90 | static struct snd_timer_instance *snd_timer_instance_new(char *owner, |
91 | struct snd_timer *timer) | |
1da177e4 | 92 | { |
53d2f744 | 93 | struct snd_timer_instance *timeri; |
ca2c0966 | 94 | timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); |
1da177e4 LT |
95 | if (timeri == NULL) |
96 | return NULL; | |
543537bd | 97 | timeri->owner = kstrdup(owner, GFP_KERNEL); |
1da177e4 LT |
98 | if (! timeri->owner) { |
99 | kfree(timeri); | |
100 | return NULL; | |
101 | } | |
102 | INIT_LIST_HEAD(&timeri->open_list); | |
103 | INIT_LIST_HEAD(&timeri->active_list); | |
104 | INIT_LIST_HEAD(&timeri->ack_list); | |
105 | INIT_LIST_HEAD(&timeri->slave_list_head); | |
106 | INIT_LIST_HEAD(&timeri->slave_active_head); | |
107 | ||
108 | timeri->timer = timer; | |
de24214d | 109 | if (timer && !try_module_get(timer->module)) { |
1da177e4 LT |
110 | kfree(timeri->owner); |
111 | kfree(timeri); | |
112 | return NULL; | |
113 | } | |
114 | ||
115 | return timeri; | |
116 | } | |
117 | ||
118 | /* | |
119 | * find a timer instance from the given timer id | |
120 | */ | |
53d2f744 | 121 | static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) |
1da177e4 | 122 | { |
53d2f744 | 123 | struct snd_timer *timer = NULL; |
1da177e4 | 124 | |
9244b2c3 | 125 | list_for_each_entry(timer, &snd_timer_list, device_list) { |
1da177e4 LT |
126 | if (timer->tmr_class != tid->dev_class) |
127 | continue; | |
128 | if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || | |
129 | timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && | |
130 | (timer->card == NULL || | |
131 | timer->card->number != tid->card)) | |
132 | continue; | |
133 | if (timer->tmr_device != tid->device) | |
134 | continue; | |
135 | if (timer->tmr_subdevice != tid->subdevice) | |
136 | continue; | |
137 | return timer; | |
138 | } | |
139 | return NULL; | |
140 | } | |
141 | ||
ee2da997 | 142 | #ifdef CONFIG_MODULES |
1da177e4 | 143 | |
53d2f744 | 144 | static void snd_timer_request(struct snd_timer_id *tid) |
1da177e4 | 145 | { |
1da177e4 LT |
146 | switch (tid->dev_class) { |
147 | case SNDRV_TIMER_CLASS_GLOBAL: | |
148 | if (tid->device < timer_limit) | |
149 | request_module("snd-timer-%i", tid->device); | |
150 | break; | |
151 | case SNDRV_TIMER_CLASS_CARD: | |
152 | case SNDRV_TIMER_CLASS_PCM: | |
153 | if (tid->card < snd_ecards_limit) | |
154 | request_module("snd-card-%i", tid->card); | |
155 | break; | |
156 | default: | |
157 | break; | |
158 | } | |
159 | } | |
160 | ||
161 | #endif | |
162 | ||
163 | /* | |
164 | * look for a master instance matching with the slave id of the given slave. | |
165 | * when found, relink the open_link of the slave. | |
166 | * | |
167 | * call this with register_mutex down. | |
168 | */ | |
9b7d869e | 169 | static int snd_timer_check_slave(struct snd_timer_instance *slave) |
1da177e4 | 170 | { |
53d2f744 TI |
171 | struct snd_timer *timer; |
172 | struct snd_timer_instance *master; | |
1da177e4 LT |
173 | |
174 | /* FIXME: it's really dumb to look up all entries.. */ | |
9244b2c3 JB |
175 | list_for_each_entry(timer, &snd_timer_list, device_list) { |
176 | list_for_each_entry(master, &timer->open_list_head, open_list) { | |
1da177e4 LT |
177 | if (slave->slave_class == master->slave_class && |
178 | slave->slave_id == master->slave_id) { | |
9b7d869e TI |
179 | if (master->timer->num_instances >= |
180 | master->timer->max_instances) | |
181 | return -EBUSY; | |
5b7c757d NK |
182 | list_move_tail(&slave->open_list, |
183 | &master->slave_list_head); | |
9b7d869e | 184 | master->timer->num_instances++; |
1da177e4 LT |
185 | spin_lock_irq(&slave_active_lock); |
186 | slave->master = master; | |
187 | slave->timer = master->timer; | |
188 | spin_unlock_irq(&slave_active_lock); | |
9b7d869e | 189 | return 0; |
1da177e4 LT |
190 | } |
191 | } | |
192 | } | |
9b7d869e | 193 | return 0; |
1da177e4 LT |
194 | } |
195 | ||
196 | /* | |
197 | * look for slave instances matching with the slave id of the given master. | |
198 | * when found, relink the open_link of slaves. | |
199 | * | |
200 | * call this with register_mutex down. | |
201 | */ | |
9b7d869e | 202 | static int snd_timer_check_master(struct snd_timer_instance *master) |
1da177e4 | 203 | { |
9244b2c3 | 204 | struct snd_timer_instance *slave, *tmp; |
1da177e4 LT |
205 | |
206 | /* check all pending slaves */ | |
9244b2c3 | 207 | list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { |
1da177e4 LT |
208 | if (slave->slave_class == master->slave_class && |
209 | slave->slave_id == master->slave_id) { | |
9b7d869e TI |
210 | if (master->timer->num_instances >= |
211 | master->timer->max_instances) | |
212 | return -EBUSY; | |
9244b2c3 | 213 | list_move_tail(&slave->open_list, &master->slave_list_head); |
9b7d869e | 214 | master->timer->num_instances++; |
1da177e4 | 215 | spin_lock_irq(&slave_active_lock); |
b5a663aa | 216 | spin_lock(&master->timer->lock); |
1da177e4 LT |
217 | slave->master = master; |
218 | slave->timer = master->timer; | |
219 | if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) | |
6b172a85 CL |
220 | list_add_tail(&slave->active_list, |
221 | &master->slave_active_head); | |
b5a663aa | 222 | spin_unlock(&master->timer->lock); |
1da177e4 LT |
223 | spin_unlock_irq(&slave_active_lock); |
224 | } | |
225 | } | |
9b7d869e | 226 | return 0; |
1da177e4 LT |
227 | } |
228 | ||
9b7d869e TI |
229 | static int snd_timer_close_locked(struct snd_timer_instance *timeri); |
230 | ||
1da177e4 LT |
231 | /* |
232 | * open a timer instance | |
233 | * when opening a master, the slave id must be here given. | |
234 | */ | |
53d2f744 TI |
235 | int snd_timer_open(struct snd_timer_instance **ti, |
236 | char *owner, struct snd_timer_id *tid, | |
1da177e4 LT |
237 | unsigned int slave_id) |
238 | { | |
53d2f744 TI |
239 | struct snd_timer *timer; |
240 | struct snd_timer_instance *timeri = NULL; | |
9b7d869e | 241 | int err; |
6b172a85 | 242 | |
41672c0c | 243 | mutex_lock(®ister_mutex); |
1da177e4 LT |
244 | if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { |
245 | /* open a slave instance */ | |
246 | if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || | |
247 | tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { | |
cf74dcf3 TI |
248 | pr_debug("ALSA: timer: invalid slave class %i\n", |
249 | tid->dev_sclass); | |
41672c0c TI |
250 | err = -EINVAL; |
251 | goto unlock; | |
1da177e4 | 252 | } |
1da177e4 | 253 | timeri = snd_timer_instance_new(owner, NULL); |
2fd43d11 | 254 | if (!timeri) { |
41672c0c TI |
255 | err = -ENOMEM; |
256 | goto unlock; | |
2fd43d11 | 257 | } |
1da177e4 LT |
258 | timeri->slave_class = tid->dev_sclass; |
259 | timeri->slave_id = tid->device; | |
260 | timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; | |
261 | list_add_tail(&timeri->open_list, &snd_timer_slave_list); | |
9b7d869e TI |
262 | err = snd_timer_check_slave(timeri); |
263 | if (err < 0) { | |
264 | snd_timer_close_locked(timeri); | |
265 | timeri = NULL; | |
266 | } | |
41672c0c | 267 | goto unlock; |
1da177e4 LT |
268 | } |
269 | ||
270 | /* open a master instance */ | |
1da177e4 | 271 | timer = snd_timer_find(tid); |
ee2da997 JB |
272 | #ifdef CONFIG_MODULES |
273 | if (!timer) { | |
1a60d4c5 | 274 | mutex_unlock(®ister_mutex); |
1da177e4 | 275 | snd_timer_request(tid); |
1a60d4c5 | 276 | mutex_lock(®ister_mutex); |
1da177e4 LT |
277 | timer = snd_timer_find(tid); |
278 | } | |
279 | #endif | |
2fd43d11 | 280 | if (!timer) { |
41672c0c TI |
281 | err = -ENODEV; |
282 | goto unlock; | |
1da177e4 | 283 | } |
2fd43d11 CL |
284 | if (!list_empty(&timer->open_list_head)) { |
285 | timeri = list_entry(timer->open_list_head.next, | |
53d2f744 | 286 | struct snd_timer_instance, open_list); |
2fd43d11 | 287 | if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { |
41672c0c TI |
288 | err = -EBUSY; |
289 | timeri = NULL; | |
290 | goto unlock; | |
2fd43d11 CL |
291 | } |
292 | } | |
9b7d869e | 293 | if (timer->num_instances >= timer->max_instances) { |
41672c0c TI |
294 | err = -EBUSY; |
295 | goto unlock; | |
9b7d869e | 296 | } |
2fd43d11 CL |
297 | timeri = snd_timer_instance_new(owner, timer); |
298 | if (!timeri) { | |
41672c0c TI |
299 | err = -ENOMEM; |
300 | goto unlock; | |
2fd43d11 | 301 | } |
230323da TI |
302 | /* take a card refcount for safe disconnection */ |
303 | if (timer->card) | |
304 | get_device(&timer->card->card_dev); | |
2fd43d11 CL |
305 | timeri->slave_class = tid->dev_sclass; |
306 | timeri->slave_id = slave_id; | |
8ddc0563 VN |
307 | |
308 | if (list_empty(&timer->open_list_head) && timer->hw.open) { | |
41672c0c | 309 | err = timer->hw.open(timer); |
8ddc0563 VN |
310 | if (err) { |
311 | kfree(timeri->owner); | |
312 | kfree(timeri); | |
41672c0c | 313 | timeri = NULL; |
8ddc0563 VN |
314 | |
315 | if (timer->card) | |
316 | put_device(&timer->card->card_dev); | |
317 | module_put(timer->module); | |
41672c0c | 318 | goto unlock; |
8ddc0563 VN |
319 | } |
320 | } | |
321 | ||
2fd43d11 | 322 | list_add_tail(&timeri->open_list, &timer->open_list_head); |
9b7d869e TI |
323 | timer->num_instances++; |
324 | err = snd_timer_check_master(timeri); | |
325 | if (err < 0) { | |
326 | snd_timer_close_locked(timeri); | |
327 | timeri = NULL; | |
328 | } | |
41672c0c TI |
329 | |
330 | unlock: | |
1a60d4c5 | 331 | mutex_unlock(®ister_mutex); |
1da177e4 | 332 | *ti = timeri; |
9b7d869e | 333 | return err; |
1da177e4 | 334 | } |
98856392 | 335 | EXPORT_SYMBOL(snd_timer_open); |
1da177e4 | 336 | |
1da177e4 LT |
337 | /* |
338 | * close a timer instance | |
9b7d869e | 339 | * call this with register_mutex down. |
1da177e4 | 340 | */ |
9b7d869e | 341 | static int snd_timer_close_locked(struct snd_timer_instance *timeri) |
1da177e4 | 342 | { |
fe1b26c9 | 343 | struct snd_timer *timer = timeri->timer; |
9244b2c3 | 344 | struct snd_timer_instance *slave, *tmp; |
1da177e4 | 345 | |
fe1b26c9 TI |
346 | if (timer) { |
347 | spin_lock_irq(&timer->lock); | |
348 | timeri->flags |= SNDRV_TIMER_IFLG_DEAD; | |
349 | spin_unlock_irq(&timer->lock); | |
350 | } | |
351 | ||
9984d1b5 TI |
352 | list_del(&timeri->open_list); |
353 | ||
1da177e4 LT |
354 | /* force to stop the timer */ |
355 | snd_timer_stop(timeri); | |
356 | ||
9984d1b5 | 357 | if (timer) { |
9b7d869e | 358 | timer->num_instances--; |
1da177e4 LT |
359 | /* wait, until the active callback is finished */ |
360 | spin_lock_irq(&timer->lock); | |
361 | while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { | |
362 | spin_unlock_irq(&timer->lock); | |
363 | udelay(10); | |
364 | spin_lock_irq(&timer->lock); | |
365 | } | |
366 | spin_unlock_irq(&timer->lock); | |
9984d1b5 | 367 | |
1da177e4 | 368 | /* remove slave links */ |
b5a663aa TI |
369 | spin_lock_irq(&slave_active_lock); |
370 | spin_lock(&timer->lock); | |
9244b2c3 JB |
371 | list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, |
372 | open_list) { | |
9244b2c3 | 373 | list_move_tail(&slave->open_list, &snd_timer_slave_list); |
9b7d869e | 374 | timer->num_instances--; |
1da177e4 LT |
375 | slave->master = NULL; |
376 | slave->timer = NULL; | |
b5a663aa TI |
377 | list_del_init(&slave->ack_list); |
378 | list_del_init(&slave->active_list); | |
1da177e4 | 379 | } |
b5a663aa TI |
380 | spin_unlock(&timer->lock); |
381 | spin_unlock_irq(&slave_active_lock); | |
9984d1b5 TI |
382 | |
383 | /* slave doesn't need to release timer resources below */ | |
384 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) | |
385 | timer = NULL; | |
1da177e4 | 386 | } |
9984d1b5 | 387 | |
1da177e4 LT |
388 | if (timeri->private_free) |
389 | timeri->private_free(timeri); | |
390 | kfree(timeri->owner); | |
391 | kfree(timeri); | |
9984d1b5 TI |
392 | |
393 | if (timer) { | |
394 | if (list_empty(&timer->open_list_head) && timer->hw.close) | |
395 | timer->hw.close(timer); | |
396 | /* release a card refcount for safe disconnection */ | |
397 | if (timer->card) | |
398 | put_device(&timer->card->card_dev); | |
de24214d | 399 | module_put(timer->module); |
9984d1b5 TI |
400 | } |
401 | ||
1da177e4 LT |
402 | return 0; |
403 | } | |
9b7d869e TI |
404 | |
405 | /* | |
406 | * close a timer instance | |
407 | */ | |
408 | int snd_timer_close(struct snd_timer_instance *timeri) | |
409 | { | |
410 | int err; | |
411 | ||
412 | if (snd_BUG_ON(!timeri)) | |
413 | return -ENXIO; | |
414 | ||
415 | mutex_lock(®ister_mutex); | |
416 | err = snd_timer_close_locked(timeri); | |
417 | mutex_unlock(®ister_mutex); | |
418 | return err; | |
419 | } | |
98856392 | 420 | EXPORT_SYMBOL(snd_timer_close); |
1da177e4 | 421 | |
fdcb5761 TI |
422 | static unsigned long snd_timer_hw_resolution(struct snd_timer *timer) |
423 | { | |
424 | if (timer->hw.c_resolution) | |
425 | return timer->hw.c_resolution(timer); | |
426 | else | |
427 | return timer->hw.resolution; | |
428 | } | |
429 | ||
53d2f744 | 430 | unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) |
1da177e4 | 431 | { |
53d2f744 | 432 | struct snd_timer * timer; |
9d4d207d TI |
433 | unsigned long ret = 0; |
434 | unsigned long flags; | |
1da177e4 LT |
435 | |
436 | if (timeri == NULL) | |
437 | return 0; | |
dd1f7ab8 | 438 | timer = timeri->timer; |
9d4d207d TI |
439 | if (timer) { |
440 | spin_lock_irqsave(&timer->lock, flags); | |
441 | ret = snd_timer_hw_resolution(timer); | |
442 | spin_unlock_irqrestore(&timer->lock, flags); | |
443 | } | |
444 | return ret; | |
1da177e4 | 445 | } |
98856392 | 446 | EXPORT_SYMBOL(snd_timer_resolution); |
1da177e4 | 447 | |
53d2f744 | 448 | static void snd_timer_notify1(struct snd_timer_instance *ti, int event) |
1da177e4 | 449 | { |
9d4d207d | 450 | struct snd_timer *timer = ti->timer; |
1da177e4 | 451 | unsigned long resolution = 0; |
53d2f744 | 452 | struct snd_timer_instance *ts; |
1da177e4 LT |
453 | struct timespec tstamp; |
454 | ||
b751eef1 | 455 | if (timer_tstamp_monotonic) |
26204e04 | 456 | ktime_get_ts(&tstamp); |
b751eef1 JK |
457 | else |
458 | getnstimeofday(&tstamp); | |
7eaa943c TI |
459 | if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || |
460 | event > SNDRV_TIMER_EVENT_PAUSE)) | |
461 | return; | |
9d4d207d TI |
462 | if (timer && |
463 | (event == SNDRV_TIMER_EVENT_START || | |
464 | event == SNDRV_TIMER_EVENT_CONTINUE)) | |
465 | resolution = snd_timer_hw_resolution(timer); | |
1da177e4 | 466 | if (ti->ccallback) |
b30477d5 | 467 | ti->ccallback(ti, event, &tstamp, resolution); |
1da177e4 LT |
468 | if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) |
469 | return; | |
1da177e4 LT |
470 | if (timer == NULL) |
471 | return; | |
472 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | |
473 | return; | |
9244b2c3 | 474 | list_for_each_entry(ts, &ti->slave_active_head, active_list) |
1da177e4 | 475 | if (ts->ccallback) |
117159f0 | 476 | ts->ccallback(ts, event + 100, &tstamp, resolution); |
1da177e4 LT |
477 | } |
478 | ||
f65e0d29 TI |
479 | /* start/continue a master timer */ |
480 | static int snd_timer_start1(struct snd_timer_instance *timeri, | |
481 | bool start, unsigned long ticks) | |
1da177e4 | 482 | { |
f65e0d29 TI |
483 | struct snd_timer *timer; |
484 | int result; | |
485 | unsigned long flags; | |
486 | ||
487 | timer = timeri->timer; | |
488 | if (!timer) | |
489 | return -EINVAL; | |
490 | ||
491 | spin_lock_irqsave(&timer->lock, flags); | |
fe1b26c9 TI |
492 | if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { |
493 | result = -EINVAL; | |
494 | goto unlock; | |
495 | } | |
f65e0d29 TI |
496 | if (timer->card && timer->card->shutdown) { |
497 | result = -ENODEV; | |
498 | goto unlock; | |
499 | } | |
500 | if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | | |
501 | SNDRV_TIMER_IFLG_START)) { | |
502 | result = -EBUSY; | |
503 | goto unlock; | |
504 | } | |
505 | ||
506 | if (start) | |
507 | timeri->ticks = timeri->cticks = ticks; | |
508 | else if (!timeri->cticks) | |
509 | timeri->cticks = 1; | |
510 | timeri->pticks = 0; | |
511 | ||
5b7c757d | 512 | list_move_tail(&timeri->active_list, &timer->active_list_head); |
1da177e4 LT |
513 | if (timer->running) { |
514 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | |
515 | goto __start_now; | |
516 | timer->flags |= SNDRV_TIMER_FLG_RESCHED; | |
517 | timeri->flags |= SNDRV_TIMER_IFLG_START; | |
f65e0d29 | 518 | result = 1; /* delayed start */ |
1da177e4 | 519 | } else { |
f65e0d29 TI |
520 | if (start) |
521 | timer->sticks = ticks; | |
1da177e4 LT |
522 | timer->hw.start(timer); |
523 | __start_now: | |
524 | timer->running++; | |
525 | timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; | |
f65e0d29 | 526 | result = 0; |
1da177e4 | 527 | } |
f65e0d29 TI |
528 | snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : |
529 | SNDRV_TIMER_EVENT_CONTINUE); | |
530 | unlock: | |
531 | spin_unlock_irqrestore(&timer->lock, flags); | |
532 | return result; | |
1da177e4 LT |
533 | } |
534 | ||
f65e0d29 TI |
535 | /* start/continue a slave timer */ |
536 | static int snd_timer_start_slave(struct snd_timer_instance *timeri, | |
537 | bool start) | |
1da177e4 LT |
538 | { |
539 | unsigned long flags; | |
fe1b26c9 | 540 | int err; |
1da177e4 LT |
541 | |
542 | spin_lock_irqsave(&slave_active_lock, flags); | |
fe1b26c9 TI |
543 | if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { |
544 | err = -EINVAL; | |
545 | goto unlock; | |
546 | } | |
f784beb7 | 547 | if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { |
fe1b26c9 TI |
548 | err = -EBUSY; |
549 | goto unlock; | |
f784beb7 | 550 | } |
1da177e4 | 551 | timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; |
b5a663aa TI |
552 | if (timeri->master && timeri->timer) { |
553 | spin_lock(&timeri->timer->lock); | |
6b172a85 CL |
554 | list_add_tail(&timeri->active_list, |
555 | &timeri->master->slave_active_head); | |
f65e0d29 TI |
556 | snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : |
557 | SNDRV_TIMER_EVENT_CONTINUE); | |
b5a663aa TI |
558 | spin_unlock(&timeri->timer->lock); |
559 | } | |
fe1b26c9 TI |
560 | err = 1; /* delayed start */ |
561 | unlock: | |
1da177e4 | 562 | spin_unlock_irqrestore(&slave_active_lock, flags); |
fe1b26c9 | 563 | return err; |
1da177e4 LT |
564 | } |
565 | ||
f65e0d29 TI |
566 | /* stop/pause a master timer */ |
567 | static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) | |
1da177e4 | 568 | { |
53d2f744 | 569 | struct snd_timer *timer; |
f65e0d29 | 570 | int result = 0; |
1da177e4 LT |
571 | unsigned long flags; |
572 | ||
1da177e4 LT |
573 | timer = timeri->timer; |
574 | if (!timer) | |
575 | return -EINVAL; | |
576 | spin_lock_irqsave(&timer->lock, flags); | |
f784beb7 TI |
577 | if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | |
578 | SNDRV_TIMER_IFLG_START))) { | |
f65e0d29 TI |
579 | result = -EBUSY; |
580 | goto unlock; | |
f784beb7 | 581 | } |
1da177e4 LT |
582 | list_del_init(&timeri->ack_list); |
583 | list_del_init(&timeri->active_list); | |
f65e0d29 TI |
584 | if (timer->card && timer->card->shutdown) |
585 | goto unlock; | |
586 | if (stop) { | |
587 | timeri->cticks = timeri->ticks; | |
588 | timeri->pticks = 0; | |
230323da | 589 | } |
1da177e4 LT |
590 | if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && |
591 | !(--timer->running)) { | |
592 | timer->hw.stop(timer); | |
593 | if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { | |
594 | timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; | |
595 | snd_timer_reschedule(timer, 0); | |
596 | if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { | |
597 | timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; | |
598 | timer->hw.start(timer); | |
599 | } | |
600 | } | |
601 | } | |
c3b16813 | 602 | timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); |
9f8a7658 TI |
603 | if (stop) |
604 | timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; | |
605 | else | |
606 | timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; | |
f65e0d29 | 607 | snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : |
3ae18097 | 608 | SNDRV_TIMER_EVENT_PAUSE); |
f65e0d29 | 609 | unlock: |
1da177e4 | 610 | spin_unlock_irqrestore(&timer->lock, flags); |
f65e0d29 TI |
611 | return result; |
612 | } | |
613 | ||
614 | /* stop/pause a slave timer */ | |
615 | static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) | |
616 | { | |
617 | unsigned long flags; | |
618 | ||
619 | spin_lock_irqsave(&slave_active_lock, flags); | |
620 | if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { | |
621 | spin_unlock_irqrestore(&slave_active_lock, flags); | |
622 | return -EBUSY; | |
623 | } | |
624 | timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; | |
625 | if (timeri->timer) { | |
626 | spin_lock(&timeri->timer->lock); | |
627 | list_del_init(&timeri->ack_list); | |
628 | list_del_init(&timeri->active_list); | |
629 | snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : | |
3ae18097 | 630 | SNDRV_TIMER_EVENT_PAUSE); |
f65e0d29 TI |
631 | spin_unlock(&timeri->timer->lock); |
632 | } | |
633 | spin_unlock_irqrestore(&slave_active_lock, flags); | |
1da177e4 LT |
634 | return 0; |
635 | } | |
636 | ||
f65e0d29 TI |
637 | /* |
638 | * start the timer instance | |
639 | */ | |
640 | int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) | |
641 | { | |
642 | if (timeri == NULL || ticks < 1) | |
643 | return -EINVAL; | |
644 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) | |
645 | return snd_timer_start_slave(timeri, true); | |
646 | else | |
647 | return snd_timer_start1(timeri, true, ticks); | |
648 | } | |
98856392 | 649 | EXPORT_SYMBOL(snd_timer_start); |
f65e0d29 | 650 | |
1da177e4 LT |
651 | /* |
652 | * stop the timer instance. | |
653 | * | |
654 | * do not call this from the timer callback! | |
655 | */ | |
53d2f744 | 656 | int snd_timer_stop(struct snd_timer_instance *timeri) |
1da177e4 | 657 | { |
f65e0d29 TI |
658 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
659 | return snd_timer_stop_slave(timeri, true); | |
660 | else | |
661 | return snd_timer_stop1(timeri, true); | |
1da177e4 | 662 | } |
98856392 | 663 | EXPORT_SYMBOL(snd_timer_stop); |
1da177e4 LT |
664 | |
665 | /* | |
666 | * start again.. the tick is kept. | |
667 | */ | |
53d2f744 | 668 | int snd_timer_continue(struct snd_timer_instance *timeri) |
1da177e4 | 669 | { |
9f8a7658 TI |
670 | /* timer can continue only after pause */ |
671 | if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) | |
672 | return -EINVAL; | |
673 | ||
1da177e4 | 674 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
f65e0d29 TI |
675 | return snd_timer_start_slave(timeri, false); |
676 | else | |
677 | return snd_timer_start1(timeri, false, 0); | |
1da177e4 | 678 | } |
98856392 | 679 | EXPORT_SYMBOL(snd_timer_continue); |
1da177e4 LT |
680 | |
681 | /* | |
682 | * pause.. remember the ticks left | |
683 | */ | |
53d2f744 | 684 | int snd_timer_pause(struct snd_timer_instance * timeri) |
1da177e4 | 685 | { |
f65e0d29 TI |
686 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
687 | return snd_timer_stop_slave(timeri, false); | |
688 | else | |
689 | return snd_timer_stop1(timeri, false); | |
1da177e4 | 690 | } |
98856392 | 691 | EXPORT_SYMBOL(snd_timer_pause); |
1da177e4 LT |
692 | |
693 | /* | |
694 | * reschedule the timer | |
695 | * | |
696 | * start pending instances and check the scheduling ticks. | |
697 | * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. | |
698 | */ | |
53d2f744 | 699 | static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left) |
1da177e4 | 700 | { |
53d2f744 | 701 | struct snd_timer_instance *ti; |
1da177e4 | 702 | unsigned long ticks = ~0UL; |
1da177e4 | 703 | |
9244b2c3 | 704 | list_for_each_entry(ti, &timer->active_list_head, active_list) { |
1da177e4 LT |
705 | if (ti->flags & SNDRV_TIMER_IFLG_START) { |
706 | ti->flags &= ~SNDRV_TIMER_IFLG_START; | |
707 | ti->flags |= SNDRV_TIMER_IFLG_RUNNING; | |
708 | timer->running++; | |
709 | } | |
710 | if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { | |
711 | if (ticks > ti->cticks) | |
712 | ticks = ti->cticks; | |
713 | } | |
714 | } | |
715 | if (ticks == ~0UL) { | |
716 | timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; | |
717 | return; | |
718 | } | |
719 | if (ticks > timer->hw.ticks) | |
720 | ticks = timer->hw.ticks; | |
721 | if (ticks_left != ticks) | |
722 | timer->flags |= SNDRV_TIMER_FLG_CHANGE; | |
723 | timer->sticks = ticks; | |
724 | } | |
725 | ||
8748b850 TI |
726 | /* call callbacks in timer ack list */ |
727 | static void snd_timer_process_callbacks(struct snd_timer *timer, | |
728 | struct list_head *head) | |
1da177e4 | 729 | { |
53d2f744 | 730 | struct snd_timer_instance *ti; |
1da177e4 | 731 | unsigned long resolution, ticks; |
230323da | 732 | |
8748b850 TI |
733 | while (!list_empty(head)) { |
734 | ti = list_first_entry(head, struct snd_timer_instance, | |
735 | ack_list); | |
1da177e4 | 736 | |
df55531b TI |
737 | /* remove from ack_list and make empty */ |
738 | list_del_init(&ti->ack_list); | |
739 | ||
fe1b26c9 TI |
740 | if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { |
741 | ticks = ti->pticks; | |
742 | ti->pticks = 0; | |
743 | resolution = ti->resolution; | |
df55531b | 744 | ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; |
fe1b26c9 TI |
745 | spin_unlock(&timer->lock); |
746 | if (ti->callback) | |
747 | ti->callback(ti, resolution, ticks); | |
748 | spin_lock(&timer->lock); | |
df55531b | 749 | ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; |
fe1b26c9 | 750 | } |
1da177e4 | 751 | } |
8748b850 TI |
752 | } |
753 | ||
7bb4a8a2 TI |
754 | /* clear pending instances from ack list */ |
755 | static void snd_timer_clear_callbacks(struct snd_timer *timer, | |
756 | struct list_head *head) | |
757 | { | |
758 | unsigned long flags; | |
759 | ||
760 | spin_lock_irqsave(&timer->lock, flags); | |
761 | while (!list_empty(head)) | |
762 | list_del_init(head->next); | |
763 | spin_unlock_irqrestore(&timer->lock, flags); | |
764 | } | |
765 | ||
1da177e4 LT |
766 | /* |
767 | * timer tasklet | |
768 | * | |
769 | */ | |
770 | static void snd_timer_tasklet(unsigned long arg) | |
771 | { | |
53d2f744 | 772 | struct snd_timer *timer = (struct snd_timer *) arg; |
2999ff5b | 773 | unsigned long flags; |
1da177e4 | 774 | |
7bb4a8a2 TI |
775 | if (timer->card && timer->card->shutdown) { |
776 | snd_timer_clear_callbacks(timer, &timer->sack_list_head); | |
230323da | 777 | return; |
7bb4a8a2 | 778 | } |
230323da | 779 | |
2999ff5b | 780 | spin_lock_irqsave(&timer->lock, flags); |
8748b850 | 781 | snd_timer_process_callbacks(timer, &timer->sack_list_head); |
2999ff5b | 782 | spin_unlock_irqrestore(&timer->lock, flags); |
1da177e4 LT |
783 | } |
784 | ||
785 | /* | |
786 | * timer interrupt | |
787 | * | |
788 | * ticks_left is usually equal to timer->sticks. | |
789 | * | |
790 | */ | |
53d2f744 | 791 | void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) |
1da177e4 | 792 | { |
9244b2c3 | 793 | struct snd_timer_instance *ti, *ts, *tmp; |
8748b850 TI |
794 | unsigned long resolution; |
795 | struct list_head *ack_list_head; | |
b32425ac | 796 | unsigned long flags; |
1da177e4 LT |
797 | int use_tasklet = 0; |
798 | ||
799 | if (timer == NULL) | |
800 | return; | |
801 | ||
7bb4a8a2 TI |
802 | if (timer->card && timer->card->shutdown) { |
803 | snd_timer_clear_callbacks(timer, &timer->ack_list_head); | |
230323da | 804 | return; |
7bb4a8a2 | 805 | } |
230323da | 806 | |
b32425ac | 807 | spin_lock_irqsave(&timer->lock, flags); |
1da177e4 LT |
808 | |
809 | /* remember the current resolution */ | |
fdcb5761 | 810 | resolution = snd_timer_hw_resolution(timer); |
1da177e4 LT |
811 | |
812 | /* loop for all active instances | |
9244b2c3 | 813 | * Here we cannot use list_for_each_entry because the active_list of a |
6b172a85 CL |
814 | * processed instance is relinked to done_list_head before the callback |
815 | * is called. | |
1da177e4 | 816 | */ |
9244b2c3 JB |
817 | list_for_each_entry_safe(ti, tmp, &timer->active_list_head, |
818 | active_list) { | |
fe1b26c9 TI |
819 | if (ti->flags & SNDRV_TIMER_IFLG_DEAD) |
820 | continue; | |
1da177e4 LT |
821 | if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) |
822 | continue; | |
823 | ti->pticks += ticks_left; | |
824 | ti->resolution = resolution; | |
825 | if (ti->cticks < ticks_left) | |
826 | ti->cticks = 0; | |
827 | else | |
828 | ti->cticks -= ticks_left; | |
829 | if (ti->cticks) /* not expired */ | |
830 | continue; | |
831 | if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { | |
832 | ti->cticks = ti->ticks; | |
833 | } else { | |
834 | ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; | |
094fd3be TI |
835 | --timer->running; |
836 | list_del_init(&ti->active_list); | |
1da177e4 | 837 | } |
6b172a85 CL |
838 | if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || |
839 | (ti->flags & SNDRV_TIMER_IFLG_FAST)) | |
840 | ack_list_head = &timer->ack_list_head; | |
841 | else | |
842 | ack_list_head = &timer->sack_list_head; | |
843 | if (list_empty(&ti->ack_list)) | |
844 | list_add_tail(&ti->ack_list, ack_list_head); | |
9244b2c3 | 845 | list_for_each_entry(ts, &ti->slave_active_head, active_list) { |
1da177e4 LT |
846 | ts->pticks = ti->pticks; |
847 | ts->resolution = resolution; | |
6b172a85 CL |
848 | if (list_empty(&ts->ack_list)) |
849 | list_add_tail(&ts->ack_list, ack_list_head); | |
1da177e4 LT |
850 | } |
851 | } | |
852 | if (timer->flags & SNDRV_TIMER_FLG_RESCHED) | |
cd93fe47 | 853 | snd_timer_reschedule(timer, timer->sticks); |
1da177e4 LT |
854 | if (timer->running) { |
855 | if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { | |
856 | timer->hw.stop(timer); | |
857 | timer->flags |= SNDRV_TIMER_FLG_CHANGE; | |
858 | } | |
859 | if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || | |
860 | (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { | |
861 | /* restart timer */ | |
862 | timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; | |
863 | timer->hw.start(timer); | |
864 | } | |
865 | } else { | |
866 | timer->hw.stop(timer); | |
867 | } | |
868 | ||
869 | /* now process all fast callbacks */ | |
8748b850 | 870 | snd_timer_process_callbacks(timer, &timer->ack_list_head); |
1da177e4 LT |
871 | |
872 | /* do we have any slow callbacks? */ | |
873 | use_tasklet = !list_empty(&timer->sack_list_head); | |
b32425ac | 874 | spin_unlock_irqrestore(&timer->lock, flags); |
1da177e4 LT |
875 | |
876 | if (use_tasklet) | |
1f04128a | 877 | tasklet_schedule(&timer->task_queue); |
1da177e4 | 878 | } |
98856392 | 879 | EXPORT_SYMBOL(snd_timer_interrupt); |
1da177e4 LT |
880 | |
881 | /* | |
882 | ||
883 | */ | |
884 | ||
53d2f744 TI |
885 | int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, |
886 | struct snd_timer **rtimer) | |
1da177e4 | 887 | { |
53d2f744 | 888 | struct snd_timer *timer; |
1da177e4 | 889 | int err; |
53d2f744 | 890 | static struct snd_device_ops ops = { |
1da177e4 LT |
891 | .dev_free = snd_timer_dev_free, |
892 | .dev_register = snd_timer_dev_register, | |
c461482c | 893 | .dev_disconnect = snd_timer_dev_disconnect, |
1da177e4 LT |
894 | }; |
895 | ||
7eaa943c TI |
896 | if (snd_BUG_ON(!tid)) |
897 | return -EINVAL; | |
d10ee9c5 S |
898 | if (tid->dev_class == SNDRV_TIMER_CLASS_CARD || |
899 | tid->dev_class == SNDRV_TIMER_CLASS_PCM) { | |
900 | if (WARN_ON(!card)) | |
901 | return -EINVAL; | |
902 | } | |
7eaa943c TI |
903 | if (rtimer) |
904 | *rtimer = NULL; | |
ca2c0966 | 905 | timer = kzalloc(sizeof(*timer), GFP_KERNEL); |
ec0e9937 | 906 | if (!timer) |
1da177e4 LT |
907 | return -ENOMEM; |
908 | timer->tmr_class = tid->dev_class; | |
909 | timer->card = card; | |
910 | timer->tmr_device = tid->device; | |
911 | timer->tmr_subdevice = tid->subdevice; | |
912 | if (id) | |
913 | strlcpy(timer->id, id, sizeof(timer->id)); | |
6b760bb2 | 914 | timer->sticks = 1; |
1da177e4 LT |
915 | INIT_LIST_HEAD(&timer->device_list); |
916 | INIT_LIST_HEAD(&timer->open_list_head); | |
917 | INIT_LIST_HEAD(&timer->active_list_head); | |
918 | INIT_LIST_HEAD(&timer->ack_list_head); | |
919 | INIT_LIST_HEAD(&timer->sack_list_head); | |
920 | spin_lock_init(&timer->lock); | |
6b172a85 CL |
921 | tasklet_init(&timer->task_queue, snd_timer_tasklet, |
922 | (unsigned long)timer); | |
9b7d869e | 923 | timer->max_instances = 1000; /* default limit per timer */ |
1da177e4 | 924 | if (card != NULL) { |
de24214d | 925 | timer->module = card->module; |
6b172a85 CL |
926 | err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops); |
927 | if (err < 0) { | |
1da177e4 LT |
928 | snd_timer_free(timer); |
929 | return err; | |
930 | } | |
931 | } | |
7eaa943c TI |
932 | if (rtimer) |
933 | *rtimer = timer; | |
1da177e4 LT |
934 | return 0; |
935 | } | |
98856392 | 936 | EXPORT_SYMBOL(snd_timer_new); |
1da177e4 | 937 | |
53d2f744 | 938 | static int snd_timer_free(struct snd_timer *timer) |
1da177e4 | 939 | { |
7eaa943c TI |
940 | if (!timer) |
941 | return 0; | |
c461482c TI |
942 | |
943 | mutex_lock(®ister_mutex); | |
944 | if (! list_empty(&timer->open_list_head)) { | |
945 | struct list_head *p, *n; | |
946 | struct snd_timer_instance *ti; | |
cf74dcf3 | 947 | pr_warn("ALSA: timer %p is busy?\n", timer); |
c461482c TI |
948 | list_for_each_safe(p, n, &timer->open_list_head) { |
949 | list_del_init(p); | |
950 | ti = list_entry(p, struct snd_timer_instance, open_list); | |
951 | ti->timer = NULL; | |
952 | } | |
953 | } | |
954 | list_del(&timer->device_list); | |
955 | mutex_unlock(®ister_mutex); | |
956 | ||
1da177e4 LT |
957 | if (timer->private_free) |
958 | timer->private_free(timer); | |
959 | kfree(timer); | |
960 | return 0; | |
961 | } | |
962 | ||
53d2f744 | 963 | static int snd_timer_dev_free(struct snd_device *device) |
1da177e4 | 964 | { |
53d2f744 | 965 | struct snd_timer *timer = device->device_data; |
1da177e4 LT |
966 | return snd_timer_free(timer); |
967 | } | |
968 | ||
53d2f744 | 969 | static int snd_timer_dev_register(struct snd_device *dev) |
1da177e4 | 970 | { |
53d2f744 TI |
971 | struct snd_timer *timer = dev->device_data; |
972 | struct snd_timer *timer1; | |
1da177e4 | 973 | |
7eaa943c TI |
974 | if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) |
975 | return -ENXIO; | |
1da177e4 LT |
976 | if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && |
977 | !timer->hw.resolution && timer->hw.c_resolution == NULL) | |
978 | return -EINVAL; | |
979 | ||
1a60d4c5 | 980 | mutex_lock(®ister_mutex); |
9244b2c3 | 981 | list_for_each_entry(timer1, &snd_timer_list, device_list) { |
1da177e4 LT |
982 | if (timer1->tmr_class > timer->tmr_class) |
983 | break; | |
984 | if (timer1->tmr_class < timer->tmr_class) | |
985 | continue; | |
986 | if (timer1->card && timer->card) { | |
987 | if (timer1->card->number > timer->card->number) | |
988 | break; | |
989 | if (timer1->card->number < timer->card->number) | |
990 | continue; | |
991 | } | |
992 | if (timer1->tmr_device > timer->tmr_device) | |
993 | break; | |
994 | if (timer1->tmr_device < timer->tmr_device) | |
995 | continue; | |
996 | if (timer1->tmr_subdevice > timer->tmr_subdevice) | |
997 | break; | |
998 | if (timer1->tmr_subdevice < timer->tmr_subdevice) | |
999 | continue; | |
1000 | /* conflicts.. */ | |
1a60d4c5 | 1001 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1002 | return -EBUSY; |
1003 | } | |
9244b2c3 | 1004 | list_add_tail(&timer->device_list, &timer1->device_list); |
1a60d4c5 | 1005 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1006 | return 0; |
1007 | } | |
1008 | ||
c461482c | 1009 | static int snd_timer_dev_disconnect(struct snd_device *device) |
1da177e4 | 1010 | { |
c461482c | 1011 | struct snd_timer *timer = device->device_data; |
230323da TI |
1012 | struct snd_timer_instance *ti; |
1013 | ||
1a60d4c5 | 1014 | mutex_lock(®ister_mutex); |
c461482c | 1015 | list_del_init(&timer->device_list); |
230323da TI |
1016 | /* wake up pending sleepers */ |
1017 | list_for_each_entry(ti, &timer->open_list_head, open_list) { | |
40ed9444 TI |
1018 | if (ti->disconnect) |
1019 | ti->disconnect(ti); | |
230323da | 1020 | } |
1a60d4c5 | 1021 | mutex_unlock(®ister_mutex); |
c461482c | 1022 | return 0; |
1da177e4 LT |
1023 | } |
1024 | ||
53d2f744 | 1025 | void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp) |
1da177e4 LT |
1026 | { |
1027 | unsigned long flags; | |
1028 | unsigned long resolution = 0; | |
53d2f744 | 1029 | struct snd_timer_instance *ti, *ts; |
1da177e4 | 1030 | |
230323da TI |
1031 | if (timer->card && timer->card->shutdown) |
1032 | return; | |
7c22f1aa TI |
1033 | if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) |
1034 | return; | |
7eaa943c TI |
1035 | if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || |
1036 | event > SNDRV_TIMER_EVENT_MRESUME)) | |
1037 | return; | |
1da177e4 | 1038 | spin_lock_irqsave(&timer->lock, flags); |
a501dfa3 JK |
1039 | if (event == SNDRV_TIMER_EVENT_MSTART || |
1040 | event == SNDRV_TIMER_EVENT_MCONTINUE || | |
fdcb5761 TI |
1041 | event == SNDRV_TIMER_EVENT_MRESUME) |
1042 | resolution = snd_timer_hw_resolution(timer); | |
9244b2c3 | 1043 | list_for_each_entry(ti, &timer->active_list_head, active_list) { |
1da177e4 LT |
1044 | if (ti->ccallback) |
1045 | ti->ccallback(ti, event, tstamp, resolution); | |
9244b2c3 | 1046 | list_for_each_entry(ts, &ti->slave_active_head, active_list) |
1da177e4 LT |
1047 | if (ts->ccallback) |
1048 | ts->ccallback(ts, event, tstamp, resolution); | |
1da177e4 LT |
1049 | } |
1050 | spin_unlock_irqrestore(&timer->lock, flags); | |
1051 | } | |
98856392 | 1052 | EXPORT_SYMBOL(snd_timer_notify); |
1da177e4 LT |
1053 | |
1054 | /* | |
1055 | * exported functions for global timers | |
1056 | */ | |
53d2f744 | 1057 | int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) |
1da177e4 | 1058 | { |
53d2f744 | 1059 | struct snd_timer_id tid; |
6b172a85 | 1060 | |
1da177e4 LT |
1061 | tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; |
1062 | tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; | |
1063 | tid.card = -1; | |
1064 | tid.device = device; | |
1065 | tid.subdevice = 0; | |
1066 | return snd_timer_new(NULL, id, &tid, rtimer); | |
1067 | } | |
98856392 | 1068 | EXPORT_SYMBOL(snd_timer_global_new); |
1da177e4 | 1069 | |
53d2f744 | 1070 | int snd_timer_global_free(struct snd_timer *timer) |
1da177e4 LT |
1071 | { |
1072 | return snd_timer_free(timer); | |
1073 | } | |
98856392 | 1074 | EXPORT_SYMBOL(snd_timer_global_free); |
1da177e4 | 1075 | |
53d2f744 | 1076 | int snd_timer_global_register(struct snd_timer *timer) |
1da177e4 | 1077 | { |
53d2f744 | 1078 | struct snd_device dev; |
1da177e4 LT |
1079 | |
1080 | memset(&dev, 0, sizeof(dev)); | |
1081 | dev.device_data = timer; | |
1082 | return snd_timer_dev_register(&dev); | |
1083 | } | |
98856392 | 1084 | EXPORT_SYMBOL(snd_timer_global_register); |
1da177e4 | 1085 | |
6b172a85 | 1086 | /* |
1da177e4 LT |
1087 | * System timer |
1088 | */ | |
1089 | ||
1090 | struct snd_timer_system_private { | |
1091 | struct timer_list tlist; | |
38e9a80f | 1092 | struct snd_timer *snd_timer; |
1da177e4 LT |
1093 | unsigned long last_expires; |
1094 | unsigned long last_jiffies; | |
1095 | unsigned long correction; | |
1096 | }; | |
1097 | ||
38e9a80f | 1098 | static void snd_timer_s_function(struct timer_list *t) |
1da177e4 | 1099 | { |
38e9a80f KC |
1100 | struct snd_timer_system_private *priv = from_timer(priv, t, |
1101 | tlist); | |
1102 | struct snd_timer *timer = priv->snd_timer; | |
1da177e4 LT |
1103 | unsigned long jiff = jiffies; |
1104 | if (time_after(jiff, priv->last_expires)) | |
6ed5eff0 | 1105 | priv->correction += (long)jiff - (long)priv->last_expires; |
1da177e4 LT |
1106 | snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); |
1107 | } | |
1108 | ||
53d2f744 | 1109 | static int snd_timer_s_start(struct snd_timer * timer) |
1da177e4 LT |
1110 | { |
1111 | struct snd_timer_system_private *priv; | |
1112 | unsigned long njiff; | |
1113 | ||
1114 | priv = (struct snd_timer_system_private *) timer->private_data; | |
1115 | njiff = (priv->last_jiffies = jiffies); | |
1116 | if (priv->correction > timer->sticks - 1) { | |
1117 | priv->correction -= timer->sticks - 1; | |
1118 | njiff++; | |
1119 | } else { | |
1120 | njiff += timer->sticks - priv->correction; | |
17f48ec3 | 1121 | priv->correction = 0; |
1da177e4 | 1122 | } |
4a07083e TI |
1123 | priv->last_expires = njiff; |
1124 | mod_timer(&priv->tlist, njiff); | |
1da177e4 LT |
1125 | return 0; |
1126 | } | |
1127 | ||
53d2f744 | 1128 | static int snd_timer_s_stop(struct snd_timer * timer) |
1da177e4 LT |
1129 | { |
1130 | struct snd_timer_system_private *priv; | |
1131 | unsigned long jiff; | |
1132 | ||
1133 | priv = (struct snd_timer_system_private *) timer->private_data; | |
1134 | del_timer(&priv->tlist); | |
1135 | jiff = jiffies; | |
1136 | if (time_before(jiff, priv->last_expires)) | |
1137 | timer->sticks = priv->last_expires - jiff; | |
1138 | else | |
1139 | timer->sticks = 1; | |
de2696d8 | 1140 | priv->correction = 0; |
1da177e4 LT |
1141 | return 0; |
1142 | } | |
1143 | ||
f146357f TI |
1144 | static int snd_timer_s_close(struct snd_timer *timer) |
1145 | { | |
1146 | struct snd_timer_system_private *priv; | |
1147 | ||
1148 | priv = (struct snd_timer_system_private *)timer->private_data; | |
1149 | del_timer_sync(&priv->tlist); | |
1150 | return 0; | |
1151 | } | |
1152 | ||
53d2f744 | 1153 | static struct snd_timer_hardware snd_timer_system = |
1da177e4 LT |
1154 | { |
1155 | .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, | |
1156 | .resolution = 1000000000L / HZ, | |
1157 | .ticks = 10000000L, | |
f146357f | 1158 | .close = snd_timer_s_close, |
1da177e4 LT |
1159 | .start = snd_timer_s_start, |
1160 | .stop = snd_timer_s_stop | |
1161 | }; | |
1162 | ||
53d2f744 | 1163 | static void snd_timer_free_system(struct snd_timer *timer) |
1da177e4 LT |
1164 | { |
1165 | kfree(timer->private_data); | |
1166 | } | |
1167 | ||
1168 | static int snd_timer_register_system(void) | |
1169 | { | |
53d2f744 | 1170 | struct snd_timer *timer; |
1da177e4 LT |
1171 | struct snd_timer_system_private *priv; |
1172 | int err; | |
1173 | ||
6b172a85 CL |
1174 | err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); |
1175 | if (err < 0) | |
1da177e4 LT |
1176 | return err; |
1177 | strcpy(timer->name, "system timer"); | |
1178 | timer->hw = snd_timer_system; | |
ca2c0966 | 1179 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
1da177e4 LT |
1180 | if (priv == NULL) { |
1181 | snd_timer_free(timer); | |
1182 | return -ENOMEM; | |
1183 | } | |
38e9a80f KC |
1184 | priv->snd_timer = timer; |
1185 | timer_setup(&priv->tlist, snd_timer_s_function, 0); | |
1da177e4 LT |
1186 | timer->private_data = priv; |
1187 | timer->private_free = snd_timer_free_system; | |
1188 | return snd_timer_global_register(timer); | |
1189 | } | |
1190 | ||
cd6a6503 | 1191 | #ifdef CONFIG_SND_PROC_FS |
1da177e4 LT |
1192 | /* |
1193 | * Info interface | |
1194 | */ | |
1195 | ||
53d2f744 TI |
1196 | static void snd_timer_proc_read(struct snd_info_entry *entry, |
1197 | struct snd_info_buffer *buffer) | |
1da177e4 | 1198 | { |
53d2f744 TI |
1199 | struct snd_timer *timer; |
1200 | struct snd_timer_instance *ti; | |
1da177e4 | 1201 | |
1a60d4c5 | 1202 | mutex_lock(®ister_mutex); |
9244b2c3 | 1203 | list_for_each_entry(timer, &snd_timer_list, device_list) { |
230323da TI |
1204 | if (timer->card && timer->card->shutdown) |
1205 | continue; | |
1da177e4 LT |
1206 | switch (timer->tmr_class) { |
1207 | case SNDRV_TIMER_CLASS_GLOBAL: | |
1208 | snd_iprintf(buffer, "G%i: ", timer->tmr_device); | |
1209 | break; | |
1210 | case SNDRV_TIMER_CLASS_CARD: | |
6b172a85 CL |
1211 | snd_iprintf(buffer, "C%i-%i: ", |
1212 | timer->card->number, timer->tmr_device); | |
1da177e4 LT |
1213 | break; |
1214 | case SNDRV_TIMER_CLASS_PCM: | |
6b172a85 CL |
1215 | snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, |
1216 | timer->tmr_device, timer->tmr_subdevice); | |
1da177e4 LT |
1217 | break; |
1218 | default: | |
6b172a85 CL |
1219 | snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, |
1220 | timer->card ? timer->card->number : -1, | |
1221 | timer->tmr_device, timer->tmr_subdevice); | |
1da177e4 LT |
1222 | } |
1223 | snd_iprintf(buffer, "%s :", timer->name); | |
1224 | if (timer->hw.resolution) | |
6b172a85 CL |
1225 | snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", |
1226 | timer->hw.resolution / 1000, | |
1227 | timer->hw.resolution % 1000, | |
1228 | timer->hw.ticks); | |
1da177e4 LT |
1229 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) |
1230 | snd_iprintf(buffer, " SLAVE"); | |
1231 | snd_iprintf(buffer, "\n"); | |
9244b2c3 | 1232 | list_for_each_entry(ti, &timer->open_list_head, open_list) |
6b172a85 CL |
1233 | snd_iprintf(buffer, " Client %s : %s\n", |
1234 | ti->owner ? ti->owner : "unknown", | |
1235 | ti->flags & (SNDRV_TIMER_IFLG_START | | |
1236 | SNDRV_TIMER_IFLG_RUNNING) | |
1237 | ? "running" : "stopped"); | |
1da177e4 | 1238 | } |
1a60d4c5 | 1239 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1240 | } |
1241 | ||
6581f4e7 | 1242 | static struct snd_info_entry *snd_timer_proc_entry; |
e28563cc TI |
1243 | |
1244 | static void __init snd_timer_proc_init(void) | |
1245 | { | |
1246 | struct snd_info_entry *entry; | |
1247 | ||
1248 | entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); | |
1249 | if (entry != NULL) { | |
e28563cc TI |
1250 | entry->c.text.read = snd_timer_proc_read; |
1251 | if (snd_info_register(entry) < 0) { | |
1252 | snd_info_free_entry(entry); | |
1253 | entry = NULL; | |
1254 | } | |
1255 | } | |
1256 | snd_timer_proc_entry = entry; | |
1257 | } | |
1258 | ||
1259 | static void __exit snd_timer_proc_done(void) | |
1260 | { | |
746d4a02 | 1261 | snd_info_free_entry(snd_timer_proc_entry); |
e28563cc | 1262 | } |
cd6a6503 | 1263 | #else /* !CONFIG_SND_PROC_FS */ |
e28563cc TI |
1264 | #define snd_timer_proc_init() |
1265 | #define snd_timer_proc_done() | |
1266 | #endif | |
1267 | ||
1da177e4 LT |
1268 | /* |
1269 | * USER SPACE interface | |
1270 | */ | |
1271 | ||
53d2f744 | 1272 | static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, |
1da177e4 LT |
1273 | unsigned long resolution, |
1274 | unsigned long ticks) | |
1275 | { | |
53d2f744 TI |
1276 | struct snd_timer_user *tu = timeri->callback_data; |
1277 | struct snd_timer_read *r; | |
1da177e4 | 1278 | int prev; |
6b172a85 | 1279 | |
1da177e4 LT |
1280 | spin_lock(&tu->qlock); |
1281 | if (tu->qused > 0) { | |
1282 | prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; | |
1283 | r = &tu->queue[prev]; | |
1284 | if (r->resolution == resolution) { | |
1285 | r->ticks += ticks; | |
1286 | goto __wake; | |
1287 | } | |
1288 | } | |
1289 | if (tu->qused >= tu->queue_size) { | |
1290 | tu->overrun++; | |
1291 | } else { | |
1292 | r = &tu->queue[tu->qtail++]; | |
1293 | tu->qtail %= tu->queue_size; | |
1294 | r->resolution = resolution; | |
1295 | r->ticks = ticks; | |
1296 | tu->qused++; | |
1297 | } | |
1298 | __wake: | |
1299 | spin_unlock(&tu->qlock); | |
1300 | kill_fasync(&tu->fasync, SIGIO, POLL_IN); | |
1301 | wake_up(&tu->qchange_sleep); | |
1302 | } | |
1303 | ||
53d2f744 TI |
1304 | static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, |
1305 | struct snd_timer_tread *tread) | |
1da177e4 LT |
1306 | { |
1307 | if (tu->qused >= tu->queue_size) { | |
1308 | tu->overrun++; | |
1309 | } else { | |
1310 | memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); | |
1311 | tu->qtail %= tu->queue_size; | |
1312 | tu->qused++; | |
1313 | } | |
1314 | } | |
1315 | ||
53d2f744 TI |
1316 | static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, |
1317 | int event, | |
1da177e4 LT |
1318 | struct timespec *tstamp, |
1319 | unsigned long resolution) | |
1320 | { | |
53d2f744 TI |
1321 | struct snd_timer_user *tu = timeri->callback_data; |
1322 | struct snd_timer_tread r1; | |
bfe70783 | 1323 | unsigned long flags; |
1da177e4 | 1324 | |
6b172a85 CL |
1325 | if (event >= SNDRV_TIMER_EVENT_START && |
1326 | event <= SNDRV_TIMER_EVENT_PAUSE) | |
1da177e4 LT |
1327 | tu->tstamp = *tstamp; |
1328 | if ((tu->filter & (1 << event)) == 0 || !tu->tread) | |
1329 | return; | |
9a47e9cf | 1330 | memset(&r1, 0, sizeof(r1)); |
1da177e4 LT |
1331 | r1.event = event; |
1332 | r1.tstamp = *tstamp; | |
1333 | r1.val = resolution; | |
bfe70783 | 1334 | spin_lock_irqsave(&tu->qlock, flags); |
1da177e4 | 1335 | snd_timer_user_append_to_tqueue(tu, &r1); |
bfe70783 | 1336 | spin_unlock_irqrestore(&tu->qlock, flags); |
1da177e4 LT |
1337 | kill_fasync(&tu->fasync, SIGIO, POLL_IN); |
1338 | wake_up(&tu->qchange_sleep); | |
1339 | } | |
1340 | ||
40ed9444 TI |
1341 | static void snd_timer_user_disconnect(struct snd_timer_instance *timeri) |
1342 | { | |
1343 | struct snd_timer_user *tu = timeri->callback_data; | |
1344 | ||
1345 | tu->disconnected = true; | |
1346 | wake_up(&tu->qchange_sleep); | |
1347 | } | |
1348 | ||
53d2f744 | 1349 | static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, |
1da177e4 LT |
1350 | unsigned long resolution, |
1351 | unsigned long ticks) | |
1352 | { | |
53d2f744 TI |
1353 | struct snd_timer_user *tu = timeri->callback_data; |
1354 | struct snd_timer_tread *r, r1; | |
1da177e4 LT |
1355 | struct timespec tstamp; |
1356 | int prev, append = 0; | |
1357 | ||
a8c006aa | 1358 | memset(&r1, 0, sizeof(r1)); |
07799e75 | 1359 | memset(&tstamp, 0, sizeof(tstamp)); |
1da177e4 | 1360 | spin_lock(&tu->qlock); |
6b172a85 CL |
1361 | if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | |
1362 | (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { | |
1da177e4 LT |
1363 | spin_unlock(&tu->qlock); |
1364 | return; | |
1365 | } | |
b751eef1 JK |
1366 | if (tu->last_resolution != resolution || ticks > 0) { |
1367 | if (timer_tstamp_monotonic) | |
26204e04 | 1368 | ktime_get_ts(&tstamp); |
b751eef1 JK |
1369 | else |
1370 | getnstimeofday(&tstamp); | |
1371 | } | |
6b172a85 CL |
1372 | if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && |
1373 | tu->last_resolution != resolution) { | |
1da177e4 LT |
1374 | r1.event = SNDRV_TIMER_EVENT_RESOLUTION; |
1375 | r1.tstamp = tstamp; | |
1376 | r1.val = resolution; | |
1377 | snd_timer_user_append_to_tqueue(tu, &r1); | |
1378 | tu->last_resolution = resolution; | |
1379 | append++; | |
1380 | } | |
1381 | if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) | |
1382 | goto __wake; | |
1383 | if (ticks == 0) | |
1384 | goto __wake; | |
1385 | if (tu->qused > 0) { | |
1386 | prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; | |
1387 | r = &tu->tqueue[prev]; | |
1388 | if (r->event == SNDRV_TIMER_EVENT_TICK) { | |
1389 | r->tstamp = tstamp; | |
1390 | r->val += ticks; | |
1391 | append++; | |
1392 | goto __wake; | |
1393 | } | |
1394 | } | |
1395 | r1.event = SNDRV_TIMER_EVENT_TICK; | |
1396 | r1.tstamp = tstamp; | |
1397 | r1.val = ticks; | |
1398 | snd_timer_user_append_to_tqueue(tu, &r1); | |
1399 | append++; | |
1400 | __wake: | |
1401 | spin_unlock(&tu->qlock); | |
1402 | if (append == 0) | |
1403 | return; | |
1404 | kill_fasync(&tu->fasync, SIGIO, POLL_IN); | |
1405 | wake_up(&tu->qchange_sleep); | |
1406 | } | |
1407 | ||
890e2cb5 TI |
1408 | static int realloc_user_queue(struct snd_timer_user *tu, int size) |
1409 | { | |
1410 | struct snd_timer_read *queue = NULL; | |
1411 | struct snd_timer_tread *tqueue = NULL; | |
1412 | ||
1413 | if (tu->tread) { | |
1414 | tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); | |
1415 | if (!tqueue) | |
1416 | return -ENOMEM; | |
1417 | } else { | |
1418 | queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); | |
1419 | if (!queue) | |
1420 | return -ENOMEM; | |
1421 | } | |
1422 | ||
1423 | spin_lock_irq(&tu->qlock); | |
1424 | kfree(tu->queue); | |
1425 | kfree(tu->tqueue); | |
1426 | tu->queue_size = size; | |
1427 | tu->queue = queue; | |
1428 | tu->tqueue = tqueue; | |
1429 | tu->qhead = tu->qtail = tu->qused = 0; | |
1430 | spin_unlock_irq(&tu->qlock); | |
1431 | ||
1432 | return 0; | |
1433 | } | |
1434 | ||
1da177e4 LT |
1435 | static int snd_timer_user_open(struct inode *inode, struct file *file) |
1436 | { | |
53d2f744 | 1437 | struct snd_timer_user *tu; |
02f4865f TI |
1438 | int err; |
1439 | ||
c5bf68fe | 1440 | err = stream_open(inode, file); |
02f4865f TI |
1441 | if (err < 0) |
1442 | return err; | |
6b172a85 | 1443 | |
ca2c0966 | 1444 | tu = kzalloc(sizeof(*tu), GFP_KERNEL); |
1da177e4 LT |
1445 | if (tu == NULL) |
1446 | return -ENOMEM; | |
1447 | spin_lock_init(&tu->qlock); | |
1448 | init_waitqueue_head(&tu->qchange_sleep); | |
af368027 | 1449 | mutex_init(&tu->ioctl_lock); |
1da177e4 | 1450 | tu->ticks = 1; |
890e2cb5 | 1451 | if (realloc_user_queue(tu, 128) < 0) { |
1da177e4 LT |
1452 | kfree(tu); |
1453 | return -ENOMEM; | |
1454 | } | |
1455 | file->private_data = tu; | |
1456 | return 0; | |
1457 | } | |
1458 | ||
1459 | static int snd_timer_user_release(struct inode *inode, struct file *file) | |
1460 | { | |
53d2f744 | 1461 | struct snd_timer_user *tu; |
1da177e4 LT |
1462 | |
1463 | if (file->private_data) { | |
1464 | tu = file->private_data; | |
1465 | file->private_data = NULL; | |
af368027 | 1466 | mutex_lock(&tu->ioctl_lock); |
1da177e4 LT |
1467 | if (tu->timeri) |
1468 | snd_timer_close(tu->timeri); | |
af368027 | 1469 | mutex_unlock(&tu->ioctl_lock); |
1da177e4 LT |
1470 | kfree(tu->queue); |
1471 | kfree(tu->tqueue); | |
1472 | kfree(tu); | |
1473 | } | |
1474 | return 0; | |
1475 | } | |
1476 | ||
53d2f744 | 1477 | static void snd_timer_user_zero_id(struct snd_timer_id *id) |
1da177e4 LT |
1478 | { |
1479 | id->dev_class = SNDRV_TIMER_CLASS_NONE; | |
1480 | id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; | |
1481 | id->card = -1; | |
1482 | id->device = -1; | |
1483 | id->subdevice = -1; | |
1484 | } | |
1485 | ||
53d2f744 | 1486 | static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer) |
1da177e4 LT |
1487 | { |
1488 | id->dev_class = timer->tmr_class; | |
1489 | id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; | |
1490 | id->card = timer->card ? timer->card->number : -1; | |
1491 | id->device = timer->tmr_device; | |
1492 | id->subdevice = timer->tmr_subdevice; | |
1493 | } | |
1494 | ||
53d2f744 | 1495 | static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) |
1da177e4 | 1496 | { |
53d2f744 TI |
1497 | struct snd_timer_id id; |
1498 | struct snd_timer *timer; | |
1da177e4 | 1499 | struct list_head *p; |
6b172a85 | 1500 | |
1da177e4 LT |
1501 | if (copy_from_user(&id, _tid, sizeof(id))) |
1502 | return -EFAULT; | |
1a60d4c5 | 1503 | mutex_lock(®ister_mutex); |
1da177e4 LT |
1504 | if (id.dev_class < 0) { /* first item */ |
1505 | if (list_empty(&snd_timer_list)) | |
1506 | snd_timer_user_zero_id(&id); | |
1507 | else { | |
9dfba380 | 1508 | timer = list_entry(snd_timer_list.next, |
53d2f744 | 1509 | struct snd_timer, device_list); |
1da177e4 LT |
1510 | snd_timer_user_copy_id(&id, timer); |
1511 | } | |
1512 | } else { | |
1513 | switch (id.dev_class) { | |
1514 | case SNDRV_TIMER_CLASS_GLOBAL: | |
1515 | id.device = id.device < 0 ? 0 : id.device + 1; | |
1516 | list_for_each(p, &snd_timer_list) { | |
53d2f744 | 1517 | timer = list_entry(p, struct snd_timer, device_list); |
1da177e4 LT |
1518 | if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { |
1519 | snd_timer_user_copy_id(&id, timer); | |
1520 | break; | |
1521 | } | |
1522 | if (timer->tmr_device >= id.device) { | |
1523 | snd_timer_user_copy_id(&id, timer); | |
1524 | break; | |
1525 | } | |
1526 | } | |
1527 | if (p == &snd_timer_list) | |
1528 | snd_timer_user_zero_id(&id); | |
1529 | break; | |
1530 | case SNDRV_TIMER_CLASS_CARD: | |
1531 | case SNDRV_TIMER_CLASS_PCM: | |
1532 | if (id.card < 0) { | |
1533 | id.card = 0; | |
1534 | } else { | |
e8ed6820 DC |
1535 | if (id.device < 0) { |
1536 | id.device = 0; | |
1da177e4 | 1537 | } else { |
e8ed6820 DC |
1538 | if (id.subdevice < 0) |
1539 | id.subdevice = 0; | |
b41f794f | 1540 | else if (id.subdevice < INT_MAX) |
e8ed6820 | 1541 | id.subdevice++; |
1da177e4 LT |
1542 | } |
1543 | } | |
1544 | list_for_each(p, &snd_timer_list) { | |
53d2f744 | 1545 | timer = list_entry(p, struct snd_timer, device_list); |
1da177e4 LT |
1546 | if (timer->tmr_class > id.dev_class) { |
1547 | snd_timer_user_copy_id(&id, timer); | |
1548 | break; | |
1549 | } | |
1550 | if (timer->tmr_class < id.dev_class) | |
1551 | continue; | |
1552 | if (timer->card->number > id.card) { | |
1553 | snd_timer_user_copy_id(&id, timer); | |
1554 | break; | |
1555 | } | |
1556 | if (timer->card->number < id.card) | |
1557 | continue; | |
1558 | if (timer->tmr_device > id.device) { | |
1559 | snd_timer_user_copy_id(&id, timer); | |
1560 | break; | |
1561 | } | |
1562 | if (timer->tmr_device < id.device) | |
1563 | continue; | |
1564 | if (timer->tmr_subdevice > id.subdevice) { | |
1565 | snd_timer_user_copy_id(&id, timer); | |
1566 | break; | |
1567 | } | |
1568 | if (timer->tmr_subdevice < id.subdevice) | |
1569 | continue; | |
1570 | snd_timer_user_copy_id(&id, timer); | |
1571 | break; | |
1572 | } | |
1573 | if (p == &snd_timer_list) | |
1574 | snd_timer_user_zero_id(&id); | |
1575 | break; | |
1576 | default: | |
1577 | snd_timer_user_zero_id(&id); | |
1578 | } | |
1579 | } | |
1a60d4c5 | 1580 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1581 | if (copy_to_user(_tid, &id, sizeof(*_tid))) |
1582 | return -EFAULT; | |
1583 | return 0; | |
6b172a85 | 1584 | } |
1da177e4 | 1585 | |
6b172a85 | 1586 | static int snd_timer_user_ginfo(struct file *file, |
53d2f744 | 1587 | struct snd_timer_ginfo __user *_ginfo) |
1da177e4 | 1588 | { |
53d2f744 TI |
1589 | struct snd_timer_ginfo *ginfo; |
1590 | struct snd_timer_id tid; | |
1591 | struct snd_timer *t; | |
1da177e4 LT |
1592 | struct list_head *p; |
1593 | int err = 0; | |
1594 | ||
ef44a1ec LZ |
1595 | ginfo = memdup_user(_ginfo, sizeof(*ginfo)); |
1596 | if (IS_ERR(ginfo)) | |
1597 | return PTR_ERR(ginfo); | |
1598 | ||
1da177e4 LT |
1599 | tid = ginfo->tid; |
1600 | memset(ginfo, 0, sizeof(*ginfo)); | |
1601 | ginfo->tid = tid; | |
1a60d4c5 | 1602 | mutex_lock(®ister_mutex); |
1da177e4 LT |
1603 | t = snd_timer_find(&tid); |
1604 | if (t != NULL) { | |
1605 | ginfo->card = t->card ? t->card->number : -1; | |
1606 | if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) | |
1607 | ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; | |
1608 | strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); | |
1609 | strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); | |
1610 | ginfo->resolution = t->hw.resolution; | |
1611 | if (t->hw.resolution_min > 0) { | |
1612 | ginfo->resolution_min = t->hw.resolution_min; | |
1613 | ginfo->resolution_max = t->hw.resolution_max; | |
1614 | } | |
1615 | list_for_each(p, &t->open_list_head) { | |
1616 | ginfo->clients++; | |
1617 | } | |
1618 | } else { | |
1619 | err = -ENODEV; | |
1620 | } | |
1a60d4c5 | 1621 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1622 | if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) |
1623 | err = -EFAULT; | |
1624 | kfree(ginfo); | |
1625 | return err; | |
1626 | } | |
1627 | ||
91d2178e | 1628 | static int timer_set_gparams(struct snd_timer_gparams *gparams) |
1da177e4 | 1629 | { |
53d2f744 | 1630 | struct snd_timer *t; |
1da177e4 LT |
1631 | int err; |
1632 | ||
1a60d4c5 | 1633 | mutex_lock(®ister_mutex); |
91d2178e | 1634 | t = snd_timer_find(&gparams->tid); |
6b172a85 | 1635 | if (!t) { |
1da177e4 | 1636 | err = -ENODEV; |
6b172a85 CL |
1637 | goto _error; |
1638 | } | |
1639 | if (!list_empty(&t->open_list_head)) { | |
1640 | err = -EBUSY; | |
1641 | goto _error; | |
1642 | } | |
1643 | if (!t->hw.set_period) { | |
1644 | err = -ENOSYS; | |
1645 | goto _error; | |
1da177e4 | 1646 | } |
91d2178e | 1647 | err = t->hw.set_period(t, gparams->period_num, gparams->period_den); |
6b172a85 | 1648 | _error: |
1a60d4c5 | 1649 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1650 | return err; |
1651 | } | |
1652 | ||
91d2178e TS |
1653 | static int snd_timer_user_gparams(struct file *file, |
1654 | struct snd_timer_gparams __user *_gparams) | |
1655 | { | |
1656 | struct snd_timer_gparams gparams; | |
1657 | ||
1658 | if (copy_from_user(&gparams, _gparams, sizeof(gparams))) | |
1659 | return -EFAULT; | |
1660 | return timer_set_gparams(&gparams); | |
1661 | } | |
1662 | ||
6b172a85 | 1663 | static int snd_timer_user_gstatus(struct file *file, |
53d2f744 | 1664 | struct snd_timer_gstatus __user *_gstatus) |
1da177e4 | 1665 | { |
53d2f744 TI |
1666 | struct snd_timer_gstatus gstatus; |
1667 | struct snd_timer_id tid; | |
1668 | struct snd_timer *t; | |
1da177e4 LT |
1669 | int err = 0; |
1670 | ||
1671 | if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) | |
1672 | return -EFAULT; | |
1673 | tid = gstatus.tid; | |
1674 | memset(&gstatus, 0, sizeof(gstatus)); | |
1675 | gstatus.tid = tid; | |
1a60d4c5 | 1676 | mutex_lock(®ister_mutex); |
1da177e4 LT |
1677 | t = snd_timer_find(&tid); |
1678 | if (t != NULL) { | |
9d4d207d | 1679 | spin_lock_irq(&t->lock); |
fdcb5761 | 1680 | gstatus.resolution = snd_timer_hw_resolution(t); |
1da177e4 | 1681 | if (t->hw.precise_resolution) { |
6b172a85 CL |
1682 | t->hw.precise_resolution(t, &gstatus.resolution_num, |
1683 | &gstatus.resolution_den); | |
1da177e4 LT |
1684 | } else { |
1685 | gstatus.resolution_num = gstatus.resolution; | |
1686 | gstatus.resolution_den = 1000000000uL; | |
1687 | } | |
9d4d207d | 1688 | spin_unlock_irq(&t->lock); |
1da177e4 LT |
1689 | } else { |
1690 | err = -ENODEV; | |
1691 | } | |
1a60d4c5 | 1692 | mutex_unlock(®ister_mutex); |
1da177e4 LT |
1693 | if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) |
1694 | err = -EFAULT; | |
1695 | return err; | |
1696 | } | |
1697 | ||
6b172a85 | 1698 | static int snd_timer_user_tselect(struct file *file, |
53d2f744 | 1699 | struct snd_timer_select __user *_tselect) |
1da177e4 | 1700 | { |
53d2f744 TI |
1701 | struct snd_timer_user *tu; |
1702 | struct snd_timer_select tselect; | |
1da177e4 | 1703 | char str[32]; |
c1935b4d | 1704 | int err = 0; |
6b172a85 | 1705 | |
1da177e4 | 1706 | tu = file->private_data; |
c1935b4d | 1707 | if (tu->timeri) { |
1da177e4 | 1708 | snd_timer_close(tu->timeri); |
c1935b4d JK |
1709 | tu->timeri = NULL; |
1710 | } | |
1711 | if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { | |
1712 | err = -EFAULT; | |
1713 | goto __err; | |
1714 | } | |
1da177e4 LT |
1715 | sprintf(str, "application %i", current->pid); |
1716 | if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) | |
1717 | tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; | |
6b172a85 CL |
1718 | err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid); |
1719 | if (err < 0) | |
c1935b4d | 1720 | goto __err; |
1da177e4 | 1721 | |
890e2cb5 TI |
1722 | tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; |
1723 | tu->timeri->callback = tu->tread | |
6b172a85 | 1724 | ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; |
890e2cb5 TI |
1725 | tu->timeri->ccallback = snd_timer_user_ccallback; |
1726 | tu->timeri->callback_data = (void *)tu; | |
1727 | tu->timeri->disconnect = snd_timer_user_disconnect; | |
c1935b4d JK |
1728 | |
1729 | __err: | |
c1935b4d | 1730 | return err; |
1da177e4 LT |
1731 | } |
1732 | ||
6b172a85 | 1733 | static int snd_timer_user_info(struct file *file, |
53d2f744 | 1734 | struct snd_timer_info __user *_info) |
1da177e4 | 1735 | { |
53d2f744 TI |
1736 | struct snd_timer_user *tu; |
1737 | struct snd_timer_info *info; | |
1738 | struct snd_timer *t; | |
1da177e4 LT |
1739 | int err = 0; |
1740 | ||
1741 | tu = file->private_data; | |
7c64ec34 CL |
1742 | if (!tu->timeri) |
1743 | return -EBADFD; | |
1da177e4 | 1744 | t = tu->timeri->timer; |
7c64ec34 CL |
1745 | if (!t) |
1746 | return -EBADFD; | |
1da177e4 | 1747 | |
ca2c0966 | 1748 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
1da177e4 LT |
1749 | if (! info) |
1750 | return -ENOMEM; | |
1751 | info->card = t->card ? t->card->number : -1; | |
1752 | if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) | |
1753 | info->flags |= SNDRV_TIMER_FLG_SLAVE; | |
1754 | strlcpy(info->id, t->id, sizeof(info->id)); | |
1755 | strlcpy(info->name, t->name, sizeof(info->name)); | |
1756 | info->resolution = t->hw.resolution; | |
1757 | if (copy_to_user(_info, info, sizeof(*_info))) | |
1758 | err = -EFAULT; | |
1759 | kfree(info); | |
1760 | return err; | |
1761 | } | |
1762 | ||
6b172a85 | 1763 | static int snd_timer_user_params(struct file *file, |
53d2f744 | 1764 | struct snd_timer_params __user *_params) |
1da177e4 | 1765 | { |
53d2f744 TI |
1766 | struct snd_timer_user *tu; |
1767 | struct snd_timer_params params; | |
1768 | struct snd_timer *t; | |
1da177e4 | 1769 | int err; |
6b172a85 | 1770 | |
1da177e4 | 1771 | tu = file->private_data; |
7c64ec34 CL |
1772 | if (!tu->timeri) |
1773 | return -EBADFD; | |
1da177e4 | 1774 | t = tu->timeri->timer; |
7c64ec34 CL |
1775 | if (!t) |
1776 | return -EBADFD; | |
1da177e4 LT |
1777 | if (copy_from_user(¶ms, _params, sizeof(params))) |
1778 | return -EFAULT; | |
71321eb3 TI |
1779 | if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { |
1780 | u64 resolution; | |
1781 | ||
1782 | if (params.ticks < 1) { | |
1783 | err = -EINVAL; | |
1784 | goto _end; | |
1785 | } | |
1786 | ||
1787 | /* Don't allow resolution less than 1ms */ | |
1788 | resolution = snd_timer_resolution(tu->timeri); | |
1789 | resolution *= params.ticks; | |
1790 | if (resolution < 1000000) { | |
1791 | err = -EINVAL; | |
1792 | goto _end; | |
1793 | } | |
1da177e4 | 1794 | } |
6b172a85 CL |
1795 | if (params.queue_size > 0 && |
1796 | (params.queue_size < 32 || params.queue_size > 1024)) { | |
1da177e4 LT |
1797 | err = -EINVAL; |
1798 | goto _end; | |
1799 | } | |
1800 | if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| | |
1801 | (1<<SNDRV_TIMER_EVENT_TICK)| | |
1802 | (1<<SNDRV_TIMER_EVENT_START)| | |
1803 | (1<<SNDRV_TIMER_EVENT_STOP)| | |
1804 | (1<<SNDRV_TIMER_EVENT_CONTINUE)| | |
1805 | (1<<SNDRV_TIMER_EVENT_PAUSE)| | |
a501dfa3 JK |
1806 | (1<<SNDRV_TIMER_EVENT_SUSPEND)| |
1807 | (1<<SNDRV_TIMER_EVENT_RESUME)| | |
1da177e4 LT |
1808 | (1<<SNDRV_TIMER_EVENT_MSTART)| |
1809 | (1<<SNDRV_TIMER_EVENT_MSTOP)| | |
1810 | (1<<SNDRV_TIMER_EVENT_MCONTINUE)| | |
65d11d95 JK |
1811 | (1<<SNDRV_TIMER_EVENT_MPAUSE)| |
1812 | (1<<SNDRV_TIMER_EVENT_MSUSPEND)| | |
a501dfa3 | 1813 | (1<<SNDRV_TIMER_EVENT_MRESUME))) { |
1da177e4 LT |
1814 | err = -EINVAL; |
1815 | goto _end; | |
1816 | } | |
1817 | snd_timer_stop(tu->timeri); | |
1818 | spin_lock_irq(&t->lock); | |
1819 | tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| | |
1820 | SNDRV_TIMER_IFLG_EXCLUSIVE| | |
1821 | SNDRV_TIMER_IFLG_EARLY_EVENT); | |
1822 | if (params.flags & SNDRV_TIMER_PSFLG_AUTO) | |
1823 | tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; | |
1824 | if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) | |
1825 | tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; | |
1826 | if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) | |
1827 | tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; | |
1828 | spin_unlock_irq(&t->lock); | |
6b172a85 CL |
1829 | if (params.queue_size > 0 && |
1830 | (unsigned int)tu->queue_size != params.queue_size) { | |
890e2cb5 TI |
1831 | err = realloc_user_queue(tu, params.queue_size); |
1832 | if (err < 0) | |
1833 | goto _end; | |
1da177e4 | 1834 | } |
d7f910bf | 1835 | spin_lock_irq(&tu->qlock); |
1da177e4 LT |
1836 | tu->qhead = tu->qtail = tu->qused = 0; |
1837 | if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { | |
1838 | if (tu->tread) { | |
53d2f744 | 1839 | struct snd_timer_tread tread; |
cec8f96e | 1840 | memset(&tread, 0, sizeof(tread)); |
1da177e4 LT |
1841 | tread.event = SNDRV_TIMER_EVENT_EARLY; |
1842 | tread.tstamp.tv_sec = 0; | |
1843 | tread.tstamp.tv_nsec = 0; | |
1844 | tread.val = 0; | |
1845 | snd_timer_user_append_to_tqueue(tu, &tread); | |
1846 | } else { | |
53d2f744 | 1847 | struct snd_timer_read *r = &tu->queue[0]; |
1da177e4 LT |
1848 | r->resolution = 0; |
1849 | r->ticks = 0; | |
1850 | tu->qused++; | |
1851 | tu->qtail++; | |
1852 | } | |
1da177e4 LT |
1853 | } |
1854 | tu->filter = params.filter; | |
1855 | tu->ticks = params.ticks; | |
d7f910bf | 1856 | spin_unlock_irq(&tu->qlock); |
1da177e4 LT |
1857 | err = 0; |
1858 | _end: | |
1859 | if (copy_to_user(_params, ¶ms, sizeof(params))) | |
1860 | return -EFAULT; | |
1861 | return err; | |
1862 | } | |
1863 | ||
6b172a85 | 1864 | static int snd_timer_user_status(struct file *file, |
53d2f744 | 1865 | struct snd_timer_status __user *_status) |
1da177e4 | 1866 | { |
53d2f744 TI |
1867 | struct snd_timer_user *tu; |
1868 | struct snd_timer_status status; | |
6b172a85 | 1869 | |
1da177e4 | 1870 | tu = file->private_data; |
7c64ec34 CL |
1871 | if (!tu->timeri) |
1872 | return -EBADFD; | |
1da177e4 LT |
1873 | memset(&status, 0, sizeof(status)); |
1874 | status.tstamp = tu->tstamp; | |
1875 | status.resolution = snd_timer_resolution(tu->timeri); | |
1876 | status.lost = tu->timeri->lost; | |
1877 | status.overrun = tu->overrun; | |
1878 | spin_lock_irq(&tu->qlock); | |
1879 | status.queue = tu->qused; | |
1880 | spin_unlock_irq(&tu->qlock); | |
1881 | if (copy_to_user(_status, &status, sizeof(status))) | |
1882 | return -EFAULT; | |
1883 | return 0; | |
1884 | } | |
1885 | ||
1886 | static int snd_timer_user_start(struct file *file) | |
1887 | { | |
1888 | int err; | |
53d2f744 | 1889 | struct snd_timer_user *tu; |
6b172a85 | 1890 | |
1da177e4 | 1891 | tu = file->private_data; |
7c64ec34 CL |
1892 | if (!tu->timeri) |
1893 | return -EBADFD; | |
1da177e4 LT |
1894 | snd_timer_stop(tu->timeri); |
1895 | tu->timeri->lost = 0; | |
1896 | tu->last_resolution = 0; | |
5d704b0d TI |
1897 | err = snd_timer_start(tu->timeri, tu->ticks); |
1898 | if (err < 0) | |
1899 | return err; | |
1900 | return 0; | |
1da177e4 LT |
1901 | } |
1902 | ||
1903 | static int snd_timer_user_stop(struct file *file) | |
1904 | { | |
1905 | int err; | |
53d2f744 | 1906 | struct snd_timer_user *tu; |
6b172a85 | 1907 | |
1da177e4 | 1908 | tu = file->private_data; |
7c64ec34 CL |
1909 | if (!tu->timeri) |
1910 | return -EBADFD; | |
5d704b0d TI |
1911 | err = snd_timer_stop(tu->timeri); |
1912 | if (err < 0) | |
1913 | return err; | |
1914 | return 0; | |
1da177e4 LT |
1915 | } |
1916 | ||
1917 | static int snd_timer_user_continue(struct file *file) | |
1918 | { | |
1919 | int err; | |
53d2f744 | 1920 | struct snd_timer_user *tu; |
6b172a85 | 1921 | |
1da177e4 | 1922 | tu = file->private_data; |
7c64ec34 CL |
1923 | if (!tu->timeri) |
1924 | return -EBADFD; | |
9f8a7658 TI |
1925 | /* start timer instead of continue if it's not used before */ |
1926 | if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) | |
1927 | return snd_timer_user_start(file); | |
1da177e4 | 1928 | tu->timeri->lost = 0; |
5d704b0d TI |
1929 | err = snd_timer_continue(tu->timeri); |
1930 | if (err < 0) | |
1931 | return err; | |
1932 | return 0; | |
1da177e4 LT |
1933 | } |
1934 | ||
15790a6b TI |
1935 | static int snd_timer_user_pause(struct file *file) |
1936 | { | |
1937 | int err; | |
53d2f744 | 1938 | struct snd_timer_user *tu; |
6b172a85 | 1939 | |
15790a6b | 1940 | tu = file->private_data; |
7c64ec34 CL |
1941 | if (!tu->timeri) |
1942 | return -EBADFD; | |
5d704b0d TI |
1943 | err = snd_timer_pause(tu->timeri); |
1944 | if (err < 0) | |
1945 | return err; | |
1946 | return 0; | |
15790a6b TI |
1947 | } |
1948 | ||
8c50b37c TI |
1949 | enum { |
1950 | SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), | |
1951 | SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), | |
1952 | SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), | |
1953 | SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), | |
1954 | }; | |
1955 | ||
af368027 | 1956 | static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, |
6b172a85 | 1957 | unsigned long arg) |
1da177e4 | 1958 | { |
53d2f744 | 1959 | struct snd_timer_user *tu; |
1da177e4 LT |
1960 | void __user *argp = (void __user *)arg; |
1961 | int __user *p = argp; | |
6b172a85 | 1962 | |
1da177e4 LT |
1963 | tu = file->private_data; |
1964 | switch (cmd) { | |
1965 | case SNDRV_TIMER_IOCTL_PVERSION: | |
1966 | return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; | |
1967 | case SNDRV_TIMER_IOCTL_NEXT_DEVICE: | |
1968 | return snd_timer_user_next_device(argp); | |
1969 | case SNDRV_TIMER_IOCTL_TREAD: | |
1970 | { | |
890e2cb5 | 1971 | int xarg, old_tread; |
6b172a85 | 1972 | |
af368027 | 1973 | if (tu->timeri) /* too late */ |
1da177e4 | 1974 | return -EBUSY; |
af368027 | 1975 | if (get_user(xarg, p)) |
1da177e4 | 1976 | return -EFAULT; |
890e2cb5 | 1977 | old_tread = tu->tread; |
1da177e4 | 1978 | tu->tread = xarg ? 1 : 0; |
890e2cb5 TI |
1979 | if (tu->tread != old_tread && |
1980 | realloc_user_queue(tu, tu->queue_size) < 0) { | |
1981 | tu->tread = old_tread; | |
1982 | return -ENOMEM; | |
1983 | } | |
1da177e4 LT |
1984 | return 0; |
1985 | } | |
1986 | case SNDRV_TIMER_IOCTL_GINFO: | |
1987 | return snd_timer_user_ginfo(file, argp); | |
1988 | case SNDRV_TIMER_IOCTL_GPARAMS: | |
1989 | return snd_timer_user_gparams(file, argp); | |
1990 | case SNDRV_TIMER_IOCTL_GSTATUS: | |
1991 | return snd_timer_user_gstatus(file, argp); | |
1992 | case SNDRV_TIMER_IOCTL_SELECT: | |
1993 | return snd_timer_user_tselect(file, argp); | |
1994 | case SNDRV_TIMER_IOCTL_INFO: | |
1995 | return snd_timer_user_info(file, argp); | |
1996 | case SNDRV_TIMER_IOCTL_PARAMS: | |
1997 | return snd_timer_user_params(file, argp); | |
1998 | case SNDRV_TIMER_IOCTL_STATUS: | |
1999 | return snd_timer_user_status(file, argp); | |
2000 | case SNDRV_TIMER_IOCTL_START: | |
8c50b37c | 2001 | case SNDRV_TIMER_IOCTL_START_OLD: |
1da177e4 LT |
2002 | return snd_timer_user_start(file); |
2003 | case SNDRV_TIMER_IOCTL_STOP: | |
8c50b37c | 2004 | case SNDRV_TIMER_IOCTL_STOP_OLD: |
1da177e4 LT |
2005 | return snd_timer_user_stop(file); |
2006 | case SNDRV_TIMER_IOCTL_CONTINUE: | |
8c50b37c | 2007 | case SNDRV_TIMER_IOCTL_CONTINUE_OLD: |
1da177e4 | 2008 | return snd_timer_user_continue(file); |
15790a6b | 2009 | case SNDRV_TIMER_IOCTL_PAUSE: |
8c50b37c | 2010 | case SNDRV_TIMER_IOCTL_PAUSE_OLD: |
15790a6b | 2011 | return snd_timer_user_pause(file); |
1da177e4 LT |
2012 | } |
2013 | return -ENOTTY; | |
2014 | } | |
2015 | ||
af368027 TI |
2016 | static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, |
2017 | unsigned long arg) | |
2018 | { | |
2019 | struct snd_timer_user *tu = file->private_data; | |
2020 | long ret; | |
2021 | ||
2022 | mutex_lock(&tu->ioctl_lock); | |
2023 | ret = __snd_timer_user_ioctl(file, cmd, arg); | |
2024 | mutex_unlock(&tu->ioctl_lock); | |
2025 | return ret; | |
2026 | } | |
2027 | ||
1da177e4 LT |
2028 | static int snd_timer_user_fasync(int fd, struct file * file, int on) |
2029 | { | |
53d2f744 | 2030 | struct snd_timer_user *tu; |
6b172a85 | 2031 | |
1da177e4 | 2032 | tu = file->private_data; |
60aa4924 | 2033 | return fasync_helper(fd, file, on, &tu->fasync); |
1da177e4 LT |
2034 | } |
2035 | ||
6b172a85 CL |
2036 | static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, |
2037 | size_t count, loff_t *offset) | |
1da177e4 | 2038 | { |
53d2f744 | 2039 | struct snd_timer_user *tu; |
1da177e4 | 2040 | long result = 0, unit; |
4dff5c7b | 2041 | int qhead; |
1da177e4 | 2042 | int err = 0; |
6b172a85 | 2043 | |
1da177e4 | 2044 | tu = file->private_data; |
53d2f744 | 2045 | unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read); |
d11662f4 | 2046 | mutex_lock(&tu->ioctl_lock); |
1da177e4 LT |
2047 | spin_lock_irq(&tu->qlock); |
2048 | while ((long)count - result >= unit) { | |
2049 | while (!tu->qused) { | |
ac6424b9 | 2050 | wait_queue_entry_t wait; |
1da177e4 LT |
2051 | |
2052 | if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { | |
2053 | err = -EAGAIN; | |
4dff5c7b | 2054 | goto _error; |
1da177e4 LT |
2055 | } |
2056 | ||
2057 | set_current_state(TASK_INTERRUPTIBLE); | |
2058 | init_waitqueue_entry(&wait, current); | |
2059 | add_wait_queue(&tu->qchange_sleep, &wait); | |
2060 | ||
2061 | spin_unlock_irq(&tu->qlock); | |
d11662f4 | 2062 | mutex_unlock(&tu->ioctl_lock); |
1da177e4 | 2063 | schedule(); |
d11662f4 | 2064 | mutex_lock(&tu->ioctl_lock); |
1da177e4 LT |
2065 | spin_lock_irq(&tu->qlock); |
2066 | ||
2067 | remove_wait_queue(&tu->qchange_sleep, &wait); | |
2068 | ||
230323da TI |
2069 | if (tu->disconnected) { |
2070 | err = -ENODEV; | |
4dff5c7b | 2071 | goto _error; |
230323da | 2072 | } |
1da177e4 LT |
2073 | if (signal_pending(current)) { |
2074 | err = -ERESTARTSYS; | |
4dff5c7b | 2075 | goto _error; |
1da177e4 LT |
2076 | } |
2077 | } | |
2078 | ||
4dff5c7b TI |
2079 | qhead = tu->qhead++; |
2080 | tu->qhead %= tu->queue_size; | |
3fa6993f | 2081 | tu->qused--; |
1da177e4 | 2082 | spin_unlock_irq(&tu->qlock); |
1da177e4 LT |
2083 | |
2084 | if (tu->tread) { | |
4dff5c7b TI |
2085 | if (copy_to_user(buffer, &tu->tqueue[qhead], |
2086 | sizeof(struct snd_timer_tread))) | |
1da177e4 | 2087 | err = -EFAULT; |
1da177e4 | 2088 | } else { |
4dff5c7b TI |
2089 | if (copy_to_user(buffer, &tu->queue[qhead], |
2090 | sizeof(struct snd_timer_read))) | |
1da177e4 | 2091 | err = -EFAULT; |
1da177e4 LT |
2092 | } |
2093 | ||
1da177e4 | 2094 | spin_lock_irq(&tu->qlock); |
4dff5c7b TI |
2095 | if (err < 0) |
2096 | goto _error; | |
2097 | result += unit; | |
2098 | buffer += unit; | |
1da177e4 | 2099 | } |
1da177e4 | 2100 | _error: |
4dff5c7b | 2101 | spin_unlock_irq(&tu->qlock); |
d11662f4 | 2102 | mutex_unlock(&tu->ioctl_lock); |
1da177e4 LT |
2103 | return result > 0 ? result : err; |
2104 | } | |
2105 | ||
680ef72a | 2106 | static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) |
1da177e4 | 2107 | { |
680ef72a | 2108 | __poll_t mask; |
53d2f744 | 2109 | struct snd_timer_user *tu; |
1da177e4 LT |
2110 | |
2111 | tu = file->private_data; | |
2112 | ||
2113 | poll_wait(file, &tu->qchange_sleep, wait); | |
6b172a85 | 2114 | |
1da177e4 | 2115 | mask = 0; |
d7f910bf | 2116 | spin_lock_irq(&tu->qlock); |
1da177e4 | 2117 | if (tu->qused) |
a9a08845 | 2118 | mask |= EPOLLIN | EPOLLRDNORM; |
230323da | 2119 | if (tu->disconnected) |
a9a08845 | 2120 | mask |= EPOLLERR; |
d7f910bf | 2121 | spin_unlock_irq(&tu->qlock); |
1da177e4 LT |
2122 | |
2123 | return mask; | |
2124 | } | |
2125 | ||
2126 | #ifdef CONFIG_COMPAT | |
2127 | #include "timer_compat.c" | |
2128 | #else | |
2129 | #define snd_timer_user_ioctl_compat NULL | |
2130 | #endif | |
2131 | ||
9c2e08c5 | 2132 | static const struct file_operations snd_timer_f_ops = |
1da177e4 LT |
2133 | { |
2134 | .owner = THIS_MODULE, | |
2135 | .read = snd_timer_user_read, | |
2136 | .open = snd_timer_user_open, | |
2137 | .release = snd_timer_user_release, | |
02f4865f | 2138 | .llseek = no_llseek, |
1da177e4 LT |
2139 | .poll = snd_timer_user_poll, |
2140 | .unlocked_ioctl = snd_timer_user_ioctl, | |
2141 | .compat_ioctl = snd_timer_user_ioctl_compat, | |
2142 | .fasync = snd_timer_user_fasync, | |
2143 | }; | |
2144 | ||
7c35860d TI |
2145 | /* unregister the system timer */ |
2146 | static void snd_timer_free_all(void) | |
2147 | { | |
2148 | struct snd_timer *timer, *n; | |
2149 | ||
2150 | list_for_each_entry_safe(timer, n, &snd_timer_list, device_list) | |
2151 | snd_timer_free(timer); | |
2152 | } | |
2153 | ||
89da061f TI |
2154 | static struct device timer_dev; |
2155 | ||
1da177e4 LT |
2156 | /* |
2157 | * ENTRY functions | |
2158 | */ | |
2159 | ||
1da177e4 LT |
2160 | static int __init alsa_timer_init(void) |
2161 | { | |
2162 | int err; | |
1da177e4 | 2163 | |
89da061f TI |
2164 | snd_device_initialize(&timer_dev, NULL); |
2165 | dev_set_name(&timer_dev, "timer"); | |
2166 | ||
1da177e4 | 2167 | #ifdef SNDRV_OSS_INFO_DEV_TIMERS |
6b172a85 CL |
2168 | snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, |
2169 | "system timer"); | |
1da177e4 | 2170 | #endif |
e28563cc | 2171 | |
7c35860d TI |
2172 | err = snd_timer_register_system(); |
2173 | if (err < 0) { | |
cf74dcf3 | 2174 | pr_err("ALSA: unable to register system timer (%i)\n", err); |
1ae0e4ce | 2175 | goto put_timer; |
7c35860d TI |
2176 | } |
2177 | ||
40a4b263 TI |
2178 | err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, |
2179 | &snd_timer_f_ops, NULL, &timer_dev); | |
7c35860d | 2180 | if (err < 0) { |
cf74dcf3 | 2181 | pr_err("ALSA: unable to register timer device (%i)\n", err); |
7c35860d | 2182 | snd_timer_free_all(); |
1ae0e4ce | 2183 | goto put_timer; |
7c35860d TI |
2184 | } |
2185 | ||
e28563cc | 2186 | snd_timer_proc_init(); |
1da177e4 | 2187 | return 0; |
1ae0e4ce ME |
2188 | |
2189 | put_timer: | |
2190 | put_device(&timer_dev); | |
2191 | return err; | |
1da177e4 LT |
2192 | } |
2193 | ||
2194 | static void __exit alsa_timer_exit(void) | |
2195 | { | |
40a4b263 | 2196 | snd_unregister_device(&timer_dev); |
7c35860d | 2197 | snd_timer_free_all(); |
89da061f | 2198 | put_device(&timer_dev); |
e28563cc | 2199 | snd_timer_proc_done(); |
1da177e4 LT |
2200 | #ifdef SNDRV_OSS_INFO_DEV_TIMERS |
2201 | snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); | |
2202 | #endif | |
2203 | } | |
2204 | ||
2205 | module_init(alsa_timer_init) | |
2206 | module_exit(alsa_timer_exit) |