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 if (!entry->c.text.write) 329 return -EIO; 330 pos = *offset; 331 if (!valid_pos(pos, count)) 332 return -EIO; 333 next = pos + count; 334 /* don't handle too large text inputs */ 335 if (next > 16 * 1024) 336 return -EIO; 337 mutex_lock(&entry->access); 338 buf = data->wbuffer; 339 if (!buf) { 340 data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL); 341 if (!buf) { 342 err = -ENOMEM; 343 goto error; 344 } 345 } 346 if (next > buf->len) { 347 char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), 348 GFP_KERNEL | __GFP_ZERO); 349 if (!nbuf) { 350 err = -ENOMEM; 351 goto error; 352 } 353 buf->buffer = nbuf; 354 buf->len = PAGE_ALIGN(next); 355 } 356 if (copy_from_user(buf->buffer + pos, buffer, count)) { 357 err = -EFAULT; 358 goto error; 359 } 360 buf->size = next; 361 error: 362 mutex_unlock(&entry->access); 363 if (err < 0) 364 return err; 365 *offset = next; 366 return count; 367 } 368 369 static int snd_info_seq_show(struct seq_file *seq, void *p) 370 { 371 struct snd_info_private_data *data = seq->private; 372 struct snd_info_entry *entry = data->entry; 373 374 if (!entry->c.text.read) { 375 return -EIO; 376 } else { 377 data->rbuffer->buffer = (char *)seq; /* XXX hack! */ 378 entry->c.text.read(entry, data->rbuffer); 379 } 380 return 0; 381 } 382 383 static int snd_info_text_entry_open(struct inode *inode, struct file *file) 384 { 385 struct snd_info_entry *entry = PDE_DATA(inode); 386 struct snd_info_private_data *data; 387 int err; 388 389 mutex_lock(&info_mutex); 390 err = alloc_info_private(entry, &data); 391 if (err < 0) 392 goto unlock; 393 394 data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL); 395 if (!data->rbuffer) { 396 err = -ENOMEM; 397 goto error; 398 } 399 if (entry->size) 400 err = single_open_size(file, snd_info_seq_show, data, 401 entry->size); 402 else 403 err = single_open(file, snd_info_seq_show, data); 404 if (err < 0) 405 goto error; 406 mutex_unlock(&info_mutex); 407 return 0; 408 409 error: 410 kfree(data->rbuffer); 411 kfree(data); 412 module_put(entry->module); 413 unlock: 414 mutex_unlock(&info_mutex); 415 return err; 416 } 417 418 static int snd_info_text_entry_release(struct inode *inode, struct file *file) 419 { 420 struct seq_file *m = file->private_data; 421 struct snd_info_private_data *data = m->private; 422 struct snd_info_entry *entry = data->entry; 423 424 if (data->wbuffer && entry->c.text.write) 425 entry->c.text.write(entry, data->wbuffer); 426 427 single_release(inode, file); 428 kfree(data->rbuffer); 429 if (data->wbuffer) { 430 kfree(data->wbuffer->buffer); 431 kfree(data->wbuffer); 432 } 433 434 module_put(entry->module); 435 kfree(data); 436 return 0; 437 } 438 439 static const struct file_operations snd_info_text_entry_ops = 440 { 441 .owner = THIS_MODULE, 442 .open = snd_info_text_entry_open, 443 .release = snd_info_text_entry_release, 444 .write = snd_info_text_entry_write, 445 .llseek = seq_lseek, 446 .read = seq_read, 447 }; 448 449 static struct snd_info_entry *create_subdir(struct module *mod, 450 const char *name) 451 { 452 struct snd_info_entry *entry; 453 454 entry = snd_info_create_module_entry(mod, name, NULL); 455 if (!entry) 456 return NULL; 457 entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; 458 if (snd_info_register(entry) < 0) { 459 snd_info_free_entry(entry); 460 return NULL; 461 } 462 return entry; 463 } 464 465 static struct snd_info_entry * 466 snd_info_create_entry(const char *name, struct snd_info_entry *parent); 467 468 int __init snd_info_init(void) 469 { 470 snd_proc_root = snd_info_create_entry("asound", NULL); 471 if (!snd_proc_root) 472 return -ENOMEM; 473 snd_proc_root->mode = S_IFDIR | S_IRUGO | S_IXUGO; 474 snd_proc_root->p = proc_mkdir("asound", NULL); 475 if (!snd_proc_root->p) 476 goto error; 477 #ifdef CONFIG_SND_OSSEMUL 478 snd_oss_root = create_subdir(THIS_MODULE, "oss"); 479 if (!snd_oss_root) 480 goto error; 481 #endif 482 #if IS_ENABLED(CONFIG_SND_SEQUENCER) 483 snd_seq_root = create_subdir(THIS_MODULE, "seq"); 484 if (!snd_seq_root) 485 goto error; 486 #endif 487 if (snd_info_version_init() < 0 || 488 snd_minor_info_init() < 0 || 489 snd_minor_info_oss_init() < 0 || 490 snd_card_info_init() < 0 || 491 snd_info_minor_register() < 0) 492 goto error; 493 return 0; 494 495 error: 496 snd_info_free_entry(snd_proc_root); 497 return -ENOMEM; 498 } 499 500 int __exit snd_info_done(void) 501 { 502 snd_info_free_entry(snd_proc_root); 503 return 0; 504 } 505 506 /* 507 * create a card proc file 508 * called from init.c 509 */ 510 int snd_info_card_create(struct snd_card *card) 511 { 512 char str[8]; 513 struct snd_info_entry *entry; 514 515 if (snd_BUG_ON(!card)) 516 return -ENXIO; 517 518 sprintf(str, "card%i", card->number); 519 entry = create_subdir(card->module, str); 520 if (!entry) 521 return -ENOMEM; 522 card->proc_root = entry; 523 return 0; 524 } 525 526 /* register all pending info entries */ 527 static int snd_info_register_recursive(struct snd_info_entry *entry) 528 { 529 struct snd_info_entry *p; 530 int err; 531 532 if (!entry->p) { 533 err = snd_info_register(entry); 534 if (err < 0) 535 return err; 536 } 537 538 list_for_each_entry(p, &entry->children, list) { 539 err = snd_info_register_recursive(p); 540 if (err < 0) 541 return err; 542 } 543 544 return 0; 545 } 546 547 /* 548 * register the card proc file 549 * called from init.c 550 * can be called multiple times for reinitialization 551 */ 552 int snd_info_card_register(struct snd_card *card) 553 { 554 struct proc_dir_entry *p; 555 int err; 556 557 if (snd_BUG_ON(!card)) 558 return -ENXIO; 559 560 err = snd_info_register_recursive(card->proc_root); 561 if (err < 0) 562 return err; 563 564 if (!strcmp(card->id, card->proc_root->name)) 565 return 0; 566 567 if (card->proc_root_link) 568 return 0; 569 p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name); 570 if (!p) 571 return -ENOMEM; 572 card->proc_root_link = p; 573 return 0; 574 } 575 576 /* 577 * called on card->id change 578 */ 579 void snd_info_card_id_change(struct snd_card *card) 580 { 581 mutex_lock(&info_mutex); 582 if (card->proc_root_link) { 583 proc_remove(card->proc_root_link); 584 card->proc_root_link = NULL; 585 } 586 if (strcmp(card->id, card->proc_root->name)) 587 card->proc_root_link = proc_symlink(card->id, 588 snd_proc_root->p, 589 card->proc_root->name); 590 mutex_unlock(&info_mutex); 591 } 592 593 /* 594 * de-register the card proc file 595 * called from init.c 596 */ 597 void snd_info_card_disconnect(struct snd_card *card) 598 { 599 if (!card) 600 return; 601 mutex_lock(&info_mutex); 602 proc_remove(card->proc_root_link); 603 card->proc_root_link = NULL; 604 if (card->proc_root) 605 snd_info_disconnect(card->proc_root); 606 mutex_unlock(&info_mutex); 607 } 608 609 /* 610 * release the card proc file resources 611 * called from init.c 612 */ 613 int snd_info_card_free(struct snd_card *card) 614 { 615 if (!card) 616 return 0; 617 snd_info_free_entry(card->proc_root); 618 card->proc_root = NULL; 619 return 0; 620 } 621 622 623 /** 624 * snd_info_get_line - read one line from the procfs buffer 625 * @buffer: the procfs buffer 626 * @line: the buffer to store 627 * @len: the max. buffer size 628 * 629 * Reads one line from the buffer and stores the string. 630 * 631 * Return: Zero if successful, or 1 if error or EOF. 632 */ 633 int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) 634 { 635 int c = -1; 636 637 if (snd_BUG_ON(!buffer || !buffer->buffer)) 638 return 1; 639 if (len <= 0 || buffer->stop || buffer->error) 640 return 1; 641 while (!buffer->stop) { 642 c = buffer->buffer[buffer->curr++]; 643 if (buffer->curr >= buffer->size) 644 buffer->stop = 1; 645 if (c == '\n') 646 break; 647 if (len > 1) { 648 len--; 649 *line++ = c; 650 } 651 } 652 *line = '\0'; 653 return 0; 654 } 655 656 EXPORT_SYMBOL(snd_info_get_line); 657 658 /** 659 * snd_info_get_str - parse a string token 660 * @dest: the buffer to store the string token 661 * @src: the original string 662 * @len: the max. length of token - 1 663 * 664 * Parses the original string and copy a token to the given 665 * string buffer. 666 * 667 * Return: The updated pointer of the original string so that 668 * it can be used for the next call. 669 */ 670 const char *snd_info_get_str(char *dest, const char *src, int len) 671 { 672 int c; 673 674 while (*src == ' ' || *src == '\t') 675 src++; 676 if (*src == '"' || *src == '\'') { 677 c = *src++; 678 while (--len > 0 && *src && *src != c) { 679 *dest++ = *src++; 680 } 681 if (*src == c) 682 src++; 683 } else { 684 while (--len > 0 && *src && *src != ' ' && *src != '\t') { 685 *dest++ = *src++; 686 } 687 } 688 *dest = 0; 689 while (*src == ' ' || *src == '\t') 690 src++; 691 return src; 692 } 693 694 EXPORT_SYMBOL(snd_info_get_str); 695 696 /* 697 * snd_info_create_entry - create an info entry 698 * @name: the proc file name 699 * @parent: the parent directory 700 * 701 * Creates an info entry with the given file name and initializes as 702 * the default state. 703 * 704 * Usually called from other functions such as 705 * snd_info_create_card_entry(). 706 * 707 * Return: The pointer of the new instance, or %NULL on failure. 708 */ 709 static struct snd_info_entry * 710 snd_info_create_entry(const char *name, struct snd_info_entry *parent) 711 { 712 struct snd_info_entry *entry; 713 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 714 if (entry == NULL) 715 return NULL; 716 entry->name = kstrdup(name, GFP_KERNEL); 717 if (entry->name == NULL) { 718 kfree(entry); 719 return NULL; 720 } 721 entry->mode = S_IFREG | S_IRUGO; 722 entry->content = SNDRV_INFO_CONTENT_TEXT; 723 mutex_init(&entry->access); 724 INIT_LIST_HEAD(&entry->children); 725 INIT_LIST_HEAD(&entry->list); 726 entry->parent = parent; 727 if (parent) 728 list_add_tail(&entry->list, &parent->children); 729 return entry; 730 } 731 732 /** 733 * snd_info_create_module_entry - create an info entry for the given module 734 * @module: the module pointer 735 * @name: the file name 736 * @parent: the parent directory 737 * 738 * Creates a new info entry and assigns it to the given module. 739 * 740 * Return: The pointer of the new instance, or %NULL on failure. 741 */ 742 struct snd_info_entry *snd_info_create_module_entry(struct module * module, 743 const char *name, 744 struct snd_info_entry *parent) 745 { 746 struct snd_info_entry *entry = snd_info_create_entry(name, parent); 747 if (entry) 748 entry->module = module; 749 return entry; 750 } 751 752 EXPORT_SYMBOL(snd_info_create_module_entry); 753 754 /** 755 * snd_info_create_card_entry - create an info entry for the given card 756 * @card: the card instance 757 * @name: the file name 758 * @parent: the parent directory 759 * 760 * Creates a new info entry and assigns it to the given card. 761 * 762 * Return: The pointer of the new instance, or %NULL on failure. 763 */ 764 struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 765 const char *name, 766 struct snd_info_entry * parent) 767 { 768 struct snd_info_entry *entry = snd_info_create_entry(name, parent); 769 if (entry) { 770 entry->module = card->module; 771 entry->card = card; 772 } 773 return entry; 774 } 775 776 EXPORT_SYMBOL(snd_info_create_card_entry); 777 778 static void snd_info_disconnect(struct snd_info_entry *entry) 779 { 780 struct snd_info_entry *p; 781 782 if (!entry->p) 783 return; 784 list_for_each_entry(p, &entry->children, list) 785 snd_info_disconnect(p); 786 proc_remove(entry->p); 787 entry->p = NULL; 788 } 789 790 /** 791 * snd_info_free_entry - release the info entry 792 * @entry: the info entry 793 * 794 * Releases the info entry. 795 */ 796 void snd_info_free_entry(struct snd_info_entry * entry) 797 { 798 struct snd_info_entry *p, *n; 799 800 if (!entry) 801 return; 802 if (entry->p) { 803 mutex_lock(&info_mutex); 804 snd_info_disconnect(entry); 805 mutex_unlock(&info_mutex); 806 } 807 808 /* free all children at first */ 809 list_for_each_entry_safe(p, n, &entry->children, list) 810 snd_info_free_entry(p); 811 812 list_del(&entry->list); 813 kfree(entry->name); 814 if (entry->private_free) 815 entry->private_free(entry); 816 kfree(entry); 817 } 818 819 EXPORT_SYMBOL(snd_info_free_entry); 820 821 /** 822 * snd_info_register - register the info entry 823 * @entry: the info entry 824 * 825 * Registers the proc info entry. 826 * 827 * Return: Zero if successful, or a negative error code on failure. 828 */ 829 int snd_info_register(struct snd_info_entry * entry) 830 { 831 struct proc_dir_entry *root, *p = NULL; 832 833 if (snd_BUG_ON(!entry)) 834 return -ENXIO; 835 root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; 836 mutex_lock(&info_mutex); 837 if (S_ISDIR(entry->mode)) { 838 p = proc_mkdir_mode(entry->name, entry->mode, root); 839 if (!p) { 840 mutex_unlock(&info_mutex); 841 return -ENOMEM; 842 } 843 } else { 844 const struct file_operations *ops; 845 if (entry->content == SNDRV_INFO_CONTENT_DATA) 846 ops = &snd_info_entry_operations; 847 else 848 ops = &snd_info_text_entry_ops; 849 p = proc_create_data(entry->name, entry->mode, root, 850 ops, entry); 851 if (!p) { 852 mutex_unlock(&info_mutex); 853 return -ENOMEM; 854 } 855 proc_set_size(p, entry->size); 856 } 857 entry->p = p; 858 mutex_unlock(&info_mutex); 859 return 0; 860 } 861 862 EXPORT_SYMBOL(snd_info_register); 863 864 /* 865 866 */ 867 868 static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 869 { 870 snd_iprintf(buffer, 871 "Advanced Linux Sound Architecture Driver Version k%s.\n", 872 init_utsname()->release); 873 } 874 875 static int __init snd_info_version_init(void) 876 { 877 struct snd_info_entry *entry; 878 879 entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 880 if (entry == NULL) 881 return -ENOMEM; 882 entry->c.text.read = snd_info_version_read; 883 return snd_info_register(entry); /* freed in error path */ 884 } 885