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