info.c 22.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Information interface for ALSA driver
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 *
 *   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
 *
 */

#include <linux/init.h>
#include <linux/time.h>
24
#include <linux/mm.h>
25
#include <linux/slab.h>
26
#include <linux/string.h>
L
Linus Torvalds 已提交
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>
32
#include <linux/mutex.h>
L
Linus Torvalds 已提交
33 34 35 36 37 38
#include <stdarg.h>

/*
 *
 */

39 40
#ifdef CONFIG_PROC_FS

L
Linus Torvalds 已提交
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 67 68 69
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;
}

70
static DEFINE_MUTEX(info_mutex);
L
Linus Torvalds 已提交
71

72 73 74 75
struct snd_info_private_data {
	struct snd_info_buffer *rbuffer;
	struct snd_info_buffer *wbuffer;
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
76
	void *file_private_data;
77
};
L
Linus Torvalds 已提交
78 79 80

static int snd_info_version_init(void);
static int snd_info_version_done(void);
81
static void snd_info_disconnect(struct snd_info_entry *entry);
L
Linus Torvalds 已提交
82 83


84 85 86 87 88 89 90
/* 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);
T
Takashi Iwai 已提交
91
	nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
92 93 94 95 96 97 98 99
	if (! nbuf)
		return -ENOMEM;

	buffer->buffer = nbuf;
	buffer->len = nsize;
	return 0;
}

L
Linus Torvalds 已提交
100 101 102 103 104 105 106 107 108
/**
 * 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.
 */
109
int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
L
Linus Torvalds 已提交
110 111 112
{
	va_list args;
	int len, res;
113
	int err = 0;
L
Linus Torvalds 已提交
114

115
	might_sleep();
L
Linus Torvalds 已提交
116 117 118 119
	if (buffer->stop || buffer->error)
		return 0;
	len = buffer->len - buffer->size;
	va_start(args, fmt);
120
	for (;;) {
T
Takashi Iwai 已提交
121 122 123 124
		va_list ap;
		va_copy(ap, args);
		res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, ap);
		va_end(ap);
125 126 127 128 129 130
		if (res < len)
			break;
		err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE);
		if (err < 0)
			break;
		len = buffer->len - buffer->size;
L
Linus Torvalds 已提交
131
	}
132 133 134 135
	va_end(args);

	if (err < 0)
		return err;
L
Linus Torvalds 已提交
136 137 138 139 140
	buffer->curr += res;
	buffer->size += res;
	return res;
}

141 142
EXPORT_SYMBOL(snd_iprintf);

L
Linus Torvalds 已提交
143 144 145 146
/*

 */

147 148
static struct proc_dir_entry *snd_proc_root;
struct snd_info_entry *snd_seq_root;
149 150
EXPORT_SYMBOL(snd_seq_root);

L
Linus Torvalds 已提交
151
#ifdef CONFIG_SND_OSSEMUL
152
struct snd_info_entry *snd_oss_root;
L
Linus Torvalds 已提交
153 154 155 156 157 158 159 160 161 162 163
#endif

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)
{
164
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
165
	struct snd_info_entry *entry;
166
	loff_t ret = -EINVAL, size;
L
Linus Torvalds 已提交
167 168 169

	data = file->private_data;
	entry = data->entry;
T
Takashi Iwai 已提交
170
	mutex_lock(&entry->access);
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
	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;
L
Linus Torvalds 已提交
187
		break;
188 189
	case SEEK_END:
		if (!size)
L
Linus Torvalds 已提交
190
			goto out;
191
		offset += size;
L
Linus Torvalds 已提交
192
		break;
193 194
	default:
		goto out;
L
Linus Torvalds 已提交
195
	}
196 197 198 199 200 201 202
	if (offset < 0)
		goto out;
	if (size && offset > size)
		offset = size;
	file->f_pos = offset;
	ret = offset;
 out:
T
Takashi Iwai 已提交
203
	mutex_unlock(&entry->access);
L
Linus Torvalds 已提交
204 205 206 207 208 209
	return ret;
}

static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
				   size_t count, loff_t * offset)
{
210
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
211
	struct snd_info_entry *entry;
212
	struct snd_info_buffer *buf;
L
Linus Torvalds 已提交
213 214 215 216
	size_t size = 0;
	loff_t pos;

	data = file->private_data;
217 218
	if (snd_BUG_ON(!data))
		return -ENXIO;
L
Linus Torvalds 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
	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:
238 239 240 241 242
		if (pos >= entry->size)
			return 0;
		if (entry->c.ops->read) {
			size = entry->size - pos;
			size = min(count, size);
L
Linus Torvalds 已提交
243 244
			size = entry->c.ops->read(entry,
						  data->file_private_data,
245 246
						  file, buffer, size, pos);
		}
L
Linus Torvalds 已提交
247 248 249 250 251 252 253 254 255 256
		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)
{
257
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
258
	struct snd_info_entry *entry;
259
	struct snd_info_buffer *buf;
260
	ssize_t size = 0;
L
Linus Torvalds 已提交
261 262 263
	loff_t pos;

	data = file->private_data;
264 265
	if (snd_BUG_ON(!data))
		return -ENXIO;
L
Linus Torvalds 已提交
266 267 268 269 270 271 272 273 274 275 276
	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;
C
Clemens Ladisch 已提交
277
		mutex_lock(&entry->access);
278 279 280 281 282 283 284 285
		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);
L
Linus Torvalds 已提交
286
			return -EFAULT;
287 288 289 290
		}
		buf->size = pos + count;
		mutex_unlock(&entry->access);
		size = count;
L
Linus Torvalds 已提交
291 292
		break;
	case SNDRV_INFO_CONTENT_DATA:
293 294 295
		if (entry->c.ops->write && count > 0) {
			size_t maxsize = entry->size - pos;
			count = min(count, maxsize);
L
Linus Torvalds 已提交
296 297 298
			size = entry->c.ops->write(entry,
						   data->file_private_data,
						   file, buffer, count, pos);
299
		}
L
Linus Torvalds 已提交
300 301 302 303 304 305 306 307 308
		break;
	}
	if ((ssize_t) size > 0)
		*offset = pos + size;
	return size;
}

static int snd_info_entry_open(struct inode *inode, struct file *file)
{
309 310 311
	struct snd_info_entry *entry;
	struct snd_info_private_data *data;
	struct snd_info_buffer *buffer;
L
Linus Torvalds 已提交
312 313 314
	struct proc_dir_entry *p;
	int mode, err;

315
	mutex_lock(&info_mutex);
L
Linus Torvalds 已提交
316
	p = PDE(inode);
317
	entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
318
	if (entry == NULL || ! entry->p) {
319
		mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
320 321 322 323 324 325 326 327
		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) {
328
		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
L
Linus Torvalds 已提交
329 330 331 332 333 334
		     entry->c.ops->read == NULL)) {
		    	err = -ENODEV;
		    	goto __error;
		}
	}
	if (mode == O_WRONLY || mode == O_RDWR) {
335
		if ((entry->content == SNDRV_INFO_CONTENT_DATA &&
L
Linus Torvalds 已提交
336 337 338 339 340
		     entry->c.ops->write == NULL)) {
		    	err = -ENODEV;
		    	goto __error;
		}
	}
341
	data = kzalloc(sizeof(*data), GFP_KERNEL);
L
Linus Torvalds 已提交
342 343 344 345 346 347 348 349
	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) {
350
			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
351 352
			if (buffer == NULL)
				goto __nomem;
L
Linus Torvalds 已提交
353
			data->rbuffer = buffer;
354 355 356 357
			buffer->len = PAGE_SIZE;
			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
			if (buffer->buffer == NULL)
				goto __nomem;
L
Linus Torvalds 已提交
358 359
		}
		if (mode == O_WRONLY || mode == O_RDWR) {
360
			buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
361 362
			if (buffer == NULL)
				goto __nomem;
L
Linus Torvalds 已提交
363
			data->wbuffer = buffer;
364 365 366 367
			buffer->len = PAGE_SIZE;
			buffer->buffer = kmalloc(buffer->len, GFP_KERNEL);
			if (buffer->buffer == NULL)
				goto __nomem;
L
Linus Torvalds 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380
		}
		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;
381
	mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
382 383 384
	if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
	    (mode == O_RDONLY || mode == O_RDWR)) {
		if (entry->c.text.read) {
385
			mutex_lock(&entry->access);
L
Linus Torvalds 已提交
386
			entry->c.text.read(entry, data->rbuffer);
387
			mutex_unlock(&entry->access);
L
Linus Torvalds 已提交
388 389 390 391
		}
	}
	return 0;

392 393 394 395 396 397 398 399 400 401 402
 __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;
L
Linus Torvalds 已提交
403 404 405
      __error:
	module_put(entry->module);
      __error1:
406
	mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
407 408 409 410 411
	return err;
}

static int snd_info_entry_release(struct inode *inode, struct file *file)
{
412 413
	struct snd_info_entry *entry;
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
414 415 416 417 418 419 420
	int mode;

	mode = file->f_flags & O_ACCMODE;
	data = file->private_data;
	entry = data->entry;
	switch (entry->content) {
	case SNDRV_INFO_CONTENT_TEXT:
421 422
		if (data->rbuffer) {
			kfree(data->rbuffer->buffer);
L
Linus Torvalds 已提交
423 424
			kfree(data->rbuffer);
		}
425
		if (data->wbuffer) {
L
Linus Torvalds 已提交
426 427 428 429 430 431 432 433
			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)\n",
						entry->name,
						data->wbuffer->error);
				}
			}
434
			kfree(data->wbuffer->buffer);
L
Linus Torvalds 已提交
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
			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)
{
451
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
	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;
}

475 476
static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg)
L
Linus Torvalds 已提交
477
{
478
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
	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;
}

static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
{
J
Josef Sipek 已提交
498
	struct inode *inode = file->f_path.dentry->d_inode;
499
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
	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;
}

517
static const struct file_operations snd_info_entry_operations =
L
Linus Torvalds 已提交
518
{
519 520 521 522 523 524 525 526 527
	.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,
L
Linus Torvalds 已提交
528 529 530 531 532 533
};

int __init snd_info_init(void)
{
	struct proc_dir_entry *p;

534
	p = proc_mkdir("asound", NULL);
L
Linus Torvalds 已提交
535 536 537 538 539
	if (p == NULL)
		return -ENOMEM;
	snd_proc_root = p;
#ifdef CONFIG_SND_OSSEMUL
	{
540
		struct snd_info_entry *entry;
L
Linus Torvalds 已提交
541 542 543 544 545 546 547 548 549 550 551 552
		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)
	{
553
		struct snd_info_entry *entry;
L
Linus Torvalds 已提交
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
		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();
	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();
	snd_info_version_done();
	if (snd_proc_root) {
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
579
		snd_info_free_entry(snd_seq_root);
L
Linus Torvalds 已提交
580 581
#endif
#ifdef CONFIG_SND_OSSEMUL
582
		snd_info_free_entry(snd_oss_root);
L
Linus Torvalds 已提交
583
#endif
584
		snd_remove_proc_entry(NULL, snd_proc_root);
L
Linus Torvalds 已提交
585 586 587 588 589 590 591 592 593 594 595 596 597
	}
	return 0;
}

/*

 */


/*
 * create a card proc file
 * called from init.c
 */
598
int snd_info_card_create(struct snd_card *card)
L
Linus Torvalds 已提交
599 600
{
	char str[8];
601
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
602

603 604
	if (snd_BUG_ON(!card))
		return -ENXIO;
L
Linus Torvalds 已提交
605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

	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
 */
622
int snd_info_card_register(struct snd_card *card)
L
Linus Torvalds 已提交
623 624 625
{
	struct proc_dir_entry *p;

626 627
	if (snd_BUG_ON(!card))
		return -ENXIO;
L
Linus Torvalds 已提交
628 629 630 631 632 633 634 635 636 637 638

	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;
}

639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
/*
 * 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);
}

L
Linus Torvalds 已提交
656 657 658 659
/*
 * de-register the card proc file
 * called from init.c
 */
660
void snd_info_card_disconnect(struct snd_card *card)
L
Linus Torvalds 已提交
661
{
662 663
	if (!card)
		return;
664
	mutex_lock(&info_mutex);
L
Linus Torvalds 已提交
665 666 667 668
	if (card->proc_root_link) {
		snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
		card->proc_root_link = NULL;
	}
669 670 671 672 673 674 675 676 677 678 679
	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)
{
680 681
	if (!card)
		return 0;
682 683
	snd_info_free_entry(card->proc_root);
	card->proc_root = NULL;
L
Linus Torvalds 已提交
684 685 686 687 688 689 690 691 692 693 694 695 696 697
	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.
 */
698
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
L
Linus Torvalds 已提交
699 700 701 702 703 704
{
	int c = -1;

	if (len <= 0 || buffer->stop || buffer->error)
		return 1;
	while (--len > 0) {
705
		c = buffer->buffer[buffer->curr++];
L
Linus Torvalds 已提交
706
		if (c == '\n') {
707
			if (buffer->curr >= buffer->size)
L
Linus Torvalds 已提交
708 709 710 711
				buffer->stop = 1;
			break;
		}
		*line++ = c;
712
		if (buffer->curr >= buffer->size) {
L
Linus Torvalds 已提交
713 714 715 716 717
			buffer->stop = 1;
			break;
		}
	}
	while (c != '\n' && !buffer->stop) {
718 719
		c = buffer->buffer[buffer->curr++];
		if (buffer->curr >= buffer->size)
L
Linus Torvalds 已提交
720 721 722 723 724 725
			buffer->stop = 1;
	}
	*line = '\0';
	return 0;
}

726 727
EXPORT_SYMBOL(snd_info_get_line);

L
Linus Torvalds 已提交
728
/**
729
 * snd_info_get_str - parse a string token
L
Linus Torvalds 已提交
730 731 732 733 734 735 736 737 738 739
 * @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.
 */
740
const char *snd_info_get_str(char *dest, const char *src, int len)
L
Linus Torvalds 已提交
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
{
	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;
}

764 765
EXPORT_SYMBOL(snd_info_get_str);

L
Linus Torvalds 已提交
766 767 768 769 770 771 772 773 774 775 776 777
/**
 * 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.
 */
778
static struct snd_info_entry *snd_info_create_entry(const char *name)
L
Linus Torvalds 已提交
779
{
780
	struct snd_info_entry *entry;
781
	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
L
Linus Torvalds 已提交
782 783
	if (entry == NULL)
		return NULL;
784
	entry->name = kstrdup(name, GFP_KERNEL);
L
Linus Torvalds 已提交
785 786 787 788 789 790
	if (entry->name == NULL) {
		kfree(entry);
		return NULL;
	}
	entry->mode = S_IFREG | S_IRUGO;
	entry->content = SNDRV_INFO_CONTENT_TEXT;
791
	mutex_init(&entry->access);
792 793
	INIT_LIST_HEAD(&entry->children);
	INIT_LIST_HEAD(&entry->list);
L
Linus Torvalds 已提交
794 795 796 797 798 799 800 801 802 803 804 805 806
	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.
 */
807
struct snd_info_entry *snd_info_create_module_entry(struct module * module,
L
Linus Torvalds 已提交
808
					       const char *name,
809
					       struct snd_info_entry *parent)
L
Linus Torvalds 已提交
810
{
811
	struct snd_info_entry *entry = snd_info_create_entry(name);
L
Linus Torvalds 已提交
812 813 814 815 816 817 818
	if (entry) {
		entry->module = module;
		entry->parent = parent;
	}
	return entry;
}

819 820
EXPORT_SYMBOL(snd_info_create_module_entry);

L
Linus Torvalds 已提交
821 822 823 824 825 826 827 828 829 830
/**
 * 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.
 */
831
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
L
Linus Torvalds 已提交
832
					     const char *name,
833
					     struct snd_info_entry * parent)
L
Linus Torvalds 已提交
834
{
835
	struct snd_info_entry *entry = snd_info_create_entry(name);
L
Linus Torvalds 已提交
836 837 838 839 840 841 842 843
	if (entry) {
		entry->module = card->module;
		entry->card = card;
		entry->parent = parent;
	}
	return entry;
}

844 845
EXPORT_SYMBOL(snd_info_create_card_entry);

846
static void snd_info_disconnect(struct snd_info_entry *entry)
L
Linus Torvalds 已提交
847
{
848 849
	struct list_head *p, *n;
	struct proc_dir_entry *root;
L
Linus Torvalds 已提交
850

851 852 853 854 855 856 857 858
	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;
859
	snd_BUG_ON(!root);
860 861
	snd_remove_proc_entry(root, entry->p);
	entry->p = NULL;
L
Linus Torvalds 已提交
862 863
}

864
static int snd_info_dev_free_entry(struct snd_device *device)
L
Linus Torvalds 已提交
865
{
866
	struct snd_info_entry *entry = device->device_data;
867
	snd_info_free_entry(entry);
L
Linus Torvalds 已提交
868 869 870
	return 0;
}

871
static int snd_info_dev_register_entry(struct snd_device *device)
L
Linus Torvalds 已提交
872
{
873
	struct snd_info_entry *entry = device->device_data;
874
	return snd_info_register(entry);
L
Linus Torvalds 已提交
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
}

/**
 * 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.
 */
897 898
int snd_card_proc_new(struct snd_card *card, const char *name,
		      struct snd_info_entry **entryp)
L
Linus Torvalds 已提交
899
{
900
	static struct snd_device_ops ops = {
L
Linus Torvalds 已提交
901 902
		.dev_free = snd_info_dev_free_entry,
		.dev_register =	snd_info_dev_register_entry,
903
		/* disconnect is done via snd_info_card_disconnect() */
L
Linus Torvalds 已提交
904
	};
905
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
906 907 908 909 910 911 912 913 914 915 916 917 918 919
	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;
}

920 921
EXPORT_SYMBOL(snd_card_proc_new);

L
Linus Torvalds 已提交
922 923 924 925 926 927
/**
 * snd_info_free_entry - release the info entry
 * @entry: the info entry
 *
 * Releases the info entry.  Don't call this after registered.
 */
928
void snd_info_free_entry(struct snd_info_entry * entry)
L
Linus Torvalds 已提交
929 930 931
{
	if (entry == NULL)
		return;
932 933 934 935 936
	if (entry->p) {
		mutex_lock(&info_mutex);
		snd_info_disconnect(entry);
		mutex_unlock(&info_mutex);
	}
L
Linus Torvalds 已提交
937 938 939 940 941 942
	kfree(entry->name);
	if (entry->private_free)
		entry->private_free(entry);
	kfree(entry);
}

943 944
EXPORT_SYMBOL(snd_info_free_entry);

L
Linus Torvalds 已提交
945 946 947 948 949 950 951 952
/**
 * 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.
 */
953
int snd_info_register(struct snd_info_entry * entry)
L
Linus Torvalds 已提交
954 955 956
{
	struct proc_dir_entry *root, *p = NULL;

957 958
	if (snd_BUG_ON(!entry))
		return -ENXIO;
L
Linus Torvalds 已提交
959
	root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
960
	mutex_lock(&info_mutex);
961
	p = create_proc_entry(entry->name, entry->mode, root);
L
Linus Torvalds 已提交
962
	if (!p) {
963
		mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
964 965 966 967 968 969 970
		return -ENOMEM;
	}
	if (!S_ISDIR(entry->mode))
		p->proc_fops = &snd_info_entry_operations;
	p->size = entry->size;
	p->data = entry;
	entry->p = p;
971 972
	if (entry->parent)
		list_add_tail(&entry->list, &entry->parent->children);
973
	mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
974 975 976
	return 0;
}

977 978
EXPORT_SYMBOL(snd_info_register);

L
Linus Torvalds 已提交
979 980 981 982
/*

 */

983
static struct snd_info_entry *snd_info_version_entry;
L
Linus Torvalds 已提交
984

985
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
986 987 988 989 990 991 992 993 994
{
	snd_iprintf(buffer,
		    "Advanced Linux Sound Architecture Driver Version "
		    CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
		   );
}

static int __init snd_info_version_init(void)
{
995
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

	entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
	if (entry == NULL)
		return -ENOMEM;
	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)
{
1011
	snd_info_free_entry(snd_info_version_entry);
L
Linus Torvalds 已提交
1012 1013 1014 1015
	return 0;
}

#endif /* CONFIG_PROC_FS */