xref: /openbmc/linux/sound/core/info.c (revision 3a554371)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Information interface for ALSA driver
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
81da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
91da177e4SLinus Torvalds  *   (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141da177e4SLinus Torvalds  *   GNU General Public License for more details.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
171da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
181da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <linux/init.h>
231da177e4SLinus Torvalds #include <linux/time.h>
2427ac792cSAndrea Righi #include <linux/mm.h>
255a0e3ad6STejun Heo #include <linux/slab.h>
26543537bdSPaulo Marques #include <linux/string.h>
27da155d5bSPaul Gortmaker #include <linux/module.h>
281da177e4SLinus Torvalds #include <sound/core.h>
291da177e4SLinus Torvalds #include <sound/minors.h>
301da177e4SLinus Torvalds #include <sound/info.h>
3142662748SJaroslav Kysela #include <linux/utsname.h>
321da177e4SLinus Torvalds #include <linux/proc_fs.h>
331a60d4c5SIngo Molnar #include <linux/mutex.h>
341da177e4SLinus Torvalds #include <stdarg.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds int snd_info_check_reserved_words(const char *str)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds 	static char *reserved[] =
391da177e4SLinus Torvalds 	{
401da177e4SLinus Torvalds 		"version",
411da177e4SLinus Torvalds 		"meminfo",
421da177e4SLinus Torvalds 		"memdebug",
431da177e4SLinus Torvalds 		"detect",
441da177e4SLinus Torvalds 		"devices",
451da177e4SLinus Torvalds 		"oss",
461da177e4SLinus Torvalds 		"cards",
471da177e4SLinus Torvalds 		"timers",
481da177e4SLinus Torvalds 		"synth",
491da177e4SLinus Torvalds 		"pcm",
501da177e4SLinus Torvalds 		"seq",
511da177e4SLinus Torvalds 		NULL
521da177e4SLinus Torvalds 	};
531da177e4SLinus Torvalds 	char **xstr = reserved;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	while (*xstr) {
561da177e4SLinus Torvalds 		if (!strcmp(*xstr, str))
571da177e4SLinus Torvalds 			return 0;
581da177e4SLinus Torvalds 		xstr++;
591da177e4SLinus Torvalds 	}
601da177e4SLinus Torvalds 	if (!strncmp(str, "card", 4))
611da177e4SLinus Torvalds 		return 0;
621da177e4SLinus Torvalds 	return 1;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651a60d4c5SIngo Molnar static DEFINE_MUTEX(info_mutex);
661da177e4SLinus Torvalds 
6724c1f931STakashi Iwai struct snd_info_private_data {
6824c1f931STakashi Iwai 	struct snd_info_buffer *rbuffer;
6924c1f931STakashi Iwai 	struct snd_info_buffer *wbuffer;
7024c1f931STakashi Iwai 	struct snd_info_entry *entry;
711da177e4SLinus Torvalds 	void *file_private_data;
7224c1f931STakashi Iwai };
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds static int snd_info_version_init(void);
75746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry);
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds /*
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds  */
801da177e4SLinus Torvalds 
81644dbd64STakashi Iwai static struct snd_info_entry *snd_proc_root;
826581f4e7STakashi Iwai struct snd_info_entry *snd_seq_root;
83c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_seq_root);
84c0d3fb39STakashi Iwai 
851da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
866581f4e7STakashi Iwai struct snd_info_entry *snd_oss_root;
871da177e4SLinus Torvalds #endif
881da177e4SLinus Torvalds 
894adb7bcbSTakashi Iwai static int alloc_info_private(struct snd_info_entry *entry,
904adb7bcbSTakashi Iwai 			      struct snd_info_private_data **ret)
914adb7bcbSTakashi Iwai {
924adb7bcbSTakashi Iwai 	struct snd_info_private_data *data;
934adb7bcbSTakashi Iwai 
944adb7bcbSTakashi Iwai 	if (!entry || !entry->p)
954adb7bcbSTakashi Iwai 		return -ENODEV;
964adb7bcbSTakashi Iwai 	if (!try_module_get(entry->module))
974adb7bcbSTakashi Iwai 		return -EFAULT;
984adb7bcbSTakashi Iwai 	data = kzalloc(sizeof(*data), GFP_KERNEL);
994adb7bcbSTakashi Iwai 	if (!data) {
1004adb7bcbSTakashi Iwai 		module_put(entry->module);
1014adb7bcbSTakashi Iwai 		return -ENOMEM;
1024adb7bcbSTakashi Iwai 	}
1034adb7bcbSTakashi Iwai 	data->entry = entry;
1044adb7bcbSTakashi Iwai 	*ret = data;
1054adb7bcbSTakashi Iwai 	return 0;
1064adb7bcbSTakashi Iwai }
1074adb7bcbSTakashi Iwai 
1084adb7bcbSTakashi Iwai static bool valid_pos(loff_t pos, size_t count)
1094adb7bcbSTakashi Iwai {
1104adb7bcbSTakashi Iwai 	if (pos < 0 || (long) pos != pos || (ssize_t) count < 0)
1114adb7bcbSTakashi Iwai 		return false;
1124adb7bcbSTakashi Iwai 	if ((unsigned long) pos + (unsigned long) count < (unsigned long) pos)
1134adb7bcbSTakashi Iwai 		return false;
1144adb7bcbSTakashi Iwai 	return true;
1154adb7bcbSTakashi Iwai }
1164adb7bcbSTakashi Iwai 
1174adb7bcbSTakashi Iwai /*
1184adb7bcbSTakashi Iwai  * file ops for binary proc files
1194adb7bcbSTakashi Iwai  */
1201da177e4SLinus Torvalds static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
1211da177e4SLinus Torvalds {
12224c1f931STakashi Iwai 	struct snd_info_private_data *data;
1231da177e4SLinus Torvalds 	struct snd_info_entry *entry;
12473029e0fSTakashi Iwai 	loff_t ret = -EINVAL, size;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	data = file->private_data;
1271da177e4SLinus Torvalds 	entry = data->entry;
1285b5cd553STakashi Iwai 	mutex_lock(&entry->access);
1294adb7bcbSTakashi Iwai 	if (entry->c.ops->llseek) {
13073029e0fSTakashi Iwai 		offset = entry->c.ops->llseek(entry,
1311da177e4SLinus Torvalds 					      data->file_private_data,
1321da177e4SLinus Torvalds 					      file, offset, orig);
1331da177e4SLinus Torvalds 		goto out;
1341da177e4SLinus Torvalds 	}
1354adb7bcbSTakashi Iwai 
13673029e0fSTakashi Iwai 	size = entry->size;
13773029e0fSTakashi Iwai 	switch (orig) {
13873029e0fSTakashi Iwai 	case SEEK_SET:
1391da177e4SLinus Torvalds 		break;
14073029e0fSTakashi Iwai 	case SEEK_CUR:
14173029e0fSTakashi Iwai 		offset += file->f_pos;
14273029e0fSTakashi Iwai 		break;
14373029e0fSTakashi Iwai 	case SEEK_END:
14473029e0fSTakashi Iwai 		if (!size)
14573029e0fSTakashi Iwai 			goto out;
14673029e0fSTakashi Iwai 		offset += size;
14773029e0fSTakashi Iwai 		break;
14873029e0fSTakashi Iwai 	default:
14973029e0fSTakashi Iwai 		goto out;
1501da177e4SLinus Torvalds 	}
15173029e0fSTakashi Iwai 	if (offset < 0)
15273029e0fSTakashi Iwai 		goto out;
15373029e0fSTakashi Iwai 	if (size && offset > size)
15473029e0fSTakashi Iwai 		offset = size;
15573029e0fSTakashi Iwai 	file->f_pos = offset;
15673029e0fSTakashi Iwai 	ret = offset;
1571da177e4SLinus Torvalds  out:
1585b5cd553STakashi Iwai 	mutex_unlock(&entry->access);
1591da177e4SLinus Torvalds 	return ret;
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds static ssize_t snd_info_entry_read(struct file *file, char __user *buffer,
1631da177e4SLinus Torvalds 				   size_t count, loff_t * offset)
1641da177e4SLinus Torvalds {
1654adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = file->private_data;
1664adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
1674adb7bcbSTakashi Iwai 	size_t size;
1681da177e4SLinus Torvalds 	loff_t pos;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	pos = *offset;
1714adb7bcbSTakashi Iwai 	if (!valid_pos(pos, count))
1721da177e4SLinus Torvalds 		return -EIO;
173d97e1b78STakashi Iwai 	if (pos >= entry->size)
174d97e1b78STakashi Iwai 		return 0;
175d97e1b78STakashi Iwai 	size = entry->size - pos;
176d97e1b78STakashi Iwai 	size = min(count, size);
1774adb7bcbSTakashi Iwai 	size = entry->c.ops->read(entry, data->file_private_data,
178d97e1b78STakashi Iwai 				  file, buffer, size, pos);
1791da177e4SLinus Torvalds 	if ((ssize_t) size > 0)
1801da177e4SLinus Torvalds 		*offset = pos + size;
1811da177e4SLinus Torvalds 	return size;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds static ssize_t snd_info_entry_write(struct file *file, const char __user *buffer,
1851da177e4SLinus Torvalds 				    size_t count, loff_t * offset)
1861da177e4SLinus Torvalds {
1874adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = file->private_data;
1884adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
1897e4eeec8STakashi Iwai 	ssize_t size = 0;
1901da177e4SLinus Torvalds 	loff_t pos;
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds 	pos = *offset;
1934adb7bcbSTakashi Iwai 	if (!valid_pos(pos, count))
1941da177e4SLinus Torvalds 		return -EIO;
1954adb7bcbSTakashi Iwai 	if (count > 0) {
196d97e1b78STakashi Iwai 		size_t maxsize = entry->size - pos;
197d97e1b78STakashi Iwai 		count = min(count, maxsize);
1984adb7bcbSTakashi Iwai 		size = entry->c.ops->write(entry, data->file_private_data,
1991da177e4SLinus Torvalds 					   file, buffer, count, pos);
200d97e1b78STakashi Iwai 	}
2014adb7bcbSTakashi Iwai 	if (size > 0)
2021da177e4SLinus Torvalds 		*offset = pos + size;
2031da177e4SLinus Torvalds 	return size;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds 
206680ef72aSAl Viro static __poll_t snd_info_entry_poll(struct file *file, poll_table *wait)
2071da177e4SLinus Torvalds {
2084adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = file->private_data;
2094adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
210680ef72aSAl Viro 	__poll_t mask = 0;
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	if (entry->c.ops->poll)
2131da177e4SLinus Torvalds 		return entry->c.ops->poll(entry,
2141da177e4SLinus Torvalds 					  data->file_private_data,
2151da177e4SLinus Torvalds 					  file, wait);
2161da177e4SLinus Torvalds 	if (entry->c.ops->read)
217a9a08845SLinus Torvalds 		mask |= EPOLLIN | EPOLLRDNORM;
2181da177e4SLinus Torvalds 	if (entry->c.ops->write)
219a9a08845SLinus Torvalds 		mask |= EPOLLOUT | EPOLLWRNORM;
2201da177e4SLinus Torvalds 	return mask;
2211da177e4SLinus Torvalds }
2221da177e4SLinus Torvalds 
223d99e9889SIngo Molnar static long snd_info_entry_ioctl(struct file *file, unsigned int cmd,
224d99e9889SIngo Molnar 				unsigned long arg)
2251da177e4SLinus Torvalds {
2264adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = file->private_data;
2274adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
2281da177e4SLinus Torvalds 
2294adb7bcbSTakashi Iwai 	if (!entry->c.ops->ioctl)
2301da177e4SLinus Torvalds 		return -ENOTTY;
2314adb7bcbSTakashi Iwai 	return entry->c.ops->ioctl(entry, data->file_private_data,
2324adb7bcbSTakashi Iwai 				   file, cmd, arg);
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds static int snd_info_entry_mmap(struct file *file, struct vm_area_struct *vma)
2361da177e4SLinus Torvalds {
237496ad9aaSAl Viro 	struct inode *inode = file_inode(file);
23824c1f931STakashi Iwai 	struct snd_info_private_data *data;
2391da177e4SLinus Torvalds 	struct snd_info_entry *entry;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	data = file->private_data;
2421da177e4SLinus Torvalds 	if (data == NULL)
2431da177e4SLinus Torvalds 		return 0;
2441da177e4SLinus Torvalds 	entry = data->entry;
2454adb7bcbSTakashi Iwai 	if (!entry->c.ops->mmap)
2461da177e4SLinus Torvalds 		return -ENXIO;
2474adb7bcbSTakashi Iwai 	return entry->c.ops->mmap(entry, data->file_private_data,
2484adb7bcbSTakashi Iwai 				  inode, file, vma);
2494adb7bcbSTakashi Iwai }
2504adb7bcbSTakashi Iwai 
2514adb7bcbSTakashi Iwai static int snd_info_entry_open(struct inode *inode, struct file *file)
2524adb7bcbSTakashi Iwai {
2534adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = PDE_DATA(inode);
2544adb7bcbSTakashi Iwai 	struct snd_info_private_data *data;
2554adb7bcbSTakashi Iwai 	int mode, err;
2564adb7bcbSTakashi Iwai 
2574adb7bcbSTakashi Iwai 	mutex_lock(&info_mutex);
2584adb7bcbSTakashi Iwai 	err = alloc_info_private(entry, &data);
2594adb7bcbSTakashi Iwai 	if (err < 0)
2604adb7bcbSTakashi Iwai 		goto unlock;
2614adb7bcbSTakashi Iwai 
2624adb7bcbSTakashi Iwai 	mode = file->f_flags & O_ACCMODE;
2634adb7bcbSTakashi Iwai 	if (((mode == O_RDONLY || mode == O_RDWR) && !entry->c.ops->read) ||
2644adb7bcbSTakashi Iwai 	    ((mode == O_WRONLY || mode == O_RDWR) && !entry->c.ops->write)) {
2654adb7bcbSTakashi Iwai 		err = -ENODEV;
2664adb7bcbSTakashi Iwai 		goto error;
2674adb7bcbSTakashi Iwai 	}
2684adb7bcbSTakashi Iwai 
2694adb7bcbSTakashi Iwai 	if (entry->c.ops->open) {
2704adb7bcbSTakashi Iwai 		err = entry->c.ops->open(entry, mode, &data->file_private_data);
2714adb7bcbSTakashi Iwai 		if (err < 0)
2724adb7bcbSTakashi Iwai 			goto error;
2734adb7bcbSTakashi Iwai 	}
2744adb7bcbSTakashi Iwai 
2754adb7bcbSTakashi Iwai 	file->private_data = data;
2764adb7bcbSTakashi Iwai 	mutex_unlock(&info_mutex);
2774adb7bcbSTakashi Iwai 	return 0;
2784adb7bcbSTakashi Iwai 
2794adb7bcbSTakashi Iwai  error:
2804adb7bcbSTakashi Iwai 	kfree(data);
2814adb7bcbSTakashi Iwai 	module_put(entry->module);
2824adb7bcbSTakashi Iwai  unlock:
2834adb7bcbSTakashi Iwai 	mutex_unlock(&info_mutex);
2844adb7bcbSTakashi Iwai 	return err;
2854adb7bcbSTakashi Iwai }
2864adb7bcbSTakashi Iwai 
2874adb7bcbSTakashi Iwai static int snd_info_entry_release(struct inode *inode, struct file *file)
2884adb7bcbSTakashi Iwai {
2894adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = file->private_data;
2904adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
2914adb7bcbSTakashi Iwai 
2924adb7bcbSTakashi Iwai 	if (entry->c.ops->release)
2934adb7bcbSTakashi Iwai 		entry->c.ops->release(entry, file->f_flags & O_ACCMODE,
2944adb7bcbSTakashi Iwai 				      data->file_private_data);
2954adb7bcbSTakashi Iwai 	module_put(entry->module);
2964adb7bcbSTakashi Iwai 	kfree(data);
2974adb7bcbSTakashi Iwai 	return 0;
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
3009c2e08c5SArjan van de Ven static const struct file_operations snd_info_entry_operations =
3011da177e4SLinus Torvalds {
3021da177e4SLinus Torvalds 	.owner =		THIS_MODULE,
3031da177e4SLinus Torvalds 	.llseek =		snd_info_entry_llseek,
3041da177e4SLinus Torvalds 	.read =			snd_info_entry_read,
3051da177e4SLinus Torvalds 	.write =		snd_info_entry_write,
3061da177e4SLinus Torvalds 	.poll =			snd_info_entry_poll,
307d99e9889SIngo Molnar 	.unlocked_ioctl =	snd_info_entry_ioctl,
3081da177e4SLinus Torvalds 	.mmap =			snd_info_entry_mmap,
3091da177e4SLinus Torvalds 	.open =			snd_info_entry_open,
3101da177e4SLinus Torvalds 	.release =		snd_info_entry_release,
3111da177e4SLinus Torvalds };
3121da177e4SLinus Torvalds 
3134adb7bcbSTakashi Iwai /*
3144adb7bcbSTakashi Iwai  * file ops for text proc files
3154adb7bcbSTakashi Iwai  */
3164adb7bcbSTakashi Iwai static ssize_t snd_info_text_entry_write(struct file *file,
3174adb7bcbSTakashi Iwai 					 const char __user *buffer,
3184adb7bcbSTakashi Iwai 					 size_t count, loff_t *offset)
3194adb7bcbSTakashi Iwai {
3204adb7bcbSTakashi Iwai 	struct seq_file *m = file->private_data;
3214adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = m->private;
3224adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
3234adb7bcbSTakashi Iwai 	struct snd_info_buffer *buf;
3244adb7bcbSTakashi Iwai 	loff_t pos;
3254adb7bcbSTakashi Iwai 	size_t next;
3264adb7bcbSTakashi Iwai 	int err = 0;
3274adb7bcbSTakashi Iwai 
3286809cd68STakashi Iwai 	if (!entry->c.text.write)
3296809cd68STakashi Iwai 		return -EIO;
3304adb7bcbSTakashi Iwai 	pos = *offset;
3314adb7bcbSTakashi Iwai 	if (!valid_pos(pos, count))
3324adb7bcbSTakashi Iwai 		return -EIO;
3334adb7bcbSTakashi Iwai 	next = pos + count;
334027a9fe6STakashi Iwai 	/* don't handle too large text inputs */
335027a9fe6STakashi Iwai 	if (next > 16 * 1024)
336027a9fe6STakashi Iwai 		return -EIO;
3374adb7bcbSTakashi Iwai 	mutex_lock(&entry->access);
3384adb7bcbSTakashi Iwai 	buf = data->wbuffer;
3394adb7bcbSTakashi Iwai 	if (!buf) {
3404adb7bcbSTakashi Iwai 		data->wbuffer = buf = kzalloc(sizeof(*buf), GFP_KERNEL);
3414adb7bcbSTakashi Iwai 		if (!buf) {
3424adb7bcbSTakashi Iwai 			err = -ENOMEM;
3434adb7bcbSTakashi Iwai 			goto error;
3444adb7bcbSTakashi Iwai 		}
3454adb7bcbSTakashi Iwai 	}
3464adb7bcbSTakashi Iwai 	if (next > buf->len) {
347ffb73b08STakashi Iwai 		char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL);
3484adb7bcbSTakashi Iwai 		if (!nbuf) {
3494adb7bcbSTakashi Iwai 			err = -ENOMEM;
3504adb7bcbSTakashi Iwai 			goto error;
3514adb7bcbSTakashi Iwai 		}
352ffb73b08STakashi Iwai 		kvfree(buf->buffer);
3534adb7bcbSTakashi Iwai 		buf->buffer = nbuf;
3544adb7bcbSTakashi Iwai 		buf->len = PAGE_ALIGN(next);
3554adb7bcbSTakashi Iwai 	}
3564adb7bcbSTakashi Iwai 	if (copy_from_user(buf->buffer + pos, buffer, count)) {
3574adb7bcbSTakashi Iwai 		err = -EFAULT;
3584adb7bcbSTakashi Iwai 		goto error;
3594adb7bcbSTakashi Iwai 	}
3604adb7bcbSTakashi Iwai 	buf->size = next;
3614adb7bcbSTakashi Iwai  error:
3624adb7bcbSTakashi Iwai 	mutex_unlock(&entry->access);
3634adb7bcbSTakashi Iwai 	if (err < 0)
3644adb7bcbSTakashi Iwai 		return err;
3654adb7bcbSTakashi Iwai 	*offset = next;
3664adb7bcbSTakashi Iwai 	return count;
3674adb7bcbSTakashi Iwai }
3684adb7bcbSTakashi Iwai 
3694adb7bcbSTakashi Iwai static int snd_info_seq_show(struct seq_file *seq, void *p)
3704adb7bcbSTakashi Iwai {
3714adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = seq->private;
3724adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
3734adb7bcbSTakashi Iwai 
3746809cd68STakashi Iwai 	if (!entry->c.text.read) {
3756809cd68STakashi Iwai 		return -EIO;
3766809cd68STakashi Iwai 	} else {
3774adb7bcbSTakashi Iwai 		data->rbuffer->buffer = (char *)seq; /* XXX hack! */
3784adb7bcbSTakashi Iwai 		entry->c.text.read(entry, data->rbuffer);
3794adb7bcbSTakashi Iwai 	}
3804adb7bcbSTakashi Iwai 	return 0;
3814adb7bcbSTakashi Iwai }
3824adb7bcbSTakashi Iwai 
3834adb7bcbSTakashi Iwai static int snd_info_text_entry_open(struct inode *inode, struct file *file)
3844adb7bcbSTakashi Iwai {
3854adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = PDE_DATA(inode);
3864adb7bcbSTakashi Iwai 	struct snd_info_private_data *data;
3874adb7bcbSTakashi Iwai 	int err;
3884adb7bcbSTakashi Iwai 
3894adb7bcbSTakashi Iwai 	mutex_lock(&info_mutex);
3904adb7bcbSTakashi Iwai 	err = alloc_info_private(entry, &data);
3914adb7bcbSTakashi Iwai 	if (err < 0)
3924adb7bcbSTakashi Iwai 		goto unlock;
3934adb7bcbSTakashi Iwai 
3944adb7bcbSTakashi Iwai 	data->rbuffer = kzalloc(sizeof(*data->rbuffer), GFP_KERNEL);
3954adb7bcbSTakashi Iwai 	if (!data->rbuffer) {
3964adb7bcbSTakashi Iwai 		err = -ENOMEM;
3974adb7bcbSTakashi Iwai 		goto error;
3984adb7bcbSTakashi Iwai 	}
3994adb7bcbSTakashi Iwai 	if (entry->size)
4004adb7bcbSTakashi Iwai 		err = single_open_size(file, snd_info_seq_show, data,
4014adb7bcbSTakashi Iwai 				       entry->size);
4024adb7bcbSTakashi Iwai 	else
4034adb7bcbSTakashi Iwai 		err = single_open(file, snd_info_seq_show, data);
4044adb7bcbSTakashi Iwai 	if (err < 0)
4054adb7bcbSTakashi Iwai 		goto error;
4064adb7bcbSTakashi Iwai 	mutex_unlock(&info_mutex);
4074adb7bcbSTakashi Iwai 	return 0;
4084adb7bcbSTakashi Iwai 
4094adb7bcbSTakashi Iwai  error:
4104adb7bcbSTakashi Iwai 	kfree(data->rbuffer);
4114adb7bcbSTakashi Iwai 	kfree(data);
4124adb7bcbSTakashi Iwai 	module_put(entry->module);
4134adb7bcbSTakashi Iwai  unlock:
4144adb7bcbSTakashi Iwai 	mutex_unlock(&info_mutex);
4154adb7bcbSTakashi Iwai 	return err;
4164adb7bcbSTakashi Iwai }
4174adb7bcbSTakashi Iwai 
4184adb7bcbSTakashi Iwai static int snd_info_text_entry_release(struct inode *inode, struct file *file)
4194adb7bcbSTakashi Iwai {
4204adb7bcbSTakashi Iwai 	struct seq_file *m = file->private_data;
4214adb7bcbSTakashi Iwai 	struct snd_info_private_data *data = m->private;
4224adb7bcbSTakashi Iwai 	struct snd_info_entry *entry = data->entry;
4234adb7bcbSTakashi Iwai 
4244adb7bcbSTakashi Iwai 	if (data->wbuffer && entry->c.text.write)
4254adb7bcbSTakashi Iwai 		entry->c.text.write(entry, data->wbuffer);
4264adb7bcbSTakashi Iwai 
4274adb7bcbSTakashi Iwai 	single_release(inode, file);
4284adb7bcbSTakashi Iwai 	kfree(data->rbuffer);
4294adb7bcbSTakashi Iwai 	if (data->wbuffer) {
430ffb73b08STakashi Iwai 		kvfree(data->wbuffer->buffer);
4314adb7bcbSTakashi Iwai 		kfree(data->wbuffer);
4324adb7bcbSTakashi Iwai 	}
4334adb7bcbSTakashi Iwai 
4344adb7bcbSTakashi Iwai 	module_put(entry->module);
4354adb7bcbSTakashi Iwai 	kfree(data);
4364adb7bcbSTakashi Iwai 	return 0;
4374adb7bcbSTakashi Iwai }
4384adb7bcbSTakashi Iwai 
4394adb7bcbSTakashi Iwai static const struct file_operations snd_info_text_entry_ops =
4404adb7bcbSTakashi Iwai {
4414adb7bcbSTakashi Iwai 	.owner =		THIS_MODULE,
4424adb7bcbSTakashi Iwai 	.open =			snd_info_text_entry_open,
4434adb7bcbSTakashi Iwai 	.release =		snd_info_text_entry_release,
4444adb7bcbSTakashi Iwai 	.write =		snd_info_text_entry_write,
4454adb7bcbSTakashi Iwai 	.llseek =		seq_lseek,
4464adb7bcbSTakashi Iwai 	.read =			seq_read,
4474adb7bcbSTakashi Iwai };
4484adb7bcbSTakashi Iwai 
449886364f6STakashi Iwai static struct snd_info_entry *create_subdir(struct module *mod,
450886364f6STakashi Iwai 					    const char *name)
451886364f6STakashi Iwai {
452886364f6STakashi Iwai 	struct snd_info_entry *entry;
453886364f6STakashi Iwai 
454886364f6STakashi Iwai 	entry = snd_info_create_module_entry(mod, name, NULL);
455886364f6STakashi Iwai 	if (!entry)
456886364f6STakashi Iwai 		return NULL;
4576a73cf46SJoe Perches 	entry->mode = S_IFDIR | 0555;
458886364f6STakashi Iwai 	if (snd_info_register(entry) < 0) {
459886364f6STakashi Iwai 		snd_info_free_entry(entry);
460886364f6STakashi Iwai 		return NULL;
461886364f6STakashi Iwai 	}
462886364f6STakashi Iwai 	return entry;
463886364f6STakashi Iwai }
464886364f6STakashi Iwai 
4658e7ccb7bSTakashi Iwai static struct snd_info_entry *
4668e7ccb7bSTakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent);
467644dbd64STakashi Iwai 
4681da177e4SLinus Torvalds int __init snd_info_init(void)
4691da177e4SLinus Torvalds {
4708e7ccb7bSTakashi Iwai 	snd_proc_root = snd_info_create_entry("asound", NULL);
471644dbd64STakashi Iwai 	if (!snd_proc_root)
4721da177e4SLinus Torvalds 		return -ENOMEM;
4736a73cf46SJoe Perches 	snd_proc_root->mode = S_IFDIR | 0555;
474644dbd64STakashi Iwai 	snd_proc_root->p = proc_mkdir("asound", NULL);
475644dbd64STakashi Iwai 	if (!snd_proc_root->p)
476644dbd64STakashi Iwai 		goto error;
4771da177e4SLinus Torvalds #ifdef CONFIG_SND_OSSEMUL
478886364f6STakashi Iwai 	snd_oss_root = create_subdir(THIS_MODULE, "oss");
479886364f6STakashi Iwai 	if (!snd_oss_root)
480886364f6STakashi Iwai 		goto error;
4811da177e4SLinus Torvalds #endif
4828eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_SEQUENCER)
483886364f6STakashi Iwai 	snd_seq_root = create_subdir(THIS_MODULE, "seq");
484886364f6STakashi Iwai 	if (!snd_seq_root)
485886364f6STakashi Iwai 		goto error;
4861da177e4SLinus Torvalds #endif
487b591b6e9STakashi Iwai 	if (snd_info_version_init() < 0 ||
488b591b6e9STakashi Iwai 	    snd_minor_info_init() < 0 ||
489b591b6e9STakashi Iwai 	    snd_minor_info_oss_init() < 0 ||
490a0dca822STakashi Iwai 	    snd_card_info_init() < 0 ||
491a0dca822STakashi Iwai 	    snd_info_minor_register() < 0)
492b591b6e9STakashi Iwai 		goto error;
4931da177e4SLinus Torvalds 	return 0;
494886364f6STakashi Iwai 
495886364f6STakashi Iwai  error:
496644dbd64STakashi Iwai 	snd_info_free_entry(snd_proc_root);
497886364f6STakashi Iwai 	return -ENOMEM;
4981da177e4SLinus Torvalds }
4991da177e4SLinus Torvalds 
5001da177e4SLinus Torvalds int __exit snd_info_done(void)
5011da177e4SLinus Torvalds {
502644dbd64STakashi Iwai 	snd_info_free_entry(snd_proc_root);
5031da177e4SLinus Torvalds 	return 0;
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds /*
5071da177e4SLinus Torvalds  * create a card proc file
5081da177e4SLinus Torvalds  * called from init.c
5091da177e4SLinus Torvalds  */
51024c1f931STakashi Iwai int snd_info_card_create(struct snd_card *card)
5111da177e4SLinus Torvalds {
5121da177e4SLinus Torvalds 	char str[8];
51324c1f931STakashi Iwai 	struct snd_info_entry *entry;
5141da177e4SLinus Torvalds 
5157eaa943cSTakashi Iwai 	if (snd_BUG_ON(!card))
5167eaa943cSTakashi Iwai 		return -ENXIO;
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 	sprintf(str, "card%i", card->number);
519886364f6STakashi Iwai 	entry = create_subdir(card->module, str);
520886364f6STakashi Iwai 	if (!entry)
5211da177e4SLinus Torvalds 		return -ENOMEM;
5221da177e4SLinus Torvalds 	card->proc_root = entry;
5231da177e4SLinus Torvalds 	return 0;
5241da177e4SLinus Torvalds }
5251da177e4SLinus Torvalds 
5262471b6c8STakashi Iwai /* register all pending info entries */
5272471b6c8STakashi Iwai static int snd_info_register_recursive(struct snd_info_entry *entry)
5282471b6c8STakashi Iwai {
5292471b6c8STakashi Iwai 	struct snd_info_entry *p;
5302471b6c8STakashi Iwai 	int err;
5312471b6c8STakashi Iwai 
5322471b6c8STakashi Iwai 	if (!entry->p) {
5332471b6c8STakashi Iwai 		err = snd_info_register(entry);
5342471b6c8STakashi Iwai 		if (err < 0)
5352471b6c8STakashi Iwai 			return err;
5362471b6c8STakashi Iwai 	}
5372471b6c8STakashi Iwai 
5382471b6c8STakashi Iwai 	list_for_each_entry(p, &entry->children, list) {
5392471b6c8STakashi Iwai 		err = snd_info_register_recursive(p);
5402471b6c8STakashi Iwai 		if (err < 0)
5412471b6c8STakashi Iwai 			return err;
5422471b6c8STakashi Iwai 	}
5432471b6c8STakashi Iwai 
5442471b6c8STakashi Iwai 	return 0;
5452471b6c8STakashi Iwai }
5462471b6c8STakashi Iwai 
5471da177e4SLinus Torvalds /*
5481da177e4SLinus Torvalds  * register the card proc file
5491da177e4SLinus Torvalds  * called from init.c
5502471b6c8STakashi Iwai  * can be called multiple times for reinitialization
5511da177e4SLinus Torvalds  */
55224c1f931STakashi Iwai int snd_info_card_register(struct snd_card *card)
5531da177e4SLinus Torvalds {
5541da177e4SLinus Torvalds 	struct proc_dir_entry *p;
5552471b6c8STakashi Iwai 	int err;
5561da177e4SLinus Torvalds 
5577eaa943cSTakashi Iwai 	if (snd_BUG_ON(!card))
5587eaa943cSTakashi Iwai 		return -ENXIO;
5591da177e4SLinus Torvalds 
5602471b6c8STakashi Iwai 	err = snd_info_register_recursive(card->proc_root);
5612471b6c8STakashi Iwai 	if (err < 0)
5622471b6c8STakashi Iwai 		return err;
5632471b6c8STakashi Iwai 
5641da177e4SLinus Torvalds 	if (!strcmp(card->id, card->proc_root->name))
5651da177e4SLinus Torvalds 		return 0;
5661da177e4SLinus Torvalds 
5672471b6c8STakashi Iwai 	if (card->proc_root_link)
5682471b6c8STakashi Iwai 		return 0;
569644dbd64STakashi Iwai 	p = proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
5702471b6c8STakashi Iwai 	if (!p)
5711da177e4SLinus Torvalds 		return -ENOMEM;
5721da177e4SLinus Torvalds 	card->proc_root_link = p;
5731da177e4SLinus Torvalds 	return 0;
5741da177e4SLinus Torvalds }
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds /*
577c2eb9c4eSJaroslav Kysela  * called on card->id change
578c2eb9c4eSJaroslav Kysela  */
579c2eb9c4eSJaroslav Kysela void snd_info_card_id_change(struct snd_card *card)
580c2eb9c4eSJaroslav Kysela {
581c2eb9c4eSJaroslav Kysela 	mutex_lock(&info_mutex);
582c2eb9c4eSJaroslav Kysela 	if (card->proc_root_link) {
583a8ca16eaSDavid Howells 		proc_remove(card->proc_root_link);
584c2eb9c4eSJaroslav Kysela 		card->proc_root_link = NULL;
585c2eb9c4eSJaroslav Kysela 	}
586c2eb9c4eSJaroslav Kysela 	if (strcmp(card->id, card->proc_root->name))
587c2eb9c4eSJaroslav Kysela 		card->proc_root_link = proc_symlink(card->id,
588644dbd64STakashi Iwai 						    snd_proc_root->p,
589c2eb9c4eSJaroslav Kysela 						    card->proc_root->name);
590c2eb9c4eSJaroslav Kysela 	mutex_unlock(&info_mutex);
591c2eb9c4eSJaroslav Kysela }
592c2eb9c4eSJaroslav Kysela 
593c2eb9c4eSJaroslav Kysela /*
5941da177e4SLinus Torvalds  * de-register the card proc file
5951da177e4SLinus Torvalds  * called from init.c
5961da177e4SLinus Torvalds  */
597746d4a02STakashi Iwai void snd_info_card_disconnect(struct snd_card *card)
5981da177e4SLinus Torvalds {
5997eaa943cSTakashi Iwai 	if (!card)
6007eaa943cSTakashi Iwai 		return;
601746d4a02STakashi Iwai 	mutex_lock(&info_mutex);
602a8ca16eaSDavid Howells 	proc_remove(card->proc_root_link);
6031da177e4SLinus Torvalds 	card->proc_root_link = NULL;
604746d4a02STakashi Iwai 	if (card->proc_root)
605746d4a02STakashi Iwai 		snd_info_disconnect(card->proc_root);
606746d4a02STakashi Iwai 	mutex_unlock(&info_mutex);
6071da177e4SLinus Torvalds }
608746d4a02STakashi Iwai 
609746d4a02STakashi Iwai /*
610746d4a02STakashi Iwai  * release the card proc file resources
611746d4a02STakashi Iwai  * called from init.c
612746d4a02STakashi Iwai  */
613746d4a02STakashi Iwai int snd_info_card_free(struct snd_card *card)
614746d4a02STakashi Iwai {
6157eaa943cSTakashi Iwai 	if (!card)
6167eaa943cSTakashi Iwai 		return 0;
617746d4a02STakashi Iwai 	snd_info_free_entry(card->proc_root);
618746d4a02STakashi Iwai 	card->proc_root = NULL;
6191da177e4SLinus Torvalds 	return 0;
6201da177e4SLinus Torvalds }
6211da177e4SLinus Torvalds 
6221da177e4SLinus Torvalds 
6231da177e4SLinus Torvalds /**
6241da177e4SLinus Torvalds  * snd_info_get_line - read one line from the procfs buffer
6251da177e4SLinus Torvalds  * @buffer: the procfs buffer
6261da177e4SLinus Torvalds  * @line: the buffer to store
627ddc64b27SClemens Ladisch  * @len: the max. buffer size
6281da177e4SLinus Torvalds  *
6291da177e4SLinus Torvalds  * Reads one line from the buffer and stores the string.
6301da177e4SLinus Torvalds  *
631eb7c06e8SYacine Belkadi  * Return: Zero if successful, or 1 if error or EOF.
6321da177e4SLinus Torvalds  */
63324c1f931STakashi Iwai int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len)
6341da177e4SLinus Torvalds {
6351da177e4SLinus Torvalds 	int c = -1;
6361da177e4SLinus Torvalds 
6370bc0ec90STakashi Iwai 	if (snd_BUG_ON(!buffer || !buffer->buffer))
6380bc0ec90STakashi Iwai 		return 1;
6391da177e4SLinus Torvalds 	if (len <= 0 || buffer->stop || buffer->error)
6401da177e4SLinus Torvalds 		return 1;
6410bc0ec90STakashi Iwai 	while (!buffer->stop) {
6427e4eeec8STakashi Iwai 		c = buffer->buffer[buffer->curr++];
6437e4eeec8STakashi Iwai 		if (buffer->curr >= buffer->size)
6441da177e4SLinus Torvalds 			buffer->stop = 1;
6450bc0ec90STakashi Iwai 		if (c == '\n')
6461da177e4SLinus Torvalds 			break;
647ddc64b27SClemens Ladisch 		if (len > 1) {
6480bc0ec90STakashi Iwai 			len--;
6491da177e4SLinus Torvalds 			*line++ = c;
6501da177e4SLinus Torvalds 		}
6511da177e4SLinus Torvalds 	}
6521da177e4SLinus Torvalds 	*line = '\0';
6531da177e4SLinus Torvalds 	return 0;
6541da177e4SLinus Torvalds }
655c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_line);
656c0d3fb39STakashi Iwai 
6571da177e4SLinus Torvalds /**
658856def8aSHenrik Kretzschmar  * snd_info_get_str - parse a string token
6591da177e4SLinus Torvalds  * @dest: the buffer to store the string token
6601da177e4SLinus Torvalds  * @src: the original string
6611da177e4SLinus Torvalds  * @len: the max. length of token - 1
6621da177e4SLinus Torvalds  *
6631da177e4SLinus Torvalds  * Parses the original string and copy a token to the given
6641da177e4SLinus Torvalds  * string buffer.
6651da177e4SLinus Torvalds  *
666eb7c06e8SYacine Belkadi  * Return: The updated pointer of the original string so that
6671da177e4SLinus Torvalds  * it can be used for the next call.
6681da177e4SLinus Torvalds  */
6694f7454a9STakashi Iwai const char *snd_info_get_str(char *dest, const char *src, int len)
6701da177e4SLinus Torvalds {
6711da177e4SLinus Torvalds 	int c;
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds 	while (*src == ' ' || *src == '\t')
6741da177e4SLinus Torvalds 		src++;
6751da177e4SLinus Torvalds 	if (*src == '"' || *src == '\'') {
6761da177e4SLinus Torvalds 		c = *src++;
6771da177e4SLinus Torvalds 		while (--len > 0 && *src && *src != c) {
6781da177e4SLinus Torvalds 			*dest++ = *src++;
6791da177e4SLinus Torvalds 		}
6801da177e4SLinus Torvalds 		if (*src == c)
6811da177e4SLinus Torvalds 			src++;
6821da177e4SLinus Torvalds 	} else {
6831da177e4SLinus Torvalds 		while (--len > 0 && *src && *src != ' ' && *src != '\t') {
6841da177e4SLinus Torvalds 			*dest++ = *src++;
6851da177e4SLinus Torvalds 		}
6861da177e4SLinus Torvalds 	}
6871da177e4SLinus Torvalds 	*dest = 0;
6881da177e4SLinus Torvalds 	while (*src == ' ' || *src == '\t')
6891da177e4SLinus Torvalds 		src++;
6901da177e4SLinus Torvalds 	return src;
6911da177e4SLinus Torvalds }
692c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_get_str);
693c0d3fb39STakashi Iwai 
694c309c467STakashi Iwai /*
6951da177e4SLinus Torvalds  * snd_info_create_entry - create an info entry
6961da177e4SLinus Torvalds  * @name: the proc file name
6978e7ccb7bSTakashi Iwai  * @parent: the parent directory
6981da177e4SLinus Torvalds  *
6991da177e4SLinus Torvalds  * Creates an info entry with the given file name and initializes as
7001da177e4SLinus Torvalds  * the default state.
7011da177e4SLinus Torvalds  *
7021da177e4SLinus Torvalds  * Usually called from other functions such as
7031da177e4SLinus Torvalds  * snd_info_create_card_entry().
7041da177e4SLinus Torvalds  *
705eb7c06e8SYacine Belkadi  * Return: The pointer of the new instance, or %NULL on failure.
7061da177e4SLinus Torvalds  */
7078e7ccb7bSTakashi Iwai static struct snd_info_entry *
7088e7ccb7bSTakashi Iwai snd_info_create_entry(const char *name, struct snd_info_entry *parent)
7091da177e4SLinus Torvalds {
71024c1f931STakashi Iwai 	struct snd_info_entry *entry;
711ca2c0966STakashi Iwai 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
7121da177e4SLinus Torvalds 	if (entry == NULL)
7131da177e4SLinus Torvalds 		return NULL;
714543537bdSPaulo Marques 	entry->name = kstrdup(name, GFP_KERNEL);
7151da177e4SLinus Torvalds 	if (entry->name == NULL) {
7161da177e4SLinus Torvalds 		kfree(entry);
7171da177e4SLinus Torvalds 		return NULL;
7181da177e4SLinus Torvalds 	}
7196a73cf46SJoe Perches 	entry->mode = S_IFREG | 0444;
7201da177e4SLinus Torvalds 	entry->content = SNDRV_INFO_CONTENT_TEXT;
7211a60d4c5SIngo Molnar 	mutex_init(&entry->access);
722746d4a02STakashi Iwai 	INIT_LIST_HEAD(&entry->children);
723746d4a02STakashi Iwai 	INIT_LIST_HEAD(&entry->list);
7248e7ccb7bSTakashi Iwai 	entry->parent = parent;
7258e7ccb7bSTakashi Iwai 	if (parent)
7268e7ccb7bSTakashi Iwai 		list_add_tail(&entry->list, &parent->children);
7271da177e4SLinus Torvalds 	return entry;
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds /**
7311da177e4SLinus Torvalds  * snd_info_create_module_entry - create an info entry for the given module
7321da177e4SLinus Torvalds  * @module: the module pointer
7331da177e4SLinus Torvalds  * @name: the file name
7341da177e4SLinus Torvalds  * @parent: the parent directory
7351da177e4SLinus Torvalds  *
7361da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given module.
7371da177e4SLinus Torvalds  *
738eb7c06e8SYacine Belkadi  * Return: The pointer of the new instance, or %NULL on failure.
7391da177e4SLinus Torvalds  */
74024c1f931STakashi Iwai struct snd_info_entry *snd_info_create_module_entry(struct module * module,
7411da177e4SLinus Torvalds 					       const char *name,
74224c1f931STakashi Iwai 					       struct snd_info_entry *parent)
7431da177e4SLinus Torvalds {
7443a554371STakashi Iwai 	struct snd_info_entry *entry;
7453a554371STakashi Iwai 
7463a554371STakashi Iwai 	if (!parent)
7473a554371STakashi Iwai 		parent = snd_proc_root;
7483a554371STakashi Iwai 	entry = snd_info_create_entry(name, parent);
7498e7ccb7bSTakashi Iwai 	if (entry)
7501da177e4SLinus Torvalds 		entry->module = module;
7511da177e4SLinus Torvalds 	return entry;
7521da177e4SLinus Torvalds }
753c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_module_entry);
754c0d3fb39STakashi Iwai 
7551da177e4SLinus Torvalds /**
7561da177e4SLinus Torvalds  * snd_info_create_card_entry - create an info entry for the given card
7571da177e4SLinus Torvalds  * @card: the card instance
7581da177e4SLinus Torvalds  * @name: the file name
7591da177e4SLinus Torvalds  * @parent: the parent directory
7601da177e4SLinus Torvalds  *
7611da177e4SLinus Torvalds  * Creates a new info entry and assigns it to the given card.
7621da177e4SLinus Torvalds  *
763eb7c06e8SYacine Belkadi  * Return: The pointer of the new instance, or %NULL on failure.
7641da177e4SLinus Torvalds  */
76524c1f931STakashi Iwai struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
7661da177e4SLinus Torvalds 					     const char *name,
76724c1f931STakashi Iwai 					     struct snd_info_entry * parent)
7681da177e4SLinus Torvalds {
7693a554371STakashi Iwai 	struct snd_info_entry *entry;
7703a554371STakashi Iwai 
7713a554371STakashi Iwai 	if (!parent)
7723a554371STakashi Iwai 		parent = card->proc_root;
7733a554371STakashi Iwai 	entry = snd_info_create_entry(name, parent);
7741da177e4SLinus Torvalds 	if (entry) {
7751da177e4SLinus Torvalds 		entry->module = card->module;
7761da177e4SLinus Torvalds 		entry->card = card;
7771da177e4SLinus Torvalds 	}
7781da177e4SLinus Torvalds 	return entry;
7791da177e4SLinus Torvalds }
780c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_create_card_entry);
781c0d3fb39STakashi Iwai 
782746d4a02STakashi Iwai static void snd_info_disconnect(struct snd_info_entry *entry)
783746d4a02STakashi Iwai {
78490a409aaSTakashi Iwai 	struct snd_info_entry *p;
785746d4a02STakashi Iwai 
786746d4a02STakashi Iwai 	if (!entry->p)
787746d4a02STakashi Iwai 		return;
78890a409aaSTakashi Iwai 	list_for_each_entry(p, &entry->children, list)
789c560a679STakashi Iwai 		snd_info_disconnect(p);
790a8ca16eaSDavid Howells 	proc_remove(entry->p);
791746d4a02STakashi Iwai 	entry->p = NULL;
792746d4a02STakashi Iwai }
793746d4a02STakashi Iwai 
7941da177e4SLinus Torvalds /**
7951da177e4SLinus Torvalds  * snd_info_free_entry - release the info entry
7961da177e4SLinus Torvalds  * @entry: the info entry
7971da177e4SLinus Torvalds  *
798c560a679STakashi Iwai  * Releases the info entry.
7991da177e4SLinus Torvalds  */
80024c1f931STakashi Iwai void snd_info_free_entry(struct snd_info_entry * entry)
8011da177e4SLinus Torvalds {
802c560a679STakashi Iwai 	struct snd_info_entry *p, *n;
803c560a679STakashi Iwai 
804c560a679STakashi Iwai 	if (!entry)
8051da177e4SLinus Torvalds 		return;
806746d4a02STakashi Iwai 	if (entry->p) {
807746d4a02STakashi Iwai 		mutex_lock(&info_mutex);
808746d4a02STakashi Iwai 		snd_info_disconnect(entry);
809746d4a02STakashi Iwai 		mutex_unlock(&info_mutex);
810746d4a02STakashi Iwai 	}
811c560a679STakashi Iwai 
812c560a679STakashi Iwai 	/* free all children at first */
813c560a679STakashi Iwai 	list_for_each_entry_safe(p, n, &entry->children, list)
814c560a679STakashi Iwai 		snd_info_free_entry(p);
815c560a679STakashi Iwai 
81690a409aaSTakashi Iwai 	list_del(&entry->list);
8171da177e4SLinus Torvalds 	kfree(entry->name);
8181da177e4SLinus Torvalds 	if (entry->private_free)
8191da177e4SLinus Torvalds 		entry->private_free(entry);
8201da177e4SLinus Torvalds 	kfree(entry);
8211da177e4SLinus Torvalds }
822c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_free_entry);
823c0d3fb39STakashi Iwai 
8241da177e4SLinus Torvalds /**
8251da177e4SLinus Torvalds  * snd_info_register - register the info entry
8261da177e4SLinus Torvalds  * @entry: the info entry
8271da177e4SLinus Torvalds  *
8281da177e4SLinus Torvalds  * Registers the proc info entry.
8291da177e4SLinus Torvalds  *
830eb7c06e8SYacine Belkadi  * Return: Zero if successful, or a negative error code on failure.
8311da177e4SLinus Torvalds  */
83224c1f931STakashi Iwai int snd_info_register(struct snd_info_entry * entry)
8331da177e4SLinus Torvalds {
8341da177e4SLinus Torvalds 	struct proc_dir_entry *root, *p = NULL;
8351da177e4SLinus Torvalds 
8367eaa943cSTakashi Iwai 	if (snd_BUG_ON(!entry))
8377eaa943cSTakashi Iwai 		return -ENXIO;
838644dbd64STakashi Iwai 	root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
8391a60d4c5SIngo Molnar 	mutex_lock(&info_mutex);
840aee0c612SAl Viro 	if (S_ISDIR(entry->mode)) {
841aee0c612SAl Viro 		p = proc_mkdir_mode(entry->name, entry->mode, root);
8421da177e4SLinus Torvalds 		if (!p) {
8431a60d4c5SIngo Molnar 			mutex_unlock(&info_mutex);
8441da177e4SLinus Torvalds 			return -ENOMEM;
8451da177e4SLinus Torvalds 		}
846aee0c612SAl Viro 	} else {
8474adb7bcbSTakashi Iwai 		const struct file_operations *ops;
8484adb7bcbSTakashi Iwai 		if (entry->content == SNDRV_INFO_CONTENT_DATA)
8494adb7bcbSTakashi Iwai 			ops = &snd_info_entry_operations;
8504adb7bcbSTakashi Iwai 		else
8514adb7bcbSTakashi Iwai 			ops = &snd_info_text_entry_ops;
852aee0c612SAl Viro 		p = proc_create_data(entry->name, entry->mode, root,
8534adb7bcbSTakashi Iwai 				     ops, entry);
854aee0c612SAl Viro 		if (!p) {
855aee0c612SAl Viro 			mutex_unlock(&info_mutex);
856aee0c612SAl Viro 			return -ENOMEM;
857aee0c612SAl Viro 		}
858271a15eaSDavid Howells 		proc_set_size(p, entry->size);
859aee0c612SAl Viro 	}
8601da177e4SLinus Torvalds 	entry->p = p;
8611a60d4c5SIngo Molnar 	mutex_unlock(&info_mutex);
8621da177e4SLinus Torvalds 	return 0;
8631da177e4SLinus Torvalds }
864c0d3fb39STakashi Iwai EXPORT_SYMBOL(snd_info_register);
865c0d3fb39STakashi Iwai 
8661da177e4SLinus Torvalds /*
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds  */
8691da177e4SLinus Torvalds 
87024c1f931STakashi Iwai static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
8711da177e4SLinus Torvalds {
8721da177e4SLinus Torvalds 	snd_iprintf(buffer,
87342662748SJaroslav Kysela 		    "Advanced Linux Sound Architecture Driver Version k%s.\n",
87442662748SJaroslav Kysela 		    init_utsname()->release);
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds static int __init snd_info_version_init(void)
8781da177e4SLinus Torvalds {
87924c1f931STakashi Iwai 	struct snd_info_entry *entry;
8801da177e4SLinus Torvalds 
8811da177e4SLinus Torvalds 	entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL);
8821da177e4SLinus Torvalds 	if (entry == NULL)
8831da177e4SLinus Torvalds 		return -ENOMEM;
8841da177e4SLinus Torvalds 	entry->c.text.read = snd_info_version_read;
885b591b6e9STakashi Iwai 	return snd_info_register(entry); /* freed in error path */
8861da177e4SLinus Torvalds }
887