1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Information interface for ALSA driver 4 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 5 */ 6 7#include <linux/init.h> 8#include <linux/time.h> 9#include <linux/mm.h> 10#include <linux/slab.h> 11#include <linux/string.h> 12#include <linux/module.h> 13#include <sound/core.h> 14#include <sound/minors.h> 15#include <sound/info.h> 16#include <linux/utsname.h> 17#include <linux/proc_fs.h> 18#include <linux/mutex.h> 19#include <stdarg.h> 20 21int snd_info_check_reserved_words(const char *str) 22{ 23 static const char * const reserved[] = 24 { 25 "version", 26 "meminfo", 27 "memdebug", 28 "detect", 29 "devices", 30 "oss", 31 "cards", 32 "timers", 33 "synth", 34 "pcm", 35 "seq", 36 NULL 37 }; 38 const char * const *xstr = reserved; 39 40 while (*xstr) { 41 if (!strcmp(*xstr, str)) 42 return 0; 43 xstr++; 44 } 45 if (!strncmp(str, "card", 4)) 46 return 0; 47 return 1; 48} 49 50static DEFINE_MUTEX(info_mutex); 51 52struct snd_info_private_data { 53 struct snd_info_buffer *rbuffer; 54 struct snd_info_buffer *wbuffer; 55 struct snd_info_entry *entry; 56 void *file_private_data; 57}; 58 59static int snd_info_version_init(void); 60static void snd_info_clear_entries(struct snd_info_entry *entry); 61 62/* 63 64 */ 65 66static struct snd_info_entry *snd_proc_root; 67struct snd_info_entry *snd_seq_root; 68EXPORT_SYMBOL(snd_seq_root); 69 70#ifdef CONFIG_SND_OSSEMUL 71struct snd_info_entry *snd_oss_root; 72#endif 73 74static int alloc_info_private(struct snd_info_entry *entry, 75 struct snd_info_private_data **ret) 76{ 77 struct snd_info_private_data *data; 78 79 if (!entry || !entry->p) 80 return -ENODEV; 81 if (!try_module_get(entry->module)) 82 return -EFAULT; 83 data = kzalloc(sizeof(*data), GFP_KERNEL); 84 if (!data) { 85 module_put(entry->module); 86 return -ENOMEM; 87 } 88 data->entry = entry; 89 *ret = data; 90 return 0; 91} 92 93static bool valid_pos(loff_t pos, size_t count) 94{ 95 if (pos < 0 || (long) pos != pos || (ssize_t) count < 0) 96 return false; 97 if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos) 98 return false; 99 return true; 100} 101 102/* 103 * file ops for binary proc files 104 */ 105static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig) 106{ 107 struct snd_info_private_data *data; 108 struct snd_info_entry *entry; 109 loff_t ret = -EINVAL, size; 110 111 data = file->private_data; 112 entry = data->entry; 113 mutex_lock(&entry->access); 114 if (entry->c.ops->llseek) { 115 ret = entry->c.ops->llseek(entry, 116 data->file_private_data, 117 file, offset, orig); 118 goto out; 119 } 120 121 size = entry->size; 122 switch (orig) { 123 case SEEK_SET: 124 break; 125 case SEEK_CUR: 126 offset += file->f_pos; 127 break; 128 case SEEK_END: 129 if (!size) 130 goto out; 131 offset += size; 132 break; 133 default: 134 goto out; 135 } 136 if (offset < 0) 137 goto out; 138 if (size && offset > size) 139 offset = size; 140 file->f_pos = offset; 141 ret = offset; 142 out: 143 mutex_unlock(&entry->access); 144 return ret; 145} 146 147static ssize_t snd_info_entry_read(struct file *file, char __user *buffer, 148 size_t count, loff_t * offset) 149{ 150 struct snd_info_private_data *data = file->private_data; 151 struct snd_info_entry *entry = data->entry; 152 size_t size; 153 loff_t pos; 154 155 pos = *offset; 156 if (!valid_pos(pos, count)) 157 return -EIO; 158 if (pos >= entry->size) 159 return 0; 160 size = entry->size - pos; 161 size = min(count, size); 162 size = entry->c.ops->read(entry, data->file_private_data, 163 file, buffer, size, pos); 164 if ((ssize_t) size > 0) 165 *offset = pos + size; 166 return size; 167} 168 169static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer, 170 size_t count, loff_t * offset) 171{ 172 struct snd_info_private_data *data = file->private_data; 173 struct snd_info_entry *entry = data->entry; 174 ssize_t size = 0; 175 loff_t pos; 176 177 pos = *offset; 178 if (!valid_pos(pos, count)) 179 return -EIO; 180 if (count > 0) { 181 size_t maxsize = entry->size - pos; 182 count = min(count, maxsize); 183 size = entry->c.ops->write(entry, data->file_private_data, 184 file, buffer, count, pos); 185 } 186 if (size > 0) 187 *offset = pos + size; 188 return size; 189} 190 191static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait) 192{ 193 struct snd_info_private_data *data = file->private_data; 194 struct snd_info_entry *entry = data->entry; 195 __poll_t mask = 0; 196 197 if (entry->c.ops->poll) 198 return entry->c.ops->poll(entry, 199 data->file_private_data, 200 file, wait); 201 if (entry->c.ops->read) 202 mask |= EPOLLIN | EPOLLRDNORM; 203 if (entry->c.ops->write) 204 mask |= EPOLLOUT | EPOLLWRNORM; 205 return mask; 206} 207 208static long snd_info_entry_ioctl(struct file *file, unsigned int cmd, 209 unsigned long arg) 210{ 211 struct snd_info_private_data *data = file->private_data; 212 struct snd_info_entry *entry = data->entry; 213 214 if (!entry->c.ops->ioctl) 215 return -ENOTTY; 216 return entry->c.ops->ioctl(entry, data->file_private_data, 217 file, cmd, arg); 218} 219 220static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma) 221{ 222 struct inode *inode = file_inode(file); 223 struct snd_info_private_data *data; 224 struct snd_info_entry *entry; 225 226 data = file->private_data; 227 if (data == NULL) 228 return 0; 229 entry = data->entry; 230 if (!entry->c.ops->mmap) 231 return -ENXIO; 232 return entry->c.ops->mmap(entry, data->file_private_data, 233 inode, file, vma); 234} 235 236static int snd_info_entry_open(struct inode *inode, struct file *file) 237{ 238 struct snd_info_entry *entry = PDE_DATA(inode); 239 struct snd_info_private_data *data; 240 int mode, err; 241 242 mutex_lock(&info_mutex); 243 err = alloc_info_private(entry, &data); 244 if (err < 0) 245 goto unlock; 246 247 mode = file->f_flags & O_ACCMODE; 248 if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) || 249 ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) { 250 err = -ENODEV; 251 goto error; 252 } 253 254 if (entry->c.ops->open) { 255 err = entry->c.ops->open(entry, mode, &data->file_private_data); 256 if (err < 0) 257 goto error; 258 } 259 260 file->private_data = data; 261 mutex_unlock(&info_mutex); 262 return 0; 263 264 error: 265 kfree(data); 266 module_put(entry->module); 267 unlock: 268 mutex_unlock(&info_mutex); 269 return err; 270} 271 272static int snd_info_entry_release(struct inode *inode, struct file *file) 273{ 274 struct snd_info_private_data *data = file->private_data; 275 struct snd_info_entry *entry = data->entry; 276 277 if (entry->c.ops->release) 278 entry->c.ops->release(entry, file->f_flags & O_ACCMODE, 279 data->file_private_data); 280 module_put(entry->module); 281 kfree(data); 282 return 0; 283} 284 285static const struct proc_ops snd_info_entry_operations = 286{ 287 .proc_lseek = snd_info_entry_llseek, 288 .proc_read = snd_info_entry_read, 289 .proc_write = snd_info_entry_write, 290 .proc_poll = snd_info_entry_poll, 291 .proc_ioctl = snd_info_entry_ioctl, 292 .proc_mmap = snd_info_entry_mmap, 293 .proc_open = snd_info_entry_open, 294 .proc_release = snd_info_entry_release, 295}; 296 297/* 298 * file ops for text proc files 299 */ 300static ssize_t snd_info_text_entry_write(struct file *file, 301 const char __user *buffer, 302 size_t count, loff_t *offset) 303{ 304 struct seq_file *m = file->private_data; 305 struct snd_info_private_data *data = m->private; 306 struct snd_info_entry *entry = data->entry; 307 struct snd_info_buffer *buf; 308 loff_t pos; 309 size_t next; 310 int err = 0; 311 312 if (!entry->c.text.write) 313 return -EIO; 314 pos = *offset; 315 if (!valid_pos(pos, count)) 316 return -EIO; 317 next = pos + count; 318 /* don't handle too large text inputs */ 319 if (next > 16 * 1024) 320 return -EIO; 321 mutex_lock(&entry->access); 322 buf = data->wbuffer; 323 if (!buf) { 324 data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); 325 if (!buf) { 326 err = -ENOMEM; 327 goto error; 328 } 329 } 330 if (next > buf->len) { 331 char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); 332 if (!nbuf) { 333 err = -ENOMEM; 334 goto error; 335 } 336 kvfree(buf->buffer); 337 buf->buffer = nbuf; 338 buf->len = PAGE_ALIGN(next); 339 } 340 if (copy_from_user(buf->buffer + pos, buffer, count)) { 341 err = -EFAULT; 342 goto error; 343 } 344 buf->size = next; 345 error: 346 mutex_unlock(&entry->access); 347 if (err < 0) 348 return err; 349 *offset = next; 350 return count; 351} 352 353static int snd_info_seq_show(struct seq_file *seq, void *p) 354{ 355 struct snd_info_private_data *data = seq->private; 356 struct snd_info_entry *entry = data->entry; 357 358 if (!entry->c.text.read) { 359 return -EIO; 360 } else { 361 data->rbuffer->buffer = (char *)seq; /* XXX hack! */ 362 entry->c.text.read(entry, data->rbuffer); 363 } 364 return 0; 365} 366 367static int snd_info_text_entry_open(struct inode *inode, struct file *file) 368{ 369 struct snd_info_entry *entry = PDE_DATA(inode); 370 struct snd_info_private_data *data; 371 int err; 372 373 mutex_lock(&info_mutex); 374 err = alloc_info_private(entry, &data); 375 if (err < 0) 376 goto unlock; 377 378 data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); 379 if (!data->rbuffer) { 380 err = -ENOMEM; 381 goto error; 382 } 383 if (entry->size) 384 err = single_open_size(file, snd_info_seq_show, data, 385 entry->size); 386 else 387 err = single_open(file, snd_info_seq_show, data); 388 if (err < 0) 389 goto error; 390 mutex_unlock(&info_mutex); 391 return 0; 392 393 error: 394 kfree(data->rbuffer); 395 kfree(data); 396 module_put(entry->module); 397 unlock: 398 mutex_unlock(&info_mutex); 399 return err; 400} 401 402static int snd_info_text_entry_release(struct inode *inode, struct file *file) 403{ 404 struct seq_file *m = file->private_data; 405 struct snd_info_private_data *data = m->private; 406 struct snd_info_entry *entry = data->entry; 407 408 if (data->wbuffer && entry->c.text.write) 409 entry->c.text.write(entry, data->wbuffer); 410 411 single_release(inode, file); 412 kfree(data->rbuffer); 413 if (data->wbuffer) { 414 kvfree(data->wbuffer->buffer); 415 kfree(data->wbuffer); 416 } 417 418 module_put(entry->module); 419 kfree(data); 420 return 0; 421} 422 423static const struct proc_ops snd_info_text_entry_ops = 424{ 425 .proc_open = snd_info_text_entry_open, 426 .proc_release = snd_info_text_entry_release, 427 .proc_write = snd_info_text_entry_write, 428 .proc_lseek = seq_lseek, 429 .proc_read = seq_read, 430}; 431 432static struct snd_info_entry *create_subdir(struct module *mod, 433 const char *name) 434{ 435 struct snd_info_entry *entry; 436 437 entry = snd_info_create_module_entry(mod, name, NULL); 438 if (!entry) 439 return NULL; 440 entry->mode = S_IFDIR | 0555; 441 if (snd_info_register(entry) < 0) { 442 snd_info_free_entry(entry); 443 return NULL; 444 } 445 return entry; 446} 447 448static struct snd_info_entry * 449snd_info_create_entry(const char *name, struct snd_info_entry *parent, 450 struct module *module); 451 452int __init snd_info_init(void) 453{ 454 snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE); 455 if (!snd_proc_root) 456 return -ENOMEM; 457 snd_proc_root->mode = S_IFDIR | 0555; 458 snd_proc_root->p = proc_mkdir("asound", NULL); 459 if (!snd_proc_root->p) 460 goto error; 461#ifdef CONFIG_SND_OSSEMUL 462 snd_oss_root = create_subdir(THIS_MODULE, "oss"); 463 if (!snd_oss_root) 464 goto error; 465#endif 466#if IS_ENABLED(CONFIG_SND_SEQUENCER) 467 snd_seq_root = create_subdir(THIS_MODULE, "seq"); 468 if (!snd_seq_root) 469 goto error; 470#endif 471 if (snd_info_version_init() < 0 || 472 snd_minor_info_init() < 0 || 473 snd_minor_info_oss_init() < 0 || 474 snd_card_info_init() < 0 || 475 snd_info_minor_register() < 0) 476 goto error; 477 return 0; 478 479 error: 480 snd_info_free_entry(snd_proc_root); 481 return -ENOMEM; 482} 483 484int __exit snd_info_done(void) 485{ 486 snd_info_free_entry(snd_proc_root); 487 return 0; 488} 489 490static void snd_card_id_read(struct snd_info_entry *entry, 491 struct snd_info_buffer *buffer) 492{ 493 struct snd_card *card = entry->private_data; 494 495 snd_iprintf(buffer, "%s\n", card->id); 496} 497 498/* 499 * create a card proc file 500 * called from init.c 501 */ 502int snd_info_card_create(struct snd_card *card) 503{ 504 char str[8]; 505 struct snd_info_entry *entry; 506 507 if (snd_BUG_ON(!card)) 508 return -ENXIO; 509 510 sprintf(str, "card%i", card->number); 511 entry = create_subdir(card->module, str); 512 if (!entry) 513 return -ENOMEM; 514 card->proc_root = entry; 515 516 return snd_card_ro_proc_new(card, "id", card, snd_card_id_read); 517} 518 519/* 520 * register the card proc file 521 * called from init.c 522 * can be called multiple times for reinitialization 523 */ 524int snd_info_card_register(struct snd_card *card) 525{ 526 struct proc_dir_entry *p; 527 int err; 528 529 if (snd_BUG_ON(!card)) 530 return -ENXIO; 531 532 err = snd_info_register(card->proc_root); 533 if (err < 0) 534 return err; 535 536 if (!strcmp(card->id, card->proc_root->name)) 537 return 0; 538 539 if (card->proc_root_link) 540 return 0; 541 p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); 542 if (!p) 543 return -ENOMEM; 544 card->proc_root_link = p; 545 return 0; 546} 547 548/* 549 * called on card->id change 550 */ 551void snd_info_card_id_change(struct snd_card *card) 552{ 553 mutex_lock(&info_mutex); 554 if (card->proc_root_link) { 555 proc_remove(card->proc_root_link); 556 card->proc_root_link = NULL; 557 } 558 if (strcmp(card->id, card->proc_root->name)) 559 card->proc_root_link = proc_symlink(card->id, 560 snd_proc_root->p, 561 card->proc_root->name); 562 mutex_unlock(&info_mutex); 563} 564 565/* 566 * de-register the card proc file 567 * called from init.c 568 */ 569void snd_info_card_disconnect(struct snd_card *card) 570{ 571 if (!card) 572 return; 573 574 proc_remove(card->proc_root_link); 575 if (card->proc_root) 576 proc_remove(card->proc_root->p); 577 578 mutex_lock(&info_mutex); 579 if (card->proc_root) 580 snd_info_clear_entries(card->proc_root); 581 card->proc_root_link = NULL; 582 card->proc_root = NULL; 583 mutex_unlock(&info_mutex); 584} 585 586/* 587 * release the card proc file resources 588 * called from init.c 589 */ 590int snd_info_card_free(struct snd_card *card) 591{ 592 if (!card) 593 return 0; 594 snd_info_free_entry(card->proc_root); 595 card->proc_root = NULL; 596 return 0; 597} 598 599 600/** 601 * snd_info_get_line - read one line from the procfs buffer 602 * @buffer: the procfs buffer 603 * @line: the buffer to store 604 * @len: the max. buffer size 605 * 606 * Reads one line from the buffer and stores the string. 607 * 608 * Return: Zero if successful, or 1 if error or EOF. 609 */ 610int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 611{ 612 int c; 613 614 if (snd_BUG_ON(!buffer)) 615 return 1; 616 if (!buffer->buffer) 617 return 1; 618 if (len <= 0 || buffer->stop || buffer->error) 619 return 1; 620 while (!buffer->stop) { 621 c = buffer->buffer[buffer->curr++]; 622 if (buffer->curr >= buffer->size) 623 buffer->stop = 1; 624 if (c == '\n') 625 break; 626 if (len > 1) { 627 len--; 628 *line++ = c; 629 } 630 } 631 *line = '\0'; 632 return 0; 633} 634EXPORT_SYMBOL(snd_info_get_line); 635 636/** 637 * snd_info_get_str - parse a string token 638 * @dest: the buffer to store the string token 639 * @src: the original string 640 * @len: the max. length of token - 1 641 * 642 * Parses the original string and copy a token to the given 643 * string buffer. 644 * 645 * Return: The updated pointer of the original string so that 646 * it can be used for the next call. 647 */ 648const char *snd_info_get_str(char *dest, const char *src, int len) 649{ 650 int c; 651 652 while (*src == ' ' || *src == '\t') 653 src++; 654 if (*src == '"' || *src == '\'') { 655 c = *src++; 656 while (--len > 0 && *src && *src != c) { 657 *dest++ = *src++; 658 } 659 if (*src == c) 660 src++; 661 } else { 662 while (--len > 0 && *src && *src != ' ' && *src != '\t') { 663 *dest++ = *src++; 664 } 665 } 666 *dest = 0; 667 while (*src == ' ' || *src == '\t') 668 src++; 669 return src; 670} 671EXPORT_SYMBOL(snd_info_get_str); 672 673/* 674 * snd_info_create_entry - create an info entry 675 * @name: the proc file name 676 * @parent: the parent directory 677 * 678 * Creates an info entry with the given file name and initializes as 679 * the default state. 680 * 681 * Usually called from other functions such as 682 * snd_info_create_card_entry(). 683 * 684 * Return: The pointer of the new instance, or %NULL on failure. 685 */ 686static struct snd_info_entry * 687snd_info_create_entry(const char *name, struct snd_info_entry *parent, 688 struct module *module) 689{ 690 struct snd_info_entry *entry; 691 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 692 if (entry == NULL) 693 return NULL; 694 entry->name = kstrdup(name, GFP_KERNEL); 695 if (entry->name == NULL) { 696 kfree(entry); 697 return NULL; 698 } 699 entry->mode = S_IFREG | 0444; 700 entry->content = SNDRV_INFO_CONTENT_TEXT; 701 mutex_init(&entry->access); 702 INIT_LIST_HEAD(&entry->children); 703 INIT_LIST_HEAD(&entry->list); 704 entry->parent = parent; 705 entry->module = module; 706 if (parent) { 707 mutex_lock(&parent->access); 708 list_add_tail(&entry->list, &parent->children); 709 mutex_unlock(&parent->access); 710 } 711 return entry; 712} 713 714/** 715 * snd_info_create_module_entry - create an info entry for the given module 716 * @module: the module pointer 717 * @name: the file name 718 * @parent: the parent directory 719 * 720 * Creates a new info entry and assigns it to the given module. 721 * 722 * Return: The pointer of the new instance, or %NULL on failure. 723 */ 724struct snd_info_entry *snd_info_create_module_entry(struct module * module, 725 const char *name, 726 struct snd_info_entry *parent) 727{ 728 if (!parent) 729 parent = snd_proc_root; 730 return snd_info_create_entry(name, parent, module); 731} 732EXPORT_SYMBOL(snd_info_create_module_entry); 733 734/** 735 * snd_info_create_card_entry - create an info entry for the given card 736 * @card: the card instance 737 * @name: the file name 738 * @parent: the parent directory 739 * 740 * Creates a new info entry and assigns it to the given card. 741 * 742 * Return: The pointer of the new instance, or %NULL on failure. 743 */ 744struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 745 const char *name, 746 struct snd_info_entry * parent) 747{ 748 if (!parent) 749 parent = card->proc_root; 750 return snd_info_create_entry(name, parent, card->module); 751} 752EXPORT_SYMBOL(snd_info_create_card_entry); 753 754static void snd_info_clear_entries(struct snd_info_entry *entry) 755{ 756 struct snd_info_entry *p; 757 758 if (!entry->p) 759 return; 760 list_for_each_entry(p, &entry->children, list) 761 snd_info_clear_entries(p); 762 entry->p = NULL; 763} 764 765/** 766 * snd_info_free_entry - release the info entry 767 * @entry: the info entry 768 * 769 * Releases the info entry. 770 */ 771void snd_info_free_entry(struct snd_info_entry * entry) 772{ 773 struct snd_info_entry *p, *n; 774 775 if (!entry) 776 return; 777 if (entry->p) { 778 proc_remove(entry->p); 779 mutex_lock(&info_mutex); 780 snd_info_clear_entries(entry); 781 mutex_unlock(&info_mutex); 782 } 783 784 /* free all children at first */ 785 list_for_each_entry_safe(p, n, &entry->children, list) 786 snd_info_free_entry(p); 787 788 p = entry->parent; 789 if (p) { 790 mutex_lock(&p->access); 791 list_del(&entry->list); 792 mutex_unlock(&p->access); 793 } 794 kfree(entry->name); 795 if (entry->private_free) 796 entry->private_free(entry); 797 kfree(entry); 798} 799EXPORT_SYMBOL(snd_info_free_entry); 800 801static int __snd_info_register(struct snd_info_entry *entry) 802{ 803 struct proc_dir_entry *root, *p = NULL; 804 805 if (snd_BUG_ON(!entry)) 806 return -ENXIO; 807 root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; 808 mutex_lock(&info_mutex); 809 if (entry->p || !root) 810 goto unlock; 811 if (S_ISDIR(entry->mode)) { 812 p = proc_mkdir_mode(entry->name, entry->mode, root); 813 if (!p) { 814 mutex_unlock(&info_mutex); 815 return -ENOMEM; 816 } 817 } else { 818 const struct proc_ops *ops; 819 if (entry->content == SNDRV_INFO_CONTENT_DATA) 820 ops = &snd_info_entry_operations; 821 else 822 ops = &snd_info_text_entry_ops; 823 p = proc_create_data(entry->name, entry->mode, root, 824 ops, entry); 825 if (!p) { 826 mutex_unlock(&info_mutex); 827 return -ENOMEM; 828 } 829 proc_set_size(p, entry->size); 830 } 831 entry->p = p; 832 unlock: 833 mutex_unlock(&info_mutex); 834 return 0; 835} 836 837/** 838 * snd_info_register - register the info entry 839 * @entry: the info entry 840 * 841 * Registers the proc info entry. 842 * The all children entries are registered recursively. 843 * 844 * Return: Zero if successful, or a negative error code on failure. 845 */ 846int snd_info_register(struct snd_info_entry *entry) 847{ 848 struct snd_info_entry *p; 849 int err; 850 851 if (!entry->p) { 852 err = __snd_info_register(entry); 853 if (err < 0) 854 return err; 855 } 856 857 list_for_each_entry(p, &entry->children, list) { 858 err = snd_info_register(p); 859 if (err < 0) 860 return err; 861 } 862 863 return 0; 864} 865EXPORT_SYMBOL(snd_info_register); 866 867/** 868 * snd_card_rw_proc_new - Create a read/write text proc file entry for the card 869 * @card: the card instance 870 * @name: the file name 871 * @private_data: the arbitrary private data 872 * @read: the read callback 873 * @write: the write callback, NULL for read-only 874 * 875 * This proc file entry will be registered via snd_card_register() call, and 876 * it will be removed automatically at the card removal, too. 877 */ 878int snd_card_rw_proc_new(struct snd_card *card, const char *name, 879 void *private_data, 880 void (*read)(struct snd_info_entry *, 881 struct snd_info_buffer *), 882 void (*write)(struct snd_info_entry *entry, 883 struct snd_info_buffer *buffer)) 884{ 885 struct snd_info_entry *entry; 886 887 entry = snd_info_create_card_entry(card, name, card->proc_root); 888 if (!entry) 889 return -ENOMEM; 890 snd_info_set_text_ops(entry, private_data, read); 891 if (write) { 892 entry->mode |= 0200; 893 entry->c.text.write = write; 894 } 895 return 0; 896} 897EXPORT_SYMBOL_GPL(snd_card_rw_proc_new); 898 899/* 900 901 */ 902 903static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 904{ 905 snd_iprintf(buffer, 906 "Advanced Linux Sound Architecture Driver Version k%s.\n", 907 init_utsname()->release); 908} 909 910static int __init snd_info_version_init(void) 911{ 912 struct snd_info_entry *entry; 913 914 entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 915 if (entry == NULL) 916 return -ENOMEM; 917 entry->c.text.read = snd_info_version_read; 918 return snd_info_register(entry); /* freed in error path */ 919} 920