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 __poll_t 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 __poll_t 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 |= EPOLLIN | EPOLLRDNORM; 218 if (entry->c.ops->write) 219 mask |= EPOLLOUT | EPOLLWRNORM; 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 = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); 348 if (!nbuf) { 349 err = -ENOMEM; 350 goto error; 351 } 352 kvfree(buf->buffer); 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 kvfree(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 | 0555; 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 | 0555; 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 EXPORT_SYMBOL(snd_info_get_line); 656 657 /** 658 * snd_info_get_str - parse a string token 659 * @dest: the buffer to store the string token 660 * @src: the original string 661 * @len: the max. length of token - 1 662 * 663 * Parses the original string and copy a token to the given 664 * string buffer. 665 * 666 * Return: The updated pointer of the original string so that 667 * it can be used for the next call. 668 */ 669 const char *snd_info_get_str(char *dest, const char *src, int len) 670 { 671 int c; 672 673 while (*src == ' ' || *src == '\t') 674 src++; 675 if (*src == '"' || *src == '\'') { 676 c = *src++; 677 while (--len > 0 && *src && *src != c) { 678 *dest++ = *src++; 679 } 680 if (*src == c) 681 src++; 682 } else { 683 while (--len > 0 && *src && *src != ' ' && *src != '\t') { 684 *dest++ = *src++; 685 } 686 } 687 *dest = 0; 688 while (*src == ' ' || *src == '\t') 689 src++; 690 return src; 691 } 692 EXPORT_SYMBOL(snd_info_get_str); 693 694 /* 695 * snd_info_create_entry - create an info entry 696 * @name: the proc file name 697 * @parent: the parent directory 698 * 699 * Creates an info entry with the given file name and initializes as 700 * the default state. 701 * 702 * Usually called from other functions such as 703 * snd_info_create_card_entry(). 704 * 705 * Return: The pointer of the new instance, or %NULL on failure. 706 */ 707 static struct snd_info_entry * 708 snd_info_create_entry(const char *name, struct snd_info_entry *parent) 709 { 710 struct snd_info_entry *entry; 711 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 712 if (entry == NULL) 713 return NULL; 714 entry->name = kstrdup(name, GFP_KERNEL); 715 if (entry->name == NULL) { 716 kfree(entry); 717 return NULL; 718 } 719 entry->mode = S_IFREG | 0444; 720 entry->content = SNDRV_INFO_CONTENT_TEXT; 721 mutex_init(&entry->access); 722 INIT_LIST_HEAD(&entry->children); 723 INIT_LIST_HEAD(&entry->list); 724 entry->parent = parent; 725 if (parent) 726 list_add_tail(&entry->list, &parent->children); 727 return entry; 728 } 729 730 /** 731 * snd_info_create_module_entry - create an info entry for the given module 732 * @module: the module pointer 733 * @name: the file name 734 * @parent: the parent directory 735 * 736 * Creates a new info entry and assigns it to the given module. 737 * 738 * Return: The pointer of the new instance, or %NULL on failure. 739 */ 740 struct snd_info_entry *snd_info_create_module_entry(struct module * module, 741 const char *name, 742 struct snd_info_entry *parent) 743 { 744 struct snd_info_entry *entry = snd_info_create_entry(name, parent); 745 if (entry) 746 entry->module = module; 747 return entry; 748 } 749 EXPORT_SYMBOL(snd_info_create_module_entry); 750 751 /** 752 * snd_info_create_card_entry - create an info entry for the given card 753 * @card: the card instance 754 * @name: the file name 755 * @parent: the parent directory 756 * 757 * Creates a new info entry and assigns it to the given card. 758 * 759 * Return: The pointer of the new instance, or %NULL on failure. 760 */ 761 struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, 762 const char *name, 763 struct snd_info_entry * parent) 764 { 765 struct snd_info_entry *entry = snd_info_create_entry(name, parent); 766 if (entry) { 767 entry->module = card->module; 768 entry->card = card; 769 } 770 return entry; 771 } 772 EXPORT_SYMBOL(snd_info_create_card_entry); 773 774 static void snd_info_disconnect(struct snd_info_entry *entry) 775 { 776 struct snd_info_entry *p; 777 778 if (!entry->p) 779 return; 780 list_for_each_entry(p, &entry->children, list) 781 snd_info_disconnect(p); 782 proc_remove(entry->p); 783 entry->p = NULL; 784 } 785 786 /** 787 * snd_info_free_entry - release the info entry 788 * @entry: the info entry 789 * 790 * Releases the info entry. 791 */ 792 void snd_info_free_entry(struct snd_info_entry * entry) 793 { 794 struct snd_info_entry *p, *n; 795 796 if (!entry) 797 return; 798 if (entry->p) { 799 mutex_lock(&info_mutex); 800 snd_info_disconnect(entry); 801 mutex_unlock(&info_mutex); 802 } 803 804 /* free all children at first */ 805 list_for_each_entry_safe(p, n, &entry->children, list) 806 snd_info_free_entry(p); 807 808 list_del(&entry->list); 809 kfree(entry->name); 810 if (entry->private_free) 811 entry->private_free(entry); 812 kfree(entry); 813 } 814 EXPORT_SYMBOL(snd_info_free_entry); 815 816 /** 817 * snd_info_register - register the info entry 818 * @entry: the info entry 819 * 820 * Registers the proc info entry. 821 * 822 * Return: Zero if successful, or a negative error code on failure. 823 */ 824 int snd_info_register(struct snd_info_entry * entry) 825 { 826 struct proc_dir_entry *root, *p = NULL; 827 828 if (snd_BUG_ON(!entry)) 829 return -ENXIO; 830 root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p; 831 mutex_lock(&info_mutex); 832 if (S_ISDIR(entry->mode)) { 833 p = proc_mkdir_mode(entry->name, entry->mode, root); 834 if (!p) { 835 mutex_unlock(&info_mutex); 836 return -ENOMEM; 837 } 838 } else { 839 const struct file_operations *ops; 840 if (entry->content == SNDRV_INFO_CONTENT_DATA) 841 ops = &snd_info_entry_operations; 842 else 843 ops = &snd_info_text_entry_ops; 844 p = proc_create_data(entry->name, entry->mode, root, 845 ops, entry); 846 if (!p) { 847 mutex_unlock(&info_mutex); 848 return -ENOMEM; 849 } 850 proc_set_size(p, entry->size); 851 } 852 entry->p = p; 853 mutex_unlock(&info_mutex); 854 return 0; 855 } 856 EXPORT_SYMBOL(snd_info_register); 857 858 /* 859 860 */ 861 862 static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) 863 { 864 snd_iprintf(buffer, 865 "Advanced Linux Sound Architecture Driver Version k%s.\n", 866 init_utsname()->release); 867 } 868 869 static int __init snd_info_version_init(void) 870 { 871 struct snd_info_entry *entry; 872 873 entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); 874 if (entry == NULL) 875 return -ENOMEM; 876 entry->c.text.read = snd_info_version_read; 877 return snd_info_register(entry); /* freed in error path */ 878 } 879