Blame view

sound/core/control.c 42.3 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);
0e82e5fa9   Clemens Ladisch   ALSA: control: cl...
273
274
  static bool snd_ctl_remove_numid_conflict(struct snd_card *card,
  					  unsigned int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  {
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) {
7c7335877   Clemens Ladisch   ALSA: control: fi...
279
  		if (kctl->id.numid < card->last_numid + 1 + count &&
0e82e5fa9   Clemens Ladisch   ALSA: control: cl...
280
281
282
283
  		    kctl->id.numid + kctl->count > card->last_numid + 1) {
  		    	card->last_numid = kctl->id.numid + kctl->count - 1;
  			return true;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  	}
0e82e5fa9   Clemens Ladisch   ALSA: control: cl...
285
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
  }
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
  {
0e82e5fa9   Clemens Ladisch   ALSA: control: cl...
289
  	unsigned int iter = 100000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290

0e82e5fa9   Clemens Ladisch   ALSA: control: cl...
291
  	while (snd_ctl_remove_numid_conflict(card, count)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
296
297
  		if (--iter == 0) {
  			/* this situation is very unlikely */
  			snd_printk(KERN_ERR "unable to allocate new control numid
  ");
  			return -ENOMEM;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
  	}
  	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...
315
  int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
317
  	struct snd_ctl_elem_id id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
  	unsigned int idx;
c6077b300   Takashi Iwai   [ALSA] Fix memory...
319
  	int err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320

73e77ba02   Takashi Iwai   [ALSA] Add error ...
321
  	if (! kcontrol)
c6077b300   Takashi Iwai   [ALSA] Fix memory...
322
  		return err;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
323
324
  	if (snd_BUG_ON(!card || !kcontrol->info))
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
328
  	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
329
330
331
332
333
334
335
  		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...
336
337
  		err = -EBUSY;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
  	}
  	if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
  		up_write(&card->controls_rwsem);
c6077b300   Takashi Iwai   [ALSA] Fix memory...
341
342
  		err = -ENOMEM;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
345
346
347
348
349
350
351
  	}
  	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...
352
353
354
355
  
   error:
  	snd_ctl_free_one(kcontrol);
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
357
  EXPORT_SYMBOL(snd_ctl_add);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  /**
66b5b9722   Dimitris Papastamos   ALSA: Add snd_ctl...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
   * snd_ctl_replace - replace the control instance of the card
   * @card: the card instance
   * @kcontrol: the control instance to replace
   * @add_on_replace: add the control if not already added
   *
   * Replaces the given control.  If the given control does not exist
   * and the add_on_replace flag is set, the control is added.  If the
   * control exists, it is destroyed first.
   *
   * Returns zero if successful, or a negative error code on failure.
   *
   * It frees automatically the control which cannot be added or replaced.
   */
  int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol,
  		    bool add_on_replace)
  {
  	struct snd_ctl_elem_id id;
  	unsigned int idx;
  	struct snd_kcontrol *old;
  	int ret;
  
  	if (!kcontrol)
  		return -EINVAL;
  	if (snd_BUG_ON(!card || !kcontrol->info)) {
  		ret = -EINVAL;
  		goto error;
  	}
  	id = kcontrol->id;
  	down_write(&card->controls_rwsem);
  	old = snd_ctl_find_id(card, &id);
  	if (!old) {
  		if (add_on_replace)
  			goto add;
  		up_write(&card->controls_rwsem);
  		ret = -EINVAL;
  		goto error;
  	}
  	ret = snd_ctl_remove(card, old);
  	if (ret < 0) {
  		up_write(&card->controls_rwsem);
  		goto error;
  	}
  add:
  	if (snd_ctl_find_hole(card, kcontrol->count) < 0) {
  		up_write(&card->controls_rwsem);
  		ret = -ENOMEM;
  		goto error;
  	}
  	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;
  
  error:
  	snd_ctl_free_one(kcontrol);
  	return ret;
  }
  EXPORT_SYMBOL(snd_ctl_replace);
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
426
427
428
429
430
431
432
   * 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...
433
  int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
435
  	struct snd_ctl_elem_id id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
436
  	unsigned int idx;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
437
438
  	if (snd_BUG_ON(!card || !kcontrol))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
441
442
443
444
445
446
  	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...
447
  EXPORT_SYMBOL(snd_ctl_remove);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
448
449
450
451
452
453
454
455
456
457
  /**
   * 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...
458
  int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
459
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
460
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
462
463
464
465
466
467
468
469
470
471
472
  	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...
473
  EXPORT_SYMBOL(snd_ctl_remove_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
474
  /**
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
475
   * snd_ctl_remove_user_ctl - remove and release the unlocked user control
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
476
477
478
479
480
481
482
483
   * @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...
484
485
  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
486
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
487
488
  	struct snd_card *card = file->card;
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
489
490
491
492
493
  	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...
494
495
  		ret = -ENOENT;
  		goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496
  	}
18dd0aa5a   Clemens Ladisch   sound: snd_ctl_re...
497
498
499
500
  	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
  		ret = -EINVAL;
  		goto error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
  	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...
503
504
  			ret = -EBUSY;
  			goto error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
  		}
  	ret = snd_ctl_remove(card, kctl);
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
507
508
509
  	if (ret < 0)
  		goto error;
  	card->user_ctl_count--;
317b80817   Clemens Ladisch   sound: snd_ctl_re...
510
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511
512
513
514
515
  	up_write(&card->controls_rwsem);
  	return ret;
  }
  
  /**
3cbdd7533   Takashi Iwai   ALSA: Add snd_ctl...
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
   * snd_ctl_activate_id - activate/inactivate the control of the given id
   * @card: the card instance
   * @id: the control id to activate/inactivate
   * @active: non-zero to activate
   *
   * Finds the control instance with the given id, and activate or
   * inactivate the control together with notification, if changed.
   *
   * Returns 0 if unchanged, 1 if changed, or a negative error code on failure.
   */
  int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
  			int active)
  {
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
  	unsigned int index_offset;
  	int ret;
  
  	down_write(&card->controls_rwsem);
  	kctl = snd_ctl_find_id(card, id);
  	if (kctl == NULL) {
  		ret = -ENOENT;
  		goto unlock;
  	}
  	index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
  	vd = &kctl->vd[index_offset];
  	ret = 0;
  	if (active) {
  		if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
  			goto unlock;
  		vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
  	} else {
  		if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
  			goto unlock;
  		vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
  	}
  	ret = 1;
   unlock:
  	up_write(&card->controls_rwsem);
  	if (ret > 0)
  		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
  	return ret;
  }
  EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
564
565
566
567
568
569
570
571
   * 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...
572
573
  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
574
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
575
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
576
577
578
579
580
581
582
583
584
585
586
587
588
  
  	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...
589
  EXPORT_SYMBOL(snd_ctl_rename_id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
595
596
597
598
599
600
601
  /**
   * 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...
602
  struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
603
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
604
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
605

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
606
607
  	if (snd_BUG_ON(!card || !numid))
  		return NULL;
9244b2c30   Johannes Berg   [ALSA] alsa core:...
608
  	list_for_each_entry(kctl, &card->controls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
609
610
611
612
613
  		if (kctl->id.numid <= numid && kctl->id.numid + kctl->count > numid)
  			return kctl;
  	}
  	return NULL;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
614
  EXPORT_SYMBOL(snd_ctl_find_numid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
615
616
617
618
619
620
621
622
623
624
625
626
  /**
   * 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...
627
628
  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
629
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
630
  	struct snd_kcontrol *kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
632
633
  	if (snd_BUG_ON(!card || !id))
  		return NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
634
635
  	if (id->numid != 0)
  		return snd_ctl_find_numid(card, id->numid);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
636
  	list_for_each_entry(kctl, &card->controls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
  		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...
653
  EXPORT_SYMBOL(snd_ctl_find_id);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
654
  static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
655
656
  			     unsigned int cmd, void __user *arg)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
657
  	struct snd_ctl_card_info *info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658

ca2c09665   Takashi Iwai   [ALSA] Replace wi...
659
  	info = kzalloc(sizeof(*info), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
663
664
665
666
667
668
669
670
  	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...
671
  	if (copy_to_user(arg, info, sizeof(struct snd_ctl_card_info))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
674
675
676
677
  		kfree(info);
  		return -EFAULT;
  	}
  	kfree(info);
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
678
679
  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
680
681
  {
  	struct list_head *plist;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
682
683
684
  	struct snd_ctl_elem_list list;
  	struct snd_kcontrol *kctl;
  	struct snd_ctl_elem_id *dst, *id;
78fa2c4d2   Luca Tettamanti   ALSA: core: remov...
685
  	unsigned int offset, space, jidx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
689
690
  	
  	if (copy_from_user(&list, _list, sizeof(list)))
  		return -EFAULT;
  	offset = list.offset;
  	space = list.space;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
692
693
694
695
  	/* try limit maximum space */
  	if (space > 16384)
  		return -ENOMEM;
  	if (space > 0) {
  		/* allocate temporary buffer for atomic operation */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
696
  		dst = vmalloc(space * sizeof(struct snd_ctl_elem_id));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
  		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...
725
726
727
  		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
728
729
730
731
732
733
734
735
736
737
738
739
740
  			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...
741
742
  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
743
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
744
745
746
  	struct snd_card *card = ctl->card;
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
749
750
751
752
753
754
755
756
757
758
759
760
  	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...
761
  		snd_BUG_ON(info->access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
763
764
765
766
767
768
769
  		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...
770
  			info->owner = pid_vnr(vd->owner->pid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
771
772
773
774
775
776
777
  		} else {
  			info->owner = -1;
  		}
  	}
  	up_read(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
778
779
  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
780
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
781
  	struct snd_ctl_elem_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782
783
784
785
  	int result;
  
  	if (copy_from_user(&info, _info, sizeof(info)))
  		return -EFAULT;
646494007   Giuliano Pochini   [ALSA] make contr...
786
  	snd_power_lock(ctl->card);
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
787
  	result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
646494007   Giuliano Pochini   [ALSA] make contr...
788
789
790
  	if (result >= 0)
  		result = snd_ctl_elem_info(ctl, &info);
  	snd_power_unlock(ctl->card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
794
795
  	if (result >= 0)
  		if (copy_to_user(_info, &info, sizeof(info)))
  			return -EFAULT;
  	return result;
  }
d3bd67cdb   Takashi Iwai   ALSA: make snd_ct...
796
797
  static int snd_ctl_elem_read(struct snd_card *card,
  			     struct snd_ctl_elem_value *control)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
798
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
799
800
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
  	unsigned int index_offset;
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
802
  	int result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
803
804
805
806
807
808
809
810
  
  	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...
811
812
813
814
815
816
  		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
817
818
819
820
  	}
  	up_read(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
821
822
  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
823
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
824
  	struct snd_ctl_elem_value *control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
825
  	int result;
ef44a1ec6   Li Zefan   ALSA: sound/core:...
826
827
828
829
  
  	control = memdup_user(_control, sizeof(*control));
  	if (IS_ERR(control))
  		return PTR_ERR(control);
646494007   Giuliano Pochini   [ALSA] make contr...
830
  	snd_power_lock(card);
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
831
  	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
646494007   Giuliano Pochini   [ALSA] make contr...
832
833
834
  	if (result >= 0)
  		result = snd_ctl_elem_read(card, control);
  	snd_power_unlock(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
835
836
837
838
839
840
  	if (result >= 0)
  		if (copy_to_user(_control, control, sizeof(*control)))
  			result = -EFAULT;
  	kfree(control);
  	return result;
  }
d3bd67cdb   Takashi Iwai   ALSA: make snd_ct...
841
842
  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
843
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
844
845
  	struct snd_kcontrol *kctl;
  	struct snd_kcontrol_volatile *vd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
  	unsigned int index_offset;
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
847
  	int result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848
849
850
851
852
853
854
855
  
  	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...
856
857
858
859
  		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
860
  		} else {
8ace4f3c9   Takashi Iwai   [ALSA] Remove ind...
861
862
863
864
865
866
867
868
  			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
869
870
871
872
873
  		}
  	}
  	up_read(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
874
875
  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
876
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
877
  	struct snd_ctl_elem_value *control;
646494007   Giuliano Pochini   [ALSA] make contr...
878
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
  	int result;
ef44a1ec6   Li Zefan   ALSA: sound/core:...
880
881
882
  	control = memdup_user(_control, sizeof(*control));
  	if (IS_ERR(control))
  		return PTR_ERR(control);
646494007   Giuliano Pochini   [ALSA] make contr...
883
884
  	card = file->card;
  	snd_power_lock(card);
cbac4b0cb   Takashi Iwai   [ALSA] Cleanup un...
885
  	result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
646494007   Giuliano Pochini   [ALSA] make contr...
886
887
888
  	if (result >= 0)
  		result = snd_ctl_elem_write(card, file, control);
  	snd_power_unlock(card);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
889
890
891
892
893
894
  	if (result >= 0)
  		if (copy_to_user(_control, control, sizeof(*control)))
  			result = -EFAULT;
  	kfree(control);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
895
896
  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
897
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
898
899
900
901
  	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
902
903
904
905
906
907
908
909
910
911
912
913
914
915
  	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
916
917
918
919
920
921
  			result = 0;
  		}
  	}
  	up_write(&card->controls_rwsem);
  	return result;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
922
923
  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
924
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
925
926
927
928
  	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
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
  	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
945
946
947
948
949
950
951
952
  			result = 0;
  		}
  	}
  	up_write(&card->controls_rwsem);
  	return result;
  }
  
  struct user_element {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
953
  	struct snd_ctl_elem_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954
955
  	void *elem_data;		/* element data */
  	unsigned long elem_data_size;	/* size of element data in bytes */
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
956
957
  	void *tlv_data;			/* TLV data */
  	unsigned long tlv_data_size;	/* TLV data size */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
958
959
960
  	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...
961
962
  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
963
964
965
966
967
968
  {
  	struct user_element *ue = kcontrol->private_data;
  
  	*uinfo = ue->info;
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
969
970
  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
971
972
973
974
975
976
  {
  	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...
977
978
  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
979
980
981
982
983
984
985
986
987
  {
  	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...
988
989
990
991
992
993
994
995
996
997
998
999
  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:...
1000
1001
1002
1003
  
  		new_data = memdup_user(tlv, size);
  		if (IS_ERR(new_data))
  			return PTR_ERR(new_data);
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1004
1005
1006
1007
1008
1009
1010
  		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...
1011
1012
  		if (! ue->tlv_data_size || ! ue->tlv_data)
  			return -ENXIO;
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1013
1014
1015
1016
1017
1018
1019
  		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...
1020
  static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1021
  {
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1022
1023
1024
1025
  	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
1026
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1027
1028
  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
1029
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1030
1031
  	struct snd_card *card = file->card;
  	struct snd_kcontrol kctl, *_kctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1032
1033
1034
1035
1036
1037
1038
  	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...
1039
  	if (info->count < 1)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1040
1041
  		return -EINVAL;
  	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1042
  		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1043
1044
  				 SNDRV_CTL_ELEM_ACCESS_INACTIVE|
  				 SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
  	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...
1070
1071
1072
1073
  	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
1074
1075
  	switch (info->type) {
  	case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
  	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...
1092
  		private_size = sizeof(struct snd_aes_iec958);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1093
1094
1095
1096
1097
1098
1099
  		if (info->count != 1)
  			return -EINVAL;
  		break;
  	default:
  		return -EINVAL;
  	}
  	private_size *= info->count;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
1100
  	ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1101
1102
1103
  	if (ue == NULL)
  		return -ENOMEM;
  	ue->info = *info;
86148e84c   Takashi Iwai   [ALSA] Fix errors...
1104
  	ue->info.access = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
1106
1107
1108
1109
  	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...
1110
  		kfree(ue);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1111
1112
1113
1114
1115
1116
  		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...
1117
  	if (err < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1118
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1119
1120
1121
1122
1123
1124
1125
  
  	down_write(&card->controls_rwsem);
  	card->user_ctl_count++;
  	up_write(&card->controls_rwsem);
  
  	return 0;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1126
1127
  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
1128
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1129
  	struct snd_ctl_elem_info info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1130
1131
1132
1133
  	if (copy_from_user(&info, _info, sizeof(info)))
  		return -EFAULT;
  	return snd_ctl_elem_add(file, &info, replace);
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1134
1135
  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
1136
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1137
  	struct snd_ctl_elem_id id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1138
1139
1140
  
  	if (copy_from_user(&id, _id, sizeof(id)))
  		return -EFAULT;
f217ac59b   Clemens Ladisch   sound: snd_ctl_re...
1141
  	return snd_ctl_remove_user_ctl(file, &id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1142
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1143
  static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
  {
  	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...
1163
1164
1165
  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...
1166
  {
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1167
  	struct snd_card *card = file->card;
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1168
1169
  	struct snd_ctl_tlv tlv;
  	struct snd_kcontrol *kctl;
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1170
  	struct snd_kcontrol_volatile *vd;
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1171
1172
1173
1174
1175
  	unsigned int len;
  	int err = 0;
  
  	if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
  		return -EFAULT;
6123637fa   Clemens Ladisch   sound: control: f...
1176
  	if (tlv.length < sizeof(unsigned int) * 2)
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
  		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...
1196
  		if (vd->owner != NULL && vd->owner != file) {
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
  			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...
1219
        __kctl_end:
8aa9b586e   Jaroslav Kysela   [ALSA] Control AP...
1220
1221
  	up_read(&card->controls_rwsem);
  	return err;
42750b04c   Jaroslav Kysela   [ALSA] Control AP...
1222
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1223
1224
  static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1225
1226
  	struct snd_ctl_file *ctl;
  	struct snd_card *card;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1227
  	struct snd_kctl_ioctl *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1228
1229
1230
1231
1232
1233
  	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...
1234
1235
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236
1237
1238
1239
1240
1241
  	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...
1242
  		return snd_ctl_elem_list(card, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
1244
1245
  	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...
1246
  		return snd_ctl_elem_read_user(card, argp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
  	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...
1261
1262
1263
1264
1265
1266
  	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
1267
  	case SNDRV_CTL_IOCTL_POWER:
a381a7a66   Takashi Iwai   [ALSA] Decentrali...
1268
  		return -ENOPROTOOPT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1269
1270
1271
1272
1273
1274
1275
1276
  	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:...
1277
  	list_for_each_entry(p, &snd_control_ioctls, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1278
1279
1280
1281
1282
1283
1284
  		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...
1285
1286
  	snd_printdd("unknown ioctl = 0x%x
  ", cmd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1287
1288
  	return -ENOTTY;
  }
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1289
1290
  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
1291
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1292
  	struct snd_ctl_file *ctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1293
1294
1295
1296
  	int err = 0;
  	ssize_t result = 0;
  
  	ctl = file->private_data;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1297
1298
  	if (snd_BUG_ON(!ctl || !ctl->card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1299
1300
  	if (!ctl->subscribed)
  		return -EBADFD;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1301
  	if (count < sizeof(struct snd_ctl_event))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1302
1303
  		return -EINVAL;
  	spin_lock_irq(&ctl->read_lock);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1304
1305
1306
  	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
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
  		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...
1320
  				return -ERESTARTSYS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1321
1322
1323
1324
1325
1326
1327
1328
1329
  			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...
1330
  		if (copy_to_user(buffer, &ev, sizeof(struct snd_ctl_event))) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
1332
1333
1334
  			err = -EFAULT;
  			goto __end;
  		}
  		spin_lock_irq(&ctl->read_lock);
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1335
1336
1337
  		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
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
  	}
        __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...
1348
  	struct snd_ctl_file *ctl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
  
  	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...
1368
  	struct snd_kctl_ioctl *pn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1369

82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1370
  	pn = kzalloc(sizeof(struct snd_kctl_ioctl), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
  	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...
1384
  EXPORT_SYMBOL(snd_ctl_register_ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1385
1386
1387
1388
1389
  #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...
1390
1391
  
  EXPORT_SYMBOL(snd_ctl_register_ioctl_compat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1392
1393
1394
1395
1396
  #endif
  
  /*
   * de-register the device-specific control-ioctls.
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1397
1398
  static int _snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn,
  				     struct list_head *lists)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1399
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1400
  	struct snd_kctl_ioctl *p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1401

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1402
1403
  	if (snd_BUG_ON(!fcn))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1404
  	down_write(&snd_ioctl_rwsem);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1405
  	list_for_each_entry(p, lists, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
  		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...
1422
  EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1423
1424
1425
1426
1427
  #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...
1428
  EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1429
1430
1431
1432
  #endif
  
  static int snd_ctl_fasync(int fd, struct file * file, int on)
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1433
  	struct snd_ctl_file *ctl;
60aa49243   Jonathan Corbet   Rationalize fasyn...
1434

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1435
  	ctl = file->private_data;
60aa49243   Jonathan Corbet   Rationalize fasyn...
1436
  	return fasync_helper(fd, file, on, &ctl->fasync);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
  }
  
  /*
   * 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...
1451
  static const struct file_operations snd_ctl_f_ops =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1452
1453
1454
1455
1456
  {
  	.owner =	THIS_MODULE,
  	.read =		snd_ctl_read,
  	.open =		snd_ctl_open,
  	.release =	snd_ctl_release,
02f4865fa   Takashi Iwai   ALSA: core - Defi...
1457
  	.llseek =	no_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1458
1459
1460
1461
1462
  	.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
1463
1464
1465
  /*
   * registration of the control device
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1466
  static int snd_ctl_dev_register(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1467
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1468
  	struct snd_card *card = device->device_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1469
1470
  	int err, cardnum;
  	char name[16];
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1471
1472
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1473
  	cardnum = card->number;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1474
1475
  	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1476
  	sprintf(name, "controlC%i", cardnum);
f87135f56   Clemens Ladisch   [ALSA] dynamic mi...
1477
1478
  	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
1479
1480
1481
1482
1483
1484
1485
  		return err;
  	return 0;
  }
  
  /*
   * disconnection of the control device
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1486
  static int snd_ctl_dev_disconnect(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1487
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1488
  	struct snd_card *card = device->device_data;
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1489
  	struct snd_ctl_file *ctl;
c461482c8   Takashi Iwai   [ALSA] Unregister...
1490
  	int err, cardnum;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1491
1492
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
c461482c8   Takashi Iwai   [ALSA] Unregister...
1493
  	cardnum = card->number;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1494
1495
  	if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1496

d8009882e   Takashi Iwai   ALSA: use correct...
1497
  	read_lock(&card->ctl_files_rwlock);
9244b2c30   Johannes Berg   [ALSA] alsa core:...
1498
  	list_for_each_entry(ctl, &card->ctl_files, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1499
1500
1501
  		wake_up(&ctl->change_sleep);
  		kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
  	}
d8009882e   Takashi Iwai   ALSA: use correct...
1502
  	read_unlock(&card->ctl_files_rwlock);
c461482c8   Takashi Iwai   [ALSA] Unregister...
1503
1504
1505
1506
  
  	if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
  					 card, -1)) < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1507
1508
1509
1510
1511
1512
  	return 0;
  }
  
  /*
   * free all controls
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1513
  static int snd_ctl_dev_free(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1514
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1515
1516
  	struct snd_card *card = device->device_data;
  	struct snd_kcontrol *control;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
  
  	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
1528
1529
1530
   * create control core:
   * called from init.c
   */
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1531
  int snd_ctl_create(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1532
  {
82e9bae6f   Takashi Iwai   [ALSA] Remove xxx...
1533
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1534
1535
1536
  		.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
1537
  	};
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
1538
1539
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1540
1541
  	return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
  }
b9ed4f2b6   Takashi Iwai   [ALSA] Add helper...
1542
1543
  
  /*
9600732b6   Clemens Ladisch   ALSA: core, oxyge...
1544
   * Frequently used control callbacks/helpers
b9ed4f2b6   Takashi Iwai   [ALSA] Add helper...
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
   */
  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);
9600732b6   Clemens Ladisch   ALSA: core, oxyge...
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
  
  /**
   * snd_ctl_enum_info - fills the info structure for an enumerated control
   * @info: the structure to be filled
   * @channels: the number of the control's channels; often one
   * @items: the number of control values; also the size of @names
   * @names: an array containing the names of all control values
   *
   * Sets all required fields in @info to their appropriate values.
   * If the control's accessibility is not the default (readable and writable),
   * the caller has to fill @info->access.
   */
  int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,
  		      unsigned int items, const char *const names[])
  {
  	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  	info->count = channels;
  	info->value.enumerated.items = items;
  	if (info->value.enumerated.item >= items)
  		info->value.enumerated.item = items - 1;
  	strlcpy(info->value.enumerated.name,
  		names[info->value.enumerated.item],
  		sizeof(info->value.enumerated.name));
  	return 0;
  }
  EXPORT_SYMBOL(snd_ctl_enum_info);