Linux-2.6.12-rc2
[linux-2.6.git] / sound / core / info.c
1 /*
2  *  Information interface for ALSA driver
3  *  Copyright (c) by Jaroslav Kysela <perex@suse.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 <sound/driver.h>
23 #include <linux/init.h>
24 #include <linux/vmalloc.h>
25 #include <linux/time.h>
26 #include <linux/smp_lock.h>
27 #include <sound/core.h>
28 #include <sound/minors.h>
29 #include <sound/info.h>
30 #include <sound/version.h>
31 #include <linux/proc_fs.h>
32 #include <linux/devfs_fs_kernel.h>
33 #include <stdarg.h>
34
35 /*
36  *
37  */
38
39 int snd_info_check_reserved_words(const char *str)
40 {
41         static char *reserved[] =
42         {
43                 "version",
44                 "meminfo",
45                 "memdebug",
46                 "detect",
47                 "devices",
48                 "oss",
49                 "cards",
50                 "timers",
51                 "synth",
52                 "pcm",
53                 "seq",
54                 NULL
55         };
56         char **xstr = reserved;
57
58         while (*xstr) {
59                 if (!strcmp(*xstr, str))
60                         return 0;
61                 xstr++;
62         }
63         if (!strncmp(str, "card", 4))
64                 return 0;
65         return 1;
66 }
67
68 #ifdef CONFIG_PROC_FS
69
70 static DECLARE_MUTEX(info_mutex);
71
72 typedef struct _snd_info_private_data {
73         snd_info_buffer_t *rbuffer;
74         snd_info_buffer_t *wbuffer;
75         snd_info_entry_t *entry;
76         void *file_private_data;
77 } snd_info_private_data_t;
78
79 static int snd_info_version_init(void);
80 static int snd_info_version_done(void);
81
82
83 /**
84  * snd_iprintf - printf on the procfs buffer
85  * @buffer: the procfs buffer
86  * @fmt: the printf format
87  *
88  * Outputs the string on the procfs buffer just like printf().
89  *
90  * Returns the size of output string.
91  */
92 int snd_iprintf(snd_info_buffer_t * buffer, char *fmt,...)
93 {
94         va_list args;
95         int len, res;
96
97         if (buffer->stop || buffer->error)
98                 return 0;
99         len = buffer->len - buffer->size;
100         va_start(args, fmt);
101         res = vsnprintf(buffer->curr, len, fmt, args);
102         va_end(args);
103         if (res >= len) {
104                 buffer->stop = 1;
105                 return 0;
106         }
107         buffer->curr += res;
108         buffer->size += res;
109         return res;
110 }
111
112 /*
113
114  */
115
116 static struct proc_dir_entry *snd_proc_root = NULL;
117 snd_info_entry_t *snd_seq_root = NULL;
118 #ifdef CONFIG_SND_OSSEMUL
119 snd_info_entry_t *snd_oss_root = NULL;
120 #endif
121
122 static inline void snd_info_entry_prepare(struct proc_dir_entry *de)
123 {
124         de->owner = THIS_MODULE;
125 }
126
127 static void snd_remove_proc_entry(struct proc_dir_entry *parent,
128                                   struct proc_dir_entry *de)
129 {
130         if (de)
131                 remove_proc_entry(de->name, parent);
132 }
133
134 static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
135 {
136         snd_info_private_data_t *data;
137         struct snd_info_entry *entry;
138         loff_t ret;
139
140         data = file->private_data;
141         entry = data->entry;
142         lock_kernel();
143         switch (entry->content) {
144         case SNDRV_INFO_CONTENT_TEXT:
145                 switch (orig) {
146                 case 0: /* SEEK_SET */
147                         file->f_pos = offset;
148                         ret = file->f_pos;
149                         goto out;
150                 case 1: /* SEEK_CUR */
151                         file->f_pos += offset;
152                         ret = file->f_pos;
153                         goto out;
154                 case 2: /* SEEK_END */
155                 default:
156                         ret = -EINVAL;
157                         goto out;
158                 }
159                 break;
160         case SNDRV_INFO_CONTENT_DATA:
161                 if (entry->c.ops->llseek) {
162                         ret = entry->c.ops->llseek(entry,
163                                                     data->file_private_data,
164                                                     file, offset, orig);
165                         goto out;
166                 }
167                 break;
168         }
169         ret = -ENXIO;
170 out:
171         unlock_kernel();
172         return ret;
173 }
174
175 static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
176                                    size_t count, loff_t * offset)
177 {
178         snd_info_private_data_t *data;
179         struct snd_info_entry *entry;
180         snd_info_buffer_t *buf;
181         size_t size = 0;
182         loff_t pos;
183
184         data = file->private_data;
185         snd_assert(data != NULL, return -ENXIO);
186         pos = *offset;
187         if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
188                 return -EIO;
189         if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
190                 return -EIO;
191         entry = data->entry;
192         switch (entry->content) {
193         case SNDRV_INFO_CONTENT_TEXT:
194                 buf = data->rbuffer;
195                 if (buf == NULL)
196                         return -EIO;
197                 if (pos >= buf->size)
198                         return 0;
199                 size = buf->size - pos;
200                 size = min(count, size);
201                 if (copy_to_user(buffer, buf->buffer + pos, size))
202                         return -EFAULT;
203                 break;
204         case SNDRV_INFO_CONTENT_DATA:
205                 if (entry->c.ops->read)
206                         size = entry->c.ops->read(entry,
207                                                   data->file_private_data,
208                                                   file, buffer, count, pos);
209                 break;
210         }
211         if ((ssize_t) size > 0)
212                 *offset = pos + size;
213         return size;
214 }
215
216 static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
217                                     size_t count, loff_t * offset)
218 {
219         snd_info_private_data_t *data;
220         struct snd_info_entry *entry;
221         snd_info_buffer_t *buf;
222         size_t size = 0;
223         loff_t pos;
224
225         data = file->private_data;
226         snd_assert(data != NULL, return -ENXIO);
227         entry = data->entry;
228         pos = *offset;
229         if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
230                 return -EIO;
231         if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
232                 return -EIO;
233         switch (entry->content) {
234         case SNDRV_INFO_CONTENT_TEXT:
235                 buf = data->wbuffer;
236                 if (buf == NULL)
237                         return -EIO;
238                 if (pos >= buf->len)
239                         return -ENOMEM;
240                 size = buf->len - pos;
241                 size = min(count, size);
242                 if (copy_from_user(buf->buffer + pos, buffer, size))
243                         return -EFAULT;
244                 if ((long)buf->size < pos + size)
245                         buf->size = pos + size;
246                 break;
247         case SNDRV_INFO_CONTENT_DATA:
248                 if (entry->c.ops->write)
249                         size = entry->c.ops->write(entry,
250                                                    data->file_private_data,
251                                                    file, buffer, count, pos);
252                 break;
253         }
254         if ((ssize_t) size > 0)
255                 *offset = pos + size;
256         return size;
257 }
258
259 static int snd_info_entry_open(struct inode *inode, struct file *file)
260 {
261         snd_info_entry_t *entry;
262         snd_info_private_data_t *data;
263         snd_info_buffer_t *buffer;
264         struct proc_dir_entry *p;
265         int mode, err;
266
267         down(&info_mutex);
268         p = PDE(inode);
269         entry = p == NULL ? NULL : (snd_info_entry_t *)p->data;
270         if (entry == NULL || entry->disconnected) {
271                 up(&info_mutex);
272                 return -ENODEV;
273         }
274         if (!try_module_get(entry->module)) {
275                 err = -EFAULT;
276                 goto __error1;
277         }
278         mode = file->f_flags & O_ACCMODE;
279         if (mode == O_RDONLY || mode == O_RDWR) {
280                 if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
281                      !entry->c.text.read_size) ||
282                     (entry->content == SNDRV_INFO_CONTENT_DATA &&
283                      entry->c.ops->read == NULL)) {
284                         err = -ENODEV;
285                         goto __error;
286                 }
287         }
288         if (mode == O_WRONLY || mode == O_RDWR) {
289                 if ((entry->content == SNDRV_INFO_CONTENT_TEXT &&
290                      !entry->c.text.write_size) ||
291                     (entry->content == SNDRV_INFO_CONTENT_DATA &&
292                      entry->c.ops->write == NULL)) {
293                         err = -ENODEV;
294                         goto __error;
295                 }
296         }
297         data = kcalloc(1, sizeof(*data), GFP_KERNEL);
298         if (data == NULL) {
299                 err = -ENOMEM;
300                 goto __error;
301         }
302         data->entry = entry;
303         switch (entry->content) {
304         case SNDRV_INFO_CONTENT_TEXT:
305                 if (mode == O_RDONLY || mode == O_RDWR) {
306                         buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);
307                         if (buffer == NULL) {
308                                 kfree(data);
309                                 err = -ENOMEM;
310                                 goto __error;
311                         }
312                         buffer->len = (entry->c.text.read_size +
313                                       (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
314                         buffer->buffer = vmalloc(buffer->len);
315                         if (buffer->buffer == NULL) {
316                                 kfree(buffer);
317                                 kfree(data);
318                                 err = -ENOMEM;
319                                 goto __error;
320                         }
321                         buffer->curr = buffer->buffer;
322                         data->rbuffer = buffer;
323                 }
324                 if (mode == O_WRONLY || mode == O_RDWR) {
325                         buffer = kcalloc(1, sizeof(*buffer), GFP_KERNEL);
326                         if (buffer == NULL) {
327                                 if (mode == O_RDWR) {
328                                         vfree(data->rbuffer->buffer);
329                                         kfree(data->rbuffer);
330                                 }
331                                 kfree(data);
332                                 err = -ENOMEM;
333                                 goto __error;
334                         }
335                         buffer->len = (entry->c.text.write_size +
336                                       (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
337                         buffer->buffer = vmalloc(buffer->len);
338                         if (buffer->buffer == NULL) {
339                                 if (mode == O_RDWR) {
340                                         vfree(data->rbuffer->buffer);
341                                         kfree(data->rbuffer);
342                                 }
343                                 kfree(buffer);
344                                 kfree(data);
345                                 err = -ENOMEM;
346                                 goto __error;
347                         }
348                         buffer->curr = buffer->buffer;
349                         data->wbuffer = buffer;
350                 }
351                 break;
352         case SNDRV_INFO_CONTENT_DATA:   /* data */
353                 if (entry->c.ops->open) {
354                         if ((err = entry->c.ops->open(entry, mode,
355                                                       &data->file_private_data)) < 0) {
356                                 kfree(data);
357                                 goto __error;
358                         }
359                 }
360                 break;
361         }
362         file->private_data = data;
363         up(&info_mutex);
364         if (entry->content == SNDRV_INFO_CONTENT_TEXT &&
365             (mode == O_RDONLY || mode == O_RDWR)) {
366                 if (entry->c.text.read) {
367                         down(&entry->access);
368                         entry->c.text.read(entry, data->rbuffer);
369                         up(&entry->access);
370                 }
371         }
372         return 0;
373
374       __error:
375         module_put(entry->module);
376       __error1:
377         up(&info_mutex);
378         return err;
379 }
380
381 static int snd_info_entry_release(struct inode *inode, struct file *file)
382 {
383         snd_info_entry_t *entry;
384         snd_info_private_data_t *data;
385         int mode;
386
387         mode = file->f_flags & O_ACCMODE;
388         data = file->private_data;
389         entry = data->entry;
390         switch (entry->content) {
391         case SNDRV_INFO_CONTENT_TEXT:
392                 if (mode == O_RDONLY || mode == O_RDWR) {
393                         vfree(data->rbuffer->buffer);
394                         kfree(data->rbuffer);
395                 }
396                 if (mode == O_WRONLY || mode == O_RDWR) {
397                         if (entry->c.text.write) {
398                                 entry->c.text.write(entry, data->wbuffer);
399                                 if (data->wbuffer->error) {
400                                         snd_printk(KERN_WARNING "data write error to %s (%i)\n",
401                                                 entry->name,
402                                                 data->wbuffer->error);
403                                 }
404                         }
405                         vfree(data->wbuffer->buffer);
406                         kfree(data->wbuffer);
407                 }
408                 break;
409         case SNDRV_INFO_CONTENT_DATA:
410                 if (entry->c.ops->release)
411                         entry->c.ops->release(entry, mode,
412                                               data->file_private_data);
413                 break;
414         }
415         module_put(entry->module);
416         kfree(data);
417         return 0;
418 }
419
420 static unsigned int snd_info_entry_poll(struct file *file, poll_table * wait)
421 {
422         snd_info_private_data_t *data;
423         struct snd_info_entry *entry;
424         unsigned int mask;
425
426         data = file->private_data;
427         if (data == NULL)
428                 return 0;
429         entry = data->entry;
430         mask = 0;
431         switch (entry->content) {
432         case SNDRV_INFO_CONTENT_DATA:
433                 if (entry->c.ops->poll)
434                         return entry->c.ops->poll(entry,
435                                                   data->file_private_data,
436                                                   file, wait);
437                 if (entry->c.ops->read)
438                         mask |= POLLIN | POLLRDNORM;
439                 if (entry->c.ops->write)
440                         mask |= POLLOUT | POLLWRNORM;
441                 break;
442         }
443         return mask;
444 }
445
446 static inline int _snd_info_entry_ioctl(struct inode *inode, struct file *file,
447                                         unsigned int cmd, unsigned long arg)
448 {
449         snd_info_private_data_t *data;
450         struct snd_info_entry *entry;
451
452         data = file->private_data;
453         if (data == NULL)
454                 return 0;
455         entry = data->entry;
456         switch (entry->content) {
457         case SNDRV_INFO_CONTENT_DATA:
458                 if (entry->c.ops->ioctl)
459                         return entry->c.ops->ioctl(entry,
460                                                    data->file_private_data,
461                                                    file, cmd, arg);
462                 break;
463         }
464         return -ENOTTY;
465 }
466
467 /* FIXME: need to unlock BKL to allow preemption */
468 static int snd_info_entry_ioctl(struct inode *inode, struct file *file,
469                                 unsigned int cmd, unsigned long arg)
470 {
471         int err;
472         unlock_kernel();
473         err = _snd_info_entry_ioctl(inode, file, cmd, arg);
474         lock_kernel();
475         return err;
476 }
477
478 static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
479 {
480         struct inode *inode = file->f_dentry->d_inode;
481         snd_info_private_data_t *data;
482         struct snd_info_entry *entry;
483
484         data = file->private_data;
485         if (data == NULL)
486                 return 0;
487         entry = data->entry;
488         switch (entry->content) {
489         case SNDRV_INFO_CONTENT_DATA:
490                 if (entry->c.ops->mmap)
491                         return entry->c.ops->mmap(entry,
492                                                   data->file_private_data,
493                                                   inode, file, vma);
494                 break;
495         }
496         return -ENXIO;
497 }
498
499 static struct file_operations snd_info_entry_operations =
500 {
501         .owner =        THIS_MODULE,
502         .llseek =       snd_info_entry_llseek,
503         .read =         snd_info_entry_read,
504         .write =        snd_info_entry_write,
505         .poll =         snd_info_entry_poll,
506         .ioctl =        snd_info_entry_ioctl,
507         .mmap =         snd_info_entry_mmap,
508         .open =         snd_info_entry_open,
509         .release =      snd_info_entry_release,
510 };
511
512 /**
513  * snd_create_proc_entry - create a procfs entry
514  * @name: the name of the proc file
515  * @mode: the file permission bits, S_Ixxx
516  * @parent: the parent proc-directory entry
517  *
518  * Creates a new proc file entry with the given name and permission
519  * on the given directory.
520  *
521  * Returns the pointer of new instance or NULL on failure.
522  */
523 static struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
524                                                     struct proc_dir_entry *parent)
525 {
526         struct proc_dir_entry *p;
527         p = create_proc_entry(name, mode, parent);
528         if (p)
529                 snd_info_entry_prepare(p);
530         return p;
531 }
532
533 int __init snd_info_init(void)
534 {
535         struct proc_dir_entry *p;
536
537         p = snd_create_proc_entry("asound", S_IFDIR | S_IRUGO | S_IXUGO, &proc_root);
538         if (p == NULL)
539                 return -ENOMEM;
540         snd_proc_root = p;
541 #ifdef CONFIG_SND_OSSEMUL
542         {
543                 snd_info_entry_t *entry;
544                 if ((entry = snd_info_create_module_entry(THIS_MODULE, "oss", NULL)) == NULL)
545                         return -ENOMEM;
546                 entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
547                 if (snd_info_register(entry) < 0) {
548                         snd_info_free_entry(entry);
549                         return -ENOMEM;
550                 }
551                 snd_oss_root = entry;
552         }
553 #endif
554 #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
555         {
556                 snd_info_entry_t *entry;
557                 if ((entry = snd_info_create_module_entry(THIS_MODULE, "seq", NULL)) == NULL)
558                         return -ENOMEM;
559                 entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
560                 if (snd_info_register(entry) < 0) {
561                         snd_info_free_entry(entry);
562                         return -ENOMEM;
563                 }
564                 snd_seq_root = entry;
565         }
566 #endif
567         snd_info_version_init();
568         snd_memory_info_init();
569         snd_minor_info_init();
570         snd_minor_info_oss_init();
571         snd_card_info_init();
572         return 0;
573 }
574
575 int __exit snd_info_done(void)
576 {
577         snd_card_info_done();
578         snd_minor_info_oss_done();
579         snd_minor_info_done();
580         snd_memory_info_done();
581         snd_info_version_done();
582         if (snd_proc_root) {
583 #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
584                 if (snd_seq_root)
585                         snd_info_unregister(snd_seq_root);
586 #endif
587 #ifdef CONFIG_SND_OSSEMUL
588                 if (snd_oss_root)
589                         snd_info_unregister(snd_oss_root);
590 #endif
591                 snd_remove_proc_entry(&proc_root, snd_proc_root);
592         }
593         return 0;
594 }
595
596 /*
597
598  */
599
600
601 /*
602  * create a card proc file
603  * called from init.c
604  */
605 int snd_info_card_create(snd_card_t * card)
606 {
607         char str[8];
608         snd_info_entry_t *entry;
609
610         snd_assert(card != NULL, return -ENXIO);
611
612         sprintf(str, "card%i", card->number);
613         if ((entry = snd_info_create_module_entry(card->module, str, NULL)) == NULL)
614                 return -ENOMEM;
615         entry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
616         if (snd_info_register(entry) < 0) {
617                 snd_info_free_entry(entry);
618                 return -ENOMEM;
619         }
620         card->proc_root = entry;
621         return 0;
622 }
623
624 /*
625  * register the card proc file
626  * called from init.c
627  */
628 int snd_info_card_register(snd_card_t * card)
629 {
630         struct proc_dir_entry *p;
631
632         snd_assert(card != NULL, return -ENXIO);
633
634         if (!strcmp(card->id, card->proc_root->name))
635                 return 0;
636
637         p = proc_symlink(card->id, snd_proc_root, card->proc_root->name);
638         if (p == NULL)
639                 return -ENOMEM;
640         card->proc_root_link = p;
641         return 0;
642 }
643
644 /*
645  * de-register the card proc file
646  * called from init.c
647  */
648 int snd_info_card_free(snd_card_t * card)
649 {
650         snd_assert(card != NULL, return -ENXIO);
651         if (card->proc_root_link) {
652                 snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
653                 card->proc_root_link = NULL;
654         }
655         if (card->proc_root) {
656                 snd_info_unregister(card->proc_root);
657                 card->proc_root = NULL;
658         }
659         return 0;
660 }
661
662
663 /**
664  * snd_info_get_line - read one line from the procfs buffer
665  * @buffer: the procfs buffer
666  * @line: the buffer to store
667  * @len: the max. buffer size - 1
668  *
669  * Reads one line from the buffer and stores the string.
670  *
671  * Returns zero if successful, or 1 if error or EOF.
672  */
673 int snd_info_get_line(snd_info_buffer_t * buffer, char *line, int len)
674 {
675         int c = -1;
676
677         if (len <= 0 || buffer->stop || buffer->error)
678                 return 1;
679         while (--len > 0) {
680                 c = *buffer->curr++;
681                 if (c == '\n') {
682                         if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
683                                 buffer->stop = 1;
684                         }
685                         break;
686                 }
687                 *line++ = c;
688                 if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
689                         buffer->stop = 1;
690                         break;
691                 }
692         }
693         while (c != '\n' && !buffer->stop) {
694                 c = *buffer->curr++;
695                 if ((buffer->curr - buffer->buffer) >= (long)buffer->size) {
696                         buffer->stop = 1;
697                 }
698         }
699         *line = '\0';
700         return 0;
701 }
702
703 /**
704  * snd_info_get_line - parse a string token
705  * @dest: the buffer to store the string token
706  * @src: the original string
707  * @len: the max. length of token - 1
708  *
709  * Parses the original string and copy a token to the given
710  * string buffer.
711  *
712  * Returns the updated pointer of the original string so that
713  * it can be used for the next call.
714  */
715 char *snd_info_get_str(char *dest, char *src, int len)
716 {
717         int c;
718
719         while (*src == ' ' || *src == '\t')
720                 src++;
721         if (*src == '"' || *src == '\'') {
722                 c = *src++;
723                 while (--len > 0 && *src && *src != c) {
724                         *dest++ = *src++;
725                 }
726                 if (*src == c)
727                         src++;
728         } else {
729                 while (--len > 0 && *src && *src != ' ' && *src != '\t') {
730                         *dest++ = *src++;
731                 }
732         }
733         *dest = 0;
734         while (*src == ' ' || *src == '\t')
735                 src++;
736         return src;
737 }
738
739 /**
740  * snd_info_create_entry - create an info entry
741  * @name: the proc file name
742  *
743  * Creates an info entry with the given file name and initializes as
744  * the default state.
745  *
746  * Usually called from other functions such as
747  * snd_info_create_card_entry().
748  *
749  * Returns the pointer of the new instance, or NULL on failure.
750  */
751 static snd_info_entry_t *snd_info_create_entry(const char *name)
752 {
753         snd_info_entry_t *entry;
754         entry = kcalloc(1, sizeof(*entry), GFP_KERNEL);
755         if (entry == NULL)
756                 return NULL;
757         entry->name = snd_kmalloc_strdup(name, GFP_KERNEL);
758         if (entry->name == NULL) {
759                 kfree(entry);
760                 return NULL;
761         }
762         entry->mode = S_IFREG | S_IRUGO;
763         entry->content = SNDRV_INFO_CONTENT_TEXT;
764         init_MUTEX(&entry->access);
765         return entry;
766 }
767
768 /**
769  * snd_info_create_module_entry - create an info entry for the given module
770  * @module: the module pointer
771  * @name: the file name
772  * @parent: the parent directory
773  *
774  * Creates a new info entry and assigns it to the given module.
775  *
776  * Returns the pointer of the new instance, or NULL on failure.
777  */
778 snd_info_entry_t *snd_info_create_module_entry(struct module * module,
779                                                const char *name,
780                                                snd_info_entry_t *parent)
781 {
782         snd_info_entry_t *entry = snd_info_create_entry(name);
783         if (entry) {
784                 entry->module = module;
785                 entry->parent = parent;
786         }
787         return entry;
788 }
789
790 /**
791  * snd_info_create_card_entry - create an info entry for the given card
792  * @card: the card instance
793  * @name: the file name
794  * @parent: the parent directory
795  *
796  * Creates a new info entry and assigns it to the given card.
797  *
798  * Returns the pointer of the new instance, or NULL on failure.
799  */
800 snd_info_entry_t *snd_info_create_card_entry(snd_card_t * card,
801                                              const char *name,
802                                              snd_info_entry_t * parent)
803 {
804         snd_info_entry_t *entry = snd_info_create_entry(name);
805         if (entry) {
806                 entry->module = card->module;
807                 entry->card = card;
808                 entry->parent = parent;
809         }
810         return entry;
811 }
812
813 static int snd_info_dev_free_entry(snd_device_t *device)
814 {
815         snd_info_entry_t *entry = device->device_data;
816         snd_info_free_entry(entry);
817         return 0;
818 }
819
820 static int snd_info_dev_register_entry(snd_device_t *device)
821 {
822         snd_info_entry_t *entry = device->device_data;
823         return snd_info_register(entry);
824 }
825
826 static int snd_info_dev_disconnect_entry(snd_device_t *device)
827 {
828         snd_info_entry_t *entry = device->device_data;
829         entry->disconnected = 1;
830         return 0;
831 }
832
833 static int snd_info_dev_unregister_entry(snd_device_t *device)
834 {
835         snd_info_entry_t *entry = device->device_data;
836         return snd_info_unregister(entry);
837 }
838
839 /**
840  * snd_card_proc_new - create an info entry for the given card
841  * @card: the card instance
842  * @name: the file name
843  * @entryp: the pointer to store the new info entry
844  *
845  * Creates a new info entry and assigns it to the given card.
846  * Unlike snd_info_create_card_entry(), this function registers the
847  * info entry as an ALSA device component, so that it can be
848  * unregistered/released without explicit call.
849  * Also, you don't have to register this entry via snd_info_register(),
850  * since this will be registered by snd_card_register() automatically.
851  *
852  * The parent is assumed as card->proc_root.
853  *
854  * For releasing this entry, use snd_device_free() instead of
855  * snd_info_free_entry(). 
856  *
857  * Returns zero if successful, or a negative error code on failure.
858  */
859 int snd_card_proc_new(snd_card_t *card, const char *name,
860                       snd_info_entry_t **entryp)
861 {
862         static snd_device_ops_t ops = {
863                 .dev_free = snd_info_dev_free_entry,
864                 .dev_register = snd_info_dev_register_entry,
865                 .dev_disconnect = snd_info_dev_disconnect_entry,
866                 .dev_unregister = snd_info_dev_unregister_entry
867         };
868         snd_info_entry_t *entry;
869         int err;
870
871         entry = snd_info_create_card_entry(card, name, card->proc_root);
872         if (! entry)
873                 return -ENOMEM;
874         if ((err = snd_device_new(card, SNDRV_DEV_INFO, entry, &ops)) < 0) {
875                 snd_info_free_entry(entry);
876                 return err;
877         }
878         if (entryp)
879                 *entryp = entry;
880         return 0;
881 }
882
883 /**
884  * snd_info_free_entry - release the info entry
885  * @entry: the info entry
886  *
887  * Releases the info entry.  Don't call this after registered.
888  */
889 void snd_info_free_entry(snd_info_entry_t * entry)
890 {
891         if (entry == NULL)
892                 return;
893         kfree(entry->name);
894         if (entry->private_free)
895                 entry->private_free(entry);
896         kfree(entry);
897 }
898
899 /**
900  * snd_info_register - register the info entry
901  * @entry: the info entry
902  *
903  * Registers the proc info entry.
904  *
905  * Returns zero if successful, or a negative error code on failure.
906  */
907 int snd_info_register(snd_info_entry_t * entry)
908 {
909         struct proc_dir_entry *root, *p = NULL;
910
911         snd_assert(entry != NULL, return -ENXIO);
912         root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
913         down(&info_mutex);
914         p = snd_create_proc_entry(entry->name, entry->mode, root);
915         if (!p) {
916                 up(&info_mutex);
917                 return -ENOMEM;
918         }
919         p->owner = entry->module;
920         if (!S_ISDIR(entry->mode))
921                 p->proc_fops = &snd_info_entry_operations;
922         p->size = entry->size;
923         p->data = entry;
924         entry->p = p;
925         up(&info_mutex);
926         return 0;
927 }
928
929 /**
930  * snd_info_unregister - de-register the info entry
931  * @entry: the info entry
932  *
933  * De-registers the info entry and releases the instance.
934  *
935  * Returns zero if successful, or a negative error code on failure.
936  */
937 int snd_info_unregister(snd_info_entry_t * entry)
938 {
939         struct proc_dir_entry *root;
940
941         snd_assert(entry != NULL && entry->p != NULL, return -ENXIO);
942         root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
943         snd_assert(root, return -ENXIO);
944         down(&info_mutex);
945         snd_remove_proc_entry(root, entry->p);
946         up(&info_mutex);
947         snd_info_free_entry(entry);
948         return 0;
949 }
950
951 /*
952
953  */
954
955 static snd_info_entry_t *snd_info_version_entry = NULL;
956
957 static void snd_info_version_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
958 {
959         snd_iprintf(buffer,
960                     "Advanced Linux Sound Architecture Driver Version "
961                     CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"
962                    );
963 }
964
965 static int __init snd_info_version_init(void)
966 {
967         snd_info_entry_t *entry;
968
969         entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
970         if (entry == NULL)
971                 return -ENOMEM;
972         entry->c.text.read_size = 256;
973         entry->c.text.read = snd_info_version_read;
974         if (snd_info_register(entry) < 0) {
975                 snd_info_free_entry(entry);
976                 return -ENOMEM;
977         }
978         snd_info_version_entry = entry;
979         return 0;
980 }
981
982 static int __exit snd_info_version_done(void)
983 {
984         if (snd_info_version_entry)
985                 snd_info_unregister(snd_info_version_entry);
986         return 0;
987 }
988
989 #endif /* CONFIG_PROC_FS */