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);
59*2dac108bSTakashi 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;
10873029e0fSTakashi Iwai loff_t ret = -EINVAL, size;
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds data = file->private_data;
1111da177e4SLinus Torvalds entry = data->entry;
1125b5cd553STakashi Iwai mutex_lock(&entry->access);
1134adb7bcbSTakashi Iwai if (entry->c.ops->llseek) {
1149be080edSAmadeusz Sławiński ret = entry->c.ops->llseek(entry,
1151da177e4SLinus Torvalds data->file_private_data,
1161da177e4SLinus Torvalds file, offset, orig);
1171da177e4SLinus Torvalds goto out;
1181da177e4SLinus Torvalds }
1194adb7bcbSTakashi Iwai
12073029e0fSTakashi Iwai size = entry->size;
12173029e0fSTakashi Iwai switch (orig) {
12273029e0fSTakashi Iwai case SEEK_SET:
1231da177e4SLinus Torvalds break;
12473029e0fSTakashi Iwai case SEEK_CUR:
12573029e0fSTakashi Iwai offset += file->f_pos;
12673029e0fSTakashi Iwai break;
12773029e0fSTakashi Iwai case SEEK_END:
12873029e0fSTakashi Iwai if (!size)
12973029e0fSTakashi Iwai goto out;
13073029e0fSTakashi Iwai offset += size;
13173029e0fSTakashi Iwai break;
13273029e0fSTakashi Iwai default:
13373029e0fSTakashi Iwai goto out;
1341da177e4SLinus Torvalds }
13573029e0fSTakashi Iwai if (offset < 0)
13673029e0fSTakashi Iwai goto out;
13773029e0fSTakashi Iwai if (size && offset > size)
13873029e0fSTakashi Iwai offset = size;
13973029e0fSTakashi Iwai file->f_pos = offset;
14073029e0fSTakashi Iwai ret = offset;
1411da177e4SLinus Torvalds out:
1425b5cd553STakashi Iwai mutex_unlock(&entry->access);
1431da177e4SLinus Torvalds return ret;
1441da177e4SLinus Torvalds }
1451da177e4SLinus Torvalds
snd_info_entry_read(struct file * file,char __user * buffer,size_t count,loff_t * offset)1461da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
1471da177e4SLinus Torvalds size_t count, loff_t * offset)
1481da177e4SLinus Torvalds {
1494adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
1504adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
1514adb7bcbSTakashi Iwai size_t size;
1521da177e4SLinus Torvalds loff_t pos;
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds pos = *offset;
1554adb7bcbSTakashi Iwai if (!valid_pos(pos, count))
1561da177e4SLinus Torvalds return -EIO;
157d97e1b78STakashi Iwai if (pos >= entry->size)
158d97e1b78STakashi Iwai return 0;
159d97e1b78STakashi Iwai size = entry->size - pos;
160d97e1b78STakashi Iwai size = min(count, size);
1614adb7bcbSTakashi Iwai size = entry->c.ops->read(entry, data->file_private_data,
162d97e1b78STakashi Iwai file, buffer, size, pos);
1631da177e4SLinus Torvalds if ((ssize_t) size > 0)
1641da177e4SLinus Torvalds *offset = pos + size;
1651da177e4SLinus Torvalds return size;
1661da177e4SLinus Torvalds }
1671da177e4SLinus Torvalds
snd_info_entry_write(struct file * file,const char __user * buffer,size_t count,loff_t * offset)1681da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
1691da177e4SLinus Torvalds size_t count, loff_t * offset)
1701da177e4SLinus Torvalds {
1714adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
1724adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
1737e4eeec8STakashi Iwai ssize_t size = 0;
1741da177e4SLinus Torvalds loff_t pos;
1751da177e4SLinus Torvalds
1761da177e4SLinus Torvalds pos = *offset;
1774adb7bcbSTakashi Iwai if (!valid_pos(pos, count))
1781da177e4SLinus Torvalds return -EIO;
1794adb7bcbSTakashi Iwai if (count > 0) {
180d97e1b78STakashi Iwai size_t maxsize = entry->size - pos;
181d97e1b78STakashi Iwai count = min(count, maxsize);
1824adb7bcbSTakashi Iwai size = entry->c.ops->write(entry, data->file_private_data,
1831da177e4SLinus Torvalds file, buffer, count, pos);
184d97e1b78STakashi Iwai }
1854adb7bcbSTakashi Iwai if (size > 0)
1861da177e4SLinus Torvalds *offset = pos + size;
1871da177e4SLinus Torvalds return size;
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds
snd_info_entry_poll(struct file * file,poll_table * wait)190680ef72aSAl Viro static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
1911da177e4SLinus Torvalds {
1924adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
1934adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
194680ef72aSAl Viro __poll_t mask = 0;
1951da177e4SLinus Torvalds
1961da177e4SLinus Torvalds if (entry->c.ops->poll)
1971da177e4SLinus Torvalds return entry->c.ops->poll(entry,
1981da177e4SLinus Torvalds data->file_private_data,
1991da177e4SLinus Torvalds file, wait);
2001da177e4SLinus Torvalds if (entry->c.ops->read)
201a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
2021da177e4SLinus Torvalds if (entry->c.ops->write)
203a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM;
2041da177e4SLinus Torvalds return mask;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds
snd_info_entry_ioctl(struct file * file,unsigned int cmd,unsigned long arg)207d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
208d99e9889SIngo Molnar unsigned long arg)
2091da177e4SLinus Torvalds {
2104adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
2114adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
2121da177e4SLinus Torvalds
2134adb7bcbSTakashi Iwai if (!entry->c.ops->ioctl)
2141da177e4SLinus Torvalds return -ENOTTY;
2154adb7bcbSTakashi Iwai return entry->c.ops->ioctl(entry, data->file_private_data,
2164adb7bcbSTakashi Iwai file, cmd, arg);
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds
snd_info_entry_mmap(struct file * file,struct vm_area_struct * vma)2191da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
2201da177e4SLinus Torvalds {
221496ad9aaSAl Viro struct inode *inode = file_inode(file);
22224c1f931STakashi Iwai struct snd_info_private_data *data;
2231da177e4SLinus Torvalds struct snd_info_entry *entry;
2241da177e4SLinus Torvalds
2251da177e4SLinus Torvalds data = file->private_data;
2261da177e4SLinus Torvalds if (data == NULL)
2271da177e4SLinus Torvalds return 0;
2281da177e4SLinus Torvalds entry = data->entry;
2294adb7bcbSTakashi Iwai if (!entry->c.ops->mmap)
2301da177e4SLinus Torvalds return -ENXIO;
2314adb7bcbSTakashi Iwai return entry->c.ops->mmap(entry, data->file_private_data,
2324adb7bcbSTakashi Iwai inode, file, vma);
2334adb7bcbSTakashi Iwai }
2344adb7bcbSTakashi Iwai
snd_info_entry_open(struct inode * inode,struct file * file)2354adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file)
2364adb7bcbSTakashi Iwai {
237359745d7SMuchun Song struct snd_info_entry *entry = pde_data(inode);
2384adb7bcbSTakashi Iwai struct snd_info_private_data *data;
2394adb7bcbSTakashi Iwai int mode, err;
2404adb7bcbSTakashi Iwai
2414adb7bcbSTakashi Iwai mutex_lock(&info_mutex);
2424adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data);
2434adb7bcbSTakashi Iwai if (err < 0)
2444adb7bcbSTakashi Iwai goto unlock;
2454adb7bcbSTakashi Iwai
2464adb7bcbSTakashi Iwai mode = file->f_flags & O_ACCMODE;
2474adb7bcbSTakashi Iwai if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
2484adb7bcbSTakashi Iwai ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
2494adb7bcbSTakashi Iwai err = -ENODEV;
2504adb7bcbSTakashi Iwai goto error;
2514adb7bcbSTakashi Iwai }
2524adb7bcbSTakashi Iwai
2534adb7bcbSTakashi Iwai if (entry->c.ops->open) {
2544adb7bcbSTakashi Iwai err = entry->c.ops->open(entry, mode, &data->file_private_data);
2554adb7bcbSTakashi Iwai if (err < 0)
2564adb7bcbSTakashi Iwai goto error;
2574adb7bcbSTakashi Iwai }
2584adb7bcbSTakashi Iwai
2594adb7bcbSTakashi Iwai file->private_data = data;
2604adb7bcbSTakashi Iwai mutex_unlock(&info_mutex);
2614adb7bcbSTakashi Iwai return 0;
2624adb7bcbSTakashi Iwai
2634adb7bcbSTakashi Iwai error:
2644adb7bcbSTakashi Iwai kfree(data);
2654adb7bcbSTakashi Iwai module_put(entry->module);
2664adb7bcbSTakashi Iwai unlock:
2674adb7bcbSTakashi Iwai mutex_unlock(&info_mutex);
2684adb7bcbSTakashi Iwai return err;
2694adb7bcbSTakashi Iwai }
2704adb7bcbSTakashi Iwai
snd_info_entry_release(struct inode * inode,struct file * file)2714adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file)
2724adb7bcbSTakashi Iwai {
2734adb7bcbSTakashi Iwai struct snd_info_private_data *data = file->private_data;
2744adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
2754adb7bcbSTakashi Iwai
2764adb7bcbSTakashi Iwai if (entry->c.ops->release)
2774adb7bcbSTakashi Iwai entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
2784adb7bcbSTakashi Iwai data->file_private_data);
2794adb7bcbSTakashi Iwai module_put(entry->module);
2804adb7bcbSTakashi Iwai kfree(data);
2814adb7bcbSTakashi Iwai return 0;
2821da177e4SLinus Torvalds }
2831da177e4SLinus Torvalds
28497a32539SAlexey Dobriyan static const struct proc_ops snd_info_entry_operations =
2851da177e4SLinus Torvalds {
28697a32539SAlexey Dobriyan .proc_lseek = snd_info_entry_llseek,
28797a32539SAlexey Dobriyan .proc_read = snd_info_entry_read,
28897a32539SAlexey Dobriyan .proc_write = snd_info_entry_write,
28997a32539SAlexey Dobriyan .proc_poll = snd_info_entry_poll,
29097a32539SAlexey Dobriyan .proc_ioctl = snd_info_entry_ioctl,
29197a32539SAlexey Dobriyan .proc_mmap = snd_info_entry_mmap,
29297a32539SAlexey Dobriyan .proc_open = snd_info_entry_open,
29397a32539SAlexey Dobriyan .proc_release = snd_info_entry_release,
2941da177e4SLinus Torvalds };
2951da177e4SLinus Torvalds
2964adb7bcbSTakashi Iwai /*
2974adb7bcbSTakashi Iwai * file ops for text proc files
2984adb7bcbSTakashi Iwai */
snd_info_text_entry_write(struct file * file,const char __user * buffer,size_t count,loff_t * offset)2994adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file,
3004adb7bcbSTakashi Iwai const char __user *buffer,
3014adb7bcbSTakashi Iwai size_t count, loff_t *offset)
3024adb7bcbSTakashi Iwai {
3034adb7bcbSTakashi Iwai struct seq_file *m = file->private_data;
3044adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private;
3054adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
3064adb7bcbSTakashi Iwai struct snd_info_buffer *buf;
3074adb7bcbSTakashi Iwai loff_t pos;
3084adb7bcbSTakashi Iwai size_t next;
3094adb7bcbSTakashi Iwai int err = 0;
3104adb7bcbSTakashi Iwai
3116809cd68STakashi Iwai if (!entry->c.text.write)
3126809cd68STakashi Iwai return -EIO;
3134adb7bcbSTakashi Iwai pos = *offset;
3144adb7bcbSTakashi Iwai if (!valid_pos(pos, count))
3154adb7bcbSTakashi Iwai return -EIO;
3164adb7bcbSTakashi Iwai next = pos + count;
317027a9fe6STakashi Iwai /* don't handle too large text inputs */
318027a9fe6STakashi Iwai if (next > 16 * 1024)
319027a9fe6STakashi Iwai return -EIO;
3204adb7bcbSTakashi Iwai mutex_lock(&entry->access);
3214adb7bcbSTakashi Iwai buf = data->wbuffer;
3224adb7bcbSTakashi Iwai if (!buf) {
3234adb7bcbSTakashi Iwai data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
3244adb7bcbSTakashi Iwai if (!buf) {
3254adb7bcbSTakashi Iwai err = -ENOMEM;
3264adb7bcbSTakashi Iwai goto error;
3274adb7bcbSTakashi Iwai }
3284adb7bcbSTakashi Iwai }
3294adb7bcbSTakashi Iwai if (next > buf->len) {
330ffb73b08STakashi Iwai char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
3314adb7bcbSTakashi Iwai if (!nbuf) {
3324adb7bcbSTakashi Iwai err = -ENOMEM;
3334adb7bcbSTakashi Iwai goto error;
3344adb7bcbSTakashi Iwai }
335ffb73b08STakashi Iwai kvfree(buf->buffer);
3364adb7bcbSTakashi Iwai buf->buffer = nbuf;
3374adb7bcbSTakashi Iwai buf->len = PAGE_ALIGN(next);
3384adb7bcbSTakashi Iwai }
3394adb7bcbSTakashi Iwai if (copy_from_user(buf->buffer + pos, buffer, count)) {
3404adb7bcbSTakashi Iwai err = -EFAULT;
3414adb7bcbSTakashi Iwai goto error;
3424adb7bcbSTakashi Iwai }
3434adb7bcbSTakashi Iwai buf->size = next;
3444adb7bcbSTakashi Iwai error:
3454adb7bcbSTakashi Iwai mutex_unlock(&entry->access);
3464adb7bcbSTakashi Iwai if (err < 0)
3474adb7bcbSTakashi Iwai return err;
3484adb7bcbSTakashi Iwai *offset = next;
3494adb7bcbSTakashi Iwai return count;
3504adb7bcbSTakashi Iwai }
3514adb7bcbSTakashi Iwai
snd_info_seq_show(struct seq_file * seq,void * p)3524adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p)
3534adb7bcbSTakashi Iwai {
3544adb7bcbSTakashi Iwai struct snd_info_private_data *data = seq->private;
3554adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
3564adb7bcbSTakashi Iwai
3576809cd68STakashi Iwai if (!entry->c.text.read) {
3586809cd68STakashi Iwai return -EIO;
3596809cd68STakashi Iwai } else {
3604adb7bcbSTakashi Iwai data->rbuffer->buffer = (char *)seq; /* XXX hack! */
3614adb7bcbSTakashi Iwai entry->c.text.read(entry, data->rbuffer);
3624adb7bcbSTakashi Iwai }
3634adb7bcbSTakashi Iwai return 0;
3644adb7bcbSTakashi Iwai }
3654adb7bcbSTakashi Iwai
snd_info_text_entry_open(struct inode * inode,struct file * file)3664adb7bcbSTakashi Iwai static int snd_info_text_entry_open(struct inode *inode, struct file *file)
3674adb7bcbSTakashi Iwai {
368359745d7SMuchun Song struct snd_info_entry *entry = pde_data(inode);
3694adb7bcbSTakashi Iwai struct snd_info_private_data *data;
3704adb7bcbSTakashi Iwai int err;
3714adb7bcbSTakashi Iwai
3724adb7bcbSTakashi Iwai mutex_lock(&info_mutex);
3734adb7bcbSTakashi Iwai err = alloc_info_private(entry, &data);
3744adb7bcbSTakashi Iwai if (err < 0)
3754adb7bcbSTakashi Iwai goto unlock;
3764adb7bcbSTakashi Iwai
3774adb7bcbSTakashi Iwai data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
3784adb7bcbSTakashi Iwai if (!data->rbuffer) {
3794adb7bcbSTakashi Iwai err = -ENOMEM;
3804adb7bcbSTakashi Iwai goto error;
3814adb7bcbSTakashi Iwai }
3824adb7bcbSTakashi Iwai if (entry->size)
3834adb7bcbSTakashi Iwai err = single_open_size(file, snd_info_seq_show, data,
3844adb7bcbSTakashi Iwai entry->size);
3854adb7bcbSTakashi Iwai else
3864adb7bcbSTakashi Iwai err = single_open(file, snd_info_seq_show, data);
3874adb7bcbSTakashi Iwai if (err < 0)
3884adb7bcbSTakashi Iwai goto error;
3894adb7bcbSTakashi Iwai mutex_unlock(&info_mutex);
3904adb7bcbSTakashi Iwai return 0;
3914adb7bcbSTakashi Iwai
3924adb7bcbSTakashi Iwai error:
3934adb7bcbSTakashi Iwai kfree(data->rbuffer);
3944adb7bcbSTakashi Iwai kfree(data);
3954adb7bcbSTakashi Iwai module_put(entry->module);
3964adb7bcbSTakashi Iwai unlock:
3974adb7bcbSTakashi Iwai mutex_unlock(&info_mutex);
3984adb7bcbSTakashi Iwai return err;
3994adb7bcbSTakashi Iwai }
4004adb7bcbSTakashi Iwai
snd_info_text_entry_release(struct inode * inode,struct file * file)4014adb7bcbSTakashi Iwai static int snd_info_text_entry_release(struct inode *inode, struct file *file)
4024adb7bcbSTakashi Iwai {
4034adb7bcbSTakashi Iwai struct seq_file *m = file->private_data;
4044adb7bcbSTakashi Iwai struct snd_info_private_data *data = m->private;
4054adb7bcbSTakashi Iwai struct snd_info_entry *entry = data->entry;
4064adb7bcbSTakashi Iwai
4074adb7bcbSTakashi Iwai if (data->wbuffer && entry->c.text.write)
4084adb7bcbSTakashi Iwai entry->c.text.write(entry, data->wbuffer);
4094adb7bcbSTakashi Iwai
4104adb7bcbSTakashi Iwai single_release(inode, file);
4114adb7bcbSTakashi Iwai kfree(data->rbuffer);
4124adb7bcbSTakashi Iwai if (data->wbuffer) {
413ffb73b08STakashi Iwai kvfree(data->wbuffer->buffer);
4144adb7bcbSTakashi Iwai kfree(data->wbuffer);
4154adb7bcbSTakashi Iwai }
4164adb7bcbSTakashi Iwai
4174adb7bcbSTakashi Iwai module_put(entry->module);
4184adb7bcbSTakashi Iwai kfree(data);
4194adb7bcbSTakashi Iwai return 0;
4204adb7bcbSTakashi Iwai }
4214adb7bcbSTakashi Iwai
42297a32539SAlexey Dobriyan static const struct proc_ops snd_info_text_entry_ops =
4234adb7bcbSTakashi Iwai {
42497a32539SAlexey Dobriyan .proc_open = snd_info_text_entry_open,
42597a32539SAlexey Dobriyan .proc_release = snd_info_text_entry_release,
42697a32539SAlexey Dobriyan .proc_write = snd_info_text_entry_write,
42797a32539SAlexey Dobriyan .proc_lseek = seq_lseek,
42897a32539SAlexey Dobriyan .proc_read = seq_read,
4294adb7bcbSTakashi Iwai };
4304adb7bcbSTakashi Iwai
create_subdir(struct module * mod,const char * name)431886364f6STakashi Iwai static struct snd_info_entry *create_subdir(struct module *mod,
432886364f6STakashi Iwai const char *name)
433886364f6STakashi Iwai {
434886364f6STakashi Iwai struct snd_info_entry *entry;
435886364f6STakashi Iwai
436886364f6STakashi Iwai entry = snd_info_create_module_entry(mod, name, NULL);
437886364f6STakashi Iwai if (!entry)
438886364f6STakashi Iwai return NULL;
4396a73cf46SJoe Perches entry->mode = S_IFDIR | 0555;
440886364f6STakashi Iwai if (snd_info_register(entry) < 0) {
441886364f6STakashi Iwai snd_info_free_entry(entry);
442886364f6STakashi Iwai return NULL;
443886364f6STakashi Iwai }
444886364f6STakashi Iwai return entry;
445886364f6STakashi Iwai }
446886364f6STakashi Iwai
4478e7ccb7bSTakashi Iwai static struct snd_info_entry *
448a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent,
449a858ee66STakashi Iwai struct module *module);
450644dbd64STakashi Iwai
snd_info_init(void)4511da177e4SLinus Torvalds int __init snd_info_init(void)
4521da177e4SLinus Torvalds {
453a858ee66STakashi Iwai snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
454644dbd64STakashi Iwai if (!snd_proc_root)
4551da177e4SLinus Torvalds return -ENOMEM;
4566a73cf46SJoe Perches snd_proc_root->mode = S_IFDIR | 0555;
457644dbd64STakashi Iwai snd_proc_root->p = proc_mkdir("asound", NULL);
458644dbd64STakashi Iwai if (!snd_proc_root->p)
459644dbd64STakashi Iwai goto error;
4601da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
461886364f6STakashi Iwai snd_oss_root = create_subdir(THIS_MODULE, "oss");
462886364f6STakashi Iwai if (!snd_oss_root)
463886364f6STakashi Iwai goto error;
4641da177e4SLinus Torvalds #endif
4658eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
466886364f6STakashi Iwai snd_seq_root = create_subdir(THIS_MODULE, "seq");
467886364f6STakashi Iwai if (!snd_seq_root)
468886364f6STakashi Iwai goto error;
4691da177e4SLinus Torvalds #endif
470b591b6e9STakashi Iwai if (snd_info_version_init() < 0 ||
471b591b6e9STakashi Iwai snd_minor_info_init() < 0 ||
472b591b6e9STakashi Iwai snd_minor_info_oss_init() < 0 ||
473a0dca822STakashi Iwai snd_card_info_init() < 0 ||
474a0dca822STakashi Iwai snd_info_minor_register() < 0)
475b591b6e9STakashi Iwai goto error;
4761da177e4SLinus Torvalds return 0;
477886364f6STakashi Iwai
478886364f6STakashi Iwai error:
479644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root);
480886364f6STakashi Iwai return -ENOMEM;
4811da177e4SLinus Torvalds }
4821da177e4SLinus Torvalds
snd_info_done(void)4831da177e4SLinus Torvalds int __exit snd_info_done(void)
4841da177e4SLinus Torvalds {
485644dbd64STakashi Iwai snd_info_free_entry(snd_proc_root);
4861da177e4SLinus Torvalds return 0;
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds
snd_card_id_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)48929b2625fSTakashi Iwai static void snd_card_id_read(struct snd_info_entry *entry,
49029b2625fSTakashi Iwai struct snd_info_buffer *buffer)
49129b2625fSTakashi Iwai {
49229b2625fSTakashi Iwai struct snd_card *card = entry->private_data;
49329b2625fSTakashi Iwai
49429b2625fSTakashi Iwai snd_iprintf(buffer, "%s\n", card->id);
49529b2625fSTakashi Iwai }
49629b2625fSTakashi Iwai
4971da177e4SLinus Torvalds /*
4981da177e4SLinus Torvalds * create a card proc file
4991da177e4SLinus Torvalds * called from init.c
5001da177e4SLinus Torvalds */
snd_info_card_create(struct snd_card * card)50124c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card)
5021da177e4SLinus Torvalds {
5031da177e4SLinus Torvalds char str[8];
50424c1f931STakashi Iwai struct snd_info_entry *entry;
5051da177e4SLinus Torvalds
5067eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
5077eaa943cSTakashi Iwai return -ENXIO;
5081da177e4SLinus Torvalds
5091da177e4SLinus Torvalds sprintf(str, "card%i", card->number);
510886364f6STakashi Iwai entry = create_subdir(card->module, str);
511886364f6STakashi Iwai if (!entry)
5121da177e4SLinus Torvalds return -ENOMEM;
5131da177e4SLinus Torvalds card->proc_root = entry;
51429b2625fSTakashi Iwai
51529b2625fSTakashi Iwai return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds
5181da177e4SLinus Torvalds /*
5191da177e4SLinus Torvalds * register the card proc file
5201da177e4SLinus Torvalds * called from init.c
5212471b6c8STakashi Iwai * can be called multiple times for reinitialization
5221da177e4SLinus Torvalds */
snd_info_card_register(struct snd_card * card)52324c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card)
5241da177e4SLinus Torvalds {
5251da177e4SLinus Torvalds struct proc_dir_entry *p;
5262471b6c8STakashi Iwai int err;
5271da177e4SLinus Torvalds
5287eaa943cSTakashi Iwai if (snd_BUG_ON(!card))
5297eaa943cSTakashi Iwai return -ENXIO;
5301da177e4SLinus Torvalds
531348c5ad5STakashi Iwai err = snd_info_register(card->proc_root);
5322471b6c8STakashi Iwai if (err < 0)
5332471b6c8STakashi Iwai return err;
5342471b6c8STakashi Iwai
5351da177e4SLinus Torvalds if (!strcmp(card->id, card->proc_root->name))
5361da177e4SLinus Torvalds return 0;
5371da177e4SLinus Torvalds
5382471b6c8STakashi Iwai if (card->proc_root_link)
5392471b6c8STakashi Iwai return 0;
540644dbd64STakashi Iwai p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
5412471b6c8STakashi Iwai if (!p)
5421da177e4SLinus Torvalds return -ENOMEM;
5431da177e4SLinus Torvalds card->proc_root_link = p;
5441da177e4SLinus Torvalds return 0;
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds
5471da177e4SLinus Torvalds /*
548c2eb9c4eSJaroslav Kysela * called on card->id change
549c2eb9c4eSJaroslav Kysela */
snd_info_card_id_change(struct snd_card * card)550c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card)
551c2eb9c4eSJaroslav Kysela {
552c2eb9c4eSJaroslav Kysela mutex_lock(&info_mutex);
553c2eb9c4eSJaroslav Kysela if (card->proc_root_link) {
554a8ca16eaSDavid Howells proc_remove(card->proc_root_link);
555c2eb9c4eSJaroslav Kysela card->proc_root_link = NULL;
556c2eb9c4eSJaroslav Kysela }
557c2eb9c4eSJaroslav Kysela if (strcmp(card->id, card->proc_root->name))
558c2eb9c4eSJaroslav Kysela card->proc_root_link = proc_symlink(card->id,
559644dbd64STakashi Iwai snd_proc_root->p,
560c2eb9c4eSJaroslav Kysela card->proc_root->name);
561c2eb9c4eSJaroslav Kysela mutex_unlock(&info_mutex);
562c2eb9c4eSJaroslav Kysela }
563c2eb9c4eSJaroslav Kysela
564c2eb9c4eSJaroslav Kysela /*
5651da177e4SLinus Torvalds * de-register the card proc file
5661da177e4SLinus Torvalds * called from init.c
5671da177e4SLinus Torvalds */
snd_info_card_disconnect(struct snd_card * card)568746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card)
5691da177e4SLinus Torvalds {
5707eaa943cSTakashi Iwai if (!card)
5717eaa943cSTakashi Iwai return;
572*2dac108bSTakashi Iwai
573a8ca16eaSDavid Howells proc_remove(card->proc_root_link);
574746d4a02STakashi Iwai if (card->proc_root)
575*2dac108bSTakashi Iwai proc_remove(card->proc_root->p);
576*2dac108bSTakashi Iwai
577*2dac108bSTakashi Iwai mutex_lock(&info_mutex);
578*2dac108bSTakashi Iwai if (card->proc_root)
579*2dac108bSTakashi Iwai snd_info_clear_entries(card->proc_root);
580*2dac108bSTakashi Iwai card->proc_root_link = NULL;
581*2dac108bSTakashi Iwai card->proc_root = NULL;
582746d4a02STakashi Iwai mutex_unlock(&info_mutex);
5831da177e4SLinus Torvalds }
584746d4a02STakashi Iwai
585746d4a02STakashi Iwai /*
586746d4a02STakashi Iwai * release the card proc file resources
587746d4a02STakashi Iwai * called from init.c
588746d4a02STakashi Iwai */
snd_info_card_free(struct snd_card * card)589746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card)
590746d4a02STakashi Iwai {
5917eaa943cSTakashi Iwai if (!card)
5927eaa943cSTakashi Iwai return 0;
593746d4a02STakashi Iwai snd_info_free_entry(card->proc_root);
594746d4a02STakashi Iwai card->proc_root = NULL;
5951da177e4SLinus Torvalds return 0;
5961da177e4SLinus Torvalds }
5971da177e4SLinus Torvalds
5981da177e4SLinus Torvalds
5991da177e4SLinus Torvalds /**
6001da177e4SLinus Torvalds * snd_info_get_line - read one line from the procfs buffer
6011da177e4SLinus Torvalds * @buffer: the procfs buffer
6021da177e4SLinus Torvalds * @line: the buffer to store
603ddc64b27SClemens Ladisch * @len: the max. buffer size
6041da177e4SLinus Torvalds *
6051da177e4SLinus Torvalds * Reads one line from the buffer and stores the string.
6061da177e4SLinus Torvalds *
607eb7c06e8SYacine Belkadi * Return: Zero if successful, or 1 if error or EOF.
6081da177e4SLinus Torvalds */
snd_info_get_line(struct snd_info_buffer * buffer,char * line,int len)60924c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
6101da177e4SLinus Torvalds {
6110e023687SColin Ian King int c;
6121da177e4SLinus Torvalds
61360379ba0STakashi Iwai if (snd_BUG_ON(!buffer))
61460379ba0STakashi Iwai return 1;
61560379ba0STakashi Iwai if (!buffer->buffer)
6160bc0ec90STakashi Iwai return 1;
6171da177e4SLinus Torvalds if (len <= 0 || buffer->stop || buffer->error)
6181da177e4SLinus Torvalds return 1;
6190bc0ec90STakashi Iwai while (!buffer->stop) {
6207e4eeec8STakashi Iwai c = buffer->buffer[buffer->curr++];
6217e4eeec8STakashi Iwai if (buffer->curr >= buffer->size)
6221da177e4SLinus Torvalds buffer->stop = 1;
6230bc0ec90STakashi Iwai if (c == '\n')
6241da177e4SLinus Torvalds break;
625ddc64b27SClemens Ladisch if (len > 1) {
6260bc0ec90STakashi Iwai len--;
6271da177e4SLinus Torvalds *line++ = c;
6281da177e4SLinus Torvalds }
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds *line = '\0';
6311da177e4SLinus Torvalds return 0;
6321da177e4SLinus Torvalds }
633c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line);
634c0d3fb39STakashi Iwai
6351da177e4SLinus Torvalds /**
636856def8aSHenrik Kretzschmar * snd_info_get_str - parse a string token
6371da177e4SLinus Torvalds * @dest: the buffer to store the string token
6381da177e4SLinus Torvalds * @src: the original string
6391da177e4SLinus Torvalds * @len: the max. length of token - 1
6401da177e4SLinus Torvalds *
6411da177e4SLinus Torvalds * Parses the original string and copy a token to the given
6421da177e4SLinus Torvalds * string buffer.
6431da177e4SLinus Torvalds *
644eb7c06e8SYacine Belkadi * Return: The updated pointer of the original string so that
6451da177e4SLinus Torvalds * it can be used for the next call.
6461da177e4SLinus Torvalds */
snd_info_get_str(char * dest,const char * src,int len)6474f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len)
6481da177e4SLinus Torvalds {
6491da177e4SLinus Torvalds int c;
6501da177e4SLinus Torvalds
6511da177e4SLinus Torvalds while (*src == ' ' || *src == '\t')
6521da177e4SLinus Torvalds src++;
6531da177e4SLinus Torvalds if (*src == '"' || *src == '\'') {
6541da177e4SLinus Torvalds c = *src++;
6551da177e4SLinus Torvalds while (--len > 0 && *src && *src != c) {
6561da177e4SLinus Torvalds *dest++ = *src++;
6571da177e4SLinus Torvalds }
6581da177e4SLinus Torvalds if (*src == c)
6591da177e4SLinus Torvalds src++;
6601da177e4SLinus Torvalds } else {
6611da177e4SLinus Torvalds while (--len > 0 && *src && *src != ' ' && *src != '\t') {
6621da177e4SLinus Torvalds *dest++ = *src++;
6631da177e4SLinus Torvalds }
6641da177e4SLinus Torvalds }
6651da177e4SLinus Torvalds *dest = 0;
6661da177e4SLinus Torvalds while (*src == ' ' || *src == '\t')
6671da177e4SLinus Torvalds src++;
6681da177e4SLinus Torvalds return src;
6691da177e4SLinus Torvalds }
670c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str);
671c0d3fb39STakashi Iwai
672c309c467STakashi Iwai /*
6731da177e4SLinus Torvalds * snd_info_create_entry - create an info entry
6741da177e4SLinus Torvalds * @name: the proc file name
6758e7ccb7bSTakashi Iwai * @parent: the parent directory
6761da177e4SLinus Torvalds *
6771da177e4SLinus Torvalds * Creates an info entry with the given file name and initializes as
6781da177e4SLinus Torvalds * the default state.
6791da177e4SLinus Torvalds *
6801da177e4SLinus Torvalds * Usually called from other functions such as
6811da177e4SLinus Torvalds * snd_info_create_card_entry().
6821da177e4SLinus Torvalds *
683eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure.
6841da177e4SLinus Torvalds */
6858e7ccb7bSTakashi Iwai static struct snd_info_entry *
snd_info_create_entry(const char * name,struct snd_info_entry * parent,struct module * module)686a858ee66STakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent,
687a858ee66STakashi Iwai struct module *module)
6881da177e4SLinus Torvalds {
68924c1f931STakashi Iwai struct snd_info_entry *entry;
690ca2c0966STakashi Iwai entry = kzalloc(sizeof(*entry), GFP_KERNEL);
6911da177e4SLinus Torvalds if (entry == NULL)
6921da177e4SLinus Torvalds return NULL;
693543537bdSPaulo Marques entry->name = kstrdup(name, GFP_KERNEL);
6941da177e4SLinus Torvalds if (entry->name == NULL) {
6951da177e4SLinus Torvalds kfree(entry);
6961da177e4SLinus Torvalds return NULL;
6971da177e4SLinus Torvalds }
6986a73cf46SJoe Perches entry->mode = S_IFREG | 0444;
6991da177e4SLinus Torvalds entry->content = SNDRV_INFO_CONTENT_TEXT;
7001a60d4c5SIngo Molnar mutex_init(&entry->access);
701746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->children);
702746d4a02STakashi Iwai INIT_LIST_HEAD(&entry->list);
7038e7ccb7bSTakashi Iwai entry->parent = parent;
704a858ee66STakashi Iwai entry->module = module;
7058c2f8708STakashi Iwai if (parent) {
7068c2f8708STakashi Iwai mutex_lock(&parent->access);
7078e7ccb7bSTakashi Iwai list_add_tail(&entry->list, &parent->children);
7088c2f8708STakashi Iwai mutex_unlock(&parent->access);
7098c2f8708STakashi Iwai }
7101da177e4SLinus Torvalds return entry;
7111da177e4SLinus Torvalds }
7121da177e4SLinus Torvalds
7131da177e4SLinus Torvalds /**
7141da177e4SLinus Torvalds * snd_info_create_module_entry - create an info entry for the given module
7151da177e4SLinus Torvalds * @module: the module pointer
7161da177e4SLinus Torvalds * @name: the file name
7171da177e4SLinus Torvalds * @parent: the parent directory
7181da177e4SLinus Torvalds *
7191da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given module.
7201da177e4SLinus Torvalds *
721eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure.
7221da177e4SLinus Torvalds */
snd_info_create_module_entry(struct module * module,const char * name,struct snd_info_entry * parent)72324c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module,
7241da177e4SLinus Torvalds const char *name,
72524c1f931STakashi Iwai struct snd_info_entry *parent)
7261da177e4SLinus Torvalds {
7273a554371STakashi Iwai if (!parent)
7283a554371STakashi Iwai parent = snd_proc_root;
729a858ee66STakashi Iwai return snd_info_create_entry(name, parent, module);
7301da177e4SLinus Torvalds }
731c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry);
732c0d3fb39STakashi Iwai
7331da177e4SLinus Torvalds /**
7341da177e4SLinus Torvalds * snd_info_create_card_entry - create an info entry for the given card
7351da177e4SLinus Torvalds * @card: the card instance
7361da177e4SLinus Torvalds * @name: the file name
7371da177e4SLinus Torvalds * @parent: the parent directory
7381da177e4SLinus Torvalds *
7391da177e4SLinus Torvalds * Creates a new info entry and assigns it to the given card.
7401da177e4SLinus Torvalds *
741eb7c06e8SYacine Belkadi * Return: The pointer of the new instance, or %NULL on failure.
7421da177e4SLinus Torvalds */
snd_info_create_card_entry(struct snd_card * card,const char * name,struct snd_info_entry * parent)74324c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
7441da177e4SLinus Torvalds const char *name,
74524c1f931STakashi Iwai struct snd_info_entry * parent)
7461da177e4SLinus Torvalds {
7473a554371STakashi Iwai if (!parent)
7483a554371STakashi Iwai parent = card->proc_root;
749a858ee66STakashi Iwai return snd_info_create_entry(name, parent, card->module);
7501da177e4SLinus Torvalds }
751c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry);
752c0d3fb39STakashi Iwai
snd_info_clear_entries(struct snd_info_entry * entry)753*2dac108bSTakashi Iwai static void snd_info_clear_entries(struct snd_info_entry *entry)
754746d4a02STakashi Iwai {
75590a409aaSTakashi Iwai struct snd_info_entry *p;
756746d4a02STakashi Iwai
757746d4a02STakashi Iwai if (!entry->p)
758746d4a02STakashi Iwai return;
75990a409aaSTakashi Iwai list_for_each_entry(p, &entry->children, list)
760*2dac108bSTakashi Iwai snd_info_clear_entries(p);
761746d4a02STakashi Iwai entry->p = NULL;
762746d4a02STakashi Iwai }
763746d4a02STakashi Iwai
7641da177e4SLinus Torvalds /**
7651da177e4SLinus Torvalds * snd_info_free_entry - release the info entry
7661da177e4SLinus Torvalds * @entry: the info entry
7671da177e4SLinus Torvalds *
768c560a679STakashi Iwai * Releases the info entry.
7691da177e4SLinus Torvalds */
snd_info_free_entry(struct snd_info_entry * entry)77024c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry)
7711da177e4SLinus Torvalds {
772c560a679STakashi Iwai struct snd_info_entry *p, *n;
773c560a679STakashi Iwai
774c560a679STakashi Iwai if (!entry)
7751da177e4SLinus Torvalds return;
776746d4a02STakashi Iwai if (entry->p) {
777*2dac108bSTakashi Iwai proc_remove(entry->p);
778746d4a02STakashi Iwai mutex_lock(&info_mutex);
779*2dac108bSTakashi Iwai snd_info_clear_entries(entry);
780746d4a02STakashi Iwai mutex_unlock(&info_mutex);
781746d4a02STakashi Iwai }
782c560a679STakashi Iwai
783c560a679STakashi Iwai /* free all children at first */
784c560a679STakashi Iwai list_for_each_entry_safe(p, n, &entry->children, list)
785c560a679STakashi Iwai snd_info_free_entry(p);
786c560a679STakashi Iwai
7878c2f8708STakashi Iwai p = entry->parent;
7888c2f8708STakashi Iwai if (p) {
7898c2f8708STakashi Iwai mutex_lock(&p->access);
79090a409aaSTakashi Iwai list_del(&entry->list);
7918c2f8708STakashi Iwai mutex_unlock(&p->access);
7928c2f8708STakashi Iwai }
7931da177e4SLinus Torvalds kfree(entry->name);
7941da177e4SLinus Torvalds if (entry->private_free)
7951da177e4SLinus Torvalds entry->private_free(entry);
7961da177e4SLinus Torvalds kfree(entry);
7971da177e4SLinus Torvalds }
798c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry);
799c0d3fb39STakashi Iwai
__snd_info_register(struct snd_info_entry * entry)800348c5ad5STakashi Iwai static int __snd_info_register(struct snd_info_entry *entry)
8011da177e4SLinus Torvalds {
8021da177e4SLinus Torvalds struct proc_dir_entry *root, *p = NULL;
8031da177e4SLinus Torvalds
8047eaa943cSTakashi Iwai if (snd_BUG_ON(!entry))
8057eaa943cSTakashi Iwai return -ENXIO;
806644dbd64STakashi Iwai root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
8071a60d4c5SIngo Molnar mutex_lock(&info_mutex);
808348c5ad5STakashi Iwai if (entry->p || !root)
809348c5ad5STakashi Iwai goto unlock;
810aee0c612SAl Viro if (S_ISDIR(entry->mode)) {
811aee0c612SAl Viro p = proc_mkdir_mode(entry->name, entry->mode, root);
8121da177e4SLinus Torvalds if (!p) {
8131a60d4c5SIngo Molnar mutex_unlock(&info_mutex);
8141da177e4SLinus Torvalds return -ENOMEM;
8151da177e4SLinus Torvalds }
816aee0c612SAl Viro } else {
81797a32539SAlexey Dobriyan const struct proc_ops *ops;
8184adb7bcbSTakashi Iwai if (entry->content == SNDRV_INFO_CONTENT_DATA)
8194adb7bcbSTakashi Iwai ops = &snd_info_entry_operations;
8204adb7bcbSTakashi Iwai else
8214adb7bcbSTakashi Iwai ops = &snd_info_text_entry_ops;
822aee0c612SAl Viro p = proc_create_data(entry->name, entry->mode, root,
8234adb7bcbSTakashi Iwai ops, entry);
824aee0c612SAl Viro if (!p) {
825aee0c612SAl Viro mutex_unlock(&info_mutex);
826aee0c612SAl Viro return -ENOMEM;
827aee0c612SAl Viro }
828271a15eaSDavid Howells proc_set_size(p, entry->size);
829aee0c612SAl Viro }
8301da177e4SLinus Torvalds entry->p = p;
831348c5ad5STakashi Iwai unlock:
8321a60d4c5SIngo Molnar mutex_unlock(&info_mutex);
8331da177e4SLinus Torvalds return 0;
8341da177e4SLinus Torvalds }
835348c5ad5STakashi Iwai
836348c5ad5STakashi Iwai /**
837348c5ad5STakashi Iwai * snd_info_register - register the info entry
838348c5ad5STakashi Iwai * @entry: the info entry
839348c5ad5STakashi Iwai *
840348c5ad5STakashi Iwai * Registers the proc info entry.
841348c5ad5STakashi Iwai * The all children entries are registered recursively.
842348c5ad5STakashi Iwai *
843348c5ad5STakashi Iwai * Return: Zero if successful, or a negative error code on failure.
844348c5ad5STakashi Iwai */
snd_info_register(struct snd_info_entry * entry)845348c5ad5STakashi Iwai int snd_info_register(struct snd_info_entry *entry)
846348c5ad5STakashi Iwai {
847348c5ad5STakashi Iwai struct snd_info_entry *p;
848348c5ad5STakashi Iwai int err;
849348c5ad5STakashi Iwai
850348c5ad5STakashi Iwai if (!entry->p) {
851348c5ad5STakashi Iwai err = __snd_info_register(entry);
852348c5ad5STakashi Iwai if (err < 0)
853348c5ad5STakashi Iwai return err;
854348c5ad5STakashi Iwai }
855348c5ad5STakashi Iwai
856348c5ad5STakashi Iwai list_for_each_entry(p, &entry->children, list) {
857348c5ad5STakashi Iwai err = snd_info_register(p);
858348c5ad5STakashi Iwai if (err < 0)
859348c5ad5STakashi Iwai return err;
860348c5ad5STakashi Iwai }
861348c5ad5STakashi Iwai
862348c5ad5STakashi Iwai return 0;
863348c5ad5STakashi Iwai }
864c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register);
865c0d3fb39STakashi Iwai
8667453e1daSTakashi Iwai /**
8677453e1daSTakashi Iwai * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
8687453e1daSTakashi Iwai * @card: the card instance
8697453e1daSTakashi Iwai * @name: the file name
8707453e1daSTakashi Iwai * @private_data: the arbitrary private data
8717453e1daSTakashi Iwai * @read: the read callback
8727453e1daSTakashi Iwai * @write: the write callback, NULL for read-only
8737453e1daSTakashi Iwai *
8747453e1daSTakashi Iwai * This proc file entry will be registered via snd_card_register() call, and
8757453e1daSTakashi Iwai * it will be removed automatically at the card removal, too.
876281dee67STakashi Iwai *
877281dee67STakashi Iwai * Return: zero if successful, or a negative error code
8787453e1daSTakashi 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))8797453e1daSTakashi Iwai int snd_card_rw_proc_new(struct snd_card *card, const char *name,
8807453e1daSTakashi Iwai void *private_data,
8817453e1daSTakashi Iwai void (*read)(struct snd_info_entry *,
8827453e1daSTakashi Iwai struct snd_info_buffer *),
8837453e1daSTakashi Iwai void (*write)(struct snd_info_entry *entry,
8847453e1daSTakashi Iwai struct snd_info_buffer *buffer))
8857453e1daSTakashi Iwai {
8867453e1daSTakashi Iwai struct snd_info_entry *entry;
8877453e1daSTakashi Iwai
8887453e1daSTakashi Iwai entry = snd_info_create_card_entry(card, name, card->proc_root);
8897453e1daSTakashi Iwai if (!entry)
8907453e1daSTakashi Iwai return -ENOMEM;
8917453e1daSTakashi Iwai snd_info_set_text_ops(entry, private_data, read);
8927453e1daSTakashi Iwai if (write) {
8937453e1daSTakashi Iwai entry->mode |= 0200;
8947453e1daSTakashi Iwai entry->c.text.write = write;
8957453e1daSTakashi Iwai }
8967453e1daSTakashi Iwai return 0;
8977453e1daSTakashi Iwai }
8987453e1daSTakashi Iwai EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
8997453e1daSTakashi Iwai
9001da177e4SLinus Torvalds /*
9011da177e4SLinus Torvalds
9021da177e4SLinus Torvalds */
9031da177e4SLinus Torvalds
snd_info_version_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)90424c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
9051da177e4SLinus Torvalds {
9061da177e4SLinus Torvalds snd_iprintf(buffer,
90742662748SJaroslav Kysela "Advanced Linux Sound Architecture Driver Version k%s.\n",
90842662748SJaroslav Kysela init_utsname()->release);
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds
snd_info_version_init(void)9111da177e4SLinus Torvalds static int __init snd_info_version_init(void)
9121da177e4SLinus Torvalds {
91324c1f931STakashi Iwai struct snd_info_entry *entry;
9141da177e4SLinus Torvalds
9151da177e4SLinus Torvalds entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
9161da177e4SLinus Torvalds if (entry == NULL)
9171da177e4SLinus Torvalds return -ENOMEM;
9181da177e4SLinus Torvalds entry->c.text.read = snd_info_version_read;
919b591b6e9STakashi Iwai return snd_info_register(entry); /* freed in error path */
9201da177e4SLinus Torvalds }
921