Blame view

sound/core/timer.c 54.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  Timers abstract layer
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
3
   *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   *
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
  #include <linux/delay.h>
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  #include <linux/slab.h>
  #include <linux/time.h>
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
25
  #include <linux/mutex.h>
51990e825   Paul Gortmaker   device.h: cleanup...
26
  #include <linux/device.h>
65a772172   Paul Gortmaker   sound: fix driver...
27
  #include <linux/module.h>
543537bd9   Paulo Marques   [PATCH] create a ...
28
  #include <linux/string.h>
174cd4b1e   Ingo Molnar   sched/headers: Pr...
29
  #include <linux/sched/signal.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
34
35
36
  #include <sound/core.h>
  #include <sound/timer.h>
  #include <sound/control.h>
  #include <sound/info.h>
  #include <sound/minors.h>
  #include <sound/initval.h>
  #include <linux/kmod.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37

9f8a7658b   Takashi Iwai   ALSA: timer: Fix ...
38
39
  /* internal flags */
  #define SNDRV_TIMER_IFLG_PAUSED		0x00010000
8eeaa2f9e   Takashi Iwai   ALSA: Replace wit...
40
  #if IS_ENABLED(CONFIG_SND_HRTIMER)
109fef9ed   Clemens Ladisch   ALSA: timer: auto...
41
  #define DEFAULT_TIMER_LIMIT 4
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
  #else
  #define DEFAULT_TIMER_LIMIT 1
  #endif
  
  static int timer_limit = DEFAULT_TIMER_LIMIT;
b751eef1f   Jaroslav Kysela   [ALSA] Use posix ...
47
  static int timer_tstamp_monotonic = 1;
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
48
  MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
  MODULE_DESCRIPTION("ALSA timer interface");
  MODULE_LICENSE("GPL");
  module_param(timer_limit, int, 0444);
  MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
b751eef1f   Jaroslav Kysela   [ALSA] Use posix ...
53
54
  module_param(timer_tstamp_monotonic, int, 0444);
  MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55

03cfe6f57   Kay Sievers   ALSA: support mod...
56
57
  MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
  MODULE_ALIAS("devname:snd/timer");
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
58
59
  struct snd_timer_user {
  	struct snd_timer_instance *timeri;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
60
  	int tread;		/* enhanced read with timestamps and events */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
62
63
64
65
66
  	unsigned long ticks;
  	unsigned long overrun;
  	int qhead;
  	int qtail;
  	int qused;
  	int queue_size;
230323dac   Takashi Iwai   ALSA: timer: Hand...
67
  	bool disconnected;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
68
69
  	struct snd_timer_read *queue;
  	struct snd_timer_tread *tqueue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
  	spinlock_t qlock;
  	unsigned long last_resolution;
  	unsigned int filter;
  	struct timespec tstamp;		/* trigger tstamp */
  	wait_queue_head_t qchange_sleep;
  	struct fasync_struct *fasync;
af368027a   Takashi Iwai   ALSA: timer: Fix ...
76
  	struct mutex ioctl_lock;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
77
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
84
85
86
  
  /* list of timers */
  static LIST_HEAD(snd_timer_list);
  
  /* list of slave instances */
  static LIST_HEAD(snd_timer_slave_list);
  
  /* lock for slave active lists */
  static DEFINE_SPINLOCK(slave_active_lock);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
87
  static DEFINE_MUTEX(register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
89
90
91
  static int snd_timer_free(struct snd_timer *timer);
  static int snd_timer_dev_free(struct snd_device *device);
  static int snd_timer_dev_register(struct snd_device *device);
c461482c8   Takashi Iwai   [ALSA] Unregister...
92
  static int snd_timer_dev_disconnect(struct snd_device *device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
93

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
94
  static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
  
  /*
   * create a timer instance with the given owner string.
   * when timer is not NULL, increments the module counter
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
100
101
  static struct snd_timer_instance *snd_timer_instance_new(char *owner,
  							 struct snd_timer *timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
103
  	struct snd_timer_instance *timeri;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
104
  	timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
  	if (timeri == NULL)
  		return NULL;
543537bd9   Paulo Marques   [PATCH] create a ...
107
  	timeri->owner = kstrdup(owner, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
110
111
112
113
114
115
116
117
118
  	if (! timeri->owner) {
  		kfree(timeri);
  		return NULL;
  	}
  	INIT_LIST_HEAD(&timeri->open_list);
  	INIT_LIST_HEAD(&timeri->active_list);
  	INIT_LIST_HEAD(&timeri->ack_list);
  	INIT_LIST_HEAD(&timeri->slave_list_head);
  	INIT_LIST_HEAD(&timeri->slave_active_head);
  
  	timeri->timer = timer;
de24214d0   Clemens Ladisch   [ALSA] timers: ad...
119
  	if (timer && !try_module_get(timer->module)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
126
127
128
129
130
  		kfree(timeri->owner);
  		kfree(timeri);
  		return NULL;
  	}
  
  	return timeri;
  }
  
  /*
   * find a timer instance from the given timer id
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
131
  static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
133
  	struct snd_timer *timer = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

9244b2c30   Johannes Berg   [ALSA] alsa core:...
135
  	list_for_each_entry(timer, &snd_timer_list, device_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  		if (timer->tmr_class != tid->dev_class)
  			continue;
  		if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
  		     timer->tmr_class == SNDRV_TIMER_CLASS_PCM) &&
  		    (timer->card == NULL ||
  		     timer->card->number != tid->card))
  			continue;
  		if (timer->tmr_device != tid->device)
  			continue;
  		if (timer->tmr_subdevice != tid->subdevice)
  			continue;
  		return timer;
  	}
  	return NULL;
  }
ee2da9978   Johannes Berg   ALSA: remove CONF...
151
  #ifdef CONFIG_MODULES
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
153
  static void snd_timer_request(struct snd_timer_id *tid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  	switch (tid->dev_class) {
  	case SNDRV_TIMER_CLASS_GLOBAL:
  		if (tid->device < timer_limit)
  			request_module("snd-timer-%i", tid->device);
  		break;
  	case SNDRV_TIMER_CLASS_CARD:
  	case SNDRV_TIMER_CLASS_PCM:
  		if (tid->card < snd_ecards_limit)
  			request_module("snd-card-%i", tid->card);
  		break;
  	default:
  		break;
  	}
  }
  
  #endif
  
  /*
   * look for a master instance matching with the slave id of the given slave.
   * when found, relink the open_link of the slave.
   *
   * call this with register_mutex down.
   */
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
178
  static int snd_timer_check_slave(struct snd_timer_instance *slave)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
180
181
  	struct snd_timer *timer;
  	struct snd_timer_instance *master;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
  
  	/* FIXME: it's really dumb to look up all entries.. */
9244b2c30   Johannes Berg   [ALSA] alsa core:...
184
185
  	list_for_each_entry(timer, &snd_timer_list, device_list) {
  		list_for_each_entry(master, &timer->open_list_head, open_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
  			if (slave->slave_class == master->slave_class &&
  			    slave->slave_id == master->slave_id) {
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
188
189
190
  				if (master->timer->num_instances >=
  				    master->timer->max_instances)
  					return -EBUSY;
5b7c757d1   Nicolas Kaiser   ALSA: sound/core:...
191
192
  				list_move_tail(&slave->open_list,
  					       &master->slave_list_head);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
193
  				master->timer->num_instances++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
  				spin_lock_irq(&slave_active_lock);
  				slave->master = master;
  				slave->timer = master->timer;
  				spin_unlock_irq(&slave_active_lock);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
198
  				return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
  			}
  		}
  	}
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
202
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
207
208
209
210
  }
  
  /*
   * look for slave instances matching with the slave id of the given master.
   * when found, relink the open_link of slaves.
   *
   * call this with register_mutex down.
   */
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
211
  static int snd_timer_check_master(struct snd_timer_instance *master)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
212
  {
9244b2c30   Johannes Berg   [ALSA] alsa core:...
213
  	struct snd_timer_instance *slave, *tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  
  	/* check all pending slaves */
9244b2c30   Johannes Berg   [ALSA] alsa core:...
216
  	list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
  		if (slave->slave_class == master->slave_class &&
  		    slave->slave_id == master->slave_id) {
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
219
220
221
  			if (master->timer->num_instances >=
  			    master->timer->max_instances)
  				return -EBUSY;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
222
  			list_move_tail(&slave->open_list, &master->slave_list_head);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
223
  			master->timer->num_instances++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  			spin_lock_irq(&slave_active_lock);
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
225
  			spin_lock(&master->timer->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
  			slave->master = master;
  			slave->timer = master->timer;
  			if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
6b172a853   Clemens Ladisch   [ALSA] timer: for...
229
230
  				list_add_tail(&slave->active_list,
  					      &master->slave_active_head);
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
231
  			spin_unlock(&master->timer->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
  			spin_unlock_irq(&slave_active_lock);
  		}
  	}
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
235
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  }
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
237
  static int snd_timer_close_locked(struct snd_timer_instance *timeri);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
239
240
241
  /*
   * open a timer instance
   * when opening a master, the slave id must be here given.
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
242
243
  int snd_timer_open(struct snd_timer_instance **ti,
  		   char *owner, struct snd_timer_id *tid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
  		   unsigned int slave_id)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
246
247
  	struct snd_timer *timer;
  	struct snd_timer_instance *timeri = NULL;
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
248
  	int err;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
249

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
253
  	if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
  		/* open a slave instance */
  		if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
  		    tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
cf74dcf35   Takashi Iwai   ALSA: timer: Use ...
254
255
256
  			pr_debug("ALSA: timer: invalid slave class %i
  ",
  				 tid->dev_sclass);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
  			return -EINVAL;
  		}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
259
  		mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  		timeri = snd_timer_instance_new(owner, NULL);
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
261
  		if (!timeri) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
262
  			mutex_unlock(&register_mutex);
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
263
264
  			return -ENOMEM;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
  		timeri->slave_class = tid->dev_sclass;
  		timeri->slave_id = tid->device;
  		timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
  		list_add_tail(&timeri->open_list, &snd_timer_slave_list);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
269
270
271
272
273
  		err = snd_timer_check_slave(timeri);
  		if (err < 0) {
  			snd_timer_close_locked(timeri);
  			timeri = NULL;
  		}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
274
  		mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  		*ti = timeri;
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
276
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
279
  	}
  
  	/* open a master instance */
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
280
  	mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  	timer = snd_timer_find(tid);
ee2da9978   Johannes Berg   ALSA: remove CONF...
282
283
  #ifdef CONFIG_MODULES
  	if (!timer) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
284
  		mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  		snd_timer_request(tid);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
286
  		mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
  		timer = snd_timer_find(tid);
  	}
  #endif
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
290
  	if (!timer) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
291
  		mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
  		return -ENODEV;
  	}
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
294
295
  	if (!list_empty(&timer->open_list_head)) {
  		timeri = list_entry(timer->open_list_head.next,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
296
  				    struct snd_timer_instance, open_list);
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
297
  		if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
298
  			mutex_unlock(&register_mutex);
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
299
300
301
  			return -EBUSY;
  		}
  	}
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
302
303
304
305
  	if (timer->num_instances >= timer->max_instances) {
  		mutex_unlock(&register_mutex);
  		return -EBUSY;
  	}
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
306
307
  	timeri = snd_timer_instance_new(owner, timer);
  	if (!timeri) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
308
  		mutex_unlock(&register_mutex);
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
309
310
  		return -ENOMEM;
  	}
230323dac   Takashi Iwai   ALSA: timer: Hand...
311
312
313
  	/* take a card refcount for safe disconnection */
  	if (timer->card)
  		get_device(&timer->card->card_dev);
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
314
315
  	timeri->slave_class = tid->dev_sclass;
  	timeri->slave_id = slave_id;
8ddc05638   Vegard Nossum   ALSA: timer: fix ...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  
  	if (list_empty(&timer->open_list_head) && timer->hw.open) {
  		int err = timer->hw.open(timer);
  		if (err) {
  			kfree(timeri->owner);
  			kfree(timeri);
  
  			if (timer->card)
  				put_device(&timer->card->card_dev);
  			module_put(timer->module);
  			mutex_unlock(&register_mutex);
  			return err;
  		}
  	}
2fd43d115   Clemens Ladisch   [ALSA] timer: fix...
330
  	list_add_tail(&timeri->open_list, &timer->open_list_head);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
331
332
333
334
335
336
  	timer->num_instances++;
  	err = snd_timer_check_master(timeri);
  	if (err < 0) {
  		snd_timer_close_locked(timeri);
  		timeri = NULL;
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
337
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  	*ti = timeri;
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
339
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
341
  EXPORT_SYMBOL(snd_timer_open);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
  /*
   * close a timer instance
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
345
   * call this with register_mutex down.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
   */
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
347
  static int snd_timer_close_locked(struct snd_timer_instance *timeri)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
349
  	struct snd_timer *timer = NULL;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
350
  	struct snd_timer_instance *slave, *tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

9984d1b58   Takashi Iwai   ALSA: timer: Prot...
352
  	list_del(&timeri->open_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353
354
  	/* force to stop the timer */
  	snd_timer_stop(timeri);
9984d1b58   Takashi Iwai   ALSA: timer: Prot...
355
356
  	timer = timeri->timer;
  	if (timer) {
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
357
  		timer->num_instances--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
365
  		/* wait, until the active callback is finished */
  		spin_lock_irq(&timer->lock);
  		while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
  			spin_unlock_irq(&timer->lock);
  			udelay(10);
  			spin_lock_irq(&timer->lock);
  		}
  		spin_unlock_irq(&timer->lock);
9984d1b58   Takashi Iwai   ALSA: timer: Prot...
366

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  		/* remove slave links */
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
368
369
  		spin_lock_irq(&slave_active_lock);
  		spin_lock(&timer->lock);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
370
371
  		list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
  					 open_list) {
9244b2c30   Johannes Berg   [ALSA] alsa core:...
372
  			list_move_tail(&slave->open_list, &snd_timer_slave_list);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
373
  			timer->num_instances--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
  			slave->master = NULL;
  			slave->timer = NULL;
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
376
377
  			list_del_init(&slave->ack_list);
  			list_del_init(&slave->active_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
  		}
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
379
380
  		spin_unlock(&timer->lock);
  		spin_unlock_irq(&slave_active_lock);
9984d1b58   Takashi Iwai   ALSA: timer: Prot...
381
382
383
384
  
  		/* slave doesn't need to release timer resources below */
  		if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
  			timer = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385
  	}
9984d1b58   Takashi Iwai   ALSA: timer: Prot...
386

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
390
  	if (timeri->private_free)
  		timeri->private_free(timeri);
  	kfree(timeri->owner);
  	kfree(timeri);
9984d1b58   Takashi Iwai   ALSA: timer: Prot...
391
392
393
394
395
396
397
  
  	if (timer) {
  		if (list_empty(&timer->open_list_head) && timer->hw.close)
  			timer->hw.close(timer);
  		/* release a card refcount for safe disconnection */
  		if (timer->card)
  			put_device(&timer->card->card_dev);
de24214d0   Clemens Ladisch   [ALSA] timers: ad...
398
  		module_put(timer->module);
9984d1b58   Takashi Iwai   ALSA: timer: Prot...
399
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
401
  	return 0;
  }
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  
  /*
   * close a timer instance
   */
  int snd_timer_close(struct snd_timer_instance *timeri)
  {
  	int err;
  
  	if (snd_BUG_ON(!timeri))
  		return -ENXIO;
  
  	mutex_lock(&register_mutex);
  	err = snd_timer_close_locked(timeri);
  	mutex_unlock(&register_mutex);
  	return err;
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
418
  EXPORT_SYMBOL(snd_timer_close);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
420
  unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
421
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
422
  	struct snd_timer * timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
  
  	if (timeri == NULL)
  		return 0;
dd1f7ab8a   Markus Elfring   ALSA: timer: Adju...
426
427
  	timer = timeri->timer;
  	if (timer) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
429
430
431
432
433
  		if (timer->hw.c_resolution)
  			return timer->hw.c_resolution(timer);
  		return timer->hw.resolution;
  	}
  	return 0;
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
434
  EXPORT_SYMBOL(snd_timer_resolution);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
436
  static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
438
  	struct snd_timer *timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
  	unsigned long resolution = 0;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
440
  	struct snd_timer_instance *ts;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  	struct timespec tstamp;
b751eef1f   Jaroslav Kysela   [ALSA] Use posix ...
442
  	if (timer_tstamp_monotonic)
26204e048   Thomas Gleixner   ALSA: core: Use k...
443
  		ktime_get_ts(&tstamp);
b751eef1f   Jaroslav Kysela   [ALSA] Use posix ...
444
445
  	else
  		getnstimeofday(&tstamp);
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
446
447
448
  	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
  		       event > SNDRV_TIMER_EVENT_PAUSE))
  		return;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
449
450
  	if (event == SNDRV_TIMER_EVENT_START ||
  	    event == SNDRV_TIMER_EVENT_CONTINUE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
  		resolution = snd_timer_resolution(ti);
  	if (ti->ccallback)
b30477d5e   Jaroslav Kysela   ALSA: timer - pas...
453
  		ti->ccallback(ti, event, &tstamp, resolution);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
459
460
  	if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
  		return;
  	timer = ti->timer;
  	if (timer == NULL)
  		return;
  	if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
  		return;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
461
  	list_for_each_entry(ts, &ti->slave_active_head, active_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
  		if (ts->ccallback)
117159f0b   Takashi Iwai   ALSA: timer: Fix ...
463
  			ts->ccallback(ts, event + 100, &tstamp, resolution);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
  }
f65e0d299   Takashi Iwai   ALSA: timer: Call...
465
466
467
  /* start/continue a master timer */
  static int snd_timer_start1(struct snd_timer_instance *timeri,
  			    bool start, unsigned long ticks)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
  {
f65e0d299   Takashi Iwai   ALSA: timer: Call...
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  	struct snd_timer *timer;
  	int result;
  	unsigned long flags;
  
  	timer = timeri->timer;
  	if (!timer)
  		return -EINVAL;
  
  	spin_lock_irqsave(&timer->lock, flags);
  	if (timer->card && timer->card->shutdown) {
  		result = -ENODEV;
  		goto unlock;
  	}
  	if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
  			     SNDRV_TIMER_IFLG_START)) {
  		result = -EBUSY;
  		goto unlock;
  	}
  
  	if (start)
  		timeri->ticks = timeri->cticks = ticks;
  	else if (!timeri->cticks)
  		timeri->cticks = 1;
  	timeri->pticks = 0;
5b7c757d1   Nicolas Kaiser   ALSA: sound/core:...
493
  	list_move_tail(&timeri->active_list, &timer->active_list_head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
495
496
497
498
  	if (timer->running) {
  		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
  			goto __start_now;
  		timer->flags |= SNDRV_TIMER_FLG_RESCHED;
  		timeri->flags |= SNDRV_TIMER_IFLG_START;
f65e0d299   Takashi Iwai   ALSA: timer: Call...
499
  		result = 1; /* delayed start */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
  	} else {
f65e0d299   Takashi Iwai   ALSA: timer: Call...
501
502
  		if (start)
  			timer->sticks = ticks;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
504
505
506
  		timer->hw.start(timer);
  	      __start_now:
  		timer->running++;
  		timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
f65e0d299   Takashi Iwai   ALSA: timer: Call...
507
  		result = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  	}
f65e0d299   Takashi Iwai   ALSA: timer: Call...
509
510
511
512
513
  	snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
  			  SNDRV_TIMER_EVENT_CONTINUE);
   unlock:
  	spin_unlock_irqrestore(&timer->lock, flags);
  	return result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
  }
f65e0d299   Takashi Iwai   ALSA: timer: Call...
515
516
517
  /* start/continue a slave timer */
  static int snd_timer_start_slave(struct snd_timer_instance *timeri,
  				 bool start)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
521
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&slave_active_lock, flags);
f784beb75   Takashi Iwai   ALSA: timer: Fix ...
522
523
524
525
  	if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
  		spin_unlock_irqrestore(&slave_active_lock, flags);
  		return -EBUSY;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
  	timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
527
528
  	if (timeri->master && timeri->timer) {
  		spin_lock(&timeri->timer->lock);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
529
530
  		list_add_tail(&timeri->active_list,
  			      &timeri->master->slave_active_head);
f65e0d299   Takashi Iwai   ALSA: timer: Call...
531
532
  		snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
  				  SNDRV_TIMER_EVENT_CONTINUE);
b5a663aa4   Takashi Iwai   ALSA: timer: Hard...
533
534
  		spin_unlock(&timeri->timer->lock);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
  	spin_unlock_irqrestore(&slave_active_lock, flags);
  	return 1; /* delayed start */
  }
f65e0d299   Takashi Iwai   ALSA: timer: Call...
538
539
  /* stop/pause a master timer */
  static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
540
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
541
  	struct snd_timer *timer;
f65e0d299   Takashi Iwai   ALSA: timer: Call...
542
  	int result = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
546
547
  	timer = timeri->timer;
  	if (!timer)
  		return -EINVAL;
  	spin_lock_irqsave(&timer->lock, flags);
f784beb75   Takashi Iwai   ALSA: timer: Fix ...
548
549
  	if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
  			       SNDRV_TIMER_IFLG_START))) {
f65e0d299   Takashi Iwai   ALSA: timer: Call...
550
551
  		result = -EBUSY;
  		goto unlock;
f784beb75   Takashi Iwai   ALSA: timer: Fix ...
552
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
  	list_del_init(&timeri->ack_list);
  	list_del_init(&timeri->active_list);
f65e0d299   Takashi Iwai   ALSA: timer: Call...
555
556
557
558
559
  	if (timer->card && timer->card->shutdown)
  		goto unlock;
  	if (stop) {
  		timeri->cticks = timeri->ticks;
  		timeri->pticks = 0;
230323dac   Takashi Iwai   ALSA: timer: Hand...
560
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
563
564
565
566
567
568
569
570
571
572
  	if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
  	    !(--timer->running)) {
  		timer->hw.stop(timer);
  		if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
  			timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
  			snd_timer_reschedule(timer, 0);
  			if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
  				timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
  				timer->hw.start(timer);
  			}
  		}
  	}
c3b168137   Takashi Iwai   ALSA: timer: Code...
573
  	timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
9f8a7658b   Takashi Iwai   ALSA: timer: Fix ...
574
575
576
577
  	if (stop)
  		timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
  	else
  		timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
f65e0d299   Takashi Iwai   ALSA: timer: Call...
578
  	snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
ba3fbb7af   Ben Hutchings   ALSA: timer: Fix ...
579
  			  SNDRV_TIMER_EVENT_PAUSE);
f65e0d299   Takashi Iwai   ALSA: timer: Call...
580
   unlock:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  	spin_unlock_irqrestore(&timer->lock, flags);
f65e0d299   Takashi Iwai   ALSA: timer: Call...
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  	return result;
  }
  
  /* stop/pause a slave timer */
  static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&slave_active_lock, flags);
  	if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
  		spin_unlock_irqrestore(&slave_active_lock, flags);
  		return -EBUSY;
  	}
  	timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
  	if (timeri->timer) {
  		spin_lock(&timeri->timer->lock);
  		list_del_init(&timeri->ack_list);
  		list_del_init(&timeri->active_list);
  		snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
ba3fbb7af   Ben Hutchings   ALSA: timer: Fix ...
601
  				  SNDRV_TIMER_EVENT_PAUSE);
f65e0d299   Takashi Iwai   ALSA: timer: Call...
602
603
604
  		spin_unlock(&timeri->timer->lock);
  	}
  	spin_unlock_irqrestore(&slave_active_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605
606
607
608
  	return 0;
  }
  
  /*
f65e0d299   Takashi Iwai   ALSA: timer: Call...
609
610
611
612
613
614
615
616
617
618
619
   *  start the timer instance
   */
  int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
  {
  	if (timeri == NULL || ticks < 1)
  		return -EINVAL;
  	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
  		return snd_timer_start_slave(timeri, true);
  	else
  		return snd_timer_start1(timeri, true, ticks);
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
620
  EXPORT_SYMBOL(snd_timer_start);
f65e0d299   Takashi Iwai   ALSA: timer: Call...
621
622
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
625
626
   * stop the timer instance.
   *
   * do not call this from the timer callback!
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
627
  int snd_timer_stop(struct snd_timer_instance *timeri)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
628
  {
f65e0d299   Takashi Iwai   ALSA: timer: Call...
629
630
631
632
  	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
  		return snd_timer_stop_slave(timeri, true);
  	else
  		return snd_timer_stop1(timeri, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
633
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
634
  EXPORT_SYMBOL(snd_timer_stop);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
636
637
638
  
  /*
   * start again..  the tick is kept.
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
639
  int snd_timer_continue(struct snd_timer_instance *timeri)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
  {
9f8a7658b   Takashi Iwai   ALSA: timer: Fix ...
641
642
643
  	/* timer can continue only after pause */
  	if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644
  	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
f65e0d299   Takashi Iwai   ALSA: timer: Call...
645
646
647
  		return snd_timer_start_slave(timeri, false);
  	else
  		return snd_timer_start1(timeri, false, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
648
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
649
  EXPORT_SYMBOL(snd_timer_continue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
652
653
  
  /*
   * pause.. remember the ticks left
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
654
  int snd_timer_pause(struct snd_timer_instance * timeri)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
  {
f65e0d299   Takashi Iwai   ALSA: timer: Call...
656
657
658
659
  	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
  		return snd_timer_stop_slave(timeri, false);
  	else
  		return snd_timer_stop1(timeri, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
661
  EXPORT_SYMBOL(snd_timer_pause);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
664
665
666
667
668
  
  /*
   * reschedule the timer
   *
   * start pending instances and check the scheduling ticks.
   * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
669
  static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
671
  	struct snd_timer_instance *ti;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
  	unsigned long ticks = ~0UL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673

9244b2c30   Johannes Berg   [ALSA] alsa core:...
674
  	list_for_each_entry(ti, &timer->active_list_head, active_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
  		if (ti->flags & SNDRV_TIMER_IFLG_START) {
  			ti->flags &= ~SNDRV_TIMER_IFLG_START;
  			ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
  			timer->running++;
  		}
  		if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
  			if (ticks > ti->cticks)
  				ticks = ti->cticks;
  		}
  	}
  	if (ticks == ~0UL) {
  		timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
  		return;
  	}
  	if (ticks > timer->hw.ticks)
  		ticks = timer->hw.ticks;
  	if (ticks_left != ticks)
  		timer->flags |= SNDRV_TIMER_FLG_CHANGE;
  	timer->sticks = ticks;
  }
  
  /*
   * timer tasklet
   *
   */
  static void snd_timer_tasklet(unsigned long arg)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
702
703
  	struct snd_timer *timer = (struct snd_timer *) arg;
  	struct snd_timer_instance *ti;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
  	struct list_head *p;
  	unsigned long resolution, ticks;
2999ff5ba   Takashi Iwai   [ALSA] Fix a dead...
706
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
707

230323dac   Takashi Iwai   ALSA: timer: Hand...
708
709
  	if (timer->card && timer->card->shutdown)
  		return;
2999ff5ba   Takashi Iwai   [ALSA] Fix a dead...
710
  	spin_lock_irqsave(&timer->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
  	/* now process all callbacks */
  	while (!list_empty(&timer->sack_list_head)) {
  		p = timer->sack_list_head.next;		/* get first item */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
714
  		ti = list_entry(p, struct snd_timer_instance, ack_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
717
  
  		/* remove from ack_list and make empty */
  		list_del_init(p);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
718

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
720
721
722
723
724
725
726
727
728
729
  		ticks = ti->pticks;
  		ti->pticks = 0;
  		resolution = ti->resolution;
  
  		ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
  		spin_unlock(&timer->lock);
  		if (ti->callback)
  			ti->callback(ti, resolution, ticks);
  		spin_lock(&timer->lock);
  		ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
  	}
2999ff5ba   Takashi Iwai   [ALSA] Fix a dead...
730
  	spin_unlock_irqrestore(&timer->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
731
732
733
734
735
736
737
738
  }
  
  /*
   * timer interrupt
   *
   * ticks_left is usually equal to timer->sticks.
   *
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
739
  void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
740
  {
9244b2c30   Johannes Berg   [ALSA] alsa core:...
741
  	struct snd_timer_instance *ti, *ts, *tmp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
  	unsigned long resolution, ticks;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
743
  	struct list_head *p, *ack_list_head;
b32425ac9   Takashi Iwai   [ALSA] Fix possib...
744
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
745
746
747
748
  	int use_tasklet = 0;
  
  	if (timer == NULL)
  		return;
230323dac   Takashi Iwai   ALSA: timer: Hand...
749
750
  	if (timer->card && timer->card->shutdown)
  		return;
b32425ac9   Takashi Iwai   [ALSA] Fix possib...
751
  	spin_lock_irqsave(&timer->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
758
759
  
  	/* remember the current resolution */
  	if (timer->hw.c_resolution)
  		resolution = timer->hw.c_resolution(timer);
  	else
  		resolution = timer->hw.resolution;
  
  	/* loop for all active instances
9244b2c30   Johannes Berg   [ALSA] alsa core:...
760
  	 * Here we cannot use list_for_each_entry because the active_list of a
6b172a853   Clemens Ladisch   [ALSA] timer: for...
761
762
  	 * processed instance is relinked to done_list_head before the callback
  	 * is called.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
  	 */
9244b2c30   Johannes Berg   [ALSA] alsa core:...
764
765
  	list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
  				 active_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
766
767
768
769
770
771
772
773
774
775
776
777
778
779
  		if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
  			continue;
  		ti->pticks += ticks_left;
  		ti->resolution = resolution;
  		if (ti->cticks < ticks_left)
  			ti->cticks = 0;
  		else
  			ti->cticks -= ticks_left;
  		if (ti->cticks) /* not expired */
  			continue;
  		if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
  			ti->cticks = ti->ticks;
  		} else {
  			ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
094fd3be8   Takashi Iwai   ALSA: timer: Fix ...
780
781
  			--timer->running;
  			list_del_init(&ti->active_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
  		}
6b172a853   Clemens Ladisch   [ALSA] timer: for...
783
784
785
786
787
788
789
  		if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
  		    (ti->flags & SNDRV_TIMER_IFLG_FAST))
  			ack_list_head = &timer->ack_list_head;
  		else
  			ack_list_head = &timer->sack_list_head;
  		if (list_empty(&ti->ack_list))
  			list_add_tail(&ti->ack_list, ack_list_head);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
790
  		list_for_each_entry(ts, &ti->slave_active_head, active_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
  			ts->pticks = ti->pticks;
  			ts->resolution = resolution;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
793
794
  			if (list_empty(&ts->ack_list))
  				list_add_tail(&ts->ack_list, ack_list_head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
796
797
  		}
  	}
  	if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
cd93fe477   Clemens Ladisch   [ALSA] timer: fix...
798
  		snd_timer_reschedule(timer, timer->sticks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
  	if (timer->running) {
  		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
  			timer->hw.stop(timer);
  			timer->flags |= SNDRV_TIMER_FLG_CHANGE;
  		}
  		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
  		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
  			/* restart timer */
  			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
  			timer->hw.start(timer);
  		}
  	} else {
  		timer->hw.stop(timer);
  	}
  
  	/* now process all fast callbacks */
  	while (!list_empty(&timer->ack_list_head)) {
  		p = timer->ack_list_head.next;		/* get first item */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
817
  		ti = list_entry(p, struct snd_timer_instance, ack_list);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
818

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
819
820
  		/* remove from ack_list and make empty */
  		list_del_init(p);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
821

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
825
826
827
828
829
830
831
832
833
834
  		ticks = ti->pticks;
  		ti->pticks = 0;
  
  		ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
  		spin_unlock(&timer->lock);
  		if (ti->callback)
  			ti->callback(ti, resolution, ticks);
  		spin_lock(&timer->lock);
  		ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
  	}
  
  	/* do we have any slow callbacks? */
  	use_tasklet = !list_empty(&timer->sack_list_head);
b32425ac9   Takashi Iwai   [ALSA] Fix possib...
835
  	spin_unlock_irqrestore(&timer->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
836
837
  
  	if (use_tasklet)
1f04128a3   Takashi Iwai   ALSA: hda - Conve...
838
  		tasklet_schedule(&timer->task_queue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
840
  EXPORT_SYMBOL(snd_timer_interrupt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
843
844
  
  /*
  
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
845
846
  int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
  		  struct snd_timer **rtimer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
848
  	struct snd_timer *timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
  	int err;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
850
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
  		.dev_free = snd_timer_dev_free,
  		.dev_register = snd_timer_dev_register,
c461482c8   Takashi Iwai   [ALSA] Unregister...
853
  		.dev_disconnect = snd_timer_dev_disconnect,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
854
  	};
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
855
856
857
858
  	if (snd_BUG_ON(!tid))
  		return -EINVAL;
  	if (rtimer)
  		*rtimer = NULL;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
859
  	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
ec0e9937a   Takashi Iwai   ALSA: core: Drop ...
860
  	if (!timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
862
863
864
865
866
867
  		return -ENOMEM;
  	timer->tmr_class = tid->dev_class;
  	timer->card = card;
  	timer->tmr_device = tid->device;
  	timer->tmr_subdevice = tid->subdevice;
  	if (id)
  		strlcpy(timer->id, id, sizeof(timer->id));
6b760bb2c   Vegard Nossum   ALSA: timer: fix ...
868
  	timer->sticks = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
872
873
874
  	INIT_LIST_HEAD(&timer->device_list);
  	INIT_LIST_HEAD(&timer->open_list_head);
  	INIT_LIST_HEAD(&timer->active_list_head);
  	INIT_LIST_HEAD(&timer->ack_list_head);
  	INIT_LIST_HEAD(&timer->sack_list_head);
  	spin_lock_init(&timer->lock);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
875
876
  	tasklet_init(&timer->task_queue, snd_timer_tasklet,
  		     (unsigned long)timer);
9b7d869ee   Takashi Iwai   ALSA: timer: Limi...
877
  	timer->max_instances = 1000; /* default limit per timer */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
  	if (card != NULL) {
de24214d0   Clemens Ladisch   [ALSA] timers: ad...
879
  		timer->module = card->module;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
880
881
  		err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
  		if (err < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
883
884
885
  			snd_timer_free(timer);
  			return err;
  		}
  	}
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
886
887
  	if (rtimer)
  		*rtimer = timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
  	return 0;
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
890
  EXPORT_SYMBOL(snd_timer_new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
892
  static int snd_timer_free(struct snd_timer *timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
894
895
  	if (!timer)
  		return 0;
c461482c8   Takashi Iwai   [ALSA] Unregister...
896
897
898
899
900
  
  	mutex_lock(&register_mutex);
  	if (! list_empty(&timer->open_list_head)) {
  		struct list_head *p, *n;
  		struct snd_timer_instance *ti;
cf74dcf35   Takashi Iwai   ALSA: timer: Use ...
901
902
  		pr_warn("ALSA: timer %p is busy?
  ", timer);
c461482c8   Takashi Iwai   [ALSA] Unregister...
903
904
905
906
907
908
909
910
  		list_for_each_safe(p, n, &timer->open_list_head) {
  			list_del_init(p);
  			ti = list_entry(p, struct snd_timer_instance, open_list);
  			ti->timer = NULL;
  		}
  	}
  	list_del(&timer->device_list);
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
911
912
913
914
915
  	if (timer->private_free)
  		timer->private_free(timer);
  	kfree(timer);
  	return 0;
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
916
  static int snd_timer_dev_free(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
918
  	struct snd_timer *timer = device->device_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
920
  	return snd_timer_free(timer);
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
921
  static int snd_timer_dev_register(struct snd_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
923
924
  	struct snd_timer *timer = dev->device_data;
  	struct snd_timer *timer1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
926
927
  	if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
  	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
  	    !timer->hw.resolution && timer->hw.c_resolution == NULL)
  	    	return -EINVAL;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
931
  	mutex_lock(&register_mutex);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
932
  	list_for_each_entry(timer1, &snd_timer_list, device_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
  		if (timer1->tmr_class > timer->tmr_class)
  			break;
  		if (timer1->tmr_class < timer->tmr_class)
  			continue;
  		if (timer1->card && timer->card) {
  			if (timer1->card->number > timer->card->number)
  				break;
  			if (timer1->card->number < timer->card->number)
  				continue;
  		}
  		if (timer1->tmr_device > timer->tmr_device)
  			break;
  		if (timer1->tmr_device < timer->tmr_device)
  			continue;
  		if (timer1->tmr_subdevice > timer->tmr_subdevice)
  			break;
  		if (timer1->tmr_subdevice < timer->tmr_subdevice)
  			continue;
  		/* conflicts.. */
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
952
  		mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953
954
  		return -EBUSY;
  	}
9244b2c30   Johannes Berg   [ALSA] alsa core:...
955
  	list_add_tail(&timer->device_list, &timer1->device_list);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
956
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
957
958
  	return 0;
  }
c461482c8   Takashi Iwai   [ALSA] Unregister...
959
  static int snd_timer_dev_disconnect(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
  {
c461482c8   Takashi Iwai   [ALSA] Unregister...
961
  	struct snd_timer *timer = device->device_data;
230323dac   Takashi Iwai   ALSA: timer: Hand...
962
  	struct snd_timer_instance *ti;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
963
  	mutex_lock(&register_mutex);
c461482c8   Takashi Iwai   [ALSA] Unregister...
964
  	list_del_init(&timer->device_list);
230323dac   Takashi Iwai   ALSA: timer: Hand...
965
966
  	/* wake up pending sleepers */
  	list_for_each_entry(ti, &timer->open_list_head, open_list) {
40ed9444c   Takashi Iwai   ALSA: timer: Intr...
967
968
  		if (ti->disconnect)
  			ti->disconnect(ti);
230323dac   Takashi Iwai   ALSA: timer: Hand...
969
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
970
  	mutex_unlock(&register_mutex);
c461482c8   Takashi Iwai   [ALSA] Unregister...
971
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
972
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
973
  void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
975
976
  {
  	unsigned long flags;
  	unsigned long resolution = 0;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
977
  	struct snd_timer_instance *ti, *ts;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
978

230323dac   Takashi Iwai   ALSA: timer: Hand...
979
980
  	if (timer->card && timer->card->shutdown)
  		return;
7c22f1aaa   Takashi Iwai   [ALSA] Remove snd...
981
982
  	if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
  		return;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
983
984
985
  	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
  		       event > SNDRV_TIMER_EVENT_MRESUME))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
  	spin_lock_irqsave(&timer->lock, flags);
a501dfa3a   Jaroslav Kysela   [ALSA] Timer API ...
987
988
989
  	if (event == SNDRV_TIMER_EVENT_MSTART ||
  	    event == SNDRV_TIMER_EVENT_MCONTINUE ||
  	    event == SNDRV_TIMER_EVENT_MRESUME) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990
991
992
993
994
  		if (timer->hw.c_resolution)
  			resolution = timer->hw.c_resolution(timer);
  		else
  			resolution = timer->hw.resolution;
  	}
9244b2c30   Johannes Berg   [ALSA] alsa core:...
995
  	list_for_each_entry(ti, &timer->active_list_head, active_list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
996
997
  		if (ti->ccallback)
  			ti->ccallback(ti, event, tstamp, resolution);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
998
  		list_for_each_entry(ts, &ti->slave_active_head, active_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
1000
  			if (ts->ccallback)
  				ts->ccallback(ts, event, tstamp, resolution);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1001
1002
1003
  	}
  	spin_unlock_irqrestore(&timer->lock, flags);
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
1004
  EXPORT_SYMBOL(snd_timer_notify);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
1006
1007
1008
  
  /*
   * exported functions for global timers
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1009
  int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1010
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1011
  	struct snd_timer_id tid;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1012

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1013
1014
1015
1016
1017
1018
1019
  	tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
  	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
  	tid.card = -1;
  	tid.device = device;
  	tid.subdevice = 0;
  	return snd_timer_new(NULL, id, &tid, rtimer);
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
1020
  EXPORT_SYMBOL(snd_timer_global_new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1022
  int snd_timer_global_free(struct snd_timer *timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
1025
  {
  	return snd_timer_free(timer);
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
1026
  EXPORT_SYMBOL(snd_timer_global_free);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1027

53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1028
  int snd_timer_global_register(struct snd_timer *timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1030
  	struct snd_device dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
1034
1035
  
  	memset(&dev, 0, sizeof(dev));
  	dev.device_data = timer;
  	return snd_timer_dev_register(&dev);
  }
988563929   Takashi Iwai   ALSA: timer: Foll...
1036
  EXPORT_SYMBOL(snd_timer_global_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1037

6b172a853   Clemens Ladisch   [ALSA] timer: for...
1038
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
1040
1041
1042
1043
   *  System timer
   */
  
  struct snd_timer_system_private {
  	struct timer_list tlist;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1044
1045
1046
1047
  	unsigned long last_expires;
  	unsigned long last_jiffies;
  	unsigned long correction;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
1049
  static void snd_timer_s_function(unsigned long data)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1050
  	struct snd_timer *timer = (struct snd_timer *)data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
  	struct snd_timer_system_private *priv = timer->private_data;
  	unsigned long jiff = jiffies;
  	if (time_after(jiff, priv->last_expires))
6ed5eff02   Clemens Ladisch   [ALSA] system tim...
1054
  		priv->correction += (long)jiff - (long)priv->last_expires;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1055
1056
  	snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1057
  static int snd_timer_s_start(struct snd_timer * timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
  {
  	struct snd_timer_system_private *priv;
  	unsigned long njiff;
  
  	priv = (struct snd_timer_system_private *) timer->private_data;
  	njiff = (priv->last_jiffies = jiffies);
  	if (priv->correction > timer->sticks - 1) {
  		priv->correction -= timer->sticks - 1;
  		njiff++;
  	} else {
  		njiff += timer->sticks - priv->correction;
17f48ec3f   Clemens Ladisch   [ALSA] system tim...
1069
  		priv->correction = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
  	}
4a07083ed   Takashi Iwai   ALSA: timer: Use ...
1071
1072
  	priv->last_expires = njiff;
  	mod_timer(&priv->tlist, njiff);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1073
1074
  	return 0;
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1075
  static int snd_timer_s_stop(struct snd_timer * timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
  {
  	struct snd_timer_system_private *priv;
  	unsigned long jiff;
  
  	priv = (struct snd_timer_system_private *) timer->private_data;
  	del_timer(&priv->tlist);
  	jiff = jiffies;
  	if (time_before(jiff, priv->last_expires))
  		timer->sticks = priv->last_expires - jiff;
  	else
  		timer->sticks = 1;
de2696d8b   Clemens Ladisch   [ALSA] system tim...
1087
  	priv->correction = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1088
1089
  	return 0;
  }
f146357f0   Takashi Iwai   ALSA: timer: Sync...
1090
1091
1092
1093
1094
1095
1096
1097
  static int snd_timer_s_close(struct snd_timer *timer)
  {
  	struct snd_timer_system_private *priv;
  
  	priv = (struct snd_timer_system_private *)timer->private_data;
  	del_timer_sync(&priv->tlist);
  	return 0;
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1098
  static struct snd_timer_hardware snd_timer_system =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1099
1100
1101
1102
  {
  	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
  	.resolution =	1000000000L / HZ,
  	.ticks =	10000000L,
f146357f0   Takashi Iwai   ALSA: timer: Sync...
1103
  	.close =	snd_timer_s_close,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1104
1105
1106
  	.start =	snd_timer_s_start,
  	.stop =		snd_timer_s_stop
  };
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1107
  static void snd_timer_free_system(struct snd_timer *timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1108
1109
1110
1111
1112
1113
  {
  	kfree(timer->private_data);
  }
  
  static int snd_timer_register_system(void)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1114
  	struct snd_timer *timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1115
1116
  	struct snd_timer_system_private *priv;
  	int err;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1117
1118
  	err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer);
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119
1120
1121
  		return err;
  	strcpy(timer->name, "system timer");
  	timer->hw = snd_timer_system;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
1122
  	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1123
1124
1125
1126
  	if (priv == NULL) {
  		snd_timer_free(timer);
  		return -ENOMEM;
  	}
f169c1059   Takashi Iwai   ALSA: timer: Use ...
1127
  	setup_timer(&priv->tlist, snd_timer_s_function, (unsigned long) timer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1128
1129
1130
1131
  	timer->private_data = priv;
  	timer->private_free = snd_timer_free_system;
  	return snd_timer_global_register(timer);
  }
cd6a65036   Jie Yang   ALSA: replace CON...
1132
  #ifdef CONFIG_SND_PROC_FS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1133
1134
1135
  /*
   *  Info interface
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1136
1137
  static void snd_timer_proc_read(struct snd_info_entry *entry,
  				struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1139
1140
  	struct snd_timer *timer;
  	struct snd_timer_instance *ti;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141

1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1142
  	mutex_lock(&register_mutex);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1143
  	list_for_each_entry(timer, &snd_timer_list, device_list) {
230323dac   Takashi Iwai   ALSA: timer: Hand...
1144
1145
  		if (timer->card && timer->card->shutdown)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1146
1147
1148
1149
1150
  		switch (timer->tmr_class) {
  		case SNDRV_TIMER_CLASS_GLOBAL:
  			snd_iprintf(buffer, "G%i: ", timer->tmr_device);
  			break;
  		case SNDRV_TIMER_CLASS_CARD:
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1151
1152
  			snd_iprintf(buffer, "C%i-%i: ",
  				    timer->card->number, timer->tmr_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1153
1154
  			break;
  		case SNDRV_TIMER_CLASS_PCM:
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1155
1156
  			snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number,
  				    timer->tmr_device, timer->tmr_subdevice);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1157
1158
  			break;
  		default:
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1159
1160
1161
  			snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class,
  				    timer->card ? timer->card->number : -1,
  				    timer->tmr_device, timer->tmr_subdevice);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1162
1163
1164
  		}
  		snd_iprintf(buffer, "%s :", timer->name);
  		if (timer->hw.resolution)
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1165
1166
1167
1168
  			snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
  				    timer->hw.resolution / 1000,
  				    timer->hw.resolution % 1000,
  				    timer->hw.ticks);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1169
1170
1171
1172
  		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
  			snd_iprintf(buffer, " SLAVE");
  		snd_iprintf(buffer, "
  ");
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1173
  		list_for_each_entry(ti, &timer->open_list_head, open_list)
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1174
1175
1176
1177
1178
1179
  			snd_iprintf(buffer, "  Client %s : %s
  ",
  				    ti->owner ? ti->owner : "unknown",
  				    ti->flags & (SNDRV_TIMER_IFLG_START |
  						 SNDRV_TIMER_IFLG_RUNNING)
  				    ? "running" : "stopped");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1181
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1182
  }
6581f4e74   Takashi Iwai   [ALSA] Remove zer...
1183
  static struct snd_info_entry *snd_timer_proc_entry;
e28563cce   Takashi Iwai   [ALSA] Optimize f...
1184
1185
1186
1187
1188
1189
1190
  
  static void __init snd_timer_proc_init(void)
  {
  	struct snd_info_entry *entry;
  
  	entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
  	if (entry != NULL) {
e28563cce   Takashi Iwai   [ALSA] Optimize f...
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
  		entry->c.text.read = snd_timer_proc_read;
  		if (snd_info_register(entry) < 0) {
  			snd_info_free_entry(entry);
  			entry = NULL;
  		}
  	}
  	snd_timer_proc_entry = entry;
  }
  
  static void __exit snd_timer_proc_done(void)
  {
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
1202
  	snd_info_free_entry(snd_timer_proc_entry);
e28563cce   Takashi Iwai   [ALSA] Optimize f...
1203
  }
cd6a65036   Jie Yang   ALSA: replace CON...
1204
  #else /* !CONFIG_SND_PROC_FS */
e28563cce   Takashi Iwai   [ALSA] Optimize f...
1205
1206
1207
  #define snd_timer_proc_init()
  #define snd_timer_proc_done()
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1208
1209
1210
  /*
   *  USER SPACE interface
   */
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1211
  static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1212
1213
1214
  				     unsigned long resolution,
  				     unsigned long ticks)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1215
1216
  	struct snd_timer_user *tu = timeri->callback_data;
  	struct snd_timer_read *r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1217
  	int prev;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1218

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
  	spin_lock(&tu->qlock);
  	if (tu->qused > 0) {
  		prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
  		r = &tu->queue[prev];
  		if (r->resolution == resolution) {
  			r->ticks += ticks;
  			goto __wake;
  		}
  	}
  	if (tu->qused >= tu->queue_size) {
  		tu->overrun++;
  	} else {
  		r = &tu->queue[tu->qtail++];
  		tu->qtail %= tu->queue_size;
  		r->resolution = resolution;
  		r->ticks = ticks;
  		tu->qused++;
  	}
        __wake:
  	spin_unlock(&tu->qlock);
  	kill_fasync(&tu->fasync, SIGIO, POLL_IN);
  	wake_up(&tu->qchange_sleep);
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1242
1243
  static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
  					    struct snd_timer_tread *tread)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1244
1245
1246
1247
1248
1249
1250
1251
1252
  {
  	if (tu->qused >= tu->queue_size) {
  		tu->overrun++;
  	} else {
  		memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread));
  		tu->qtail %= tu->queue_size;
  		tu->qused++;
  	}
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1253
1254
  static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
  				     int event,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1255
1256
1257
  				     struct timespec *tstamp,
  				     unsigned long resolution)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1258
1259
  	struct snd_timer_user *tu = timeri->callback_data;
  	struct snd_timer_tread r1;
bfe70783c   Dan Carpenter   ALSA: take tu->ql...
1260
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1261

6b172a853   Clemens Ladisch   [ALSA] timer: for...
1262
1263
  	if (event >= SNDRV_TIMER_EVENT_START &&
  	    event <= SNDRV_TIMER_EVENT_PAUSE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1264
1265
1266
  		tu->tstamp = *tstamp;
  	if ((tu->filter & (1 << event)) == 0 || !tu->tread)
  		return;
9a47e9cff   Kangjie Lu   ALSA: timer: Fix ...
1267
  	memset(&r1, 0, sizeof(r1));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1268
1269
1270
  	r1.event = event;
  	r1.tstamp = *tstamp;
  	r1.val = resolution;
bfe70783c   Dan Carpenter   ALSA: take tu->ql...
1271
  	spin_lock_irqsave(&tu->qlock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1272
  	snd_timer_user_append_to_tqueue(tu, &r1);
bfe70783c   Dan Carpenter   ALSA: take tu->ql...
1273
  	spin_unlock_irqrestore(&tu->qlock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1274
1275
1276
  	kill_fasync(&tu->fasync, SIGIO, POLL_IN);
  	wake_up(&tu->qchange_sleep);
  }
40ed9444c   Takashi Iwai   ALSA: timer: Intr...
1277
1278
1279
1280
1281
1282
1283
  static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
  {
  	struct snd_timer_user *tu = timeri->callback_data;
  
  	tu->disconnected = true;
  	wake_up(&tu->qchange_sleep);
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1284
  static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
1286
1287
  				      unsigned long resolution,
  				      unsigned long ticks)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1288
1289
  	struct snd_timer_user *tu = timeri->callback_data;
  	struct snd_timer_tread *r, r1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1290
1291
  	struct timespec tstamp;
  	int prev, append = 0;
a8c006aaf   Dan Carpenter   ALSA: timer: Info...
1292
  	memset(&r1, 0, sizeof(r1));
07799e756   Takashi Iwai   [ALSA] Use getnst...
1293
  	memset(&tstamp, 0, sizeof(tstamp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
  	spin_lock(&tu->qlock);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1295
1296
  	if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
  			   (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1297
1298
1299
  		spin_unlock(&tu->qlock);
  		return;
  	}
b751eef1f   Jaroslav Kysela   [ALSA] Use posix ...
1300
1301
  	if (tu->last_resolution != resolution || ticks > 0) {
  		if (timer_tstamp_monotonic)
26204e048   Thomas Gleixner   ALSA: core: Use k...
1302
  			ktime_get_ts(&tstamp);
b751eef1f   Jaroslav Kysela   [ALSA] Use posix ...
1303
1304
1305
  		else
  			getnstimeofday(&tstamp);
  	}
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1306
1307
  	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
  	    tu->last_resolution != resolution) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
  		r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
  		r1.tstamp = tstamp;
  		r1.val = resolution;
  		snd_timer_user_append_to_tqueue(tu, &r1);
  		tu->last_resolution = resolution;
  		append++;
  	}
  	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
  		goto __wake;
  	if (ticks == 0)
  		goto __wake;
  	if (tu->qused > 0) {
  		prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
  		r = &tu->tqueue[prev];
  		if (r->event == SNDRV_TIMER_EVENT_TICK) {
  			r->tstamp = tstamp;
  			r->val += ticks;
  			append++;
  			goto __wake;
  		}
  	}
  	r1.event = SNDRV_TIMER_EVENT_TICK;
  	r1.tstamp = tstamp;
  	r1.val = ticks;
  	snd_timer_user_append_to_tqueue(tu, &r1);
  	append++;
        __wake:
  	spin_unlock(&tu->qlock);
  	if (append == 0)
  		return;
  	kill_fasync(&tu->fasync, SIGIO, POLL_IN);
  	wake_up(&tu->qchange_sleep);
  }
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
  static int realloc_user_queue(struct snd_timer_user *tu, int size)
  {
  	struct snd_timer_read *queue = NULL;
  	struct snd_timer_tread *tqueue = NULL;
  
  	if (tu->tread) {
  		tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
  		if (!tqueue)
  			return -ENOMEM;
  	} else {
  		queue = kcalloc(size, sizeof(*queue), GFP_KERNEL);
  		if (!queue)
  			return -ENOMEM;
  	}
  
  	spin_lock_irq(&tu->qlock);
  	kfree(tu->queue);
  	kfree(tu->tqueue);
  	tu->queue_size = size;
  	tu->queue = queue;
  	tu->tqueue = tqueue;
  	tu->qhead = tu->qtail = tu->qused = 0;
  	spin_unlock_irq(&tu->qlock);
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1367
1368
  static int snd_timer_user_open(struct inode *inode, struct file *file)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1369
  	struct snd_timer_user *tu;
02f4865fa   Takashi Iwai   ALSA: core - Defi...
1370
1371
1372
1373
1374
  	int err;
  
  	err = nonseekable_open(inode, file);
  	if (err < 0)
  		return err;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1375

ca2c09665   Takashi Iwai   [ALSA] Replace wi...
1376
  	tu = kzalloc(sizeof(*tu), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377
1378
1379
1380
  	if (tu == NULL)
  		return -ENOMEM;
  	spin_lock_init(&tu->qlock);
  	init_waitqueue_head(&tu->qchange_sleep);
af368027a   Takashi Iwai   ALSA: timer: Fix ...
1381
  	mutex_init(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1382
  	tu->ticks = 1;
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1383
  	if (realloc_user_queue(tu, 128) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1384
1385
1386
1387
1388
1389
1390
1391
1392
  		kfree(tu);
  		return -ENOMEM;
  	}
  	file->private_data = tu;
  	return 0;
  }
  
  static int snd_timer_user_release(struct inode *inode, struct file *file)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1393
  	struct snd_timer_user *tu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1394
1395
1396
1397
  
  	if (file->private_data) {
  		tu = file->private_data;
  		file->private_data = NULL;
af368027a   Takashi Iwai   ALSA: timer: Fix ...
1398
  		mutex_lock(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399
1400
  		if (tu->timeri)
  			snd_timer_close(tu->timeri);
af368027a   Takashi Iwai   ALSA: timer: Fix ...
1401
  		mutex_unlock(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1402
1403
1404
1405
1406
1407
  		kfree(tu->queue);
  		kfree(tu->tqueue);
  		kfree(tu);
  	}
  	return 0;
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1408
  static void snd_timer_user_zero_id(struct snd_timer_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1409
1410
1411
1412
1413
1414
1415
  {
  	id->dev_class = SNDRV_TIMER_CLASS_NONE;
  	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
  	id->card = -1;
  	id->device = -1;
  	id->subdevice = -1;
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1416
  static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1417
1418
1419
1420
1421
1422
1423
  {
  	id->dev_class = timer->tmr_class;
  	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
  	id->card = timer->card ? timer->card->number : -1;
  	id->device = timer->tmr_device;
  	id->subdevice = timer->tmr_subdevice;
  }
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1424
  static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1426
1427
  	struct snd_timer_id id;
  	struct snd_timer *timer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1428
  	struct list_head *p;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1429

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1430
1431
  	if (copy_from_user(&id, _tid, sizeof(id)))
  		return -EFAULT;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1432
  	mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
1434
1435
1436
  	if (id.dev_class < 0) {		/* first item */
  		if (list_empty(&snd_timer_list))
  			snd_timer_user_zero_id(&id);
  		else {
9dfba3801   Clemens Ladisch   [ALSA] timer: rem...
1437
  			timer = list_entry(snd_timer_list.next,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1438
  					   struct snd_timer, device_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1439
1440
1441
1442
1443
1444
1445
  			snd_timer_user_copy_id(&id, timer);
  		}
  	} else {
  		switch (id.dev_class) {
  		case SNDRV_TIMER_CLASS_GLOBAL:
  			id.device = id.device < 0 ? 0 : id.device + 1;
  			list_for_each(p, &snd_timer_list) {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1446
  				timer = list_entry(p, struct snd_timer, device_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
  				if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
  					snd_timer_user_copy_id(&id, timer);
  					break;
  				}
  				if (timer->tmr_device >= id.device) {
  					snd_timer_user_copy_id(&id, timer);
  					break;
  				}
  			}
  			if (p == &snd_timer_list)
  				snd_timer_user_zero_id(&id);
  			break;
  		case SNDRV_TIMER_CLASS_CARD:
  		case SNDRV_TIMER_CLASS_PCM:
  			if (id.card < 0) {
  				id.card = 0;
  			} else {
e8ed68205   Dan Carpenter   ALSA: timer: remo...
1464
1465
  				if (id.device < 0) {
  					id.device = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1466
  				} else {
e8ed68205   Dan Carpenter   ALSA: timer: remo...
1467
1468
  					if (id.subdevice < 0)
  						id.subdevice = 0;
69f96e9b5   Takashi Iwai   ALSA: timer: Fix ...
1469
  					else if (id.subdevice < INT_MAX)
e8ed68205   Dan Carpenter   ALSA: timer: remo...
1470
  						id.subdevice++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1471
1472
1473
  				}
  			}
  			list_for_each(p, &snd_timer_list) {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1474
  				timer = list_entry(p, struct snd_timer, device_list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
  				if (timer->tmr_class > id.dev_class) {
  					snd_timer_user_copy_id(&id, timer);
  					break;
  				}
  				if (timer->tmr_class < id.dev_class)
  					continue;
  				if (timer->card->number > id.card) {
  					snd_timer_user_copy_id(&id, timer);
  					break;
  				}
  				if (timer->card->number < id.card)
  					continue;
  				if (timer->tmr_device > id.device) {
  					snd_timer_user_copy_id(&id, timer);
  					break;
  				}
  				if (timer->tmr_device < id.device)
  					continue;
  				if (timer->tmr_subdevice > id.subdevice) {
  					snd_timer_user_copy_id(&id, timer);
  					break;
  				}
  				if (timer->tmr_subdevice < id.subdevice)
  					continue;
  				snd_timer_user_copy_id(&id, timer);
  				break;
  			}
  			if (p == &snd_timer_list)
  				snd_timer_user_zero_id(&id);
  			break;
  		default:
  			snd_timer_user_zero_id(&id);
  		}
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1509
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1510
1511
1512
  	if (copy_to_user(_tid, &id, sizeof(*_tid)))
  		return -EFAULT;
  	return 0;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1513
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1514

6b172a853   Clemens Ladisch   [ALSA] timer: for...
1515
  static int snd_timer_user_ginfo(struct file *file,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1516
  				struct snd_timer_ginfo __user *_ginfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1517
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1518
1519
1520
  	struct snd_timer_ginfo *ginfo;
  	struct snd_timer_id tid;
  	struct snd_timer *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1521
1522
  	struct list_head *p;
  	int err = 0;
ef44a1ec6   Li Zefan   ALSA: sound/core:...
1523
1524
1525
  	ginfo = memdup_user(_ginfo, sizeof(*ginfo));
  	if (IS_ERR(ginfo))
  		return PTR_ERR(ginfo);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1526
1527
1528
  	tid = ginfo->tid;
  	memset(ginfo, 0, sizeof(*ginfo));
  	ginfo->tid = tid;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1529
  	mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
  	t = snd_timer_find(&tid);
  	if (t != NULL) {
  		ginfo->card = t->card ? t->card->number : -1;
  		if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
  			ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
  		strlcpy(ginfo->id, t->id, sizeof(ginfo->id));
  		strlcpy(ginfo->name, t->name, sizeof(ginfo->name));
  		ginfo->resolution = t->hw.resolution;
  		if (t->hw.resolution_min > 0) {
  			ginfo->resolution_min = t->hw.resolution_min;
  			ginfo->resolution_max = t->hw.resolution_max;
  		}
  		list_for_each(p, &t->open_list_head) {
  			ginfo->clients++;
  		}
  	} else {
  		err = -ENODEV;
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1548
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1549
1550
1551
1552
1553
  	if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
  		err = -EFAULT;
  	kfree(ginfo);
  	return err;
  }
91d2178e2   Takashi Sakamoto   ALSA: timer: fix ...
1554
  static int timer_set_gparams(struct snd_timer_gparams *gparams)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1555
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1556
  	struct snd_timer *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1557
  	int err;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1558
  	mutex_lock(&register_mutex);
91d2178e2   Takashi Sakamoto   ALSA: timer: fix ...
1559
  	t = snd_timer_find(&gparams->tid);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1560
  	if (!t) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1561
  		err = -ENODEV;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1562
1563
1564
1565
1566
1567
1568
1569
1570
  		goto _error;
  	}
  	if (!list_empty(&t->open_list_head)) {
  		err = -EBUSY;
  		goto _error;
  	}
  	if (!t->hw.set_period) {
  		err = -ENOSYS;
  		goto _error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1571
  	}
91d2178e2   Takashi Sakamoto   ALSA: timer: fix ...
1572
  	err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1573
  _error:
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1574
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1575
1576
  	return err;
  }
91d2178e2   Takashi Sakamoto   ALSA: timer: fix ...
1577
1578
1579
1580
1581
1582
1583
1584
1585
  static int snd_timer_user_gparams(struct file *file,
  				  struct snd_timer_gparams __user *_gparams)
  {
  	struct snd_timer_gparams gparams;
  
  	if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
  		return -EFAULT;
  	return timer_set_gparams(&gparams);
  }
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1586
  static int snd_timer_user_gstatus(struct file *file,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1587
  				  struct snd_timer_gstatus __user *_gstatus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1588
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1589
1590
1591
  	struct snd_timer_gstatus gstatus;
  	struct snd_timer_id tid;
  	struct snd_timer *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1592
1593
1594
1595
1596
1597
1598
  	int err = 0;
  
  	if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
  		return -EFAULT;
  	tid = gstatus.tid;
  	memset(&gstatus, 0, sizeof(gstatus));
  	gstatus.tid = tid;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1599
  	mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1600
1601
1602
1603
1604
1605
1606
  	t = snd_timer_find(&tid);
  	if (t != NULL) {
  		if (t->hw.c_resolution)
  			gstatus.resolution = t->hw.c_resolution(t);
  		else
  			gstatus.resolution = t->hw.resolution;
  		if (t->hw.precise_resolution) {
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1607
1608
  			t->hw.precise_resolution(t, &gstatus.resolution_num,
  						 &gstatus.resolution_den);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1609
1610
1611
1612
1613
1614
1615
  		} else {
  			gstatus.resolution_num = gstatus.resolution;
  			gstatus.resolution_den = 1000000000uL;
  		}
  	} else {
  		err = -ENODEV;
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
1616
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1617
1618
1619
1620
  	if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
  		err = -EFAULT;
  	return err;
  }
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1621
  static int snd_timer_user_tselect(struct file *file,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1622
  				  struct snd_timer_select __user *_tselect)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1623
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1624
1625
  	struct snd_timer_user *tu;
  	struct snd_timer_select tselect;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1626
  	char str[32];
c1935b4d6   Jaroslav Kysela   [ALSA] timer - ad...
1627
  	int err = 0;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1628

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1629
  	tu = file->private_data;
c1935b4d6   Jaroslav Kysela   [ALSA] timer - ad...
1630
  	if (tu->timeri) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1631
  		snd_timer_close(tu->timeri);
c1935b4d6   Jaroslav Kysela   [ALSA] timer - ad...
1632
1633
1634
1635
1636
1637
  		tu->timeri = NULL;
  	}
  	if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
  		err = -EFAULT;
  		goto __err;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1638
1639
1640
  	sprintf(str, "application %i", current->pid);
  	if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
  		tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1641
1642
  	err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid);
  	if (err < 0)
c1935b4d6   Jaroslav Kysela   [ALSA] timer - ad...
1643
  		goto __err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1644

890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1645
1646
  	tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
  	tu->timeri->callback = tu->tread
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1647
  			? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1648
1649
1650
  	tu->timeri->ccallback = snd_timer_user_ccallback;
  	tu->timeri->callback_data = (void *)tu;
  	tu->timeri->disconnect = snd_timer_user_disconnect;
c1935b4d6   Jaroslav Kysela   [ALSA] timer - ad...
1651
1652
  
        __err:
c1935b4d6   Jaroslav Kysela   [ALSA] timer - ad...
1653
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1654
  }
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1655
  static int snd_timer_user_info(struct file *file,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1656
  			       struct snd_timer_info __user *_info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1657
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1658
1659
1660
  	struct snd_timer_user *tu;
  	struct snd_timer_info *info;
  	struct snd_timer *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1661
1662
1663
  	int err = 0;
  
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1664
1665
  	if (!tu->timeri)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1666
  	t = tu->timeri->timer;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1667
1668
  	if (!t)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1669

ca2c09665   Takashi Iwai   [ALSA] Replace wi...
1670
  	info = kzalloc(sizeof(*info), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
  	if (! info)
  		return -ENOMEM;
  	info->card = t->card ? t->card->number : -1;
  	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
  		info->flags |= SNDRV_TIMER_FLG_SLAVE;
  	strlcpy(info->id, t->id, sizeof(info->id));
  	strlcpy(info->name, t->name, sizeof(info->name));
  	info->resolution = t->hw.resolution;
  	if (copy_to_user(_info, info, sizeof(*_info)))
  		err = -EFAULT;
  	kfree(info);
  	return err;
  }
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1684
  static int snd_timer_user_params(struct file *file,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1685
  				 struct snd_timer_params __user *_params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1686
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1687
1688
1689
  	struct snd_timer_user *tu;
  	struct snd_timer_params params;
  	struct snd_timer *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1690
  	int err;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1691

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1692
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1693
1694
  	if (!tu->timeri)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1695
  	t = tu->timeri->timer;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1696
1697
  	if (!t)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1698
1699
  	if (copy_from_user(&params, _params, sizeof(params)))
  		return -EFAULT;
71321eb3f   Takashi Iwai   ALSA: timer: Reje...
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
  	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
  		u64 resolution;
  
  		if (params.ticks < 1) {
  			err = -EINVAL;
  			goto _end;
  		}
  
  		/* Don't allow resolution less than 1ms */
  		resolution = snd_timer_resolution(tu->timeri);
  		resolution *= params.ticks;
  		if (resolution < 1000000) {
  			err = -EINVAL;
  			goto _end;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1715
  	}
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1716
1717
  	if (params.queue_size > 0 &&
  	    (params.queue_size < 32 || params.queue_size > 1024)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1718
1719
1720
1721
1722
1723
1724
1725
1726
  		err = -EINVAL;
  		goto _end;
  	}
  	if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)|
  			      (1<<SNDRV_TIMER_EVENT_TICK)|
  			      (1<<SNDRV_TIMER_EVENT_START)|
  			      (1<<SNDRV_TIMER_EVENT_STOP)|
  			      (1<<SNDRV_TIMER_EVENT_CONTINUE)|
  			      (1<<SNDRV_TIMER_EVENT_PAUSE)|
a501dfa3a   Jaroslav Kysela   [ALSA] Timer API ...
1727
1728
  			      (1<<SNDRV_TIMER_EVENT_SUSPEND)|
  			      (1<<SNDRV_TIMER_EVENT_RESUME)|
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1729
1730
1731
  			      (1<<SNDRV_TIMER_EVENT_MSTART)|
  			      (1<<SNDRV_TIMER_EVENT_MSTOP)|
  			      (1<<SNDRV_TIMER_EVENT_MCONTINUE)|
65d11d955   Jaroslav Kysela   [ALSA] ALSA timer...
1732
1733
  			      (1<<SNDRV_TIMER_EVENT_MPAUSE)|
  			      (1<<SNDRV_TIMER_EVENT_MSUSPEND)|
a501dfa3a   Jaroslav Kysela   [ALSA] Timer API ...
1734
  			      (1<<SNDRV_TIMER_EVENT_MRESUME))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
  		err = -EINVAL;
  		goto _end;
  	}
  	snd_timer_stop(tu->timeri);
  	spin_lock_irq(&t->lock);
  	tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
  			       SNDRV_TIMER_IFLG_EXCLUSIVE|
  			       SNDRV_TIMER_IFLG_EARLY_EVENT);
  	if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
  		tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
  	if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
  		tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
  	if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
  		tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
  	spin_unlock_irq(&t->lock);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1750
1751
  	if (params.queue_size > 0 &&
  	    (unsigned int)tu->queue_size != params.queue_size) {
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1752
1753
1754
  		err = realloc_user_queue(tu, params.queue_size);
  		if (err < 0)
  			goto _end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1755
  	}
d7f910bfe   Takashi Iwai   ALSA: timer: Wrap...
1756
  	spin_lock_irq(&tu->qlock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1757
1758
1759
  	tu->qhead = tu->qtail = tu->qused = 0;
  	if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
  		if (tu->tread) {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1760
  			struct snd_timer_tread tread;
cec8f96e4   Kangjie Lu   ALSA: timer: Fix ...
1761
  			memset(&tread, 0, sizeof(tread));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1762
1763
1764
1765
1766
1767
  			tread.event = SNDRV_TIMER_EVENT_EARLY;
  			tread.tstamp.tv_sec = 0;
  			tread.tstamp.tv_nsec = 0;
  			tread.val = 0;
  			snd_timer_user_append_to_tqueue(tu, &tread);
  		} else {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1768
  			struct snd_timer_read *r = &tu->queue[0];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1769
1770
1771
1772
1773
  			r->resolution = 0;
  			r->ticks = 0;
  			tu->qused++;
  			tu->qtail++;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1774
1775
1776
  	}
  	tu->filter = params.filter;
  	tu->ticks = params.ticks;
d7f910bfe   Takashi Iwai   ALSA: timer: Wrap...
1777
  	spin_unlock_irq(&tu->qlock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1778
1779
1780
1781
1782
1783
  	err = 0;
   _end:
  	if (copy_to_user(_params, &params, sizeof(params)))
  		return -EFAULT;
  	return err;
  }
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1784
  static int snd_timer_user_status(struct file *file,
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1785
  				 struct snd_timer_status __user *_status)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1786
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1787
1788
  	struct snd_timer_user *tu;
  	struct snd_timer_status status;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1789

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1790
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1791
1792
  	if (!tu->timeri)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
  	memset(&status, 0, sizeof(status));
  	status.tstamp = tu->tstamp;
  	status.resolution = snd_timer_resolution(tu->timeri);
  	status.lost = tu->timeri->lost;
  	status.overrun = tu->overrun;
  	spin_lock_irq(&tu->qlock);
  	status.queue = tu->qused;
  	spin_unlock_irq(&tu->qlock);
  	if (copy_to_user(_status, &status, sizeof(status)))
  		return -EFAULT;
  	return 0;
  }
  
  static int snd_timer_user_start(struct file *file)
  {
  	int err;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1809
  	struct snd_timer_user *tu;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1810

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1811
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1812
1813
  	if (!tu->timeri)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1814
1815
1816
1817
1818
1819
1820
1821
1822
  	snd_timer_stop(tu->timeri);
  	tu->timeri->lost = 0;
  	tu->last_resolution = 0;
  	return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
  }
  
  static int snd_timer_user_stop(struct file *file)
  {
  	int err;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1823
  	struct snd_timer_user *tu;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1824

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1825
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1826
1827
  	if (!tu->timeri)
  		return -EBADFD;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1828
1829
1830
1831
1832
1833
  	return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
  }
  
  static int snd_timer_user_continue(struct file *file)
  {
  	int err;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1834
  	struct snd_timer_user *tu;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1835

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1836
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1837
1838
  	if (!tu->timeri)
  		return -EBADFD;
9f8a7658b   Takashi Iwai   ALSA: timer: Fix ...
1839
1840
1841
  	/* start timer instead of continue if it's not used before */
  	if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
  		return snd_timer_user_start(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1842
1843
1844
  	tu->timeri->lost = 0;
  	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
  }
15790a6be   Takashi Iwai   [ALSA] Add missin...
1845
1846
1847
  static int snd_timer_user_pause(struct file *file)
  {
  	int err;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1848
  	struct snd_timer_user *tu;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1849

15790a6be   Takashi Iwai   [ALSA] Add missin...
1850
  	tu = file->private_data;
7c64ec343   Clemens Ladisch   [ALSA] timer: che...
1851
1852
  	if (!tu->timeri)
  		return -EBADFD;
d138b4458   Jaroslav Kysela   [ALSA] fixed PAUS...
1853
  	return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
15790a6be   Takashi Iwai   [ALSA] Add missin...
1854
  }
8c50b37c0   Takashi Iwai   [ALSA] Change som...
1855
1856
1857
1858
1859
1860
  enum {
  	SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
  	SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
  	SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
  	SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
  };
af368027a   Takashi Iwai   ALSA: timer: Fix ...
1861
  static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1862
  				 unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1863
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1864
  	struct snd_timer_user *tu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1865
1866
  	void __user *argp = (void __user *)arg;
  	int __user *p = argp;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1867

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1868
1869
1870
1871
1872
1873
1874
1875
  	tu = file->private_data;
  	switch (cmd) {
  	case SNDRV_TIMER_IOCTL_PVERSION:
  		return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
  	case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
  		return snd_timer_user_next_device(argp);
  	case SNDRV_TIMER_IOCTL_TREAD:
  	{
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1876
  		int xarg, old_tread;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1877

af368027a   Takashi Iwai   ALSA: timer: Fix ...
1878
  		if (tu->timeri)	/* too late */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1879
  			return -EBUSY;
af368027a   Takashi Iwai   ALSA: timer: Fix ...
1880
  		if (get_user(xarg, p))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1881
  			return -EFAULT;
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1882
  		old_tread = tu->tread;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1883
  		tu->tread = xarg ? 1 : 0;
890e2cb5d   Takashi Iwai   ALSA: timer: Impr...
1884
1885
1886
1887
1888
  		if (tu->tread != old_tread &&
  		    realloc_user_queue(tu, tu->queue_size) < 0) {
  			tu->tread = old_tread;
  			return -ENOMEM;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
  		return 0;
  	}
  	case SNDRV_TIMER_IOCTL_GINFO:
  		return snd_timer_user_ginfo(file, argp);
  	case SNDRV_TIMER_IOCTL_GPARAMS:
  		return snd_timer_user_gparams(file, argp);
  	case SNDRV_TIMER_IOCTL_GSTATUS:
  		return snd_timer_user_gstatus(file, argp);
  	case SNDRV_TIMER_IOCTL_SELECT:
  		return snd_timer_user_tselect(file, argp);
  	case SNDRV_TIMER_IOCTL_INFO:
  		return snd_timer_user_info(file, argp);
  	case SNDRV_TIMER_IOCTL_PARAMS:
  		return snd_timer_user_params(file, argp);
  	case SNDRV_TIMER_IOCTL_STATUS:
  		return snd_timer_user_status(file, argp);
  	case SNDRV_TIMER_IOCTL_START:
8c50b37c0   Takashi Iwai   [ALSA] Change som...
1906
  	case SNDRV_TIMER_IOCTL_START_OLD:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1907
1908
  		return snd_timer_user_start(file);
  	case SNDRV_TIMER_IOCTL_STOP:
8c50b37c0   Takashi Iwai   [ALSA] Change som...
1909
  	case SNDRV_TIMER_IOCTL_STOP_OLD:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1910
1911
  		return snd_timer_user_stop(file);
  	case SNDRV_TIMER_IOCTL_CONTINUE:
8c50b37c0   Takashi Iwai   [ALSA] Change som...
1912
  	case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1913
  		return snd_timer_user_continue(file);
15790a6be   Takashi Iwai   [ALSA] Add missin...
1914
  	case SNDRV_TIMER_IOCTL_PAUSE:
8c50b37c0   Takashi Iwai   [ALSA] Change som...
1915
  	case SNDRV_TIMER_IOCTL_PAUSE_OLD:
15790a6be   Takashi Iwai   [ALSA] Add missin...
1916
  		return snd_timer_user_pause(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1917
1918
1919
  	}
  	return -ENOTTY;
  }
af368027a   Takashi Iwai   ALSA: timer: Fix ...
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
  static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
  				 unsigned long arg)
  {
  	struct snd_timer_user *tu = file->private_data;
  	long ret;
  
  	mutex_lock(&tu->ioctl_lock);
  	ret = __snd_timer_user_ioctl(file, cmd, arg);
  	mutex_unlock(&tu->ioctl_lock);
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1931
1932
  static int snd_timer_user_fasync(int fd, struct file * file, int on)
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1933
  	struct snd_timer_user *tu;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1934

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1935
  	tu = file->private_data;
60aa49243   Jonathan Corbet   Rationalize fasyn...
1936
  	return fasync_helper(fd, file, on, &tu->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1937
  }
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1938
1939
  static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
  				   size_t count, loff_t *offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1940
  {
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1941
  	struct snd_timer_user *tu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1942
  	long result = 0, unit;
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1943
  	int qhead;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1944
  	int err = 0;
6b172a853   Clemens Ladisch   [ALSA] timer: for...
1945

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1946
  	tu = file->private_data;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
1947
  	unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read);
d11662f4f   Takashi Iwai   ALSA: timer: Fix ...
1948
  	mutex_lock(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1949
1950
1951
  	spin_lock_irq(&tu->qlock);
  	while ((long)count - result >= unit) {
  		while (!tu->qused) {
ac6424b98   Ingo Molnar   sched/wait: Renam...
1952
  			wait_queue_entry_t wait;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1953
1954
1955
  
  			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
  				err = -EAGAIN;
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1956
  				goto _error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1957
1958
1959
1960
1961
1962
1963
  			}
  
  			set_current_state(TASK_INTERRUPTIBLE);
  			init_waitqueue_entry(&wait, current);
  			add_wait_queue(&tu->qchange_sleep, &wait);
  
  			spin_unlock_irq(&tu->qlock);
d11662f4f   Takashi Iwai   ALSA: timer: Fix ...
1964
  			mutex_unlock(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1965
  			schedule();
d11662f4f   Takashi Iwai   ALSA: timer: Fix ...
1966
  			mutex_lock(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1967
1968
1969
  			spin_lock_irq(&tu->qlock);
  
  			remove_wait_queue(&tu->qchange_sleep, &wait);
230323dac   Takashi Iwai   ALSA: timer: Hand...
1970
1971
  			if (tu->disconnected) {
  				err = -ENODEV;
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1972
  				goto _error;
230323dac   Takashi Iwai   ALSA: timer: Hand...
1973
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1974
1975
  			if (signal_pending(current)) {
  				err = -ERESTARTSYS;
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1976
  				goto _error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1977
1978
  			}
  		}
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1979
1980
  		qhead = tu->qhead++;
  		tu->qhead %= tu->queue_size;
3fa6993fe   Takashi Iwai   ALSA: timer: Fix ...
1981
  		tu->qused--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1982
  		spin_unlock_irq(&tu->qlock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1983
1984
  
  		if (tu->tread) {
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1985
1986
  			if (copy_to_user(buffer, &tu->tqueue[qhead],
  					 sizeof(struct snd_timer_tread)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1987
  				err = -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1988
  		} else {
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1989
1990
  			if (copy_to_user(buffer, &tu->queue[qhead],
  					 sizeof(struct snd_timer_read)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1991
  				err = -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1992
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1993
  		spin_lock_irq(&tu->qlock);
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
1994
1995
1996
1997
  		if (err < 0)
  			goto _error;
  		result += unit;
  		buffer += unit;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1998
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1999
   _error:
4dff5c7b7   Takashi Iwai   ALSA: timer: Fix ...
2000
  	spin_unlock_irq(&tu->qlock);
d11662f4f   Takashi Iwai   ALSA: timer: Fix ...
2001
  	mutex_unlock(&tu->ioctl_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2002
2003
2004
2005
2006
2007
  	return result > 0 ? result : err;
  }
  
  static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
  {
          unsigned int mask;
53d2f744a   Takashi Iwai   [ALSA] Remove xxx...
2008
          struct snd_timer_user *tu;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2009
2010
2011
2012
  
          tu = file->private_data;
  
          poll_wait(file, &tu->qchange_sleep, wait);
6b172a853   Clemens Ladisch   [ALSA] timer: for...
2013

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2014
  	mask = 0;
d7f910bfe   Takashi Iwai   ALSA: timer: Wrap...
2015
  	spin_lock_irq(&tu->qlock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2016
2017
  	if (tu->qused)
  		mask |= POLLIN | POLLRDNORM;
230323dac   Takashi Iwai   ALSA: timer: Hand...
2018
2019
  	if (tu->disconnected)
  		mask |= POLLERR;
d7f910bfe   Takashi Iwai   ALSA: timer: Wrap...
2020
  	spin_unlock_irq(&tu->qlock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2021
2022
2023
2024
2025
2026
2027
2028
2029
  
  	return mask;
  }
  
  #ifdef CONFIG_COMPAT
  #include "timer_compat.c"
  #else
  #define snd_timer_user_ioctl_compat	NULL
  #endif
9c2e08c59   Arjan van de Ven   [PATCH] mark stru...
2030
  static const struct file_operations snd_timer_f_ops =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2031
2032
2033
2034
2035
  {
  	.owner =	THIS_MODULE,
  	.read =		snd_timer_user_read,
  	.open =		snd_timer_user_open,
  	.release =	snd_timer_user_release,
02f4865fa   Takashi Iwai   ALSA: core - Defi...
2036
  	.llseek =	no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2037
2038
2039
2040
2041
  	.poll =		snd_timer_user_poll,
  	.unlocked_ioctl =	snd_timer_user_ioctl,
  	.compat_ioctl =	snd_timer_user_ioctl_compat,
  	.fasync = 	snd_timer_user_fasync,
  };
7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2042
2043
2044
2045
2046
2047
2048
2049
  /* unregister the system timer */
  static void snd_timer_free_all(void)
  {
  	struct snd_timer *timer, *n;
  
  	list_for_each_entry_safe(timer, n, &snd_timer_list, device_list)
  		snd_timer_free(timer);
  }
89da061f0   Takashi Iwai   ALSA: timer: Hand...
2050
  static struct device timer_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2051
2052
2053
  /*
   *  ENTRY functions
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2054
2055
2056
  static int __init alsa_timer_init(void)
  {
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2057

89da061f0   Takashi Iwai   ALSA: timer: Hand...
2058
2059
  	snd_device_initialize(&timer_dev, NULL);
  	dev_set_name(&timer_dev, "timer");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2060
  #ifdef SNDRV_OSS_INFO_DEV_TIMERS
6b172a853   Clemens Ladisch   [ALSA] timer: for...
2061
2062
  	snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
  			      "system timer");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2063
  #endif
e28563cce   Takashi Iwai   [ALSA] Optimize f...
2064

7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2065
2066
  	err = snd_timer_register_system();
  	if (err < 0) {
cf74dcf35   Takashi Iwai   ALSA: timer: Use ...
2067
2068
  		pr_err("ALSA: unable to register system timer (%i)
  ", err);
1ae0e4ce5   Markus Elfring   ALSA: timer: Use ...
2069
  		goto put_timer;
7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2070
  	}
40a4b2638   Takashi Iwai   ALSA: Simplify sn...
2071
2072
  	err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
  				  &snd_timer_f_ops, NULL, &timer_dev);
7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2073
  	if (err < 0) {
cf74dcf35   Takashi Iwai   ALSA: timer: Use ...
2074
2075
  		pr_err("ALSA: unable to register timer device (%i)
  ", err);
7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2076
  		snd_timer_free_all();
1ae0e4ce5   Markus Elfring   ALSA: timer: Use ...
2077
  		goto put_timer;
7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2078
  	}
e28563cce   Takashi Iwai   [ALSA] Optimize f...
2079
  	snd_timer_proc_init();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2080
  	return 0;
1ae0e4ce5   Markus Elfring   ALSA: timer: Use ...
2081
2082
2083
2084
  
  put_timer:
  	put_device(&timer_dev);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2085
2086
2087
2088
  }
  
  static void __exit alsa_timer_exit(void)
  {
40a4b2638   Takashi Iwai   ALSA: Simplify sn...
2089
  	snd_unregister_device(&timer_dev);
7c35860d1   Takashi Iwai   ALSA: timer: Prop...
2090
  	snd_timer_free_all();
89da061f0   Takashi Iwai   ALSA: timer: Hand...
2091
  	put_device(&timer_dev);
e28563cce   Takashi Iwai   [ALSA] Optimize f...
2092
  	snd_timer_proc_done();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2093
2094
2095
2096
2097
2098
2099
  #ifdef SNDRV_OSS_INFO_DEV_TIMERS
  	snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
  #endif
  }
  
  module_init(alsa_timer_init)
  module_exit(alsa_timer_exit)