Blame view

sound/core/info.c 23 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  Information interface for ALSA driver
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
  #include <linux/init.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/time.h>
27ac792ca   Andrea Righi   PAGE_ALIGN(): cor...
23
  #include <linux/mm.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
543537bd9   Paulo Marques   [PATCH] create a ...
25
  #include <linux/string.h>
da155d5b4   Paul Gortmaker   sound: Add module...
26
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
  #include <sound/core.h>
  #include <sound/minors.h>
  #include <sound/info.h>
  #include <sound/version.h>
  #include <linux/proc_fs.h>
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
32
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
  #include <stdarg.h>
  
  /*
   *
   */
e28563cce   Takashi Iwai   [ALSA] Optimize f...
38
  #ifdef CONFIG_PROC_FS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  int snd_info_check_reserved_words(const char *str)
  {
  	static char *reserved[] =
  	{
  		"version",
  		"meminfo",
  		"memdebug",
  		"detect",
  		"devices",
  		"oss",
  		"cards",
  		"timers",
  		"synth",
  		"pcm",
  		"seq",
  		NULL
  	};
  	char **xstr = reserved;
  
  	while (*xstr) {
  		if (!strcmp(*xstr, str))
  			return 0;
  		xstr++;
  	}
  	if (!strncmp(str, "card", 4))
  		return 0;
  	return 1;
  }
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
67
  static DEFINE_MUTEX(info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68

24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
69
70
71
72
  struct snd_info_private_data {
  	struct snd_info_buffer *rbuffer;
  	struct snd_info_buffer *wbuffer;
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  	void *file_private_data;
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
74
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
  
  static int snd_info_version_init(void);
  static int snd_info_version_done(void);
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
78
  static void snd_info_disconnect(struct snd_info_entry *entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
80
81
82
83
84
85
86
  /* resize the proc r/w buffer */
  static int resize_info_buffer(struct snd_info_buffer *buffer,
  			      unsigned int nsize)
  {
  	char *nbuf;
  
  	nsize = PAGE_ALIGN(nsize);
9983aa62c   Takashi Iwai   ALSA: info - Use ...
87
  	nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
88
89
  	if (! nbuf)
  		return -ENOMEM;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
90
91
92
93
  	buffer->buffer = nbuf;
  	buffer->len = nsize;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
101
102
  /**
   * snd_iprintf - printf on the procfs buffer
   * @buffer: the procfs buffer
   * @fmt: the printf format
   *
   * Outputs the string on the procfs buffer just like printf().
   *
   * Returns the size of output string.
   */
4f7454a99   Takashi Iwai   ALSA: Add const p...
103
  int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
  {
  	va_list args;
  	int len, res;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
107
  	int err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108

f001c3acf   Takashi Iwai   [ALSA] Insert mig...
109
  	might_sleep();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
  	if (buffer->stop || buffer->error)
  		return 0;
  	len = buffer->len - buffer->size;
  	va_start(args, fmt);
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
114
  	for (;;) {
dbedca39f   Takashi Iwai   [ALSA] Fix re-use...
115
116
117
118
  		va_list ap;
  		va_copy(ap, args);
  		res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
  		va_end(ap);
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
119
120
121
122
123
124
  		if (res < len)
  			break;
  		err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
  		if (err < 0)
  			break;
  		len = buffer->len - buffer->size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
  	}
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
126
127
128
129
  	va_end(args);
  
  	if (err < 0)
  		return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
132
133
  	buffer->curr += res;
  	buffer->size += res;
  	return res;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
134
  EXPORT_SYMBOL(snd_iprintf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
137
  /*
  
   */
6581f4e74   Takashi Iwai   [ALSA] Remove zer...
138
139
  static struct proc_dir_entry *snd_proc_root;
  struct snd_info_entry *snd_seq_root;
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
140
  EXPORT_SYMBOL(snd_seq_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
  #ifdef CONFIG_SND_OSSEMUL
6581f4e74   Takashi Iwai   [ALSA] Remove zer...
142
  struct snd_info_entry *snd_oss_root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
149
150
151
152
  static void snd_remove_proc_entry(struct proc_dir_entry *parent,
  				  struct proc_dir_entry *de)
  {
  	if (de)
  		remove_proc_entry(de->name, parent);
  }
  
  static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
153
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  	struct snd_info_entry *entry;
73029e0ff   Takashi Iwai   ALSA: info - Impl...
155
  	loff_t ret = -EINVAL, size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
  
  	data = file->private_data;
  	entry = data->entry;
5b5cd553e   Takashi Iwai   ALSA: info - Remo...
159
  	mutex_lock(&entry->access);
73029e0ff   Takashi Iwai   ALSA: info - Impl...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  	if (entry->content == SNDRV_INFO_CONTENT_DATA &&
  	    entry->c.ops->llseek) {
  		offset = entry->c.ops->llseek(entry,
  					      data->file_private_data,
  					      file, offset, orig);
  		goto out;
  	}
  	if (entry->content == SNDRV_INFO_CONTENT_DATA)
  		size = entry->size;
  	else
  		size = 0;
  	switch (orig) {
  	case SEEK_SET:
  		break;
  	case SEEK_CUR:
  		offset += file->f_pos;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  		break;
73029e0ff   Takashi Iwai   ALSA: info - Impl...
177
178
  	case SEEK_END:
  		if (!size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179
  			goto out;
73029e0ff   Takashi Iwai   ALSA: info - Impl...
180
  		offset += size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  		break;
73029e0ff   Takashi Iwai   ALSA: info - Impl...
182
183
  	default:
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  	}
73029e0ff   Takashi Iwai   ALSA: info - Impl...
185
186
187
188
189
190
191
  	if (offset < 0)
  		goto out;
  	if (size && offset > size)
  		offset = size;
  	file->f_pos = offset;
  	ret = offset;
   out:
5b5cd553e   Takashi Iwai   ALSA: info - Remo...
192
  	mutex_unlock(&entry->access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
196
197
198
  	return ret;
  }
  
  static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
  				   size_t count, loff_t * offset)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
199
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
  	struct snd_info_entry *entry;
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
201
  	struct snd_info_buffer *buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
  	size_t size = 0;
  	loff_t pos;
  
  	data = file->private_data;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
206
207
  	if (snd_BUG_ON(!data))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  	pos = *offset;
  	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
  		return -EIO;
  	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
  		return -EIO;
  	entry = data->entry;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_TEXT:
  		buf = data->rbuffer;
  		if (buf == NULL)
  			return -EIO;
  		if (pos >= buf->size)
  			return 0;
  		size = buf->size - pos;
  		size = min(count, size);
  		if (copy_to_user(buffer, buf->buffer + pos, size))
  			return -EFAULT;
  		break;
  	case SNDRV_INFO_CONTENT_DATA:
d97e1b782   Takashi Iwai   ALSA: info - Chec...
227
228
229
230
231
  		if (pos >= entry->size)
  			return 0;
  		if (entry->c.ops->read) {
  			size = entry->size - pos;
  			size = min(count, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
  			size = entry->c.ops->read(entry,
  						  data->file_private_data,
d97e1b782   Takashi Iwai   ALSA: info - Chec...
234
235
  						  file, buffer, size, pos);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
241
242
243
244
245
  		break;
  	}
  	if ((ssize_t) size > 0)
  		*offset = pos + size;
  	return size;
  }
  
  static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
  				    size_t count, loff_t * offset)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
246
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  	struct snd_info_entry *entry;
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
248
  	struct snd_info_buffer *buf;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
249
  	ssize_t size = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
251
252
  	loff_t pos;
  
  	data = file->private_data;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
253
254
  	if (snd_BUG_ON(!data))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
257
258
259
260
261
262
263
264
265
  	entry = data->entry;
  	pos = *offset;
  	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
  		return -EIO;
  	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
  		return -EIO;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_TEXT:
  		buf = data->wbuffer;
  		if (buf == NULL)
  			return -EIO;
f4a747f15   Clemens Ladisch   [ALSA] fix a wron...
266
  		mutex_lock(&entry->access);
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
267
268
269
270
271
272
273
274
  		if (pos + count >= buf->len) {
  			if (resize_info_buffer(buf, pos + count)) {
  				mutex_unlock(&entry->access);
  				return -ENOMEM;
  			}
  		}
  		if (copy_from_user(buf->buffer + pos, buffer, count)) {
  			mutex_unlock(&entry->access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  			return -EFAULT;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
276
277
278
279
  		}
  		buf->size = pos + count;
  		mutex_unlock(&entry->access);
  		size = count;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
  		break;
  	case SNDRV_INFO_CONTENT_DATA:
d97e1b782   Takashi Iwai   ALSA: info - Chec...
282
283
284
  		if (entry->c.ops->write && count > 0) {
  			size_t maxsize = entry->size - pos;
  			count = min(count, maxsize);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
  			size = entry->c.ops->write(entry,
  						   data->file_private_data,
  						   file, buffer, count, pos);
d97e1b782   Takashi Iwai   ALSA: info - Chec...
288
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
290
291
292
293
294
295
296
297
  		break;
  	}
  	if ((ssize_t) size > 0)
  		*offset = pos + size;
  	return size;
  }
  
  static int snd_info_entry_open(struct inode *inode, struct file *file)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
298
299
300
  	struct snd_info_entry *entry;
  	struct snd_info_private_data *data;
  	struct snd_info_buffer *buffer;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
302
  	struct proc_dir_entry *p;
  	int mode, err;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
303
  	mutex_lock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  	p = PDE(inode);
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
305
  	entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
306
  	if (entry == NULL || ! entry->p) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
307
  		mutex_unlock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
314
315
  		return -ENODEV;
  	}
  	if (!try_module_get(entry->module)) {
  		err = -EFAULT;
  		goto __error1;
  	}
  	mode = file->f_flags & O_ACCMODE;
  	if (mode == O_RDONLY || mode == O_RDWR) {
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
316
  		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
317
318
319
320
321
322
  		     entry->c.ops->read == NULL)) {
  		    	err = -ENODEV;
  		    	goto __error;
  		}
  	}
  	if (mode == O_WRONLY || mode == O_RDWR) {
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
323
  		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
327
328
  		     entry->c.ops->write == NULL)) {
  		    	err = -ENODEV;
  		    	goto __error;
  		}
  	}
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
329
  	data = kzalloc(sizeof(*data), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
331
332
333
334
335
336
337
  	if (data == NULL) {
  		err = -ENOMEM;
  		goto __error;
  	}
  	data->entry = entry;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_TEXT:
  		if (mode == O_RDONLY || mode == O_RDWR) {
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
338
  			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
339
340
  			if (buffer == NULL)
  				goto __nomem;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
  			data->rbuffer = buffer;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
342
343
344
345
  			buffer->len = PAGE_SIZE;
  			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
  			if (buffer->buffer == NULL)
  				goto __nomem;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
  		}
  		if (mode == O_WRONLY || mode == O_RDWR) {
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
348
  			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
349
350
  			if (buffer == NULL)
  				goto __nomem;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
  			data->wbuffer = buffer;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
352
353
354
355
  			buffer->len = PAGE_SIZE;
  			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
  			if (buffer->buffer == NULL)
  				goto __nomem;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
360
361
362
363
364
365
366
367
368
  		}
  		break;
  	case SNDRV_INFO_CONTENT_DATA:	/* data */
  		if (entry->c.ops->open) {
  			if ((err = entry->c.ops->open(entry, mode,
  						      &data->file_private_data)) < 0) {
  				kfree(data);
  				goto __error;
  			}
  		}
  		break;
  	}
  	file->private_data = data;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
369
  	mutex_unlock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
372
  	if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
  	    (mode == O_RDONLY || mode == O_RDWR)) {
  		if (entry->c.text.read) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
373
  			mutex_lock(&entry->access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
  			entry->c.text.read(entry, data->rbuffer);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
375
  			mutex_unlock(&entry->access);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
  		}
  	}
  	return 0;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
379
380
381
382
383
384
385
386
387
388
389
   __nomem:
  	if (data->rbuffer) {
  		kfree(data->rbuffer->buffer);
  		kfree(data->rbuffer);
  	}
  	if (data->wbuffer) {
  		kfree(data->wbuffer->buffer);
  		kfree(data->wbuffer);
  	}
  	kfree(data);
  	err = -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
392
        __error:
  	module_put(entry->module);
        __error1:
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
393
  	mutex_unlock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
  	return err;
  }
  
  static int snd_info_entry_release(struct inode *inode, struct file *file)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
399
400
  	struct snd_info_entry *entry;
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
404
405
406
407
  	int mode;
  
  	mode = file->f_flags & O_ACCMODE;
  	data = file->private_data;
  	entry = data->entry;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_TEXT:
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
408
409
  		if (data->rbuffer) {
  			kfree(data->rbuffer->buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
410
411
  			kfree(data->rbuffer);
  		}
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
412
  		if (data->wbuffer) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
417
418
419
420
421
  			if (entry->c.text.write) {
  				entry->c.text.write(entry, data->wbuffer);
  				if (data->wbuffer->error) {
  					snd_printk(KERN_WARNING "data write error to %s (%i)
  ",
  						entry->name,
  						data->wbuffer->error);
  				}
  			}
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
422
  			kfree(data->wbuffer->buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
  			kfree(data->wbuffer);
  		}
  		break;
  	case SNDRV_INFO_CONTENT_DATA:
  		if (entry->c.ops->release)
  			entry->c.ops->release(entry, mode,
  					      data->file_private_data);
  		break;
  	}
  	module_put(entry->module);
  	kfree(data);
  	return 0;
  }
  
  static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
439
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  	struct snd_info_entry *entry;
  	unsigned int mask;
  
  	data = file->private_data;
  	if (data == NULL)
  		return 0;
  	entry = data->entry;
  	mask = 0;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_DATA:
  		if (entry->c.ops->poll)
  			return entry->c.ops->poll(entry,
  						  data->file_private_data,
  						  file, wait);
  		if (entry->c.ops->read)
  			mask |= POLLIN | POLLRDNORM;
  		if (entry->c.ops->write)
  			mask |= POLLOUT | POLLWRNORM;
  		break;
  	}
  	return mask;
  }
d99e98891   Ingo Molnar   [ALSA] Remove BKL...
462
463
  static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
  				unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
465
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
  	struct snd_info_entry *entry;
  
  	data = file->private_data;
  	if (data == NULL)
  		return 0;
  	entry = data->entry;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_DATA:
  		if (entry->c.ops->ioctl)
  			return entry->c.ops->ioctl(entry,
  						   data->file_private_data,
  						   file, cmd, arg);
  		break;
  	}
  	return -ENOTTY;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
483
  static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
  {
7bc563239   Josef Sipek   [PATCH] struct pa...
484
  	struct inode *inode = file->f_path.dentry->d_inode;
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
485
  	struct snd_info_private_data *data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
  	struct snd_info_entry *entry;
  
  	data = file->private_data;
  	if (data == NULL)
  		return 0;
  	entry = data->entry;
  	switch (entry->content) {
  	case SNDRV_INFO_CONTENT_DATA:
  		if (entry->c.ops->mmap)
  			return entry->c.ops->mmap(entry,
  						  data->file_private_data,
  						  inode, file, vma);
  		break;
  	}
  	return -ENXIO;
  }
9c2e08c59   Arjan van de Ven   [PATCH] mark stru...
502
  static const struct file_operations snd_info_entry_operations =
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503
  {
d99e98891   Ingo Molnar   [ALSA] Remove BKL...
504
505
506
507
508
509
510
511
512
  	.owner =		THIS_MODULE,
  	.llseek =		snd_info_entry_llseek,
  	.read =			snd_info_entry_read,
  	.write =		snd_info_entry_write,
  	.poll =			snd_info_entry_poll,
  	.unlocked_ioctl =	snd_info_entry_ioctl,
  	.mmap =			snd_info_entry_mmap,
  	.open =			snd_info_entry_open,
  	.release =		snd_info_entry_release,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
515
516
  int __init snd_info_init(void)
  {
  	struct proc_dir_entry *p;
e55d92b92   Al Viro   get rid of create...
517
  	p = proc_mkdir("asound", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
519
520
521
522
  	if (p == NULL)
  		return -ENOMEM;
  	snd_proc_root = p;
  #ifdef CONFIG_SND_OSSEMUL
  	{
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
523
  		struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
528
529
530
531
532
533
534
535
  		if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
  			return -ENOMEM;
  		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
  		if (snd_info_register(entry) < 0) {
  			snd_info_free_entry(entry);
  			return -ENOMEM;
  		}
  		snd_oss_root = entry;
  	}
  #endif
  #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
  	{
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
536
  		struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
541
542
543
544
545
546
547
  		if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
  			return -ENOMEM;
  		entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
  		if (snd_info_register(entry) < 0) {
  			snd_info_free_entry(entry);
  			return -ENOMEM;
  		}
  		snd_seq_root = entry;
  	}
  #endif
  	snd_info_version_init();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
548
549
550
551
552
553
554
555
556
557
558
  	snd_minor_info_init();
  	snd_minor_info_oss_init();
  	snd_card_info_init();
  	return 0;
  }
  
  int __exit snd_info_done(void)
  {
  	snd_card_info_done();
  	snd_minor_info_oss_done();
  	snd_minor_info_done();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
559
560
561
  	snd_info_version_done();
  	if (snd_proc_root) {
  #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
562
  		snd_info_free_entry(snd_seq_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
  #endif
  #ifdef CONFIG_SND_OSSEMUL
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
565
  		snd_info_free_entry(snd_oss_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
  #endif
c74c120a2   Alexey Dobriyan   proc: remove proc...
567
  		snd_remove_proc_entry(NULL, snd_proc_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
578
579
580
  	}
  	return 0;
  }
  
  /*
  
   */
  
  
  /*
   * create a card proc file
   * called from init.c
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
581
  int snd_info_card_create(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582
583
  {
  	char str[8];
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
584
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
585

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
586
587
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
  
  	sprintf(str, "card%i", card->number);
  	if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
  		return -ENOMEM;
  	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
  	if (snd_info_register(entry) < 0) {
  		snd_info_free_entry(entry);
  		return -ENOMEM;
  	}
  	card->proc_root = entry;
  	return 0;
  }
  
  /*
   * register the card proc file
   * called from init.c
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
605
  int snd_info_card_register(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
  {
  	struct proc_dir_entry *p;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
608
609
  	if (snd_BUG_ON(!card))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
610
611
612
613
614
615
616
617
618
619
620
621
  
  	if (!strcmp(card->id, card->proc_root->name))
  		return 0;
  
  	p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
  	if (p == NULL)
  		return -ENOMEM;
  	card->proc_root_link = p;
  	return 0;
  }
  
  /*
c2eb9c4ea   Jaroslav Kysela   ALSA: when card i...
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
   * called on card->id change
   */
  void snd_info_card_id_change(struct snd_card *card)
  {
  	mutex_lock(&info_mutex);
  	if (card->proc_root_link) {
  		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
  		card->proc_root_link = NULL;
  	}
  	if (strcmp(card->id, card->proc_root->name))
  		card->proc_root_link = proc_symlink(card->id,
  						    snd_proc_root,
  						    card->proc_root->name);
  	mutex_unlock(&info_mutex);
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
639
640
641
   * de-register the card proc file
   * called from init.c
   */
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
642
  void snd_info_card_disconnect(struct snd_card *card)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
643
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
644
645
  	if (!card)
  		return;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
646
  	mutex_lock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647
648
649
650
  	if (card->proc_root_link) {
  		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
  		card->proc_root_link = NULL;
  	}
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
651
652
653
654
655
656
657
658
659
660
661
  	if (card->proc_root)
  		snd_info_disconnect(card->proc_root);
  	mutex_unlock(&info_mutex);
  }
  
  /*
   * release the card proc file resources
   * called from init.c
   */
  int snd_info_card_free(struct snd_card *card)
  {
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
662
663
  	if (!card)
  		return 0;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
664
665
  	snd_info_free_entry(card->proc_root);
  	card->proc_root = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  	return 0;
  }
  
  
  /**
   * snd_info_get_line - read one line from the procfs buffer
   * @buffer: the procfs buffer
   * @line: the buffer to store
   * @len: the max. buffer size - 1
   *
   * Reads one line from the buffer and stores the string.
   *
   * Returns zero if successful, or 1 if error or EOF.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
680
  int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
686
  {
  	int c = -1;
  
  	if (len <= 0 || buffer->stop || buffer->error)
  		return 1;
  	while (--len > 0) {
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
687
  		c = buffer->buffer[buffer->curr++];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
  		if (c == '
  ') {
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
690
  			if (buffer->curr >= buffer->size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  				buffer->stop = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
694
  			break;
  		}
  		*line++ = c;
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
695
  		if (buffer->curr >= buffer->size) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
701
  			buffer->stop = 1;
  			break;
  		}
  	}
  	while (c != '
  ' && !buffer->stop) {
7e4eeec8a   Takashi Iwai   [ALSA] Make buffe...
702
703
  		c = buffer->buffer[buffer->curr++];
  		if (buffer->curr >= buffer->size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
  			buffer->stop = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
706
707
708
  	}
  	*line = '\0';
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
709
  EXPORT_SYMBOL(snd_info_get_line);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
  /**
856def8a4   Henrik Kretzschmar   [ALSA] typo-fix a...
711
   * snd_info_get_str - parse a string token
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
712
713
714
715
716
717
718
719
720
721
   * @dest: the buffer to store the string token
   * @src: the original string
   * @len: the max. length of token - 1
   *
   * Parses the original string and copy a token to the given
   * string buffer.
   *
   * Returns the updated pointer of the original string so that
   * it can be used for the next call.
   */
4f7454a99   Takashi Iwai   ALSA: Add const p...
722
  const char *snd_info_get_str(char *dest, const char *src, int len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
  {
  	int c;
  
  	while (*src == ' ' || *src == '\t')
  		src++;
  	if (*src == '"' || *src == '\'') {
  		c = *src++;
  		while (--len > 0 && *src && *src != c) {
  			*dest++ = *src++;
  		}
  		if (*src == c)
  			src++;
  	} else {
  		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
  			*dest++ = *src++;
  		}
  	}
  	*dest = 0;
  	while (*src == ' ' || *src == '\t')
  		src++;
  	return src;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
745
  EXPORT_SYMBOL(snd_info_get_str);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
746
747
748
749
750
751
752
753
754
755
756
757
  /**
   * snd_info_create_entry - create an info entry
   * @name: the proc file name
   *
   * Creates an info entry with the given file name and initializes as
   * the default state.
   *
   * Usually called from other functions such as
   * snd_info_create_card_entry().
   *
   * Returns the pointer of the new instance, or NULL on failure.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
758
  static struct snd_info_entry *snd_info_create_entry(const char *name)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
760
  	struct snd_info_entry *entry;
ca2c09665   Takashi Iwai   [ALSA] Replace wi...
761
  	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
762
763
  	if (entry == NULL)
  		return NULL;
543537bd9   Paulo Marques   [PATCH] create a ...
764
  	entry->name = kstrdup(name, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
765
766
767
768
769
770
  	if (entry->name == NULL) {
  		kfree(entry);
  		return NULL;
  	}
  	entry->mode = S_IFREG | S_IRUGO;
  	entry->content = SNDRV_INFO_CONTENT_TEXT;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
771
  	mutex_init(&entry->access);
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
772
773
  	INIT_LIST_HEAD(&entry->children);
  	INIT_LIST_HEAD(&entry->list);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
775
776
777
778
779
780
781
782
783
784
785
786
  	return entry;
  }
  
  /**
   * snd_info_create_module_entry - create an info entry for the given module
   * @module: the module pointer
   * @name: the file name
   * @parent: the parent directory
   *
   * Creates a new info entry and assigns it to the given module.
   *
   * Returns the pointer of the new instance, or NULL on failure.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
787
  struct snd_info_entry *snd_info_create_module_entry(struct module * module,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
  					       const char *name,
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
789
  					       struct snd_info_entry *parent)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
791
  	struct snd_info_entry *entry = snd_info_create_entry(name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
793
794
795
796
797
  	if (entry) {
  		entry->module = module;
  		entry->parent = parent;
  	}
  	return entry;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
798
  EXPORT_SYMBOL(snd_info_create_module_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
801
802
803
804
805
806
807
808
  /**
   * snd_info_create_card_entry - create an info entry for the given card
   * @card: the card instance
   * @name: the file name
   * @parent: the parent directory
   *
   * Creates a new info entry and assigns it to the given card.
   *
   * Returns the pointer of the new instance, or NULL on failure.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
809
  struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
810
  					     const char *name,
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
811
  					     struct snd_info_entry * parent)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
812
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
813
  	struct snd_info_entry *entry = snd_info_create_entry(name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
816
817
818
819
820
  	if (entry) {
  		entry->module = card->module;
  		entry->card = card;
  		entry->parent = parent;
  	}
  	return entry;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
821
  EXPORT_SYMBOL(snd_info_create_card_entry);
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
822
  static void snd_info_disconnect(struct snd_info_entry *entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
823
  {
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
824
825
  	struct list_head *p, *n;
  	struct proc_dir_entry *root;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826

746d4a02e   Takashi Iwai   [ALSA] Fix discon...
827
828
829
830
831
832
833
834
  	list_for_each_safe(p, n, &entry->children) {
  		snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
  	}
  
  	if (! entry->p)
  		return;
  	list_del_init(&entry->list);
  	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
835
  	snd_BUG_ON(!root);
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
836
837
  	snd_remove_proc_entry(root, entry->p);
  	entry->p = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
  }
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
839
  static int snd_info_dev_free_entry(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
841
  	struct snd_info_entry *entry = device->device_data;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
842
  	snd_info_free_entry(entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
  	return 0;
  }
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
845
  static int snd_info_dev_register_entry(struct snd_device *device)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
846
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
847
  	struct snd_info_entry *entry = device->device_data;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
848
  	return snd_info_register(entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
  }
  
  /**
   * snd_card_proc_new - create an info entry for the given card
   * @card: the card instance
   * @name: the file name
   * @entryp: the pointer to store the new info entry
   *
   * Creates a new info entry and assigns it to the given card.
   * Unlike snd_info_create_card_entry(), this function registers the
   * info entry as an ALSA device component, so that it can be
   * unregistered/released without explicit call.
   * Also, you don't have to register this entry via snd_info_register(),
   * since this will be registered by snd_card_register() automatically.
   *
   * The parent is assumed as card->proc_root.
   *
   * For releasing this entry, use snd_device_free() instead of
   * snd_info_free_entry(). 
   *
   * Returns zero if successful, or a negative error code on failure.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
871
872
  int snd_card_proc_new(struct snd_card *card, const char *name,
  		      struct snd_info_entry **entryp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
874
  	static struct snd_device_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
875
876
  		.dev_free = snd_info_dev_free_entry,
  		.dev_register =	snd_info_dev_register_entry,
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
877
  		/* disconnect is done via snd_info_card_disconnect() */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
  	};
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
879
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880
881
882
883
884
885
886
887
888
889
890
891
892
  	int err;
  
  	entry = snd_info_create_card_entry(card, name, card->proc_root);
  	if (! entry)
  		return -ENOMEM;
  	if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
  		snd_info_free_entry(entry);
  		return err;
  	}
  	if (entryp)
  		*entryp = entry;
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
893
  EXPORT_SYMBOL(snd_card_proc_new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
896
897
898
899
  /**
   * snd_info_free_entry - release the info entry
   * @entry: the info entry
   *
   * Releases the info entry.  Don't call this after registered.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
900
  void snd_info_free_entry(struct snd_info_entry * entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
902
903
  {
  	if (entry == NULL)
  		return;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
904
905
906
907
908
  	if (entry->p) {
  		mutex_lock(&info_mutex);
  		snd_info_disconnect(entry);
  		mutex_unlock(&info_mutex);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
909
910
911
912
913
  	kfree(entry->name);
  	if (entry->private_free)
  		entry->private_free(entry);
  	kfree(entry);
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
914
  EXPORT_SYMBOL(snd_info_free_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
917
918
919
920
921
922
  /**
   * snd_info_register - register the info entry
   * @entry: the info entry
   *
   * Registers the proc info entry.
   *
   * Returns zero if successful, or a negative error code on failure.
   */
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
923
  int snd_info_register(struct snd_info_entry * entry)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
924
925
  {
  	struct proc_dir_entry *root, *p = NULL;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
926
927
  	if (snd_BUG_ON(!entry))
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
  	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
929
  	mutex_lock(&info_mutex);
99b762338   Alexey Dobriyan   proc 2/2: remove ...
930
  	p = create_proc_entry(entry->name, entry->mode, root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
  	if (!p) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
932
  		mutex_unlock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
933
934
  		return -ENOMEM;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
935
936
937
938
939
  	if (!S_ISDIR(entry->mode))
  		p->proc_fops = &snd_info_entry_operations;
  	p->size = entry->size;
  	p->data = entry;
  	entry->p = p;
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
940
941
  	if (entry->parent)
  		list_add_tail(&entry->list, &entry->parent->children);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
942
  	mutex_unlock(&info_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
943
944
  	return 0;
  }
c0d3fb39e   Takashi Iwai   [ALSA] Clean up E...
945
  EXPORT_SYMBOL(snd_info_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
946
947
948
  /*
  
   */
6581f4e74   Takashi Iwai   [ALSA] Remove zer...
949
  static struct snd_info_entry *snd_info_version_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
950

24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
951
  static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
952
953
954
955
956
957
958
959
960
961
  {
  	snd_iprintf(buffer,
  		    "Advanced Linux Sound Architecture Driver Version "
  		    CONFIG_SND_VERSION CONFIG_SND_DATE ".
  "
  		   );
  }
  
  static int __init snd_info_version_init(void)
  {
24c1f9318   Takashi Iwai   [ALSA] Remove xxx...
962
  	struct snd_info_entry *entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
964
965
966
  
  	entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
  	if (entry == NULL)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
968
969
970
971
972
973
974
975
976
977
  	entry->c.text.read = snd_info_version_read;
  	if (snd_info_register(entry) < 0) {
  		snd_info_free_entry(entry);
  		return -ENOMEM;
  	}
  	snd_info_version_entry = entry;
  	return 0;
  }
  
  static int __exit snd_info_version_done(void)
  {
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
978
  	snd_info_free_entry(snd_info_version_entry);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
979
980
981
982
  	return 0;
  }
  
  #endif /* CONFIG_PROC_FS */