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