0001
0002
0003
0004
0005
0006
0007 #include <linux/init.h>
0008 #include <linux/time.h>
0009 #include <linux/mm.h>
0010 #include <linux/slab.h>
0011 #include <linux/string.h>
0012 #include <linux/module.h>
0013 #include <sound/core.h>
0014 #include <sound/minors.h>
0015 #include <sound/info.h>
0016 #include <linux/utsname.h>
0017 #include <linux/proc_fs.h>
0018 #include <linux/mutex.h>
0019
0020 int snd_info_check_reserved_words(const char *str)
0021 {
0022 static const char * const reserved[] =
0023 {
0024 "version",
0025 "meminfo",
0026 "memdebug",
0027 "detect",
0028 "devices",
0029 "oss",
0030 "cards",
0031 "timers",
0032 "synth",
0033 "pcm",
0034 "seq",
0035 NULL
0036 };
0037 const char * const *xstr = reserved;
0038
0039 while (*xstr) {
0040 if (!strcmp(*xstr, str))
0041 return 0;
0042 xstr++;
0043 }
0044 if (!strncmp(str, "card", 4))
0045 return 0;
0046 return 1;
0047 }
0048
0049 static DEFINE_MUTEX(info_mutex);
0050
0051 struct snd_info_private_data {
0052 struct snd_info_buffer *rbuffer;
0053 struct snd_info_buffer *wbuffer;
0054 struct snd_info_entry *entry;
0055 void *file_private_data;
0056 };
0057
0058 static int snd_info_version_init(void);
0059 static void snd_info_disconnect(struct snd_info_entry *entry);
0060
0061
0062
0063
0064
0065 static struct snd_info_entry *snd_proc_root;
0066 struct snd_info_entry *snd_seq_root;
0067 EXPORT_SYMBOL(snd_seq_root);
0068
0069 #ifdef CONFIG_SND_OSSEMUL
0070 struct snd_info_entry *snd_oss_root;
0071 #endif
0072
0073 static int alloc_info_private(struct snd_info_entry *entry,
0074 struct snd_info_private_data **ret)
0075 {
0076 struct snd_info_private_data *data;
0077
0078 if (!entry || !entry->p)
0079 return -ENODEV;
0080 if (!try_module_get(entry->module))
0081 return -EFAULT;
0082 data = kzalloc(sizeof(*data), GFP_KERNEL);
0083 if (!data) {
0084 module_put(entry->module);
0085 return -ENOMEM;
0086 }
0087 data->entry = entry;
0088 *ret = data;
0089 return 0;
0090 }
0091
0092 static bool valid_pos(loff_t pos, size_t count)
0093 {
0094 if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
0095 return false;
0096 if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
0097 return false;
0098 return true;
0099 }
0100
0101
0102
0103
0104 static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
0105 {
0106 struct snd_info_private_data *data;
0107 struct snd_info_entry *entry;
0108 loff_t ret = -EINVAL, size;
0109
0110 data = file->private_data;
0111 entry = data->entry;
0112 mutex_lock(&entry->access);
0113 if (entry->c.ops->llseek) {
0114 ret = entry->c.ops->llseek(entry,
0115 data->file_private_data,
0116 file, offset, orig);
0117 goto out;
0118 }
0119
0120 size = entry->size;
0121 switch (orig) {
0122 case SEEK_SET:
0123 break;
0124 case SEEK_CUR:
0125 offset += file->f_pos;
0126 break;
0127 case SEEK_END:
0128 if (!size)
0129 goto out;
0130 offset += size;
0131 break;
0132 default:
0133 goto out;
0134 }
0135 if (offset < 0)
0136 goto out;
0137 if (size && offset > size)
0138 offset = size;
0139 file->f_pos = offset;
0140 ret = offset;
0141 out:
0142 mutex_unlock(&entry->access);
0143 return ret;
0144 }
0145
0146 static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
0147 size_t count, loff_t * offset)
0148 {
0149 struct snd_info_private_data *data = file->private_data;
0150 struct snd_info_entry *entry = data->entry;
0151 size_t size;
0152 loff_t pos;
0153
0154 pos = *offset;
0155 if (!valid_pos(pos, count))
0156 return -EIO;
0157 if (pos >= entry->size)
0158 return 0;
0159 size = entry->size - pos;
0160 size = min(count, size);
0161 size = entry->c.ops->read(entry, data->file_private_data,
0162 file, buffer, size, pos);
0163 if ((ssize_t) size > 0)
0164 *offset = pos + size;
0165 return size;
0166 }
0167
0168 static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
0169 size_t count, loff_t * offset)
0170 {
0171 struct snd_info_private_data *data = file->private_data;
0172 struct snd_info_entry *entry = data->entry;
0173 ssize_t size = 0;
0174 loff_t pos;
0175
0176 pos = *offset;
0177 if (!valid_pos(pos, count))
0178 return -EIO;
0179 if (count > 0) {
0180 size_t maxsize = entry->size - pos;
0181 count = min(count, maxsize);
0182 size = entry->c.ops->write(entry, data->file_private_data,
0183 file, buffer, count, pos);
0184 }
0185 if (size > 0)
0186 *offset = pos + size;
0187 return size;
0188 }
0189
0190 static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
0191 {
0192 struct snd_info_private_data *data = file->private_data;
0193 struct snd_info_entry *entry = data->entry;
0194 __poll_t mask = 0;
0195
0196 if (entry->c.ops->poll)
0197 return entry->c.ops->poll(entry,
0198 data->file_private_data,
0199 file, wait);
0200 if (entry->c.ops->read)
0201 mask |= EPOLLIN | EPOLLRDNORM;
0202 if (entry->c.ops->write)
0203 mask |= EPOLLOUT | EPOLLWRNORM;
0204 return mask;
0205 }
0206
0207 static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
0208 unsigned long arg)
0209 {
0210 struct snd_info_private_data *data = file->private_data;
0211 struct snd_info_entry *entry = data->entry;
0212
0213 if (!entry->c.ops->ioctl)
0214 return -ENOTTY;
0215 return entry->c.ops->ioctl(entry, data->file_private_data,
0216 file, cmd, arg);
0217 }
0218
0219 static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
0220 {
0221 struct inode *inode = file_inode(file);
0222 struct snd_info_private_data *data;
0223 struct snd_info_entry *entry;
0224
0225 data = file->private_data;
0226 if (data == NULL)
0227 return 0;
0228 entry = data->entry;
0229 if (!entry->c.ops->mmap)
0230 return -ENXIO;
0231 return entry->c.ops->mmap(entry, data->file_private_data,
0232 inode, file, vma);
0233 }
0234
0235 static int snd_info_entry_open(struct inode *inode, struct file *file)
0236 {
0237 struct snd_info_entry *entry = pde_data(inode);
0238 struct snd_info_private_data *data;
0239 int mode, err;
0240
0241 mutex_lock(&info_mutex);
0242 err = alloc_info_private(entry, &data);
0243 if (err < 0)
0244 goto unlock;
0245
0246 mode = file->f_flags & O_ACCMODE;
0247 if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
0248 ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
0249 err = -ENODEV;
0250 goto error;
0251 }
0252
0253 if (entry->c.ops->open) {
0254 err = entry->c.ops->open(entry, mode, &data->file_private_data);
0255 if (err < 0)
0256 goto error;
0257 }
0258
0259 file->private_data = data;
0260 mutex_unlock(&info_mutex);
0261 return 0;
0262
0263 error:
0264 kfree(data);
0265 module_put(entry->module);
0266 unlock:
0267 mutex_unlock(&info_mutex);
0268 return err;
0269 }
0270
0271 static int snd_info_entry_release(struct inode *inode, struct file *file)
0272 {
0273 struct snd_info_private_data *data = file->private_data;
0274 struct snd_info_entry *entry = data->entry;
0275
0276 if (entry->c.ops->release)
0277 entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
0278 data->file_private_data);
0279 module_put(entry->module);
0280 kfree(data);
0281 return 0;
0282 }
0283
0284 static const struct proc_ops snd_info_entry_operations =
0285 {
0286 .proc_lseek = snd_info_entry_llseek,
0287 .proc_read = snd_info_entry_read,
0288 .proc_write = snd_info_entry_write,
0289 .proc_poll = snd_info_entry_poll,
0290 .proc_ioctl = snd_info_entry_ioctl,
0291 .proc_mmap = snd_info_entry_mmap,
0292 .proc_open = snd_info_entry_open,
0293 .proc_release = snd_info_entry_release,
0294 };
0295
0296
0297
0298
0299 static ssize_t snd_info_text_entry_write(struct file *file,
0300 const char __user *buffer,
0301 size_t count, loff_t *offset)
0302 {
0303 struct seq_file *m = file->private_data;
0304 struct snd_info_private_data *data = m->private;
0305 struct snd_info_entry *entry = data->entry;
0306 struct snd_info_buffer *buf;
0307 loff_t pos;
0308 size_t next;
0309 int err = 0;
0310
0311 if (!entry->c.text.write)
0312 return -EIO;
0313 pos = *offset;
0314 if (!valid_pos(pos, count))
0315 return -EIO;
0316 next = pos + count;
0317
0318 if (next > 16 * 1024)
0319 return -EIO;
0320 mutex_lock(&entry->access);
0321 buf = data->wbuffer;
0322 if (!buf) {
0323 data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
0324 if (!buf) {
0325 err = -ENOMEM;
0326 goto error;
0327 }
0328 }
0329 if (next > buf->len) {
0330 char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
0331 if (!nbuf) {
0332 err = -ENOMEM;
0333 goto error;
0334 }
0335 kvfree(buf->buffer);
0336 buf->buffer = nbuf;
0337 buf->len = PAGE_ALIGN(next);
0338 }
0339 if (copy_from_user(buf->buffer + pos, buffer, count)) {
0340 err = -EFAULT;
0341 goto error;
0342 }
0343 buf->size = next;
0344 error:
0345 mutex_unlock(&entry->access);
0346 if (err < 0)
0347 return err;
0348 *offset = next;
0349 return count;
0350 }
0351
0352 static int snd_info_seq_show(struct seq_file *seq, void *p)
0353 {
0354 struct snd_info_private_data *data = seq->private;
0355 struct snd_info_entry *entry = data->entry;
0356
0357 if (!entry->c.text.read) {
0358 return -EIO;
0359 } else {
0360 data->rbuffer->buffer = (char *)seq;
0361 entry->c.text.read(entry, data->rbuffer);
0362 }
0363 return 0;
0364 }
0365
0366 static int snd_info_text_entry_open(struct inode *inode, struct file *file)
0367 {
0368 struct snd_info_entry *entry = pde_data(inode);
0369 struct snd_info_private_data *data;
0370 int err;
0371
0372 mutex_lock(&info_mutex);
0373 err = alloc_info_private(entry, &data);
0374 if (err < 0)
0375 goto unlock;
0376
0377 data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
0378 if (!data->rbuffer) {
0379 err = -ENOMEM;
0380 goto error;
0381 }
0382 if (entry->size)
0383 err = single_open_size(file, snd_info_seq_show, data,
0384 entry->size);
0385 else
0386 err = single_open(file, snd_info_seq_show, data);
0387 if (err < 0)
0388 goto error;
0389 mutex_unlock(&info_mutex);
0390 return 0;
0391
0392 error:
0393 kfree(data->rbuffer);
0394 kfree(data);
0395 module_put(entry->module);
0396 unlock:
0397 mutex_unlock(&info_mutex);
0398 return err;
0399 }
0400
0401 static int snd_info_text_entry_release(struct inode *inode, struct file *file)
0402 {
0403 struct seq_file *m = file->private_data;
0404 struct snd_info_private_data *data = m->private;
0405 struct snd_info_entry *entry = data->entry;
0406
0407 if (data->wbuffer && entry->c.text.write)
0408 entry->c.text.write(entry, data->wbuffer);
0409
0410 single_release(inode, file);
0411 kfree(data->rbuffer);
0412 if (data->wbuffer) {
0413 kvfree(data->wbuffer->buffer);
0414 kfree(data->wbuffer);
0415 }
0416
0417 module_put(entry->module);
0418 kfree(data);
0419 return 0;
0420 }
0421
0422 static const struct proc_ops snd_info_text_entry_ops =
0423 {
0424 .proc_open = snd_info_text_entry_open,
0425 .proc_release = snd_info_text_entry_release,
0426 .proc_write = snd_info_text_entry_write,
0427 .proc_lseek = seq_lseek,
0428 .proc_read = seq_read,
0429 };
0430
0431 static struct snd_info_entry *create_subdir(struct module *mod,
0432 const char *name)
0433 {
0434 struct snd_info_entry *entry;
0435
0436 entry = snd_info_create_module_entry(mod, name, NULL);
0437 if (!entry)
0438 return NULL;
0439 entry->mode = S_IFDIR | 0555;
0440 if (snd_info_register(entry) < 0) {
0441 snd_info_free_entry(entry);
0442 return NULL;
0443 }
0444 return entry;
0445 }
0446
0447 static struct snd_info_entry *
0448 snd_info_create_entry(const char *name, struct snd_info_entry *parent,
0449 struct module *module);
0450
0451 int __init snd_info_init(void)
0452 {
0453 snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
0454 if (!snd_proc_root)
0455 return -ENOMEM;
0456 snd_proc_root->mode = S_IFDIR | 0555;
0457 snd_proc_root->p = proc_mkdir("asound", NULL);
0458 if (!snd_proc_root->p)
0459 goto error;
0460 #ifdef CONFIG_SND_OSSEMUL
0461 snd_oss_root = create_subdir(THIS_MODULE, "oss");
0462 if (!snd_oss_root)
0463 goto error;
0464 #endif
0465 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
0466 snd_seq_root = create_subdir(THIS_MODULE, "seq");
0467 if (!snd_seq_root)
0468 goto error;
0469 #endif
0470 if (snd_info_version_init() < 0 ||
0471 snd_minor_info_init() < 0 ||
0472 snd_minor_info_oss_init() < 0 ||
0473 snd_card_info_init() < 0 ||
0474 snd_info_minor_register() < 0)
0475 goto error;
0476 return 0;
0477
0478 error:
0479 snd_info_free_entry(snd_proc_root);
0480 return -ENOMEM;
0481 }
0482
0483 int __exit snd_info_done(void)
0484 {
0485 snd_info_free_entry(snd_proc_root);
0486 return 0;
0487 }
0488
0489 static void snd_card_id_read(struct snd_info_entry *entry,
0490 struct snd_info_buffer *buffer)
0491 {
0492 struct snd_card *card = entry->private_data;
0493
0494 snd_iprintf(buffer, "%s\n", card->id);
0495 }
0496
0497
0498
0499
0500
0501 int snd_info_card_create(struct snd_card *card)
0502 {
0503 char str[8];
0504 struct snd_info_entry *entry;
0505
0506 if (snd_BUG_ON(!card))
0507 return -ENXIO;
0508
0509 sprintf(str, "card%i", card->number);
0510 entry = create_subdir(card->module, str);
0511 if (!entry)
0512 return -ENOMEM;
0513 card->proc_root = entry;
0514
0515 return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
0516 }
0517
0518
0519
0520
0521
0522
0523 int snd_info_card_register(struct snd_card *card)
0524 {
0525 struct proc_dir_entry *p;
0526 int err;
0527
0528 if (snd_BUG_ON(!card))
0529 return -ENXIO;
0530
0531 err = snd_info_register(card->proc_root);
0532 if (err < 0)
0533 return err;
0534
0535 if (!strcmp(card->id, card->proc_root->name))
0536 return 0;
0537
0538 if (card->proc_root_link)
0539 return 0;
0540 p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
0541 if (!p)
0542 return -ENOMEM;
0543 card->proc_root_link = p;
0544 return 0;
0545 }
0546
0547
0548
0549
0550 void snd_info_card_id_change(struct snd_card *card)
0551 {
0552 mutex_lock(&info_mutex);
0553 if (card->proc_root_link) {
0554 proc_remove(card->proc_root_link);
0555 card->proc_root_link = NULL;
0556 }
0557 if (strcmp(card->id, card->proc_root->name))
0558 card->proc_root_link = proc_symlink(card->id,
0559 snd_proc_root->p,
0560 card->proc_root->name);
0561 mutex_unlock(&info_mutex);
0562 }
0563
0564
0565
0566
0567
0568 void snd_info_card_disconnect(struct snd_card *card)
0569 {
0570 if (!card)
0571 return;
0572 mutex_lock(&info_mutex);
0573 proc_remove(card->proc_root_link);
0574 card->proc_root_link = NULL;
0575 if (card->proc_root)
0576 snd_info_disconnect(card->proc_root);
0577 mutex_unlock(&info_mutex);
0578 }
0579
0580
0581
0582
0583
0584 int snd_info_card_free(struct snd_card *card)
0585 {
0586 if (!card)
0587 return 0;
0588 snd_info_free_entry(card->proc_root);
0589 card->proc_root = NULL;
0590 return 0;
0591 }
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603
0604 int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
0605 {
0606 int c;
0607
0608 if (snd_BUG_ON(!buffer))
0609 return 1;
0610 if (!buffer->buffer)
0611 return 1;
0612 if (len <= 0 || buffer->stop || buffer->error)
0613 return 1;
0614 while (!buffer->stop) {
0615 c = buffer->buffer[buffer->curr++];
0616 if (buffer->curr >= buffer->size)
0617 buffer->stop = 1;
0618 if (c == '\n')
0619 break;
0620 if (len > 1) {
0621 len--;
0622 *line++ = c;
0623 }
0624 }
0625 *line = '\0';
0626 return 0;
0627 }
0628 EXPORT_SYMBOL(snd_info_get_line);
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641
0642 const char *snd_info_get_str(char *dest, const char *src, int len)
0643 {
0644 int c;
0645
0646 while (*src == ' ' || *src == '\t')
0647 src++;
0648 if (*src == '"' || *src == '\'') {
0649 c = *src++;
0650 while (--len > 0 && *src && *src != c) {
0651 *dest++ = *src++;
0652 }
0653 if (*src == c)
0654 src++;
0655 } else {
0656 while (--len > 0 && *src && *src != ' ' && *src != '\t') {
0657 *dest++ = *src++;
0658 }
0659 }
0660 *dest = 0;
0661 while (*src == ' ' || *src == '\t')
0662 src++;
0663 return src;
0664 }
0665 EXPORT_SYMBOL(snd_info_get_str);
0666
0667
0668
0669
0670
0671
0672
0673
0674
0675
0676
0677
0678
0679
0680 static struct snd_info_entry *
0681 snd_info_create_entry(const char *name, struct snd_info_entry *parent,
0682 struct module *module)
0683 {
0684 struct snd_info_entry *entry;
0685 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
0686 if (entry == NULL)
0687 return NULL;
0688 entry->name = kstrdup(name, GFP_KERNEL);
0689 if (entry->name == NULL) {
0690 kfree(entry);
0691 return NULL;
0692 }
0693 entry->mode = S_IFREG | 0444;
0694 entry->content = SNDRV_INFO_CONTENT_TEXT;
0695 mutex_init(&entry->access);
0696 INIT_LIST_HEAD(&entry->children);
0697 INIT_LIST_HEAD(&entry->list);
0698 entry->parent = parent;
0699 entry->module = module;
0700 if (parent) {
0701 mutex_lock(&parent->access);
0702 list_add_tail(&entry->list, &parent->children);
0703 mutex_unlock(&parent->access);
0704 }
0705 return entry;
0706 }
0707
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717
0718 struct snd_info_entry *snd_info_create_module_entry(struct module * module,
0719 const char *name,
0720 struct snd_info_entry *parent)
0721 {
0722 if (!parent)
0723 parent = snd_proc_root;
0724 return snd_info_create_entry(name, parent, module);
0725 }
0726 EXPORT_SYMBOL(snd_info_create_module_entry);
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736
0737
0738 struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
0739 const char *name,
0740 struct snd_info_entry * parent)
0741 {
0742 if (!parent)
0743 parent = card->proc_root;
0744 return snd_info_create_entry(name, parent, card->module);
0745 }
0746 EXPORT_SYMBOL(snd_info_create_card_entry);
0747
0748 static void snd_info_disconnect(struct snd_info_entry *entry)
0749 {
0750 struct snd_info_entry *p;
0751
0752 if (!entry->p)
0753 return;
0754 list_for_each_entry(p, &entry->children, list)
0755 snd_info_disconnect(p);
0756 proc_remove(entry->p);
0757 entry->p = NULL;
0758 }
0759
0760
0761
0762
0763
0764
0765
0766 void snd_info_free_entry(struct snd_info_entry * entry)
0767 {
0768 struct snd_info_entry *p, *n;
0769
0770 if (!entry)
0771 return;
0772 if (entry->p) {
0773 mutex_lock(&info_mutex);
0774 snd_info_disconnect(entry);
0775 mutex_unlock(&info_mutex);
0776 }
0777
0778
0779 list_for_each_entry_safe(p, n, &entry->children, list)
0780 snd_info_free_entry(p);
0781
0782 p = entry->parent;
0783 if (p) {
0784 mutex_lock(&p->access);
0785 list_del(&entry->list);
0786 mutex_unlock(&p->access);
0787 }
0788 kfree(entry->name);
0789 if (entry->private_free)
0790 entry->private_free(entry);
0791 kfree(entry);
0792 }
0793 EXPORT_SYMBOL(snd_info_free_entry);
0794
0795 static int __snd_info_register(struct snd_info_entry *entry)
0796 {
0797 struct proc_dir_entry *root, *p = NULL;
0798
0799 if (snd_BUG_ON(!entry))
0800 return -ENXIO;
0801 root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
0802 mutex_lock(&info_mutex);
0803 if (entry->p || !root)
0804 goto unlock;
0805 if (S_ISDIR(entry->mode)) {
0806 p = proc_mkdir_mode(entry->name, entry->mode, root);
0807 if (!p) {
0808 mutex_unlock(&info_mutex);
0809 return -ENOMEM;
0810 }
0811 } else {
0812 const struct proc_ops *ops;
0813 if (entry->content == SNDRV_INFO_CONTENT_DATA)
0814 ops = &snd_info_entry_operations;
0815 else
0816 ops = &snd_info_text_entry_ops;
0817 p = proc_create_data(entry->name, entry->mode, root,
0818 ops, entry);
0819 if (!p) {
0820 mutex_unlock(&info_mutex);
0821 return -ENOMEM;
0822 }
0823 proc_set_size(p, entry->size);
0824 }
0825 entry->p = p;
0826 unlock:
0827 mutex_unlock(&info_mutex);
0828 return 0;
0829 }
0830
0831
0832
0833
0834
0835
0836
0837
0838
0839
0840 int snd_info_register(struct snd_info_entry *entry)
0841 {
0842 struct snd_info_entry *p;
0843 int err;
0844
0845 if (!entry->p) {
0846 err = __snd_info_register(entry);
0847 if (err < 0)
0848 return err;
0849 }
0850
0851 list_for_each_entry(p, &entry->children, list) {
0852 err = snd_info_register(p);
0853 if (err < 0)
0854 return err;
0855 }
0856
0857 return 0;
0858 }
0859 EXPORT_SYMBOL(snd_info_register);
0860
0861
0862
0863
0864
0865
0866
0867
0868
0869
0870
0871
0872
0873
0874 int snd_card_rw_proc_new(struct snd_card *card, const char *name,
0875 void *private_data,
0876 void (*read)(struct snd_info_entry *,
0877 struct snd_info_buffer *),
0878 void (*write)(struct snd_info_entry *entry,
0879 struct snd_info_buffer *buffer))
0880 {
0881 struct snd_info_entry *entry;
0882
0883 entry = snd_info_create_card_entry(card, name, card->proc_root);
0884 if (!entry)
0885 return -ENOMEM;
0886 snd_info_set_text_ops(entry, private_data, read);
0887 if (write) {
0888 entry->mode |= 0200;
0889 entry->c.text.write = write;
0890 }
0891 return 0;
0892 }
0893 EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
0894
0895
0896
0897
0898
0899 static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
0900 {
0901 snd_iprintf(buffer,
0902 "Advanced Linux Sound Architecture Driver Version k%s.\n",
0903 init_utsname()->release);
0904 }
0905
0906 static int __init snd_info_version_init(void)
0907 {
0908 struct snd_info_entry *entry;
0909
0910 entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
0911 if (entry == NULL)
0912 return -ENOMEM;
0913 entry->c.text.read = snd_info_version_read;
0914 return snd_info_register(entry);
0915 }