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