11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Information interface for ALSA driver
4c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds #include <linux/init.h>
81da177e4SLinus Torvalds #include <linux/time.h>
927ac792cSAndrea Righi #include <linux/mm.h>
105a0e3ad6STejun Heo #include <linux/slab.h>
11543537bdSPaulo Marques #include <linux/string.h>
12da155d5bSPaul Gortmaker #include <linux/module.h>
131da177e4SLinus Torvalds #include <sound/core.h>
141da177e4SLinus Torvalds #include <sound/minors.h>
151da177e4SLinus Torvalds #include <sound/info.h>
1642662748SJaroslav Kysela #include <linux/utsname.h>
171da177e4SLinus Torvalds #include <linux/proc_fs.h>
181a60d4c5SIngo Molnar #include <linux/mutex.h>
191da177e4SLinus Torvalds
snd_info_check_reserved_words(const char * str)201da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str)
211da177e4SLinus Torvalds {
2251d7847aSTakashi Iwai static const char * const reserved[] =
231da177e4SLinus Torvalds {
241da177e4SLinus Torvalds "version",
251da177e4SLinus Torvalds "meminfo",
261da177e4SLinus Torvalds "memdebug",
271da177e4SLinus Torvalds "detect",
281da177e4SLinus Torvalds "devices",
291da177e4SLinus Torvalds "oss",
301da177e4SLinus Torvalds "cards",
311da177e4SLinus Torvalds "timers",
321da177e4SLinus Torvalds "synth",
331da177e4SLinus Torvalds "pcm",
341da177e4SLinus Torvalds "seq",
351da177e4SLinus Torvalds NULL
361da177e4SLinus Torvalds };
3751d7847aSTakashi Iwai const char * const *xstr = reserved;
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds while (*xstr) {
401da177e4SLinus Torvalds if (!strcmp(*xstr, str))
411da177e4SLinus Torvalds return 0;
421da177e4SLinus Torvalds xstr++;
431da177e4SLinus Torvalds }
441da177e4SLinus Torvalds if (!strncmp(str, "card", 4))
451da177e4SLinus Torvalds return 0;
461da177e4SLinus Torvalds return 1;
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds
491a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex);
501da177e4SLinus Torvalds
5124c1f931STakashi Iwai struct snd_info_private_data {
5224c1f931STakashi Iwai struct snd_info_buffer *rbuffer;
5324c1f931STakashi Iwai struct snd_info_buffer *wbuffer;
5424c1f931STakashi Iwai struct snd_info_entry *entry;
551da177e4SLinus Torvalds void *file_private_data;
5624c1f931STakashi Iwai };
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds static int snd_info_version_init(void);
59c7a60651STakashi Iwai static void snd_info_clear_entries(struct snd_info_entry *entry);
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds /*
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds */
641da177e4SLinus Torvalds
65644dbd64STakashi Iwai static struct snd_info_entry *snd_proc_root;
666581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root;
67c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root);
68c0d3fb39STakashi Iwai
691da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
706581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root;
711da177e4SLinus Torvalds #endif
721da177e4SLinus Torvalds
alloc_info_private(struct snd_info_entry * entry,struct snd_info_private_data ** ret)734adb7bcbSTakashi Iwai static int alloc_info_private(struct snd_info_entry *entry,
744adb7bcbSTakashi Iwai struct snd_info_private_data **ret)
754adb7bcbSTakashi Iwai {
764adb7bcbSTakashi Iwai struct snd_info_private_data *data;
774adb7bcbSTakashi Iwai
784adb7bcbSTakashi Iwai if (!entry || !entry->p)
794adb7bcbSTakashi Iwai return -ENODEV;
804adb7bcbSTakashi Iwai if (!try_module_get(entry->module))
814adb7bcbSTakashi Iwai return -EFAULT;
824adb7bcbSTakashi Iwai data = kzalloc(sizeof(*data), GFP_KERNEL);
834adb7bcbSTakashi Iwai if (!data) {
844adb7bcbSTakashi Iwai module_put(entry->module);
854adb7bcbSTakashi Iwai return -ENOMEM;
864adb7bcbSTakashi Iwai }
874adb7bcbSTakashi Iwai data->entry = entry;
884adb7bcbSTakashi Iwai *ret = data;
894adb7bcbSTakashi Iwai return 0;
904adb7bcbSTakashi Iwai }
914adb7bcbSTakashi Iwai
valid_pos(loff_t pos,size_t count)924adb7bcbSTakashi Iwai static bool valid_pos(loff_t pos, size_t count)
934adb7bcbSTakashi Iwai {
944adb7bcbSTakashi Iwai if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
954adb7bcbSTakashi Iwai return false;
964adb7bcbSTakashi Iwai if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
974adb7bcbSTakashi Iwai return false;
984adb7bcbSTakashi Iwai return true;
994adb7bcbSTakashi Iwai }
1004adb7bcbSTakashi Iwai
1014adb7bcbSTakashi Iwai /*
1024adb7bcbSTakashi Iwai * file ops for binary proc files
1034adb7bcbSTakashi Iwai */
snd_info_entry_llseek(struct file * file,loff_t offset,int orig)1041da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
1051da177e4SLinus Torvalds {
10624c1f931STakashi Iwai struct snd_info_private_data *data;
1071da177e4SLinus Torvalds struct snd_info_entry *entry;
108*4b72362bSTakashi Iwai loff_t size;
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds data = file->private_data;
1111da177e4SLinus Torvalds entry = data->entry;
112*4b72362bSTakashi Iwai guard(mutex)(&entry->access);
113*4b72362bSTakashi Iwai if (entry->c.ops->llseek)
114*4b72362bSTakashi Iwai return entry->c.ops->llseek(entry,
1151da177e4SLinus Torvalds data->file_private_data,
1161da177e4SLinus Torvalds file, offset, orig);
1174adb7bcbSTakashi Iwai
11873029e0fSTakashi Iwai size = entry->size;
11973029e0fSTakashi Iwai switch (orig) {
12073029e0fSTakashi Iwai case SEEK_SET:
1211da177e4SLinus Torvalds break;
12273029e0fSTakashi Iwai case SEEK_CUR:
12373029e0fSTakashi Iwai offset += file->f_pos;
12473029e0fSTakashi Iwai break;
12573029e0fSTakashi Iwai case SEEK_END:
12673029e0fSTakashi Iwai if (!size)
127*4b72362bSTakashi Iwai return -EINVAL;
12873029e0fSTakashi Iwai offset += size;
12973029e0fSTakashi Iwai break;
13073029e0fSTakashi Iwai default:
131*4b72362bSTakashi Iwai return -EINVAL;
1321da177e4SLinus Torvalds }
13373029e0fSTakashi Iwai if (offset < 0)
134*4b72362bSTakashi Iwai return -EINVAL;
13573029e0fSTakashi Iwai if (size && offset > size)
13673029e0fSTakashi Iwai offset = size;
13773029e0fSTakashi Iwai file->f_pos = offset;
138*4b72362bSTakashi Iwai return offset;
1391da177e4SLinus Torvalds }
1401da177e4SLinus Torvalds
snd_info_entry_read(struct file * file,char __user * buffer,size_t count,loff_t * offset)1411da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
1421da177e4SLinus Torvalds size_t count, loff_t * offset)
1431da177e4SLinus Torvalds {
1444adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
1454adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
1464adb7bcbSTakashi Iwai size_t size;
1471da177e4SLinus Torvalds loff_t pos;
1481da177e4SLinus Torvalds
1491da177e4SLinus Torvalds pos = *offset;
1504adb7bcbSTakashi Iwai if (!valid_pos(pos, count))
1511da177e4SLinus Torvalds return -EIO;
152d97e1b78STakashi Iwai if (pos >= entry->size)
153d97e1b78STakashi Iwai return 0;
154d97e1b78STakashi Iwai size = entry->size - pos;
155d97e1b78STakashi Iwai size = min(count, size);
1564adb7bcbSTakashi Iwai size = entry->c.ops->read(entry, data->file_private_data,
157d97e1b78STakashi Iwai file, buffer, size, pos);
1581da177e4SLinus Torvalds if ((ssize_t) size > 0)
1591da177e4SLinus Torvalds *offset = pos + size;
1601da177e4SLinus Torvalds return size;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds
snd_info_entry_write(struct file * file,const char __user * buffer,size_t count,loff_t * offset)1631da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
1641da177e4SLinus Torvalds size_t count, loff_t * offset)
1651da177e4SLinus Torvalds {
1664adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
1674adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
1687e4eeec8STakashi Iwai ssize_t size = 0;
1691da177e4SLinus Torvalds loff_t pos;
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds pos = *offset;
1724adb7bcbSTakashi Iwai if (!valid_pos(pos, count))
1731da177e4SLinus Torvalds return -EIO;
1744adb7bcbSTakashi Iwai if (count > 0) {
175d97e1b78STakashi Iwai size_t maxsize = entry->size - pos;
176d97e1b78STakashi Iwai count = min(count, maxsize);
1774adb7bcbSTakashi Iwai size = entry->c.ops->write(entry, data->file_private_data,
1781da177e4SLinus Torvalds file, buffer, count, pos);
179d97e1b78STakashi Iwai }
1804adb7bcbSTakashi Iwai if (size > 0)
1811da177e4SLinus Torvalds *offset = pos + size;
1821da177e4SLinus Torvalds return size;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds
snd_info_entry_poll(struct file * file,poll_table * wait)185680ef72aSAl Viro static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
1861da177e4SLinus Torvalds {
1874adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
1884adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
189680ef72aSAl Viro __poll_t mask = 0;
1901da177e4SLinus Torvalds
1911da177e4SLinus Torvalds if (entry->c.ops->poll)
1921da177e4SLinus Torvalds return entry->c.ops->poll(entry,
1931da177e4SLinus Torvalds data->file_private_data,
1941da177e4SLinus Torvalds file, wait);
1951da177e4SLinus Torvalds if (entry->c.ops->read)
196a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
1971da177e4SLinus Torvalds if (entry->c.ops->write)
198a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM;
1991da177e4SLinus Torvalds return mask;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds
snd_info_entry_ioctl(struct file * file,unsigned int cmd,unsigned long arg)202d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
203d99e9889SIngo Molnar unsigned long arg)
2041da177e4SLinus Torvalds {
2054adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
2064adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
2071da177e4SLinus Torvalds
2084adb7bcbSTakashi Iwai if (!entry->c.ops->ioctl)
2091da177e4SLinus Torvalds return -ENOTTY;
2104adb7bcbSTakashi Iwai return entry->c.ops->ioctl(entry, data->file_private_data,
2114adb7bcbSTakashi Iwai file, cmd, arg);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds
snd_info_entry_mmap(struct file * file,struct vm_area_struct * vma)2141da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
2151da177e4SLinus Torvalds {
216496ad9aaSAl Viro struct inode *inode = file_inode(file);
21724c1f931STakashi Iwai struct snd_info_private_data *data;
2181da177e4SLinus Torvalds struct snd_info_entry *entry;
2191da177e4SLinus Torvalds
2201da177e4SLinus Torvalds data = file->private_data;
2211da177e4SLinus Torvalds if (data == NULL)
2221da177e4SLinus Torvalds return 0;
2231da177e4SLinus Torvalds entry = data->entry;
2244adb7bcbSTakashi Iwai if (!entry->c.ops->mmap)
2251da177e4SLinus Torvalds return -ENXIO;
2264adb7bcbSTakashi Iwai return entry->c.ops->mmap(entry, data->file_private_data,
2274adb7bcbSTakashi Iwai inode, file, vma);
2284adb7bcbSTakashi Iwai }
2294adb7bcbSTakashi Iwai
snd_info_entry_open(struct inode * inode,struct file * file)2304adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file)
2314adb7bcbSTakashi Iwai {
232359745d7SMuchun Song struct snd_info_entry *entry = pde_data(inode);
2334adb7bcbSTakashi Iwai struct snd_info_private_data *data;
2344adb7bcbSTakashi Iwai int mode, err;
2354adb7bcbSTakashi Iwai
236*4b72362bSTakashi Iwai guard(mutex)(&info_mutex);
2374adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data);
2384adb7bcbSTakashi Iwai if (err < 0)
239*4b72362bSTakashi Iwai return err;
2404adb7bcbSTakashi Iwai
2414adb7bcbSTakashi Iwai mode = file->f_flags & O_ACCMODE;
2424adb7bcbSTakashi Iwai if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
2434adb7bcbSTakashi Iwai ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
2444adb7bcbSTakashi Iwai err = -ENODEV;
2454adb7bcbSTakashi Iwai goto error;
2464adb7bcbSTakashi Iwai }
2474adb7bcbSTakashi Iwai
2484adb7bcbSTakashi Iwai if (entry->c.ops->open) {
2494adb7bcbSTakashi Iwai err = entry->c.ops->open(entry, mode, &data->file_private_data);
2504adb7bcbSTakashi Iwai if (err < 0)
2514adb7bcbSTakashi Iwai goto error;
2524adb7bcbSTakashi Iwai }
2534adb7bcbSTakashi Iwai
2544adb7bcbSTakashi Iwai file->private_data = data;
2554adb7bcbSTakashi Iwai return 0;
2564adb7bcbSTakashi Iwai
2574adb7bcbSTakashi Iwai error:
2584adb7bcbSTakashi Iwai kfree(data);
2594adb7bcbSTakashi Iwai module_put(entry->module);
2604adb7bcbSTakashi Iwai return err;
2614adb7bcbSTakashi Iwai }
2624adb7bcbSTakashi Iwai
snd_info_entry_release(struct inode * inode,struct file * file)2634adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file)
2644adb7bcbSTakashi Iwai {
2654adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
2664adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
2674adb7bcbSTakashi Iwai
2684adb7bcbSTakashi Iwai if (entry->c.ops->release)
2694adb7bcbSTakashi Iwai entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
2704adb7bcbSTakashi Iwai data->file_private_data);
2714adb7bcbSTakashi Iwai module_put(entry->module);
2724adb7bcbSTakashi Iwai kfree(data);
2734adb7bcbSTakashi Iwai return 0;
2741da177e4SLinus Torvalds }
2751da177e4SLinus Torvalds
27697a32539SAlexey Dobriyan static const struct proc_ops snd_info_entry_operations =
2771da177e4SLinus Torvalds {
27897a32539SAlexey Dobriyan .proc_lseek = snd_info_entry_llseek,
27997a32539SAlexey Dobriyan .proc_read = snd_info_entry_read,
28097a32539SAlexey Dobriyan .proc_write = snd_info_entry_write,
28197a32539SAlexey Dobriyan .proc_poll = snd_info_entry_poll,
28297a32539SAlexey Dobriyan .proc_ioctl = snd_info_entry_ioctl,
28397a32539SAlexey Dobriyan .proc_mmap = snd_info_entry_mmap,
28497a32539SAlexey Dobriyan .proc_open = snd_info_entry_open,
28597a32539SAlexey Dobriyan .proc_release = snd_info_entry_release,
2861da177e4SLinus Torvalds };
2871da177e4SLinus Torvalds
2884adb7bcbSTakashi Iwai /*
2894adb7bcbSTakashi Iwai * file ops for text proc files
2904adb7bcbSTakashi Iwai */
snd_info_text_entry_write(struct file * file,const char __user * buffer,size_t count,loff_t * offset)2914adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file,
2924adb7bcbSTakashi Iwai const char __user *buffer,
2934adb7bcbSTakashi Iwai size_t count, loff_t *offset)
2944adb7bcbSTakashi Iwai {
2954adb7bcbSTakashi Iwai struct seq_file *m = file->private_data;
2964adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private;
2974adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
2984adb7bcbSTakashi Iwai struct snd_info_buffer *buf;
2994adb7bcbSTakashi Iwai loff_t pos;
3004adb7bcbSTakashi Iwai size_t next;
3014adb7bcbSTakashi Iwai
3026809cd68STakashi Iwai if (!entry->c.text.write)
3036809cd68STakashi Iwai return -EIO;
3044adb7bcbSTakashi Iwai pos = *offset;
3054adb7bcbSTakashi Iwai if (!valid_pos(pos, count))
3064adb7bcbSTakashi Iwai return -EIO;
3074adb7bcbSTakashi Iwai next = pos + count;
308027a9fe6STakashi Iwai /* don't handle too large text inputs */
309027a9fe6STakashi Iwai if (next > 16 * 1024)
310027a9fe6STakashi Iwai return -EIO;
311*4b72362bSTakashi Iwai guard(mutex)(&entry->access);
3124adb7bcbSTakashi Iwai buf = data->wbuffer;
3134adb7bcbSTakashi Iwai if (!buf) {
3144adb7bcbSTakashi Iwai data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
315*4b72362bSTakashi Iwai if (!buf)
316*4b72362bSTakashi Iwai return -ENOMEM;
3174adb7bcbSTakashi Iwai }
3184adb7bcbSTakashi Iwai if (next > buf->len) {
319ffb73b08STakashi Iwai char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
320*4b72362bSTakashi Iwai if (!nbuf)
321*4b72362bSTakashi Iwai return -ENOMEM;
322ffb73b08STakashi Iwai kvfree(buf->buffer);
3234adb7bcbSTakashi Iwai buf->buffer = nbuf;
3244adb7bcbSTakashi Iwai buf->len = PAGE_ALIGN(next);
3254adb7bcbSTakashi Iwai }
326*4b72362bSTakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count))
327*4b72362bSTakashi Iwai return -EFAULT;
3284adb7bcbSTakashi Iwai buf->size = next;
3294adb7bcbSTakashi Iwai *offset = next;
3304adb7bcbSTakashi Iwai return count;
3314adb7bcbSTakashi Iwai }
3324adb7bcbSTakashi Iwai
snd_info_seq_show(struct seq_file * seq,void * p)3334adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p)
3344adb7bcbSTakashi Iwai {
3354adb7bcbSTakashi Iwai struct snd_info_private_data *data = seq->private;
3364adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
3374adb7bcbSTakashi Iwai
3386809cd68STakashi Iwai if (!entry->c.text.read) {
3396809cd68STakashi Iwai return -EIO;
3406809cd68STakashi Iwai } else {
3414adb7bcbSTakashi Iwai data->rbuffer->buffer = (char *)seq; /* XXX hack! */
3424adb7bcbSTakashi Iwai entry->c.text.read(entry, data->rbuffer);
3434adb7bcbSTakashi Iwai }
3444adb7bcbSTakashi Iwai return 0;
3454adb7bcbSTakashi Iwai }
3464adb7bcbSTakashi Iwai
snd_info_text_entry_open(struct inode * inode,struct file * file)3474adb7bcbSTakashi Iwai static int snd_info_text_entry_open(struct inode *inode, struct file *file)
3484adb7bcbSTakashi Iwai {
349359745d7SMuchun Song struct snd_info_entry *entry = pde_data(inode);
3504adb7bcbSTakashi Iwai struct snd_info_private_data *data;
3514adb7bcbSTakashi Iwai int err;
3524adb7bcbSTakashi Iwai
353*4b72362bSTakashi Iwai guard(mutex)(&info_mutex);
3544adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data);
3554adb7bcbSTakashi Iwai if (err < 0)
356*4b72362bSTakashi Iwai return err;
3574adb7bcbSTakashi Iwai
3584adb7bcbSTakashi Iwai data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
3594adb7bcbSTakashi Iwai if (!data->rbuffer) {
3604adb7bcbSTakashi Iwai err = -ENOMEM;
3614adb7bcbSTakashi Iwai goto error;
3624adb7bcbSTakashi Iwai }
3634adb7bcbSTakashi Iwai if (entry->size)
3644adb7bcbSTakashi Iwai err = single_open_size(file, snd_info_seq_show, data,
3654adb7bcbSTakashi Iwai entry->size);
3664adb7bcbSTakashi Iwai else
3674adb7bcbSTakashi Iwai err = single_open(file, snd_info_seq_show, data);
3684adb7bcbSTakashi Iwai if (err < 0)
3694adb7bcbSTakashi Iwai goto error;
3704adb7bcbSTakashi Iwai return 0;
3714adb7bcbSTakashi Iwai
3724adb7bcbSTakashi Iwai error:
3734adb7bcbSTakashi Iwai kfree(data->rbuffer);
3744adb7bcbSTakashi Iwai kfree(data);
3754adb7bcbSTakashi Iwai module_put(entry->module);
3764adb7bcbSTakashi Iwai return err;
3774adb7bcbSTakashi Iwai }
3784adb7bcbSTakashi Iwai
snd_info_text_entry_release(struct inode * inode,struct file * file)3794adb7bcbSTakashi Iwai static int snd_info_text_entry_release(struct inode *inode, struct file *file)
3804adb7bcbSTakashi Iwai {
3814adb7bcbSTakashi Iwai struct seq_file *m = file->private_data;
3824adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private;
3834adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
3844adb7bcbSTakashi Iwai
3854adb7bcbSTakashi Iwai if (data->wbuffer && entry->c.text.write)
3864adb7bcbSTakashi Iwai entry->c.text.write(entry, data->wbuffer);
3874adb7bcbSTakashi Iwai
3884adb7bcbSTakashi Iwai single_release(inode, file);
3894adb7bcbSTakashi Iwai kfree(data->rbuffer);
3904adb7bcbSTakashi Iwai if (data->wbuffer) {
391ffb73b08STakashi Iwai kvfree(data->wbuffer->buffer);
3924adb7bcbSTakashi Iwai kfree(data->wbuffer);
3934adb7bcbSTakashi Iwai }
3944adb7bcbSTakashi Iwai
3954adb7bcbSTakashi Iwai module_put(entry->module);
3964adb7bcbSTakashi Iwai kfree(data);
3974adb7bcbSTakashi Iwai return 0;
3984adb7bcbSTakashi Iwai }
3994adb7bcbSTakashi Iwai
40097a32539SAlexey Dobriyan static const struct proc_ops snd_info_text_entry_ops =
4014adb7bcbSTakashi Iwai {
40297a32539SAlexey Dobriyan .proc_open = snd_info_text_entry_open,
40397a32539SAlexey Dobriyan .proc_release = snd_info_text_entry_release,
40497a32539SAlexey Dobriyan .proc_write = snd_info_text_entry_write,
40597a32539SAlexey Dobriyan .proc_lseek = seq_lseek,
40697a32539SAlexey Dobriyan .proc_read = seq_read,
4074adb7bcbSTakashi Iwai };
4084adb7bcbSTakashi Iwai
create_subdir(struct module * mod,const char * name)409886364f6STakashi Iwai static struct snd_info_entry *create_subdir(struct module *mod,
410886364f6STakashi Iwai const char *name)
411886364f6STakashi Iwai {
412886364f6STakashi Iwai struct snd_info_entry *entry;
413886364f6STakashi Iwai
414886364f6STakashi Iwai entry = snd_info_create_module_entry(mod, name, NULL);
415886364f6STakashi Iwai if (!entry)
416886364f6STakashi Iwai return NULL;
4176a73cf46SJoe Perches entry->mode = S_IFDIR | 0555;
418886364f6STakashi Iwai if (snd_info_register(entry) < 0) {
419886364f6STakashi Iwai snd_info_free_entry(entry);
420886364f6STakashi Iwai return NULL;
421886364f6STakashi Iwai }
422886364f6STakashi Iwai return entry;
423886364f6STakashi Iwai }
424886364f6STakashi Iwai
4258e7ccb7bSTakashi Iwai static struct snd_info_entry *
426a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent,
427a858ee66STakashi Iwai struct module *module);
428644dbd64STakashi Iwai
snd_info_init(void)4291da177e4SLinus Torvalds int __init snd_info_init(void)
4301da177e4SLinus Torvalds {
431a858ee66STakashi Iwai snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
432644dbd64STakashi Iwai if (!snd_proc_root)
4331da177e4SLinus Torvalds return -ENOMEM;
4346a73cf46SJoe Perches snd_proc_root->mode = S_IFDIR | 0555;
435644dbd64STakashi Iwai snd_proc_root->p = proc_mkdir("asound", NULL);
436644dbd64STakashi Iwai if (!snd_proc_root->p)
437644dbd64STakashi Iwai goto error;
4381da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
439886364f6STakashi Iwai snd_oss_root = create_subdir(THIS_MODULE, "oss");
440886364f6STakashi Iwai if (!snd_oss_root)
441886364f6STakashi Iwai goto error;
4421da177e4SLinus Torvalds #endif
4438eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
444886364f6STakashi Iwai snd_seq_root = create_subdir(THIS_MODULE, "seq");
445886364f6STakashi Iwai if (!snd_seq_root)
446886364f6STakashi Iwai goto error;
4471da177e4SLinus Torvalds #endif
448b591b6e9STakashi Iwai if (snd_info_version_init() < 0 ||
449b591b6e9STakashi Iwai snd_minor_info_init() < 0 ||
450b591b6e9STakashi Iwai snd_minor_info_oss_init() < 0 ||
451a0dca822STakashi Iwai snd_card_info_init() < 0 ||
452a0dca822STakashi Iwai snd_info_minor_register() < 0)
453b591b6e9STakashi Iwai goto error;
4541da177e4SLinus Torvalds return 0;
455886364f6STakashi Iwai
456886364f6STakashi Iwai error:
457644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root);
458886364f6STakashi Iwai return -ENOMEM;
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds
snd_info_done(void)4611da177e4SLinus Torvalds int __exit snd_info_done(void)
4621da177e4SLinus Torvalds {
463644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root);
4641da177e4SLinus Torvalds return 0;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds
snd_card_id_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)46729b2625fSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry,
46829b2625fSTakashi Iwai struct snd_info_buffer *buffer)
46929b2625fSTakashi Iwai {
47029b2625fSTakashi Iwai struct snd_card *card = entry->private_data;
47129b2625fSTakashi Iwai
47229b2625fSTakashi Iwai snd_iprintf(buffer, "%s\n", card->id);
47329b2625fSTakashi Iwai }
47429b2625fSTakashi Iwai
4751da177e4SLinus Torvalds /*
4761da177e4SLinus Torvalds * create a card proc file
4771da177e4SLinus Torvalds * called from init.c
4781da177e4SLinus Torvalds */
snd_info_card_create(struct snd_card * card)47924c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card)
4801da177e4SLinus Torvalds {
4811da177e4SLinus Torvalds char str[8];
48224c1f931STakashi Iwai struct snd_info_entry *entry;
4831da177e4SLinus Torvalds
4847eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
4857eaa943cSTakashi Iwai return -ENXIO;
4861da177e4SLinus Torvalds
4871da177e4SLinus Torvalds sprintf(str, "card%i", card->number);
488886364f6STakashi Iwai entry = create_subdir(card->module, str);
489886364f6STakashi Iwai if (!entry)
4901da177e4SLinus Torvalds return -ENOMEM;
4911da177e4SLinus Torvalds card->proc_root = entry;
49229b2625fSTakashi Iwai
49329b2625fSTakashi Iwai return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds
4961da177e4SLinus Torvalds /*
4971da177e4SLinus Torvalds * register the card proc file
4981da177e4SLinus Torvalds * called from init.c
4992471b6c8STakashi Iwai * can be called multiple times for reinitialization
5001da177e4SLinus Torvalds */
snd_info_card_register(struct snd_card * card)50124c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card)
5021da177e4SLinus Torvalds {
5031da177e4SLinus Torvalds struct proc_dir_entry *p;
5042471b6c8STakashi Iwai int err;
5051da177e4SLinus Torvalds
5067eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
5077eaa943cSTakashi Iwai return -ENXIO;
5081da177e4SLinus Torvalds
509348c5ad5STakashi Iwai err = snd_info_register(card->proc_root);
5102471b6c8STakashi Iwai if (err < 0)
5112471b6c8STakashi Iwai return err;
5122471b6c8STakashi Iwai
5131da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name))
5141da177e4SLinus Torvalds return 0;
5151da177e4SLinus Torvalds
5162471b6c8STakashi Iwai if (card->proc_root_link)
5172471b6c8STakashi Iwai return 0;
518644dbd64STakashi Iwai p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
5192471b6c8STakashi Iwai if (!p)
5201da177e4SLinus Torvalds return -ENOMEM;
5211da177e4SLinus Torvalds card->proc_root_link = p;
5221da177e4SLinus Torvalds return 0;
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds
5251da177e4SLinus Torvalds /*
526c2eb9c4eSJaroslav Kysela * called on card->id change
527c2eb9c4eSJaroslav Kysela */
snd_info_card_id_change(struct snd_card * card)528c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card)
529c2eb9c4eSJaroslav Kysela {
530*4b72362bSTakashi Iwai guard(mutex)(&info_mutex);
531c2eb9c4eSJaroslav Kysela if (card->proc_root_link) {
532a8ca16eaSDavid Howells proc_remove(card->proc_root_link);
533c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL;
534c2eb9c4eSJaroslav Kysela }
535c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name))
536c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id,
537644dbd64STakashi Iwai snd_proc_root->p,
538c2eb9c4eSJaroslav Kysela card->proc_root->name);
539c2eb9c4eSJaroslav Kysela }
540c2eb9c4eSJaroslav Kysela
541c2eb9c4eSJaroslav Kysela /*
5421da177e4SLinus Torvalds * de-register the card proc file
5431da177e4SLinus Torvalds * called from init.c
5441da177e4SLinus Torvalds */
snd_info_card_disconnect(struct snd_card * card)545746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card)
5461da177e4SLinus Torvalds {
5477eaa943cSTakashi Iwai if (!card)
5487eaa943cSTakashi Iwai return;
549c7a60651STakashi Iwai
550a8ca16eaSDavid Howells proc_remove(card->proc_root_link);
551746d4a02STakashi Iwai if (card->proc_root)
552c7a60651STakashi Iwai proc_remove(card->proc_root->p);
553c7a60651STakashi Iwai
554*4b72362bSTakashi Iwai guard(mutex)(&info_mutex);
555c7a60651STakashi Iwai if (card->proc_root)
556c7a60651STakashi Iwai snd_info_clear_entries(card->proc_root);
557c7a60651STakashi Iwai card->proc_root_link = NULL;
558c7a60651STakashi Iwai card->proc_root = NULL;
5591da177e4SLinus Torvalds }
560746d4a02STakashi Iwai
561746d4a02STakashi Iwai /*
562746d4a02STakashi Iwai * release the card proc file resources
563746d4a02STakashi Iwai * called from init.c
564746d4a02STakashi Iwai */
snd_info_card_free(struct snd_card * card)565746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card)
566746d4a02STakashi Iwai {
5677eaa943cSTakashi Iwai if (!card)
5687eaa943cSTakashi Iwai return 0;
569746d4a02STakashi Iwai snd_info_free_entry(card->proc_root);
570746d4a02STakashi Iwai card->proc_root = NULL;
5711da177e4SLinus Torvalds return 0;
5721da177e4SLinus Torvalds }
5731da177e4SLinus Torvalds
5741da177e4SLinus Torvalds
5751da177e4SLinus Torvalds /**
5761da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer
5771da177e4SLinus Torvalds * @buffer: the procfs buffer
5781da177e4SLinus Torvalds * @line: the buffer to store
579ddc64b27SClemens Ladisch * @len: the max. buffer size
5801da177e4SLinus Torvalds *
5811da177e4SLinus Torvalds * Reads one line from the buffer and stores the string.
5821da177e4SLinus Torvalds *
583eb7c06e8SYacine Belkadi * Return: Zero if successful, or 1 if error or EOF.
5841da177e4SLinus Torvalds */
snd_info_get_line(struct snd_info_buffer * buffer,char * line,int len)58524c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
5861da177e4SLinus Torvalds {
5870e023687SColin Ian King int c;
5881da177e4SLinus Torvalds
58960379ba0STakashi Iwai if (snd_BUG_ON(!buffer))
59060379ba0STakashi Iwai return 1;
59160379ba0STakashi Iwai if (!buffer->buffer)
5920bc0ec90STakashi Iwai return 1;
5931da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error)
5941da177e4SLinus Torvalds return 1;
5950bc0ec90STakashi Iwai while (!buffer->stop) {
5967e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++];
5977e4eeec8STakashi Iwai if (buffer->curr >= buffer->size)
5981da177e4SLinus Torvalds buffer->stop = 1;
5990bc0ec90STakashi Iwai if (c == '\n')
6001da177e4SLinus Torvalds break;
601ddc64b27SClemens Ladisch if (len > 1) {
6020bc0ec90STakashi Iwai len--;
6031da177e4SLinus Torvalds *line++ = c;
6041da177e4SLinus Torvalds }
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds *line = '\0';
6071da177e4SLinus Torvalds return 0;
6081da177e4SLinus Torvalds }
609c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line);
610c0d3fb39STakashi Iwai
6111da177e4SLinus Torvalds /**
612856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token
6131da177e4SLinus Torvalds * @dest: the buffer to store the string token
6141da177e4SLinus Torvalds * @src: the original string
6151da177e4SLinus Torvalds * @len: the max. length of token - 1
6161da177e4SLinus Torvalds *
6171da177e4SLinus Torvalds * Parses the original string and copy a token to the given
6181da177e4SLinus Torvalds * string buffer.
6191da177e4SLinus Torvalds *
620eb7c06e8SYacine Belkadi * Return: The updated pointer of the original string so that
6211da177e4SLinus Torvalds * it can be used for the next call.
6221da177e4SLinus Torvalds */
snd_info_get_str(char * dest,const char * src,int len)6234f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len)
6241da177e4SLinus Torvalds {
6251da177e4SLinus Torvalds int c;
6261da177e4SLinus Torvalds
6271da177e4SLinus Torvalds while (*src == ' ' || *src == '\t')
6281da177e4SLinus Torvalds src++;
6291da177e4SLinus Torvalds if (*src == '"' || *src == '\'') {
6301da177e4SLinus Torvalds c = *src++;
6311da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) {
6321da177e4SLinus Torvalds *dest++ = *src++;
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds if (*src == c)
6351da177e4SLinus Torvalds src++;
6361da177e4SLinus Torvalds } else {
6371da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') {
6381da177e4SLinus Torvalds *dest++ = *src++;
6391da177e4SLinus Torvalds }
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds *dest = 0;
6421da177e4SLinus Torvalds while (*src == ' ' || *src == '\t')
6431da177e4SLinus Torvalds src++;
6441da177e4SLinus Torvalds return src;
6451da177e4SLinus Torvalds }
646c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str);
647c0d3fb39STakashi Iwai
648c309c467STakashi Iwai /*
6491da177e4SLinus Torvalds * snd_info_create_entry - create an info entry
6501da177e4SLinus Torvalds * @name: the proc file name
6518e7ccb7bSTakashi Iwai * @parent: the parent directory
6521da177e4SLinus Torvalds *
6531da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as
6541da177e4SLinus Torvalds * the default state.
6551da177e4SLinus Torvalds *
6561da177e4SLinus Torvalds * Usually called from other functions such as
6571da177e4SLinus Torvalds * snd_info_create_card_entry().
6581da177e4SLinus Torvalds *
659eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure.
6601da177e4SLinus Torvalds */
6618e7ccb7bSTakashi Iwai static struct snd_info_entry *
snd_info_create_entry(const char * name,struct snd_info_entry * parent,struct module * module)662a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent,
663a858ee66STakashi Iwai struct module *module)
6641da177e4SLinus Torvalds {
66524c1f931STakashi Iwai struct snd_info_entry *entry;
666ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL);
6671da177e4SLinus Torvalds if (entry == NULL)
6681da177e4SLinus Torvalds return NULL;
669543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL);
6701da177e4SLinus Torvalds if (entry->name == NULL) {
6711da177e4SLinus Torvalds kfree(entry);
6721da177e4SLinus Torvalds return NULL;
6731da177e4SLinus Torvalds }
6746a73cf46SJoe Perches entry->mode = S_IFREG | 0444;
6751da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT;
6761a60d4c5SIngo Molnar mutex_init(&entry->access);
677746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children);
678746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list);
6798e7ccb7bSTakashi Iwai entry->parent = parent;
680a858ee66STakashi Iwai entry->module = module;
6818c2f8708STakashi Iwai if (parent) {
682*4b72362bSTakashi Iwai guard(mutex)(&parent->access);
6838e7ccb7bSTakashi Iwai list_add_tail(&entry->list, &parent->children);
6848c2f8708STakashi Iwai }
6851da177e4SLinus Torvalds return entry;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds
6881da177e4SLinus Torvalds /**
6891da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module
6901da177e4SLinus Torvalds * @module: the module pointer
6911da177e4SLinus Torvalds * @name: the file name
6921da177e4SLinus Torvalds * @parent: the parent directory
6931da177e4SLinus Torvalds *
6941da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module.
6951da177e4SLinus Torvalds *
696eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure.
6971da177e4SLinus Torvalds */
snd_info_create_module_entry(struct module * module,const char * name,struct snd_info_entry * parent)69824c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module,
6991da177e4SLinus Torvalds const char *name,
70024c1f931STakashi Iwai struct snd_info_entry *parent)
7011da177e4SLinus Torvalds {
7023a554371STakashi Iwai if (!parent)
7033a554371STakashi Iwai parent = snd_proc_root;
704a858ee66STakashi Iwai return snd_info_create_entry(name, parent, module);
7051da177e4SLinus Torvalds }
706c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry);
707c0d3fb39STakashi Iwai
7081da177e4SLinus Torvalds /**
7091da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card
7101da177e4SLinus Torvalds * @card: the card instance
7111da177e4SLinus Torvalds * @name: the file name
7121da177e4SLinus Torvalds * @parent: the parent directory
7131da177e4SLinus Torvalds *
7141da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card.
7151da177e4SLinus Torvalds *
716eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure.
7171da177e4SLinus Torvalds */
snd_info_create_card_entry(struct snd_card * card,const char * name,struct snd_info_entry * parent)71824c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
7191da177e4SLinus Torvalds const char *name,
72024c1f931STakashi Iwai struct snd_info_entry * parent)
7211da177e4SLinus Torvalds {
7223a554371STakashi Iwai if (!parent)
7233a554371STakashi Iwai parent = card->proc_root;
724a858ee66STakashi Iwai return snd_info_create_entry(name, parent, card->module);
7251da177e4SLinus Torvalds }
726c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry);
727c0d3fb39STakashi Iwai
snd_info_clear_entries(struct snd_info_entry * entry)728c7a60651STakashi Iwai static void snd_info_clear_entries(struct snd_info_entry *entry)
729746d4a02STakashi Iwai {
73090a409aaSTakashi Iwai struct snd_info_entry *p;
731746d4a02STakashi Iwai
732746d4a02STakashi Iwai if (!entry->p)
733746d4a02STakashi Iwai return;
73490a409aaSTakashi Iwai list_for_each_entry(p, &entry->children, list)
735c7a60651STakashi Iwai snd_info_clear_entries(p);
736746d4a02STakashi Iwai entry->p = NULL;
737746d4a02STakashi Iwai }
738746d4a02STakashi Iwai
7391da177e4SLinus Torvalds /**
7401da177e4SLinus Torvalds * snd_info_free_entry - release the info entry
7411da177e4SLinus Torvalds * @entry: the info entry
7421da177e4SLinus Torvalds *
743c560a679STakashi Iwai * Releases the info entry.
7441da177e4SLinus Torvalds */
snd_info_free_entry(struct snd_info_entry * entry)74524c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry)
7461da177e4SLinus Torvalds {
747c560a679STakashi Iwai struct snd_info_entry *p, *n;
748c560a679STakashi Iwai
749c560a679STakashi Iwai if (!entry)
7501da177e4SLinus Torvalds return;
751746d4a02STakashi Iwai if (entry->p) {
752c7a60651STakashi Iwai proc_remove(entry->p);
753*4b72362bSTakashi Iwai guard(mutex)(&info_mutex);
754c7a60651STakashi Iwai snd_info_clear_entries(entry);
755746d4a02STakashi Iwai }
756c560a679STakashi Iwai
757c560a679STakashi Iwai /* free all children at first */
758c560a679STakashi Iwai list_for_each_entry_safe(p, n, &entry->children, list)
759c560a679STakashi Iwai snd_info_free_entry(p);
760c560a679STakashi Iwai
7618c2f8708STakashi Iwai p = entry->parent;
7628c2f8708STakashi Iwai if (p) {
763*4b72362bSTakashi Iwai guard(mutex)(&p->access);
76490a409aaSTakashi Iwai list_del(&entry->list);
7658c2f8708STakashi Iwai }
7661da177e4SLinus Torvalds kfree(entry->name);
7671da177e4SLinus Torvalds if (entry->private_free)
7681da177e4SLinus Torvalds entry->private_free(entry);
7691da177e4SLinus Torvalds kfree(entry);
7701da177e4SLinus Torvalds }
771c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry);
772c0d3fb39STakashi Iwai
__snd_info_register(struct snd_info_entry * entry)773348c5ad5STakashi Iwai static int __snd_info_register(struct snd_info_entry *entry)
7741da177e4SLinus Torvalds {
7751da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL;
7761da177e4SLinus Torvalds
7777eaa943cSTakashi Iwai if (snd_BUG_ON(!entry))
7787eaa943cSTakashi Iwai return -ENXIO;
779644dbd64STakashi Iwai root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
780*4b72362bSTakashi Iwai guard(mutex)(&info_mutex);
781348c5ad5STakashi Iwai if (entry->p || !root)
782*4b72362bSTakashi Iwai return 0;
783aee0c612SAl Viro if (S_ISDIR(entry->mode)) {
784aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root);
785*4b72362bSTakashi Iwai if (!p)
7861da177e4SLinus Torvalds return -ENOMEM;
787aee0c612SAl Viro } else {
78897a32539SAlexey Dobriyan const struct proc_ops *ops;
7894adb7bcbSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA)
7904adb7bcbSTakashi Iwai ops = &snd_info_entry_operations;
7914adb7bcbSTakashi Iwai else
7924adb7bcbSTakashi Iwai ops = &snd_info_text_entry_ops;
793aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root,
7944adb7bcbSTakashi Iwai ops, entry);
795*4b72362bSTakashi Iwai if (!p)
796aee0c612SAl Viro return -ENOMEM;
797271a15eaSDavid Howells proc_set_size(p, entry->size);
798aee0c612SAl Viro }
7991da177e4SLinus Torvalds entry->p = p;
8001da177e4SLinus Torvalds return 0;
8011da177e4SLinus Torvalds }
802348c5ad5STakashi Iwai
803348c5ad5STakashi Iwai /**
804348c5ad5STakashi Iwai * snd_info_register - register the info entry
805348c5ad5STakashi Iwai * @entry: the info entry
806348c5ad5STakashi Iwai *
807348c5ad5STakashi Iwai * Registers the proc info entry.
808348c5ad5STakashi Iwai * The all children entries are registered recursively.
809348c5ad5STakashi Iwai *
810348c5ad5STakashi Iwai * Return: Zero if successful, or a negative error code on failure.
811348c5ad5STakashi Iwai */
snd_info_register(struct snd_info_entry * entry)812348c5ad5STakashi Iwai int snd_info_register(struct snd_info_entry *entry)
813348c5ad5STakashi Iwai {
814348c5ad5STakashi Iwai struct snd_info_entry *p;
815348c5ad5STakashi Iwai int err;
816348c5ad5STakashi Iwai
817348c5ad5STakashi Iwai if (!entry->p) {
818348c5ad5STakashi Iwai err = __snd_info_register(entry);
819348c5ad5STakashi Iwai if (err < 0)
820348c5ad5STakashi Iwai return err;
821348c5ad5STakashi Iwai }
822348c5ad5STakashi Iwai
823348c5ad5STakashi Iwai list_for_each_entry(p, &entry->children, list) {
824348c5ad5STakashi Iwai err = snd_info_register(p);
825348c5ad5STakashi Iwai if (err < 0)
826348c5ad5STakashi Iwai return err;
827348c5ad5STakashi Iwai }
828348c5ad5STakashi Iwai
829348c5ad5STakashi Iwai return 0;
830348c5ad5STakashi Iwai }
831c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register);
832c0d3fb39STakashi Iwai
8337453e1daSTakashi Iwai /**
8347453e1daSTakashi Iwai * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
8357453e1daSTakashi Iwai * @card: the card instance
8367453e1daSTakashi Iwai * @name: the file name
8377453e1daSTakashi Iwai * @private_data: the arbitrary private data
8387453e1daSTakashi Iwai * @read: the read callback
8397453e1daSTakashi Iwai * @write: the write callback, NULL for read-only
8407453e1daSTakashi Iwai *
8417453e1daSTakashi Iwai * This proc file entry will be registered via snd_card_register() call, and
8427453e1daSTakashi Iwai * it will be removed automatically at the card removal, too.
843281dee67STakashi Iwai *
844281dee67STakashi Iwai * Return: zero if successful, or a negative error code
8457453e1daSTakashi Iwai */
snd_card_rw_proc_new(struct snd_card * card,const char * name,void * private_data,void (* read)(struct snd_info_entry *,struct snd_info_buffer *),void (* write)(struct snd_info_entry * entry,struct snd_info_buffer * buffer))8467453e1daSTakashi Iwai int snd_card_rw_proc_new(struct snd_card *card, const char *name,
8477453e1daSTakashi Iwai void *private_data,
8487453e1daSTakashi Iwai void (*read)(struct snd_info_entry *,
8497453e1daSTakashi Iwai struct snd_info_buffer *),
8507453e1daSTakashi Iwai void (*write)(struct snd_info_entry *entry,
8517453e1daSTakashi Iwai struct snd_info_buffer *buffer))
8527453e1daSTakashi Iwai {
8537453e1daSTakashi Iwai struct snd_info_entry *entry;
8547453e1daSTakashi Iwai
8557453e1daSTakashi Iwai entry = snd_info_create_card_entry(card, name, card->proc_root);
8567453e1daSTakashi Iwai if (!entry)
8577453e1daSTakashi Iwai return -ENOMEM;
8587453e1daSTakashi Iwai snd_info_set_text_ops(entry, private_data, read);
8597453e1daSTakashi Iwai if (write) {
8607453e1daSTakashi Iwai entry->mode |= 0200;
8617453e1daSTakashi Iwai entry->c.text.write = write;
8627453e1daSTakashi Iwai }
8637453e1daSTakashi Iwai return 0;
8647453e1daSTakashi Iwai }
8657453e1daSTakashi Iwai EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
8667453e1daSTakashi Iwai
8671da177e4SLinus Torvalds /*
8681da177e4SLinus Torvalds
8691da177e4SLinus Torvalds */
8701da177e4SLinus Torvalds
snd_info_version_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)87124c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
8721da177e4SLinus Torvalds {
8731da177e4SLinus Torvalds snd_iprintf(buffer,
87442662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n",
87542662748SJaroslav Kysela init_utsname()->release);
8761da177e4SLinus Torvalds }
8771da177e4SLinus Torvalds
snd_info_version_init(void)8781da177e4SLinus Torvalds static int __init snd_info_version_init(void)
8791da177e4SLinus Torvalds {
88024c1f931STakashi Iwai struct snd_info_entry *entry;
8811da177e4SLinus Torvalds
8821da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
8831da177e4SLinus Torvalds if (entry == NULL)
8841da177e4SLinus Torvalds return -ENOMEM;
8851da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read;
886b591b6e9STakashi Iwai return snd_info_register(entry); /* freed in error path */
8871da177e4SLinus Torvalds }
888