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