info.c 18.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>
27
#include <linux/module.h>
L
Linus Torvalds 已提交
28 29 30
#include <sound/core.h>
#include <sound/minors.h>
#include <sound/info.h>
31
#include <linux/utsname.h>
L
Linus Torvalds 已提交
32
#include <linux/proc_fs.h>
33
#include <linux/mutex.h>
L
Linus Torvalds 已提交
34 35 36 37 38 39
#include <stdarg.h>

/*
 *
 */

40 41
#ifdef CONFIG_PROC_FS

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

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

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

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

/*

 */

87
static struct snd_info_entry *snd_proc_root;
88
struct snd_info_entry *snd_seq_root;
89 90
EXPORT_SYMBOL(snd_seq_root);

L
Linus Torvalds 已提交
91
#ifdef CONFIG_SND_OSSEMUL
92
struct snd_info_entry *snd_oss_root;
L
Linus Torvalds 已提交
93 94
#endif

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
static int alloc_info_private(struct snd_info_entry *entry,
			      struct snd_info_private_data **ret)
{
	struct snd_info_private_data *data;

	if (!entry || !entry->p)
		return -ENODEV;
	if (!try_module_get(entry->module))
		return -EFAULT;
	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (!data) {
		module_put(entry->module);
		return -ENOMEM;
	}
	data->entry = entry;
	*ret = data;
	return 0;
}

static bool valid_pos(loff_t pos, size_t count)
{
	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
		return false;
	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
		return false;
	return true;
}

/*
 * file ops for binary proc files
 */
L
Linus Torvalds 已提交
126 127
static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
{
128
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
129
	struct snd_info_entry *entry;
130
	loff_t ret = -EINVAL, size;
L
Linus Torvalds 已提交
131 132 133

	data = file->private_data;
	entry = data->entry;
T
Takashi Iwai 已提交
134
	mutex_lock(&entry->access);
135
	if (entry->c.ops->llseek) {
136 137 138 139 140
		offset = entry->c.ops->llseek(entry,
					      data->file_private_data,
					      file, offset, orig);
		goto out;
	}
141 142

	size = entry->size;
143 144 145 146 147
	switch (orig) {
	case SEEK_SET:
		break;
	case SEEK_CUR:
		offset += file->f_pos;
L
Linus Torvalds 已提交
148
		break;
149 150
	case SEEK_END:
		if (!size)
L
Linus Torvalds 已提交
151
			goto out;
152
		offset += size;
L
Linus Torvalds 已提交
153
		break;
154 155
	default:
		goto out;
L
Linus Torvalds 已提交
156
	}
157 158 159 160 161 162 163
	if (offset < 0)
		goto out;
	if (size && offset > size)
		offset = size;
	file->f_pos = offset;
	ret = offset;
 out:
T
Takashi Iwai 已提交
164
	mutex_unlock(&entry->access);
L
Linus Torvalds 已提交
165 166 167 168 169 170
	return ret;
}

static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
				   size_t count, loff_t * offset)
{
171 172 173
	struct snd_info_private_data *data = file->private_data;
	struct snd_info_entry *entry = data->entry;
	size_t size;
L
Linus Torvalds 已提交
174 175 176
	loff_t pos;

	pos = *offset;
177
	if (!valid_pos(pos, count))
L
Linus Torvalds 已提交
178
		return -EIO;
179 180 181 182 183 184
	if (pos >= entry->size)
		return 0;
	size = entry->size - pos;
	size = min(count, size);
	size = entry->c.ops->read(entry, data->file_private_data,
				  file, buffer, size, pos);
L
Linus Torvalds 已提交
185 186 187 188 189 190 191 192
	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)
{
193 194
	struct snd_info_private_data *data = file->private_data;
	struct snd_info_entry *entry = data->entry;
195
	ssize_t size = 0;
L
Linus Torvalds 已提交
196 197 198
	loff_t pos;

	pos = *offset;
199
	if (!valid_pos(pos, count))
L
Linus Torvalds 已提交
200
		return -EIO;
201 202 203 204 205
	if (count > 0) {
		size_t maxsize = entry->size - pos;
		count = min(count, maxsize);
		size = entry->c.ops->write(entry, data->file_private_data,
					   file, buffer, count, pos);
L
Linus Torvalds 已提交
206
	}
207
	if (size > 0)
L
Linus Torvalds 已提交
208 209 210 211
		*offset = pos + size;
	return size;
}

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
static unsigned int snd_info_entry_poll(struct file *file, poll_table *wait)
{
	struct snd_info_private_data *data = file->private_data;
	struct snd_info_entry *entry = data->entry;
	unsigned int mask = 0;

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

static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg)
{
	struct snd_info_private_data *data = file->private_data;
	struct snd_info_entry *entry = data->entry;

	if (!entry->c.ops->ioctl)
		return -ENOTTY;
	return entry->c.ops->ioctl(entry, data->file_private_data,
				   file, cmd, arg);
}

static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
L
Linus Torvalds 已提交
242
{
243 244
	struct inode *inode = file_inode(file);
	struct snd_info_private_data *data;
245
	struct snd_info_entry *entry;
246 247 248 249 250 251 252 253 254 255 256 257 258 259

	data = file->private_data;
	if (data == NULL)
		return 0;
	entry = data->entry;
	if (!entry->c.ops->mmap)
		return -ENXIO;
	return entry->c.ops->mmap(entry, data->file_private_data,
				  inode, file, vma);
}

static int snd_info_entry_open(struct inode *inode, struct file *file)
{
	struct snd_info_entry *entry = PDE_DATA(inode);
260
	struct snd_info_private_data *data;
L
Linus Torvalds 已提交
261 262
	int mode, err;

263
	mutex_lock(&info_mutex);
264 265 266 267
	err = alloc_info_private(entry, &data);
	if (err < 0)
		goto unlock;

L
Linus Torvalds 已提交
268
	mode = file->f_flags & O_ACCMODE;
269 270 271 272
	if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
	    ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
		err = -ENODEV;
		goto error;
L
Linus Torvalds 已提交
273
	}
274 275 276 277 278

	if (entry->c.ops->open) {
		err = entry->c.ops->open(entry, mode, &data->file_private_data);
		if (err < 0)
			goto error;
L
Linus Torvalds 已提交
279
	}
280

L
Linus Torvalds 已提交
281
	file->private_data = data;
282
	mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
283 284
	return 0;

285
 error:
286
	kfree(data);
L
Linus Torvalds 已提交
287
	module_put(entry->module);
288
 unlock:
289
	mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
290 291 292 293 294
	return err;
}

static int snd_info_entry_release(struct inode *inode, struct file *file)
{
295 296
	struct snd_info_private_data *data = file->private_data;
	struct snd_info_entry *entry = data->entry;
L
Linus Torvalds 已提交
297

298 299 300
	if (entry->c.ops->release)
		entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
				      data->file_private_data);
L
Linus Torvalds 已提交
301 302 303 304 305
	module_put(entry->module);
	kfree(data);
	return 0;
}

306
static const struct file_operations snd_info_entry_operations =
L
Linus Torvalds 已提交
307
{
308 309 310 311 312 313 314 315 316 317
	.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 已提交
318

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
/*
 * file ops for text proc files
 */
static ssize_t snd_info_text_entry_write(struct file *file,
					 const char __user *buffer,
					 size_t count, loff_t *offset)
{
	struct seq_file *m = file->private_data;
	struct snd_info_private_data *data = m->private;
	struct snd_info_entry *entry = data->entry;
	struct snd_info_buffer *buf;
	loff_t pos;
	size_t next;
	int err = 0;

	pos = *offset;
	if (!valid_pos(pos, count))
		return -EIO;
	next = pos + count;
	mutex_lock(&entry->access);
	buf = data->wbuffer;
	if (!buf) {
		data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
		if (!buf) {
			err = -ENOMEM;
			goto error;
		}
L
Linus Torvalds 已提交
346
	}
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
	if (next > buf->len) {
		char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next),
				      GFP_KERNEL | __GFP_ZERO);
		if (!nbuf) {
			err = -ENOMEM;
			goto error;
		}
		buf->buffer = nbuf;
		buf->len = PAGE_ALIGN(next);
	}
	if (copy_from_user(buf->buffer + pos, buffer, count)) {
		err = -EFAULT;
		goto error;
	}
	buf->size = next;
 error:
	mutex_unlock(&entry->access);
	if (err < 0)
		return err;
	*offset = next;
	return count;
L
Linus Torvalds 已提交
368 369
}

370
static int snd_info_seq_show(struct seq_file *seq, void *p)
L
Linus Torvalds 已提交
371
{
372 373
	struct snd_info_private_data *data = seq->private;
	struct snd_info_entry *entry = data->entry;
L
Linus Torvalds 已提交
374

375 376 377
	if (entry->c.text.read) {
		data->rbuffer->buffer = (char *)seq; /* XXX hack! */
		entry->c.text.read(entry, data->rbuffer);
L
Linus Torvalds 已提交
378
	}
379
	return 0;
L
Linus Torvalds 已提交
380 381
}

382
static int snd_info_text_entry_open(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
383
{
384
	struct snd_info_entry *entry = PDE_DATA(inode);
385
	struct snd_info_private_data *data;
386
	int err;
L
Linus Torvalds 已提交
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 423 424 425 426 427 428 429 430
	mutex_lock(&info_mutex);
	err = alloc_info_private(entry, &data);
	if (err < 0)
		goto unlock;

	data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
	if (!data->rbuffer) {
		err = -ENOMEM;
		goto error;
	}
	if (entry->size)
		err = single_open_size(file, snd_info_seq_show, data,
				       entry->size);
	else
		err = single_open(file, snd_info_seq_show, data);
	if (err < 0)
		goto error;
	mutex_unlock(&info_mutex);
	return 0;

 error:
	kfree(data->rbuffer);
	kfree(data);
	module_put(entry->module);
 unlock:
	mutex_unlock(&info_mutex);
	return err;
}

static int snd_info_text_entry_release(struct inode *inode, struct file *file)
{
	struct seq_file *m = file->private_data;
	struct snd_info_private_data *data = m->private;
	struct snd_info_entry *entry = data->entry;

	if (data->wbuffer && entry->c.text.write)
		entry->c.text.write(entry, data->wbuffer);

	single_release(inode, file);
	kfree(data->rbuffer);
	if (data->wbuffer) {
		kfree(data->wbuffer->buffer);
		kfree(data->wbuffer);
L
Linus Torvalds 已提交
431
	}
432 433 434 435

	module_put(entry->module);
	kfree(data);
	return 0;
L
Linus Torvalds 已提交
436 437
}

438
static const struct file_operations snd_info_text_entry_ops =
L
Linus Torvalds 已提交
439
{
440
	.owner =		THIS_MODULE,
441 442 443 444 445
	.open =			snd_info_text_entry_open,
	.release =		snd_info_text_entry_release,
	.write =		snd_info_text_entry_write,
	.llseek =		seq_lseek,
	.read =			seq_read,
L
Linus Torvalds 已提交
446 447
};

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
static struct snd_info_entry *create_subdir(struct module *mod,
					    const char *name)
{
	struct snd_info_entry *entry;

	entry = snd_info_create_module_entry(mod, name, NULL);
	if (!entry)
		return NULL;
	entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
	if (snd_info_register(entry) < 0) {
		snd_info_free_entry(entry);
		return NULL;
	}
	return entry;
}

464 465
static struct snd_info_entry *snd_info_create_entry(const char *name);

L
Linus Torvalds 已提交
466 467
int __init snd_info_init(void)
{
468 469
	snd_proc_root = snd_info_create_entry("asound");
	if (!snd_proc_root)
L
Linus Torvalds 已提交
470
		return -ENOMEM;
471 472 473 474
	snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
	snd_proc_root->p = proc_mkdir("asound", NULL);
	if (!snd_proc_root->p)
		goto error;
L
Linus Torvalds 已提交
475
#ifdef CONFIG_SND_OSSEMUL
476 477 478
	snd_oss_root = create_subdir(THIS_MODULE, "oss");
	if (!snd_oss_root)
		goto error;
L
Linus Torvalds 已提交
479
#endif
T
Takashi Iwai 已提交
480
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
481 482 483
	snd_seq_root = create_subdir(THIS_MODULE, "seq");
	if (!snd_seq_root)
		goto error;
L
Linus Torvalds 已提交
484 485 486 487 488 489
#endif
	snd_info_version_init();
	snd_minor_info_init();
	snd_minor_info_oss_init();
	snd_card_info_init();
	return 0;
490 491

 error:
492
	snd_info_free_entry(snd_proc_root);
493
	return -ENOMEM;
L
Linus Torvalds 已提交
494 495 496 497
}

int __exit snd_info_done(void)
{
498
	snd_info_free_entry(snd_proc_root);
L
Linus Torvalds 已提交
499 500 501 502 503 504 505
	return 0;
}

/*
 * create a card proc file
 * called from init.c
 */
506
int snd_info_card_create(struct snd_card *card)
L
Linus Torvalds 已提交
507 508
{
	char str[8];
509
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
510

511 512
	if (snd_BUG_ON(!card))
		return -ENXIO;
L
Linus Torvalds 已提交
513 514

	sprintf(str, "card%i", card->number);
515 516
	entry = create_subdir(card->module, str);
	if (!entry)
L
Linus Torvalds 已提交
517 518 519 520 521 522 523 524 525
		return -ENOMEM;
	card->proc_root = entry;
	return 0;
}

/*
 * register the card proc file
 * called from init.c
 */
526
int snd_info_card_register(struct snd_card *card)
L
Linus Torvalds 已提交
527 528 529
{
	struct proc_dir_entry *p;

530 531
	if (snd_BUG_ON(!card))
		return -ENXIO;
L
Linus Torvalds 已提交
532 533 534 535

	if (!strcmp(card->id, card->proc_root->name))
		return 0;

536
	p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
L
Linus Torvalds 已提交
537 538 539 540 541 542
	if (p == NULL)
		return -ENOMEM;
	card->proc_root_link = p;
	return 0;
}

543 544 545 546 547 548 549
/*
 * called on card->id change
 */
void snd_info_card_id_change(struct snd_card *card)
{
	mutex_lock(&info_mutex);
	if (card->proc_root_link) {
550
		proc_remove(card->proc_root_link);
551 552 553 554
		card->proc_root_link = NULL;
	}
	if (strcmp(card->id, card->proc_root->name))
		card->proc_root_link = proc_symlink(card->id,
555
						    snd_proc_root->p,
556 557 558 559
						    card->proc_root->name);
	mutex_unlock(&info_mutex);
}

L
Linus Torvalds 已提交
560 561 562 563
/*
 * de-register the card proc file
 * called from init.c
 */
564
void snd_info_card_disconnect(struct snd_card *card)
L
Linus Torvalds 已提交
565
{
566 567
	if (!card)
		return;
568
	mutex_lock(&info_mutex);
569 570
	proc_remove(card->proc_root_link);
	card->proc_root_link = NULL;
571 572 573 574 575 576 577 578 579 580 581
	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)
{
582 583
	if (!card)
		return 0;
584 585
	snd_info_free_entry(card->proc_root);
	card->proc_root = NULL;
L
Linus Torvalds 已提交
586 587 588 589 590 591 592 593
	return 0;
}


/**
 * snd_info_get_line - read one line from the procfs buffer
 * @buffer: the procfs buffer
 * @line: the buffer to store
594
 * @len: the max. buffer size
L
Linus Torvalds 已提交
595 596 597
 *
 * Reads one line from the buffer and stores the string.
 *
598
 * Return: Zero if successful, or 1 if error or EOF.
L
Linus Torvalds 已提交
599
 */
600
int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
L
Linus Torvalds 已提交
601 602 603
{
	int c = -1;

604 605
	if (snd_BUG_ON(!buffer || !buffer->buffer))
		return 1;
L
Linus Torvalds 已提交
606 607
	if (len <= 0 || buffer->stop || buffer->error)
		return 1;
608
	while (!buffer->stop) {
609
		c = buffer->buffer[buffer->curr++];
610
		if (buffer->curr >= buffer->size)
L
Linus Torvalds 已提交
611
			buffer->stop = 1;
612
		if (c == '\n')
L
Linus Torvalds 已提交
613
			break;
614
		if (len > 1) {
615 616
			len--;
			*line++ = c;
L
Linus Torvalds 已提交
617 618 619 620 621 622
		}
	}
	*line = '\0';
	return 0;
}

623 624
EXPORT_SYMBOL(snd_info_get_line);

L
Linus Torvalds 已提交
625
/**
626
 * snd_info_get_str - parse a string token
L
Linus Torvalds 已提交
627 628 629 630 631 632 633
 * @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.
 *
634
 * Return: The updated pointer of the original string so that
L
Linus Torvalds 已提交
635 636
 * it can be used for the next call.
 */
637
const char *snd_info_get_str(char *dest, const char *src, int len)
L
Linus Torvalds 已提交
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
{
	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;
}

661 662
EXPORT_SYMBOL(snd_info_get_str);

L
Linus Torvalds 已提交
663 664 665 666 667 668 669 670 671 672
/**
 * 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().
 *
673
 * Return: The pointer of the new instance, or %NULL on failure.
L
Linus Torvalds 已提交
674
 */
675
static struct snd_info_entry *snd_info_create_entry(const char *name)
L
Linus Torvalds 已提交
676
{
677
	struct snd_info_entry *entry;
678
	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
L
Linus Torvalds 已提交
679 680
	if (entry == NULL)
		return NULL;
681
	entry->name = kstrdup(name, GFP_KERNEL);
L
Linus Torvalds 已提交
682 683 684 685 686 687
	if (entry->name == NULL) {
		kfree(entry);
		return NULL;
	}
	entry->mode = S_IFREG | S_IRUGO;
	entry->content = SNDRV_INFO_CONTENT_TEXT;
688
	mutex_init(&entry->access);
689 690
	INIT_LIST_HEAD(&entry->children);
	INIT_LIST_HEAD(&entry->list);
L
Linus Torvalds 已提交
691 692 693 694 695 696 697 698 699 700 701
	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.
 *
702
 * Return: The pointer of the new instance, or %NULL on failure.
L
Linus Torvalds 已提交
703
 */
704
struct snd_info_entry *snd_info_create_module_entry(struct module * module,
L
Linus Torvalds 已提交
705
					       const char *name,
706
					       struct snd_info_entry *parent)
L
Linus Torvalds 已提交
707
{
708
	struct snd_info_entry *entry = snd_info_create_entry(name);
L
Linus Torvalds 已提交
709 710 711 712 713 714 715
	if (entry) {
		entry->module = module;
		entry->parent = parent;
	}
	return entry;
}

716 717
EXPORT_SYMBOL(snd_info_create_module_entry);

L
Linus Torvalds 已提交
718 719 720 721 722 723 724 725
/**
 * 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.
 *
726
 * Return: The pointer of the new instance, or %NULL on failure.
L
Linus Torvalds 已提交
727
 */
728
struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
L
Linus Torvalds 已提交
729
					     const char *name,
730
					     struct snd_info_entry * parent)
L
Linus Torvalds 已提交
731
{
732
	struct snd_info_entry *entry = snd_info_create_entry(name);
L
Linus Torvalds 已提交
733 734 735 736 737 738 739 740
	if (entry) {
		entry->module = card->module;
		entry->card = card;
		entry->parent = parent;
	}
	return entry;
}

741 742
EXPORT_SYMBOL(snd_info_create_card_entry);

743
static void snd_info_disconnect(struct snd_info_entry *entry)
L
Linus Torvalds 已提交
744
{
745
	struct snd_info_entry *p, *n;
L
Linus Torvalds 已提交
746

747
	if (!entry->p)
748
		return;
749 750
	list_for_each_entry_safe(p, n, &entry->children, list)
		snd_info_disconnect(p);
751
	list_del_init(&entry->list);
752
	proc_remove(entry->p);
753
	entry->p = NULL;
L
Linus Torvalds 已提交
754 755 756 757 758 759
}

/**
 * snd_info_free_entry - release the info entry
 * @entry: the info entry
 *
760
 * Releases the info entry.
L
Linus Torvalds 已提交
761
 */
762
void snd_info_free_entry(struct snd_info_entry * entry)
L
Linus Torvalds 已提交
763
{
764 765 766
	struct snd_info_entry *p, *n;

	if (!entry)
L
Linus Torvalds 已提交
767
		return;
768 769 770 771 772
	if (entry->p) {
		mutex_lock(&info_mutex);
		snd_info_disconnect(entry);
		mutex_unlock(&info_mutex);
	}
773 774 775 776 777

	/* free all children at first */
	list_for_each_entry_safe(p, n, &entry->children, list)
		snd_info_free_entry(p);

L
Linus Torvalds 已提交
778 779 780 781 782 783
	kfree(entry->name);
	if (entry->private_free)
		entry->private_free(entry);
	kfree(entry);
}

784 785
EXPORT_SYMBOL(snd_info_free_entry);

L
Linus Torvalds 已提交
786 787 788 789 790 791
/**
 * snd_info_register - register the info entry
 * @entry: the info entry
 *
 * Registers the proc info entry.
 *
792
 * Return: Zero if successful, or a negative error code on failure.
L
Linus Torvalds 已提交
793
 */
794
int snd_info_register(struct snd_info_entry * entry)
L
Linus Torvalds 已提交
795 796 797
{
	struct proc_dir_entry *root, *p = NULL;

798 799
	if (snd_BUG_ON(!entry))
		return -ENXIO;
800
	root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
801
	mutex_lock(&info_mutex);
802 803 804 805 806 807 808
	if (S_ISDIR(entry->mode)) {
		p = proc_mkdir_mode(entry->name, entry->mode, root);
		if (!p) {
			mutex_unlock(&info_mutex);
			return -ENOMEM;
		}
	} else {
809 810 811 812 813
		const struct file_operations *ops;
		if (entry->content == SNDRV_INFO_CONTENT_DATA)
			ops = &snd_info_entry_operations;
		else
			ops = &snd_info_text_entry_ops;
814
		p = proc_create_data(entry->name, entry->mode, root,
815
				     ops, entry);
816 817 818 819
		if (!p) {
			mutex_unlock(&info_mutex);
			return -ENOMEM;
		}
820
		proc_set_size(p, entry->size);
L
Linus Torvalds 已提交
821 822
	}
	entry->p = p;
823 824
	if (entry->parent)
		list_add_tail(&entry->list, &entry->parent->children);
825
	mutex_unlock(&info_mutex);
L
Linus Torvalds 已提交
826 827 828
	return 0;
}

829 830
EXPORT_SYMBOL(snd_info_register);

L
Linus Torvalds 已提交
831 832 833 834
/*

 */

835
static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
L
Linus Torvalds 已提交
836 837
{
	snd_iprintf(buffer,
838 839
		    "Advanced Linux Sound Architecture Driver Version k%s.\n",
		    init_utsname()->release);
L
Linus Torvalds 已提交
840 841 842 843
}

static int __init snd_info_version_init(void)
{
844
	struct snd_info_entry *entry;
L
Linus Torvalds 已提交
845 846 847 848 849 850 851 852 853 854 855 856 857

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

#endif /* CONFIG_PROC_FS */