Blame view

sound/core/control.c 38.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  Routines for driver control interface
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/threads.h>
  #include <linux/interrupt.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
30
31
32
  #include <linux/slab.h>
  #include <linux/vmalloc.h>
  #include <linux/time.h>
  #include <sound/core.h>
  #include <sound/minors.h>
  #include <sound/info.h>
  #include <sound/control.h>
  
  /* max number of user-defined controls */
  #define MAX_USER_CONTROLS	32
5591bf072   Dan Rosenberg   ALSA: prevent hea...
33
  #define MAX_CONTROL_COUNT	1028
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
35
  struct snd_kctl_ioctl {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  	struct list_head list;		/* list of all ioctls */
  	snd_kctl_ioctl_func_t fioctl;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
38
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
  
  static DECLARE_RWSEM(snd_ioctl_rwsem);
  static LIST_HEAD(snd_control_ioctls);
  #ifdef CONFIG_COMPAT
  static LIST_HEAD(snd_control_compat_ioctls);
  #endif
  
  static int snd_ctl_open(struct inode *inode, struct file *file)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
  	unsigned long flags;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
49
50
  	struct snd_card *card;
  	struct snd_ctl_file *ctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
  	int err;
02f4865fa   Takashi Iwai   ALSA: core - Defi...
52
53
54
  	err = nonseekable_open(inode, file);
  	if (err < 0)
  		return err;
f87135f56   Clemens Ladisch   [ALSA] dynamic mi...
55
  	card = snd_lookup_minor_data(iminor(inode), SNDRV_DEVICE_TYPE_CONTROL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
64
65
66
67
68
  	if (!card) {
  		err = -ENODEV;
  		goto __error1;
  	}
  	err = snd_card_file_add(card, file);
  	if (err < 0) {
  		err = -ENODEV;
  		goto __error1;
  	}
  	if (!try_module_get(card->module)) {
  		err = -EFAULT;
  		goto __error2;
  	}
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
69
  	ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
73
74
75
76
77
  	if (ctl == NULL) {
  		err = -ENOMEM;
  		goto __error;
  	}
  	INIT_LIST_HEAD(&ctl->events);
  	init_waitqueue_head(&ctl->change_sleep);
  	spin_lock_init(&ctl->read_lock);
  	ctl->card = card;
2529bba76   Takashi Iwai   [ALSA] Fix substr...
78
79
  	ctl->prefer_pcm_subdevice = -1;
  	ctl->prefer_rawmidi_subdevice = -1;
25d27eded   Clemens Ladisch   control: use refe...
80
  	ctl->pid = get_pid(task_pid(current));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
90
91
92
93
  	file->private_data = ctl;
  	write_lock_irqsave(&card->ctl_files_rwlock, flags);
  	list_add_tail(&ctl->list, &card->ctl_files);
  	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
  	return 0;
  
        __error:
  	module_put(card->module);
        __error2:
  	snd_card_file_remove(card, file);
        __error1:
        	return err;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
94
  static void snd_ctl_empty_read_queue(struct snd_ctl_file * ctl)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  {
7507e8da2   Borislav Petkov   [ALSA] sound/core...
96
  	unsigned long flags;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
97
  	struct snd_kctl_event *cread;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
  	
7507e8da2   Borislav Petkov   [ALSA] sound/core...
99
  	spin_lock_irqsave(&ctl->read_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
  	while (!list_empty(&ctl->events)) {
  		cread = snd_kctl_event(ctl->events.next);
  		list_del(&cread->list);
  		kfree(cread);
  	}
7507e8da2   Borislav Petkov   [ALSA] sound/core...
105
  	spin_unlock_irqrestore(&ctl->read_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
107
108
109
110
  }
  
  static int snd_ctl_release(struct inode *inode, struct file *file)
  {
  	unsigned long flags;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
111
112
113
  	struct snd_card *card;
  	struct snd_ctl_file *ctl;
  	struct snd_kcontrol *control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
  	unsigned int idx;
  
  	ctl = file->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
119
120
121
122
  	file->private_data = NULL;
  	card = ctl->card;
  	write_lock_irqsave(&card->ctl_files_rwlock, flags);
  	list_del(&ctl->list);
  	write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
  	down_write(&card->controls_rwsem);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
123
  	list_for_each_entry(control, &card->controls, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
  		for (idx = 0; idx < control->count; idx++)
  			if (control->vd[idx].owner == ctl)
  				control->vd[idx].owner = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
128
  	up_write(&card->controls_rwsem);
  	snd_ctl_empty_read_queue(ctl);
25d27eded   Clemens Ladisch   control: use refe...
129
  	put_pid(ctl->pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
134
  	kfree(ctl);
  	module_put(card->module);
  	snd_card_file_remove(card, file);
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
135
136
  void snd_ctl_notify(struct snd_card *card, unsigned int mask,
  		    struct snd_ctl_elem_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
  {
  	unsigned long flags;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
139
140
  	struct snd_ctl_file *ctl;
  	struct snd_kctl_event *ev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  	
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
142
143
  	if (snd_BUG_ON(!card || !id))
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
  	read_lock(&card->ctl_files_rwlock);
  #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
  	card->mixer_oss_change_count++;
  #endif
9244b2c30   Johannes Berg   [ALSA] alsa core:...
148
  	list_for_each_entry(ctl, &card->ctl_files, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  		if (!ctl->subscribed)
  			continue;
  		spin_lock_irqsave(&ctl->read_lock, flags);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
152
  		list_for_each_entry(ev, &ctl->events, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
154
155
156
157
  			if (ev->id.numid == id->numid) {
  				ev->mask |= mask;
  				goto _found;
  			}
  		}
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
158
  		ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  		if (ev) {
  			ev->id = *id;
  			ev->mask = mask;
  			list_add_tail(&ev->list, &ctl->events);
  		} else {
  			snd_printk(KERN_ERR "No memory available to allocate event
  ");
  		}
  	_found:
  		wake_up(&ctl->change_sleep);
  		spin_unlock_irqrestore(&ctl->read_lock, flags);
  		kill_fasync(&ctl->fasync, SIGIO, POLL_IN);
  	}
  	read_unlock(&card->ctl_files_rwlock);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
174
  EXPORT_SYMBOL(snd_ctl_notify);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
  /**
   * snd_ctl_new - create a control instance from the template
   * @control: the control template
   * @access: the default control access
   *
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
180
   * Allocates a new struct snd_kcontrol instance and copies the given template 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
182
183
184
   * to the new instance. It does not copy volatile data (access).
   *
   * Returns the pointer of the new instance, or NULL on failure.
   */
0b51ba07e   Adrian Bunk   [ALSA] make sound...
185
186
  static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control,
  					unsigned int access)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
187
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
188
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
  	unsigned int idx;
  	
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
191
192
  	if (snd_BUG_ON(!control || !control->count))
  		return NULL;
5591bf072   Dan Rosenberg   ALSA: prevent hea...
193
194
195
  
  	if (control->count > MAX_CONTROL_COUNT)
  		return NULL;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
196
  	kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL);
73e77ba02   Takashi Iwai   [ALSA] Add error ...
197
198
199
  	if (kctl == NULL) {
  		snd_printk(KERN_ERR "Cannot allocate control instance
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  		return NULL;
73e77ba02   Takashi Iwai   [ALSA] Add error ...
201
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
206
207
208
209
210
211
212
  	*kctl = *control;
  	for (idx = 0; idx < kctl->count; idx++)
  		kctl->vd[idx].access = access;
  	return kctl;
  }
  
  /**
   * snd_ctl_new1 - create a control instance from the template
   * @ncontrol: the initialization record
   * @private_data: the private data to set
   *
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
213
   * Allocates a new struct snd_kcontrol instance and initialize from the given 
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
216
217
218
   * template.  When the access field of ncontrol is 0, it's assumed as
   * READWRITE access. When the count field is 0, it's assumes as one.
   *
   * Returns the pointer of the newly generated instance, or NULL on failure.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
219
220
  struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
  				  void *private_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
222
  	struct snd_kcontrol kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  	unsigned int access;
  	
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
225
226
  	if (snd_BUG_ON(!ncontrol || !ncontrol->info))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
  	memset(&kctl, 0, sizeof(kctl));
  	kctl.id.iface = ncontrol->iface;
  	kctl.id.device = ncontrol->device;
  	kctl.id.subdevice = ncontrol->subdevice;
366840d7e   Mark Brown   ALSA: Warn when c...
231
  	if (ncontrol->name) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  		strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name));
366840d7e   Mark Brown   ALSA: Warn when c...
233
234
235
236
237
238
  		if (strcmp(ncontrol->name, kctl.id.name) != 0)
  			snd_printk(KERN_WARNING
  				   "Control name '%s' truncated to '%s'
  ",
  				   ncontrol->name, kctl.id.name);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
  	kctl.id.index = ncontrol->index;
  	kctl.count = ncontrol->count ? ncontrol->count : 1;
  	access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
242
243
  		 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
  				      SNDRV_CTL_ELEM_ACCESS_INACTIVE|
a75d7a4cf   Clemens Ladisch   sound: control: a...
244
245
246
  				      SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
  				      SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
  				      SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
  	kctl.info = ncontrol->info;
  	kctl.get = ncontrol->get;
  	kctl.put = ncontrol->put;
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
250
  	kctl.tlv.p = ncontrol->tlv.p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
252
253
254
  	kctl.private_value = ncontrol->private_value;
  	kctl.private_data = private_data;
  	return snd_ctl_new(&kctl, access);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
255
  EXPORT_SYMBOL(snd_ctl_new1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
259
260
261
262
263
  /**
   * snd_ctl_free_one - release the control instance
   * @kcontrol: the control instance
   *
   * Releases the control instance created via snd_ctl_new()
   * or snd_ctl_new1().
   * Don't call this after the control was added to the card.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
264
  void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
  {
  	if (kcontrol) {
  		if (kcontrol->private_free)
  			kcontrol->private_free(kcontrol);
  		kfree(kcontrol);
  	}
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
272
  EXPORT_SYMBOL(snd_ctl_free_one);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
273
  static unsigned int snd_ctl_hole_check(struct snd_card *card,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274
275
  				       unsigned int count)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
276
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

9244b2c30   Johannes Berg   [ALSA] alsa core:...
278
  	list_for_each_entry(kctl, &card->controls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
281
282
283
284
285
286
  		if ((kctl->id.numid <= card->last_numid &&
  		     kctl->id.numid + kctl->count > card->last_numid) ||
  		    (kctl->id.numid <= card->last_numid + count - 1 &&
  		     kctl->id.numid + kctl->count > card->last_numid + count - 1))
  		    	return card->last_numid = kctl->id.numid + kctl->count - 1;
  	}
  	return card->last_numid;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
287
  static int snd_ctl_find_hole(struct snd_card *card, unsigned int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  {
  	unsigned int last_numid, iter = 100000;
  
  	last_numid = card->last_numid;
  	while (last_numid != snd_ctl_hole_check(card, count)) {
  		if (--iter == 0) {
  			/* this situation is very unlikely */
  			snd_printk(KERN_ERR "unable to allocate new control numid
  ");
  			return -ENOMEM;
  		}
  		last_numid = card->last_numid;
  	}
  	return 0;
  }
  
  /**
   * snd_ctl_add - add the control instance to the card
   * @card: the card instance
   * @kcontrol: the control instance to add
   *
   * Adds the control instance created via snd_ctl_new() or
   * snd_ctl_new1() to the given card. Assigns also an unique
   * numid used for fast search.
   *
   * Returns zero if successful, or a negative error code on failure.
   *
   * It frees automatically the control which cannot be added.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
317
  int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
319
  	struct snd_ctl_elem_id id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  	unsigned int idx;
c6077b300   Takashi Iwai   [ALSA] Fix memory...
321
  	int err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322

73e77ba02   Takashi Iwai   [ALSA] Add error ...
323
  	if (! kcontrol)
c6077b300   Takashi Iwai   [ALSA] Fix memory...
324
  		return err;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
325
326
  	if (snd_BUG_ON(!card || !kcontrol->info))
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
  	id = kcontrol->id;
  	down_write(&card->controls_rwsem);
  	if (snd_ctl_find_id(card, &id)) {
  		up_write(&card->controls_rwsem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
335
336
337
  		snd_printd(KERN_ERR "control %i:%i:%i:%s:%i is already present
  ",
  					id.iface,
  					id.device,
  					id.subdevice,
  					id.name,
  					id.index);
c6077b300   Takashi Iwai   [ALSA] Fix memory...
338
339
  		err = -EBUSY;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340
341
342
  	}
  	if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
  		up_write(&card->controls_rwsem);
c6077b300   Takashi Iwai   [ALSA] Fix memory...
343
344
  		err = -ENOMEM;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345
346
347
348
349
350
351
352
353
  	}
  	list_add_tail(&kcontrol->list, &card->controls);
  	card->controls_count += kcontrol->count;
  	kcontrol->id.numid = card->last_numid + 1;
  	card->last_numid += kcontrol->count;
  	up_write(&card->controls_rwsem);
  	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
  		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_ADD, &id);
  	return 0;
c6077b300   Takashi Iwai   [ALSA] Fix memory...
354
355
356
357
  
   error:
  	snd_ctl_free_one(kcontrol);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
359
  EXPORT_SYMBOL(snd_ctl_add);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
365
366
367
368
369
370
  /**
   * snd_ctl_remove - remove the control from the card and release it
   * @card: the card instance
   * @kcontrol: the control instance to remove
   *
   * Removes the control from the card and then releases the instance.
   * You don't need to call snd_ctl_free_one(). You must be in
   * the write lock - down_write(&card->controls_rwsem).
   * 
   * Returns 0 if successful, or a negative error code on failure.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
371
  int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
373
  	struct snd_ctl_elem_id id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  	unsigned int idx;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
375
376
  	if (snd_BUG_ON(!card || !kcontrol))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
378
379
380
381
382
383
384
  	list_del(&kcontrol->list);
  	card->controls_count -= kcontrol->count;
  	id = kcontrol->id;
  	for (idx = 0; idx < kcontrol->count; idx++, id.index++, id.numid++)
  		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_REMOVE, &id);
  	snd_ctl_free_one(kcontrol);
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
385
  EXPORT_SYMBOL(snd_ctl_remove);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
388
389
390
391
392
393
394
395
  /**
   * snd_ctl_remove_id - remove the control of the given id and release it
   * @card: the card instance
   * @id: the control id to remove
   *
   * Finds the control instance with the given id, removes it from the
   * card list and releases it.
   * 
   * Returns 0 if successful, or a negative error code on failure.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
396
  int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
397
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
398
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
399
400
401
402
403
404
405
406
407
408
409
410
  	int ret;
  
  	down_write(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, id);
  	if (kctl == NULL) {
  		up_write(&card->controls_rwsem);
  		return -ENOENT;
  	}
  	ret = snd_ctl_remove(card, kctl);
  	up_write(&card->controls_rwsem);
  	return ret;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
411
  EXPORT_SYMBOL(snd_ctl_remove_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
  /**
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
413
   * snd_ctl_remove_user_ctl - remove and release the unlocked user control
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
419
420
421
   * @file: active control handle
   * @id: the control id to remove
   *
   * Finds the control instance with the given id, removes it from the
   * card list and releases it.
   * 
   * Returns 0 if successful, or a negative error code on failure.
   */
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
422
423
  static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
  				   struct snd_ctl_elem_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
424
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
425
426
  	struct snd_card *card = file->card;
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
427
428
429
430
431
  	int idx, ret;
  
  	down_write(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, id);
  	if (kctl == NULL) {
317b80817   Clemens Ladisch   sound: snd_ctl_re...
432
433
  		ret = -ENOENT;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
  	}
18dd0aa5a   Clemens Ladisch   sound: snd_ctl_re...
435
436
437
438
  	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
  		ret = -EINVAL;
  		goto error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
  	for (idx = 0; idx < kctl->count; idx++)
  		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
317b80817   Clemens Ladisch   sound: snd_ctl_re...
441
442
  			ret = -EBUSY;
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
  		}
  	ret = snd_ctl_remove(card, kctl);
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
445
446
447
  	if (ret < 0)
  		goto error;
  	card->user_ctl_count--;
317b80817   Clemens Ladisch   sound: snd_ctl_re...
448
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  	up_write(&card->controls_rwsem);
  	return ret;
  }
  
  /**
   * snd_ctl_rename_id - replace the id of a control on the card
   * @card: the card instance
   * @src_id: the old id
   * @dst_id: the new id
   *
   * Finds the control with the old id from the card, and replaces the
   * id with the new one.
   *
   * Returns zero if successful, or a negative error code on failure.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
464
465
  int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id,
  		      struct snd_ctl_elem_id *dst_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
467
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
468
469
470
471
472
473
474
475
476
477
478
479
480
  
  	down_write(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, src_id);
  	if (kctl == NULL) {
  		up_write(&card->controls_rwsem);
  		return -ENOENT;
  	}
  	kctl->id = *dst_id;
  	kctl->id.numid = card->last_numid + 1;
  	card->last_numid += kctl->count;
  	up_write(&card->controls_rwsem);
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
481
  EXPORT_SYMBOL(snd_ctl_rename_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
484
485
486
487
488
489
490
491
492
493
  /**
   * snd_ctl_find_numid - find the control instance with the given number-id
   * @card: the card instance
   * @numid: the number-id to search
   *
   * Finds the control instance with the given number-id from the card.
   *
   * Returns the pointer of the instance if found, or NULL if not.
   *
   * The caller must down card->controls_rwsem before calling this function
   * (if the race condition can happen).
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
494
  struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
496
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
498
499
  	if (snd_BUG_ON(!card || !numid))
  		return NULL;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
500
  	list_for_each_entry(kctl, &card->controls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
504
505
  		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
  			return kctl;
  	}
  	return NULL;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
506
  EXPORT_SYMBOL(snd_ctl_find_numid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507
508
509
510
511
512
513
514
515
516
517
518
  /**
   * snd_ctl_find_id - find the control instance with the given id
   * @card: the card instance
   * @id: the id to search
   *
   * Finds the control instance with the given id from the card.
   *
   * Returns the pointer of the instance if found, or NULL if not.
   *
   * The caller must down card->controls_rwsem before calling this function
   * (if the race condition can happen).
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
519
520
  struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,
  				     struct snd_ctl_elem_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
522
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
524
525
  	if (snd_BUG_ON(!card || !id))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
526
527
  	if (id->numid != 0)
  		return snd_ctl_find_numid(card, id->numid);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
528
  	list_for_each_entry(kctl, &card->controls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  		if (kctl->id.iface != id->iface)
  			continue;
  		if (kctl->id.device != id->device)
  			continue;
  		if (kctl->id.subdevice != id->subdevice)
  			continue;
  		if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
  			continue;
  		if (kctl->id.index > id->index)
  			continue;
  		if (kctl->id.index + kctl->count <= id->index)
  			continue;
  		return kctl;
  	}
  	return NULL;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
545
  EXPORT_SYMBOL(snd_ctl_find_id);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
546
  static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
548
  			     unsigned int cmd, void __user *arg)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
549
  	struct snd_ctl_card_info *info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550

ca2c09665   Takashi Iwai   [ALSA] Replace wi...
551
  	info = kzalloc(sizeof(*info), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
555
556
557
558
559
560
561
562
  	if (! info)
  		return -ENOMEM;
  	down_read(&snd_ioctl_rwsem);
  	info->card = card->number;
  	strlcpy(info->id, card->id, sizeof(info->id));
  	strlcpy(info->driver, card->driver, sizeof(info->driver));
  	strlcpy(info->name, card->shortname, sizeof(info->name));
  	strlcpy(info->longname, card->longname, sizeof(info->longname));
  	strlcpy(info->mixername, card->mixername, sizeof(info->mixername));
  	strlcpy(info->components, card->components, sizeof(info->components));
  	up_read(&snd_ioctl_rwsem);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
563
  	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564
565
566
567
568
569
  		kfree(info);
  		return -EFAULT;
  	}
  	kfree(info);
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
570
571
  static int snd_ctl_elem_list(struct snd_card *card,
  			     struct snd_ctl_elem_list __user *_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
572
573
  {
  	struct list_head *plist;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
574
575
576
  	struct snd_ctl_elem_list list;
  	struct snd_kcontrol *kctl;
  	struct snd_ctl_elem_id *dst, *id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
577
578
579
580
581
582
583
584
585
586
587
588
  	unsigned int offset, space, first, jidx;
  	
  	if (copy_from_user(&list, _list, sizeof(list)))
  		return -EFAULT;
  	offset = list.offset;
  	space = list.space;
  	first = 0;
  	/* try limit maximum space */
  	if (space > 16384)
  		return -ENOMEM;
  	if (space > 0) {
  		/* allocate temporary buffer for atomic operation */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
589
  		dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
  		if (dst == NULL)
  			return -ENOMEM;
  		down_read(&card->controls_rwsem);
  		list.count = card->controls_count;
  		plist = card->controls.next;
  		while (plist != &card->controls) {
  			if (offset == 0)
  				break;
  			kctl = snd_kcontrol(plist);
  			if (offset < kctl->count)
  				break;
  			offset -= kctl->count;
  			plist = plist->next;
  		}
  		list.used = 0;
  		id = dst;
  		while (space > 0 && plist != &card->controls) {
  			kctl = snd_kcontrol(plist);
  			for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) {
  				snd_ctl_build_ioff(id, kctl, jidx);
  				id++;
  				space--;
  				list.used++;
  			}
  			plist = plist->next;
  			offset = 0;
  		}
  		up_read(&card->controls_rwsem);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
618
619
620
  		if (list.used > 0 &&
  		    copy_to_user(list.pids, dst,
  				 list.used * sizeof(struct snd_ctl_elem_id))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
622
623
624
625
626
627
628
629
630
631
632
633
  			vfree(dst);
  			return -EFAULT;
  		}
  		vfree(dst);
  	} else {
  		down_read(&card->controls_rwsem);
  		list.count = card->controls_count;
  		up_read(&card->controls_rwsem);
  	}
  	if (copy_to_user(_list, &list, sizeof(list)))
  		return -EFAULT;
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
634
635
  static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
  			     struct snd_ctl_elem_info *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
637
638
639
  	struct snd_card *card = ctl->card;
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  	unsigned int index_offset;
  	int result;
  	
  	down_read(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, &info->id);
  	if (kctl == NULL) {
  		up_read(&card->controls_rwsem);
  		return -ENOENT;
  	}
  #ifdef CONFIG_SND_DEBUG
  	info->access = 0;
  #endif
  	result = kctl->info(kctl, info);
  	if (result >= 0) {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
654
  		snd_BUG_ON(info->access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
657
658
659
660
661
662
  		index_offset = snd_ctl_get_ioff(kctl, &info->id);
  		vd = &kctl->vd[index_offset];
  		snd_ctl_build_ioff(&info->id, kctl, index_offset);
  		info->access = vd->access;
  		if (vd->owner) {
  			info->access |= SNDRV_CTL_ELEM_ACCESS_LOCK;
  			if (vd->owner == ctl)
  				info->access |= SNDRV_CTL_ELEM_ACCESS_OWNER;
25d27eded   Clemens Ladisch   control: use refe...
663
  			info->owner = pid_vnr(vd->owner->pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
666
667
668
669
670
  		} else {
  			info->owner = -1;
  		}
  	}
  	up_read(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
671
672
  static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
  				  struct snd_ctl_elem_info __user *_info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
673
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
674
  	struct snd_ctl_elem_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675
676
677
678
  	int result;
  
  	if (copy_from_user(&info, _info, sizeof(info)))
  		return -EFAULT;
646494007   Giuliano Pochini   [ALSA] make contr...
679
  	snd_power_lock(ctl->card);
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
680
  	result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
646494007   Giuliano Pochini   [ALSA] make contr...
681
682
683
  	if (result >= 0)
  		result = snd_ctl_elem_info(ctl, &info);
  	snd_power_unlock(ctl->card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
686
687
688
  	if (result >= 0)
  		if (copy_to_user(_info, &info, sizeof(info)))
  			return -EFAULT;
  	return result;
  }
d3bd67cdb   Takashi Iwai   ALSA: make snd_ct...
689
690
  static int snd_ctl_elem_read(struct snd_card *card,
  			     struct snd_ctl_elem_value *control)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
692
693
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
694
  	unsigned int index_offset;
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
695
  	int result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
701
702
703
  
  	down_read(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, &control->id);
  	if (kctl == NULL) {
  		result = -ENOENT;
  	} else {
  		index_offset = snd_ctl_get_ioff(kctl, &control->id);
  		vd = &kctl->vd[index_offset];
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
704
705
706
707
708
709
  		if ((vd->access & SNDRV_CTL_ELEM_ACCESS_READ) &&
  		    kctl->get != NULL) {
  			snd_ctl_build_ioff(&control->id, kctl, index_offset);
  			result = kctl->get(kctl, control);
  		} else
  			result = -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
  	}
  	up_read(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
714
715
  static int snd_ctl_elem_read_user(struct snd_card *card,
  				  struct snd_ctl_elem_value __user *_control)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
716
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
717
  	struct snd_ctl_elem_value *control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
  	int result;
ef44a1ec6   Li Zefan   ALSA: sound/core:...
719
720
721
722
  
  	control = memdup_user(_control, sizeof(*control));
  	if (IS_ERR(control))
  		return PTR_ERR(control);
646494007   Giuliano Pochini   [ALSA] make contr...
723
  	snd_power_lock(card);
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
724
  	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
646494007   Giuliano Pochini   [ALSA] make contr...
725
726
727
  	if (result >= 0)
  		result = snd_ctl_elem_read(card, control);
  	snd_power_unlock(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
729
730
731
732
733
  	if (result >= 0)
  		if (copy_to_user(_control, control, sizeof(*control)))
  			result = -EFAULT;
  	kfree(control);
  	return result;
  }
d3bd67cdb   Takashi Iwai   ALSA: make snd_ct...
734
735
  static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,
  			      struct snd_ctl_elem_value *control)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
737
738
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
739
  	unsigned int index_offset;
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
740
  	int result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
741
742
743
744
745
746
747
748
  
  	down_read(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, &control->id);
  	if (kctl == NULL) {
  		result = -ENOENT;
  	} else {
  		index_offset = snd_ctl_get_ioff(kctl, &control->id);
  		vd = &kctl->vd[index_offset];
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
749
750
751
752
  		if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_WRITE) ||
  		    kctl->put == NULL ||
  		    (file && vd->owner && vd->owner != file)) {
  			result = -EPERM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
753
  		} else {
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
754
755
756
757
758
759
760
761
  			snd_ctl_build_ioff(&control->id, kctl, index_offset);
  			result = kctl->put(kctl, control);
  		}
  		if (result > 0) {
  			up_read(&card->controls_rwsem);
  			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
  				       &control->id);
  			return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
763
764
765
766
  		}
  	}
  	up_read(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
767
768
  static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
  				   struct snd_ctl_elem_value __user *_control)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
770
  	struct snd_ctl_elem_value *control;
646494007   Giuliano Pochini   [ALSA] make contr...
771
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
  	int result;
ef44a1ec6   Li Zefan   ALSA: sound/core:...
773
774
775
  	control = memdup_user(_control, sizeof(*control));
  	if (IS_ERR(control))
  		return PTR_ERR(control);
646494007   Giuliano Pochini   [ALSA] make contr...
776
777
  	card = file->card;
  	snd_power_lock(card);
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
778
  	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
646494007   Giuliano Pochini   [ALSA] make contr...
779
780
781
  	if (result >= 0)
  		result = snd_ctl_elem_write(card, file, control);
  	snd_power_unlock(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
786
787
  	if (result >= 0)
  		if (copy_to_user(_control, control, sizeof(*control)))
  			result = -EFAULT;
  	kfree(control);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
788
789
  static int snd_ctl_elem_lock(struct snd_ctl_file *file,
  			     struct snd_ctl_elem_id __user *_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
791
792
793
794
  	struct snd_card *card = file->card;
  	struct snd_ctl_elem_id id;
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  	int result;
  	
  	if (copy_from_user(&id, _id, sizeof(id)))
  		return -EFAULT;
  	down_write(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, &id);
  	if (kctl == NULL) {
  		result = -ENOENT;
  	} else {
  		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
  		if (vd->owner != NULL)
  			result = -EBUSY;
  		else {
  			vd->owner = file;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
810
811
812
813
814
  			result = 0;
  		}
  	}
  	up_write(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
815
816
  static int snd_ctl_elem_unlock(struct snd_ctl_file *file,
  			       struct snd_ctl_elem_id __user *_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
818
819
820
821
  	struct snd_card *card = file->card;
  	struct snd_ctl_elem_id id;
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
  	int result;
  	
  	if (copy_from_user(&id, _id, sizeof(id)))
  		return -EFAULT;
  	down_write(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, &id);
  	if (kctl == NULL) {
  		result = -ENOENT;
  	} else {
  		vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
  		if (vd->owner == NULL)
  			result = -EINVAL;
  		else if (vd->owner != file)
  			result = -EPERM;
  		else {
  			vd->owner = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
840
841
842
843
844
845
  			result = 0;
  		}
  	}
  	up_write(&card->controls_rwsem);
  	return result;
  }
  
  struct user_element {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
846
  	struct snd_ctl_elem_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
847
848
  	void *elem_data;		/* element data */
  	unsigned long elem_data_size;	/* size of element data in bytes */
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
849
850
  	void *tlv_data;			/* TLV data */
  	unsigned long tlv_data_size;	/* TLV data size */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851
852
853
  	void *priv_data;		/* private data (like strings for enumerated type) */
  	unsigned long priv_data_size;	/* size of private data in bytes */
  };
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
854
855
  static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
  				  struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
857
858
859
860
861
  {
  	struct user_element *ue = kcontrol->private_data;
  
  	*uinfo = ue->info;
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
862
863
  static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
865
866
867
868
869
  {
  	struct user_element *ue = kcontrol->private_data;
  
  	memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size);
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
870
871
  static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
873
874
875
876
877
878
879
880
  {
  	int change;
  	struct user_element *ue = kcontrol->private_data;
  	
  	change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0;
  	if (change)
  		memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size);
  	return change;
  }
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
881
882
883
884
885
886
887
888
889
890
891
892
  static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
  				 int op_flag,
  				 unsigned int size,
  				 unsigned int __user *tlv)
  {
  	struct user_element *ue = kcontrol->private_data;
  	int change = 0;
  	void *new_data;
  
  	if (op_flag > 0) {
  		if (size > 1024 * 128)	/* sane value */
  			return -EINVAL;
ef44a1ec6   Li Zefan   ALSA: sound/core:...
893
894
895
896
  
  		new_data = memdup_user(tlv, size);
  		if (IS_ERR(new_data))
  			return PTR_ERR(new_data);
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
897
898
899
900
901
902
903
  		change = ue->tlv_data_size != size;
  		if (!change)
  			change = memcmp(ue->tlv_data, new_data, size);
  		kfree(ue->tlv_data);
  		ue->tlv_data = new_data;
  		ue->tlv_data_size = size;
  	} else {
18c1c3f69   Takashi Iwai   [ALSA] Return err...
904
905
  		if (! ue->tlv_data_size || ! ue->tlv_data)
  			return -ENXIO;
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
906
907
908
909
910
911
912
  		if (size < ue->tlv_data_size)
  			return -ENOSPC;
  		if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
  			return -EFAULT;
  	}
  	return change;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
913
  static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
914
  {
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
915
916
917
918
  	struct user_element *ue = kcontrol->private_data;
  	if (ue->tlv_data)
  		kfree(ue->tlv_data);
  	kfree(ue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
919
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
920
921
  static int snd_ctl_elem_add(struct snd_ctl_file *file,
  			    struct snd_ctl_elem_info *info, int replace)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
923
924
  	struct snd_card *card = file->card;
  	struct snd_kcontrol kctl, *_kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
926
927
928
929
930
931
  	unsigned int access;
  	long private_size;
  	struct user_element *ue;
  	int idx, err;
  	
  	if (card->user_ctl_count >= MAX_USER_CONTROLS)
  		return -ENOMEM;
2a031aedf   Clemens Ladisch   sound: snd_ctl_el...
932
  	if (info->count < 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
934
  		return -EINVAL;
  	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
935
  		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
936
937
  				 SNDRV_CTL_ELEM_ACCESS_INACTIVE|
  				 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
  	info->id.numid = 0;
  	memset(&kctl, 0, sizeof(kctl));
  	down_write(&card->controls_rwsem);
  	_kctl = snd_ctl_find_id(card, &info->id);
  	err = 0;
  	if (_kctl) {
  		if (replace)
  			err = snd_ctl_remove(card, _kctl);
  		else
  			err = -EBUSY;
  	} else {
  		if (replace)
  			err = -ENOENT;
  	}
  	up_write(&card->controls_rwsem);
  	if (err < 0)
  		return err;
  	memcpy(&kctl.id, &info->id, sizeof(info->id));
  	kctl.count = info->owner ? info->owner : 1;
  	access |= SNDRV_CTL_ELEM_ACCESS_USER;
  	kctl.info = snd_ctl_elem_user_info;
  	if (access & SNDRV_CTL_ELEM_ACCESS_READ)
  		kctl.get = snd_ctl_elem_user_get;
  	if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
  		kctl.put = snd_ctl_elem_user_put;
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
963
964
965
966
  	if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
  		kctl.tlv.c = snd_ctl_elem_user_tlv;
  		access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
968
  	switch (info->type) {
  	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
  	case SNDRV_CTL_ELEM_TYPE_INTEGER:
  		private_size = sizeof(long);
  		if (info->count > 128)
  			return -EINVAL;
  		break;
  	case SNDRV_CTL_ELEM_TYPE_INTEGER64:
  		private_size = sizeof(long long);
  		if (info->count > 64)
  			return -EINVAL;
  		break;
  	case SNDRV_CTL_ELEM_TYPE_BYTES:
  		private_size = sizeof(unsigned char);
  		if (info->count > 512)
  			return -EINVAL;
  		break;
  	case SNDRV_CTL_ELEM_TYPE_IEC958:
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
985
  		private_size = sizeof(struct snd_aes_iec958);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
986
987
988
989
990
991
992
  		if (info->count != 1)
  			return -EINVAL;
  		break;
  	default:
  		return -EINVAL;
  	}
  	private_size *= info->count;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
993
  	ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
995
996
  	if (ue == NULL)
  		return -ENOMEM;
  	ue->info = *info;
86148e84c   Takashi Iwai   [ALSA] Fix errors...
997
  	ue->info.access = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998
999
1000
1001
1002
  	ue->elem_data = (char *)ue + sizeof(*ue);
  	ue->elem_data_size = private_size;
  	kctl.private_free = snd_ctl_elem_user_free;
  	_kctl = snd_ctl_new(&kctl, access);
  	if (_kctl == NULL) {
2fbf182ed   Takashi Iwai   [PATCH] alsa: fix...
1003
  		kfree(ue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1004
1005
1006
1007
1008
1009
  		return -ENOMEM;
  	}
  	_kctl->private_data = ue;
  	for (idx = 0; idx < _kctl->count; idx++)
  		_kctl->vd[idx].owner = file;
  	err = snd_ctl_add(card, _kctl);
2fbf182ed   Takashi Iwai   [PATCH] alsa: fix...
1010
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1011
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1012
1013
1014
1015
1016
1017
1018
  
  	down_write(&card->controls_rwsem);
  	card->user_ctl_count++;
  	up_write(&card->controls_rwsem);
  
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1019
1020
  static int snd_ctl_elem_add_user(struct snd_ctl_file *file,
  				 struct snd_ctl_elem_info __user *_info, int replace)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1022
  	struct snd_ctl_elem_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1023
1024
1025
1026
  	if (copy_from_user(&info, _info, sizeof(info)))
  		return -EFAULT;
  	return snd_ctl_elem_add(file, &info, replace);
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1027
1028
  static int snd_ctl_elem_remove(struct snd_ctl_file *file,
  			       struct snd_ctl_elem_id __user *_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1029
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1030
  	struct snd_ctl_elem_id id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
1033
  
  	if (copy_from_user(&id, _id, sizeof(id)))
  		return -EFAULT;
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
1034
  	return snd_ctl_remove_user_ctl(file, &id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1035
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1036
  static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
  {
  	int subscribe;
  	if (get_user(subscribe, ptr))
  		return -EFAULT;
  	if (subscribe < 0) {
  		subscribe = file->subscribed;
  		if (put_user(subscribe, ptr))
  			return -EFAULT;
  		return 0;
  	}
  	if (subscribe) {
  		file->subscribed = 1;
  		return 0;
  	} else if (file->subscribed) {
  		snd_ctl_empty_read_queue(file);
  		file->subscribed = 0;
  	}
  	return 0;
  }
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1056
1057
1058
  static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
                               struct snd_ctl_tlv __user *_tlv,
                               int op_flag)
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1059
  {
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1060
  	struct snd_card *card = file->card;
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1061
1062
  	struct snd_ctl_tlv tlv;
  	struct snd_kcontrol *kctl;
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1063
  	struct snd_kcontrol_volatile *vd;
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1064
1065
1066
1067
1068
  	unsigned int len;
  	int err = 0;
  
  	if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
  		return -EFAULT;
6123637fa   Clemens Ladisch   sound: control: f...
1069
  	if (tlv.length < sizeof(unsigned int) * 2)
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
  		return -EINVAL;
  	down_read(&card->controls_rwsem);
  	kctl = snd_ctl_find_numid(card, tlv.numid);
  	if (kctl == NULL) {
  		err = -ENOENT;
  		goto __kctl_end;
  	}
  	if (kctl->tlv.p == NULL) {
  		err = -ENXIO;
  		goto __kctl_end;
  	}
  	vd = &kctl->vd[tlv.numid - kctl->id.numid];
  	if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
  	    (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
  	    (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
  	    	err = -ENXIO;
  	    	goto __kctl_end;
  	}
  	if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
bec145ae6   Dan Carpenter   ALSA: remove unne...
1089
  		if (vd->owner != NULL && vd->owner != file) {
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
  			err = -EPERM;
  			goto __kctl_end;
  		}
  		err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); 
  		if (err > 0) {
  			up_read(&card->controls_rwsem);
  			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
  			return 0;
  		}
  	} else {
  		if (op_flag) {
  			err = -ENXIO;
  			goto __kctl_end;
  		}
  		len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
  		if (tlv.length < len) {
  			err = -ENOMEM;
  			goto __kctl_end;
  		}
  		if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
  			err = -EFAULT;
  	}
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1112
        __kctl_end:
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1113
1114
  	up_read(&card->controls_rwsem);
  	return err;
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1115
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1116
1117
  static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1118
1119
  	struct snd_ctl_file *ctl;
  	struct snd_card *card;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1120
  	struct snd_kctl_ioctl *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1121
1122
1123
1124
1125
1126
  	void __user *argp = (void __user *)arg;
  	int __user *ip = argp;
  	int err;
  
  	ctl = file->private_data;
  	card = ctl->card;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1127
1128
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1129
1130
1131
1132
1133
1134
  	switch (cmd) {
  	case SNDRV_CTL_IOCTL_PVERSION:
  		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
  	case SNDRV_CTL_IOCTL_CARD_INFO:
  		return snd_ctl_card_info(card, ctl, cmd, argp);
  	case SNDRV_CTL_IOCTL_ELEM_LIST:
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1135
  		return snd_ctl_elem_list(card, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1136
1137
1138
  	case SNDRV_CTL_IOCTL_ELEM_INFO:
  		return snd_ctl_elem_info_user(ctl, argp);
  	case SNDRV_CTL_IOCTL_ELEM_READ:
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1139
  		return snd_ctl_elem_read_user(card, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
  	case SNDRV_CTL_IOCTL_ELEM_WRITE:
  		return snd_ctl_elem_write_user(ctl, argp);
  	case SNDRV_CTL_IOCTL_ELEM_LOCK:
  		return snd_ctl_elem_lock(ctl, argp);
  	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
  		return snd_ctl_elem_unlock(ctl, argp);
  	case SNDRV_CTL_IOCTL_ELEM_ADD:
  		return snd_ctl_elem_add_user(ctl, argp, 0);
  	case SNDRV_CTL_IOCTL_ELEM_REPLACE:
  		return snd_ctl_elem_add_user(ctl, argp, 1);
  	case SNDRV_CTL_IOCTL_ELEM_REMOVE:
  		return snd_ctl_elem_remove(ctl, argp);
  	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
  		return snd_ctl_subscribe_events(ctl, ip);
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1154
1155
1156
1157
1158
1159
  	case SNDRV_CTL_IOCTL_TLV_READ:
  		return snd_ctl_tlv_ioctl(ctl, argp, 0);
  	case SNDRV_CTL_IOCTL_TLV_WRITE:
  		return snd_ctl_tlv_ioctl(ctl, argp, 1);
  	case SNDRV_CTL_IOCTL_TLV_COMMAND:
  		return snd_ctl_tlv_ioctl(ctl, argp, -1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1160
  	case SNDRV_CTL_IOCTL_POWER:
a381a7a66   Takashi Iwai   [ALSA] Decentrali...
1161
  		return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1162
1163
1164
1165
1166
1167
1168
1169
  	case SNDRV_CTL_IOCTL_POWER_STATE:
  #ifdef CONFIG_PM
  		return put_user(card->power_state, ip) ? -EFAULT : 0;
  #else
  		return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
  #endif
  	}
  	down_read(&snd_ioctl_rwsem);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1170
  	list_for_each_entry(p, &snd_control_ioctls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1171
1172
1173
1174
1175
1176
1177
  		err = p->fioctl(card, ctl, cmd, arg);
  		if (err != -ENOIOCTLCMD) {
  			up_read(&snd_ioctl_rwsem);
  			return err;
  		}
  	}
  	up_read(&snd_ioctl_rwsem);
6d85be612   Takashi Iwai   [ALSA] Suppress d...
1178
1179
  	snd_printdd("unknown ioctl = 0x%x
  ", cmd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1180
1181
  	return -ENOTTY;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1182
1183
  static ssize_t snd_ctl_read(struct file *file, char __user *buffer,
  			    size_t count, loff_t * offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1184
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1185
  	struct snd_ctl_file *ctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1186
1187
1188
1189
  	int err = 0;
  	ssize_t result = 0;
  
  	ctl = file->private_data;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1190
1191
  	if (snd_BUG_ON(!ctl || !ctl->card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1192
1193
  	if (!ctl->subscribed)
  		return -EBADFD;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1194
  	if (count < sizeof(struct snd_ctl_event))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1195
1196
  		return -EINVAL;
  	spin_lock_irq(&ctl->read_lock);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1197
1198
1199
  	while (count >= sizeof(struct snd_ctl_event)) {
  		struct snd_ctl_event ev;
  		struct snd_kctl_event *kev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
  		while (list_empty(&ctl->events)) {
  			wait_queue_t wait;
  			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
  				err = -EAGAIN;
  				goto __end_lock;
  			}
  			init_waitqueue_entry(&wait, current);
  			add_wait_queue(&ctl->change_sleep, &wait);
  			set_current_state(TASK_INTERRUPTIBLE);
  			spin_unlock_irq(&ctl->read_lock);
  			schedule();
  			remove_wait_queue(&ctl->change_sleep, &wait);
  			if (signal_pending(current))
0e5d720ce   Adrian Bunk   [ALSA] sound/core...
1213
  				return -ERESTARTSYS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1214
1215
1216
1217
1218
1219
1220
1221
1222
  			spin_lock_irq(&ctl->read_lock);
  		}
  		kev = snd_kctl_event(ctl->events.next);
  		ev.type = SNDRV_CTL_EVENT_ELEM;
  		ev.data.elem.mask = kev->mask;
  		ev.data.elem.id = kev->id;
  		list_del(&kev->list);
  		spin_unlock_irq(&ctl->read_lock);
  		kfree(kev);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1223
  		if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1224
1225
1226
1227
  			err = -EFAULT;
  			goto __end;
  		}
  		spin_lock_irq(&ctl->read_lock);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1228
1229
1230
  		buffer += sizeof(struct snd_ctl_event);
  		count -= sizeof(struct snd_ctl_event);
  		result += sizeof(struct snd_ctl_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
  	}
        __end_lock:
  	spin_unlock_irq(&ctl->read_lock);
        __end:
        	return result > 0 ? result : err;
  }
  
  static unsigned int snd_ctl_poll(struct file *file, poll_table * wait)
  {
  	unsigned int mask;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1241
  	struct snd_ctl_file *ctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
  
  	ctl = file->private_data;
  	if (!ctl->subscribed)
  		return 0;
  	poll_wait(file, &ctl->change_sleep, wait);
  
  	mask = 0;
  	if (!list_empty(&ctl->events))
  		mask |= POLLIN | POLLRDNORM;
  
  	return mask;
  }
  
  /*
   * register the device-specific control-ioctls.
   * called from each device manager like pcm.c, hwdep.c, etc.
   */
  static int _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1261
  	struct snd_kctl_ioctl *pn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1262

82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1263
  	pn = kzalloc(sizeof(struct snd_kctl_ioctl), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
  	if (pn == NULL)
  		return -ENOMEM;
  	pn->fioctl = fcn;
  	down_write(&snd_ioctl_rwsem);
  	list_add_tail(&pn->list, lists);
  	up_write(&snd_ioctl_rwsem);
  	return 0;
  }
  
  int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
  {
  	return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
1277
  EXPORT_SYMBOL(snd_ctl_register_ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1278
1279
1280
1281
1282
  #ifdef CONFIG_COMPAT
  int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
  {
  	return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
1283
1284
  
  EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
1286
1287
1288
1289
  #endif
  
  /*
   * de-register the device-specific control-ioctls.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1290
1291
  static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
  				     struct list_head *lists)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1292
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1293
  	struct snd_kctl_ioctl *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1295
1296
  	if (snd_BUG_ON(!fcn))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1297
  	down_write(&snd_ioctl_rwsem);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1298
  	list_for_each_entry(p, lists, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
  		if (p->fioctl == fcn) {
  			list_del(&p->list);
  			up_write(&snd_ioctl_rwsem);
  			kfree(p);
  			return 0;
  		}
  	}
  	up_write(&snd_ioctl_rwsem);
  	snd_BUG();
  	return -EINVAL;
  }
  
  int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
  {
  	return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
1315
  EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1316
1317
1318
1319
1320
  #ifdef CONFIG_COMPAT
  int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
  {
  	return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
1321
  EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1322
1323
1324
1325
  #endif
  
  static int snd_ctl_fasync(int fd, struct file * file, int on)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1326
  	struct snd_ctl_file *ctl;
60aa49243   Jonathan Corbet   Rationalize fasyn...
1327

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1328
  	ctl = file->private_data;
60aa49243   Jonathan Corbet   Rationalize fasyn...
1329
  	return fasync_helper(fd, file, on, &ctl->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
  }
  
  /*
   * ioctl32 compat
   */
  #ifdef CONFIG_COMPAT
  #include "control_compat.c"
  #else
  #define snd_ctl_ioctl_compat	NULL
  #endif
  
  /*
   *  INIT PART
   */
9c2e08c59   Arjan van de Ven   [PATCH] mark stru...
1344
  static const struct file_operations snd_ctl_f_ops =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1345
1346
1347
1348
1349
  {
  	.owner =	THIS_MODULE,
  	.read =		snd_ctl_read,
  	.open =		snd_ctl_open,
  	.release =	snd_ctl_release,
02f4865fa   Takashi Iwai   ALSA: core - Defi...
1350
  	.llseek =	no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
1352
1353
1354
1355
  	.poll =		snd_ctl_poll,
  	.unlocked_ioctl =	snd_ctl_ioctl,
  	.compat_ioctl =	snd_ctl_ioctl_compat,
  	.fasync =	snd_ctl_fasync,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1356
1357
1358
  /*
   * registration of the control device
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1359
  static int snd_ctl_dev_register(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1360
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1361
  	struct snd_card *card = device->device_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1362
1363
  	int err, cardnum;
  	char name[16];
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1364
1365
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1366
  	cardnum = card->number;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1367
1368
  	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1369
  	sprintf(name, "controlC%i", cardnum);
f87135f56   Clemens Ladisch   [ALSA] dynamic mi...
1370
1371
  	if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
  				       &snd_ctl_f_ops, card, name)) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1372
1373
1374
1375
1376
1377
1378
  		return err;
  	return 0;
  }
  
  /*
   * disconnection of the control device
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1379
  static int snd_ctl_dev_disconnect(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1380
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1381
  	struct snd_card *card = device->device_data;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1382
  	struct snd_ctl_file *ctl;
c461482c8   Takashi Iwai   [ALSA] Unregister...
1383
  	int err, cardnum;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1384
1385
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
c461482c8   Takashi Iwai   [ALSA] Unregister...
1386
  	cardnum = card->number;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1387
1388
  	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1389

d8009882e   Takashi Iwai   ALSA: use correct...
1390
  	read_lock(&card->ctl_files_rwlock);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1391
  	list_for_each_entry(ctl, &card->ctl_files, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1392
1393
1394
  		wake_up(&ctl->change_sleep);
  		kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
  	}
d8009882e   Takashi Iwai   ALSA: use correct...
1395
  	read_unlock(&card->ctl_files_rwlock);
c461482c8   Takashi Iwai   [ALSA] Unregister...
1396
1397
1398
1399
  
  	if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
  					 card, -1)) < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1400
1401
1402
1403
1404
1405
  	return 0;
  }
  
  /*
   * free all controls
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1406
  static int snd_ctl_dev_free(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1407
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1408
1409
  	struct snd_card *card = device->device_data;
  	struct snd_kcontrol *control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
  
  	down_write(&card->controls_rwsem);
  	while (!list_empty(&card->controls)) {
  		control = snd_kcontrol(card->controls.next);
  		snd_ctl_remove(card, control);
  	}
  	up_write(&card->controls_rwsem);
  	return 0;
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421
1422
1423
   * create control core:
   * called from init.c
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1424
  int snd_ctl_create(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1426
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1427
1428
1429
  		.dev_free = snd_ctl_dev_free,
  		.dev_register =	snd_ctl_dev_register,
  		.dev_disconnect = snd_ctl_dev_disconnect,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1430
  	};
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1431
1432
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1433
1434
  	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
  }
b9ed4f2b6   Takashi Iwai   [ALSA] Add helper...
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
  
  /*
   * Frequently used control callbacks
   */
  int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_info *uinfo)
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 1;
  	return 0;
  }
  
  EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
  
  int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,
  				struct snd_ctl_elem_info *uinfo)
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  	uinfo->count = 2;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 1;
  	return 0;
  }
  
  EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);